From 3997a88b44eeccc8ebdb3824339b53e1494021dc Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Fri, 30 May 2025 05:10:26 +0000 Subject: [PATCH] fix: rename postcss.config.js to .cjs for ES module compatibility - Added "type": "module" to package.json to support ES module imports for Electron SQLite - Renamed postcss.config.js to postcss.config.cjs to maintain CommonJS syntax - This ensures build tools can properly load the PostCSS configuration --- experiment.sh | 69 +++++++++ package.json | 9 +- postcss.config.js => postcss.config.cjs | 0 scripts/build-electron.js | 4 +- src/electron/main.ts | 144 +++++++----------- src/electron/preload.js | 49 ++++++ src/libs/endorserServer.ts | 15 +- src/main.electron.ts | 24 ++- .../platforms/ElectronPlatformService.ts | 8 +- src/types/capacitor-sqlite-electron.d.ts | 44 +++++- src/types/global.d.ts | 7 + vite.config.electron.mts | 4 +- 12 files changed, 262 insertions(+), 115 deletions(-) create mode 100755 experiment.sh rename postcss.config.js => postcss.config.cjs (100%) diff --git a/experiment.sh b/experiment.sh new file mode 100755 index 00000000..c6cf4548 --- /dev/null +++ b/experiment.sh @@ -0,0 +1,69 @@ +#! /bin/bash + +# Exit on any error +set -e + +# Function to check if a command exists +check_command() { + if ! command -v $1 &> /dev/null; then + echo "Error: $1 is required but not installed." + exit 1 + fi +} + +# Check required commands +check_command node +check_command npm + +# Clean up previous builds +echo "Cleaning previous builds..." +rm -rf dist* + +# Set environment variables for the build +echo "Setting up environment variables..." +export VITE_PLATFORM=electron +export VITE_PWA_ENABLED=false +export VITE_DISABLE_PWA=true + +# Ensure TypeScript is installed +echo "Checking TypeScript installation..." +if [ ! -f "./node_modules/.bin/tsc" ]; then + echo "Installing TypeScript..." + npm install --save-dev typescript@~5.2.2 + # Verify installation + if [ ! -f "./node_modules/.bin/tsc" ]; then + echo "TypeScript installation failed!" + exit 1 + fi +fi + +# Get git hash for versioning +GIT_HASH=$(git log -1 --pretty=format:%h) + +# Build web assets +echo "Building web assets..." +VITE_GIT_HASH=$GIT_HASH npx vite build --config vite.config.app.electron.mts --mode electron + +# TypeScript compilation +echo "Running TypeScript compilation..." +if ! ./node_modules/.bin/tsc -p tsconfig.electron.json; then + echo "TypeScript compilation failed!" + exit 1 +fi + +# Build electron main process +echo "Building electron main process..." +VITE_GIT_HASH=$GIT_HASH npx vite build --config vite.config.electron.mts --mode electron + +# Organize files +echo "Organizing build files..." +mkdir -p dist-electron/www +cp -r dist/* dist-electron/www/ +mkdir -p dist-electron/resources +cp src/electron/preload.js dist-electron/resources/preload.js + +# Build the AppImage +echo "Building AppImage..." +npx electron-builder --linux AppImage + +echo "Build completed successfully!" \ No newline at end of file diff --git a/package.json b/package.json index 5c078376..d3f65809 100644 --- a/package.json +++ b/package.json @@ -172,7 +172,7 @@ "vite": "^5.2.0", "vite-plugin-pwa": "^1.0.0" }, - "main": "./dist-electron/main.js", + "main": "./dist-electron/main.mjs", "build": { "appId": "app.timesafari", "productName": "TimeSafari", @@ -190,8 +190,8 @@ "to": "www" }, { - "from": "dist-electron/resources/preload.js", - "to": "preload.js" + "from": "dist-electron/resources/preload.mjs", + "to": "preload.mjs" } ], "linux": { @@ -229,5 +229,6 @@ } ] } - } + }, + "type": "module" } diff --git a/postcss.config.js b/postcss.config.cjs similarity index 100% rename from postcss.config.js rename to postcss.config.cjs diff --git a/scripts/build-electron.js b/scripts/build-electron.js index 0a601184..f9c61381 100644 --- a/scripts/build-electron.js +++ b/scripts/build-electron.js @@ -49,8 +49,8 @@ indexContent = indexContent.replace( fs.writeFileSync(finalIndexPath, indexContent); // Copy preload script to resources -const preloadSrc = path.join(electronDistPath, "preload.js"); -const preloadDest = path.join(electronDistPath, "resources", "preload.js"); +const preloadSrc = path.join(electronDistPath, "preload.mjs"); +const preloadDest = path.join(electronDistPath, "resources", "preload.mjs"); // Ensure resources directory exists const resourcesDir = path.join(electronDistPath, "resources"); diff --git a/src/electron/main.ts b/src/electron/main.ts index 3c14eba5..bca2aec8 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -1,8 +1,12 @@ import { app, BrowserWindow, ipcMain } from "electron"; -import path from "path"; -import fs from "fs"; import { Capacitor } from "@capacitor/core"; -import { SQLiteConnection } from "@capacitor-community/sqlite"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +// Get __dirname equivalent in ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); // Simple logger implementation const logger = { @@ -24,16 +28,17 @@ logger.info("Starting main process initialization..."); try { logger.info("About to initialize Capacitor..."); logger.info("Capacitor before init:", { - hasPlatform: 'platform' in Capacitor, - hasIsNativePlatform: 'isNativePlatform' in Capacitor, + hasPlatform: "platform" in Capacitor, + hasIsNativePlatform: "isNativePlatform" in Capacitor, platformType: typeof Capacitor.platform, - isNativePlatformType: typeof Capacitor.isNativePlatform + isNativePlatformType: typeof Capacitor.isNativePlatform, }); // Try direct assignment first try { - (Capacitor as any).platform = 'electron'; - (Capacitor as any).isNativePlatform = true; + (Capacitor as unknown as { platform: string }).platform = "electron"; + (Capacitor as unknown as { isNativePlatform: boolean }).isNativePlatform = + true; logger.info("Direct assignment successful"); } catch (e) { logger.warn("Direct assignment failed, trying defineProperty:", e); @@ -43,7 +48,7 @@ try { }); Object.defineProperty(Capacitor, "platform", { - get: () => 'electron', + get: () => "electron", configurable: true, }); } @@ -52,90 +57,50 @@ try { platform: Capacitor.platform, isNativePlatform: Capacitor.isNativePlatform, platformType: typeof Capacitor.platform, - isNativePlatformType: typeof Capacitor.isNativePlatform + isNativePlatformType: typeof Capacitor.isNativePlatform, }); } catch (error) { logger.error("Failed to initialize Capacitor:", error); throw error; } -// Initialize SQLite plugin for Electron -let sqliteConnection: SQLiteConnection | null = null; - -try { - logger.info("About to initialize SQLite plugin..."); - // Use require to ensure we get the CommonJS module - const sqliteModule = require('@capacitor-community/sqlite/electron/dist/plugin'); - logger.info("SQLite module loaded:", Object.keys(sqliteModule)); - - // Debug the module contents - logger.info("SQLite module contents:", { - default: typeof sqliteModule.default, - defaultKeys: sqliteModule.default ? Object.keys(sqliteModule.default) : [], - CapacitorSQLite: typeof sqliteModule.CapacitorSQLite, - CapacitorSQLiteKeys: sqliteModule.CapacitorSQLite ? Object.keys(sqliteModule.CapacitorSQLite) : [], - moduleType: typeof sqliteModule, - moduleKeys: Object.keys(sqliteModule), - hasHandle: sqliteModule.default?.handle ? 'yes' : 'no', - handleType: typeof sqliteModule.default?.handle, - defaultProto: Object.getPrototypeOf(sqliteModule.default), - defaultProtoKeys: Object.getPrototypeOf(sqliteModule.default) ? Object.keys(Object.getPrototypeOf(sqliteModule.default)) : [] - }); - - // Get the plugin constructor - const SQLitePlugin = sqliteModule.CapacitorSQLite; - logger.info("Got SQLite plugin constructor:", { - type: typeof SQLitePlugin, - name: SQLitePlugin?.name, - prototype: SQLitePlugin ? Object.getPrototypeOf(SQLitePlugin) : null, - prototypeKeys: SQLitePlugin ? Object.keys(Object.getPrototypeOf(SQLitePlugin)) : [] - }); - - if (typeof SQLitePlugin !== 'function') { - throw new Error(`SQLite plugin is not a constructor, got ${typeof SQLitePlugin}`); - } - - // Create a connection using the plugin - logger.info("Creating SQLite connection..."); - sqliteConnection = new SQLiteConnection(SQLitePlugin); - - logger.info("SQLite connection created:", { - type: typeof sqliteConnection, - keys: Object.keys(sqliteConnection), - prototype: Object.getPrototypeOf(sqliteConnection), - prototypeKeys: Object.getPrototypeOf(sqliteConnection) ? Object.keys(Object.getPrototypeOf(sqliteConnection)) : [] - }); - - logger.info("SQLite plugin initialized successfully"); -} catch (error) { - logger.error("Failed to initialize SQLite plugin:", error); - throw error; -} - -// Set up IPC handler for SQLite -ipcMain.handle("capacitor-sqlite", async (_event, ...args) => { - if (!sqliteConnection) { - const error = new Error("SQLite connection not initialized"); - logger.error(error); - throw error; - } +// Initialize SQLite plugin +let sqlitePlugin: any = null; +async function initializeSQLite() { try { - logger.debug("Handling SQLite request:", args); - // The first argument should be the method name - const [method, ...restArgs] = args; - if (typeof method !== 'string' || !(method in sqliteConnection)) { - throw new Error(`Invalid SQLite method: ${method}`); + // Import the plugin directly in the main process + const plugin = await import("@capacitor-community/sqlite/electron/dist/plugin.js"); + sqlitePlugin = new plugin.CapacitorSQLite(); + + // Test the plugin with a simple operation + const result = await sqlitePlugin.echo({ value: "test" }); + if (result.value !== "test") { + throw new Error("SQLite plugin echo test failed"); } - // Call the method on the connection - const result = await (sqliteConnection as any)[method](...restArgs); - logger.debug("SQLite request completed successfully"); - return result; + logger.info("SQLite plugin initialized successfully"); } catch (error) { - logger.error("SQLite request failed:", error); + logger.error("Failed to initialize SQLite plugin:", error); throw error; } +} + +// Initialize SQLite plugin +initializeSQLite().catch(error => { + logger.error("Failed to initialize SQLite plugin:", error); +}); + +// Set up IPC handlers for SQLite operations +ipcMain.handle("check-sqlite-availability", () => { + return sqlitePlugin !== null; +}); + +ipcMain.handle("capacitor-sqlite", async (event, ...args) => { + if (!sqlitePlugin) { + throw new Error("SQLite plugin not initialized"); + } + return sqlitePlugin.handle(event, ...args); }); // Initialize app when ready @@ -150,8 +115,8 @@ const isDev = process.argv.includes("--inspect"); function createWindow(): void { // Resolve preload path based on environment const preloadPath = app.isPackaged - ? path.join(process.resourcesPath, "preload.js") - : path.join(__dirname, "preload.js"); + ? path.join(process.resourcesPath, "preload.mjs") + : path.join(__dirname, "preload.mjs"); logger.log("[Electron] Preload path:", preloadPath); logger.log("[Electron] Preload exists:", fs.existsSync(preloadPath)); @@ -183,18 +148,17 @@ function createWindow(): void { webPreferences: { nodeIntegration: false, contextIsolation: true, - webSecurity: true, - allowRunningInsecureContent: false, - preload: preloadPath, // Use the resolved preload path + sandbox: false, + preload: preloadPath, }, }); // Track DevTools state - mainWindow.webContents.on('devtools-opened', () => { + mainWindow.webContents.on("devtools-opened", () => { logger.info("[Electron] DevTools opened"); }); - mainWindow.webContents.on('devtools-closed', () => { + mainWindow.webContents.on("devtools-closed", () => { logger.warn("[Electron] DevTools closed - reopening"); mainWindow.webContents.openDevTools(); }); @@ -215,7 +179,7 @@ function createWindow(): void { urls: [ "file://*/*/assets/*", "file://*/assets/*", - "file:///assets/*" + "file:///assets/*", // Removed to reduce noise ], }, @@ -339,8 +303,8 @@ function createWindow(): void { }, ); - mainWindow.webContents.openDevTools({ mode: 'detach' }); - mainWindow.webContents.once('devtools-opened', () => { + mainWindow.webContents.openDevTools({ mode: "detach" }); + mainWindow.webContents.once("devtools-opened", () => { if (mainWindow.webContents.devToolsWebContents) { mainWindow.webContents.devToolsWebContents.focus(); } diff --git a/src/electron/preload.js b/src/electron/preload.js index 2b37f099..57262f48 100644 --- a/src/electron/preload.js +++ b/src/electron/preload.js @@ -85,6 +85,55 @@ try { }, }); + // Expose protected methods that allow the renderer process to use + // the ipcRenderer without exposing the entire object + contextBridge.exposeInMainWorld( + 'electron', + { + // SQLite plugin bridge + sqlite: { + // Check if SQLite is available + isAvailable: () => ipcRenderer.invoke('check-sqlite-availability'), + // Execute SQLite operations + execute: (method, ...args) => ipcRenderer.invoke('capacitor-sqlite', method, ...args), + }, + // ... existing exposed methods ... + } + ); + + // Create a proxy for the CapacitorSQLite plugin + const createSQLiteProxy = () => { + return { + async createConnection(...args) { + return ipcRenderer.invoke('capacitor-sqlite', 'createConnection', ...args); + }, + async isConnection(...args) { + return ipcRenderer.invoke('capacitor-sqlite', 'isConnection', ...args); + }, + async retrieveConnection(...args) { + return ipcRenderer.invoke('capacitor-sqlite', 'retrieveConnection', ...args); + }, + async retrieveAllConnections() { + return ipcRenderer.invoke('capacitor-sqlite', 'retrieveAllConnections'); + }, + async closeConnection(...args) { + return ipcRenderer.invoke('capacitor-sqlite', 'closeConnection', ...args); + }, + async closeAllConnections() { + return ipcRenderer.invoke('capacitor-sqlite', 'closeAllConnections'); + }, + async isAvailable() { + return ipcRenderer.invoke('capacitor-sqlite', 'isAvailable'); + }, + async getPlatform() { + return 'electron'; + }, + }; + }; + + // Expose the SQLite plugin proxy + contextBridge.exposeInMainWorld('CapacitorSQLite', createSQLiteProxy()); + logger.info("Preload script completed successfully"); } catch (error) { logger.error("Error in preload script:", error); diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index f44bcf56..ff56017d 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -685,6 +685,7 @@ export function hydrateGive( if (amount && !isNaN(amount)) { const quantitativeValue: QuantitativeValue = { + "@type": "QuantitativeValue", amountOfThisGood: amount, unitCode: unitCode || "HUR", }; @@ -870,7 +871,6 @@ export function hydrateOffer( if (amount && !isNaN(amount)) { const quantitativeValue: QuantitativeValue = { - "@context": SCHEMA_ORG_CONTEXT, "@type": "QuantitativeValue", amountOfThisGood: amount, unitCode: unitCode || "HUR", @@ -997,11 +997,12 @@ export async function createAndSubmitClaim( axios: Axios, ): Promise { try { - const vcPayload = { + const vcPayload: { vc: VerifiableCredentialClaim } = { vc: { - "@context": ["https://www.w3.org/2018/credentials/v1"], + "@context": "https://www.w3.org/2018/credentials/v1", + "@type": "VerifiableCredential", type: ["VerifiableCredential"], - credentialSubject: vcClaim, + credentialSubject: vcClaim as unknown as ClaimObject, }, }; @@ -1307,7 +1308,7 @@ export async function createEndorserJwtVcFromClaim( // Make a payload for the claim const vcPayload = { vc: { - "@context": ["https://www.w3.org/2018/credentials/v1"], + "@context": "https://www.w3.org/2018/credentials/v1", type: ["VerifiableCredential"], credentialSubject: claim, }, @@ -1332,10 +1333,10 @@ export async function createInviteJwt( // Make a payload for the claim const vcPayload: { vc: VerifiableCredentialClaim } = { vc: { - "@context": ["https://www.w3.org/2018/credentials/v1"], + "@context": "https://www.w3.org/2018/credentials/v1", "@type": "VerifiableCredential", type: ["VerifiableCredential"], - credentialSubject: vcClaim as unknown as ClaimObject, // Type assertion needed due to object being string + credentialSubject: vcClaim as unknown as ClaimObject, }, }; diff --git a/src/main.electron.ts b/src/main.electron.ts index 5be7b0ab..5404f722 100644 --- a/src/main.electron.ts +++ b/src/main.electron.ts @@ -1,12 +1,17 @@ import { initializeApp } from "./main.common"; import { logger } from "./utils/logger"; -import { CapacitorSQLite } from "@capacitor-community/sqlite"; -// Initialize SQLite plugin for Electron -if (typeof window !== "undefined") { - // Register the plugin globally - window.CapacitorSQLite = CapacitorSQLite; - logger.info("[Electron] SQLite plugin initialized in native mode"); +async function initializeSQLite() { + try { + const isAvailable = await window.electron.sqlite.isAvailable(); + if (!isAvailable) { + throw new Error("SQLite plugin not available in main process"); + } + logger.info("[Electron] SQLite plugin bridge initialized"); + } catch (error) { + logger.error("[Electron] Failed to initialize SQLite plugin bridge:", error); + throw error; + } } const platform = process.env.VITE_PLATFORM; @@ -20,5 +25,10 @@ if (pwa_enabled) { logger.warn("[Electron] PWA is enabled, but not supported in electron"); } +// Initialize app and SQLite const app = initializeApp(); -app.mount("#app"); +initializeSQLite().then(() => { + app.mount("#app"); +}).catch(error => { + logger.error("[Electron] Failed to initialize app:", error); +}); diff --git a/src/services/platforms/ElectronPlatformService.ts b/src/services/platforms/ElectronPlatformService.ts index 48c2fb91..3e2baccd 100644 --- a/src/services/platforms/ElectronPlatformService.ts +++ b/src/services/platforms/ElectronPlatformService.ts @@ -32,7 +32,7 @@ export class ElectronPlatformService implements PlatformService { private initialized = false; constructor() { - // Ensure we're using the native implementation + // Use the IPC bridge for SQLite operations if (!window.CapacitorSQLite) { throw new Error("CapacitorSQLite not initialized in Electron"); } @@ -45,6 +45,12 @@ export class ElectronPlatformService implements PlatformService { } try { + // Check if SQLite is available through IPC + const isAvailable = await window.electron.sqlite.isAvailable(); + if (!isAvailable) { + throw new Error("SQLite is not available in the main process"); + } + // Create/Open database with native implementation this.db = await this.sqlite.createConnection( this.dbName, diff --git a/src/types/capacitor-sqlite-electron.d.ts b/src/types/capacitor-sqlite-electron.d.ts index 09269124..7599291a 100644 --- a/src/types/capacitor-sqlite-electron.d.ts +++ b/src/types/capacitor-sqlite-electron.d.ts @@ -1,6 +1,46 @@ -declare module '@capacitor-community/sqlite/electron/dist/plugin' { - export class CapacitorSQLiteElectron { +declare module '@capacitor-community/sqlite/electron/dist/plugin.js' { + export class CapacitorSQLite { constructor(); handle(event: Electron.IpcMainInvokeEvent, ...args: any[]): Promise; + createConnection(options: any): Promise; + closeConnection(options: any): Promise; + echo(options: any): Promise; + open(options: any): Promise; + close(options: any): Promise; + beginTransaction(options: any): Promise; + commitTransaction(options: any): Promise; + rollbackTransaction(options: any): Promise; + isTransactionActive(options: any): Promise; + getVersion(options: any): Promise; + getTableList(options: any): Promise; + execute(options: any): Promise; + executeSet(options: any): Promise; + run(options: any): Promise; + query(options: any): Promise; + isDBExists(options: any): Promise; + isDBOpen(options: any): Promise; + isDatabase(options: any): Promise; + isTableExists(options: any): Promise; + deleteDatabase(options: any): Promise; + isJsonValid(options: any): Promise; + importFromJson(options: any): Promise; + exportToJson(options: any): Promise; + createSyncTable(options: any): Promise; + setSyncDate(options: any): Promise; + getSyncDate(options: any): Promise; + deleteExportedRows(options: any): Promise; + addUpgradeStatement(options: any): Promise; + copyFromAssets(options: any): Promise; + getFromHTTPRequest(options: any): Promise; + getDatabaseList(): Promise; + checkConnectionsConsistency(options: any): Promise; + isSecretStored(): Promise; + isPassphraseValid(options: any): Promise; + setEncryptionSecret(options: any): Promise; + changeEncryptionSecret(options: any): Promise; + clearEncryptionSecret(): Promise; + isInConfigEncryption(): Promise; + isDatabaseEncrypted(options: any): Promise; + checkEncryptionSecret(options: any): Promise; } } \ No newline at end of file diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 28749dd4..fc904372 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -4,6 +4,13 @@ import type { CapacitorSQLite } from '@capacitor-community/sqlite'; declare global { interface Window { CapacitorSQLite: typeof CapacitorSQLite; + electron: { + sqlite: { + isAvailable: () => Promise; + execute: (method: string, ...args: unknown[]) => Promise; + }; + // Add other electron IPC methods as needed + }; } } diff --git a/vite.config.electron.mts b/vite.config.electron.mts index 6291af92..cdb1fd90 100644 --- a/vite.config.electron.mts +++ b/vite.config.electron.mts @@ -39,8 +39,8 @@ export default defineConfig({ 'axios/dist/node/axios.cjs' ], output: { - format: 'cjs', - entryFileNames: '[name].js', + format: 'es', + entryFileNames: '[name].mjs', assetFileNames: 'assets/[name].[ext]', }, },