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.
- package/Readme.md +35 -9
- package/android/manifest-additions.xml +28 -0
- package/android/src/main/AndroidManifest.xml +3 -7
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +21 -13
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +15 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +23 -7
- package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +346 -31
- package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +32 -34
- package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +10 -1
- package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +18 -18
- package/cli/AGENTS.md +43 -0
- package/cli/CLAUDE.md +65 -0
- package/cli/commands/backend-create.js +56 -15
- package/cli/commands/bundle-create.js +39 -7
- package/cli/commands/monitor.js +72 -51
- package/cli/index.js +10 -24
- package/cli/package.json +11 -3
- package/dist/esm/app-review/app-review-manager.js +0 -1
- package/dist/esm/app-review/index.js +0 -1
- package/dist/esm/app-review/platform-review-handler.js +0 -1
- package/dist/esm/app-review/review-conditions-checker.js +0 -1
- package/dist/esm/app-review/review-rate-limiter.js +0 -1
- package/dist/esm/app-review/types.js +0 -1
- package/dist/esm/app-update/app-update-checker.js +0 -1
- package/dist/esm/app-update/app-update-installer.d.ts +1 -0
- package/dist/esm/app-update/app-update-installer.js +19 -8
- package/dist/esm/app-update/app-update-manager.js +0 -1
- package/dist/esm/app-update/app-update-notifier.js +0 -1
- package/dist/esm/app-update/index.js +0 -1
- package/dist/esm/app-update/platform-app-update.js +12 -3
- package/dist/esm/app-update/types.js +0 -1
- package/dist/esm/background-update/background-scheduler.js +0 -1
- package/dist/esm/background-update/index.js +0 -1
- package/dist/esm/background-update/notification-manager.js +0 -1
- package/dist/esm/config/support.js +0 -1
- package/dist/esm/core/analytics.d.ts +13 -0
- package/dist/esm/core/analytics.js +15 -1
- package/dist/esm/core/cache-manager.js +0 -1
- package/dist/esm/core/config.d.ts +6 -30
- package/dist/esm/core/config.js +1 -8
- package/dist/esm/core/errors.d.ts +1 -0
- package/dist/esm/core/errors.js +1 -1
- package/dist/esm/core/event-emitter.d.ts +0 -3
- package/dist/esm/core/event-emitter.js +8 -3
- package/dist/esm/core/logger.js +0 -1
- package/dist/esm/core/performance.js +0 -1
- package/dist/esm/core/plugin-manager.js +3 -1
- package/dist/esm/core/security.d.ts +22 -3
- package/dist/esm/core/security.js +46 -7
- package/dist/esm/definitions.d.ts +13 -40
- package/dist/esm/definitions.js +0 -1
- package/dist/esm/index.d.ts +3 -4
- package/dist/esm/index.js +3 -3
- package/dist/esm/live-update/bundle-manager.d.ts +8 -0
- package/dist/esm/live-update/bundle-manager.js +20 -1
- package/dist/esm/live-update/certificate-pinning.js +0 -1
- package/dist/esm/live-update/delta-processor.d.ts +1 -1
- package/dist/esm/live-update/delta-processor.js +3 -1
- package/dist/esm/live-update/download-manager.d.ts +36 -5
- package/dist/esm/live-update/download-manager.js +70 -24
- package/dist/esm/live-update/rollout-checker.d.ts +1 -1
- package/dist/esm/live-update/rollout-checker.js +5 -6
- package/dist/esm/live-update/update-manager.d.ts +12 -1
- package/dist/esm/live-update/update-manager.js +38 -11
- package/dist/esm/live-update/version-manager.d.ts +9 -12
- package/dist/esm/live-update/version-manager.js +40 -68
- package/dist/esm/plugin.js +82 -89
- package/dist/esm/security/crypto.js +11 -7
- package/dist/esm/security/validator.js +0 -1
- package/dist/esm/types/rollout.d.ts +85 -0
- package/dist/esm/types/rollout.js +25 -0
- package/dist/esm/web.d.ts +26 -1
- package/dist/esm/web.js +214 -75
- package/dist/plugin.cjs.js +1 -2
- package/dist/plugin.esm.js +1 -2
- package/dist/plugin.js +2 -3
- package/docs/AGENTS.md +38 -0
- package/docs/CHANGELOG.md +275 -0
- package/docs/CLAUDE.md +101 -0
- package/docs/MANUAL-TASKS.md +17 -0
- package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
- package/docs/MIGRATION.md +170 -0
- package/docs/PACKAGES.md +167 -0
- package/docs/PERFORMANCE.md +110 -0
- package/docs/PROJECT_COMPLETION_TRACKER.md +4 -35
- package/docs/README.md +12 -2
- package/docs/REMAINING_FEATURES.md +123 -76
- package/docs/ROADMAP.md +200 -120
- package/docs/SECURITY-IF-REPO-PUBLIC.md +232 -0
- package/docs/TESTING_REQUIREMENTS.md +1 -112
- package/docs/ai-knowledge-base/00-project-profile.md +38 -0
- package/docs/ai-knowledge-base/01-features.md +46 -0
- package/docs/ai-knowledge-base/02-routes.md +72 -0
- package/docs/ai-knowledge-base/03-tech-stack.md +57 -0
- package/docs/ai-knowledge-base/README.md +23 -0
- package/docs/analytics-tracking.md +37 -0
- package/docs/capacitor-official-plugins.md +46 -0
- package/docs/capawesome-plugins.md +29 -0
- package/docs/deployment/HOSTINGER_DEPLOY.md +370 -0
- package/docs/deployment/PRODUCTION-DEPLOY-CHECKLIST.md +184 -0
- package/docs/docs-site/plan.md +201 -0
- package/docs/docs-site/scope.md +127 -0
- package/docs/docs-site/tracker.json +235 -0
- package/docs/error-handling-tracking.md +37 -0
- package/docs/features/laravel-nova-backend/ASSESSMENT-SUMMARY.md +96 -0
- package/docs/features/laravel-nova-backend/IMPLEMENTATION-PLAN.md +504 -0
- package/docs/features/laravel-nova-backend/progress-tracker.json +184 -0
- package/docs/guides/testing-guide.md +15 -41
- package/docs/package-audit.md +41 -0
- package/docs/play-store/ASO-METADATA.md +5 -0
- package/docs/play-store/DATA-SAFETY.md +5 -0
- package/docs/play-store/DECLARATIONS.md +5 -0
- package/docs/play-store/README.md +27 -0
- package/docs/play-store/RELEASE-NOTES.md +99 -0
- package/docs/play-store/RELEASE-STEPS.md +6 -0
- package/docs/play-store/STORE-LISTING.md +7 -0
- package/docs/play-store/SUBMISSION-CHECKLIST.md +5 -0
- package/docs/production-readiness.md +1 -1
- package/docs/project-audit/2026-05-16/00-FINALIZATION-PLAN.md +123 -0
- package/docs/project-audit/2026-05-16/01-BATCH-PLAN.md +227 -0
- package/docs/project-audit/2026-05-16/02-AUDIT-FINDINGS.md +144 -0
- package/docs/project-audit/2026-05-16/sessions/batch-01.md +42 -0
- package/docs/project-audit/2026-05-16/sessions/batch-02.md +74 -0
- package/docs/project-audit/2026-05-16/sessions/batch-03.md +71 -0
- package/docs/project-audit/2026-05-16/sessions/batch-04.md +88 -0
- package/docs/project-audit/2026-05-16/sessions/batch-05.md +63 -0
- package/docs/project-audit/2026-05-16/sessions/batch-06.md +103 -0
- package/docs/project-audit/2026-05-16/sessions/batch-07.md +61 -0
- package/docs/project-audit/2026-05-16/sessions/batch-08.md +101 -0
- package/docs/project-audit/2026-05-16/sessions/batch-09.md +105 -0
- package/docs/project-audit/2026-05-16/sessions/batch-10.md +102 -0
- package/docs/project-audit/AUDIT-HISTORY.md +74 -0
- package/docs/project-audit/BATCH-PLAN.md +168 -0
- package/docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.md +73 -0
- package/docs/project-audit/PRODUCTION-READINESS-AUDIT-2026-05-08.md +275 -0
- package/docs/project-finalization/00-tracker.json +60 -0
- package/docs/project-knowledge-base/01-system-overview.md +227 -0
- package/docs/project-knowledge-base/02-routes-pages-forms-users.md +352 -0
- package/docs/project-knowledge-base/03-tech-stack-modules-services.md +379 -0
- package/docs/project-knowledge-base/04-data-models-integrations.md +244 -0
- package/docs/project-knowledge-base/05-docs-corpus-inventory.md +256 -0
- package/docs/project-knowledge-base/06-operations-testing-legal-content.md +208 -0
- package/docs/project-knowledge-base/README.md +103 -0
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-05-19.md +81 -0
- package/docs/rules-tracker.md +102 -0
- package/docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md +116 -0
- package/docs/seo-aeo-rules.json +3043 -0
- package/docs/social-media-marketing/README.md +81 -0
- package/docs/social-media-marketing/posts-batch-01.md +613 -0
- package/docs/social-media-marketing/posts-batch-02.md +528 -0
- package/docs/social-media-marketing/whatsapp-messages-batch-01.md +458 -0
- package/docs/social-media-marketing/whatsapp-status-batch-01.md +355 -0
- package/docs/tracking/finalization-2026-05-16-tracker.json +346 -0
- package/docs/tracking/full-audit-tracker.json +121 -0
- package/docs/tracking/production-readiness-audit-tracker.json +429 -0
- package/docs/tracking/seo-checklist-tracker.json +333 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +66 -9
- package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +306 -52
- package/ios/Plugin/NativeUpdatePlugin.swift +22 -9
- package/ios/Plugin/Security/SecurityManager.swift +13 -14
- package/package.json +37 -38
- package/cli/node_modules/.yarn-integrity +0 -16
- package/cli/node_modules/commander/LICENSE +0 -22
- package/cli/node_modules/commander/Readme.md +0 -1148
- package/cli/node_modules/commander/esm.mjs +0 -16
- package/cli/node_modules/commander/index.js +0 -26
- package/cli/node_modules/commander/lib/argument.js +0 -145
- package/cli/node_modules/commander/lib/command.js +0 -2179
- package/cli/node_modules/commander/lib/error.js +0 -43
- package/cli/node_modules/commander/lib/help.js +0 -462
- package/cli/node_modules/commander/lib/option.js +0 -329
- package/cli/node_modules/commander/lib/suggestSimilar.js +0 -100
- package/cli/node_modules/commander/package-support.json +0 -16
- package/cli/node_modules/commander/package.json +0 -80
- package/cli/node_modules/commander/typings/esm.d.mts +0 -3
- package/cli/node_modules/commander/typings/index.d.ts +0 -884
- package/cli/yarn.lock +0 -8
- package/dist/esm/__tests__/bundle-manager.test.d.ts +0 -1
- package/dist/esm/__tests__/bundle-manager.test.js +0 -151
- package/dist/esm/__tests__/bundle-manager.test.js.map +0 -1
- package/dist/esm/__tests__/config.test.d.ts +0 -1
- package/dist/esm/__tests__/config.test.js +0 -70
- package/dist/esm/__tests__/config.test.js.map +0 -1
- package/dist/esm/__tests__/delta-processor.test.d.ts +0 -1
- package/dist/esm/__tests__/delta-processor.test.js +0 -77
- package/dist/esm/__tests__/delta-processor.test.js.map +0 -1
- package/dist/esm/__tests__/firestore-schema.test.d.ts +0 -1
- package/dist/esm/__tests__/firestore-schema.test.js +0 -74
- package/dist/esm/__tests__/firestore-schema.test.js.map +0 -1
- package/dist/esm/__tests__/integration.test.d.ts +0 -1
- package/dist/esm/__tests__/integration.test.js +0 -78
- package/dist/esm/__tests__/integration.test.js.map +0 -1
- package/dist/esm/__tests__/manifest-reader.test.d.ts +0 -1
- package/dist/esm/__tests__/manifest-reader.test.js +0 -271
- package/dist/esm/__tests__/manifest-reader.test.js.map +0 -1
- package/dist/esm/__tests__/rollout-checker.test.d.ts +0 -1
- package/dist/esm/__tests__/rollout-checker.test.js +0 -210
- package/dist/esm/__tests__/rollout-checker.test.js.map +0 -1
- package/dist/esm/__tests__/security.test.d.ts +0 -1
- package/dist/esm/__tests__/security.test.js +0 -54
- package/dist/esm/__tests__/security.test.js.map +0 -1
- package/dist/esm/__tests__/version-manager.test.d.ts +0 -1
- package/dist/esm/__tests__/version-manager.test.js +0 -45
- package/dist/esm/__tests__/version-manager.test.js.map +0 -1
- package/dist/esm/app-review/app-review-manager.js.map +0 -1
- package/dist/esm/app-review/index.js.map +0 -1
- package/dist/esm/app-review/platform-review-handler.js.map +0 -1
- package/dist/esm/app-review/review-conditions-checker.js.map +0 -1
- package/dist/esm/app-review/review-rate-limiter.js.map +0 -1
- package/dist/esm/app-review/types.js.map +0 -1
- package/dist/esm/app-update/app-update-checker.js.map +0 -1
- package/dist/esm/app-update/app-update-installer.js.map +0 -1
- package/dist/esm/app-update/app-update-manager.js.map +0 -1
- package/dist/esm/app-update/app-update-notifier.js.map +0 -1
- package/dist/esm/app-update/index.js.map +0 -1
- package/dist/esm/app-update/platform-app-update.js.map +0 -1
- package/dist/esm/app-update/types.js.map +0 -1
- package/dist/esm/background-update/background-scheduler.js.map +0 -1
- package/dist/esm/background-update/index.js.map +0 -1
- package/dist/esm/background-update/notification-manager.js.map +0 -1
- package/dist/esm/config/support.js.map +0 -1
- package/dist/esm/core/analytics.js.map +0 -1
- package/dist/esm/core/cache-manager.js.map +0 -1
- package/dist/esm/core/config.js.map +0 -1
- package/dist/esm/core/errors.js.map +0 -1
- package/dist/esm/core/event-emitter.js.map +0 -1
- package/dist/esm/core/logger.js.map +0 -1
- package/dist/esm/core/performance.js.map +0 -1
- package/dist/esm/core/plugin-manager.js.map +0 -1
- package/dist/esm/core/security.js.map +0 -1
- package/dist/esm/definitions.js.map +0 -1
- package/dist/esm/firestore/firestore-client.d.ts +0 -109
- package/dist/esm/firestore/firestore-client.js +0 -264
- package/dist/esm/firestore/firestore-client.js.map +0 -1
- package/dist/esm/firestore/index.d.ts +0 -11
- package/dist/esm/firestore/index.js +0 -11
- package/dist/esm/firestore/index.js.map +0 -1
- package/dist/esm/firestore/manifest-reader.d.ts +0 -87
- package/dist/esm/firestore/manifest-reader.js +0 -294
- package/dist/esm/firestore/manifest-reader.js.map +0 -1
- package/dist/esm/firestore/schema.d.ts +0 -507
- package/dist/esm/firestore/schema.js +0 -73
- package/dist/esm/firestore/schema.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/live-update/bundle-manager.js.map +0 -1
- package/dist/esm/live-update/certificate-pinning.js.map +0 -1
- package/dist/esm/live-update/delta-processor.js.map +0 -1
- package/dist/esm/live-update/download-manager.js.map +0 -1
- package/dist/esm/live-update/rollout-checker.js.map +0 -1
- package/dist/esm/live-update/update-manager.js.map +0 -1
- package/dist/esm/live-update/version-manager.js.map +0 -1
- package/dist/esm/plugin.js.map +0 -1
- package/dist/esm/security/crypto.js.map +0 -1
- package/dist/esm/security/validator.js.map +0 -1
- package/dist/esm/web.js.map +0 -1
- package/dist/plugin.cjs.js.map +0 -1
- package/dist/plugin.esm.js.map +0 -1
- package/dist/plugin.js.map +0 -1
- package/docs/FIREBASE_INTEGRATION_TRACKER.md +0 -321
- package/docs/FIREBASE_QUERIES_AND_INDEXES_AUDIT.md +0 -221
- package/docs/examples/firebase-backend-example.md +0 -27
- package/docs/guides/no-cost-backend-implementation-plan.md +0 -77
- package/docs/guides/no-cost-firestore-google-drive-backend.md +0 -60
- package/docs/play-console-rejection-rules.json +0 -428
- /package/docs/{MARKETING_WEBSITE_PLAN.md → archive/MARKETING_WEBSITE_PLAN.md} +0 -0
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// Main plugin export
|
|
2
2
|
export { NativeUpdate } from './plugin';
|
|
3
|
+
// Runtime enums — MUST be `export {}` not `export type {}` so they survive
|
|
4
|
+
// TypeScript erasure and reach the published bundle.
|
|
5
|
+
export { BackgroundUpdateType, NotificationPriority, UpdateStrategy, UpdateMode, InstallMode, ChecksumAlgorithm, SyncStatus, BundleStatus, InstallStatus, UpdateErrorCode, } from './definitions';
|
|
3
6
|
// Error exports
|
|
4
7
|
export { ErrorCode, NativeUpdateError, ConfigurationError, DownloadError, ValidationError, StorageError, UpdateError as UpdateErrorClass, } from './core/errors';
|
|
5
8
|
// Core exports for advanced users
|
|
@@ -8,11 +11,8 @@ export { Logger, LogLevel } from './core/logger';
|
|
|
8
11
|
export { SecurityValidator } from './core/security';
|
|
9
12
|
export { PluginManager } from './core/plugin-manager';
|
|
10
13
|
export { CacheManager } from './core/cache-manager';
|
|
11
|
-
// Security/Crypto exports
|
|
12
|
-
export { CryptoUtils } from './security/crypto';
|
|
13
14
|
// Manager exports for advanced users
|
|
14
15
|
export { BundleManager } from './live-update/bundle-manager';
|
|
15
16
|
export { DownloadManager } from './live-update/download-manager';
|
|
16
17
|
export { VersionManager } from './live-update/version-manager';
|
|
17
18
|
export { UpdateManager } from './live-update/update-manager';
|
|
18
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BundleInfo } from '../definitions';
|
|
2
|
+
import type { DownloadManager } from './download-manager';
|
|
2
3
|
/**
|
|
3
4
|
* Manages bundle storage and lifecycle
|
|
4
5
|
*/
|
|
@@ -10,7 +11,14 @@ export declare class BundleManager {
|
|
|
10
11
|
private readonly configManager;
|
|
11
12
|
private cache;
|
|
12
13
|
private cacheExpiry;
|
|
14
|
+
private downloadManager;
|
|
13
15
|
constructor();
|
|
16
|
+
/**
|
|
17
|
+
* Wire the DownloadManager so deleteBundle can also remove the
|
|
18
|
+
* persisted bundle file. Without this, deleted bundles leave their
|
|
19
|
+
* blob on disk forever.
|
|
20
|
+
*/
|
|
21
|
+
setDownloadManager(downloadManager: DownloadManager): void;
|
|
14
22
|
/**
|
|
15
23
|
* Initialize the bundle manager with preferences
|
|
16
24
|
*/
|
|
@@ -11,9 +11,18 @@ export class BundleManager {
|
|
|
11
11
|
this.preferences = null;
|
|
12
12
|
this.cache = new Map();
|
|
13
13
|
this.cacheExpiry = 0;
|
|
14
|
+
this.downloadManager = null;
|
|
14
15
|
this.logger = Logger.getInstance();
|
|
15
16
|
this.configManager = ConfigManager.getInstance();
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Wire the DownloadManager so deleteBundle can also remove the
|
|
20
|
+
* persisted bundle file. Without this, deleted bundles leave their
|
|
21
|
+
* blob on disk forever.
|
|
22
|
+
*/
|
|
23
|
+
setDownloadManager(downloadManager) {
|
|
24
|
+
this.downloadManager = downloadManager;
|
|
25
|
+
}
|
|
17
26
|
/**
|
|
18
27
|
* Initialize the bundle manager with preferences
|
|
19
28
|
*/
|
|
@@ -127,6 +136,17 @@ export class BundleManager {
|
|
|
127
136
|
if (activeBundleId === bundleId) {
|
|
128
137
|
await this.clearActiveBundle();
|
|
129
138
|
}
|
|
139
|
+
// Remove the persisted bundle file from filesystem. Failure to delete
|
|
140
|
+
// metadata earlier was a storage leak: the on-disk zip stayed forever
|
|
141
|
+
// because deleteBundle only cleaned the metadata cache.
|
|
142
|
+
if (this.downloadManager) {
|
|
143
|
+
try {
|
|
144
|
+
await this.downloadManager.deleteBlob(bundleId);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
this.logger.warn('Failed to delete bundle file', { bundleId, error });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
130
150
|
this.logger.info('Bundle deleted', { bundleId });
|
|
131
151
|
}
|
|
132
152
|
/**
|
|
@@ -322,4 +342,3 @@ export class BundleManager {
|
|
|
322
342
|
}
|
|
323
343
|
}
|
|
324
344
|
}
|
|
325
|
-
//# sourceMappingURL=bundle-manager.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For production, a WASM-based bsdiff/bspatch implementation can be added
|
|
8
8
|
* for better performance on binary files.
|
|
9
9
|
*/
|
|
10
|
-
import type { UpdateCheckResponse } from '../
|
|
10
|
+
import type { UpdateCheckResponse } from '../types/rollout';
|
|
11
11
|
/**
|
|
12
12
|
* Delta patch information
|
|
13
13
|
*/
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { Logger } from '../core/logger';
|
|
11
11
|
import { ConfigManager } from '../core/config';
|
|
12
12
|
import { DownloadError, ErrorCode, ValidationError } from '../core/errors';
|
|
13
|
+
import { SecurityValidator } from '../core/security';
|
|
13
14
|
/**
|
|
14
15
|
* Delta processor for applying patch updates
|
|
15
16
|
*/
|
|
@@ -92,6 +93,7 @@ export class DeltaProcessor {
|
|
|
92
93
|
* Download a delta patch file
|
|
93
94
|
*/
|
|
94
95
|
async downloadPatch(url) {
|
|
96
|
+
SecurityValidator.validateDownloadUrl(url);
|
|
95
97
|
const response = await fetch(url);
|
|
96
98
|
if (!response.ok) {
|
|
97
99
|
throw new DownloadError(ErrorCode.DOWNLOAD_FAILED, `Failed to download delta patch: ${response.status}`);
|
|
@@ -102,6 +104,7 @@ export class DeltaProcessor {
|
|
|
102
104
|
* Download full bundle
|
|
103
105
|
*/
|
|
104
106
|
async downloadFull(url, onProgress) {
|
|
107
|
+
SecurityValidator.validateDownloadUrl(url);
|
|
105
108
|
const response = await fetch(url);
|
|
106
109
|
if (!response.ok) {
|
|
107
110
|
throw new DownloadError(ErrorCode.DOWNLOAD_FAILED, `Failed to download bundle: ${response.status}`);
|
|
@@ -296,4 +299,3 @@ export class DeltaProcessor {
|
|
|
296
299
|
export function createDeltaProcessor() {
|
|
297
300
|
return new DeltaProcessor();
|
|
298
301
|
}
|
|
299
|
-
//# sourceMappingURL=delta-processor.js.map
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import type { DownloadProgressEvent } from '../definitions';
|
|
2
|
+
export interface DownloadOptions {
|
|
3
|
+
onProgress?: (event: DownloadProgressEvent) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Byte offset to resume from. When set, the request carries a
|
|
6
|
+
* `Range: bytes=<N>-` header. If the server responds with 200 instead
|
|
7
|
+
* of 206, the plugin discards any partial data the caller held and
|
|
8
|
+
* treats the response as a full download (see resumeDownload).
|
|
9
|
+
*/
|
|
10
|
+
resumePosition?: number;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Manages file downloads with progress tracking and resume capability
|
|
4
14
|
*/
|
|
5
15
|
export declare class DownloadManager {
|
|
6
16
|
private activeDownloads;
|
|
17
|
+
/**
|
|
18
|
+
* Persisted-past-request metadata for the most recent `download()`
|
|
19
|
+
* call per bundleId. Today we only stash whether the server honoured
|
|
20
|
+
* a Range request (HTTP 206 vs 200). resumeDownload() consults this
|
|
21
|
+
* to decide whether to concatenate or discard its partial prefix.
|
|
22
|
+
*/
|
|
23
|
+
private lastResponseMeta;
|
|
7
24
|
private readonly logger;
|
|
8
25
|
private readonly configManager;
|
|
9
26
|
private filesystem;
|
|
@@ -17,11 +34,25 @@ export declare class DownloadManager {
|
|
|
17
34
|
*/
|
|
18
35
|
private validateUrl;
|
|
19
36
|
/**
|
|
20
|
-
* Download a file with progress tracking
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
37
|
+
* Download a file with progress tracking.
|
|
38
|
+
*
|
|
39
|
+
* Accepts either a bare `onProgress` callback (back-compat) or a full
|
|
40
|
+
* DownloadOptions object. When `resumePosition` is set, the request
|
|
41
|
+
* carries a Range header; the resulting download state records
|
|
42
|
+
* whether the server actually honoured it (`serverReturnedPartial`).
|
|
43
|
+
*/
|
|
44
|
+
download(url: string, bundleId: string, onProgressOrOptions?: ((event: DownloadProgressEvent) => void) | DownloadOptions): Promise<Blob>;
|
|
45
|
+
/**
|
|
46
|
+
* Resume a previously interrupted download.
|
|
47
|
+
*
|
|
48
|
+
* Emits a Range-request download starting at the partial-data offset.
|
|
49
|
+
* If the server honours the range (HTTP 206), the partial + remaining
|
|
50
|
+
* bytes are concatenated. If the server returns a full response
|
|
51
|
+
* (HTTP 200 — common when the upstream URL's ETag changed, or when a
|
|
52
|
+
* CDN doesn't honour ranges) the partial data is discarded and the
|
|
53
|
+
* full response is used. Previously this path silently double-wrote
|
|
54
|
+
* bytes when the server fell back to 200; this was a latent
|
|
55
|
+
* bundle-corruption bug.
|
|
25
56
|
*/
|
|
26
57
|
resumeDownload(url: string, bundleId: string, partialData: Blob, onProgress?: (event: DownloadProgressEvent) => void): Promise<Blob>;
|
|
27
58
|
/**
|
|
@@ -8,6 +8,13 @@ import { Directory } from '@capacitor/filesystem';
|
|
|
8
8
|
export class DownloadManager {
|
|
9
9
|
constructor() {
|
|
10
10
|
this.activeDownloads = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Persisted-past-request metadata for the most recent `download()`
|
|
13
|
+
* call per bundleId. Today we only stash whether the server honoured
|
|
14
|
+
* a Range request (HTTP 206 vs 200). resumeDownload() consults this
|
|
15
|
+
* to decide whether to concatenate or discard its partial prefix.
|
|
16
|
+
*/
|
|
17
|
+
this.lastResponseMeta = new Map();
|
|
11
18
|
this.filesystem = null;
|
|
12
19
|
this.logger = Logger.getInstance();
|
|
13
20
|
this.configManager = ConfigManager.getInstance();
|
|
@@ -45,9 +52,20 @@ export class DownloadManager {
|
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
/**
|
|
48
|
-
* Download a file with progress tracking
|
|
55
|
+
* Download a file with progress tracking.
|
|
56
|
+
*
|
|
57
|
+
* Accepts either a bare `onProgress` callback (back-compat) or a full
|
|
58
|
+
* DownloadOptions object. When `resumePosition` is set, the request
|
|
59
|
+
* carries a Range header; the resulting download state records
|
|
60
|
+
* whether the server actually honoured it (`serverReturnedPartial`).
|
|
49
61
|
*/
|
|
50
|
-
async download(url, bundleId,
|
|
62
|
+
async download(url, bundleId, onProgressOrOptions) {
|
|
63
|
+
var _a;
|
|
64
|
+
const options = typeof onProgressOrOptions === 'function'
|
|
65
|
+
? { onProgress: onProgressOrOptions }
|
|
66
|
+
: onProgressOrOptions !== null && onProgressOrOptions !== void 0 ? onProgressOrOptions : {};
|
|
67
|
+
const onProgress = options.onProgress;
|
|
68
|
+
const resumePosition = (_a = options.resumePosition) !== null && _a !== void 0 ? _a : 0;
|
|
51
69
|
// Validate inputs
|
|
52
70
|
this.validateUrl(url);
|
|
53
71
|
if (!bundleId) {
|
|
@@ -62,6 +80,7 @@ export class DownloadManager {
|
|
|
62
80
|
const downloadState = {
|
|
63
81
|
controller: abortController,
|
|
64
82
|
startTime: Date.now(),
|
|
83
|
+
resumePosition: resumePosition > 0 ? resumePosition : undefined,
|
|
65
84
|
};
|
|
66
85
|
this.activeDownloads.set(bundleId, downloadState);
|
|
67
86
|
try {
|
|
@@ -73,8 +92,8 @@ export class DownloadManager {
|
|
|
73
92
|
Accept: 'application/octet-stream, application/zip',
|
|
74
93
|
};
|
|
75
94
|
// Add Range header if resuming from a previous position
|
|
76
|
-
if (
|
|
77
|
-
headers['Range'] = `bytes=${
|
|
95
|
+
if (resumePosition > 0) {
|
|
96
|
+
headers['Range'] = `bytes=${resumePosition}-`;
|
|
78
97
|
}
|
|
79
98
|
const response = await fetch(url, {
|
|
80
99
|
signal: abortController.signal,
|
|
@@ -84,6 +103,13 @@ export class DownloadManager {
|
|
|
84
103
|
if (!response.ok) {
|
|
85
104
|
throw new DownloadError(ErrorCode.DOWNLOAD_FAILED, `Download failed: ${response.status} ${response.statusText}`, { status: response.status, statusText: response.statusText });
|
|
86
105
|
}
|
|
106
|
+
// Record whether the server actually honoured the Range request.
|
|
107
|
+
// Stored on both the live state (for introspection during the
|
|
108
|
+
// download) and the lastResponseMeta map (so resumeDownload can
|
|
109
|
+
// read it after download() cleans up the live state).
|
|
110
|
+
const serverReturnedPartial = resumePosition > 0 && response.status === 206;
|
|
111
|
+
downloadState.serverReturnedPartial = serverReturnedPartial;
|
|
112
|
+
this.lastResponseMeta.set(bundleId, { partial: serverReturnedPartial });
|
|
87
113
|
// Validate content type
|
|
88
114
|
const contentType = response.headers.get('content-type');
|
|
89
115
|
if (contentType && !this.isValidContentType(contentType)) {
|
|
@@ -155,25 +181,31 @@ export class DownloadManager {
|
|
|
155
181
|
}
|
|
156
182
|
}
|
|
157
183
|
/**
|
|
158
|
-
* Resume a previously interrupted download
|
|
184
|
+
* Resume a previously interrupted download.
|
|
185
|
+
*
|
|
186
|
+
* Emits a Range-request download starting at the partial-data offset.
|
|
187
|
+
* If the server honours the range (HTTP 206), the partial + remaining
|
|
188
|
+
* bytes are concatenated. If the server returns a full response
|
|
189
|
+
* (HTTP 200 — common when the upstream URL's ETag changed, or when a
|
|
190
|
+
* CDN doesn't honour ranges) the partial data is discarded and the
|
|
191
|
+
* full response is used. Previously this path silently double-wrote
|
|
192
|
+
* bytes when the server fell back to 200; this was a latent
|
|
193
|
+
* bundle-corruption bug.
|
|
159
194
|
*/
|
|
160
195
|
async resumeDownload(url, bundleId, partialData, onProgress) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return new Blob([partialData,
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
// Resume failed, clean up
|
|
174
|
-
this.activeDownloads.delete(bundleId);
|
|
175
|
-
throw error;
|
|
196
|
+
const responseBlob = await this.download(url, bundleId, {
|
|
197
|
+
onProgress,
|
|
198
|
+
resumePosition: partialData.size,
|
|
199
|
+
});
|
|
200
|
+
// Read the flag download() set in lastResponseMeta. If the server
|
|
201
|
+
// returned 206 we concatenate; if 200 we discard the partial.
|
|
202
|
+
const meta = this.lastResponseMeta.get(bundleId);
|
|
203
|
+
this.lastResponseMeta.delete(bundleId);
|
|
204
|
+
if (meta === null || meta === void 0 ? void 0 : meta.partial) {
|
|
205
|
+
return new Blob([partialData, responseBlob]);
|
|
176
206
|
}
|
|
207
|
+
this.logger.info('Resume: server returned full response; discarding partial data', { bundleId, partialSize: partialData.size, responseSize: responseBlob.size });
|
|
208
|
+
return responseBlob;
|
|
177
209
|
}
|
|
178
210
|
/**
|
|
179
211
|
* Check if a download can be resumed
|
|
@@ -259,9 +291,16 @@ export class DownloadManager {
|
|
|
259
291
|
let lastError = null;
|
|
260
292
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
261
293
|
try {
|
|
262
|
-
//
|
|
294
|
+
// Exponential backoff with ±25% jitter, capped at 30s. Jitter
|
|
295
|
+
// avoids a thundering-herd when many clients retry in lockstep
|
|
296
|
+
// after a common failure (CDN blip, server restart) — without
|
|
297
|
+
// it every client would wake up at exactly the same moment and
|
|
298
|
+
// DDoS the recovering server. 25% is enough desync without
|
|
299
|
+
// making the retry interval feel unpredictable.
|
|
263
300
|
if (attempt > 0) {
|
|
264
|
-
const
|
|
301
|
+
const base = retryDelay * Math.pow(2, attempt - 1);
|
|
302
|
+
const jitter = (Math.random() - 0.5) * 0.5 * base;
|
|
303
|
+
const delay = Math.min(Math.max(base + jitter, 100), 30000);
|
|
265
304
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
266
305
|
this.logger.debug('Retrying download', { bundleId, attempt, delay });
|
|
267
306
|
}
|
|
@@ -296,7 +335,15 @@ export class DownloadManager {
|
|
|
296
335
|
throw new DownloadError(ErrorCode.MISSING_DEPENDENCY, 'Filesystem not initialized');
|
|
297
336
|
}
|
|
298
337
|
const arrayBuffer = await this.blobToArrayBuffer(blob);
|
|
299
|
-
|
|
338
|
+
// Spreading a 100MB Uint8Array into String.fromCharCode is a stack
|
|
339
|
+
// overflow on most engines. Chunk the conversion in 32KB blocks.
|
|
340
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
341
|
+
const chunkSize = 0x8000;
|
|
342
|
+
let binary = '';
|
|
343
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
344
|
+
binary += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + chunkSize)));
|
|
345
|
+
}
|
|
346
|
+
const base64 = btoa(binary);
|
|
300
347
|
const path = `bundles/${bundleId}/bundle.zip`;
|
|
301
348
|
await this.filesystem.writeFile({
|
|
302
349
|
path,
|
|
@@ -363,4 +410,3 @@ export class DownloadManager {
|
|
|
363
410
|
}
|
|
364
411
|
}
|
|
365
412
|
}
|
|
366
|
-
//# sourceMappingURL=download-manager.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Client-side rollout eligibility checking for staged deployments.
|
|
5
5
|
* Uses deterministic hashing for consistent rollout decisions.
|
|
6
6
|
*/
|
|
7
|
-
import type { RolloutConfig, DeviceInfo } from '../
|
|
7
|
+
import type { RolloutConfig, DeviceInfo } from '../types/rollout';
|
|
8
8
|
/**
|
|
9
9
|
* Rollout eligibility result
|
|
10
10
|
*/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Client-side rollout eligibility checking for staged deployments.
|
|
5
5
|
* Uses deterministic hashing for consistent rollout decisions.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { toEpochMs } from '../types/rollout';
|
|
8
8
|
import { Logger } from '../core/logger';
|
|
9
9
|
import { ConfigManager } from '../core/config';
|
|
10
10
|
/**
|
|
@@ -77,7 +77,7 @@ export class RolloutChecker {
|
|
|
77
77
|
*/
|
|
78
78
|
checkTimeWindow(rollout) {
|
|
79
79
|
const now = Date.now();
|
|
80
|
-
const startTime =
|
|
80
|
+
const startTime = toEpochMs(rollout.startTime);
|
|
81
81
|
if (now < startTime) {
|
|
82
82
|
return {
|
|
83
83
|
eligible: false,
|
|
@@ -85,7 +85,7 @@ export class RolloutChecker {
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
if (rollout.endTime) {
|
|
88
|
-
const endTime =
|
|
88
|
+
const endTime = toEpochMs(rollout.endTime);
|
|
89
89
|
if (now > endTime) {
|
|
90
90
|
return {
|
|
91
91
|
eligible: false,
|
|
@@ -100,7 +100,7 @@ export class RolloutChecker {
|
|
|
100
100
|
*/
|
|
101
101
|
checkScheduled(scheduledTime) {
|
|
102
102
|
const now = Date.now();
|
|
103
|
-
const scheduled =
|
|
103
|
+
const scheduled = toEpochMs(scheduledTime);
|
|
104
104
|
if (now < scheduled) {
|
|
105
105
|
return {
|
|
106
106
|
eligible: false,
|
|
@@ -170,7 +170,7 @@ export class RolloutChecker {
|
|
|
170
170
|
if (!gradualSteps || !gradualInterval) {
|
|
171
171
|
return rollout.percentage;
|
|
172
172
|
}
|
|
173
|
-
const startTime =
|
|
173
|
+
const startTime = toEpochMs(rollout.startTime);
|
|
174
174
|
const elapsedHours = (Date.now() - startTime) / (1000 * 60 * 60);
|
|
175
175
|
const stepIndex = Math.floor(elapsedHours / gradualInterval);
|
|
176
176
|
if (stepIndex >= gradualSteps.length) {
|
|
@@ -302,4 +302,3 @@ export async function collectDeviceInfo() {
|
|
|
302
302
|
region: undefined, // Could be determined via geolocation or timezone
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
|
-
//# sourceMappingURL=rollout-checker.js.map
|
|
@@ -18,7 +18,18 @@ export declare class UpdateManager {
|
|
|
18
18
|
*/
|
|
19
19
|
applyUpdate(bundleId: string, options?: UpdateOptions): Promise<void>;
|
|
20
20
|
/**
|
|
21
|
-
* Validate update before applying
|
|
21
|
+
* Validate update before applying.
|
|
22
|
+
*
|
|
23
|
+
* Version-safety checks here are the last line of defense before a bundle
|
|
24
|
+
* is activated. We enforce two invariants:
|
|
25
|
+
* 1. Active-bundle monotonicity — new bundle must be strictly newer than
|
|
26
|
+
* whatever is currently active on this device.
|
|
27
|
+
* 2. Persisted high-water-mark — new bundle must be strictly newer than
|
|
28
|
+
* the highest version this device has ever applied in this channel.
|
|
29
|
+
* Catches an attacker who wipes the download cache (or serves an
|
|
30
|
+
* older manifest) to force a downgrade to a vulnerable build.
|
|
31
|
+
* The old `allowDowngrade` escape hatch was removed in v2 — downgrades are
|
|
32
|
+
* a debug/recovery flow and belong in a dedicated API, not an option flag.
|
|
22
33
|
*/
|
|
23
34
|
private validateUpdate;
|
|
24
35
|
/**
|
|
@@ -26,6 +26,7 @@ export class UpdateManager {
|
|
|
26
26
|
* Apply bundle update atomically
|
|
27
27
|
*/
|
|
28
28
|
async applyUpdate(bundleId, options) {
|
|
29
|
+
var _a;
|
|
29
30
|
if (this.updateInProgress) {
|
|
30
31
|
throw new UpdateError(ErrorCode.UPDATE_FAILED, 'Another update is already in progress');
|
|
31
32
|
}
|
|
@@ -64,9 +65,20 @@ export class UpdateManager {
|
|
|
64
65
|
await this.verifyUpdate(newBundle);
|
|
65
66
|
// Mark as active
|
|
66
67
|
await bundleManager.setActiveBundle(bundleId);
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// Advance the monotonic high-water mark so future update checks and
|
|
69
|
+
// reinstalls cannot be tricked into applying an older version.
|
|
70
|
+
const versionManager = this.pluginManager.getVersionManager();
|
|
71
|
+
const configManager = this.pluginManager.getConfigManager();
|
|
72
|
+
await versionManager.rememberAppliedVersion(configManager.get('channel'), newBundle.version);
|
|
73
|
+
// Auto-cleanup by default. The old opt-in flag left most
|
|
74
|
+
// integrations accumulating bundles forever, which is a disk
|
|
75
|
+
// exhaustion path on devices with limited storage. Host apps
|
|
76
|
+
// that genuinely want every historical bundle can pass
|
|
77
|
+
// cleanupOldBundles: false explicitly.
|
|
78
|
+
const shouldCleanup = (options === null || options === void 0 ? void 0 : options.cleanupOldBundles) !== false;
|
|
79
|
+
if (shouldCleanup) {
|
|
80
|
+
const keep = (_a = options === null || options === void 0 ? void 0 : options.keepBundleCount) !== null && _a !== void 0 ? _a : 3;
|
|
81
|
+
await bundleManager.cleanupOldBundles(keep);
|
|
70
82
|
}
|
|
71
83
|
logger.info('Bundle update completed successfully', {
|
|
72
84
|
bundleId,
|
|
@@ -89,16 +101,32 @@ export class UpdateManager {
|
|
|
89
101
|
}
|
|
90
102
|
}
|
|
91
103
|
/**
|
|
92
|
-
* Validate update before applying
|
|
104
|
+
* Validate update before applying.
|
|
105
|
+
*
|
|
106
|
+
* Version-safety checks here are the last line of defense before a bundle
|
|
107
|
+
* is activated. We enforce two invariants:
|
|
108
|
+
* 1. Active-bundle monotonicity — new bundle must be strictly newer than
|
|
109
|
+
* whatever is currently active on this device.
|
|
110
|
+
* 2. Persisted high-water-mark — new bundle must be strictly newer than
|
|
111
|
+
* the highest version this device has ever applied in this channel.
|
|
112
|
+
* Catches an attacker who wipes the download cache (or serves an
|
|
113
|
+
* older manifest) to force a downgrade to a vulnerable build.
|
|
114
|
+
* The old `allowDowngrade` escape hatch was removed in v2 — downgrades are
|
|
115
|
+
* a debug/recovery flow and belong in a dedicated API, not an option flag.
|
|
93
116
|
*/
|
|
94
|
-
async validateUpdate(currentBundle, newBundle,
|
|
117
|
+
async validateUpdate(currentBundle, newBundle, _options) {
|
|
95
118
|
const logger = this.pluginManager.getLogger();
|
|
96
119
|
const versionManager = this.pluginManager.getVersionManager();
|
|
97
|
-
|
|
98
|
-
if (currentBundle &&
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
const configManager = this.pluginManager.getConfigManager();
|
|
121
|
+
if (currentBundle &&
|
|
122
|
+
versionManager.shouldBlockDowngrade(currentBundle.version, newBundle.version)) {
|
|
123
|
+
throw new ValidationError(ErrorCode.VERSION_DOWNGRADE, `Cannot downgrade from ${currentBundle.version} to ${newBundle.version}`);
|
|
124
|
+
}
|
|
125
|
+
const channel = configManager.get('channel');
|
|
126
|
+
const maxSeen = await versionManager.getMaxSeenVersion(channel);
|
|
127
|
+
if (maxSeen &&
|
|
128
|
+
versionManager.shouldBlockDowngrade(maxSeen, newBundle.version)) {
|
|
129
|
+
throw new ValidationError(ErrorCode.VERSION_DOWNGRADE, `Cannot apply ${newBundle.version} - device has previously applied ${maxSeen} on channel ${channel}`);
|
|
102
130
|
}
|
|
103
131
|
// Verify bundle integrity
|
|
104
132
|
if (!newBundle.verified) {
|
|
@@ -291,4 +319,3 @@ export class UpdateManager {
|
|
|
291
319
|
this.currentState = null;
|
|
292
320
|
}
|
|
293
321
|
}
|
|
294
|
-
//# sourceMappingURL=update-manager.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { LatestVersion } from '../definitions';
|
|
2
|
-
import type { DeviceInfo, UpdateCheckResponse } from '../firestore/schema';
|
|
3
2
|
/**
|
|
4
3
|
* Manages version checking and comparison
|
|
5
4
|
*/
|
|
@@ -11,8 +10,6 @@ export declare class VersionManager {
|
|
|
11
10
|
private readonly securityValidator;
|
|
12
11
|
private preferences;
|
|
13
12
|
private memoryCache;
|
|
14
|
-
private firestoreClient;
|
|
15
|
-
private manifestReader;
|
|
16
13
|
constructor();
|
|
17
14
|
/**
|
|
18
15
|
* Compare two semantic versions
|
|
@@ -30,15 +27,6 @@ export declare class VersionManager {
|
|
|
30
27
|
* Initialize the version manager
|
|
31
28
|
*/
|
|
32
29
|
initialize(): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Check for updates from Firestore backend
|
|
35
|
-
* Uses ManifestReader for efficient single-document reads
|
|
36
|
-
*/
|
|
37
|
-
checkForUpdatesFromFirestore(currentVersion: string, deviceInfo: DeviceInfo): Promise<UpdateCheckResponse | null>;
|
|
38
|
-
/**
|
|
39
|
-
* Get the appropriate update checker based on backend type
|
|
40
|
-
*/
|
|
41
|
-
checkForUpdatesAuto(currentVersion: string, deviceInfo?: DeviceInfo): Promise<LatestVersion | UpdateCheckResponse | null>;
|
|
42
30
|
/**
|
|
43
31
|
* Check for latest version from server
|
|
44
32
|
*/
|
|
@@ -93,4 +81,13 @@ export declare class VersionManager {
|
|
|
93
81
|
* Check if downgrade protection should block update
|
|
94
82
|
*/
|
|
95
83
|
shouldBlockDowngrade(currentVersion: string, newVersion: string): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Persisted high-water-mark version per channel. Survives reinstalls that
|
|
86
|
+
* leave Preferences intact; protects against an attacker feeding an older
|
|
87
|
+
* (and potentially vulnerable) bundle even after a full cache wipe of
|
|
88
|
+
* downloaded bundles. Paired with `rememberAppliedVersion`.
|
|
89
|
+
*/
|
|
90
|
+
private maxVersionKey;
|
|
91
|
+
getMaxSeenVersion(channel: string): Promise<string | null>;
|
|
92
|
+
rememberAppliedVersion(channel: string, version: string): Promise<void>;
|
|
96
93
|
}
|