UNPKG

native-update

Version:

Foundation package for building a comprehensive update system for Capacitor apps. Provides architecture and interfaces but requires backend implementation.

341 lines 11.9 kB
import { registerPlugin } from '@capacitor/core'; import { SyncStatus, BundleStatus, UpdateErrorCode } from './definitions'; import { PluginManager } from './core/plugin-manager'; import { NativeUpdateError, ErrorCode } from './core/errors'; /** * Web implementation of the Native Update Plugin */ class NativeUpdatePluginWeb { constructor() { this.initialized = false; this.windowEventListeners = new Map(); this.pluginManager = PluginManager.getInstance(); } // Main plugin methods async initialize(config) { await this.pluginManager.initialize(config); this.initialized = true; // Setup window event bridge for AppUpdateNotifier events this.setupWindowEventBridge(); } isInitialized() { return this.initialized && this.pluginManager.isInitialized(); } async reset() { await this.pluginManager.reset(); } async cleanup() { await this.pluginManager.cleanup(); } // NativeUpdatePlugin methods async configure(config) { var _a; // Handle both UpdateConfig and wrapped PluginInitConfig formats let initConfig; if ('config' in config && typeof config.config === 'object') { // Format: { config: PluginInitConfig } initConfig = config.config; } else { // Format: UpdateConfig - convert to PluginInitConfig initConfig = { // Auto-imported Capacitor plugins will be added by plugin-manager baseUrl: (_a = config.liveUpdate) === null || _a === void 0 ? void 0 : _a.serverUrl, }; } if (!this.initialized) { // Auto-initialize with the provided config await this.initialize(initConfig); } else { // Apply plugin configuration const configManager = this.pluginManager.getConfigManager(); configManager.configure(initConfig); } } async getSecurityInfo() { return { enforceHttps: true, certificatePinning: { enabled: false, pins: [], }, validateInputs: true, secureStorage: true, }; } // LiveUpdatePlugin methods async sync(_options) { const bundleManager = this.pluginManager.getBundleManager(); try { // Check for updates const currentBundle = await bundleManager.getActiveBundle(); const currentVersion = (currentBundle === null || currentBundle === void 0 ? void 0 : currentBundle.version) || '1.0.0'; // For now, return up-to-date status return { status: SyncStatus.UP_TO_DATE, version: currentVersion, }; } catch (error) { return { status: SyncStatus.ERROR, error: { code: UpdateErrorCode.UNKNOWN_ERROR, message: error instanceof Error ? error.message : 'Sync failed', }, }; } } async download(options) { const downloadManager = this.pluginManager.getDownloadManager(); const bundleManager = this.pluginManager.getBundleManager(); const blob = await downloadManager.downloadWithRetry(options.url, options.version); const path = await downloadManager.saveBlob(options.version, blob); const bundleInfo = { bundleId: options.version, version: options.version, path, downloadTime: Date.now(), size: blob.size, status: BundleStatus.READY, checksum: options.checksum, signature: options.signature, verified: false, }; await bundleManager.saveBundleInfo(bundleInfo); return bundleInfo; } async set(bundle) { const bundleManager = this.pluginManager.getBundleManager(); await bundleManager.setActiveBundle(bundle.bundleId); } async reload() { // In web implementation, we can reload the page if (typeof window !== 'undefined') { window.location.reload(); } } async current() { const bundleManager = this.pluginManager.getBundleManager(); const bundle = await bundleManager.getActiveBundle(); if (!bundle) { throw new NativeUpdateError(ErrorCode.FILE_NOT_FOUND, 'No active bundle found'); } return bundle; } async list() { const bundleManager = this.pluginManager.getBundleManager(); return bundleManager.getAllBundles(); } async delete(options) { const bundleManager = this.pluginManager.getBundleManager(); if (options.bundleId) { await bundleManager.deleteBundle(options.bundleId); } else if (options.keepVersions !== undefined) { // Delete old versions keeping the specified number const bundles = await bundleManager.getAllBundles(); const sortedBundles = bundles.sort((a, b) => b.downloadTime - a.downloadTime); for (let i = options.keepVersions; i < sortedBundles.length; i++) { await bundleManager.deleteBundle(sortedBundles[i].bundleId); } } } async notifyAppReady() { // Mark the current bundle as stable const bundleManager = this.pluginManager.getBundleManager(); const activeBundle = await bundleManager.getActiveBundle(); if (activeBundle) { activeBundle.status = BundleStatus.ACTIVE; await bundleManager.saveBundleInfo(activeBundle); } } async getLatest() { // For web, we'll return no update available return { available: false, }; } async setChannel(channel) { // Store the channel preference const preferences = this.pluginManager .getConfigManager() .get('preferences'); if (preferences) { await preferences.set({ key: 'update_channel', value: channel, }); } } async setUpdateUrl(url) { const configManager = this.pluginManager.getConfigManager(); configManager.configure({ baseUrl: url }); } async validateUpdate(options) { const securityValidator = this.pluginManager.getSecurityValidator(); try { // Validate checksum const isValid = await securityValidator.validateChecksum(new ArrayBuffer(0), // Placeholder for bundle data options.checksum); return { isValid, details: { checksumValid: isValid, signatureValid: true, sizeValid: true, versionValid: true, }, }; } catch (error) { return { isValid: false, error: error instanceof Error ? error.message : 'Validation failed', }; } } // AppUpdatePlugin methods async getAppUpdateInfo() { const appUpdateManager = this.pluginManager.getAppUpdateManager(); return appUpdateManager.getAppUpdateInfo(); } async performImmediateUpdate() { const appUpdateManager = this.pluginManager.getAppUpdateManager(); return appUpdateManager.performImmediateUpdate(); } async startFlexibleUpdate() { const appUpdateManager = this.pluginManager.getAppUpdateManager(); return appUpdateManager.startFlexibleUpdate(); } async completeFlexibleUpdate() { const appUpdateManager = this.pluginManager.getAppUpdateManager(); return appUpdateManager.completeFlexibleUpdate(); } async openAppStore(options) { const appUpdateManager = this.pluginManager.getAppUpdateManager(); return appUpdateManager.openAppStore(options); } // AppReviewPlugin methods async requestReview() { return { displayed: false, error: 'Reviews are not supported on web', }; } async canRequestReview() { return { canRequest: false, reason: 'Reviews are not supported on web', }; } // BackgroundUpdatePlugin methods async enableBackgroundUpdates(config) { // Store the configuration const preferences = this.pluginManager .getConfigManager() .get('preferences'); if (preferences) { await preferences.set({ key: 'background_update_config', value: JSON.stringify(config), }); } } async disableBackgroundUpdates() { const preferences = this.pluginManager .getConfigManager() .get('preferences'); if (preferences) { await preferences.remove({ key: 'background_update_config' }); } } async getBackgroundUpdateStatus() { return { enabled: false, isRunning: false, checkCount: 0, failureCount: 0, }; } async scheduleBackgroundCheck(_interval) { // Not supported on web throw new NativeUpdateError(ErrorCode.PLATFORM_NOT_SUPPORTED, 'Background updates are not supported on web'); } async triggerBackgroundCheck() { return { success: false, updatesFound: false, notificationSent: false, error: { code: UpdateErrorCode.PLATFORM_NOT_SUPPORTED, message: 'Background updates are not supported on web', }, }; } async setNotificationPreferences(preferences) { // Store preferences but notifications aren't supported on web const prefs = this.pluginManager.getConfigManager().get('preferences'); if (prefs) { await prefs.set({ key: 'notification_preferences', value: JSON.stringify(preferences), }); } } async getNotificationPermissions() { return { granted: false, canRequest: false, }; } async requestNotificationPermissions() { return false; } // Event listener methods async addListener(eventName, listenerFunc) { const eventEmitter = this.pluginManager.getEventEmitter(); // Add listener to central event emitter const removeListener = eventEmitter.addListener(eventName, listenerFunc); return { remove: async () => { removeListener(); }, }; } async removeAllListeners() { const eventEmitter = this.pluginManager.getEventEmitter(); eventEmitter.removeAllListeners(); } /** * Setup bridge between window custom events and central event emitter */ setupWindowEventBridge() { const eventEmitter = this.pluginManager.getEventEmitter(); // List of events emitted by AppUpdateNotifier via window const windowEvents = [ 'appUpdateAvailable', 'appUpdateProgress', 'appUpdateReady', 'appUpdateFailed', 'appUpdateNotificationClicked', 'appUpdateInstallClicked' ]; windowEvents.forEach(eventName => { const listener = (event) => { eventEmitter.emit(eventName, event.detail); }; window.addEventListener(eventName, listener); this.windowEventListeners.set(eventName, listener); }); } } /** * Register the plugin */ const NativeUpdate = registerPlugin('NativeUpdate', { web: () => new NativeUpdatePluginWeb(), }); export { NativeUpdate }; //# sourceMappingURL=plugin.js.map