native-update 1.0.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.
Files changed (184) hide show
  1. package/CapacitorNativeUpdate.podspec +18 -0
  2. package/LICENSE +21 -0
  3. package/Readme.md +451 -0
  4. package/android/build.gradle +92 -0
  5. package/android/gradle/wrapper/gradle-wrapper.properties +8 -0
  6. package/android/gradle.properties +17 -0
  7. package/android/proguard-rules.pro +29 -0
  8. package/android/settings.gradle +2 -0
  9. package/android/src/main/AndroidManifest.xml +34 -0
  10. package/android/src/main/java/com/aoneahsan/nativeupdate/AppReviewPlugin.kt +153 -0
  11. package/android/src/main/java/com/aoneahsan/nativeupdate/AppUpdatePlugin.kt +275 -0
  12. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +390 -0
  13. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateManager.kt +46 -0
  14. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +333 -0
  15. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +251 -0
  16. package/android/src/main/java/com/aoneahsan/nativeupdate/CapacitorNativeUpdatePlugin.kt +265 -0
  17. package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +526 -0
  18. package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +99 -0
  19. package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +249 -0
  20. package/dist/esm/__tests__/bundle-manager.test.d.ts +1 -0
  21. package/dist/esm/__tests__/bundle-manager.test.js +123 -0
  22. package/dist/esm/__tests__/bundle-manager.test.js.map +1 -0
  23. package/dist/esm/__tests__/config.test.d.ts +1 -0
  24. package/dist/esm/__tests__/config.test.js +69 -0
  25. package/dist/esm/__tests__/config.test.js.map +1 -0
  26. package/dist/esm/__tests__/integration.test.d.ts +1 -0
  27. package/dist/esm/__tests__/integration.test.js +78 -0
  28. package/dist/esm/__tests__/integration.test.js.map +1 -0
  29. package/dist/esm/__tests__/security.test.d.ts +1 -0
  30. package/dist/esm/__tests__/security.test.js +54 -0
  31. package/dist/esm/__tests__/security.test.js.map +1 -0
  32. package/dist/esm/__tests__/version-manager.test.d.ts +1 -0
  33. package/dist/esm/__tests__/version-manager.test.js +45 -0
  34. package/dist/esm/__tests__/version-manager.test.js.map +1 -0
  35. package/dist/esm/app-review/app-review-manager.d.ts +24 -0
  36. package/dist/esm/app-review/app-review-manager.js +195 -0
  37. package/dist/esm/app-review/app-review-manager.js.map +1 -0
  38. package/dist/esm/app-review/index.d.ts +5 -0
  39. package/dist/esm/app-review/index.js +6 -0
  40. package/dist/esm/app-review/index.js.map +1 -0
  41. package/dist/esm/app-review/platform-review-handler.d.ts +20 -0
  42. package/dist/esm/app-review/platform-review-handler.js +138 -0
  43. package/dist/esm/app-review/platform-review-handler.js.map +1 -0
  44. package/dist/esm/app-review/review-conditions-checker.d.ts +22 -0
  45. package/dist/esm/app-review/review-conditions-checker.js +155 -0
  46. package/dist/esm/app-review/review-conditions-checker.js.map +1 -0
  47. package/dist/esm/app-review/review-rate-limiter.d.ts +23 -0
  48. package/dist/esm/app-review/review-rate-limiter.js +164 -0
  49. package/dist/esm/app-review/review-rate-limiter.js.map +1 -0
  50. package/dist/esm/app-review/types.d.ts +41 -0
  51. package/dist/esm/app-review/types.js +2 -0
  52. package/dist/esm/app-review/types.js.map +1 -0
  53. package/dist/esm/app-update/app-update-checker.d.ts +13 -0
  54. package/dist/esm/app-update/app-update-checker.js +104 -0
  55. package/dist/esm/app-update/app-update-checker.js.map +1 -0
  56. package/dist/esm/app-update/app-update-installer.d.ts +19 -0
  57. package/dist/esm/app-update/app-update-installer.js +123 -0
  58. package/dist/esm/app-update/app-update-installer.js.map +1 -0
  59. package/dist/esm/app-update/app-update-manager.d.ts +28 -0
  60. package/dist/esm/app-update/app-update-manager.js +199 -0
  61. package/dist/esm/app-update/app-update-manager.js.map +1 -0
  62. package/dist/esm/app-update/app-update-notifier.d.ts +14 -0
  63. package/dist/esm/app-update/app-update-notifier.js +100 -0
  64. package/dist/esm/app-update/app-update-notifier.js.map +1 -0
  65. package/dist/esm/app-update/index.d.ts +6 -0
  66. package/dist/esm/app-update/index.js +7 -0
  67. package/dist/esm/app-update/index.js.map +1 -0
  68. package/dist/esm/app-update/platform-app-update.d.ts +19 -0
  69. package/dist/esm/app-update/platform-app-update.js +129 -0
  70. package/dist/esm/app-update/platform-app-update.js.map +1 -0
  71. package/dist/esm/app-update/types.d.ts +58 -0
  72. package/dist/esm/app-update/types.js +12 -0
  73. package/dist/esm/app-update/types.js.map +1 -0
  74. package/dist/esm/background-update/background-scheduler.d.ts +17 -0
  75. package/dist/esm/background-update/background-scheduler.js +195 -0
  76. package/dist/esm/background-update/background-scheduler.js.map +1 -0
  77. package/dist/esm/background-update/index.d.ts +3 -0
  78. package/dist/esm/background-update/index.js +3 -0
  79. package/dist/esm/background-update/index.js.map +1 -0
  80. package/dist/esm/background-update/notification-manager.d.ts +29 -0
  81. package/dist/esm/background-update/notification-manager.js +89 -0
  82. package/dist/esm/background-update/notification-manager.js.map +1 -0
  83. package/dist/esm/core/analytics.d.ts +70 -0
  84. package/dist/esm/core/analytics.js +137 -0
  85. package/dist/esm/core/analytics.js.map +1 -0
  86. package/dist/esm/core/cache-manager.d.ts +72 -0
  87. package/dist/esm/core/cache-manager.js +275 -0
  88. package/dist/esm/core/cache-manager.js.map +1 -0
  89. package/dist/esm/core/config.d.ts +48 -0
  90. package/dist/esm/core/config.js +83 -0
  91. package/dist/esm/core/config.js.map +1 -0
  92. package/dist/esm/core/errors.d.ts +51 -0
  93. package/dist/esm/core/errors.js +80 -0
  94. package/dist/esm/core/errors.js.map +1 -0
  95. package/dist/esm/core/logger.d.ts +21 -0
  96. package/dist/esm/core/logger.js +109 -0
  97. package/dist/esm/core/logger.js.map +1 -0
  98. package/dist/esm/core/performance.d.ts +53 -0
  99. package/dist/esm/core/performance.js +140 -0
  100. package/dist/esm/core/performance.js.map +1 -0
  101. package/dist/esm/core/plugin-manager.d.ts +66 -0
  102. package/dist/esm/core/plugin-manager.js +148 -0
  103. package/dist/esm/core/plugin-manager.js.map +1 -0
  104. package/dist/esm/core/security.d.ts +93 -0
  105. package/dist/esm/core/security.js +315 -0
  106. package/dist/esm/core/security.js.map +1 -0
  107. package/dist/esm/definitions.d.ts +639 -0
  108. package/dist/esm/definitions.js +103 -0
  109. package/dist/esm/definitions.js.map +1 -0
  110. package/dist/esm/index.d.ts +12 -0
  111. package/dist/esm/index.js +16 -0
  112. package/dist/esm/index.js.map +1 -0
  113. package/dist/esm/live-update/bundle-manager.d.ts +94 -0
  114. package/dist/esm/live-update/bundle-manager.js +310 -0
  115. package/dist/esm/live-update/bundle-manager.js.map +1 -0
  116. package/dist/esm/live-update/certificate-pinning.d.ts +38 -0
  117. package/dist/esm/live-update/certificate-pinning.js +78 -0
  118. package/dist/esm/live-update/certificate-pinning.js.map +1 -0
  119. package/dist/esm/live-update/download-manager.d.ts +67 -0
  120. package/dist/esm/live-update/download-manager.js +319 -0
  121. package/dist/esm/live-update/download-manager.js.map +1 -0
  122. package/dist/esm/live-update/update-manager.d.ts +52 -0
  123. package/dist/esm/live-update/update-manager.js +294 -0
  124. package/dist/esm/live-update/update-manager.js.map +1 -0
  125. package/dist/esm/live-update/version-manager.d.ts +84 -0
  126. package/dist/esm/live-update/version-manager.js +335 -0
  127. package/dist/esm/live-update/version-manager.js.map +1 -0
  128. package/dist/esm/plugin.d.ts +6 -0
  129. package/dist/esm/plugin.js +283 -0
  130. package/dist/esm/plugin.js.map +1 -0
  131. package/dist/esm/security/crypto.d.ts +25 -0
  132. package/dist/esm/security/crypto.js +70 -0
  133. package/dist/esm/security/crypto.js.map +1 -0
  134. package/dist/esm/security/validator.d.ts +60 -0
  135. package/dist/esm/security/validator.js +143 -0
  136. package/dist/esm/security/validator.js.map +1 -0
  137. package/dist/esm/web.d.ts +74 -0
  138. package/dist/esm/web.js +595 -0
  139. package/dist/esm/web.js.map +1 -0
  140. package/dist/plugin.cjs.js +2 -0
  141. package/dist/plugin.cjs.js.map +1 -0
  142. package/dist/plugin.esm.js +2 -0
  143. package/dist/plugin.esm.js.map +1 -0
  144. package/dist/plugin.js +3 -0
  145. package/dist/plugin.js.map +1 -0
  146. package/docs/APP_REVIEW_GUIDE.md +768 -0
  147. package/docs/BUNDLE_SIGNING.md +264 -0
  148. package/docs/LIVE_UPDATES_GUIDE.md +650 -0
  149. package/docs/MIGRATION.md +192 -0
  150. package/docs/NATIVE_UPDATES_GUIDE.md +694 -0
  151. package/docs/QUICK_START.md +606 -0
  152. package/docs/README.md +111 -0
  153. package/docs/REMAINING_FEATURES.md +139 -0
  154. package/docs/api/app-review-api.md +259 -0
  155. package/docs/api/app-update-api.md +238 -0
  156. package/docs/api/events-api.md +451 -0
  157. package/docs/api/live-update-api.md +265 -0
  158. package/docs/background-updates.md +392 -0
  159. package/docs/examples/advanced-scenarios.md +410 -0
  160. package/docs/examples/basic-usage.md +185 -0
  161. package/docs/features/app-reviews.md +975 -0
  162. package/docs/features/app-updates.md +785 -0
  163. package/docs/features/live-updates.md +633 -0
  164. package/docs/getting-started/configuration.md +468 -0
  165. package/docs/getting-started/installation.md +209 -0
  166. package/docs/getting-started/quick-start.md +379 -0
  167. package/docs/guides/deployment-guide.md +333 -0
  168. package/docs/guides/migration-from-codepush.md +142 -0
  169. package/docs/guides/security-best-practices.md +1057 -0
  170. package/docs/guides/testing-guide.md +373 -0
  171. package/docs/production-readiness.md +478 -0
  172. package/docs/security/certificate-pinning.md +122 -0
  173. package/docs/server-requirements.md +147 -0
  174. package/ios/Plugin/AppReview/AppReviewPlugin.swift +158 -0
  175. package/ios/Plugin/AppUpdate/AppUpdatePlugin.swift +234 -0
  176. package/ios/Plugin/BackgroundUpdate/BackgroundNotificationManager.swift +329 -0
  177. package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +396 -0
  178. package/ios/Plugin/CapacitorNativeUpdatePlugin.m +45 -0
  179. package/ios/Plugin/CapacitorNativeUpdatePlugin.swift +190 -0
  180. package/ios/Plugin/Info.plist +43 -0
  181. package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +689 -0
  182. package/ios/Plugin/LiveUpdate/WebViewConfiguration.swift +45 -0
  183. package/ios/Plugin/Security/SecurityManager.swift +289 -0
  184. package/package.json +90 -0
@@ -0,0 +1,45 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import WebKit
4
+
5
+ extension NativeUpdatePlugin {
6
+
7
+ // This method should be called by the Capacitor app during WebView setup
8
+ @objc public func configureWebView(for webView: WKWebView) {
9
+ // Check if there's an active bundle to load
10
+ if let activeBundleId = UserDefaults.standard.string(forKey: "native_update_active_bundle"),
11
+ let bundles = UserDefaults.standard.dictionary(forKey: "native_update_bundles"),
12
+ let bundleInfo = bundles[activeBundleId] as? [String: Any],
13
+ let extractedPath = bundleInfo["extractedPath"] as? String {
14
+
15
+ // Verify the bundle still exists
16
+ let bundleURL = URL(fileURLWithPath: extractedPath)
17
+ let indexURL = bundleURL.appendingPathComponent("index.html")
18
+
19
+ if FileManager.default.fileExists(atPath: indexURL.path) {
20
+ // Configure WebView to load from the active bundle
21
+ webView.loadFileURL(indexURL, allowingReadAccessTo: bundleURL)
22
+ return
23
+ }
24
+ }
25
+
26
+ // If no active bundle or bundle doesn't exist, clear the active bundle
27
+ UserDefaults.standard.removeObject(forKey: "native_update_active_bundle")
28
+ }
29
+
30
+ // Helper method to get the current bundle's base URL
31
+ @objc public func getCurrentBundleURL() -> URL? {
32
+ if let activeBundleId = UserDefaults.standard.string(forKey: "native_update_active_bundle"),
33
+ let bundles = UserDefaults.standard.dictionary(forKey: "native_update_bundles"),
34
+ let bundleInfo = bundles[activeBundleId] as? [String: Any],
35
+ let extractedPath = bundleInfo["extractedPath"] as? String {
36
+
37
+ let bundleURL = URL(fileURLWithPath: extractedPath)
38
+ if FileManager.default.fileExists(atPath: bundleURL.path) {
39
+ return bundleURL
40
+ }
41
+ }
42
+
43
+ return nil
44
+ }
45
+ }
@@ -0,0 +1,289 @@
1
+ import Foundation
2
+ import Security
3
+ import CommonCrypto
4
+ import CryptoKit
5
+
6
+ class SecurityManager {
7
+ private var config: [String: Any]?
8
+ private let keychain = KeychainWrapper()
9
+
10
+ func configure(_ config: [String: Any]) throws {
11
+ self.config = config
12
+
13
+ // Validate security configuration
14
+ let enforceHttps = config["enforceHttps"] as? Bool ?? true
15
+ if !enforceHttps {
16
+ print("⚠️ SecurityManager: HTTPS enforcement is disabled. This is not recommended for production.")
17
+ }
18
+ }
19
+
20
+ func getSecurityInfo() -> [String: Any] {
21
+ return [
22
+ "enforceHttps": config?["enforceHttps"] as? Bool ?? true,
23
+ "certificatePinning": [
24
+ "enabled": (config?["certificatePinning"] as? [String: Any])?["enabled"] as? Bool ?? false,
25
+ "pins": getCertificatePins()
26
+ ],
27
+ "validateInputs": config?["validateInputs"] as? Bool ?? true,
28
+ "secureStorage": config?["secureStorage"] as? Bool ?? true
29
+ ]
30
+ }
31
+
32
+ func getCertificatePins() -> [String: [String]] {
33
+ guard let pinningConfig = config?["certificatePinning"] as? [String: Any],
34
+ pinningConfig["enabled"] as? Bool == true,
35
+ let pins = pinningConfig["pins"] as? [[String: Any]] else {
36
+ return [:]
37
+ }
38
+
39
+ var hostPins: [String: [String]] = [:]
40
+
41
+ for pin in pins {
42
+ guard let hostname = pin["hostname"] as? String,
43
+ let sha256Pins = pin["sha256"] as? [String] else {
44
+ continue
45
+ }
46
+
47
+ hostPins[hostname] = sha256Pins
48
+ }
49
+
50
+ return hostPins
51
+ }
52
+
53
+ func validateUrl(_ url: String) -> Bool {
54
+ if !url.hasPrefix("https://") && isHttpsEnforced() {
55
+ return false
56
+ }
57
+
58
+ // Check against allowed hosts if configured
59
+ if let allowedHosts = getAllowedHosts(), !allowedHosts.isEmpty {
60
+ guard let urlComponents = URLComponents(string: url),
61
+ let host = urlComponents.host else {
62
+ return false
63
+ }
64
+ return allowedHosts.contains(host)
65
+ }
66
+
67
+ return true
68
+ }
69
+
70
+ func verifySignature(data: Data, signature: String, publicKeyString: String) -> Bool {
71
+ // Handle PEM format or base64 encoded public key
72
+ let publicKeyData: Data
73
+ if publicKeyString.contains("-----BEGIN PUBLIC KEY-----") {
74
+ // Convert PEM to base64
75
+ let base64String = publicKeyString
76
+ .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "")
77
+ .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")
78
+ .replacingOccurrences(of: "\n", with: "")
79
+ .replacingOccurrences(of: "\r", with: "")
80
+ .trimmingCharacters(in: .whitespacesAndNewlines)
81
+
82
+ guard let data = Data(base64Encoded: base64String) else {
83
+ print("Failed to decode PEM public key")
84
+ return false
85
+ }
86
+ publicKeyData = data
87
+ } else {
88
+ // Already base64 encoded
89
+ guard let data = Data(base64Encoded: publicKeyString) else {
90
+ print("Failed to decode base64 public key")
91
+ return false
92
+ }
93
+ publicKeyData = data
94
+ }
95
+
96
+ guard let signatureData = Data(base64Encoded: signature) else {
97
+ print("Failed to decode signature")
98
+ return false
99
+ }
100
+
101
+ do {
102
+ // Create SecKey from public key data
103
+ let attributes: [String: Any] = [
104
+ kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
105
+ kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
106
+ kSecAttrKeySizeInBits as String: 2048
107
+ ]
108
+
109
+ guard let secKey = SecKeyCreateWithData(publicKeyData as CFData, attributes as CFDictionary, nil) else {
110
+ print("Failed to create SecKey from public key data")
111
+ return false
112
+ }
113
+
114
+ // Verify signature using RSA-PSS (matching web implementation)
115
+ let algorithm = SecKeyAlgorithm.rsaSignatureMessagePSSSHA256
116
+ let result = SecKeyVerifySignature(secKey, algorithm, data as CFData, signatureData as CFData, nil)
117
+
118
+ if !result {
119
+ print("Signature verification failed")
120
+ }
121
+
122
+ return result
123
+ } catch {
124
+ print("Signature verification error: \(error)")
125
+ return false
126
+ }
127
+ }
128
+
129
+ func calculateChecksum(for data: Data, algorithm: String = "SHA-256") -> String {
130
+ let digest: Data
131
+
132
+ switch algorithm {
133
+ case "SHA-256":
134
+ digest = SHA256.hash(data: data).data
135
+ case "SHA-512":
136
+ digest = SHA512.hash(data: data).data
137
+ default:
138
+ // Fallback to SHA-256
139
+ digest = SHA256.hash(data: data).data
140
+ }
141
+
142
+ return digest.map { String(format: "%02x", $0) }.joined()
143
+ }
144
+
145
+ func saveSecureData(key: String, value: String) {
146
+ if isSecureStorageEnabled() {
147
+ keychain.set(value, forKey: key)
148
+ } else {
149
+ // Fallback to UserDefaults (not recommended)
150
+ UserDefaults.standard.set(value, forKey: key)
151
+ }
152
+ }
153
+
154
+ func getSecureData(key: String) -> String? {
155
+ if isSecureStorageEnabled() {
156
+ return keychain.string(forKey: key)
157
+ } else {
158
+ return UserDefaults.standard.string(forKey: key)
159
+ }
160
+ }
161
+
162
+ func validatePath(_ path: String) -> Bool {
163
+ // Prevent directory traversal attacks
164
+ if path.contains("..") || path.contains("//") {
165
+ return false
166
+ }
167
+
168
+ // Ensure path is within app's sandbox
169
+ let url = URL(fileURLWithPath: path)
170
+ let standardizedPath = url.standardizedFileURL.path
171
+
172
+ // Check if path is within allowed directories
173
+ let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path
174
+ let cachesPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.path
175
+ let tempPath = NSTemporaryDirectory()
176
+
177
+ return standardizedPath.hasPrefix(documentsPath) ||
178
+ standardizedPath.hasPrefix(cachesPath) ||
179
+ standardizedPath.hasPrefix(tempPath)
180
+ }
181
+
182
+ func sanitizeInput(_ input: String) -> String {
183
+ // Remove potentially dangerous characters
184
+ let allowedCharacters = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "._-"))
185
+ return input.components(separatedBy: allowedCharacters.inverted).joined()
186
+ }
187
+
188
+ func isHttpsEnforced() -> Bool {
189
+ return config?["enforceHttps"] as? Bool ?? true
190
+ }
191
+
192
+ func isSecureStorageEnabled() -> Bool {
193
+ return config?["secureStorage"] as? Bool ?? true
194
+ }
195
+
196
+ func isInputValidationEnabled() -> Bool {
197
+ return config?["validateInputs"] as? Bool ?? true
198
+ }
199
+
200
+ func logSecurityEvent(_ event: String, details: String? = nil) {
201
+ guard config?["logSecurityEvents"] as? Bool == true else { return }
202
+
203
+ let message = "Security Event: \(event) \(details ?? "")"
204
+ print("🔒 \(message)")
205
+
206
+ // In production, you might want to send these to a security monitoring service
207
+ }
208
+
209
+ private func getAllowedHosts() -> [String]? {
210
+ var hosts: [String] = []
211
+
212
+ // Add hosts from live update config
213
+ if let liveUpdateConfig = config?["liveUpdate"] as? [String: Any],
214
+ let allowedHosts = liveUpdateConfig["allowedHosts"] as? [String] {
215
+ hosts.append(contentsOf: allowedHosts)
216
+ }
217
+
218
+ // Add host from server URL
219
+ if let serverUrl = config?["serverUrl"] as? String,
220
+ let urlComponents = URLComponents(string: serverUrl),
221
+ let host = urlComponents.host {
222
+ hosts.append(host)
223
+ }
224
+
225
+ return hosts.isEmpty ? nil : Array(Set(hosts))
226
+ }
227
+ }
228
+
229
+ // MARK: - Keychain Wrapper
230
+
231
+ private class KeychainWrapper {
232
+ private let serviceName = "com.aoneahsan.nativeupdate"
233
+
234
+ func set(_ value: String, forKey key: String) {
235
+ guard let data = value.data(using: .utf8) else { return }
236
+
237
+ let query: [String: Any] = [
238
+ kSecClass as String: kSecClassGenericPassword,
239
+ kSecAttrService as String: serviceName,
240
+ kSecAttrAccount as String: key,
241
+ kSecValueData as String: data
242
+ ]
243
+
244
+ // Delete any existing item
245
+ SecItemDelete(query as CFDictionary)
246
+
247
+ // Add new item
248
+ SecItemAdd(query as CFDictionary, nil)
249
+ }
250
+
251
+ func string(forKey key: String) -> String? {
252
+ let query: [String: Any] = [
253
+ kSecClass as String: kSecClassGenericPassword,
254
+ kSecAttrService as String: serviceName,
255
+ kSecAttrAccount as String: key,
256
+ kSecReturnData as String: true,
257
+ kSecMatchLimit as String: kSecMatchLimitOne
258
+ ]
259
+
260
+ var dataTypeRef: AnyObject?
261
+ let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
262
+
263
+ guard status == errSecSuccess,
264
+ let data = dataTypeRef as? Data,
265
+ let string = String(data: data, encoding: .utf8) else {
266
+ return nil
267
+ }
268
+
269
+ return string
270
+ }
271
+
272
+ func remove(forKey key: String) {
273
+ let query: [String: Any] = [
274
+ kSecClass as String: kSecClassGenericPassword,
275
+ kSecAttrService as String: serviceName,
276
+ kSecAttrAccount as String: key
277
+ ]
278
+
279
+ SecItemDelete(query as CFDictionary)
280
+ }
281
+ }
282
+
283
+ // MARK: - CryptoKit Extensions
284
+
285
+ extension Digest {
286
+ var data: Data {
287
+ Data(self)
288
+ }
289
+ }
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "native-update",
3
+ "version": "1.0.0",
4
+ "description": "Foundation package for building a comprehensive update system for Capacitor apps. Provides architecture and interfaces but requires backend implementation.",
5
+ "type": "module",
6
+ "main": "dist/plugin.cjs.js",
7
+ "module": "dist/esm/index.js",
8
+ "types": "dist/esm/index.d.ts",
9
+ "unpkg": "dist/plugin.js",
10
+ "files": [
11
+ "android/src/main/",
12
+ "android/build.gradle",
13
+ "android/variables.gradle",
14
+ "android/gradle.properties",
15
+ "android/gradle/",
16
+ "android/settings.gradle",
17
+ "android/proguard-rules.pro",
18
+ "dist/",
19
+ "ios/Plugin/",
20
+ "CapacitorNativeUpdate.podspec",
21
+ "docs/"
22
+ ],
23
+ "author": {
24
+ "name": "Ahsan Mahmood",
25
+ "email": "aoneahsan@gmail.com",
26
+ "url": "https://aoneahsan.com"
27
+ },
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/aoneahsan/native-update.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/aoneahsan/native-update/issues"
35
+ },
36
+ "homepage": "https://github.com/aoneahsan/native-update#readme",
37
+ "keywords": [
38
+ "capacitor",
39
+ "plugin",
40
+ "native",
41
+ "app-updates",
42
+ "native-updates"
43
+ ],
44
+ "scripts": {
45
+ "build": "npm run clean && npm run tsc && rollup -c rollup.config.js",
46
+ "build:prod": "npm run clean && npm run tsc && NODE_ENV=production rollup -c rollup.config.js",
47
+ "clean": "rimraf ./dist",
48
+ "tsc": "tsc",
49
+ "watch": "tsc --watch",
50
+ "lint": "eslint . --ext ts",
51
+ "prettier": "prettier --write .",
52
+ "prepublishOnly": "npm run build:prod",
53
+ "swiftlint": "cd ios && swiftlint lint --fix --format --path Plugin --verbose",
54
+ "test": "vitest",
55
+ "test:ui": "vitest --ui",
56
+ "test:coverage": "vitest --coverage"
57
+ },
58
+ "devDependencies": {
59
+ "@capacitor/android": "^7.4.2",
60
+ "@capacitor/core": "^7.4.2",
61
+ "@capacitor/filesystem": "^7.1.4",
62
+ "@capacitor/ios": "^7.4.2",
63
+ "@capacitor/preferences": "^7.0.2",
64
+ "@rollup/plugin-json": "^6.1.0",
65
+ "@rollup/plugin-node-resolve": "^16.0.1",
66
+ "@rollup/plugin-terser": "^0.4.4",
67
+ "@types/node": "^24.2.1",
68
+ "@typescript-eslint/eslint-plugin": "^8.39.1",
69
+ "@typescript-eslint/parser": "^8.39.1",
70
+ "@vitest/ui": "^3.2.4",
71
+ "eslint": "^9.33.0",
72
+ "happy-dom": "^18.0.1",
73
+ "prettier": "^3.6.2",
74
+ "rimraf": "^6.0.1",
75
+ "rollup": "^4.46.2",
76
+ "typescript": "^5.9.2",
77
+ "vitest": "^3.2.4"
78
+ },
79
+ "peerDependencies": {
80
+ "@capacitor/core": "^7.4.2"
81
+ },
82
+ "capacitor": {
83
+ "ios": {
84
+ "src": "ios"
85
+ },
86
+ "android": {
87
+ "src": "android"
88
+ }
89
+ }
90
+ }