UNPKG

capacitor-firebase-kit

Version:

Provider-less Firebase Kit - Universal Firebase services integration for React, React Native, and Capacitor apps

729 lines 28.6 kB
import { WebPlugin } from '@capacitor/core'; import { FirebaseKitErrorCode, } from './definitions'; /** * Web implementation of Firebase Kit Plugin * Provides partial implementation for web platform where applicable */ export class FirebaseKitWeb extends WebPlugin { constructor() { super(); // Initialize service implementations this.appCheck = new AppCheckWebService(); this.adMob = new AdMobWebService(); this.crashlytics = new CrashlyticsWebService(); this.performance = new PerformanceWebService(); this.analytics = new AnalyticsWebService(); this.remoteConfig = new RemoteConfigWebService(); } } /** * Web implementation of App Check Service */ class AppCheckWebService extends WebPlugin { constructor() { super(...arguments); this.appCheckInstance = null; this.isInitialized = false; } async initialize(options) { var _a; // Check if Firebase is loaded if (typeof window === 'undefined' || !window.firebase) { throw this.createError('Firebase SDK not loaded', FirebaseKitErrorCode.NOT_INITIALIZED); } try { const firebase = window.firebase; // Initialize App Check based on provider if (options.provider === 'recaptchaV3' || options.provider === 'recaptchaEnterprise') { if (!options.siteKey) { throw this.createError('Site key required for reCAPTCHA provider', FirebaseKitErrorCode.INVALID_ARGUMENT); } const provider = options.provider === 'recaptchaV3' ? new firebase.appCheck.ReCaptchaV3Provider(options.siteKey) : new firebase.appCheck.ReCaptchaEnterpriseProvider(options.siteKey); this.appCheckInstance = firebase.appCheck(); // Default to false for token auto-refresh if not specified const isTokenAutoRefreshEnabled = (_a = options.isTokenAutoRefreshEnabled) !== null && _a !== void 0 ? _a : false; await this.appCheckInstance.activate(provider, isTokenAutoRefreshEnabled); this.isInitialized = true; } else if (options.provider === 'debug') { // Debug provider for development if (options.debugToken) { window.FIREBASE_APPCHECK_DEBUG_TOKEN = options.debugToken; } this.appCheckInstance = firebase.appCheck(); this.isInitialized = true; } else { // Other providers not supported on web throw this.createError(`Provider ${options.provider} not supported on web platform`, FirebaseKitErrorCode.NOT_SUPPORTED_ON_PLATFORM); } } catch (error) { throw this.createError(error.message || 'Failed to initialize App Check', FirebaseKitErrorCode.APP_CHECK_PROVIDER_ERROR); } } async getToken(options) { this.ensureInitialized(); try { const tokenResult = await this.appCheckInstance.getToken(options === null || options === void 0 ? void 0 : options.forceRefresh); return { token: tokenResult.token, expireTimeMillis: tokenResult.expireTimeMillis, }; } catch (error) { throw this.createError(error.message || 'Failed to get App Check token', FirebaseKitErrorCode.APP_CHECK_PROVIDER_ERROR); } } async setTokenAutoRefreshEnabled(options) { this.ensureInitialized(); this.appCheckInstance.setTokenAutoRefreshEnabled(options.enabled); } async addListener(_eventName, listenerFunc) { this.ensureInitialized(); const unsubscribe = this.appCheckInstance.onTokenChanged((tokenResult) => { listenerFunc({ token: tokenResult.token, expireTimeMillis: tokenResult.expireTimeMillis, }); }); return { remove: async () => { unsubscribe(); }, }; } ensureInitialized() { if (!this.isInitialized) { throw this.createError('App Check not initialized. Call initialize() first.', FirebaseKitErrorCode.NOT_INITIALIZED); } } createError(message, code) { const error = new Error(message); error.code = code; return error; } } /** * Web implementation of AdMob Service */ class AdMobWebService extends WebPlugin { async initialize(_options) { throw this.unimplemented('AdMob is not supported on web platform. Use native platforms (iOS/Android) for ad functionality.'); } async requestConsentInfo(_options) { throw this.unimplemented('AdMob is not supported on web platform.'); } async showConsentForm() { throw this.unimplemented('AdMob is not supported on web platform.'); } async resetConsentInfo() { throw this.unimplemented('AdMob is not supported on web platform.'); } async setRequestConfiguration(_options) { throw this.unimplemented('AdMob is not supported on web platform.'); } async showBanner(_options) { throw this.unimplemented('AdMob is not supported on web platform.'); } async hideBanner() { throw this.unimplemented('AdMob is not supported on web platform.'); } async removeBanner() { throw this.unimplemented('AdMob is not supported on web platform.'); } async loadInterstitial(_options) { throw this.unimplemented('AdMob is not supported on web platform.'); } async showInterstitial() { throw this.unimplemented('AdMob is not supported on web platform.'); } async loadRewarded(_options) { throw this.unimplemented('AdMob is not supported on web platform.'); } async showRewarded() { throw this.unimplemented('AdMob is not supported on web platform.'); } async loadRewardedInterstitial(_options) { throw this.unimplemented('AdMob is not supported on web platform.'); } async showRewardedInterstitial() { throw this.unimplemented('AdMob is not supported on web platform.'); } async addListener(_eventName, _listenerFunc) { throw this.unimplemented('AdMob is not supported on web platform.'); } } /** * Web implementation of Crashlytics Service */ class CrashlyticsWebService extends WebPlugin { constructor() { super(...arguments); this.logs = []; this.customKeys = {}; this.userId = null; } async crash() { // Log warning instead of crashing on web console.warn('[FirebaseKit] Crashlytics crash() called - simulated on web platform'); console.warn('Crash simulation: Manual crash triggered'); } async forceCrash(options) { console.error('[FirebaseKit] Force crash:', options.message); throw new Error(`Forced crash: ${options.message}`); } async log(options) { this.logs.push(`[${new Date().toISOString()}] ${options.message}`); console.warn('[FirebaseKit Crashlytics]', options.message); } async logException(options) { var _a; const error = { message: options.message, code: options.code, domain: options.domain, stackTrace: options.stackTrace, timestamp: new Date().toISOString(), userId: this.userId, customKeys: Object.assign({}, this.customKeys), }; console.error('[FirebaseKit Crashlytics Exception]', error); // In a real implementation, this would send to a logging service if ((_a = window.firebase) === null || _a === void 0 ? void 0 : _a.crashlytics) { try { window.firebase.crashlytics().recordError(new Error(options.message)); } catch (_b) { console.warn('Firebase Crashlytics not available on web'); } } } async setUserId(options) { this.userId = options.userId; console.warn('[FirebaseKit Crashlytics] User ID set:', options.userId); } async setCustomKeys(options) { this.customKeys = Object.assign(Object.assign({}, this.customKeys), options.attributes); console.warn('[FirebaseKit Crashlytics] Custom keys updated:', this.customKeys); } async setCrashlyticsCollectionEnabled(options) { console.warn('[FirebaseKit Crashlytics] Collection enabled:', options.enabled); } async isCrashlyticsCollectionEnabled() { // Always return true for web as it's simulated return { enabled: true }; } async deleteUnsentReports() { this.logs = []; console.warn('[FirebaseKit Crashlytics] Unsent reports deleted'); } async sendUnsentReports() { console.warn('[FirebaseKit Crashlytics] Sending reports:', this.logs); // In a real implementation, this would send logs to a service } async recordBreadcrumb(options) { const breadcrumb = `[Breadcrumb] ${options.name}${options.params ? `: ${JSON.stringify(options.params)}` : ''}`; this.logs.push(`[${new Date().toISOString()}] ${breadcrumb}`); console.warn('[FirebaseKit Crashlytics]', breadcrumb); } } /** * Web implementation of Performance Service */ class PerformanceWebService extends WebPlugin { constructor() { super(...arguments); this.traces = new Map(); this.performanceInstance = null; } async initialize(options) { var _a, _b; if (typeof window !== 'undefined' && ((_a = window.firebase) === null || _a === void 0 ? void 0 : _a.performance)) { try { this.performanceInstance = window.firebase.performance(); // Set performance collection enabled with proper default (true if not specified) const enabled = (_b = options === null || options === void 0 ? void 0 : options.enabled) !== null && _b !== void 0 ? _b : true; this.performanceInstance.setPerformanceCollectionEnabled(enabled); // Set data collection enabled if specified if ((options === null || options === void 0 ? void 0 : options.dataCollectionEnabled) !== undefined) { this.performanceInstance.setPerformanceCollectionEnabled(options.dataCollectionEnabled); } // Note: instrumentationEnabled is typically handled at SDK initialization level // and may not be configurable after initialization on web } catch (error) { console.warn('[FirebaseKit Performance] Failed to initialize:', error); } } } async setPerformanceCollectionEnabled(options) { if (this.performanceInstance) { this.performanceInstance.setPerformanceCollectionEnabled(options.enabled); } } async isPerformanceCollectionEnabled() { if (this.performanceInstance) { return { enabled: this.performanceInstance.isPerformanceCollectionEnabled() }; } return { enabled: false }; } async startTrace(options) { const traceId = `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; if (this.performanceInstance) { try { const trace = this.performanceInstance.trace(options.traceName); trace.start(); this.traces.set(traceId, trace); } catch (error) { console.warn('[FirebaseKit Performance] Failed to start trace:', error); } } else { // Fallback implementation this.traces.set(traceId, { name: options.traceName, startTime: performance.now(), metrics: {}, attributes: {}, }); } return { traceId }; } async stopTrace(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.stop) { trace.stop(); } else { trace.duration = performance.now() - trace.startTime; } this.traces.delete(options.traceId); } } async incrementMetric(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.incrementMetric) { trace.incrementMetric(options.metricName, options.value); } else { trace.metrics[options.metricName] = (trace.metrics[options.metricName] || 0) + options.value; } } } async setMetric(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.putMetric) { trace.putMetric(options.metricName, options.value); } else { trace.metrics[options.metricName] = options.value; } } } async getMetric(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.getMetric) { return { value: trace.getMetric(options.metricName) }; } else { return { value: trace.metrics[options.metricName] || 0 }; } } return { value: 0 }; } async putAttribute(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.putAttribute) { trace.putAttribute(options.attribute, options.value); } else { trace.attributes[options.attribute] = options.value; } } } async getAttributes(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.getAttributes) { return { attributes: trace.getAttributes() }; } else { return { attributes: trace.attributes || {} }; } } return { attributes: {} }; } async removeAttribute(options) { const trace = this.traces.get(options.traceId); if (trace) { if (trace.removeAttribute) { trace.removeAttribute(options.attribute); } else if (trace.attributes) { delete trace.attributes[options.attribute]; } } } async startScreenTrace(options) { return this.startTrace({ traceName: `screen_${options.screenName}` }); } async stopScreenTrace(options) { return this.stopTrace(options); } async recordNetworkRequest(options) { if (this.performanceInstance) { try { // Firebase Performance automatically tracks network requests console.warn('[FirebaseKit Performance] Network request recorded:', options.url); } catch (error) { console.warn('[FirebaseKit Performance] Failed to record network request:', error); } } } } /** * Web implementation of Analytics Service */ class AnalyticsWebService extends WebPlugin { constructor() { super(...arguments); this.analyticsInstance = null; this.gtag = null; this.isInitialized = false; } async initialize(options) { var _a, _b; // Try Firebase Analytics first if (typeof window !== 'undefined' && ((_a = window.firebase) === null || _a === void 0 ? void 0 : _a.analytics)) { try { this.analyticsInstance = window.firebase.analytics(); this.isInitialized = true; // Set collection enabled with proper default (true if not specified) const collectionEnabled = (_b = options === null || options === void 0 ? void 0 : options.collectionEnabled) !== null && _b !== void 0 ? _b : true; await this.analyticsInstance.setAnalyticsCollectionEnabled(collectionEnabled); // Set session timeout if provided if ((options === null || options === void 0 ? void 0 : options.sessionTimeoutDuration) !== undefined) { await this.analyticsInstance.setSessionTimeoutDuration(options.sessionTimeoutDuration * 1000); } } catch (error) { console.warn('[FirebaseKit Analytics] Firebase Analytics not available:', error); } } // Fallback to gtag if available if (!this.isInitialized && typeof window !== 'undefined' && window.gtag) { this.gtag = window.gtag; this.isInitialized = true; } if (!this.isInitialized) { throw this.createError('Analytics not available. Load Firebase or Google Analytics first.', FirebaseKitErrorCode.NOT_INITIALIZED); } } async setCollectionEnabled(options) { if (this.analyticsInstance) { await this.analyticsInstance.setAnalyticsCollectionEnabled(options.enabled); } else if (this.gtag) { this.gtag('config', 'GA_MEASUREMENT_ID', { 'send_page_view': options.enabled, 'allow_google_signals': options.enabled, }); } } async setCurrentScreen(options) { this.ensureInitialized(); if (this.analyticsInstance) { await this.analyticsInstance.setCurrentScreen(options.screenName, options.screenClass); } else if (this.gtag) { this.gtag('event', 'screen_view', { screen_name: options.screenName, screen_class: options.screenClass, }); } } async logEvent(options) { this.ensureInitialized(); if (this.analyticsInstance) { await this.analyticsInstance.logEvent(options.name, options.params); } else if (this.gtag) { this.gtag('event', options.name, options.params); } } async setUserProperty(options) { this.ensureInitialized(); if (this.analyticsInstance) { await this.analyticsInstance.setUserProperties({ [options.key]: options.value }); } else if (this.gtag) { this.gtag('set', 'user_properties', { [options.key]: options.value }); } } async setUserId(options) { this.ensureInitialized(); if (this.analyticsInstance) { await this.analyticsInstance.setUserId(options.userId); } else if (this.gtag) { this.gtag('config', 'GA_MEASUREMENT_ID', { 'user_id': options.userId, }); } } async setSessionTimeoutDuration(options) { this.ensureInitialized(); if (this.analyticsInstance) { await this.analyticsInstance.setSessionTimeoutDuration(options.duration); } else if (this.gtag) { this.gtag('config', 'GA_MEASUREMENT_ID', { 'session_timeout': options.duration, }); } } async getAppInstanceId() { this.ensureInitialized(); if (this.analyticsInstance) { const appInstanceId = await this.analyticsInstance.getAppInstanceId(); return { appInstanceId }; } // Generate a pseudo app instance ID for gtag const storedId = localStorage.getItem('firebase_kit_app_instance_id'); if (storedId) { return { appInstanceId: storedId }; } const newId = `web_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; localStorage.setItem('firebase_kit_app_instance_id', newId); return { appInstanceId: newId }; } async resetAnalyticsData() { this.ensureInitialized(); if (this.analyticsInstance) { // Firebase doesn't have a direct reset method on web console.warn('[FirebaseKit Analytics] Reset not directly supported on web. Clear user properties and ID instead.'); await this.analyticsInstance.setUserId(null); } localStorage.removeItem('firebase_kit_app_instance_id'); } async setConsent(options) { this.ensureInitialized(); if (this.gtag) { const consentConfig = {}; if (options.analyticsStorage !== undefined) { consentConfig.analytics_storage = options.analyticsStorage; } if (options.adStorage !== undefined) { consentConfig.ad_storage = options.adStorage; } if (options.adUserData !== undefined) { consentConfig.ad_user_data = options.adUserData; } if (options.adPersonalization !== undefined) { consentConfig.ad_personalization = options.adPersonalization; } this.gtag('consent', 'update', consentConfig); } else { console.warn('[FirebaseKit Analytics] Consent management requires gtag'); } } async setDefaultEventParameters(options) { this.ensureInitialized(); if (this.analyticsInstance) { // Firebase Analytics web SDK doesn't support default parameters directly console.warn('[FirebaseKit Analytics] Default parameters not supported in Firebase Analytics web SDK'); } else if (this.gtag) { if (options.params) { this.gtag('set', options.params); } } } ensureInitialized() { if (!this.isInitialized) { throw this.createError('Analytics not initialized. Call initialize() first.', FirebaseKitErrorCode.NOT_INITIALIZED); } } createError(message, code) { const error = new Error(message); error.code = code; return error; } } /** * Web implementation of Remote Config Service */ class RemoteConfigWebService extends WebPlugin { constructor() { super(...arguments); this.remoteConfigInstance = null; this.isInitialized = false; this.configListeners = new Map(); } async initialize(options) { var _a, _b, _c; if (typeof window === 'undefined' || !((_a = window.firebase) === null || _a === void 0 ? void 0 : _a.remoteConfig)) { throw this.createError('Firebase Remote Config not loaded', FirebaseKitErrorCode.NOT_INITIALIZED); } try { this.remoteConfigInstance = window.firebase.remoteConfig(); // Set settings with proper defaults const settings = {}; // Default minimum fetch interval is 12 hours (43200 seconds) for production const minimumFetchIntervalInSeconds = (_b = options === null || options === void 0 ? void 0 : options.minimumFetchIntervalInSeconds) !== null && _b !== void 0 ? _b : 43200; settings.minimumFetchIntervalMillis = minimumFetchIntervalInSeconds * 1000; // Default fetch timeout is 60 seconds const fetchTimeoutInSeconds = (_c = options === null || options === void 0 ? void 0 : options.fetchTimeoutInSeconds) !== null && _c !== void 0 ? _c : 60; settings.fetchTimeoutMillis = fetchTimeoutInSeconds * 1000; this.remoteConfigInstance.settings = settings; this.isInitialized = true; } catch (error) { throw this.createError(error.message || 'Failed to initialize Remote Config', FirebaseKitErrorCode.INTERNAL); } } async setDefaults(options) { this.ensureInitialized(); this.remoteConfigInstance.defaultConfig = options.defaults; } async fetch(options) { this.ensureInitialized(); try { if ((options === null || options === void 0 ? void 0 : options.minimumFetchIntervalInSeconds) !== undefined) { const settings = Object.assign({}, this.remoteConfigInstance.settings); settings.minimumFetchIntervalMillis = options.minimumFetchIntervalInSeconds * 1000; this.remoteConfigInstance.settings = settings; } await this.remoteConfigInstance.fetch(); } catch (error) { throw this.createError(error.message || 'Failed to fetch config', FirebaseKitErrorCode.CONFIG_FETCH_FAILED); } } async activate() { this.ensureInitialized(); try { const activated = await this.remoteConfigInstance.activate(); // Notify listeners if activated if (activated) { this.notifyConfigListeners(); } return { activated }; } catch (error) { throw this.createError(error.message || 'Failed to activate config', FirebaseKitErrorCode.CONFIG_UPDATE_FAILED); } } async fetchAndActivate(options) { this.ensureInitialized(); try { await this.fetch(options); return await this.activate(); } catch (error) { throw this.createError(error.message || 'Failed to fetch and activate config', FirebaseKitErrorCode.CONFIG_FETCH_FAILED); } } async getValue(options) { this.ensureInitialized(); const value = this.remoteConfigInstance.getValue(options.key); return { asString: value.asString(), asNumber: value.asNumber(), asBoolean: value.asBoolean(), source: value.getSource(), }; } async getAll() { this.ensureInitialized(); const all = this.remoteConfigInstance.getAll(); const values = {}; for (const [key, value] of Object.entries(all)) { values[key] = { asString: value.asString(), asNumber: value.asNumber(), asBoolean: value.asBoolean(), source: value.getSource(), }; } return { values }; } async getSettings() { this.ensureInitialized(); const settings = this.remoteConfigInstance.settings; return { minimumFetchIntervalInSeconds: settings.minimumFetchIntervalMillis / 1000, fetchTimeoutInSeconds: settings.fetchTimeoutMillis / 1000, }; } async setSettings(options) { this.ensureInitialized(); this.remoteConfigInstance.settings = { minimumFetchIntervalMillis: options.minimumFetchIntervalInSeconds * 1000, fetchTimeoutMillis: options.fetchTimeoutInSeconds * 1000, }; } async ensureInitialized() { if (!this.isInitialized) { await this.initialize(); } } async reset() { this.ensureInitialized(); // Clear all values by setting empty defaults this.remoteConfigInstance.defaultConfig = {}; // Clear listeners this.configListeners.clear(); } async addListener(eventName, listenerFunc) { if (!this.configListeners.has(eventName)) { this.configListeners.set(eventName, []); } this.configListeners.get(eventName).push(listenerFunc); return { remove: async () => { const listeners = this.configListeners.get(eventName); if (listeners) { const index = listeners.indexOf(listenerFunc); if (index > -1) { listeners.splice(index, 1); } } }, }; } notifyConfigListeners() { const listeners = this.configListeners.get('remoteConfigUpdated'); if (listeners) { // Get all keys that have been updated const all = this.remoteConfigInstance.getAll(); const updatedKeys = Object.keys(all); const update = { updatedKeys }; listeners.forEach(listener => { try { listener(update); } catch (error) { console.error('[FirebaseKit RemoteConfig] Error in listener:', error); } }); } } createError(message, code) { const error = new Error(message); error.code = code; return error; } } //# sourceMappingURL=web.js.map