import { app, BrowserWindow, protocol } from "electron"; import { mkdtempSync } from "node:fs"; import { mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; import { DESKTOP_APP_ORIGIN, DESKTOP_APP_SCHEME, createDesktopAppProtocolHandler, } from "../apps/desktop/src/main/app-protocol.mjs"; import { RENDERER_STORAGE_MIGRATION_MARKER, migrateRendererStorageOrigin, } from "../apps/desktop/src/main/renderer-storage-migration.mjs"; console.log("renderer-origin: verifier loaded"); protocol.registerSchemesAsPrivileged([{ scheme: DESKTOP_APP_SCHEME, privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, stream: true, codeCache: true }, }]); const testDirectory = mkdtempSync(join(tmpdir(), "edgeever-renderer-origin-")); const userDataDirectory = join(testDirectory, "profile"); const webRoot = join(testDirectory, "web"); const legacyEntryPath = join(webRoot, "index.html"); const bridgePath = join(webRoot, "desktop-storage-bridge.html"); const markerPath = join(userDataDirectory, RENDERER_STORAGE_MIGRATION_MARKER); app.setPath("userData", userDataDirectory); const createWindow = () => new BrowserWindow({ show: false, webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true, }, }); const execute = (webContents, fn) => webContents.executeJavaScript(`(${fn.toString()})()`, true); const seedLegacyStorage = async () => { const requestResult = (request) => new Promise((resolve, reject) => { request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); const transactionDone = (transaction) => new Promise((resolve, reject) => { transaction.oncomplete = resolve; transaction.onerror = () => reject(transaction.error); transaction.onabort = () => reject(transaction.error); }); localStorage.setItem("edgeever.test.origin-migration", "preserved"); const localOpen = indexedDB.open("edgeever-local", 1); localOpen.onupgradeneeded = () => localOpen.result.createObjectStore("drafts", { keyPath: "memoId" }); const local = await requestResult(localOpen); const localTransaction = local.transaction("drafts", "readwrite"); const localCompleted = transactionDone(localTransaction); localTransaction.objectStore("drafts").put({ memoId: "draft-1", title: "Unsynced draft", updatedAt: "2026-09-09T00:00:00.000Z" }); await localCompleted; local.close(); const packageOpen = indexedDB.open("edgeever-plugin-packages-v1", 2); packageOpen.onupgradeneeded = () => { const store = packageOpen.result.createObjectStore("packageVersions", { keyPath: ["pluginId", "version"] }); store.createIndex("pluginId", "pluginId"); }; const packages = await requestResult(packageOpen); const packageTransaction = packages.transaction("packageVersions", "readwrite"); const packageCompleted = transactionDone(packageTransaction); packageTransaction.objectStore("packageVersions").put({ pluginId: "org.edgeever.test", version: "1.0.0", mainJs: "export default {}" }); await packageCompleted; packages.close(); const secretOpen = indexedDB.open("edgeever-plugin-secrets-v1", 1); secretOpen.onupgradeneeded = () => { secretOpen.result.createObjectStore("keys"); secretOpen.result.createObjectStore("secrets", { keyPath: "id" }); }; const secrets = await requestResult(secretOpen); const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]); const iv = crypto.getRandomValues(new Uint8Array(12)); const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, new TextEncoder().encode("secret-value")); const secretTransaction = secrets.transaction(["keys", "secrets"], "readwrite"); const secretCompleted = transactionDone(secretTransaction); secretTransaction.objectStore("keys").put(key, "device-master-key"); secretTransaction.objectStore("secrets").put({ id: "org.edgeever.test:token", iv, ciphertext }); await secretCompleted; secrets.close(); }; const inspectStorage = async () => { const requestResult = (request) => new Promise((resolve, reject) => { request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); const transactionDone = (transaction) => new Promise((resolve, reject) => { transaction.oncomplete = resolve; transaction.onerror = () => reject(transaction.error); transaction.onabort = () => reject(transaction.error); }); const local = await requestResult(indexedDB.open("edgeever-local")); const localTransaction = local.transaction("drafts", "readonly"); const localCompleted = transactionDone(localTransaction); const draft = await requestResult(localTransaction.objectStore("drafts").get("draft-1")); await localCompleted; local.close(); const packages = await requestResult(indexedDB.open("edgeever-plugin-packages-v1")); const packageTransaction = packages.transaction("packageVersions", "readonly"); const packageCompleted = transactionDone(packageTransaction); const pluginPackage = await requestResult(packageTransaction.objectStore("packageVersions").get(["org.edgeever.test", "1.0.0"])); await packageCompleted; packages.close(); const secrets = await requestResult(indexedDB.open("edgeever-plugin-secrets-v1")); const secretTransaction = secrets.transaction(["keys", "secrets"], "readonly"); const secretCompleted = transactionDone(secretTransaction); const key = await requestResult(secretTransaction.objectStore("keys").get("device-master-key")); const encrypted = await requestResult(secretTransaction.objectStore("secrets").get("org.edgeever.test:token")); await secretCompleted; secrets.close(); const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv: encrypted.iv }, key, encrypted.ciphertext); return { origin: window.location.origin, localStorageValue: localStorage.getItem("edgeever.test.origin-migration"), draftTitle: draft?.title, packageVersion: pluginPackage?.version, secret: new TextDecoder().decode(plaintext), }; }; let legacyWindow; let targetWindow; const run = async () => { try { console.log("renderer-origin: app ready"); await mkdir(webRoot, { recursive: true }); await writeFile(legacyEntryPath, "