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
JavaScript
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