{"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 = '';\nlet mlsInitPromise = null;\n// --- IDB Group Store (same schema as the marmot SW used) ---\nconst GDB_NAME = 'marmot-groups';\nconst GDB_VER = 1;\nconst GDB_STORE = 'groups';\n// Open DB for writes — creates the store if it doesn't exist yet.\nfunction gdbOpenWrite() {\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}\n// Open DB for reads — returns null if the DB/store doesn't exist (no side-effects).\nfunction gdbOpenRead() {\n  return new Promise(resolve => {\n    const req = indexedDB.open(GDB_NAME);\n    req.onupgradeneeded = () => {\n      // DB doesn't exist yet — abort so we don't create an empty one.\n      req.result.close();\n      req.transaction?.abort();\n    };\n    req.onsuccess = () => {\n      const db = req.result;\n      if (!db.objectStoreNames.contains(GDB_STORE)) {\n        db.close();\n        resolve(null);\n        return;\n      }\n      resolve(db);\n    };\n    req.onerror = () => resolve(null);\n    req.onblocked = () => resolve(null);\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    gdbOpenWrite().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    gdbOpenRead().then(db => {\n      if (!db) {\n        storeResult(id, '', '');\n        return;\n      }\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    gdbOpenRead().then(db => {\n      if (!db) {\n        storeResult(id, '', '');\n        return;\n      }\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    gdbOpenRead().then(db => {\n      if (!db) {\n        storeResult(id, '', '');\n        return;\n      }\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).then(() => {\n      console.error('[mls-engine] Go program exited unexpectedly');\n      wasmReady = false;\n    }, err => {\n      console.error('[mls-engine] Go program crashed:', err);\n      wasmReady = false;\n    });\n    // Verify the Go side registered its API before we proceed.\n    if (!globalThis._marmot) {\n      throw new Error('Go WASM started but _marmot global not registered');\n    }\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  // Returns a Promise that resolves when NewClient completes (it runs in a\n  // goroutine because store.ListGroups blocks on async IDB callbacks).\n  g._marmot_init = /*#__PURE__*/function () {\n    var _ref = _asyncToGenerator(function* (pubkeyHex, ...relayURLs) {\n      const marmot = g._marmot;\n      if (!marmot) return 'error: wasm not loaded';\n      // Load persisted high-water mark so marmot skips already-processed events.\n      let lastEventTS = 0;\n      try {\n        const stored = yield browser.storage.local.get('marmot_last_event_ts');\n        lastEventTS = stored?.marmot_last_event_ts || 0;\n      } catch (_) {}\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      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      // Periodically persist lastEventTS so next startup skips old events.\n      setInterval(() => {\n        try {\n          const ts = marmot.lastEventTS?.();\n          if (ts > 0) browser.storage.local.set({\n            marmot_last_event_ts: ts\n          });\n        } catch (_) {}\n      }, 30000);\n      return new Promise(resolve => {\n        const onReadyFn = result => {\n          resolve(result || 'ok');\n        };\n        marmot.init(pubkeyHex, publishFn, subscribeFn, cryptoSendFn, onDMFn, onStatusFn, onReadyFn, lastEventTS, ...relayURLs);\n      });\n    });\n    return function (_x) {\n      return _ref.apply(this, arguments);\n    };\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      case 'nip44Encrypt':\n        nip44Encrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n        break;\n      case 'nip44.decrypt':\n      case 'nip44Decrypt':\n        nip44Decrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n        break;\n      case 'nip04.encrypt':\n      case 'nip04Encrypt':\n        nip04Encrypt(currentPrivkey, peerHex, data).then(r => resolve(r, ''), e => resolve('', e.message));\n        break;\n      case 'nip04.decrypt':\n      case 'nip04Decrypt':\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) {\n    console.warn('[mls-engine] pushToTab: no tab ID');\n    return;\n  }\n  browser.tabs.sendMessage(mlsTabId, {\n    ext: 'smesh-signer',\n    type: 'mls-push',\n    data\n  }).catch(err => {\n    console.warn('[mls-engine] pushToTab failed:', err);\n    mlsTabId = null;\n  });\n}\n// --- Public API (called from background.ts) ---\nexport function mlsInit(_x2, _x3, _x4, _x5) {\n  return _mlsInit.apply(this, arguments);\n}\nfunction _mlsInit() {\n  _mlsInit = _asyncToGenerator(function* (privkey, pubkey, relayURLs, tabId) {\n    mlsTabId = tabId;\n    currentPrivkey = privkey;\n    mlsInitPromise = _asyncToGenerator(function* () {\n      yield loadWasm();\n      setupMarmotInit();\n      const initFn = globalThis._marmot_init;\n      if (!initFn) return 'error: wasm bridge not ready';\n      const result = yield initFn(pubkey, ...relayURLs);\n      // Auto-bootstrap: publish key package and start subscription loop.\n      if (!result || result === 'ok') {\n        const m = globalThis._marmot;\n        if (m) {\n          m.publishKP();\n          m.subscribe();\n        }\n      }\n      return String(result || 'ok');\n    })();\n    return mlsInitPromise;\n  });\n  return _mlsInit.apply(this, arguments);\n}\nexport function mlsSetTab(tabId) {\n  mlsTabId = tabId;\n}\nexport function mlsSendDM(_x6, _x7) {\n  return _mlsSendDM.apply(this, arguments);\n}\nfunction _mlsSendDM() {\n  _mlsSendDM = _asyncToGenerator(function* (recipient, content) {\n    if (mlsInitPromise) yield mlsInitPromise;\n    const m = globalThis._marmot;\n    if (!m) return 'error: not initialized';\n    return String(m.sendDM(recipient, content) || 'ok');\n  });\n  return _mlsSendDM.apply(this, arguments);\n}\nexport function mlsSubscribe() {\n  return _mlsSubscribe.apply(this, arguments);\n}\nfunction _mlsSubscribe() {\n  _mlsSubscribe = _asyncToGenerator(function* () {\n    if (mlsInitPromise) yield mlsInitPromise;\n    const m = globalThis._marmot;\n    if (!m) return 'error: not initialized';\n    return String(m.subscribe() || 'ok');\n  });\n  return _mlsSubscribe.apply(this, arguments);\n}\nexport function mlsPublishKP() {\n  return _mlsPublishKP.apply(this, arguments);\n}\nfunction _mlsPublishKP() {\n  _mlsPublishKP = _asyncToGenerator(function* () {\n    if (mlsInitPromise) yield mlsInitPromise;\n    const m = globalThis._marmot;\n    if (!m) return 'error: not initialized';\n    return String(m.publishKP() || 'ok');\n  });\n  return _mlsPublishKP.apply(this, arguments);\n}\nexport function mlsListGroups() {\n  return _mlsListGroups.apply(this, arguments);\n}\nfunction _mlsListGroups() {\n  _mlsListGroups = _asyncToGenerator(function* () {\n    if (mlsInitPromise) yield mlsInitPromise;\n    const m = globalThis._marmot;\n    if (!m) return '[]';\n    return String(m.listGroups() || '[]');\n  });\n  return _mlsListGroups.apply(this, arguments);\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":[]}