native-update 1.4.9 → 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 (265) hide show
  1. package/Readme.md +35 -9
  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/BackgroundUpdatePlugin.kt +15 -0
  6. package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +23 -7
  7. package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +346 -31
  8. package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +32 -34
  9. package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +10 -1
  10. package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +18 -18
  11. package/cli/AGENTS.md +43 -0
  12. package/cli/CLAUDE.md +65 -0
  13. package/cli/commands/backend-create.js +56 -15
  14. package/cli/commands/bundle-create.js +39 -7
  15. package/cli/commands/monitor.js +72 -51
  16. package/cli/index.js +10 -24
  17. package/cli/package.json +11 -3
  18. package/dist/esm/app-review/app-review-manager.js +0 -1
  19. package/dist/esm/app-review/index.js +0 -1
  20. package/dist/esm/app-review/platform-review-handler.js +0 -1
  21. package/dist/esm/app-review/review-conditions-checker.js +0 -1
  22. package/dist/esm/app-review/review-rate-limiter.js +0 -1
  23. package/dist/esm/app-review/types.js +0 -1
  24. package/dist/esm/app-update/app-update-checker.js +0 -1
  25. package/dist/esm/app-update/app-update-installer.d.ts +1 -0
  26. package/dist/esm/app-update/app-update-installer.js +19 -8
  27. package/dist/esm/app-update/app-update-manager.js +0 -1
  28. package/dist/esm/app-update/app-update-notifier.js +0 -1
  29. package/dist/esm/app-update/index.js +0 -1
  30. package/dist/esm/app-update/platform-app-update.js +12 -3
  31. package/dist/esm/app-update/types.js +0 -1
  32. package/dist/esm/background-update/background-scheduler.js +0 -1
  33. package/dist/esm/background-update/index.js +0 -1
  34. package/dist/esm/background-update/notification-manager.js +0 -1
  35. package/dist/esm/config/support.js +0 -1
  36. package/dist/esm/core/analytics.d.ts +13 -0
  37. package/dist/esm/core/analytics.js +15 -1
  38. package/dist/esm/core/cache-manager.js +0 -1
  39. package/dist/esm/core/config.d.ts +6 -30
  40. package/dist/esm/core/config.js +1 -8
  41. package/dist/esm/core/errors.d.ts +1 -0
  42. package/dist/esm/core/errors.js +1 -1
  43. package/dist/esm/core/event-emitter.d.ts +0 -3
  44. package/dist/esm/core/event-emitter.js +8 -3
  45. package/dist/esm/core/logger.js +0 -1
  46. package/dist/esm/core/performance.js +0 -1
  47. package/dist/esm/core/plugin-manager.js +3 -1
  48. package/dist/esm/core/security.d.ts +22 -3
  49. package/dist/esm/core/security.js +46 -7
  50. package/dist/esm/definitions.d.ts +13 -40
  51. package/dist/esm/definitions.js +0 -1
  52. package/dist/esm/index.d.ts +3 -4
  53. package/dist/esm/index.js +3 -3
  54. package/dist/esm/live-update/bundle-manager.d.ts +8 -0
  55. package/dist/esm/live-update/bundle-manager.js +20 -1
  56. package/dist/esm/live-update/certificate-pinning.js +0 -1
  57. package/dist/esm/live-update/delta-processor.d.ts +1 -1
  58. package/dist/esm/live-update/delta-processor.js +3 -1
  59. package/dist/esm/live-update/download-manager.d.ts +36 -5
  60. package/dist/esm/live-update/download-manager.js +70 -24
  61. package/dist/esm/live-update/rollout-checker.d.ts +1 -1
  62. package/dist/esm/live-update/rollout-checker.js +5 -6
  63. package/dist/esm/live-update/update-manager.d.ts +12 -1
  64. package/dist/esm/live-update/update-manager.js +38 -11
  65. package/dist/esm/live-update/version-manager.d.ts +9 -12
  66. package/dist/esm/live-update/version-manager.js +40 -68
  67. package/dist/esm/plugin.js +82 -89
  68. package/dist/esm/security/crypto.js +11 -7
  69. package/dist/esm/security/validator.js +0 -1
  70. package/dist/esm/types/rollout.d.ts +85 -0
  71. package/dist/esm/types/rollout.js +25 -0
  72. package/dist/esm/web.d.ts +26 -1
  73. package/dist/esm/web.js +214 -75
  74. package/dist/plugin.cjs.js +1 -2
  75. package/dist/plugin.esm.js +1 -2
  76. package/dist/plugin.js +2 -3
  77. package/docs/AGENTS.md +38 -0
  78. package/docs/CHANGELOG.md +275 -0
  79. package/docs/CLAUDE.md +101 -0
  80. package/docs/MANUAL-TASKS.md +17 -0
  81. package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
  82. package/docs/MIGRATION.md +170 -0
  83. package/docs/PACKAGES.md +167 -0
  84. package/docs/PERFORMANCE.md +110 -0
  85. package/docs/PROJECT_COMPLETION_TRACKER.md +4 -35
  86. package/docs/README.md +12 -2
  87. package/docs/REMAINING_FEATURES.md +123 -76
  88. package/docs/ROADMAP.md +200 -120
  89. package/docs/SECURITY-IF-REPO-PUBLIC.md +232 -0
  90. package/docs/TESTING_REQUIREMENTS.md +1 -112
  91. package/docs/ai-knowledge-base/00-project-profile.md +38 -0
  92. package/docs/ai-knowledge-base/01-features.md +46 -0
  93. package/docs/ai-knowledge-base/02-routes.md +72 -0
  94. package/docs/ai-knowledge-base/03-tech-stack.md +57 -0
  95. package/docs/ai-knowledge-base/README.md +23 -0
  96. package/docs/analytics-tracking.md +37 -0
  97. package/docs/capacitor-official-plugins.md +46 -0
  98. package/docs/capawesome-plugins.md +29 -0
  99. package/docs/deployment/HOSTINGER_DEPLOY.md +370 -0
  100. package/docs/deployment/PRODUCTION-DEPLOY-CHECKLIST.md +184 -0
  101. package/docs/docs-site/plan.md +201 -0
  102. package/docs/docs-site/scope.md +127 -0
  103. package/docs/docs-site/tracker.json +235 -0
  104. package/docs/error-handling-tracking.md +37 -0
  105. package/docs/features/laravel-nova-backend/ASSESSMENT-SUMMARY.md +96 -0
  106. package/docs/features/laravel-nova-backend/IMPLEMENTATION-PLAN.md +504 -0
  107. package/docs/features/laravel-nova-backend/progress-tracker.json +184 -0
  108. package/docs/guides/testing-guide.md +15 -41
  109. package/docs/package-audit.md +41 -0
  110. package/docs/play-store/ASO-METADATA.md +5 -0
  111. package/docs/play-store/DATA-SAFETY.md +5 -0
  112. package/docs/play-store/DECLARATIONS.md +5 -0
  113. package/docs/play-store/README.md +27 -0
  114. package/docs/play-store/RELEASE-NOTES.md +99 -0
  115. package/docs/play-store/RELEASE-STEPS.md +6 -0
  116. package/docs/play-store/STORE-LISTING.md +7 -0
  117. package/docs/play-store/SUBMISSION-CHECKLIST.md +5 -0
  118. package/docs/production-readiness.md +1 -1
  119. package/docs/project-audit/2026-05-16/00-FINALIZATION-PLAN.md +123 -0
  120. package/docs/project-audit/2026-05-16/01-BATCH-PLAN.md +227 -0
  121. package/docs/project-audit/2026-05-16/02-AUDIT-FINDINGS.md +144 -0
  122. package/docs/project-audit/2026-05-16/sessions/batch-01.md +42 -0
  123. package/docs/project-audit/2026-05-16/sessions/batch-02.md +74 -0
  124. package/docs/project-audit/2026-05-16/sessions/batch-03.md +71 -0
  125. package/docs/project-audit/2026-05-16/sessions/batch-04.md +88 -0
  126. package/docs/project-audit/2026-05-16/sessions/batch-05.md +63 -0
  127. package/docs/project-audit/2026-05-16/sessions/batch-06.md +103 -0
  128. package/docs/project-audit/2026-05-16/sessions/batch-07.md +61 -0
  129. package/docs/project-audit/2026-05-16/sessions/batch-08.md +101 -0
  130. package/docs/project-audit/2026-05-16/sessions/batch-09.md +105 -0
  131. package/docs/project-audit/2026-05-16/sessions/batch-10.md +102 -0
  132. package/docs/project-audit/AUDIT-HISTORY.md +74 -0
  133. package/docs/project-audit/BATCH-PLAN.md +168 -0
  134. package/docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.md +73 -0
  135. package/docs/project-audit/PRODUCTION-READINESS-AUDIT-2026-05-08.md +275 -0
  136. package/docs/project-finalization/00-tracker.json +60 -0
  137. package/docs/project-knowledge-base/01-system-overview.md +227 -0
  138. package/docs/project-knowledge-base/02-routes-pages-forms-users.md +352 -0
  139. package/docs/project-knowledge-base/03-tech-stack-modules-services.md +379 -0
  140. package/docs/project-knowledge-base/04-data-models-integrations.md +244 -0
  141. package/docs/project-knowledge-base/05-docs-corpus-inventory.md +256 -0
  142. package/docs/project-knowledge-base/06-operations-testing-legal-content.md +208 -0
  143. package/docs/project-knowledge-base/README.md +103 -0
  144. package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-05-19.md +81 -0
  145. package/docs/rules-tracker.md +102 -0
  146. package/docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md +116 -0
  147. package/docs/seo-aeo-rules.json +3043 -0
  148. package/docs/social-media-marketing/README.md +81 -0
  149. package/docs/social-media-marketing/posts-batch-01.md +613 -0
  150. package/docs/social-media-marketing/posts-batch-02.md +528 -0
  151. package/docs/social-media-marketing/whatsapp-messages-batch-01.md +458 -0
  152. package/docs/social-media-marketing/whatsapp-status-batch-01.md +355 -0
  153. package/docs/tracking/finalization-2026-05-16-tracker.json +346 -0
  154. package/docs/tracking/full-audit-tracker.json +121 -0
  155. package/docs/tracking/production-readiness-audit-tracker.json +429 -0
  156. package/docs/tracking/seo-checklist-tracker.json +333 -0
  157. package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +66 -9
  158. package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +306 -52
  159. package/ios/Plugin/NativeUpdatePlugin.swift +22 -9
  160. package/ios/Plugin/Security/SecurityManager.swift +13 -14
  161. package/package.json +37 -38
  162. package/cli/node_modules/.yarn-integrity +0 -16
  163. package/cli/node_modules/commander/LICENSE +0 -22
  164. package/cli/node_modules/commander/Readme.md +0 -1148
  165. package/cli/node_modules/commander/esm.mjs +0 -16
  166. package/cli/node_modules/commander/index.js +0 -26
  167. package/cli/node_modules/commander/lib/argument.js +0 -145
  168. package/cli/node_modules/commander/lib/command.js +0 -2179
  169. package/cli/node_modules/commander/lib/error.js +0 -43
  170. package/cli/node_modules/commander/lib/help.js +0 -462
  171. package/cli/node_modules/commander/lib/option.js +0 -329
  172. package/cli/node_modules/commander/lib/suggestSimilar.js +0 -100
  173. package/cli/node_modules/commander/package-support.json +0 -16
  174. package/cli/node_modules/commander/package.json +0 -80
  175. package/cli/node_modules/commander/typings/esm.d.mts +0 -3
  176. package/cli/node_modules/commander/typings/index.d.ts +0 -884
  177. package/cli/yarn.lock +0 -8
  178. package/dist/esm/__tests__/bundle-manager.test.d.ts +0 -1
  179. package/dist/esm/__tests__/bundle-manager.test.js +0 -151
  180. package/dist/esm/__tests__/bundle-manager.test.js.map +0 -1
  181. package/dist/esm/__tests__/config.test.d.ts +0 -1
  182. package/dist/esm/__tests__/config.test.js +0 -70
  183. package/dist/esm/__tests__/config.test.js.map +0 -1
  184. package/dist/esm/__tests__/delta-processor.test.d.ts +0 -1
  185. package/dist/esm/__tests__/delta-processor.test.js +0 -77
  186. package/dist/esm/__tests__/delta-processor.test.js.map +0 -1
  187. package/dist/esm/__tests__/firestore-schema.test.d.ts +0 -1
  188. package/dist/esm/__tests__/firestore-schema.test.js +0 -74
  189. package/dist/esm/__tests__/firestore-schema.test.js.map +0 -1
  190. package/dist/esm/__tests__/integration.test.d.ts +0 -1
  191. package/dist/esm/__tests__/integration.test.js +0 -78
  192. package/dist/esm/__tests__/integration.test.js.map +0 -1
  193. package/dist/esm/__tests__/manifest-reader.test.d.ts +0 -1
  194. package/dist/esm/__tests__/manifest-reader.test.js +0 -271
  195. package/dist/esm/__tests__/manifest-reader.test.js.map +0 -1
  196. package/dist/esm/__tests__/rollout-checker.test.d.ts +0 -1
  197. package/dist/esm/__tests__/rollout-checker.test.js +0 -210
  198. package/dist/esm/__tests__/rollout-checker.test.js.map +0 -1
  199. package/dist/esm/__tests__/security.test.d.ts +0 -1
  200. package/dist/esm/__tests__/security.test.js +0 -54
  201. package/dist/esm/__tests__/security.test.js.map +0 -1
  202. package/dist/esm/__tests__/version-manager.test.d.ts +0 -1
  203. package/dist/esm/__tests__/version-manager.test.js +0 -45
  204. package/dist/esm/__tests__/version-manager.test.js.map +0 -1
  205. package/dist/esm/app-review/app-review-manager.js.map +0 -1
  206. package/dist/esm/app-review/index.js.map +0 -1
  207. package/dist/esm/app-review/platform-review-handler.js.map +0 -1
  208. package/dist/esm/app-review/review-conditions-checker.js.map +0 -1
  209. package/dist/esm/app-review/review-rate-limiter.js.map +0 -1
  210. package/dist/esm/app-review/types.js.map +0 -1
  211. package/dist/esm/app-update/app-update-checker.js.map +0 -1
  212. package/dist/esm/app-update/app-update-installer.js.map +0 -1
  213. package/dist/esm/app-update/app-update-manager.js.map +0 -1
  214. package/dist/esm/app-update/app-update-notifier.js.map +0 -1
  215. package/dist/esm/app-update/index.js.map +0 -1
  216. package/dist/esm/app-update/platform-app-update.js.map +0 -1
  217. package/dist/esm/app-update/types.js.map +0 -1
  218. package/dist/esm/background-update/background-scheduler.js.map +0 -1
  219. package/dist/esm/background-update/index.js.map +0 -1
  220. package/dist/esm/background-update/notification-manager.js.map +0 -1
  221. package/dist/esm/config/support.js.map +0 -1
  222. package/dist/esm/core/analytics.js.map +0 -1
  223. package/dist/esm/core/cache-manager.js.map +0 -1
  224. package/dist/esm/core/config.js.map +0 -1
  225. package/dist/esm/core/errors.js.map +0 -1
  226. package/dist/esm/core/event-emitter.js.map +0 -1
  227. package/dist/esm/core/logger.js.map +0 -1
  228. package/dist/esm/core/performance.js.map +0 -1
  229. package/dist/esm/core/plugin-manager.js.map +0 -1
  230. package/dist/esm/core/security.js.map +0 -1
  231. package/dist/esm/definitions.js.map +0 -1
  232. package/dist/esm/firestore/firestore-client.d.ts +0 -109
  233. package/dist/esm/firestore/firestore-client.js +0 -264
  234. package/dist/esm/firestore/firestore-client.js.map +0 -1
  235. package/dist/esm/firestore/index.d.ts +0 -11
  236. package/dist/esm/firestore/index.js +0 -11
  237. package/dist/esm/firestore/index.js.map +0 -1
  238. package/dist/esm/firestore/manifest-reader.d.ts +0 -87
  239. package/dist/esm/firestore/manifest-reader.js +0 -294
  240. package/dist/esm/firestore/manifest-reader.js.map +0 -1
  241. package/dist/esm/firestore/schema.d.ts +0 -507
  242. package/dist/esm/firestore/schema.js +0 -73
  243. package/dist/esm/firestore/schema.js.map +0 -1
  244. package/dist/esm/index.js.map +0 -1
  245. package/dist/esm/live-update/bundle-manager.js.map +0 -1
  246. package/dist/esm/live-update/certificate-pinning.js.map +0 -1
  247. package/dist/esm/live-update/delta-processor.js.map +0 -1
  248. package/dist/esm/live-update/download-manager.js.map +0 -1
  249. package/dist/esm/live-update/rollout-checker.js.map +0 -1
  250. package/dist/esm/live-update/update-manager.js.map +0 -1
  251. package/dist/esm/live-update/version-manager.js.map +0 -1
  252. package/dist/esm/plugin.js.map +0 -1
  253. package/dist/esm/security/crypto.js.map +0 -1
  254. package/dist/esm/security/validator.js.map +0 -1
  255. package/dist/esm/web.js.map +0 -1
  256. package/dist/plugin.cjs.js.map +0 -1
  257. package/dist/plugin.esm.js.map +0 -1
  258. package/dist/plugin.js.map +0 -1
  259. package/docs/FIREBASE_INTEGRATION_TRACKER.md +0 -321
  260. package/docs/FIREBASE_QUERIES_AND_INDEXES_AUDIT.md +0 -221
  261. package/docs/examples/firebase-backend-example.md +0 -27
  262. package/docs/guides/no-cost-backend-implementation-plan.md +0 -77
  263. package/docs/guides/no-cost-firestore-google-drive-backend.md +0 -60
  264. package/docs/play-console-rejection-rules.json +0 -428
  265. /package/docs/{MARKETING_WEBSITE_PLAN.md → archive/MARKETING_WEBSITE_PLAN.md} +0 -0
package/dist/esm/web.js CHANGED
@@ -1,6 +1,11 @@
1
1
  import { WebPlugin } from '@capacitor/core';
2
2
  import { SyncStatus, BundleStatus, UpdateErrorCode } from './definitions';
3
3
  import { ConfigManager } from './core/config';
4
+ import { Logger } from './core/logger';
5
+ // Module-level logger so we never leak sensitive headers / tokens
6
+ // through raw console.* calls. The Logger sanitises payloads (paths,
7
+ // keys, secrets) and respects the runtime log-level switch.
8
+ const logger = new Logger('NativeUpdateWeb');
4
9
  export class NativeUpdateWeb extends WebPlugin {
5
10
  constructor() {
6
11
  super();
@@ -17,6 +22,10 @@ export class NativeUpdateWeb extends WebPlugin {
17
22
  };
18
23
  this.backgroundCheckInterval = null;
19
24
  this.isConfigured = false;
25
+ // Single-slot cache of the most recently downloaded bundle's bytes so a
26
+ // follow-up validateUpdate() on the same URL doesn't re-download the
27
+ // whole bundle just to hash it. Overwritten on each download().
28
+ this.downloadedBundleBytes = null;
20
29
  this.configManager = ConfigManager.getInstance();
21
30
  this.loadStoredData();
22
31
  this.incrementLaunchCount();
@@ -84,8 +93,7 @@ export class NativeUpdateWeb extends WebPlugin {
84
93
  }
85
94
  }
86
95
  async download(options) {
87
- var _a;
88
- // Validate URL
96
+ var _a, _b;
89
97
  if (!options.url.startsWith('https://') &&
90
98
  ((_a = this.config.security) === null || _a === void 0 ? void 0 : _a.enforceHttps) !== false) {
91
99
  throw this.createError(UpdateErrorCode.INSECURE_URL, 'Download URL must use HTTPS');
@@ -96,44 +104,82 @@ export class NativeUpdateWeb extends WebPlugin {
96
104
  version: options.version,
97
105
  path: options.url,
98
106
  downloadTime: Date.now(),
99
- size: 0, // Would be calculated during actual download
107
+ size: 0,
100
108
  status: BundleStatus.DOWNLOADING,
101
109
  checksum: options.checksum,
102
110
  signature: options.signature,
103
111
  verified: false,
104
112
  };
105
113
  this.bundles.set(bundleId, bundle);
106
- // Simulate download progress
107
- for (let i = 0; i <= 100; i += 10) {
108
- await this.notifyListeners('downloadProgress', {
109
- percent: i,
110
- bytesDownloaded: i * 1000,
111
- totalBytes: 10000,
112
- bundleId,
113
- });
114
- await new Promise((resolve) => setTimeout(resolve, 100));
114
+ // Actually fetch the bundle so we hash REAL bytes, not the bundleId
115
+ // string. This was a P0: validateChecksum used to receive `bundleId`
116
+ // and silently fail (or pass) regardless of the bundle contents.
117
+ let bundleBytes;
118
+ try {
119
+ const response = await fetch(options.url);
120
+ if (!response.ok) {
121
+ throw new Error(`HTTP ${response.status}`);
122
+ }
123
+ const reader = (_b = response.body) === null || _b === void 0 ? void 0 : _b.getReader();
124
+ const contentLength = Number(response.headers.get('content-length') || 0);
125
+ if (reader) {
126
+ const chunks = [];
127
+ let received = 0;
128
+ while (true) {
129
+ const { done, value } = await reader.read();
130
+ if (done)
131
+ break;
132
+ chunks.push(value);
133
+ received += value.length;
134
+ if (contentLength > 0) {
135
+ await this.notifyListeners('downloadProgress', {
136
+ percent: Math.round((received / contentLength) * 100),
137
+ bytesDownloaded: received,
138
+ totalBytes: contentLength,
139
+ bundleId,
140
+ });
141
+ }
142
+ }
143
+ const combined = new Uint8Array(received);
144
+ let offset = 0;
145
+ for (const chunk of chunks) {
146
+ combined.set(chunk, offset);
147
+ offset += chunk.length;
148
+ }
149
+ bundleBytes = combined.buffer;
150
+ }
151
+ else {
152
+ bundleBytes = await response.arrayBuffer();
153
+ }
154
+ }
155
+ catch (error) {
156
+ bundle.status = BundleStatus.FAILED;
157
+ this.bundles.delete(bundleId);
158
+ throw this.createError(UpdateErrorCode.NETWORK_ERROR, `Failed to download bundle: ${error.message}`);
115
159
  }
116
- // Verify bundle integrity if checksum/signature provided
117
- let verified = true;
160
+ bundle.size = bundleBytes.byteLength;
161
+ let verified = false;
118
162
  if (options.checksum) {
119
- const checksumValid = await this.validateChecksum(bundleId, options.checksum);
163
+ const checksumValid = await this.validateChecksum(bundleBytes, options.checksum);
120
164
  if (!checksumValid) {
121
165
  bundle.status = BundleStatus.FAILED;
122
166
  this.bundles.delete(bundleId);
123
167
  throw this.createError(UpdateErrorCode.CHECKSUM_ERROR, 'Bundle checksum verification failed');
124
168
  }
169
+ verified = true;
125
170
  }
126
171
  if (options.signature) {
127
- const signatureValid = await this.validateSignature(bundleId, options.signature);
172
+ const signatureValid = await this.validateSignature(bundleBytes, options.signature);
128
173
  if (!signatureValid) {
129
174
  bundle.status = BundleStatus.FAILED;
130
175
  this.bundles.delete(bundleId);
131
176
  throw this.createError(UpdateErrorCode.SIGNATURE_ERROR, 'Bundle signature verification failed');
132
177
  }
178
+ verified = true;
133
179
  }
134
- // Update bundle status
135
180
  bundle.status = BundleStatus.READY;
136
181
  bundle.verified = verified;
182
+ this.downloadedBundleBytes = { path: options.url, bytes: bundleBytes };
137
183
  this.saveStoredData();
138
184
  await this.notifyListeners('updateStateChanged', {
139
185
  status: bundle.status,
@@ -146,24 +192,25 @@ export class NativeUpdateWeb extends WebPlugin {
146
192
  if (!this.bundles.has(bundle.bundleId)) {
147
193
  throw this.createError(UpdateErrorCode.UNKNOWN_ERROR, 'Bundle not found');
148
194
  }
195
+ const stored = this.bundles.get(bundle.bundleId);
196
+ if (!stored.verified) {
197
+ throw this.createError(UpdateErrorCode.SIGNATURE_ERROR, `Refusing to activate unverified bundle ${bundle.bundleId}`);
198
+ }
149
199
  const previousBundle = this.currentBundle;
150
- this.currentBundle = bundle;
200
+ this.currentBundle = stored;
151
201
  this.currentBundle.status = BundleStatus.ACTIVE;
152
- // Update previous bundle status
153
202
  if (previousBundle) {
154
203
  previousBundle.status = BundleStatus.READY;
155
204
  }
156
205
  this.saveStoredData();
157
206
  }
158
207
  async reload() {
159
- // console.log('Web: Reloading application...');
160
208
  // In web, we can reload the page
161
209
  if (typeof window !== 'undefined') {
162
210
  window.location.reload();
163
211
  }
164
212
  }
165
213
  async reset() {
166
- // console.log('Web: Resetting to original bundle...');
167
214
  this.currentBundle = null;
168
215
  this.bundles.clear();
169
216
  this.saveStoredData();
@@ -197,7 +244,6 @@ export class NativeUpdateWeb extends WebPlugin {
197
244
  this.saveStoredData();
198
245
  }
199
246
  async notifyAppReady() {
200
- // console.log('Web: App ready notification received');
201
247
  if (this.currentBundle) {
202
248
  this.currentBundle.verified = true;
203
249
  this.saveStoredData();
@@ -226,25 +272,49 @@ export class NativeUpdateWeb extends WebPlugin {
226
272
  this.config.liveUpdate.channel = channel;
227
273
  this.saveConfiguration();
228
274
  }
275
+ /**
276
+ * @deprecated since v2. See NativeUpdatePluginWeb.setUpdateUrl in plugin.ts.
277
+ * Update URL is locked after initialize(); this method is a no-op.
278
+ */
229
279
  async setUpdateUrl(url) {
280
+ void url;
281
+ logger.warn('setUpdateUrl() is deprecated and ignored. Update URL is locked after initialize().');
282
+ return;
283
+ }
284
+ async validateUpdate(options) {
230
285
  var _a;
231
- if (!url.startsWith('https://') &&
232
- ((_a = this.config.security) === null || _a === void 0 ? void 0 : _a.enforceHttps) !== false) {
233
- throw this.createError(UpdateErrorCode.INSECURE_URL, 'Update URL must use HTTPS');
286
+ // bundlePath on web is the bundle URL (we don't persist bytes locally
287
+ // here). Reuse the bytes from the last download() when the URL matches;
288
+ // only re-fetch when validating a bundle we never downloaded.
289
+ let bundleBytes;
290
+ if (((_a = this.downloadedBundleBytes) === null || _a === void 0 ? void 0 : _a.path) === options.bundlePath) {
291
+ bundleBytes = this.downloadedBundleBytes.bytes;
234
292
  }
235
- if (!this.config.liveUpdate) {
236
- this.config.liveUpdate = {};
293
+ else {
294
+ try {
295
+ const response = await fetch(options.bundlePath);
296
+ if (!response.ok) {
297
+ return {
298
+ isValid: false,
299
+ error: `Failed to fetch bundle for validation: HTTP ${response.status}`,
300
+ };
301
+ }
302
+ bundleBytes = await response.arrayBuffer();
303
+ }
304
+ catch (error) {
305
+ return {
306
+ isValid: false,
307
+ error: `Failed to fetch bundle for validation: ${error.message}`,
308
+ };
309
+ }
237
310
  }
238
- this.config.liveUpdate.serverUrl = url;
239
- this.saveConfiguration();
240
- }
241
- async validateUpdate(options) {
242
- // Simulate validation
243
- const checksumValid = await this.validateChecksum(options.bundlePath, options.checksum);
311
+ const checksumValid = await this.validateChecksum(bundleBytes, options.checksum);
244
312
  const signatureValid = options.signature
245
- ? await this.validateSignature(options.bundlePath, options.signature)
313
+ ? await this.validateSignature(bundleBytes, options.signature)
314
+ : true;
315
+ const sizeValid = options.maxSize
316
+ ? bundleBytes.byteLength <= options.maxSize
246
317
  : true;
247
- const sizeValid = options.maxSize ? true : true; // Would check actual size
248
318
  const isValid = checksumValid && signatureValid && sizeValid;
249
319
  return {
250
320
  isValid,
@@ -349,7 +419,6 @@ export class NativeUpdateWeb extends WebPlugin {
349
419
  * App Update Methods
350
420
  */
351
421
  async getAppUpdateInfo() {
352
- // console.log('Web: App updates not supported on web platform');
353
422
  return {
354
423
  updateAvailable: false,
355
424
  currentVersion: '1.0.0',
@@ -381,6 +450,7 @@ export class NativeUpdateWeb extends WebPlugin {
381
450
  * App Review Methods
382
451
  */
383
452
  async requestReview() {
453
+ var _a;
384
454
  const canRequest = await this.canRequestReview();
385
455
  if (!canRequest.canRequest) {
386
456
  return {
@@ -388,10 +458,9 @@ export class NativeUpdateWeb extends WebPlugin {
388
458
  error: canRequest.reason,
389
459
  };
390
460
  }
391
- // console.log('Web: Showing review request fallback');
392
461
  // Update last request time
393
462
  this.lastReviewRequest = Date.now();
394
- localStorage.setItem('native-update-last-review', this.lastReviewRequest.toString());
463
+ (_a = this.getLocalStorage()) === null || _a === void 0 ? void 0 : _a.setItem('native-update-last-review', this.lastReviewRequest.toString());
395
464
  // In web, we could show a custom modal or redirect to a review page
396
465
  // Note: Web platform cannot display native in-app review dialogs
397
466
  // We can offer a fallback redirect, but `displayed` reflects native capability
@@ -447,7 +516,6 @@ export class NativeUpdateWeb extends WebPlugin {
447
516
  * Background Update Methods
448
517
  */
449
518
  async enableBackgroundUpdates(config) {
450
- // console.log('Web: Enabling background updates', config);
451
519
  if (!this.config.backgroundUpdate) {
452
520
  this.config.backgroundUpdate = config;
453
521
  }
@@ -464,7 +532,6 @@ export class NativeUpdateWeb extends WebPlugin {
464
532
  this.saveConfiguration();
465
533
  }
466
534
  async disableBackgroundUpdates() {
467
- // console.log('Web: Disabling background updates');
468
535
  if (this.backgroundCheckInterval) {
469
536
  clearInterval(this.backgroundCheckInterval);
470
537
  this.backgroundCheckInterval = null;
@@ -481,7 +548,6 @@ export class NativeUpdateWeb extends WebPlugin {
481
548
  return Object.assign({}, this.backgroundUpdateStatus);
482
549
  }
483
550
  async scheduleBackgroundCheck(interval) {
484
- // console.log('Web: Scheduling background check with interval', interval);
485
551
  if (this.backgroundCheckInterval) {
486
552
  clearInterval(this.backgroundCheckInterval);
487
553
  }
@@ -494,7 +560,6 @@ export class NativeUpdateWeb extends WebPlugin {
494
560
  this.backgroundUpdateStatus.nextCheckTime = Date.now() + interval;
495
561
  }
496
562
  async triggerBackgroundCheck() {
497
- // console.log('Web: Triggering background check');
498
563
  if (!this.backgroundUpdateStatus.enabled) {
499
564
  return {
500
565
  success: false,
@@ -546,7 +611,6 @@ export class NativeUpdateWeb extends WebPlugin {
546
611
  }
547
612
  }
548
613
  async setNotificationPreferences(preferences) {
549
- // console.log('Web: Setting notification preferences', preferences);
550
614
  if (!this.config.backgroundUpdate) {
551
615
  this.config.backgroundUpdate = {};
552
616
  }
@@ -625,7 +689,7 @@ export class NativeUpdateWeb extends WebPlugin {
625
689
  return true;
626
690
  }
627
691
  catch (error) {
628
- console.error('Web: Failed to send notification', error);
692
+ logger.error('Failed to send notification', error);
629
693
  return false;
630
694
  }
631
695
  }
@@ -657,8 +721,7 @@ export class NativeUpdateWeb extends WebPlugin {
657
721
  */
658
722
  async validateChecksum(data, expectedChecksum) {
659
723
  if (!expectedChecksum) {
660
- // If no checksum provided, skip validation
661
- return true;
724
+ throw new Error('Checksum is required but was not provided');
662
725
  }
663
726
  try {
664
727
  let dataBuffer;
@@ -677,7 +740,7 @@ export class NativeUpdateWeb extends WebPlugin {
677
740
  // Compare checksums (case-insensitive)
678
741
  const isValid = hashHex.toLowerCase() === expectedChecksum.toLowerCase();
679
742
  if (!isValid) {
680
- console.warn('Checksum validation failed', {
743
+ logger.warn('Checksum validation failed', {
681
744
  expected: expectedChecksum.toLowerCase(),
682
745
  actual: hashHex.toLowerCase(),
683
746
  });
@@ -685,28 +748,34 @@ export class NativeUpdateWeb extends WebPlugin {
685
748
  return isValid;
686
749
  }
687
750
  catch (error) {
688
- console.error('Checksum validation error:', error);
751
+ logger.error('Checksum validation error', error);
689
752
  return false;
690
753
  }
691
754
  }
692
755
  /**
693
- * Validate digital signature using RSA-PSS or ECDSA
756
+ * Validate digital signature using RSA-PSS or ECDSA.
757
+ *
758
+ * Fails closed: if the host app configured a public key or
759
+ * requireSignature, a missing signature throws. Silent-pass was a P0 gap.
760
+ *
694
761
  * @param data - The data that was signed (as string path or ArrayBuffer)
695
762
  * @param signature - The signature to verify (base64 encoded)
696
763
  */
697
764
  async validateSignature(data, signature) {
698
- var _a;
765
+ var _a, _b;
766
+ const publicKeyPem = ((_a = this.config.liveUpdate) === null || _a === void 0 ? void 0 : _a.publicKey) ||
767
+ this.config.publicKey;
768
+ const requireSignature = ((_b = this.config.liveUpdate) === null || _b === void 0 ? void 0 : _b.requireSignature) ||
769
+ this.config.requireSignature === true;
770
+ const signatureRequired = !!publicKeyPem || requireSignature;
699
771
  if (!signature) {
700
- // If no signature provided, skip validation
772
+ if (signatureRequired) {
773
+ throw new Error('Signature is required but was not provided');
774
+ }
701
775
  return true;
702
776
  }
703
- // Get public key from config (can be in liveUpdate.publicKey or top-level config)
704
- const publicKeyPem = ((_a = this.config.liveUpdate) === null || _a === void 0 ? void 0 : _a.publicKey) ||
705
- this.config.publicKey;
706
777
  if (!publicKeyPem) {
707
- // If no public key configured, skip signature validation
708
- console.warn('No public key configured for signature validation');
709
- return true;
778
+ throw new Error('Public key not configured for signature validation');
710
779
  }
711
780
  try {
712
781
  let dataBuffer;
@@ -726,12 +795,12 @@ export class NativeUpdateWeb extends WebPlugin {
726
795
  saltLength: 32,
727
796
  }, publicKey, signatureBuffer, dataBuffer);
728
797
  if (!isValid) {
729
- console.warn('Signature validation failed');
798
+ logger.warn('Signature validation failed');
730
799
  }
731
800
  return isValid;
732
801
  }
733
802
  catch (error) {
734
- console.error('Signature validation error:', error);
803
+ logger.error('Signature validation error', error);
735
804
  // Try ECDSA as fallback
736
805
  return this.validateSignatureECDSA(data, signature);
737
806
  }
@@ -744,7 +813,10 @@ export class NativeUpdateWeb extends WebPlugin {
744
813
  const publicKeyPem = ((_a = this.config.liveUpdate) === null || _a === void 0 ? void 0 : _a.publicKey) ||
745
814
  this.config.publicKey;
746
815
  if (!publicKeyPem) {
747
- return true;
816
+ // ECDSA is only reached as a fallback from validateSignature, which
817
+ // already confirmed a key is configured. Reaching here without one
818
+ // is a logic error — fail closed rather than silently passing.
819
+ return false;
748
820
  }
749
821
  try {
750
822
  let dataBuffer;
@@ -763,7 +835,7 @@ export class NativeUpdateWeb extends WebPlugin {
763
835
  return isValid;
764
836
  }
765
837
  catch (error) {
766
- console.error('ECDSA signature validation error:', error);
838
+ logger.error('ECDSA signature validation error', error);
767
839
  return false;
768
840
  }
769
841
  }
@@ -796,55 +868,109 @@ export class NativeUpdateWeb extends WebPlugin {
796
868
  namedCurve: 'P-256',
797
869
  }, false, ['verify']);
798
870
  }
871
+ /**
872
+ * Safe localStorage accessor. Returns `null` in environments where
873
+ * `localStorage` isn't available — SSR, Node-only test runners that
874
+ * stub `window` without storage, or sandboxed iframes. All callers
875
+ * MUST tolerate a `null` return and skip persistence.
876
+ */
877
+ getLocalStorage() {
878
+ try {
879
+ if (typeof window === 'undefined')
880
+ return null;
881
+ if (typeof window.localStorage === 'undefined')
882
+ return null;
883
+ return window.localStorage;
884
+ }
885
+ catch (_a) {
886
+ // Some browsers throw on localStorage access in private mode.
887
+ return null;
888
+ }
889
+ }
799
890
  loadStoredData() {
800
- // Load configuration
801
- const storedConfig = localStorage.getItem('native-update-config');
891
+ const storage = this.getLocalStorage();
892
+ if (!storage)
893
+ return;
894
+ // Load configuration — note that any previously-persisted copy was
895
+ // pre-v2 and may contain an apiKey/publicKey in plaintext. We still
896
+ // read it to preserve non-sensitive state (channel, etc.) but secrets
897
+ // are stripped on save; an old cache wipes itself on first save.
898
+ const storedConfig = storage.getItem('native-update-config');
802
899
  if (storedConfig) {
803
900
  this.config = JSON.parse(storedConfig);
804
901
  }
805
902
  // Load bundles
806
- const storedBundles = localStorage.getItem('native-update-bundles');
903
+ const storedBundles = storage.getItem('native-update-bundles');
807
904
  if (storedBundles) {
808
905
  const bundlesArray = JSON.parse(storedBundles);
809
906
  bundlesArray.forEach((bundle) => this.bundles.set(bundle.bundleId, bundle));
810
907
  }
811
908
  // Load current bundle
812
- const storedCurrent = localStorage.getItem('native-update-current');
909
+ const storedCurrent = storage.getItem('native-update-current');
813
910
  if (storedCurrent) {
814
911
  this.currentBundle = JSON.parse(storedCurrent);
815
912
  }
816
913
  // Load last review request time
817
- const storedLastReview = localStorage.getItem('native-update-last-review');
914
+ const storedLastReview = storage.getItem('native-update-last-review');
818
915
  if (storedLastReview) {
819
916
  this.lastReviewRequest = parseInt(storedLastReview, 10);
820
917
  }
821
918
  // Load launch count
822
- const storedLaunchCount = localStorage.getItem('native-update-launch-count');
919
+ const storedLaunchCount = storage.getItem('native-update-launch-count');
823
920
  if (storedLaunchCount) {
824
921
  this.launchCount = parseInt(storedLaunchCount, 10);
825
922
  }
826
923
  }
827
924
  saveStoredData() {
828
- localStorage.setItem('native-update-bundles', JSON.stringify(Array.from(this.bundles.values())));
925
+ const storage = this.getLocalStorage();
926
+ if (!storage)
927
+ return;
928
+ storage.setItem('native-update-bundles', JSON.stringify(Array.from(this.bundles.values())));
829
929
  if (this.currentBundle) {
830
- localStorage.setItem('native-update-current', JSON.stringify(this.currentBundle));
930
+ storage.setItem('native-update-current', JSON.stringify(this.currentBundle));
931
+ }
932
+ }
933
+ sanitizeConfigForPersistence(raw) {
934
+ if (!raw || typeof raw !== 'object')
935
+ return raw;
936
+ const clone = JSON.parse(JSON.stringify(raw));
937
+ for (const path of NativeUpdateWeb.SENSITIVE_CONFIG_PATHS) {
938
+ const segments = path.split('.');
939
+ let cursor = clone;
940
+ for (let i = 0; i < segments.length - 1; i++) {
941
+ const next = cursor === null || cursor === void 0 ? void 0 : cursor[segments[i]];
942
+ if (next && typeof next === 'object') {
943
+ cursor = next;
944
+ }
945
+ else {
946
+ cursor = undefined;
947
+ break;
948
+ }
949
+ }
950
+ if (cursor) {
951
+ delete cursor[segments[segments.length - 1]];
952
+ }
831
953
  }
954
+ return clone;
832
955
  }
833
956
  saveConfiguration() {
834
- localStorage.setItem('native-update-config', JSON.stringify(this.config));
957
+ var _a;
958
+ (_a = this.getLocalStorage()) === null || _a === void 0 ? void 0 : _a.setItem('native-update-config', JSON.stringify(this.sanitizeConfigForPersistence(this.config)));
835
959
  }
836
960
  getInstallDate() {
837
- const stored = localStorage.getItem('native-update-install-date');
961
+ const storage = this.getLocalStorage();
962
+ const stored = storage === null || storage === void 0 ? void 0 : storage.getItem('native-update-install-date');
838
963
  if (stored) {
839
964
  return parseInt(stored, 10);
840
965
  }
841
966
  const now = Date.now();
842
- localStorage.setItem('native-update-install-date', now.toString());
967
+ storage === null || storage === void 0 ? void 0 : storage.setItem('native-update-install-date', now.toString());
843
968
  return now;
844
969
  }
845
970
  incrementLaunchCount() {
971
+ var _a;
846
972
  this.launchCount++;
847
- localStorage.setItem('native-update-launch-count', this.launchCount.toString());
973
+ (_a = this.getLocalStorage()) === null || _a === void 0 ? void 0 : _a.setItem('native-update-launch-count', this.launchCount.toString());
848
974
  }
849
975
  /**
850
976
  * Get configured review URL from plugin config
@@ -879,11 +1005,24 @@ export class NativeUpdateWeb extends WebPlugin {
879
1005
  return `https://play.google.com/store/apps/details?id=${packageName}&showAllReviews=true`;
880
1006
  }
881
1007
  // No review URL configured - return null to indicate review not available
882
- console.warn('No review URL configured. Set appUpdate.storeUrl.ios, appUpdate.storeUrl.android, ' +
1008
+ logger.warn('No review URL configured. Set appUpdate.storeUrl.ios, appUpdate.storeUrl.android, ' +
883
1009
  'appReview.webReviewUrl, appStoreId, or packageName in plugin config.');
884
1010
  return null;
885
1011
  }
886
1012
  }
1013
+ /**
1014
+ * Strip secrets before persisting config to localStorage. A browser's
1015
+ * localStorage is accessible to any script running in the page (XSS,
1016
+ * compromised deps) — an `apiKey` or `publicKey` sitting there is a
1017
+ * ready-to-use credential. Host apps must pass these on every
1018
+ * `initialize()` call; they are never cached cross-session.
1019
+ */
1020
+ NativeUpdateWeb.SENSITIVE_CONFIG_PATHS = [
1021
+ 'apiKey',
1022
+ 'publicKey',
1023
+ 'liveUpdate.publicKey',
1024
+ 'liveUpdate.apiKey',
1025
+ 'security.publicKey',
1026
+ ];
887
1027
  // Alias for backward compatibility with tests
888
1028
  // Already exported above
889
- //# sourceMappingURL=web.js.map