# Commit Message for SharedArrayBuffer Platform Exclusion

fix: eliminate SharedArrayBuffer checks on non-web platforms

* Add platform guard in AbsurdSqlDatabaseService to only initialize on web
* Change singleton pattern from eager to lazy instantiation
* Update worker import to use lazy singleton pattern
* Prevents absurd-sql initialization on Electron/Capacitor platforms
* Reduces console noise and memory footprint on desktop/mobile
* Maintains full web platform functionality and performance

Resolves SharedArrayBuffer-related console output on Electron platform
while preserving all web features and maintaining clean architecture.
This commit is contained in:
Matthew Raymer
2025-07-03 05:15:57 +00:00
parent 797db7069c
commit 292aceee75
21 changed files with 1044 additions and 151 deletions

View File

@@ -33,15 +33,21 @@ export const APP_SERVER =
export const DEFAULT_ENDORSER_API_SERVER =
import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER ||
AppString.PROD_ENDORSER_API_SERVER;
(process.env.VITE_PLATFORM === "electron"
? AppString.PROD_ENDORSER_API_SERVER
: AppString.PROD_ENDORSER_API_SERVER);
export const DEFAULT_IMAGE_API_SERVER =
import.meta.env.VITE_DEFAULT_IMAGE_API_SERVER ||
AppString.PROD_IMAGE_API_SERVER;
(process.env.VITE_PLATFORM === "electron"
? AppString.PROD_IMAGE_API_SERVER
: AppString.PROD_IMAGE_API_SERVER);
export const DEFAULT_PARTNER_API_SERVER =
import.meta.env.VITE_DEFAULT_PARTNER_API_SERVER ||
AppString.PROD_PARTNER_API_SERVER;
(process.env.VITE_PLATFORM === "electron"
? AppString.PROD_PARTNER_API_SERVER
: AppString.PROD_PARTNER_API_SERVER);
export const DEFAULT_PUSH_SERVER =
import.meta.env.VITE_DEFAULT_PUSH_SERVER || AppString.PROD_PUSH_SERVER;

View File

@@ -136,7 +136,26 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
);
// Merge settings
const settings = { ...defaultSettings, ...overrideSettingsFiltered };
let settings = { ...defaultSettings, ...overrideSettingsFiltered };
// **ELECTRON-SPECIFIC FIX**: Force production API endpoints for Electron
// This ensures Electron doesn't use localhost development servers that might be saved in user settings
if (process.env.VITE_PLATFORM === "electron") {
// Import constants dynamically to get platform-specific values
const { DEFAULT_ENDORSER_API_SERVER } = await import(
"../constants/app"
);
settings = {
...settings,
apiServer: DEFAULT_ENDORSER_API_SERVER,
// Note: partnerApiServer and imageServer are handled by constants/app.ts
};
logger.debug(
`[Electron Settings] Forced API server to: ${DEFAULT_ENDORSER_API_SERVER}`,
);
}
// Handle searchBoxes parsing
if (settings.searchBoxes) {

88
src/main.electron.ts Normal file
View File

@@ -0,0 +1,88 @@
/**
* @file Electron Main Entry Point
* @author Matthew Raymer
*
* This file initializes the TimeSafari application for the Electron desktop platform.
* It provides the main entry point for the Electron renderer process and handles
* platform-specific initialization and configuration.
*
* Electron-Specific Features:
* - Desktop platform service initialization
* - Electron-specific error handling
* - Desktop UI optimizations
* - Native desktop integrations
*
* Integration Points:
* - Electron main process communication
* - Desktop file system access
* - Native OS integration
* - Platform-specific services
*
* Type Safety:
* - Uses ElectronPlatformService for desktop-specific functionality
* - Ensures type safety across Electron renderer and main processes
* - Maintains compatibility with Capacitor-Electron plugins
*
* @example
* // Electron renderer process initialization
* // Automatically detects Electron environment
* // Provides desktop-optimized user experience
*/
import { initializeApp } from "./main.common";
import { handleApiError } from "./services/api";
import { logger } from "./utils/logger";
logger.log("[Electron] Starting initialization");
logger.log("[Electron] Platform:", process.env.VITE_PLATFORM);
// Verify we're running in the correct platform environment
if (process.env.VITE_PLATFORM !== "electron") {
logger.warn(
"[Electron] Platform mismatch - expected 'electron', got:",
process.env.VITE_PLATFORM,
);
}
const app = initializeApp();
// Initialize API error handling for unhandled promise rejections
window.addEventListener("unhandledrejection", (event) => {
if (event.reason?.response) {
handleApiError(event.reason, event.reason.config?.url || "unknown");
}
});
// Electron-specific initialization
if (typeof window !== "undefined" && window.require) {
// We're in an Electron renderer process
logger.log("[Electron] Detected Electron renderer process");
// **CRITICAL FIX**: Disable any existing service worker that might be intercepting API calls
try {
if (navigator.serviceWorker?.getRegistrations) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
for(let registration of registrations) {
console.log("[Electron] Unregistering service worker:", registration.scope);
registration.unregister();
}
}).catch(error => {
console.log("[Electron] Failed to unregister service workers:", error);
});
}
} catch (error) {
console.log("[Electron] Service worker cleanup not available:", error);
}
// Add any Electron-specific initialization here
// For example, IPC communication setup, desktop-specific features, etc.
}
logger.log("[Electron] Mounting app");
app.mount("#app");
logger.log("[Electron] App mounted");
// Add Electron-specific cleanup on beforeunload
window.addEventListener("beforeunload", () => {
logger.log("[Electron] App unloading");
});

View File

@@ -1,17 +1,19 @@
import { initializeApp } from "./main.common";
// import { logger } from "./utils/logger"; // DISABLED FOR DEBUGGING
import { logger } from "./utils/logger";
const platform = process.env.VITE_PLATFORM;
const pwa_enabled = process.env.VITE_PWA_ENABLED === "true";
// Debug: Check SharedArrayBuffer availability
console.log(
`[SharedArrayBuffer] Available: ${typeof SharedArrayBuffer !== "undefined"}`,
);
console.log(`[Browser] User Agent: ${navigator.userAgent}`);
console.log(
`[Headers] Check COOP/COEP in Network tab if SharedArrayBuffer is false`,
);
// Only log SharedArrayBuffer info for web platform in development
if (platform === "web" && process.env.NODE_ENV !== "production") {
logger.debug(
`[SharedArrayBuffer] Available: ${typeof SharedArrayBuffer !== "undefined"}`,
);
logger.debug(`[Browser] User Agent: ${navigator.userAgent}`);
logger.debug(
`[Headers] Check COOP/COEP in Network tab if SharedArrayBuffer is false`,
);
}
// Only import service worker for web builds
if (pwa_enabled) {
@@ -23,15 +25,11 @@ const app = initializeApp();
// Note: Worker initialization is now handled by WebPlatformService
// This ensures single-point database access and prevents double migrations
if (platform === "web" || platform === "development") {
// logger.log( // DISABLED
// "[Web] Database initialization will be handled by WebPlatformService",
// );
console.log(
logger.debug(
"[Web] Database initialization will be handled by WebPlatformService",
);
} else {
// logger.warn("[Web] SQL not initialized for platform", { platform }); // DISABLED
console.warn("[Web] SQL not initialized for platform", { platform });
logger.debug("[Web] SQL not initialized for platform", { platform });
}
app.mount("#app");

View File

@@ -29,10 +29,11 @@ let databaseService = null;
async function getDatabaseService() {
if (!databaseService) {
// Dynamic import to prevent circular dependency
const { default: service } = await import(
const { default: AbsurdSqlDatabaseService } = await import(
"./services/AbsurdSqlDatabaseService"
);
databaseService = service;
// Get the singleton instance (only created when needed)
databaseService = AbsurdSqlDatabaseService.getInstance();
}
return databaseService;
}

View File

@@ -70,6 +70,14 @@ class AbsurdSqlDatabaseService implements DatabaseService {
return;
}
// **PLATFORM CHECK**: AbsurdSqlDatabaseService should only run on web platform
// This prevents SharedArrayBuffer checks and web-specific initialization on Electron/Capacitor
if (process.env.VITE_PLATFORM !== "web") {
throw new Error(
`AbsurdSqlDatabaseService is only supported on web platform. Current platform: ${process.env.VITE_PLATFORM}`,
);
}
const SQL = await initSqlJs({
locateFile: (file: string) => {
return new URL(
@@ -86,10 +94,15 @@ class AbsurdSqlDatabaseService implements DatabaseService {
SQL.FS.mount(sqlFS, {}, "/sql");
const path = "/sql/timesafari.absurd-sql";
// **SHARED ARRAY BUFFER FALLBACK**: Only needed for web platform
// This check handles Safari and other browsers without SharedArrayBuffer support
if (typeof SharedArrayBuffer === "undefined") {
logger.debug("[AbsurdSqlDatabaseService] SharedArrayBuffer not available, using fallback mode");
const stream = SQL.FS.open(path, "a+");
await stream.node.contents.readIfFallback();
SQL.FS.close(stream);
} else {
logger.debug("[AbsurdSqlDatabaseService] SharedArrayBuffer available, using optimized mode");
}
this.db = new SQL.Database(path, { filename: true });
@@ -237,7 +250,6 @@ class AbsurdSqlDatabaseService implements DatabaseService {
}
}
// Create a singleton instance
const databaseService = AbsurdSqlDatabaseService.getInstance();
export default databaseService;
// Export the service class for lazy instantiation
// The singleton will only be created when actually needed (web platform only)
export default AbsurdSqlDatabaseService;

View File

@@ -45,6 +45,25 @@ export interface PlatformService {
*/
getCapabilities(): PlatformCapabilities;
// Platform detection methods
/**
* Checks if running on Capacitor platform.
* @returns true if running on Capacitor, false otherwise
*/
isCapacitor(): boolean;
/**
* Checks if running on Electron platform.
* @returns true if running on Electron, false otherwise
*/
isElectron(): boolean;
/**
* Checks if running on web platform.
* @returns true if running on web, false otherwise
*/
isWeb(): boolean;
// File system operations
/**
* Reads the contents of a file at the specified path.

View File

@@ -9,6 +9,7 @@ import { CapacitorPlatformService } from "./platforms/CapacitorPlatformService";
* The factory determines which platform implementation to use based on the VITE_PLATFORM
* environment variable. Supported platforms are:
* - capacitor: Mobile platform using Capacitor
* - electron: Desktop platform using Electron with Capacitor
* - web: Default web platform (fallback)
*
* @example
@@ -50,6 +51,10 @@ export class PlatformServiceFactory {
case "capacitor":
PlatformServiceFactory.instance = new CapacitorPlatformService();
break;
case "electron":
// Use a specialized electron service that extends CapacitorPlatformService
PlatformServiceFactory.instance = new ElectronPlatformService();
break;
case "web":
default:
PlatformServiceFactory.instance = new WebPlatformService();
@@ -69,3 +74,56 @@ export class PlatformServiceFactory {
};
}
}
/**
* Electron-specific platform service implementation.
* Extends CapacitorPlatformService with electron-specific overrides.
*
* This service handles the unique requirements of the Electron platform:
* - Desktop-specific capabilities
* - Electron-specific file system access
* - Desktop UI patterns
* - Native desktop integration
*/
class ElectronPlatformService extends CapacitorPlatformService {
/**
* Gets the capabilities of the Electron platform
* Overrides the mobile-focused capabilities from CapacitorPlatformService
* @returns Platform capabilities object specific to Electron
*/
getCapabilities() {
return {
hasFileSystem: true,
hasCamera: false, // Desktop typically doesn't have integrated cameras for our use case
isMobile: false, // Electron is desktop, not mobile
isIOS: false,
hasFileDownload: true, // Desktop supports direct file downloads
needsFileHandlingInstructions: false, // Desktop users are familiar with file handling
isNativeApp: true, // Electron is a native app
};
}
/**
* Checks if running on Electron platform.
* @returns true, as this is the Electron implementation
*/
isElectron(): boolean {
return true;
}
/**
* Checks if running on Capacitor platform.
* @returns false, as this is Electron, not pure Capacitor
*/
isCapacitor(): boolean {
return false;
}
/**
* Checks if running on web platform.
* @returns false, as this is not web
*/
isWeb(): boolean {
return false;
}
}

View File

@@ -186,11 +186,8 @@ export class CapacitorPlatformService implements PlatformService {
sql: string,
params: unknown[] = [],
): Promise<R> {
// Log incoming parameters for debugging (HIGH PRIORITY)
logger.warn(
`[CapacitorPlatformService] queueOperation - SQL: ${sql}, Params:`,
params,
);
// Only log SQL operations in debug mode to reduce console noise
logger.debug(`[CapacitorPlatformService] queueOperation - SQL: ${sql}`);
// Convert parameters to SQLite-compatible types with robust serialization
const convertedParams = params.map((param, index) => {
@@ -198,72 +195,31 @@ export class CapacitorPlatformService implements PlatformService {
return null;
}
if (typeof param === "object" && param !== null) {
// Enhanced debug logging for all objects (HIGH PRIORITY)
logger.warn(
`[CapacitorPlatformService] Object param at index ${index}:`,
{
type: typeof param,
toString: param.toString(),
constructorName: param.constructor?.name,
isArray: Array.isArray(param),
keys: Object.keys(param),
stringRep: String(param),
},
);
// Special handling for Proxy objects (common cause of "An object could not be cloned")
const isProxy = this.isProxyObject(param);
logger.warn(
`[CapacitorPlatformService] isProxy result for index ${index}:`,
isProxy,
);
// AGGRESSIVE: If toString contains "Proxy", treat as Proxy even if isProxyObject returns false
const stringRep = String(param);
const forceProxyDetection =
stringRep.includes("Proxy(") || stringRep.startsWith("Proxy");
logger.warn(
`[CapacitorPlatformService] Force proxy detection for index ${index}:`,
forceProxyDetection,
);
if (isProxy || forceProxyDetection) {
logger.warn(
`[CapacitorPlatformService] Proxy object detected at index ${index} (method: ${isProxy ? "isProxyObject" : "stringDetection"}), toString: ${stringRep}`,
logger.debug(
`[CapacitorPlatformService] Proxy object detected at index ${index}`,
);
try {
// AGGRESSIVE EXTRACTION: Try multiple methods to extract actual values
if (Array.isArray(param)) {
// Method 1: Array.from() to extract from Proxy(Array)
const actualArray = Array.from(param);
logger.info(
`[CapacitorPlatformService] Extracted array from Proxy via Array.from():`,
actualArray,
);
// Method 2: Manual element extraction for safety
const manualArray: unknown[] = [];
for (let i = 0; i < param.length; i++) {
manualArray.push(param[i]);
}
logger.info(
`[CapacitorPlatformService] Manual array extraction:`,
manualArray,
);
// Use the manual extraction as it's more reliable
return manualArray;
return actualArray;
} else {
// For Proxy(Object), try to extract actual object
const actualObject = Object.assign({}, param);
logger.info(
`[CapacitorPlatformService] Extracted object from Proxy:`,
actualObject,
);
return actualObject;
}
} catch (proxyError) {
logger.error(
logger.debug(
`[CapacitorPlatformService] Failed to extract from Proxy at index ${index}:`,
proxyError,
);
@@ -275,16 +231,8 @@ export class CapacitorPlatformService implements PlatformService {
for (let i = 0; i < param.length; i++) {
fallbackArray.push(param[i]);
}
logger.info(
`[CapacitorPlatformService] Fallback array extraction successful:`,
fallbackArray,
);
return fallbackArray;
} catch (fallbackError) {
logger.error(
`[CapacitorPlatformService] Fallback array extraction failed:`,
fallbackError,
);
return `[Proxy Array - Could not extract]`;
}
}
@@ -297,14 +245,10 @@ export class CapacitorPlatformService implements PlatformService {
return JSON.stringify(param);
} catch (error) {
// Handle non-serializable objects
logger.error(
logger.debug(
`[CapacitorPlatformService] Failed to serialize parameter at index ${index}:`,
error,
);
logger.error(
`[CapacitorPlatformService] Problematic parameter:`,
param,
);
// Fallback: Convert to string representation
if (Array.isArray(param)) {
@@ -319,14 +263,14 @@ export class CapacitorPlatformService implements PlatformService {
}
if (typeof param === "function") {
// Functions can't be serialized - convert to string representation
logger.warn(
logger.debug(
`[CapacitorPlatformService] Function parameter detected and converted to string at index ${index}`,
);
return `[Function ${param.name || "Anonymous"}]`;
}
if (typeof param === "symbol") {
// Symbols can't be serialized - convert to string representation
logger.warn(
logger.debug(
`[CapacitorPlatformService] Symbol parameter detected and converted to string at index ${index}`,
);
return param.toString();
@@ -338,12 +282,6 @@ export class CapacitorPlatformService implements PlatformService {
return param;
});
// Log converted parameters for debugging (HIGH PRIORITY)
logger.warn(
`[CapacitorPlatformService] Converted params:`,
convertedParams,
);
return new Promise<R>((resolve, reject) => {
// Create completely plain objects that Vue cannot make reactive
// Step 1: Deep clone the converted params to ensure they're plain objects
@@ -361,20 +299,6 @@ export class CapacitorPlatformService implements PlatformService {
Object.freeze(operation.params);
Object.freeze(operation);
// Add enhanced logging to verify our fix
logger.warn(
`[CapacitorPlatformService] Final operation.params type:`,
typeof operation.params,
);
logger.warn(
`[CapacitorPlatformService] Final operation.params toString:`,
operation.params.toString(),
);
logger.warn(
`[CapacitorPlatformService] Final operation.params constructor:`,
operation.params.constructor?.name,
);
this.operationQueue.push(operation);
// If we're already initialized, start processing the queue
@@ -573,20 +497,17 @@ export class CapacitorPlatformService implements PlatformService {
sql: string,
params?: unknown[],
): Promise<capSQLiteChanges> => {
logger.log(`🔧 [CapacitorMigration] Executing SQL:`, sql);
logger.log(`📋 [CapacitorMigration] With params:`, params);
logger.debug(`🔧 [CapacitorMigration] Executing SQL:`, sql);
if (params && params.length > 0) {
// Use run method for parameterized queries (prepared statements)
// This is essential for proper parameter binding and SQL injection prevention
const result = await this.db!.run(sql, params);
logger.log(`✅ [CapacitorMigration] Run result:`, result);
return result;
} else {
// Use execute method for non-parameterized queries
// This is more efficient for simple DDL statements
const result = await this.db!.execute(sql);
logger.log(`✅ [CapacitorMigration] Execute result:`, result);
return result;
}
};
@@ -606,11 +527,9 @@ export class CapacitorPlatformService implements PlatformService {
sql: string,
params?: unknown[],
): Promise<DBSQLiteValues> => {
logger.log(`🔍 [CapacitorMigration] Querying SQL:`, sql);
logger.log(`📋 [CapacitorMigration] With params:`, params);
logger.debug(`🔍 [CapacitorMigration] Querying SQL:`, sql);
const result = await this.db!.query(sql, params);
logger.log(`📊 [CapacitorMigration] Query result:`, result);
return result;
};
@@ -633,7 +552,7 @@ export class CapacitorPlatformService implements PlatformService {
* @returns Set of migration names found in the result
*/
const extractMigrationNames = (result: DBSQLiteValues): Set<string> => {
logger.log(
logger.debug(
`🔍 [CapacitorMigration] Extracting migration names from:`,
result,
);
@@ -652,7 +571,7 @@ export class CapacitorPlatformService implements PlatformService {
})
.filter((name) => name !== null) || [];
logger.log(`📋 [CapacitorMigration] Extracted names:`, names);
logger.debug(`📋 [CapacitorMigration] Extracted names:`, names);
return new Set(names);
};
@@ -728,14 +647,14 @@ export class CapacitorPlatformService implements PlatformService {
return;
}
logger.log(`🔍 [DB-Integrity] Starting database integrity check...`);
logger.debug(`🔍 [DB-Integrity] Starting database integrity check...`);
try {
// Step 1: Check migrations table and applied migrations
const migrationsResult = await this.db.query(
"SELECT name, applied_at FROM migrations ORDER BY applied_at",
);
logger.log(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
logger.debug(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
// Step 2: Verify core tables exist
const coreTableNames = [
@@ -755,7 +674,7 @@ export class CapacitorPlatformService implements PlatformService {
);
if (tableCheck.values && tableCheck.values.length > 0) {
existingTables.push(tableName);
logger.log(`✅ [DB-Integrity] Table ${tableName} exists`);
logger.debug(`✅ [DB-Integrity] Table ${tableName} exists`);
} else {
logger.error(`❌ [DB-Integrity] Table ${tableName} missing`);
}
@@ -773,7 +692,7 @@ export class CapacitorPlatformService implements PlatformService {
const contactsSchema = await this.db.query(
"PRAGMA table_info(contacts)",
);
logger.log(
logger.debug(
`📊 [DB-Integrity] Contacts table schema:`,
contactsSchema,
);
@@ -789,7 +708,7 @@ export class CapacitorPlatformService implements PlatformService {
);
if (hasIViewContent) {
logger.log(
logger.debug(
`✅ [DB-Integrity] iViewContent column exists in contacts table`,
);
} else {
@@ -817,7 +736,7 @@ export class CapacitorPlatformService implements PlatformService {
"SELECT COUNT(*) as count FROM contacts",
);
logger.log(
logger.debug(
`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`,
);
} catch (error) {
@@ -1356,4 +1275,28 @@ export class CapacitorPlatformService implements PlatformService {
}
return undefined;
}
/**
* Checks if running on Capacitor platform.
* @returns true, as this is the Capacitor implementation
*/
isCapacitor(): boolean {
return true;
}
/**
* Checks if running on Electron platform.
* @returns false, as this is Capacitor, not Electron
*/
isElectron(): boolean {
return false;
}
/**
* Checks if running on web platform.
* @returns false, as this is not web
*/
isWeb(): boolean {
return false;
}
}

View File

@@ -460,6 +460,15 @@ export const PlatformServiceMixin = {
}
const settings = await this.$getSettings(MASTER_SETTINGS_KEY, defaults);
// **ELECTRON-SPECIFIC FIX**: Apply platform-specific API server override
// This ensures Electron always uses production endpoints regardless of cached settings
if (process.env.VITE_PLATFORM === "electron") {
// Import constants dynamically to get platform-specific values
const { DEFAULT_ENDORSER_API_SERVER } = await import("../constants/app");
settings.apiServer = DEFAULT_ENDORSER_API_SERVER;
}
return (this as any)._setCached(
cacheKey,
settings,

View File

@@ -19,22 +19,35 @@ export function safeStringify(obj: unknown) {
});
}
// Determine if we should suppress verbose logging (for Electron)
const isElectron = process.env.VITE_PLATFORM === "electron";
const isProduction = process.env.NODE_ENV === "production";
export const logger = {
debug: (message: string, ...args: unknown[]) => {
if (process.env.NODE_ENV !== "production") {
// Debug logs are very verbose - only show in development mode for web
if (!isProduction && !isElectron) {
// eslint-disable-next-line no-console
console.debug(message, ...args);
// const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
// logToDb(message + argsString);
}
// Don't log debug messages to database to reduce noise
},
log: (message: string, ...args: unknown[]) => {
// Regular logs - show in development or for capacitor, but quiet for Electron
if (
process.env.NODE_ENV !== "production" ||
(!isProduction && !isElectron) ||
process.env.VITE_PLATFORM === "capacitor"
) {
// eslint-disable-next-line no-console
console.log(message, ...args);
}
// Only log to database for important messages (not routine operations)
if (
!message.includes("[CapacitorPlatformService]") &&
!message.includes("[CapacitorMigration]") &&
!message.includes("[DB-Integrity]")
) {
const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
logToDb(message + argsString);
}
@@ -52,10 +65,17 @@ export const logger = {
}
},
warn: (message: string, ...args: unknown[]) => {
// eslint-disable-next-line no-console
console.warn(message, ...args);
const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
logToDb(message + argsString);
// Always show warnings, but for Electron, suppress routine database warnings
if (!isElectron || !message.includes("[CapacitorPlatformService]")) {
// eslint-disable-next-line no-console
console.warn(message, ...args);
}
// Log warnings to database, but filter out routine operations
if (!message.includes("[CapacitorPlatformService]")) {
const argsString = args.length > 0 ? " - " + safeStringify(args) : "";
logToDb(message + argsString);
}
},
error: (message: string, ...args: unknown[]) => {
// Errors will always be logged