UNPKG

native-update

Version:

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

559 lines (434 loc) 13.8 kB
# Native Update - API Reference This document provides a complete API reference for the Native Update plugin. ## Table of Contents - [Configuration](#configuration) - [Live Update API](#live-update-api) - [App Update API](#app-update-api) - [App Review API](#app-review-api) - [Security API](#security-api) - [Types and Interfaces](#types-and-interfaces) - [Error Codes](#error-codes) - [Security Best Practices](#security-best-practices) ## Configuration ### configure(config: UpdateConfig): Promise<void> Configure the plugin with your settings. ```typescript await NativeUpdate.configure({ liveUpdate: { appId: 'your-app-id', serverUrl: 'https://your-update-server.com', // HTTPS required channel: 'production', autoUpdate: true, publicKey: 'your-public-key', // For signature verification requireSignature: true, // Enforce signature verification maxBundleSize: 50 * 1024 * 1024, // 50MB limit checksumAlgorithm: 'SHA-256', // Required checksum validation }, appUpdate: { minimumVersion: '1.0.0', updatePriority: 3, allowDowngrade: false, // Prevent version downgrade attacks }, appReview: { minimumDaysSinceInstall: 7, minimumLaunchCount: 3, }, security: { enforceHttps: true, certificatePinning: { enabled: true, certificates: ['sha256/...'], // Pin update server certificates }, }, }); ``` ## Live Update API ### sync(options?: SyncOptions): Promise<SyncResult> Synchronize with the update server and apply updates if available. ```typescript const result = await NativeUpdate.sync({ channel: 'production', updateMode: 'background', }); console.log(result.status); // 'UP_TO_DATE' | 'UPDATE_AVAILABLE' | 'UPDATE_INSTALLED' | 'ERROR' ``` ### download(options: DownloadOptions): Promise<BundleInfo> Download a specific bundle version. ```typescript const bundle = await NativeUpdate.download({ url: 'https://updates.example.com/bundle-v2.0.0.zip', // HTTPS enforced version: '2.0.0', checksum: 'sha256:...', // Required for integrity verification signature: 'base64...', // Required if signature verification enabled maxRetries: 3, timeout: 30000, // 30 second timeout }); ``` ### set(bundle: BundleInfo): Promise<void> Set the active bundle to a specific downloaded version. ```typescript await NativeUpdate.set({ bundleId: 'bundle-123', version: '2.0.0', path: '/path/to/bundle', }); ``` ### reload(): Promise<void> Reload the app with the currently active bundle. ```typescript await NativeUpdate.reload(); ``` ### reset(): Promise<void> Reset to the original bundle shipped with the app. ```typescript await NativeUpdate.reset(); ``` ### current(): Promise<BundleInfo> Get information about the currently active bundle. ```typescript const currentBundle = await NativeUpdate.current(); console.log(currentBundle.version); ``` ### list(): Promise<BundleInfo[]> List all downloaded bundles. ```typescript const bundles = await NativeUpdate.list(); bundles.forEach((bundle) => { console.log(`${bundle.version} - ${bundle.downloadTime}`); }); ``` ### delete(options: DeleteOptions): Promise<void> Delete a specific bundle or clean up old bundles. ```typescript // Delete specific bundle await NativeUpdate.delete({ bundleId: 'bundle-123' }); // Clean up old bundles await NativeUpdate.delete({ keepVersions: 2, olderThan: Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days }); ``` ### notifyAppReady(): Promise<void> Notify that the app has successfully started with the new bundle. ```typescript // Call this after your app has successfully initialized await NativeUpdate.notifyAppReady(); ``` ### getLatest(): Promise<LatestVersion> Check for the latest available version without downloading. ```typescript const latest = await NativeUpdate.getLatest(); if (latest.available) { console.log(`New version ${latest.version} available`); } ``` ### setChannel(channel: string): Promise<void> Switch to a different update channel. ```typescript await NativeUpdate.setChannel('beta'); ``` ### setUpdateUrl(url: string): Promise<void> Change the update server URL. ```typescript await NativeUpdate.setUpdateUrl('https://new-server.com/updates'); ``` ## App Update API ### getAppUpdateInfo(): Promise<AppUpdateInfo> Check for native app updates in the app store. ```typescript const updateInfo = await NativeUpdate.getAppUpdateInfo(); if (updateInfo.updateAvailable) { console.log(`Version ${updateInfo.availableVersion} is available`); } ``` ### performImmediateUpdate(): Promise<void> Start an immediate (blocking) update from the app store. ```typescript try { await NativeUpdate.performImmediateUpdate(); // App will restart after update } catch (error) { if (error.code === 'UPDATE_CANCELLED') { // User cancelled the update } } ``` ### startFlexibleUpdate(): Promise<void> Start a flexible (background) update from the app store. ```typescript await NativeUpdate.startFlexibleUpdate(); // Update downloads in background ``` ### completeFlexibleUpdate(): Promise<void> Complete a flexible update that has finished downloading. ```typescript await NativeUpdate.completeFlexibleUpdate(); // App will restart ``` ### openAppStore(options?: OpenAppStoreOptions): Promise<void> Open the app store page for the app. ```typescript await NativeUpdate.openAppStore({ appId: 'com.example.app', // Optional, uses current app ID if not provided }); ``` ## App Review API ### requestReview(): Promise<ReviewResult> Request an in-app review from the user. ```typescript const result = await NativeUpdate.requestReview(); if (result.shown) { console.log('Review dialog was shown'); } ``` ### canRequestReview(): Promise<CanRequestReviewResult> Check if it's appropriate to request a review. ```typescript const canRequest = await NativeUpdate.canRequestReview(); if (canRequest.allowed) { await NativeUpdate.requestReview(); } else { console.log(`Cannot request: ${canRequest.reason}`); } ``` ## Types and Interfaces ### UpdateConfig ```typescript interface UpdateConfig { liveUpdate?: LiveUpdateConfig; appUpdate?: AppUpdateConfig; appReview?: AppReviewConfig; security?: SecurityConfig; } interface LiveUpdateConfig { appId: string; serverUrl: string; // Must be HTTPS channel?: string; autoUpdate?: boolean; updateStrategy?: 'immediate' | 'background' | 'manual'; publicKey?: string; // Base64 encoded public key for signature verification requireSignature?: boolean; // Default: true checksumAlgorithm?: 'SHA-256' | 'SHA-512'; // Default: 'SHA-256' checkInterval?: number; allowEmulator?: boolean; // Default: false for production mandatoryInstallMode?: InstallMode; optionalInstallMode?: InstallMode; maxBundleSize?: number; // Maximum allowed bundle size in bytes allowedHosts?: string[]; // Whitelist of allowed update servers } interface AppUpdateConfig { minimumVersion?: string; updatePriority?: number; storeUrl?: { android?: string; ios?: string; }; checkOnAppStart?: boolean; allowDowngrade?: boolean; // Default: false } interface AppReviewConfig { minimumDaysSinceInstall?: number; minimumDaysSinceLastPrompt?: number; minimumLaunchCount?: number; customTriggers?: string[]; interface SecurityConfig { enforceHttps?: boolean; // Default: true certificatePinning?: { enabled: boolean; certificates: string[]; // SHA256 fingerprints includeSubdomains?: boolean; maxAge?: number; // Pin duration in seconds }; validateInputs?: boolean; // Default: true secureStorage?: boolean; // Default: true logSecurityEvents?: boolean; // Default: true in debug } debugMode?: boolean; } ``` ### SyncResult ```typescript interface SyncResult { status: 'UP_TO_DATE' | 'UPDATE_AVAILABLE' | 'UPDATE_INSTALLED' | 'ERROR'; version?: string; description?: string; mandatory?: boolean; error?: UpdateError; } ``` ### BundleInfo ```typescript interface BundleInfo { bundleId: string; version: string; path: string; // Sanitized path within app sandbox downloadTime: number; size: number; status: 'PENDING' | 'DOWNLOADING' | 'READY' | 'ACTIVE' | 'FAILED'; checksum: string; // SHA-256 checksum of the bundle signature?: string; // Digital signature if available verified: boolean; // Indicates if bundle passed all security checks metadata?: Record<string, any>; } ``` ### AppUpdateInfo ```typescript interface AppUpdateInfo { updateAvailable: boolean; currentVersion: string; availableVersion?: string; updatePriority?: number; immediateUpdateAllowed?: boolean; flexibleUpdateAllowed?: boolean; clientVersionStalenessDays?: number; installStatus?: InstallStatus; bytesDownloaded?: number; totalBytesToDownload?: number; } ``` ### ReviewResult ```typescript interface ReviewResult { shown: boolean; error?: string; } ``` ## Error Codes The plugin uses standard error codes for different failure scenarios: ### Live Update Errors - `NETWORK_ERROR` - Network connection failed - `SERVER_ERROR` - Update server returned an error - `DOWNLOAD_ERROR` - Bundle download failed - `VERIFICATION_ERROR` - Bundle signature verification failed - `CHECKSUM_ERROR` - Bundle checksum validation failed - `STORAGE_ERROR` - Insufficient storage or write error - `INSTALL_ERROR` - Bundle installation failed - `ROLLBACK_ERROR` - Rollback operation failed - `INSECURE_URL` - Attempted to use non-HTTPS URL - `INVALID_CERTIFICATE` - Certificate pinning validation failed - `SIZE_LIMIT_EXCEEDED` - Bundle exceeds maximum allowed size - `PERMISSION_DENIED` - Required permission not granted - `PATH_TRAVERSAL` - Attempted directory traversal detected ### App Update Errors - `UPDATE_NOT_AVAILABLE` - No update available in app store - `UPDATE_IN_PROGRESS` - Another update is already in progress - `UPDATE_CANCELLED` - User cancelled the update - `PLATFORM_NOT_SUPPORTED` - Feature not supported on this platform ### App Review Errors - `REVIEW_NOT_SUPPORTED` - In-app reviews not supported - `QUOTA_EXCEEDED` - Review request quota exceeded - `CONDITIONS_NOT_MET` - Review conditions not satisfied ## Events The plugin emits events for update progress and state changes: ```typescript // Listen for download progress NativeUpdate.addListener('downloadProgress', (progress) => { console.log(`Downloaded ${progress.percent}%`); }); // Listen for update state changes NativeUpdate.addListener('updateStateChanged', (state) => { console.log(`Update state: ${state.status}`); }); // Remove listeners when done NativeUpdate.removeAllListeners(); ``` ## Security API ### validateUpdate(options: ValidateOptions): Promise<ValidationResult> Validate an update bundle before installation. ```typescript const validation = await NativeUpdate.validateUpdate({ bundlePath: '/path/to/bundle', checksum: 'sha256:...', signature: 'base64...', maxSize: 50 * 1024 * 1024, }); if (!validation.isValid) { console.error(`Validation failed: ${validation.error}`); } ``` ### getSecurityInfo(): Promise<SecurityInfo> Get current security configuration and status. ```typescript const security = await NativeUpdate.getSecurityInfo(); console.log(`HTTPS enforced: ${security.enforceHttps}`); console.log(`Certificate pinning: ${security.certificatePinning.enabled}`); ``` ## Platform-Specific Notes ### iOS - App updates require manual version checking against iTunes API - In-app reviews use StoreKit framework - Maximum 3 review requests per year per user - Uses Keychain Services for secure storage - File operations validated within app sandbox ### Android - App updates use Google Play Core Library - Requires Play Store app to be installed - In-app reviews use Play Core Review API - Uses Android Keystore for secure storage - Runtime permissions required for storage access ### Web - Live updates work through service worker updates - App updates and reviews show fallback UI - Limited functionality compared to native platforms - Uses Web Crypto API for security features ## Security Best Practices ### Secure Configuration Example ```typescript // Recommended security configuration await NativeUpdate.configure({ liveUpdate: { appId: 'your-app-id', serverUrl: 'https://updates.example.com', // Always use HTTPS publicKey: process.env.UPDATE_PUBLIC_KEY, // Never hardcode keys requireSignature: true, checksumAlgorithm: 'SHA-256', maxBundleSize: 50 * 1024 * 1024, allowedHosts: ['updates.example.com'], // Whitelist update servers allowEmulator: false, // Disable in production }, security: { enforceHttps: true, certificatePinning: { enabled: true, certificates: [process.env.UPDATE_CERT_PIN], }, validateInputs: true, secureStorage: true, }, }); ``` ### Input Validation Always validate user inputs and options: ```typescript // Good: Validate before use try { if (!isValidUrl(updateUrl) || !updateUrl.startsWith('https://')) { throw new Error('Invalid update URL'); } await NativeUpdate.download({ url: updateUrl, checksum: checksum, signature: signature, }); } catch (error) { // Handle validation error } ``` ### Error Handling Handle errors without exposing sensitive information: ````typescript try { await NativeUpdate.sync(); } catch (error) { // Don't expose internal paths or server details if (error.code === 'VERIFICATION_ERROR') { console.error('Update verification failed'); // Log detailed error internally, show generic message to user } } ``` ## Support For questions, issues, or contributions: - Email: aoneahsan@gmail.com - Website: [native-update.web.app](https://native-update.web.app) - Author: Ahsan Mahmood ([Portfolio](https://aoneahsan.com))