/** * 插件存储 worker(免打包纯 ESM,只依赖 node 内置模块;构建期拷到 out/main/ 旁)。 * 持有全部插件的 SQLite 连接(node:sqlite DatabaseSync),按消息逐个执行—— * 同步 API 全部留在本线程,主进程只做异步 RPC,插件查询永不阻塞主进程事件循环。 * * 协议:主进程 → { reqId, op, ...payload };worker → { reqId, ok, result? | error? }。 * 首条消息必须是 init(下发 dataRoot,避免 worker 依赖 electron app 路径)。 */ import { DatabaseSync } from 'node:sqlite' import { mkdirSync } from 'node:fs' import { join } from 'node:path' import { parentPort } from 'node:worker_threads' /** dataRoot(userData/plugin-data);由 init 消息下发 */ let dataRoot = null /** pluginId → 已打开连接(跨消息复用;close/clearData 时移除) */ const dbs = new Map() function openDb(pluginId) { let db = dbs.get(pluginId) if (db) return db // pluginId 已由宿主按 manifest 规则校验(小写字母开头,字母/数字/连字符),可安全作路径段 mkdirSync(join(dataRoot, pluginId), { recursive: true }) db = new DatabaseSync(join(dataRoot, pluginId, 'data.db')) db.exec('PRAGMA journal_mode = WAL;') // 宿主管理的 KV 表:api.storage.kv 的底层(value JSON 编码) db.exec('CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)') dbs.set(pluginId, db) return db } function handle(op, msg) { switch (op) { case 'init': dataRoot = msg.dataRoot return null case 'close': { const db = dbs.get(msg.pluginId) if (db) { dbs.delete(msg.pluginId) db.close() } return null } case 'kvGet': { const row = openDb(msg.pluginId).prepare('SELECT value FROM kv WHERE key = ?').get(msg.key) return row ? JSON.parse(row.value) : null } case 'kvSet': openDb(msg.pluginId) .prepare('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value') .run(msg.key, JSON.stringify(msg.value ?? null)) return null case 'kvDelete': openDb(msg.pluginId).prepare('DELETE FROM kv WHERE key = ?').run(msg.key) return null case 'kvKeys': return openDb(msg.pluginId) .prepare('SELECT key FROM kv ORDER BY key') .all() .map((r) => r.key) case 'exec': openDb(msg.pluginId).exec(msg.sql) return null case 'run': openDb(msg.pluginId).prepare(msg.sql).run(...(msg.params ?? [])) return null case 'query': return openDb(msg.pluginId).prepare(msg.sql).all(...(msg.params ?? [])) case 'batch': { const db = openDb(msg.pluginId) db.exec('BEGIN') try { for (const st of msg.statements ?? []) db.prepare(st.sql).run(...(st.params ?? [])) db.exec('COMMIT') } catch (err) { try { db.exec('ROLLBACK') } catch { // 连接已坏时 ROLLBACK 自身会失败——原始错误优先 } throw err } return null } default: throw new Error(`未知存储操作: ${op}`) } } parentPort.on('message', (msg) => { if (!dataRoot && msg.op !== 'init') { parentPort.postMessage({ reqId: msg.reqId, ok: false, error: '存储 worker 未初始化' }) return } try { parentPort.postMessage({ reqId: msg.reqId, ok: true, result: handle(msg.op, msg) ?? null }) } catch (err) { parentPort.postMessage({ reqId: msg.reqId, ok: false, error: err instanceof Error ? err.message : String(err) }) } })