b41273d56d51f4e242ae495426b64d662d57303356dac73c4b1dee6830c2505b.json raw
1 {"ast":null,"code":"import _asyncToGenerator from \"/home/mleku/src/orly.dev/next/signer/node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js\";\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// mls-engine.ts — Loads marmot.wasm in the signer extension background.\n// All MLS crypto happens here with direct access to vault keys.\n// No bus round-trips for signing or encryption.\nimport { signEvent, nip44Encrypt, nip44Decrypt, nip04Encrypt, nip04Decrypt } from './background-common';\nimport browser from 'webextension-polyfill';\n// --- State ---\nlet wasmReady = false;\nlet mlsTabId = null;\nlet currentPrivkey = '';\n// --- IDB Group Store (same schema as the marmot SW used) ---\nconst GDB_NAME = 'marmot-groups';\nconst GDB_VER = 1;\nconst GDB_STORE = 'groups';\nfunction gdbOpen() {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(GDB_NAME, GDB_VER);\n req.onupgradeneeded = e => {\n const db = e.target.result;\n if (!db.objectStoreNames.contains(GDB_STORE)) {\n db.createObjectStore(GDB_STORE);\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\nfunction storeResult(id, data, err) {\n const m = globalThis._marmot;\n if (m?.storeResult) m.storeResult(id, data || '', err || '');\n}\nfunction setupStoreCallbacks() {\n const g = globalThis;\n g._marmot_store_save = (id, groupIDHex, stateHex) => {\n gdbOpen().then(db => {\n const tx = db.transaction(GDB_STORE, 'readwrite');\n tx.objectStore(GDB_STORE).put(stateHex, groupIDHex);\n tx.oncomplete = () => storeResult(id, '', '');\n tx.onerror = () => storeResult(id, '', tx.error?.message || 'save failed');\n }).catch(e => storeResult(id, '', e.message));\n };\n g._marmot_store_load = (id, groupIDHex) => {\n gdbOpen().then(db => {\n const tx = db.transaction(GDB_STORE, 'readonly');\n const req = tx.objectStore(GDB_STORE).get(groupIDHex);\n req.onsuccess = () => storeResult(id, req.result || '', '');\n req.onerror = () => storeResult(id, '', req.error?.message || 'load failed');\n }).catch(e => storeResult(id, '', e.message));\n };\n g._marmot_store_list = id => {\n gdbOpen().then(db => {\n const tx = db.transaction(GDB_STORE, 'readonly');\n const req = tx.objectStore(GDB_STORE).getAllKeys();\n req.onsuccess = () => storeResult(id, (req.result || []).join(','), '');\n req.onerror = () => storeResult(id, '', req.error?.message || 'list failed');\n }).catch(e => storeResult(id, '', e.message));\n };\n g._marmot_store_delete = (id, groupIDHex) => {\n gdbOpen().then(db => {\n const tx = db.transaction(GDB_STORE, 'readwrite');\n tx.objectStore(GDB_STORE).delete(groupIDHex);\n tx.oncomplete = () => storeResult(id, '', '');\n tx.onerror = () => storeResult(id, '', tx.error?.message || 'delete failed');\n }).catch(e => storeResult(id, '', e.message));\n };\n}\n// --- WASM Loading ---\nfunction loadWasm() {\n return _loadWasm.apply(this, arguments);\n} // --- Callback Bridge ---\nfunction _loadWasm() {\n _loadWasm = _asyncToGenerator(function* () {\n if (wasmReady) return;\n // wasm_exec.js is loaded as a background script (manifest.json),\n // so globalThis.Go is already available.\n const GoClass = globalThis.Go;\n if (!GoClass) throw new Error('Go WASM runtime not loaded (wasm_exec.js missing from background scripts)');\n const go = new GoClass();\n const wasmUrl = browser.runtime.getURL('marmot.wasm');\n const wasmResponse = yield fetch(wasmUrl);\n const wasmBytes = yield wasmResponse.arrayBuffer();\n const result = yield WebAssembly.instantiate(wasmBytes, go.importObject);\n // Start the Go program. Don't await — it blocks forever (select {}).\n // The synchronous part sets up globalThis._marmot before yielding.\n go.run(result.instance);\n setupStoreCallbacks();\n wasmReady = true;\n });\n return _loadWasm.apply(this, arguments);\n}\nfunction setupMarmotInit() {\n const g = globalThis;\n // Override _marmot_init to wire our local callbacks\n g._marmot_init = (pubkeyHex, ...relayURLs) => {\n const marmot = g._marmot;\n if (!marmot) return 'error: wasm not loaded';\n const publishFn = eventJSON => {\n pushToTab({\n cmd: 'publish',\n event: eventJSON\n });\n };\n const subscribeFn = (subId, filterJSON) => {\n pushToTab({\n cmd: 'subscribe',\n subId,\n filter: filterJSON\n });\n };\n // KEY ADVANTAGE: crypto happens locally, no round-trip!\n const cryptoSendFn = (op, peerHex, data, id) => {\n handleCryptoLocal(op, peerHex, data, id);\n };\n const onDMFn = (senderHex, plaintext) => {\n const ts = Math.floor(Date.now() / 1000);\n pushToTab({\n cmd: 'dm',\n peer: senderHex,\n sender: senderHex,\n content: plaintext,\n ts,\n source: 'marmot',\n eventId: ''\n });\n };\n const onStatusFn = msg => {\n pushToTab({\n cmd: 'status',\n msg\n });\n };\n return marmot.init(pubkeyHex, publishFn, subscribeFn, cryptoSendFn, onDMFn, onStatusFn, ...relayURLs);\n };\n}\n// --- Local Crypto (no round-trip!) ---\nfunction handleCryptoLocal(op, peerHex, data, id) {\n const marmot = globalThis._marmot;\n if (!marmot) return;\n const resolve = (result, err) => {\n marmot.cryptoResult(id, result, err);\n };\n try {\n switch (op) {\n case 'signEvent':\n {\n const eventTemplate = JSON.parse(data);\n const signed = signEvent(eventTemplate, currentPrivkey);\n resolve(JSON.stringify(signed), '');\n break;\n }\n case 'nip44.encrypt':\n nip44Encrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n break;\n case 'nip44.decrypt':\n nip44Decrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n break;\n case 'nip04.encrypt':\n nip04Encrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n break;\n case 'nip04.decrypt':\n nip04Decrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n break;\n default:\n resolve('', 'unsupported crypto op: ' + op);\n }\n } catch (err) {\n resolve('', err.message);\n }\n}\n// --- Push to originating tab ---\nfunction pushToTab(data) {\n if (mlsTabId === null) return;\n browser.tabs.sendMessage(mlsTabId, {\n ext: 'smesh-signer',\n type: 'mls-push',\n data\n }).catch(() => {\n mlsTabId = null;\n });\n}\n// --- Public API (called from background.ts) ---\nexport function mlsInit(_x, _x2, _x3, _x4) {\n return _mlsInit.apply(this, arguments);\n}\nfunction _mlsInit() {\n _mlsInit = _asyncToGenerator(function* (privkey, pubkey, relayURLs, tabId) {\n mlsTabId = tabId;\n currentPrivkey = privkey;\n yield loadWasm();\n setupMarmotInit();\n const initFn = globalThis._marmot_init;\n if (!initFn) return 'error: wasm bridge not ready';\n return String(initFn(pubkey, ...relayURLs) || 'ok');\n });\n return _mlsInit.apply(this, arguments);\n}\nexport function mlsSetTab(tabId) {\n mlsTabId = tabId;\n}\nexport function mlsSendDM(recipient, content) {\n const m = globalThis._marmot;\n if (!m) return 'error: not initialized';\n return String(m.sendDM(recipient, content) || 'ok');\n}\nexport function mlsSubscribe() {\n const m = globalThis._marmot;\n if (!m) return 'error: not initialized';\n return String(m.subscribe() || 'ok');\n}\nexport function mlsPublishKP() {\n const m = globalThis._marmot;\n if (!m) return 'error: not initialized';\n return String(m.publishKP() || 'ok');\n}\nexport function mlsListGroups() {\n const m = globalThis._marmot;\n if (!m) return '[]';\n return String(m.listGroups() || '[]');\n}\nexport function mlsDeliverEvent(subId, eventJSON) {\n const m = globalThis._marmot;\n if (!m) return;\n m.deliverEvent(subId, eventJSON);\n}\nexport function mlsHandleEvent(eventJSON) {\n const m = globalThis._marmot;\n if (!m) return;\n m.handleEvent(eventJSON);\n}\nexport function isMlsMethod(method) {\n return method.startsWith('mls.');\n}","map":null,"metadata":{},"sourceType":"module","externalDependencies":[]}