@thelacanians/vue-native-cli 0.4.15 → 0.6.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.
Files changed (116) hide show
  1. package/dist/cli.js +329 -15
  2. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +118 -0
  3. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
  6. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
  7. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
  8. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
  9. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
  10. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
  11. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
  12. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
  13. package/native/android/gradle.properties +1 -0
  14. package/native/android/gradlew +1 -1
  15. package/native/ios/VueNativeCore/Package.swift +1 -1
  16. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +1 -0
  17. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +143 -5
  18. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
  19. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
  20. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
  21. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
  22. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
  23. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
  24. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
  25. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
  26. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
  27. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
  28. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
  29. package/native/macos/VueNativeMacOS/Package.swift +34 -0
  30. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
  31. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
  32. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
  33. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
  34. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
  35. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
  36. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
  37. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
  38. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
  39. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
  40. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
  41. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
  42. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
  43. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
  44. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
  45. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
  46. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
  47. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
  48. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
  49. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
  50. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
  51. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
  52. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
  53. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
  54. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
  55. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
  56. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
  57. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
  58. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
  59. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
  60. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
  61. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
  62. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
  63. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
  64. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
  65. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
  66. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
  67. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
  68. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
  69. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
  70. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
  71. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
  72. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
  73. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
  74. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
  75. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
  76. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
  77. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
  78. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
  79. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
  80. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
  81. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
  82. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
  83. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
  84. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
  85. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
  86. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
  87. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
  88. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
  89. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
  90. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
  91. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
  92. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
  93. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
  94. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
  95. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
  96. package/native/shared/VueNativeShared/AGENTS.md +129 -0
  97. package/native/shared/VueNativeShared/Package.swift +14 -0
  98. package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
  99. package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
  100. package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
  101. package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
  102. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
  103. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
  104. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
  105. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
  106. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
  107. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
  108. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
  109. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
  110. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
  111. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
  112. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
  113. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
  114. package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
  115. package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
  116. package/package.json +8 -2
@@ -0,0 +1,233 @@
1
+ import Foundation
2
+
3
+ /// Native module providing file system access.
4
+ ///
5
+ /// Methods:
6
+ /// - readFile(path: String, encoding: String?) -> String
7
+ /// - writeFile(path: String, content: String, encoding: String?)
8
+ /// - deleteFile(path: String)
9
+ /// - exists(path: String) -> Bool
10
+ /// - listDirectory(path: String) -> [String]
11
+ /// - downloadFile(url: String, destPath: String) -> String
12
+ /// - getDocumentsPath() -> String
13
+ /// - getCachesPath() -> String
14
+ /// - stat(path: String) -> { size, isDirectory, modified }
15
+ /// - mkdir(path: String)
16
+ /// - copyFile(srcPath: String, destPath: String)
17
+ /// - moveFile(srcPath: String, destPath: String)
18
+ public final class FileSystemModule: NativeModule {
19
+ public let moduleName = "FileSystem"
20
+
21
+ private let fileManager = FileManager.default
22
+
23
+ public init() {}
24
+
25
+ public func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
26
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
27
+ guard let self = self else { return }
28
+ switch method {
29
+ case "readFile":
30
+ guard let path = args.first as? String else {
31
+ callback(nil, "readFile: missing path")
32
+ return
33
+ }
34
+ let encoding = (args.count > 1 ? args[1] as? String : nil) ?? "utf8"
35
+ guard self.fileManager.fileExists(atPath: path) else {
36
+ callback(nil, "readFile: file not found at \(path)")
37
+ return
38
+ }
39
+ guard let data = self.fileManager.contents(atPath: path) else {
40
+ callback(nil, "readFile: could not read file at \(path)")
41
+ return
42
+ }
43
+ if encoding == "base64" {
44
+ callback(data.base64EncodedString(), nil)
45
+ } else {
46
+ guard let text = String(data: data, encoding: .utf8) else {
47
+ callback(nil, "readFile: file is not valid UTF-8")
48
+ return
49
+ }
50
+ callback(text, nil)
51
+ }
52
+
53
+ case "writeFile":
54
+ guard args.count >= 2,
55
+ let path = args[0] as? String,
56
+ let content = args[1] as? String else {
57
+ callback(nil, "writeFile: missing path or content")
58
+ return
59
+ }
60
+ let encoding = (args.count > 2 ? args[2] as? String : nil) ?? "utf8"
61
+ let data: Data?
62
+ if encoding == "base64" {
63
+ data = Data(base64Encoded: content)
64
+ } else {
65
+ data = content.data(using: .utf8)
66
+ }
67
+ guard let fileData = data else {
68
+ callback(nil, "writeFile: could not encode content")
69
+ return
70
+ }
71
+ // Create parent directory if needed
72
+ let dir = (path as NSString).deletingLastPathComponent
73
+ if !self.fileManager.fileExists(atPath: dir) {
74
+ do {
75
+ try self.fileManager.createDirectory(atPath: dir, withIntermediateDirectories: true)
76
+ } catch {
77
+ callback(nil, "writeFile: could not create directory: \(error.localizedDescription)")
78
+ return
79
+ }
80
+ }
81
+ self.fileManager.createFile(atPath: path, contents: fileData)
82
+ callback(nil, nil)
83
+
84
+ case "deleteFile":
85
+ guard let path = args.first as? String else {
86
+ callback(nil, "deleteFile: missing path")
87
+ return
88
+ }
89
+ guard self.fileManager.fileExists(atPath: path) else {
90
+ callback(nil, "deleteFile: file not found at \(path)")
91
+ return
92
+ }
93
+ do {
94
+ try self.fileManager.removeItem(atPath: path)
95
+ callback(nil, nil)
96
+ } catch {
97
+ callback(nil, "deleteFile: \(error.localizedDescription)")
98
+ }
99
+
100
+ case "exists":
101
+ guard let path = args.first as? String else {
102
+ callback(nil, "exists: missing path")
103
+ return
104
+ }
105
+ callback(self.fileManager.fileExists(atPath: path), nil)
106
+
107
+ case "listDirectory":
108
+ guard let path = args.first as? String else {
109
+ callback(nil, "listDirectory: missing path")
110
+ return
111
+ }
112
+ do {
113
+ let contents = try self.fileManager.contentsOfDirectory(atPath: path)
114
+ callback(contents, nil)
115
+ } catch {
116
+ callback(nil, "listDirectory: \(error.localizedDescription)")
117
+ }
118
+
119
+ case "downloadFile":
120
+ guard args.count >= 2,
121
+ let urlString = args[0] as? String,
122
+ let destPath = args[1] as? String else {
123
+ callback(nil, "downloadFile: missing url or destPath")
124
+ return
125
+ }
126
+ guard let url = URL(string: urlString) else {
127
+ callback(nil, "downloadFile: invalid URL")
128
+ return
129
+ }
130
+ let task = URLSession.shared.dataTask(with: url) { data, response, error in
131
+ if let error = error {
132
+ callback(nil, "downloadFile: \(error.localizedDescription)")
133
+ return
134
+ }
135
+ guard let data = data else {
136
+ callback(nil, "downloadFile: no data received")
137
+ return
138
+ }
139
+ // Create parent directory if needed
140
+ let dir = (destPath as NSString).deletingLastPathComponent
141
+ if !self.fileManager.fileExists(atPath: dir) {
142
+ do {
143
+ try self.fileManager.createDirectory(atPath: dir, withIntermediateDirectories: true)
144
+ } catch {
145
+ callback(nil, "downloadFile: could not create directory: \(error.localizedDescription)")
146
+ return
147
+ }
148
+ }
149
+ self.fileManager.createFile(atPath: destPath, contents: data)
150
+ callback(destPath, nil)
151
+ }
152
+ task.resume()
153
+
154
+ case "getDocumentsPath":
155
+ let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
156
+ callback(paths.first, nil)
157
+
158
+ case "getCachesPath":
159
+ let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
160
+ callback(paths.first, nil)
161
+
162
+ case "stat":
163
+ guard let path = args.first as? String else {
164
+ callback(nil, "stat: missing path")
165
+ return
166
+ }
167
+ do {
168
+ let attrs = try self.fileManager.attributesOfItem(atPath: path)
169
+ let size = (attrs[.size] as? Int) ?? 0
170
+ let isDir = (attrs[.type] as? FileAttributeType) == .typeDirectory
171
+ let modified = (attrs[.modificationDate] as? Date)?.timeIntervalSince1970 ?? 0
172
+ callback([
173
+ "size": size,
174
+ "isDirectory": isDir,
175
+ "modified": modified * 1000 // milliseconds for JS
176
+ ] as [String: Any], nil)
177
+ } catch {
178
+ callback(nil, "stat: \(error.localizedDescription)")
179
+ }
180
+
181
+ case "mkdir":
182
+ guard let path = args.first as? String else {
183
+ callback(nil, "mkdir: missing path")
184
+ return
185
+ }
186
+ do {
187
+ try self.fileManager.createDirectory(atPath: path, withIntermediateDirectories: true)
188
+ callback(nil, nil)
189
+ } catch {
190
+ callback(nil, "mkdir: \(error.localizedDescription)")
191
+ }
192
+
193
+ case "copyFile":
194
+ guard args.count >= 2,
195
+ let srcPath = args[0] as? String,
196
+ let destPath = args[1] as? String else {
197
+ callback(nil, "copyFile: missing srcPath or destPath")
198
+ return
199
+ }
200
+ do {
201
+ // Remove destination if it exists (copyItem throws if dest exists)
202
+ if self.fileManager.fileExists(atPath: destPath) {
203
+ try self.fileManager.removeItem(atPath: destPath)
204
+ }
205
+ try self.fileManager.copyItem(atPath: srcPath, toPath: destPath)
206
+ callback(nil, nil)
207
+ } catch {
208
+ callback(nil, "copyFile: \(error.localizedDescription)")
209
+ }
210
+
211
+ case "moveFile":
212
+ guard args.count >= 2,
213
+ let srcPath = args[0] as? String,
214
+ let destPath = args[1] as? String else {
215
+ callback(nil, "moveFile: missing srcPath or destPath")
216
+ return
217
+ }
218
+ do {
219
+ if self.fileManager.fileExists(atPath: destPath) {
220
+ try self.fileManager.removeItem(atPath: destPath)
221
+ }
222
+ try self.fileManager.moveItem(atPath: srcPath, toPath: destPath)
223
+ callback(nil, nil)
224
+ } catch {
225
+ callback(nil, "moveFile: \(error.localizedDescription)")
226
+ }
227
+
228
+ default:
229
+ callback(nil, "FileSystemModule: Unknown method '\(method)'")
230
+ }
231
+ }
232
+ }
233
+ }
@@ -0,0 +1,156 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ /// Native module for GPS/location access.
5
+ /// Uses CoreLocation which is available on both iOS and macOS.
6
+ ///
7
+ /// Methods:
8
+ /// - getCurrentPosition() -> location payload
9
+ /// - watchPosition() -> watchId (Int); fires "location:update" global events
10
+ /// - clearWatch(watchId: Int)
11
+ ///
12
+ /// Location payload keys: latitude, longitude, altitude, accuracy,
13
+ /// altitudeAccuracy, heading, speed, timestamp
14
+ public final class GeolocationModule: NativeModule {
15
+ public var moduleName: String { "Geolocation" }
16
+ private weak var eventDispatcher: NativeEventDispatcher?
17
+
18
+ public init(eventDispatcher: NativeEventDispatcher) {
19
+ self.eventDispatcher = eventDispatcher
20
+ }
21
+
22
+ public func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
23
+ switch method {
24
+ case "getCurrentPosition":
25
+ DispatchQueue.main.async {
26
+ GeolocationManager.shared.getCurrentPosition(callback: callback)
27
+ }
28
+ case "watchPosition":
29
+ let weakDispatcher = eventDispatcher
30
+ DispatchQueue.main.async {
31
+ let watchId = GeolocationManager.shared.watchPosition(eventDispatcher: weakDispatcher)
32
+ callback(watchId, nil)
33
+ }
34
+ case "clearWatch":
35
+ guard let watchId = args.first.flatMap({ $0 as? Int }) else {
36
+ callback(nil, nil); return
37
+ }
38
+ DispatchQueue.main.async {
39
+ GeolocationManager.shared.clearWatch(watchId)
40
+ callback(nil, nil)
41
+ }
42
+ default:
43
+ callback(nil, "GeolocationModule: Unknown method '\(method)'")
44
+ }
45
+ }
46
+
47
+ public func invokeSync(method: String, args: [Any]) -> Any? { nil }
48
+ }
49
+
50
+ // MARK: - Internal location manager
51
+
52
+ /// All CLLocationManager interactions happen on @MainActor (main thread),
53
+ /// matching the CoreLocation threading requirement.
54
+ @MainActor
55
+ private final class GeolocationManager: NSObject, CLLocationManagerDelegate {
56
+ static let shared = GeolocationManager()
57
+
58
+ private var manager: CLLocationManager?
59
+ private var pendingCallbacks: [(Any?, String?) -> Void] = []
60
+
61
+ // Map watchId -> weak event dispatcher reference
62
+ private struct WeakDispatcher { weak var dispatcher: NativeEventDispatcher? }
63
+ private var watchCallbacks: [Int: WeakDispatcher] = [:]
64
+ private var nextWatchId = 1
65
+
66
+ // MARK: Setup
67
+
68
+ private func ensureManager() {
69
+ guard manager == nil else { return }
70
+ let mgr = CLLocationManager()
71
+ mgr.delegate = self
72
+ mgr.desiredAccuracy = kCLLocationAccuracyBest
73
+ manager = mgr
74
+ }
75
+
76
+ // MARK: Public interface (called on main thread)
77
+
78
+ private func isAuthorized(_ status: CLAuthorizationStatus) -> Bool {
79
+ if status == .authorizedAlways { return true }
80
+ #if os(iOS)
81
+ if status == .authorizedWhenInUse { return true }
82
+ #endif
83
+ // On macOS, .authorized maps to .authorizedAlways — already covered above
84
+ return false
85
+ }
86
+
87
+ func getCurrentPosition(callback: @escaping (Any?, String?) -> Void) {
88
+ ensureManager()
89
+ guard let manager = manager else {
90
+ callback(nil, "Failed to initialize location manager"); return
91
+ }
92
+ let status = manager.authorizationStatus
93
+ guard isAuthorized(status) else {
94
+ callback(nil, "Location permission not granted"); return
95
+ }
96
+ pendingCallbacks.append(callback)
97
+ manager.requestLocation()
98
+ }
99
+
100
+ func watchPosition(eventDispatcher: NativeEventDispatcher?) -> Int {
101
+ ensureManager()
102
+ guard let manager = manager else {
103
+ return -1
104
+ }
105
+ let status = manager.authorizationStatus
106
+ guard isAuthorized(status) else {
107
+ return -1
108
+ }
109
+ let watchId = nextWatchId; nextWatchId += 1
110
+ watchCallbacks[watchId] = WeakDispatcher(dispatcher: eventDispatcher)
111
+ manager.startUpdatingLocation()
112
+ return watchId
113
+ }
114
+
115
+ func clearWatch(_ watchId: Int) {
116
+ watchCallbacks.removeValue(forKey: watchId)
117
+ if watchCallbacks.isEmpty {
118
+ manager?.stopUpdatingLocation()
119
+ }
120
+ }
121
+
122
+ // MARK: CLLocationManagerDelegate
123
+ // nonisolated required by protocol; we hop to @MainActor via Task.
124
+
125
+ nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
126
+ guard let loc = locations.last else { return }
127
+ let payload: [String: Any] = [
128
+ "latitude": loc.coordinate.latitude,
129
+ "longitude": loc.coordinate.longitude,
130
+ "altitude": loc.altitude,
131
+ "accuracy": loc.horizontalAccuracy,
132
+ "altitudeAccuracy": loc.verticalAccuracy,
133
+ "heading": loc.course,
134
+ "speed": loc.speed,
135
+ "timestamp": loc.timestamp.timeIntervalSince1970 * 1000
136
+ ]
137
+ Task { @MainActor in
138
+ // Fire one-shot callbacks
139
+ let cbs = self.pendingCallbacks
140
+ self.pendingCallbacks.removeAll()
141
+ for cb in cbs { cb(payload, nil) }
142
+ // Fire watch callbacks
143
+ for (_, wd) in self.watchCallbacks {
144
+ wd.dispatcher?.dispatchGlobalEvent("location:update", payload: payload)
145
+ }
146
+ }
147
+ }
148
+
149
+ nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
150
+ Task { @MainActor in
151
+ let cbs = self.pendingCallbacks
152
+ self.pendingCallbacks.removeAll()
153
+ for cb in cbs { cb(nil, error.localizedDescription) }
154
+ }
155
+ }
156
+ }
@@ -0,0 +1,59 @@
1
+ import Network
2
+ import Foundation
3
+
4
+ /// Monitors network connectivity and pushes changes to JS via global events.
5
+ /// Uses NativeEventDispatcher protocol instead of a concrete bridge type.
6
+ public final class NetworkModule: NativeModule {
7
+ public var moduleName: String { "Network" }
8
+
9
+ private let monitor = NWPathMonitor()
10
+ private let monitorQueue = DispatchQueue(label: "vue-native.network")
11
+ private weak var eventDispatcher: NativeEventDispatcher?
12
+
13
+ public init(eventDispatcher: NativeEventDispatcher) {
14
+ self.eventDispatcher = eventDispatcher
15
+ startMonitoring()
16
+ }
17
+
18
+ private func startMonitoring() {
19
+ monitor.pathUpdateHandler = { [weak self] path in
20
+ let isConnected = path.status == .satisfied
21
+ let connectionType: String
22
+ if path.usesInterfaceType(.wifi) {
23
+ connectionType = "wifi"
24
+ } else if path.usesInterfaceType(.cellular) {
25
+ connectionType = "cellular"
26
+ } else if path.usesInterfaceType(.wiredEthernet) {
27
+ connectionType = "ethernet"
28
+ } else {
29
+ connectionType = "none"
30
+ }
31
+ let dispatcher = self?.eventDispatcher
32
+ DispatchQueue.main.async {
33
+ dispatcher?.dispatchGlobalEvent("network:change", payload: [
34
+ "isConnected": isConnected,
35
+ "connectionType": connectionType
36
+ ])
37
+ }
38
+ }
39
+ monitor.start(queue: monitorQueue)
40
+ }
41
+
42
+ public func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
43
+ switch method {
44
+ case "getStatus":
45
+ let path = monitor.currentPath
46
+ let isConnected = path.status == .satisfied
47
+ let connectionType: String
48
+ if path.usesInterfaceType(.wifi) { connectionType = "wifi" }
49
+ else if path.usesInterfaceType(.cellular) { connectionType = "cellular" }
50
+ else if path.usesInterfaceType(.wiredEthernet) { connectionType = "ethernet" }
51
+ else { connectionType = "none" }
52
+ callback(["isConnected": isConnected, "connectionType": connectionType], nil)
53
+ default:
54
+ callback(nil, "Unknown method: \(method)")
55
+ }
56
+ }
57
+
58
+ public func invokeSync(method: String, args: [Any]) -> Any? { nil }
59
+ }
@@ -0,0 +1,113 @@
1
+ import Foundation
2
+
3
+ /// Native module for performance profiling.
4
+ /// Tracks memory usage via task_info and bridge operation counts.
5
+ /// Dispatches `perf:metrics` global events every 1 second while profiling is active.
6
+ ///
7
+ /// NOTE: FPS tracking is platform-specific (requires CADisplayLink on iOS,
8
+ /// CVDisplayLink or CADisplayLink on macOS). This shared module reports FPS as 0
9
+ /// unless a platform-specific subclass or wrapper provides it.
10
+ /// Platforms can set `fpsProvider` to supply real-time FPS data.
11
+ public final class PerformanceModule: NSObject, NativeModule {
12
+
13
+ public let moduleName = "Performance"
14
+
15
+ private weak var eventDispatcher: NativeEventDispatcher?
16
+ private var isProfiling = false
17
+
18
+ // Metrics timer
19
+ private var metricsTimer: Timer?
20
+
21
+ // Bridge operation count
22
+ private var bridgeOpsCount: Int = 0
23
+
24
+ /// Optional FPS provider. Platform-specific code sets this to a closure
25
+ /// that returns the current FPS reading (e.g., from CADisplayLink).
26
+ public var fpsProvider: (() -> Double)?
27
+
28
+ public init(eventDispatcher: NativeEventDispatcher) {
29
+ self.eventDispatcher = eventDispatcher
30
+ super.init()
31
+ }
32
+
33
+ public func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
34
+ switch method {
35
+ case "startProfiling":
36
+ startProfiling(callback: callback)
37
+ case "stopProfiling":
38
+ stopProfiling(callback: callback)
39
+ case "getMetrics":
40
+ let metrics = collectMetrics()
41
+ callback(metrics, nil)
42
+ default:
43
+ callback(nil, "PerformanceModule: unknown method '\(method)'")
44
+ }
45
+ }
46
+
47
+ // MARK: - Start / Stop
48
+
49
+ private func startProfiling(callback: @escaping (Any?, String?) -> Void) {
50
+ guard !isProfiling else { callback(true, nil); return }
51
+ isProfiling = true
52
+ bridgeOpsCount = 0
53
+
54
+ DispatchQueue.main.async { [weak self] in
55
+ guard let self = self else { return }
56
+
57
+ // Timer for periodic metrics dispatch (every 1 second)
58
+ self.metricsTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
59
+ self?.dispatchMetrics()
60
+ }
61
+
62
+ callback(true, nil)
63
+ }
64
+ }
65
+
66
+ private func stopProfiling(callback: @escaping (Any?, String?) -> Void) {
67
+ guard isProfiling else { callback(true, nil); return }
68
+ isProfiling = false
69
+
70
+ DispatchQueue.main.async { [weak self] in
71
+ self?.metricsTimer?.invalidate()
72
+ self?.metricsTimer = nil
73
+ callback(true, nil)
74
+ }
75
+ }
76
+
77
+ // MARK: - Metrics collection
78
+
79
+ private func collectMetrics() -> [String: Any] {
80
+ let fps = fpsProvider?() ?? 0
81
+ return [
82
+ "fps": round(fps * 10) / 10,
83
+ "memoryMB": getMemoryUsageMB(),
84
+ "bridgeOps": bridgeOpsCount,
85
+ "timestamp": Date().timeIntervalSince1970 * 1000,
86
+ ]
87
+ }
88
+
89
+ private func dispatchMetrics() {
90
+ guard isProfiling else { return }
91
+ bridgeOpsCount += 1 // Count the metrics dispatch itself
92
+ let metrics = collectMetrics()
93
+ DispatchQueue.main.async { [weak self] in
94
+ self?.eventDispatcher?.dispatchGlobalEvent("perf:metrics", payload: metrics)
95
+ }
96
+ }
97
+
98
+ // MARK: - Memory measurement
99
+
100
+ private func getMemoryUsageMB() -> Double {
101
+ var info = mach_task_basic_info()
102
+ var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size) / 4
103
+ let result = withUnsafeMutablePointer(to: &info) {
104
+ $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) {
105
+ task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
106
+ }
107
+ }
108
+ if result == KERN_SUCCESS {
109
+ return Double(info.resident_size) / (1024 * 1024)
110
+ }
111
+ return 0
112
+ }
113
+ }
@@ -0,0 +1,119 @@
1
+ import Foundation
2
+ import Security
3
+
4
+ /// Native module providing secure key-value storage backed by the Keychain.
5
+ /// Works on both iOS and macOS via the Security framework.
6
+ ///
7
+ /// Methods:
8
+ /// - get(key: String) -> String?
9
+ /// - set(key: String, value: String)
10
+ /// - remove(key: String)
11
+ /// - clear()
12
+ public final class SecureStorageModule: NativeModule {
13
+ public let moduleName = "SecureStorage"
14
+
15
+ private let service = "com.vuenative.securestorage"
16
+
17
+ public init() {}
18
+
19
+ public func invoke(method: String, args: [Any], callback: @escaping (Any?, String?) -> Void) {
20
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
21
+ guard let self = self else { return }
22
+ switch method {
23
+ case "get":
24
+ guard let key = args.first as? String else {
25
+ callback(nil, "get: missing key")
26
+ return
27
+ }
28
+ let query: [String: Any] = [
29
+ kSecClass as String: kSecClassGenericPassword,
30
+ kSecAttrService as String: self.service,
31
+ kSecAttrAccount as String: key,
32
+ kSecReturnData as String: true,
33
+ kSecMatchLimit as String: kSecMatchLimitOne,
34
+ ]
35
+ var result: AnyObject?
36
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
37
+ if status == errSecSuccess, let data = result as? Data,
38
+ let value = String(data: data, encoding: .utf8) {
39
+ callback(value, nil)
40
+ } else if status == errSecItemNotFound {
41
+ callback(nil, nil)
42
+ } else {
43
+ callback(nil, "get: Keychain error \(status)")
44
+ }
45
+
46
+ case "set":
47
+ guard args.count >= 2,
48
+ let key = args[0] as? String,
49
+ let value = args[1] as? String else {
50
+ callback(nil, "set: missing key or value")
51
+ return
52
+ }
53
+ guard let data = value.data(using: .utf8) else {
54
+ callback(nil, "set: failed to encode value")
55
+ return
56
+ }
57
+
58
+ // Try to update first
59
+ let searchQuery: [String: Any] = [
60
+ kSecClass as String: kSecClassGenericPassword,
61
+ kSecAttrService as String: self.service,
62
+ kSecAttrAccount as String: key,
63
+ ]
64
+ let updateAttributes: [String: Any] = [
65
+ kSecValueData as String: data,
66
+ ]
67
+ let updateStatus = SecItemUpdate(searchQuery as CFDictionary, updateAttributes as CFDictionary)
68
+
69
+ if updateStatus == errSecItemNotFound {
70
+ // Item doesn't exist yet, add it
71
+ var addQuery = searchQuery
72
+ addQuery[kSecValueData as String] = data
73
+ let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
74
+ if addStatus == errSecSuccess {
75
+ callback(nil, nil)
76
+ } else {
77
+ callback(nil, "set: Keychain add error \(addStatus)")
78
+ }
79
+ } else if updateStatus == errSecSuccess {
80
+ callback(nil, nil)
81
+ } else {
82
+ callback(nil, "set: Keychain update error \(updateStatus)")
83
+ }
84
+
85
+ case "remove":
86
+ guard let key = args.first as? String else {
87
+ callback(nil, "remove: missing key")
88
+ return
89
+ }
90
+ let query: [String: Any] = [
91
+ kSecClass as String: kSecClassGenericPassword,
92
+ kSecAttrService as String: self.service,
93
+ kSecAttrAccount as String: key,
94
+ ]
95
+ let status = SecItemDelete(query as CFDictionary)
96
+ if status == errSecSuccess || status == errSecItemNotFound {
97
+ callback(nil, nil)
98
+ } else {
99
+ callback(nil, "remove: Keychain error \(status)")
100
+ }
101
+
102
+ case "clear":
103
+ let query: [String: Any] = [
104
+ kSecClass as String: kSecClassGenericPassword,
105
+ kSecAttrService as String: self.service,
106
+ ]
107
+ let status = SecItemDelete(query as CFDictionary)
108
+ if status == errSecSuccess || status == errSecItemNotFound {
109
+ callback(nil, nil)
110
+ } else {
111
+ callback(nil, "clear: Keychain error \(status)")
112
+ }
113
+
114
+ default:
115
+ callback(nil, "SecureStorageModule: Unknown method '\(method)'")
116
+ }
117
+ }
118
+ }
119
+ }