injected.js raw

   1  // Injected into page context. Exposes window.nostr (NIP-07) and window.nostr.smesh (management).
   2  // All methods return Promises. Communication via postMessage → content-script → extension background.
   3  
   4  (function() {
   5    let nextID = 1;
   6    const pending = {};
   7    let promptHandler = null;
   8  
   9    window.addEventListener("message", (e) => {
  10      if (e.source !== window || !e.data || e.data.target !== "smesh-page") return;
  11      // Response to a pending request.
  12      if (e.data.id && pending[e.data.id]) {
  13        const { resolve, reject } = pending[e.data.id];
  14        delete pending[e.data.id];
  15        if (e.data.error) reject(new Error(e.data.error));
  16        else resolve(e.data.response);
  17        return;
  18      }
  19      // Broadcast from extension.
  20      if (e.data.broadcast && e.data.payload) {
  21        const msg = e.data.payload;
  22        if (msg.type === "PROMPT" && promptHandler) {
  23          promptHandler(msg);
  24        }
  25      }
  26    });
  27  
  28    function request(method, params) {
  29      return new Promise((resolve, reject) => {
  30        const id = nextID++;
  31        pending[id] = { resolve, reject };
  32        window.postMessage({ target: "smesh-signer", id, payload: { method, params } }, "*");
  33        setTimeout(() => {
  34          if (pending[id]) {
  35            delete pending[id];
  36            reject(new Error("timeout"));
  37          }
  38        }, 300000);
  39      });
  40    }
  41  
  42    window.nostr = {
  43      getPublicKey:  ()           => request("getPublicKey"),
  44      signEvent:     (event)      => request("signEvent", { event }),
  45      getRelays:     ()           => request("getRelays"),
  46  
  47      nip04: {
  48        encrypt: (pk, pt) => request("nip04.encrypt", { pubkey: pk, plaintext: pt }),
  49        decrypt: (pk, ct) => request("nip04.decrypt", { pubkey: pk, ciphertext: ct }),
  50      },
  51  
  52      nip44: {
  53        encrypt: (pk, pt) => request("nip44.encrypt", { pubkey: pk, plaintext: pt }),
  54        decrypt: (pk, ct) => request("nip44.decrypt", { pubkey: pk, ciphertext: ct }),
  55      },
  56  
  57      // Management API (sm3sh modal → extension).
  58      smesh: {
  59        isInstalled:      ()              => true,
  60        getVaultStatus:   ()              => request("smesh.getVaultStatus"),
  61        unlockVault:      (password)      => request("smesh.unlockVault", { password }),
  62        lockVault:        ()              => request("smesh.lockVault"),
  63        createVault:      (password)      => request("smesh.createVault", { password }),
  64        listIdentities:   ()              => request("smesh.listIdentities"),
  65        switchIdentity:   (pubkey)        => request("smesh.switchIdentity", { pubkey }),
  66        addIdentity:      (nsec)          => request("smesh.addIdentity", { nsec }),
  67        removeIdentity:   (pubkey)        => request("smesh.removeIdentity", { pubkey }),
  68        exportVault:      ()              => request("smesh.exportVault"),
  69        importVault:      (data)          => request("smesh.importVault", { data }),
  70        getPermissions:   ()              => request("smesh.getPermissions"),
  71        setPermission:    (host, method, policy) => request("smesh.setPermission", { host, method, policy }),
  72        onPrompt:         (cb)            => { promptHandler = cb; },
  73  
  74        // HD keychain
  75        generateMnemonic: ()              => request("smesh.generateMnemonic"),
  76        validateMnemonic: (mnemonic)      => request("smesh.validateMnemonic", { mnemonic }),
  77        createHDVault:    (password, name) => request("smesh.createHDVault", { password, name }),
  78        restoreHDVault:   (password, mnemonic, name) => request("smesh.restoreHDVault", { password, mnemonic, name }),
  79        deriveIdentity:   (name)          => request("smesh.deriveIdentity", { name }),
  80        getMnemonic:      ()              => request("smesh.getMnemonic"),
  81        probeAccount:     (index)         => request("smesh.probeAccount", { index }),
  82        isHD:             ()              => request("smesh.isHD"),
  83        resetExtension:   ()              => request("smesh.resetExtension"),
  84      },
  85    };
  86  })();
  87