capacitor-firebase-kit
Version:
Provider-less Firebase Kit - Universal Firebase services integration for React, React Native, and Capacitor apps
729 lines • 28.6 kB
JavaScript
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