catalyst-core-internal 0.1.2 → 0.1.3

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.
Files changed (53) hide show
  1. package/README.md +4 -4
  2. package/bin/catalyst.js +8 -1
  3. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +3 -11
  4. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +12 -1
  5. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +18 -3
  6. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +5 -0
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +6 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +240 -0
  9. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/SecurityBridgeTest.kt +199 -0
  10. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +121 -0
  11. package/dist/native/bridge/hooks.js +4 -4
  12. package/dist/native/bridge/useBaseHook.js +5 -4
  13. package/dist/native/bridge/utils/NativeBridge.js +4 -4
  14. package/dist/native/buildAppAndroid.js +2 -2
  15. package/dist/native/buildAppIos.js +10 -17
  16. package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
  17. package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
  18. package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
  19. package/dist/native/internalPluginUtils.js +1 -0
  20. package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +5 -0
  21. package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +6 -0
  22. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +364 -0
  23. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +13 -2
  24. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +13 -2
  25. package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +14 -0
  26. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +6 -0
  27. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +4 -0
  28. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +36 -0
  29. package/dist/native/iosnativeWebView/iosnativeWebView.xctestplan +1 -0
  30. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeCommandHandlerSecurityTests.swift +212 -0
  31. package/dist/native/iosnativeWebView/iosnativeWebViewTests/FrameworkServerUtilsTests.swift +14 -4
  32. package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +160 -0
  33. package/dist/native/iosnativeWebView/iosnativeWebViewTests/ScreenSecureManagerTests.swift +121 -0
  34. package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +9 -21
  35. package/dist/native/plugin-bridge/PluginBridge.js +1 -0
  36. package/dist/native/pluginComposerAndroid.js +9 -0
  37. package/dist/native/pluginComposerIos.js +7 -0
  38. package/dist/scripts/plugins.js +1 -0
  39. package/package.json +3 -2
  40. package/mcp_v2/conversion-tasks.json +0 -371
  41. package/mcp_v2/knowledge-base.json +0 -1450
  42. package/mcp_v2/lib/helpers.js +0 -145
  43. package/mcp_v2/mcp.js +0 -366
  44. package/mcp_v2/package.json +0 -13
  45. package/mcp_v2/schema.sql +0 -88
  46. package/mcp_v2/setup.js +0 -262
  47. package/mcp_v2/tools/build.js +0 -449
  48. package/mcp_v2/tools/config.js +0 -262
  49. package/mcp_v2/tools/conversion.js +0 -492
  50. package/mcp_v2/tools/debug.js +0 -62
  51. package/mcp_v2/tools/knowledge.js +0 -213
  52. package/mcp_v2/tools/sync.js +0 -21
  53. package/mcp_v2/tools/tasks.js +0 -844
@@ -0,0 +1,364 @@
1
+ import Foundation
2
+ import WebKit
3
+ import UIKit
4
+ import os
5
+
6
+ private let pluginBridgeLogger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.app", category: "PluginBridge")
7
+
8
+ private struct PluginBridgeValidationError: LocalizedError {
9
+ let message: String
10
+ let code: String
11
+
12
+ var errorDescription: String? {
13
+ message
14
+ }
15
+ }
16
+
17
+ private struct PluginRequest {
18
+ let pluginId: String
19
+ let command: String
20
+ let data: Any?
21
+ let requestId: String?
22
+ }
23
+
24
+ protocol PluginBridgeMessage {
25
+ var name: String { get }
26
+ var body: Any { get }
27
+ }
28
+
29
+ extension WKScriptMessage: PluginBridgeMessage {}
30
+
31
+ final class PluginBridge: NSObject {
32
+ private weak var webView: WKWebView?
33
+ private weak var viewController: UIViewController?
34
+ private var messageHandlerProxy: WeakScriptMessageHandler?
35
+ private var isRegistered = false
36
+
37
+ private let pluginFactories = GeneratedPluginIndex.pluginFactories
38
+ private let pluginToCommands = GeneratedPluginIndex.pluginToCommands
39
+
40
+ private let bridgeName = "PluginBridge"
41
+ private let errorEvent = "PLUGIN_BRIDGE_ERROR"
42
+ private let systemPluginId = "__bridge__"
43
+
44
+ init(webView: WKWebView, viewController: UIViewController) {
45
+ self.webView = webView
46
+ self.viewController = viewController
47
+ super.init()
48
+ }
49
+
50
+ func register() {
51
+ guard !isRegistered else { return }
52
+ guard let userContentController = webView?.configuration.userContentController else {
53
+ return
54
+ }
55
+ let proxy = WeakScriptMessageHandler(delegate: self)
56
+ userContentController.add(proxy, name: bridgeName)
57
+ messageHandlerProxy = proxy
58
+ isRegistered = true
59
+ }
60
+
61
+ func unregister() {
62
+ guard isRegistered else { return }
63
+ webView?.configuration.userContentController.removeScriptMessageHandler(forName: bridgeName)
64
+ messageHandlerProxy = nil
65
+ isRegistered = false
66
+ }
67
+
68
+ deinit {
69
+ unregister()
70
+ }
71
+
72
+ private func parseRequest(_ message: PluginBridgeMessage) throws -> PluginRequest {
73
+ guard message.name == bridgeName else {
74
+ throw PluginBridgeValidationError(message: "Invalid message handler", code: "INVALID_PAYLOAD")
75
+ }
76
+
77
+ let body: [String: Any]
78
+ if let dictionary = message.body as? [String: Any] {
79
+ body = dictionary
80
+ } else if let dictionary = message.body as? NSDictionary,
81
+ let castedBody = dictionary as? [String: Any] {
82
+ body = castedBody
83
+ } else {
84
+ throw PluginBridgeValidationError(message: "Payload must be an object", code: "INVALID_PAYLOAD")
85
+ }
86
+
87
+ guard JSONSerialization.isValidJSONObject(body) else {
88
+ throw PluginBridgeValidationError(message: "Payload must be JSON-serializable", code: "INVALID_PAYLOAD")
89
+ }
90
+
91
+ let payloadData: Data
92
+ do {
93
+ payloadData = try JSONSerialization.data(withJSONObject: body)
94
+ } catch {
95
+ throw PluginBridgeValidationError(message: "Invalid payload", code: "INVALID_PAYLOAD")
96
+ }
97
+
98
+ if payloadData.count > CatalystConstants.Bridge.maxMessageSize {
99
+ throw PluginBridgeValidationError(message: "Payload exceeds maximum size", code: "INVALID_PAYLOAD")
100
+ }
101
+
102
+ let pluginId = (body["pluginId"] as? String ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
103
+ let command = (body["command"] as? String ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
104
+ let requestId = (body["requestId"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
105
+
106
+ return PluginRequest(
107
+ pluginId: pluginId,
108
+ command: command,
109
+ data: body["data"],
110
+ requestId: requestId?.isEmpty == true ? nil : requestId
111
+ )
112
+ }
113
+
114
+ private func hasPlugin(_ pluginId: String) -> Bool {
115
+ pluginFactories[pluginId] != nil
116
+ }
117
+
118
+ private func hasCommand(pluginId: String, command: String) -> Bool {
119
+ pluginToCommands[pluginId]?.contains(command) ?? false
120
+ }
121
+
122
+ private func sendBridgeError(message: String, code: String, request: PluginRequest?) {
123
+ let bridge = PluginBridgeContext(
124
+ webView: webView,
125
+ viewController: viewController,
126
+ pluginId: systemPluginId,
127
+ command: request?.command,
128
+ requestId: request?.requestId
129
+ )
130
+ var payload: [String: Any] = [
131
+ "message": message,
132
+ "code": code,
133
+ "pluginId": request?.pluginId ?? systemPluginId,
134
+ ]
135
+ if let command = request?.command, !command.isEmpty {
136
+ payload["command"] = command
137
+ }
138
+ bridge.callback(eventName: errorEvent, data: payload)
139
+ }
140
+
141
+ func handleMessage(_ message: PluginBridgeMessage) {
142
+ var request: PluginRequest?
143
+
144
+ do {
145
+ let parsedRequest = try parseRequest(message)
146
+ request = parsedRequest
147
+
148
+ if parsedRequest.pluginId.isEmpty {
149
+ sendBridgeError(message: "pluginId is required", code: "INVALID_PAYLOAD", request: parsedRequest)
150
+ return
151
+ }
152
+ if parsedRequest.command.isEmpty {
153
+ sendBridgeError(message: "command is required", code: "INVALID_PAYLOAD", request: parsedRequest)
154
+ return
155
+ }
156
+
157
+ if !hasPlugin(parsedRequest.pluginId) {
158
+ sendBridgeError(
159
+ message: "Unsupported plugin: \(parsedRequest.pluginId)",
160
+ code: "PLUGIN_NOT_FOUND",
161
+ request: parsedRequest
162
+ )
163
+ return
164
+ }
165
+
166
+ if !hasCommand(pluginId: parsedRequest.pluginId, command: parsedRequest.command) {
167
+ sendBridgeError(
168
+ message: "Unsupported command '\(parsedRequest.command)' for plugin '\(parsedRequest.pluginId)'",
169
+ code: "COMMAND_NOT_SUPPORTED",
170
+ request: parsedRequest
171
+ )
172
+ return
173
+ }
174
+
175
+ guard let factory = pluginFactories[parsedRequest.pluginId] else {
176
+ sendBridgeError(
177
+ message: "No plugin registered for id: \(parsedRequest.pluginId)",
178
+ code: "PLUGIN_NOT_REGISTERED",
179
+ request: parsedRequest
180
+ )
181
+ return
182
+ }
183
+
184
+ let plugin = factory()
185
+ let bridge = PluginBridgeContext(
186
+ webView: webView,
187
+ viewController: viewController,
188
+ pluginId: parsedRequest.pluginId,
189
+ command: parsedRequest.command,
190
+ requestId: parsedRequest.requestId
191
+ )
192
+ plugin.handle(command: parsedRequest.command, data: parsedRequest.data, bridge: bridge)
193
+ } catch let error as PluginBridgeValidationError {
194
+ sendBridgeError(message: error.message, code: error.code, request: request)
195
+ } catch {
196
+ pluginBridgeLogger.error("Plugin command failed: \(error.localizedDescription)")
197
+ sendBridgeError(
198
+ message: "Plugin execution failed: \(error.localizedDescription)",
199
+ code: "PLUGIN_EXECUTION_FAILED",
200
+ request: request
201
+ )
202
+ }
203
+ }
204
+ }
205
+
206
+ extension PluginBridge: WKScriptMessageHandler {
207
+ func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
208
+ handleMessage(message)
209
+ }
210
+ }
211
+
212
+ final class PluginBridgeContext {
213
+ weak var webView: WKWebView?
214
+ weak var viewController: UIViewController?
215
+
216
+ private let systemPluginId = "__bridge__"
217
+ private let bridgeErrorEvent = "PLUGIN_BRIDGE_ERROR"
218
+
219
+ let pluginId: String
220
+ let command: String?
221
+ let requestId: String?
222
+
223
+ init(
224
+ webView: WKWebView?,
225
+ viewController: UIViewController?,
226
+ pluginId: String,
227
+ command: String?,
228
+ requestId: String?
229
+ ) {
230
+ self.webView = webView
231
+ self.viewController = viewController
232
+ self.pluginId = pluginId
233
+ self.command = command
234
+ self.requestId = requestId
235
+ }
236
+
237
+ func callback(
238
+ eventName: String,
239
+ data: Any?,
240
+ command: String? = nil
241
+ ) {
242
+ let resolvedRequestId = self.requestId
243
+ let resolvedCommand = command ?? self.command
244
+ let trimmedEventName = eventName.trimmingCharacters(in: .whitespacesAndNewlines)
245
+ guard !trimmedEventName.isEmpty else {
246
+ emitBridgeError(
247
+ message: "Rejected callback with blank event name for plugin \(self.pluginId)",
248
+ code: "INVALID_CALLBACK",
249
+ requestId: resolvedRequestId,
250
+ command: resolvedCommand
251
+ )
252
+ return
253
+ }
254
+ if !dispatchEnvelope(
255
+ pluginId: pluginId,
256
+ eventName: trimmedEventName,
257
+ payload: data,
258
+ requestId: resolvedRequestId,
259
+ command: resolvedCommand
260
+ ) {
261
+ if pluginId == systemPluginId && trimmedEventName == bridgeErrorEvent {
262
+ pluginBridgeLogger.error("Failed to dispatch bridge error event for plugin \(self.pluginId)")
263
+ return
264
+ }
265
+
266
+ emitBridgeError(
267
+ message: "Failed to dispatch callback \(trimmedEventName) for plugin \(self.pluginId)",
268
+ code: "PLUGIN_EXECUTION_FAILED",
269
+ requestId: resolvedRequestId,
270
+ command: resolvedCommand
271
+ )
272
+ }
273
+ }
274
+
275
+ private func emitBridgeError(
276
+ message: String,
277
+ code: String,
278
+ requestId: String?,
279
+ command: String?
280
+ ) {
281
+ var payload: [String: Any] = [
282
+ "message": message,
283
+ "code": code,
284
+ "pluginId": pluginId,
285
+ ]
286
+
287
+ if let command = command, !command.isEmpty {
288
+ payload["command"] = command
289
+ }
290
+
291
+ if !dispatchEnvelope(
292
+ pluginId: systemPluginId,
293
+ eventName: bridgeErrorEvent,
294
+ payload: payload,
295
+ requestId: requestId,
296
+ command: command,
297
+ logFailures: false
298
+ ) {
299
+ pluginBridgeLogger.error("Failed to dispatch bridge error for plugin \(self.pluginId): \(message)")
300
+ }
301
+ }
302
+
303
+ private func dispatchEnvelope(
304
+ pluginId: String,
305
+ eventName: String,
306
+ payload: Any?,
307
+ requestId: String?,
308
+ command: String?,
309
+ logFailures: Bool = true
310
+ ) -> Bool {
311
+ guard let webView = webView else {
312
+ if logFailures {
313
+ pluginBridgeLogger.error("WebView unavailable for plugin callback \(eventName)")
314
+ }
315
+ return false
316
+ }
317
+
318
+ let envelope: [String: Any] = [
319
+ "pluginId": pluginId,
320
+ "eventName": eventName,
321
+ "payload": payload ?? NSNull(),
322
+ "requestId": requestId ?? NSNull(),
323
+ "command": command ?? NSNull(),
324
+ ]
325
+
326
+ guard JSONSerialization.isValidJSONObject(envelope),
327
+ let envelopeData = try? JSONSerialization.data(withJSONObject: envelope),
328
+ let envelopeJson = String(data: envelopeData, encoding: .utf8) else {
329
+ if logFailures {
330
+ pluginBridgeLogger.error("Failed to serialize plugin callback envelope for \(pluginId).\(eventName)")
331
+ }
332
+ return false
333
+ }
334
+
335
+ let envelopeLiteral = javaScriptStringLiteral(envelopeJson)
336
+ let script = "window.PluginBridgeWeb && window.PluginBridgeWeb.dispatch(\(envelopeLiteral));"
337
+
338
+ DispatchQueue.main.async {
339
+ webView.evaluateJavaScript(
340
+ script,
341
+ completionHandler: { _, error in
342
+ if let error = error {
343
+ pluginBridgeLogger.error(
344
+ "Plugin callback JS failed for \(pluginId).\(eventName): \(error.localizedDescription)"
345
+ )
346
+ }
347
+ }
348
+ )
349
+ }
350
+
351
+ return true
352
+ }
353
+
354
+ private func javaScriptStringLiteral(_ value: String) -> String {
355
+ let escaped = value
356
+ .replacingOccurrences(of: "\\", with: "\\\\")
357
+ .replacingOccurrences(of: "\"", with: "\\\"")
358
+ .replacingOccurrences(of: "\n", with: "\\n")
359
+ .replacingOccurrences(of: "\r", with: "\\r")
360
+ .replacingOccurrences(of: "\u{2028}", with: "\\u2028")
361
+ .replacingOccurrences(of: "\u{2029}", with: "\\u2029")
362
+ return "\"\(escaped)\""
363
+ }
364
+ }
@@ -265,9 +265,20 @@ public final class CacheManager {
265
265
  }
266
266
 
267
267
  func getCacheStatistics() -> (memoryUsed: Int, diskUsed: Int) {
268
- // URLCache methods are thread-safe, no need for sync queue
269
268
  let memoryUsed = session.configuration.urlCache?.currentMemoryUsage ?? 0
270
- let diskUsed = session.configuration.urlCache?.currentDiskUsage ?? 0
269
+
270
+ // Sum size of all custom .cache files written to disk (separate from URLCache internals)
271
+ var diskUsed = 0
272
+ if let files = try? FileManager.default.contentsOfDirectory(
273
+ at: cacheDirectory,
274
+ includingPropertiesForKeys: [.fileSizeKey]
275
+ ) {
276
+ diskUsed = files.reduce(0) { total, url in
277
+ let size = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? 0
278
+ return total + size
279
+ }
280
+ }
281
+
271
282
  return (memoryUsed, diskUsed)
272
283
  }
273
284
  }
@@ -17,6 +17,8 @@ class NativeBridge: NSObject, BridgeCommandHandlerDelegate, BridgeFileHandlerDel
17
17
  weak var webView: WKWebView?
18
18
  private weak var viewController: UIViewController?
19
19
  private weak var webViewModel: WebViewModel?
20
+ private var messageHandlerProxy: WeakScriptMessageHandler?
21
+ private var isRegistered = false
20
22
 
21
23
  // Protocol-based notification handler (injected at runtime)
22
24
  private var notificationHandler: NotificationHandlerProtocol = NullNotificationHandler.shared
@@ -133,10 +135,16 @@ class NativeBridge: NSObject, BridgeCommandHandlerDelegate, BridgeFileHandlerDel
133
135
 
134
136
  // Register the JavaScript interface with the WebView
135
137
  func register() {
138
+ guard !isRegistered else { return }
136
139
  let registerStart = CFAbsoluteTimeGetCurrent()
137
140
 
138
- let userContentController = webView?.configuration.userContentController
139
- userContentController?.add(self, name: "NativeBridge")
141
+ guard let userContentController = webView?.configuration.userContentController else {
142
+ return
143
+ }
144
+ let proxy = WeakScriptMessageHandler(delegate: self)
145
+ userContentController.add(proxy, name: "NativeBridge")
146
+ messageHandlerProxy = proxy
147
+ isRegistered = true
140
148
 
141
149
  let registerTime = (CFAbsoluteTimeGetCurrent() - registerStart) * 1000
142
150
  logWithTimestamp("✅ NativeBridge registered (took \(String(format: "%.2f", registerTime))ms)")
@@ -144,7 +152,10 @@ class NativeBridge: NSObject, BridgeCommandHandlerDelegate, BridgeFileHandlerDel
144
152
 
145
153
  // Unregister to prevent memory leaks
146
154
  func unregister() {
155
+ guard isRegistered else { return }
147
156
  webView?.configuration.userContentController.removeScriptMessageHandler(forName: "NativeBridge")
157
+ messageHandlerProxy = nil
158
+ isRegistered = false
148
159
 
149
160
  if let listenerId = networkStatusListenerId {
150
161
  NetworkMonitor.shared.removeListener(listenerId)
@@ -0,0 +1,14 @@
1
+ import WebKit
2
+
3
+ final class WeakScriptMessageHandler: NSObject, WKScriptMessageHandler {
4
+ weak var delegate: WKScriptMessageHandler?
5
+
6
+ init(delegate: WKScriptMessageHandler) {
7
+ self.delegate = delegate
8
+ super.init()
9
+ }
10
+
11
+ func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
12
+ delegate?.userContentController(userContentController, didReceive: message)
13
+ }
14
+ }
@@ -121,6 +121,8 @@ public struct WebView: UIViewRepresentable, Equatable {
121
121
  // Clean up native bridge
122
122
  coordinator.nativeBridge?.unregister()
123
123
  coordinator.nativeBridge = nil
124
+ coordinator.pluginBridge?.unregister()
125
+ coordinator.pluginBridge = nil
124
126
  coordinator.hostingController = nil
125
127
 
126
128
  // Unregister custom URL protocol
@@ -132,6 +134,7 @@ public struct WebView: UIViewRepresentable, Equatable {
132
134
  public class Coordinator: NSObject {
133
135
  var parent: WebView
134
136
  var nativeBridge: NativeBridge?
137
+ var pluginBridge: PluginBridge?
135
138
  var hostingController: UIViewController?
136
139
  var isObserverAdded = false
137
140
 
@@ -146,6 +149,7 @@ public struct WebView: UIViewRepresentable, Equatable {
146
149
 
147
150
  // Create and register the native bridge
148
151
  let bridge = NativeBridge(webView: webView, viewController: hostingController)
152
+ let pluginBridge = PluginBridge(webView: webView, viewController: hostingController)
149
153
 
150
154
  // Inject WebViewModel for safe area handling
151
155
  Task { @MainActor in
@@ -156,7 +160,9 @@ public struct WebView: UIViewRepresentable, Equatable {
156
160
  bridge.setNotificationHandler(NotificationHandlerProvider.shared)
157
161
 
158
162
  bridge.register()
163
+ pluginBridge.register()
159
164
  self.nativeBridge = bridge
165
+ self.pluginBridge = pluginBridge
160
166
  }
161
167
 
162
168
  override public func observeValue(forKeyPath keyPath: String?,
@@ -23,6 +23,7 @@
23
23
  E9F699E02EE065B0005E972E /* NotificationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F699DF2EE065B0005E972E /* NotificationHandlerTests.swift */; };
24
24
  E9F699E22EE06650005E972E /* FrameworkServerUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F699E12EE06650005E972E /* FrameworkServerUtilsTests.swift */; };
25
25
  E9F699E42EE0696A005E972E /* BootTimingUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F699E32EE0696A005E972E /* BootTimingUtilityTests.swift */; };
26
+ F2B100012F11111100AAA001 /* PluginBridgeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2B100022F11111100AAA001 /* PluginBridgeTests.swift */; };
26
27
  F1234AC22E990180008C7F58 /* localhost.p12 in Resources */ = {isa = PBXBuildFile; fileRef = F1234AC32E990180008C7F58 /* localhost.p12 */; };
27
28
  /* End PBXBuildFile section */
28
29
 
@@ -66,6 +67,7 @@
66
67
  E9F699DF2EE065B0005E972E /* NotificationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerTests.swift; sourceTree = "<group>"; };
67
68
  E9F699E12EE06650005E972E /* FrameworkServerUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameworkServerUtilsTests.swift; sourceTree = "<group>"; };
68
69
  E9F699E32EE0696A005E972E /* BootTimingUtilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BootTimingUtilityTests.swift; sourceTree = "<group>"; };
70
+ F2B100022F11111100AAA001 /* PluginBridgeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginBridgeTests.swift; sourceTree = "<group>"; };
69
71
  F1234AC32E990180008C7F58 /* localhost.p12 */ = {isa = PBXFileReference; lastKnownFileType = file; path = localhost.p12; sourceTree = "<group>"; };
70
72
  XCCONFIG001000000001 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
71
73
  XCCONFIG001000000002 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
@@ -152,6 +154,7 @@
152
154
  E9F699DB2EE06482005E972E /* CacheManagerTests.swift */,
153
155
  E9F699D92EE063D5005E972E /* WebViewTests.swift */,
154
156
  E9F699D72EE06068005E972E /* NativeBridgeTests.swift */,
157
+ F2B100022F11111100AAA001 /* PluginBridgeTests.swift */,
155
158
  E9F699D52EE05D74005E972E /* BridgeMessageValidatorTests.swift */,
156
159
  E9F699D32EE05984005E972E /* ConfigMappingTests.swift */,
157
160
  E9F699D02EDEFEF2005E972E /* URLWhitelistManagerTests.swift */,
@@ -321,6 +324,7 @@
321
324
  E9F699DC2EE06483005E972E /* CacheManagerTests.swift in Sources */,
322
325
  E9F699D42EE05984005E972E /* ConfigMappingTests.swift in Sources */,
323
326
  E9F699D82EE06068005E972E /* NativeBridgeTests.swift in Sources */,
327
+ F2B100012F11111100AAA001 /* PluginBridgeTests.swift in Sources */,
324
328
  E9F699DA2EE063D5005E972E /* WebViewTests.swift in Sources */,
325
329
  E9F699E42EE0696A005E972E /* BootTimingUtilityTests.swift in Sources */,
326
330
  E9F699E22EE06650005E972E /* FrameworkServerUtilsTests.swift in Sources */,
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "originHash" : "3d4b03cf830e06eaa66e0727fd91253b9df9d2675cbe24eb099df0d4bb4d1f01",
3
3
  "pins" : [
4
+ {
5
+ "identity" : "appauth-ios",
6
+ "kind" : "remoteSourceControl",
7
+ "location" : "https://github.com/openid/AppAuth-iOS.git",
8
+ "state" : {
9
+ "revision" : "2781038865a80e2c425a1da12cc1327bcd56501f",
10
+ "version" : "1.7.6"
11
+ }
12
+ },
13
+ {
14
+ "identity" : "googlesignin-ios",
15
+ "kind" : "remoteSourceControl",
16
+ "location" : "https://github.com/google/GoogleSignIn-iOS",
17
+ "state" : {
18
+ "revision" : "a7965d134c5d3567026c523e0a8a583f73b62b0d",
19
+ "version" : "7.1.0"
20
+ }
21
+ },
22
+ {
23
+ "identity" : "gtm-session-fetcher",
24
+ "kind" : "remoteSourceControl",
25
+ "location" : "https://github.com/google/gtm-session-fetcher.git",
26
+ "state" : {
27
+ "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b",
28
+ "version" : "3.5.0"
29
+ }
30
+ },
31
+ {
32
+ "identity" : "gtmappauth",
33
+ "kind" : "remoteSourceControl",
34
+ "location" : "https://github.com/google/GTMAppAuth.git",
35
+ "state" : {
36
+ "revision" : "5d7d66f647400952b1758b230e019b07c0b4b22a",
37
+ "version" : "4.1.1"
38
+ }
39
+ },
4
40
  {
5
41
  "identity" : "jsonschema.swift",
6
42
  "kind" : "remoteSourceControl",
@@ -9,6 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "defaultOptions" : {
12
+ "codeCoverage" : true,
12
13
  "commandLineArgumentEntries" : [
13
14
  {
14
15
  "argument" : "-OSLogEnable"