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.
- package/Readme.md +28 -14
- 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/LiveUpdatePlugin.kt +196 -29
- package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +21 -36
- package/cli/AGENTS.md +15 -1
- package/cli/CLAUDE.md +15 -1
- 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 +0 -15
- package/dist/esm/core/config.js +0 -4
- 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 +11 -0
- package/dist/esm/core/security.js +27 -1
- package/dist/esm/definitions.d.ts +0 -11
- package/dist/esm/definitions.js +0 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -1
- 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.js +9 -2
- 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.js +0 -1
- package/dist/esm/live-update/version-manager.d.ts +0 -12
- package/dist/esm/live-update/version-manager.js +0 -68
- package/dist/esm/plugin.js +69 -43
- 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 +8 -0
- package/dist/esm/web.js +148 -54
- package/dist/plugin.cjs.js +1 -2
- package/dist/plugin.esm.js +1 -2
- package/dist/plugin.js +2 -3
- package/docs/CHANGELOG.md +124 -0
- package/docs/CLAUDE.md +1 -1
- package/docs/MANUAL-TASKS.md +17 -0
- package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
- package/docs/MIGRATION.md +83 -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 +0 -1
- 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 +75 -34
- 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/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 +39 -30
- package/docs/project-knowledge-base/02-routes-pages-forms-users.md +87 -81
- package/docs/project-knowledge-base/03-tech-stack-modules-services.md +140 -108
- package/docs/project-knowledge-base/04-data-models-integrations.md +191 -254
- package/docs/project-knowledge-base/05-docs-corpus-inventory.md +83 -20
- package/docs/project-knowledge-base/06-operations-testing-legal-content.md +67 -29
- package/docs/project-knowledge-base/README.md +27 -14
- 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/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/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +16 -3
- package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +71 -47
- package/ios/Plugin/NativeUpdatePlugin.swift +14 -9
- package/package.json +29 -29
- 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-enforcement.test.d.ts +0 -1
- package/dist/esm/__tests__/security-enforcement.test.js +0 -95
- package/dist/esm/__tests__/security-enforcement.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/features/laravel-nova-backend/credentials.ignore.md +0 -34
- 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/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-16.md +0 -454
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-24.md +0 -66
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-03-25.md +0 -67
- /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
|
package/dist/esm/plugin.js
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
141
|
+
size: validatedBlob.size,
|
|
137
142
|
status: BundleStatus.READY,
|
|
138
143
|
checksum: options.checksum,
|
|
139
144
|
signature: options.signature,
|
|
140
|
-
verified:
|
|
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
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
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
|
|
231
|
-
signatureValid
|
|
232
|
-
sizeValid
|
|
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
|
-
|
|
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
|
-
|
|
300
|
-
|
|
319
|
+
if (!data.available) {
|
|
320
|
+
return {
|
|
321
|
+
available: false,
|
|
322
|
+
currentVersion,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
301
325
|
return {
|
|
302
|
-
available:
|
|
326
|
+
available: true,
|
|
303
327
|
currentVersion,
|
|
304
|
-
latestVersion:
|
|
305
|
-
url:
|
|
328
|
+
latestVersion: data.version,
|
|
329
|
+
url: data.downloadUrl,
|
|
306
330
|
mandatory: data.mandatory,
|
|
307
|
-
notes: data.
|
|
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
|
-
//
|
|
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(
|
|
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
|
-
|
|
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
|
|
@@ -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
|
/**
|