native-update 2.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/Readme.md +28 -14
  2. package/android/manifest-additions.xml +28 -0
  3. package/android/src/main/AndroidManifest.xml +3 -7
  4. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +21 -13
  5. package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +196 -29
  6. package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +21 -36
  7. package/cli/AGENTS.md +15 -1
  8. package/cli/CLAUDE.md +15 -1
  9. package/cli/commands/backend-create.js +56 -15
  10. package/cli/commands/bundle-create.js +39 -7
  11. package/cli/commands/monitor.js +72 -51
  12. package/cli/index.js +10 -24
  13. package/cli/package.json +11 -3
  14. package/dist/esm/app-review/app-review-manager.js +0 -1
  15. package/dist/esm/app-review/index.js +0 -1
  16. package/dist/esm/app-review/platform-review-handler.js +0 -1
  17. package/dist/esm/app-review/review-conditions-checker.js +0 -1
  18. package/dist/esm/app-review/review-rate-limiter.js +0 -1
  19. package/dist/esm/app-review/types.js +0 -1
  20. package/dist/esm/app-update/app-update-checker.js +0 -1
  21. package/dist/esm/app-update/app-update-installer.d.ts +1 -0
  22. package/dist/esm/app-update/app-update-installer.js +19 -8
  23. package/dist/esm/app-update/app-update-manager.js +0 -1
  24. package/dist/esm/app-update/app-update-notifier.js +0 -1
  25. package/dist/esm/app-update/index.js +0 -1
  26. package/dist/esm/app-update/platform-app-update.js +12 -3
  27. package/dist/esm/app-update/types.js +0 -1
  28. package/dist/esm/background-update/background-scheduler.js +0 -1
  29. package/dist/esm/background-update/index.js +0 -1
  30. package/dist/esm/background-update/notification-manager.js +0 -1
  31. package/dist/esm/config/support.js +0 -1
  32. package/dist/esm/core/analytics.d.ts +13 -0
  33. package/dist/esm/core/analytics.js +15 -1
  34. package/dist/esm/core/cache-manager.js +0 -1
  35. package/dist/esm/core/config.d.ts +0 -15
  36. package/dist/esm/core/config.js +0 -4
  37. package/dist/esm/core/errors.d.ts +1 -0
  38. package/dist/esm/core/errors.js +1 -1
  39. package/dist/esm/core/event-emitter.d.ts +0 -3
  40. package/dist/esm/core/event-emitter.js +8 -3
  41. package/dist/esm/core/logger.js +0 -1
  42. package/dist/esm/core/performance.js +0 -1
  43. package/dist/esm/core/plugin-manager.js +3 -1
  44. package/dist/esm/core/security.d.ts +11 -0
  45. package/dist/esm/core/security.js +27 -1
  46. package/dist/esm/definitions.d.ts +0 -11
  47. package/dist/esm/definitions.js +0 -1
  48. package/dist/esm/index.d.ts +3 -2
  49. package/dist/esm/index.js +3 -1
  50. package/dist/esm/live-update/bundle-manager.d.ts +8 -0
  51. package/dist/esm/live-update/bundle-manager.js +20 -1
  52. package/dist/esm/live-update/certificate-pinning.js +0 -1
  53. package/dist/esm/live-update/delta-processor.d.ts +1 -1
  54. package/dist/esm/live-update/delta-processor.js +3 -1
  55. package/dist/esm/live-update/download-manager.js +9 -2
  56. package/dist/esm/live-update/rollout-checker.d.ts +1 -1
  57. package/dist/esm/live-update/rollout-checker.js +5 -6
  58. package/dist/esm/live-update/update-manager.js +0 -1
  59. package/dist/esm/live-update/version-manager.d.ts +0 -12
  60. package/dist/esm/live-update/version-manager.js +0 -68
  61. package/dist/esm/plugin.js +69 -43
  62. package/dist/esm/security/crypto.js +11 -7
  63. package/dist/esm/security/validator.js +0 -1
  64. package/dist/esm/types/rollout.d.ts +85 -0
  65. package/dist/esm/types/rollout.js +25 -0
  66. package/dist/esm/web.d.ts +8 -0
  67. package/dist/esm/web.js +148 -54
  68. package/dist/plugin.cjs.js +1 -2
  69. package/dist/plugin.esm.js +1 -2
  70. package/dist/plugin.js +2 -3
  71. package/docs/CHANGELOG.md +124 -0
  72. package/docs/CLAUDE.md +1 -1
  73. package/docs/MANUAL-TASKS.md +17 -0
  74. package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
  75. package/docs/MIGRATION.md +83 -0
  76. package/docs/PACKAGES.md +167 -0
  77. package/docs/PERFORMANCE.md +110 -0
  78. package/docs/PROJECT_COMPLETION_TRACKER.md +4 -35
  79. package/docs/README.md +0 -1
  80. package/docs/REMAINING_FEATURES.md +123 -76
  81. package/docs/ROADMAP.md +200 -120
  82. package/docs/SECURITY-IF-REPO-PUBLIC.md +232 -0
  83. package/docs/TESTING_REQUIREMENTS.md +1 -112
  84. package/docs/ai-knowledge-base/00-project-profile.md +38 -0
  85. package/docs/ai-knowledge-base/01-features.md +46 -0
  86. package/docs/ai-knowledge-base/02-routes.md +72 -0
  87. package/docs/ai-knowledge-base/03-tech-stack.md +57 -0
  88. package/docs/ai-knowledge-base/README.md +23 -0
  89. package/docs/analytics-tracking.md +37 -0
  90. package/docs/capacitor-official-plugins.md +46 -0
  91. package/docs/capawesome-plugins.md +29 -0
  92. package/docs/deployment/HOSTINGER_DEPLOY.md +75 -34
  93. package/docs/deployment/PRODUCTION-DEPLOY-CHECKLIST.md +184 -0
  94. package/docs/docs-site/plan.md +201 -0
  95. package/docs/docs-site/scope.md +127 -0
  96. package/docs/docs-site/tracker.json +235 -0
  97. package/docs/error-handling-tracking.md +37 -0
  98. package/docs/guides/testing-guide.md +15 -41
  99. package/docs/package-audit.md +41 -0
  100. package/docs/play-store/ASO-METADATA.md +5 -0
  101. package/docs/play-store/DATA-SAFETY.md +5 -0
  102. package/docs/play-store/DECLARATIONS.md +5 -0
  103. package/docs/play-store/README.md +27 -0
  104. package/docs/play-store/RELEASE-NOTES.md +99 -0
  105. package/docs/play-store/RELEASE-STEPS.md +6 -0
  106. package/docs/play-store/STORE-LISTING.md +7 -0
  107. package/docs/play-store/SUBMISSION-CHECKLIST.md +5 -0
  108. package/docs/production-readiness.md +1 -1
  109. package/docs/project-audit/2026-05-16/00-FINALIZATION-PLAN.md +123 -0
  110. package/docs/project-audit/2026-05-16/01-BATCH-PLAN.md +227 -0
  111. package/docs/project-audit/2026-05-16/02-AUDIT-FINDINGS.md +144 -0
  112. package/docs/project-audit/2026-05-16/sessions/batch-01.md +42 -0
  113. package/docs/project-audit/2026-05-16/sessions/batch-02.md +74 -0
  114. package/docs/project-audit/2026-05-16/sessions/batch-03.md +71 -0
  115. package/docs/project-audit/2026-05-16/sessions/batch-04.md +88 -0
  116. package/docs/project-audit/2026-05-16/sessions/batch-05.md +63 -0
  117. package/docs/project-audit/2026-05-16/sessions/batch-06.md +103 -0
  118. package/docs/project-audit/2026-05-16/sessions/batch-07.md +61 -0
  119. package/docs/project-audit/2026-05-16/sessions/batch-08.md +101 -0
  120. package/docs/project-audit/2026-05-16/sessions/batch-09.md +105 -0
  121. package/docs/project-audit/2026-05-16/sessions/batch-10.md +102 -0
  122. package/docs/project-audit/AUDIT-HISTORY.md +74 -0
  123. package/docs/project-audit/BATCH-PLAN.md +168 -0
  124. package/docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.md +73 -0
  125. package/docs/project-audit/PRODUCTION-READINESS-AUDIT-2026-05-08.md +275 -0
  126. package/docs/project-finalization/00-tracker.json +60 -0
  127. package/docs/project-knowledge-base/01-system-overview.md +39 -30
  128. package/docs/project-knowledge-base/02-routes-pages-forms-users.md +87 -81
  129. package/docs/project-knowledge-base/03-tech-stack-modules-services.md +140 -108
  130. package/docs/project-knowledge-base/04-data-models-integrations.md +191 -254
  131. package/docs/project-knowledge-base/05-docs-corpus-inventory.md +83 -20
  132. package/docs/project-knowledge-base/06-operations-testing-legal-content.md +67 -29
  133. package/docs/project-knowledge-base/README.md +27 -14
  134. package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-05-19.md +81 -0
  135. package/docs/rules-tracker.md +102 -0
  136. package/docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md +116 -0
  137. package/docs/social-media-marketing/README.md +81 -0
  138. package/docs/social-media-marketing/posts-batch-01.md +613 -0
  139. package/docs/social-media-marketing/posts-batch-02.md +528 -0
  140. package/docs/social-media-marketing/whatsapp-messages-batch-01.md +458 -0
  141. package/docs/social-media-marketing/whatsapp-status-batch-01.md +355 -0
  142. package/docs/tracking/finalization-2026-05-16-tracker.json +346 -0
  143. package/docs/tracking/full-audit-tracker.json +121 -0
  144. package/docs/tracking/production-readiness-audit-tracker.json +429 -0
  145. package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +16 -3
  146. package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +71 -47
  147. package/ios/Plugin/NativeUpdatePlugin.swift +14 -9
  148. package/package.json +29 -29
  149. package/cli/node_modules/.yarn-integrity +0 -16
  150. package/cli/node_modules/commander/LICENSE +0 -22
  151. package/cli/node_modules/commander/Readme.md +0 -1148
  152. package/cli/node_modules/commander/esm.mjs +0 -16
  153. package/cli/node_modules/commander/index.js +0 -26
  154. package/cli/node_modules/commander/lib/argument.js +0 -145
  155. package/cli/node_modules/commander/lib/command.js +0 -2179
  156. package/cli/node_modules/commander/lib/error.js +0 -43
  157. package/cli/node_modules/commander/lib/help.js +0 -462
  158. package/cli/node_modules/commander/lib/option.js +0 -329
  159. package/cli/node_modules/commander/lib/suggestSimilar.js +0 -100
  160. package/cli/node_modules/commander/package-support.json +0 -16
  161. package/cli/node_modules/commander/package.json +0 -80
  162. package/cli/node_modules/commander/typings/esm.d.mts +0 -3
  163. package/cli/node_modules/commander/typings/index.d.ts +0 -884
  164. package/cli/yarn.lock +0 -8
  165. package/dist/esm/__tests__/bundle-manager.test.d.ts +0 -1
  166. package/dist/esm/__tests__/bundle-manager.test.js +0 -151
  167. package/dist/esm/__tests__/bundle-manager.test.js.map +0 -1
  168. package/dist/esm/__tests__/config.test.d.ts +0 -1
  169. package/dist/esm/__tests__/config.test.js +0 -70
  170. package/dist/esm/__tests__/config.test.js.map +0 -1
  171. package/dist/esm/__tests__/delta-processor.test.d.ts +0 -1
  172. package/dist/esm/__tests__/delta-processor.test.js +0 -77
  173. package/dist/esm/__tests__/delta-processor.test.js.map +0 -1
  174. package/dist/esm/__tests__/firestore-schema.test.d.ts +0 -1
  175. package/dist/esm/__tests__/firestore-schema.test.js +0 -74
  176. package/dist/esm/__tests__/firestore-schema.test.js.map +0 -1
  177. package/dist/esm/__tests__/integration.test.d.ts +0 -1
  178. package/dist/esm/__tests__/integration.test.js +0 -78
  179. package/dist/esm/__tests__/integration.test.js.map +0 -1
  180. package/dist/esm/__tests__/manifest-reader.test.d.ts +0 -1
  181. package/dist/esm/__tests__/manifest-reader.test.js +0 -271
  182. package/dist/esm/__tests__/manifest-reader.test.js.map +0 -1
  183. package/dist/esm/__tests__/rollout-checker.test.d.ts +0 -1
  184. package/dist/esm/__tests__/rollout-checker.test.js +0 -210
  185. package/dist/esm/__tests__/rollout-checker.test.js.map +0 -1
  186. package/dist/esm/__tests__/security-enforcement.test.d.ts +0 -1
  187. package/dist/esm/__tests__/security-enforcement.test.js +0 -95
  188. package/dist/esm/__tests__/security-enforcement.test.js.map +0 -1
  189. package/dist/esm/__tests__/security.test.d.ts +0 -1
  190. package/dist/esm/__tests__/security.test.js +0 -54
  191. package/dist/esm/__tests__/security.test.js.map +0 -1
  192. package/dist/esm/__tests__/version-manager.test.d.ts +0 -1
  193. package/dist/esm/__tests__/version-manager.test.js +0 -45
  194. package/dist/esm/__tests__/version-manager.test.js.map +0 -1
  195. package/dist/esm/app-review/app-review-manager.js.map +0 -1
  196. package/dist/esm/app-review/index.js.map +0 -1
  197. package/dist/esm/app-review/platform-review-handler.js.map +0 -1
  198. package/dist/esm/app-review/review-conditions-checker.js.map +0 -1
  199. package/dist/esm/app-review/review-rate-limiter.js.map +0 -1
  200. package/dist/esm/app-review/types.js.map +0 -1
  201. package/dist/esm/app-update/app-update-checker.js.map +0 -1
  202. package/dist/esm/app-update/app-update-installer.js.map +0 -1
  203. package/dist/esm/app-update/app-update-manager.js.map +0 -1
  204. package/dist/esm/app-update/app-update-notifier.js.map +0 -1
  205. package/dist/esm/app-update/index.js.map +0 -1
  206. package/dist/esm/app-update/platform-app-update.js.map +0 -1
  207. package/dist/esm/app-update/types.js.map +0 -1
  208. package/dist/esm/background-update/background-scheduler.js.map +0 -1
  209. package/dist/esm/background-update/index.js.map +0 -1
  210. package/dist/esm/background-update/notification-manager.js.map +0 -1
  211. package/dist/esm/config/support.js.map +0 -1
  212. package/dist/esm/core/analytics.js.map +0 -1
  213. package/dist/esm/core/cache-manager.js.map +0 -1
  214. package/dist/esm/core/config.js.map +0 -1
  215. package/dist/esm/core/errors.js.map +0 -1
  216. package/dist/esm/core/event-emitter.js.map +0 -1
  217. package/dist/esm/core/logger.js.map +0 -1
  218. package/dist/esm/core/performance.js.map +0 -1
  219. package/dist/esm/core/plugin-manager.js.map +0 -1
  220. package/dist/esm/core/security.js.map +0 -1
  221. package/dist/esm/definitions.js.map +0 -1
  222. package/dist/esm/firestore/firestore-client.d.ts +0 -109
  223. package/dist/esm/firestore/firestore-client.js +0 -264
  224. package/dist/esm/firestore/firestore-client.js.map +0 -1
  225. package/dist/esm/firestore/index.d.ts +0 -11
  226. package/dist/esm/firestore/index.js +0 -11
  227. package/dist/esm/firestore/index.js.map +0 -1
  228. package/dist/esm/firestore/manifest-reader.d.ts +0 -87
  229. package/dist/esm/firestore/manifest-reader.js +0 -294
  230. package/dist/esm/firestore/manifest-reader.js.map +0 -1
  231. package/dist/esm/firestore/schema.d.ts +0 -507
  232. package/dist/esm/firestore/schema.js +0 -73
  233. package/dist/esm/firestore/schema.js.map +0 -1
  234. package/dist/esm/index.js.map +0 -1
  235. package/dist/esm/live-update/bundle-manager.js.map +0 -1
  236. package/dist/esm/live-update/certificate-pinning.js.map +0 -1
  237. package/dist/esm/live-update/delta-processor.js.map +0 -1
  238. package/dist/esm/live-update/download-manager.js.map +0 -1
  239. package/dist/esm/live-update/rollout-checker.js.map +0 -1
  240. package/dist/esm/live-update/update-manager.js.map +0 -1
  241. package/dist/esm/live-update/version-manager.js.map +0 -1
  242. package/dist/esm/plugin.js.map +0 -1
  243. package/dist/esm/security/crypto.js.map +0 -1
  244. package/dist/esm/security/validator.js.map +0 -1
  245. package/dist/esm/web.js.map +0 -1
  246. package/dist/plugin.cjs.js.map +0 -1
  247. package/dist/plugin.esm.js.map +0 -1
  248. package/dist/plugin.js.map +0 -1
  249. package/docs/FIREBASE_INTEGRATION_TRACKER.md +0 -321
  250. package/docs/FIREBASE_QUERIES_AND_INDEXES_AUDIT.md +0 -221
  251. package/docs/examples/firebase-backend-example.md +0 -27
  252. package/docs/features/laravel-nova-backend/credentials.ignore.md +0 -34
  253. package/docs/guides/no-cost-backend-implementation-plan.md +0 -77
  254. package/docs/guides/no-cost-firestore-google-drive-backend.md +0 -60
  255. package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-16.md +0 -454
  256. package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-24.md +0 -66
  257. package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-25.md +0 -67
  258. /package/docs/{MARKETING_WEBSITE_PLAN.md → archive/MARKETING_WEBSITE_PLAN.md} +0 -0
@@ -2,8 +2,6 @@ import { ConfigManager } from '../core/config';
2
2
  import { Logger } from '../core/logger';
3
3
  import { SecurityValidator } from '../core/security';
4
4
  import { ValidationError, ErrorCode } from '../core/errors';
5
- import { FirestoreClient } from '../firestore/firestore-client';
6
- import { ManifestReader } from '../firestore/manifest-reader';
7
5
  /**
8
6
  * Manages version checking and comparison
9
7
  */
@@ -13,8 +11,6 @@ export class VersionManager {
13
11
  this.CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
14
12
  this.preferences = null;
15
13
  this.memoryCache = new Map();
16
- this.firestoreClient = null;
17
- this.manifestReader = null;
18
14
  this.logger = Logger.getInstance();
19
15
  this.configManager = ConfigManager.getInstance();
20
16
  this.securityValidator = SecurityValidator.getInstance();
@@ -78,69 +74,6 @@ export class VersionManager {
78
74
  if (!this.preferences) {
79
75
  throw new ValidationError(ErrorCode.MISSING_DEPENDENCY, 'Preferences not configured. Please configure the plugin first.');
80
76
  }
81
- // Initialize Firestore client if configured
82
- const backendType = this.configManager.get('backendType');
83
- if (backendType === 'firestore') {
84
- const firestoreConfig = this.configManager.get('firestore');
85
- if (!firestoreConfig) {
86
- throw new ValidationError(ErrorCode.INVALID_CONFIG, 'Firestore configuration required when using firestore backend');
87
- }
88
- this.firestoreClient = new FirestoreClient(firestoreConfig);
89
- this.manifestReader = new ManifestReader(this.firestoreClient);
90
- this.logger.debug('Firestore backend initialized');
91
- }
92
- }
93
- /**
94
- * Check for updates from Firestore backend
95
- * Uses ManifestReader for efficient single-document reads
96
- */
97
- async checkForUpdatesFromFirestore(currentVersion, deviceInfo) {
98
- if (!this.manifestReader) {
99
- throw new ValidationError(ErrorCode.INVALID_CONFIG, 'Firestore backend not initialized. Call initialize() first or configure firestore backend.');
100
- }
101
- // Validate current version
102
- this.securityValidator.validateVersion(currentVersion);
103
- const options = {
104
- currentVersion,
105
- deviceInfo,
106
- checkDeltas: this.configManager.get('enableDeltaUpdates'),
107
- };
108
- try {
109
- const response = await this.manifestReader.checkForUpdates(options);
110
- if (response) {
111
- this.logger.info('Firestore update check completed', {
112
- currentVersion,
113
- latestVersion: response.version,
114
- updateAvailable: response.updateAvailable,
115
- eligible: response.rolloutEligible,
116
- });
117
- }
118
- return response;
119
- }
120
- catch (error) {
121
- this.logger.error('Failed to check for updates from Firestore', error);
122
- return null;
123
- }
124
- }
125
- /**
126
- * Get the appropriate update checker based on backend type
127
- */
128
- async checkForUpdatesAuto(currentVersion, deviceInfo) {
129
- const backendType = this.configManager.get('backendType') || 'http';
130
- if (backendType === 'firestore') {
131
- if (!deviceInfo) {
132
- throw new ValidationError(ErrorCode.INVALID_CONFIG, 'Device info is required for Firestore backend');
133
- }
134
- return this.checkForUpdatesFromFirestore(currentVersion, deviceInfo);
135
- }
136
- // Fall back to HTTP-based update check
137
- const serverUrl = this.configManager.get('serverUrl');
138
- const channel = this.configManager.get('channel') || 'production';
139
- const appId = this.configManager.get('appId');
140
- if (!serverUrl || !appId) {
141
- throw new ValidationError(ErrorCode.INVALID_CONFIG, 'Server URL and App ID are required for HTTP backend');
142
- }
143
- return this.checkForUpdates(serverUrl, channel, currentVersion, appId);
144
77
  }
145
78
  /**
146
79
  * Check for latest version from server
@@ -439,4 +372,3 @@ export class VersionManager {
439
372
  }
440
373
  }
441
374
  }
442
- //# sourceMappingURL=version-manager.js.map
@@ -52,8 +52,6 @@ class NativeUpdatePluginWeb {
52
52
  requireSignature: liveUpdate === null || liveUpdate === void 0 ? void 0 : liveUpdate.requireSignature,
53
53
  checksumAlgorithm: liveUpdate === null || liveUpdate === void 0 ? void 0 : liveUpdate.checksumAlgorithm,
54
54
  checkInterval: liveUpdate === null || liveUpdate === void 0 ? void 0 : liveUpdate.checkInterval,
55
- backendType: liveUpdate === null || liveUpdate === void 0 ? void 0 : liveUpdate.backendType,
56
- firestore: liveUpdate === null || liveUpdate === void 0 ? void 0 : liveUpdate.firestore,
57
55
  security: config.security,
58
56
  appStoreId: appUpdate === null || appUpdate === void 0 ? void 0 : appUpdate.appStoreId,
59
57
  packageName: appUpdate === null || appUpdate === void 0 ? void 0 : appUpdate.packageName,
@@ -127,23 +125,33 @@ class NativeUpdatePluginWeb {
127
125
  const downloadManager = this.pluginManager.getDownloadManager();
128
126
  const bundleManager = this.pluginManager.getBundleManager();
129
127
  const blob = await downloadManager.downloadWithRetry(options.url, options.version);
130
- const path = await downloadManager.saveBlob(options.version, blob);
128
+ // Verify checksum/signature against ACTUAL downloaded bytes before
129
+ // persisting. Without this, an attacker who swaps the URL or proxies
130
+ // a tampered bundle can plant arbitrary code and the next set() will
131
+ // activate it. validateBundleData throws on mismatch.
132
+ const bundleData = await blob.arrayBuffer();
133
+ await this.validateBundleData(bundleData, options.checksum, options.signature);
134
+ const validatedBlob = new Blob([bundleData], { type: blob.type });
135
+ const path = await downloadManager.saveBlob(options.version, validatedBlob);
131
136
  const bundleInfo = {
132
137
  bundleId: options.version,
133
138
  version: options.version,
134
139
  path,
135
140
  downloadTime: Date.now(),
136
- size: blob.size,
141
+ size: validatedBlob.size,
137
142
  status: BundleStatus.READY,
138
143
  checksum: options.checksum,
139
144
  signature: options.signature,
140
- verified: false,
145
+ verified: true,
141
146
  };
142
147
  await bundleManager.saveBundleInfo(bundleInfo);
143
148
  return bundleInfo;
144
149
  }
145
150
  async set(bundle) {
146
151
  const bundleManager = this.pluginManager.getBundleManager();
152
+ if (!bundle.verified) {
153
+ throw new NativeUpdateError(ErrorCode.SIGNATURE_INVALID, `Refusing to activate unverified bundle ${bundle.bundleId}`);
154
+ }
147
155
  await bundleManager.setActiveBundle(bundle.bundleId);
148
156
  }
149
157
  async reload() {
@@ -220,16 +228,35 @@ class NativeUpdatePluginWeb {
220
228
  }
221
229
  async validateUpdate(options) {
222
230
  const securityValidator = this.pluginManager.getSecurityValidator();
231
+ const downloadManager = this.pluginManager.getDownloadManager();
223
232
  try {
224
- // Validate checksum
225
- const isValid = await securityValidator.validateChecksum(new ArrayBuffer(0), // Placeholder for bundle data
226
- options.checksum);
233
+ // bundlePath may be either a stored path (`bundles/<id>/bundle.zip`)
234
+ // or a bare bundleId. Normalize to bundleId so we can read the
235
+ // persisted blob.
236
+ const pathMatch = options.bundlePath.match(/bundles\/([^/]+)\/bundle\.zip$/);
237
+ const bundleId = pathMatch ? pathMatch[1] : options.bundlePath;
238
+ const blob = await downloadManager.loadBlob(bundleId);
239
+ if (!blob) {
240
+ return {
241
+ isValid: false,
242
+ error: `Bundle not found at ${options.bundlePath}`,
243
+ };
244
+ }
245
+ const bundleData = await blob.arrayBuffer();
246
+ const checksumValid = await securityValidator.verifyChecksum(bundleData, options.checksum);
247
+ let signatureValid = true;
248
+ if (options.signature) {
249
+ signatureValid = await securityValidator.verifySignature(bundleData, options.signature);
250
+ }
251
+ const sizeValid = options.maxSize
252
+ ? bundleData.byteLength <= options.maxSize
253
+ : true;
227
254
  return {
228
- isValid,
255
+ isValid: checksumValid && signatureValid && sizeValid,
229
256
  details: {
230
- checksumValid: isValid,
231
- signatureValid: true,
232
- sizeValid: true,
257
+ checksumValid,
258
+ signatureValid,
259
+ sizeValid,
233
260
  versionValid: true,
234
261
  },
235
262
  };
@@ -250,31 +277,9 @@ class NativeUpdatePluginWeb {
250
277
  async checkForUpdate() {
251
278
  const bundleManager = this.pluginManager.getBundleManager();
252
279
  const configManager = this.pluginManager.getConfigManager();
253
- const versionManager = this.pluginManager.getVersionManager();
254
280
  try {
255
281
  const currentBundle = await bundleManager.getActiveBundle();
256
282
  const currentVersion = (currentBundle === null || currentBundle === void 0 ? void 0 : currentBundle.version) || '1.0.0';
257
- const backendType = configManager.get('backendType');
258
- if (backendType === 'firestore') {
259
- const result = (await versionManager.checkForUpdatesAuto(currentVersion, this.getDeviceInfo(currentVersion)));
260
- if (!(result === null || result === void 0 ? void 0 : result.updateAvailable)) {
261
- return {
262
- available: false,
263
- currentVersion,
264
- };
265
- }
266
- return {
267
- available: true,
268
- currentVersion,
269
- latestVersion: result.version,
270
- url: result.bundleUrl,
271
- mandatory: result.mandatory,
272
- notes: result.releaseNotes,
273
- size: result.size,
274
- checksum: result.checksum,
275
- signature: result.signature,
276
- };
277
- }
278
283
  const baseUrl = configManager.get('baseUrl');
279
284
  if (!baseUrl) {
280
285
  return {
@@ -282,11 +287,26 @@ class NativeUpdatePluginWeb {
282
287
  currentVersion,
283
288
  };
284
289
  }
285
- const response = await fetch(`${baseUrl}/updates/latest`, {
290
+ // Laravel backend contract: GET /v1/updates/check with X-API-Key,
291
+ // X-Device-ID, X-Current-Version, X-Platform headers. The
292
+ // backend returns either {available: false} or {available: true,
293
+ // version, downloadUrl, checksum, signature, size, ...}.
294
+ const apiKey = configManager.get('apiKey');
295
+ if (!apiKey) {
296
+ throw new NativeUpdateError(ErrorCode.INVALID_CONFIG, 'apiKey is required when using Laravel backend');
297
+ }
298
+ const channel = configManager.get('channel') || 'production';
299
+ const deviceInfo = this.getDeviceInfo(currentVersion);
300
+ const url = `${baseUrl.replace(/\/$/, '')}/v1/updates/check?channel=${encodeURIComponent(channel)}`;
301
+ const response = await fetch(url, {
286
302
  method: 'GET',
287
303
  headers: {
288
304
  'Content-Type': 'application/json',
305
+ 'X-API-Key': apiKey,
306
+ 'X-Device-ID': deviceInfo.deviceId,
289
307
  'X-Current-Version': currentVersion,
308
+ 'X-Platform': deviceInfo.platform,
309
+ 'X-App-Version': deviceInfo.appVersion,
290
310
  },
291
311
  });
292
312
  if (!response.ok) {
@@ -296,15 +316,19 @@ class NativeUpdatePluginWeb {
296
316
  };
297
317
  }
298
318
  const data = await response.json();
299
- const latestVersion = data.version || data.latestVersion;
300
- const isNewer = this.compareVersions(currentVersion, latestVersion) < 0;
319
+ if (!data.available) {
320
+ return {
321
+ available: false,
322
+ currentVersion,
323
+ };
324
+ }
301
325
  return {
302
- available: isNewer,
326
+ available: true,
303
327
  currentVersion,
304
- latestVersion: isNewer ? latestVersion : undefined,
305
- url: isNewer ? data.url || data.downloadUrl : undefined,
328
+ latestVersion: data.version,
329
+ url: data.downloadUrl,
306
330
  mandatory: data.mandatory,
307
- notes: data.notes || data.releaseNotes,
331
+ notes: data.releaseNotes,
308
332
  size: data.size,
309
333
  checksum: data.checksum,
310
334
  signature: data.signature,
@@ -422,9 +446,12 @@ class NativeUpdatePluginWeb {
422
446
  return downloadManager.getActiveDownloadCount();
423
447
  }
424
448
  /**
425
- * Compare two semantic versions
449
+ * Compare two semantic versions. Currently unused — the Laravel
450
+ * backend decides update availability server-side. Retained as a
451
+ * helper for advanced consumers that need client-side comparison.
426
452
  * Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
427
453
  */
454
+ // @ts-expect-error retained as a helper for advanced consumers
428
455
  compareVersions(v1, v2) {
429
456
  const parts1 = v1.split('.').map(Number);
430
457
  const parts2 = v2.split('.').map(Number);
@@ -623,4 +650,3 @@ const NativeUpdate = registerPlugin('NativeUpdate', {
623
650
  web: () => new NativeUpdatePluginWeb(),
624
651
  });
625
652
  export { NativeUpdate };
626
- //# sourceMappingURL=plugin.js.map
@@ -26,25 +26,30 @@ export class CryptoUtils {
26
26
  */
27
27
  static async verifySignature(data, signature, publicKey) {
28
28
  try {
29
- // Convert base64 strings to ArrayBuffer
29
+ // Accept either PEM (`-----BEGIN PUBLIC KEY-----...`) or raw base64
30
+ // SPKI. Two callers existed historically and they passed different
31
+ // formats; standardize on PEM-aware parsing here so both work.
32
+ const pemBody = publicKey
33
+ .replace(/-----BEGIN PUBLIC KEY-----/g, '')
34
+ .replace(/-----END PUBLIC KEY-----/g, '')
35
+ .replace(/\s+/g, '');
30
36
  const signatureBuffer = this.base64ToArrayBuffer(signature);
31
- const publicKeyBuffer = this.base64ToArrayBuffer(publicKey);
32
- // Import public key
37
+ const publicKeyBuffer = this.base64ToArrayBuffer(pemBody);
33
38
  const key = await crypto.subtle.importKey('spki', publicKeyBuffer, {
34
39
  name: 'RSA-PSS',
35
40
  hash: 'SHA-256',
36
41
  }, false, ['verify']);
37
- // Prepare data
38
42
  const encoder = new TextEncoder();
39
43
  const dataBuffer = typeof data === 'string' ? encoder.encode(data) : data;
40
- // Verify signature
41
44
  return await crypto.subtle.verify({
42
45
  name: 'RSA-PSS',
43
46
  saltLength: 32,
44
47
  }, key, signatureBuffer, dataBuffer);
45
48
  }
46
49
  catch (error) {
47
- console.error('Signature verification failed:', error);
50
+ // Verification failure must NOT throw — we want a clean false so
51
+ // upstream code can refuse the bundle without crashing.
52
+ void error;
48
53
  return false;
49
54
  }
50
55
  }
@@ -224,4 +229,3 @@ export class CryptoUtils {
224
229
  return btoa(binary);
225
230
  }
226
231
  }
227
- //# sourceMappingURL=crypto.js.map
@@ -140,4 +140,3 @@ export class Validator {
140
140
  return { valid: true };
141
141
  }
142
142
  }
143
- //# sourceMappingURL=validator.js.map
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Rollout + update-check types.
3
+ *
4
+ * These types describe staged-rollout configuration and the response
5
+ * shape the Laravel backend returns from `GET /v1/updates/check`.
6
+ * They were previously co-located with the Firestore schema; v3 of the
7
+ * plugin dropped Firestore as a backend, so the types live here in a
8
+ * backend-neutral location.
9
+ */
10
+ /**
11
+ * A point in time in a rollout config. Accepts ISO-8601 strings (what
12
+ * the Laravel backend returns), epoch milliseconds, or a `Date` for
13
+ * convenience when constructing config in code.
14
+ */
15
+ export type RolloutTimestamp = number | string | Date;
16
+ /**
17
+ * Convert a `RolloutTimestamp` to epoch milliseconds. Throws on a
18
+ * value that doesn't parse — callers are expected to validate before
19
+ * persisting rollout config.
20
+ */
21
+ export declare function toEpochMs(value: RolloutTimestamp): number;
22
+ /**
23
+ * Staged rollout configuration returned by the backend on update checks.
24
+ */
25
+ export interface RolloutConfig {
26
+ enabled: boolean;
27
+ percentage: number;
28
+ startTime: RolloutTimestamp;
29
+ endTime: RolloutTimestamp | null;
30
+ targetSegments?: RolloutTargetSegments;
31
+ schedule?: RolloutSchedule;
32
+ }
33
+ export interface RolloutTargetSegments {
34
+ platforms?: ('ios' | 'android' | 'web')[];
35
+ minAppVersion?: string;
36
+ maxAppVersion?: string;
37
+ deviceIds?: string[];
38
+ regions?: string[];
39
+ }
40
+ export interface RolloutSchedule {
41
+ type: 'immediate' | 'gradual' | 'scheduled';
42
+ gradualSteps?: number[];
43
+ gradualInterval?: number;
44
+ scheduledTime?: RolloutTimestamp;
45
+ }
46
+ /**
47
+ * Device information used for rollout eligibility checks. Sent to the
48
+ * backend on `/v1/updates/check` via `X-Device-ID`, `X-Platform`,
49
+ * `X-App-Version` headers and used locally by `RolloutChecker` for
50
+ * deterministic percentile bucketing.
51
+ */
52
+ export interface DeviceInfo {
53
+ deviceId: string;
54
+ platform: 'ios' | 'android' | 'web';
55
+ appVersion: string;
56
+ bundleVersion: string;
57
+ osVersion: string;
58
+ region?: string;
59
+ locale: string;
60
+ }
61
+ /**
62
+ * Normalized response from `GET /v1/updates/check`. The Laravel
63
+ * backend returns this exact shape (snake_case fields are mapped on
64
+ * the wire — this interface is the camelCase local form).
65
+ */
66
+ export interface UpdateCheckResponse {
67
+ updateAvailable: boolean;
68
+ version?: string;
69
+ bundleUrl?: string;
70
+ minNativeVersion?: string;
71
+ releaseNotes?: string;
72
+ signature?: string;
73
+ checksum?: string;
74
+ mandatory?: boolean;
75
+ size?: number;
76
+ delta?: {
77
+ available: boolean;
78
+ patchUrl?: string;
79
+ patchSize?: number;
80
+ patchChecksum?: string;
81
+ targetChecksum?: string;
82
+ };
83
+ rolloutEligible?: boolean;
84
+ rolloutReason?: string;
85
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Rollout + update-check types.
3
+ *
4
+ * These types describe staged-rollout configuration and the response
5
+ * shape the Laravel backend returns from `GET /v1/updates/check`.
6
+ * They were previously co-located with the Firestore schema; v3 of the
7
+ * plugin dropped Firestore as a backend, so the types live here in a
8
+ * backend-neutral location.
9
+ */
10
+ /**
11
+ * Convert a `RolloutTimestamp` to epoch milliseconds. Throws on a
12
+ * value that doesn't parse — callers are expected to validate before
13
+ * persisting rollout config.
14
+ */
15
+ export function toEpochMs(value) {
16
+ if (typeof value === 'number')
17
+ return value;
18
+ if (value instanceof Date)
19
+ return value.getTime();
20
+ const parsed = new Date(value).getTime();
21
+ if (Number.isNaN(parsed)) {
22
+ throw new Error(`Invalid rollout timestamp: ${String(value)}`);
23
+ }
24
+ return parsed;
25
+ }
package/dist/esm/web.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare class NativeUpdateWeb extends WebPlugin implements NativeUpdateCo
11
11
  private backgroundCheckInterval;
12
12
  private configManager;
13
13
  private isConfigured;
14
+ private downloadedBundleBytes;
14
15
  constructor();
15
16
  /**
16
17
  * Configuration and Core Methods
@@ -107,6 +108,13 @@ export declare class NativeUpdateWeb extends WebPlugin implements NativeUpdateCo
107
108
  * Import ECDSA public key from PEM format
108
109
  */
109
110
  private importPublicKeyECDSA;
111
+ /**
112
+ * Safe localStorage accessor. Returns `null` in environments where
113
+ * `localStorage` isn't available — SSR, Node-only test runners that
114
+ * stub `window` without storage, or sandboxed iframes. All callers
115
+ * MUST tolerate a `null` return and skip persistence.
116
+ */
117
+ private getLocalStorage;
110
118
  private loadStoredData;
111
119
  private saveStoredData;
112
120
  /**