All files / client pubsub.js

16.07% Statements 9/56
0% Branches 0/14
0% Functions 0/14
18.75% Lines 9/48

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 1191x 1x 1x 1x 1x 1x     39x                         39x                                                                                                                                                                                             39x    
import { caller, expose } from 'postmsg-rpc'
import callbackify from 'callbackify'
import shortid from 'shortid'
import { pre } from 'prepost'
import { functionToJson } from '../serialization/function'
import { isBufferJson, bufferFromJson, preBufferToJson } from '../serialization/buffer'
 
export default function (opts) {
  const subs = [
  /*
    {
      topic,      // name of the topic subscribed to
      handler,    // the handler provided by the subscriber - rpc.exposedFn calls this function
      rpc: {      // details of the exposed RPC function created to receive updates
        fnName,   // the RPC function name
        exposedFn // the exposed RPC function created by postmsg-rpc
      }
    }
  */
  ]
 
  const api = {
    publish: callbackify.variadic(
      pre(
        preBufferToJson(1),
        caller('ipfs.pubsub.publish', opts)
      )
    ),
    subscribe: function (topic, handler, options, cb) {
      let sub
 
      if (typeof options === 'function') {
        cb = options
        options = {}
      }
 
      const stub = pre(
        (...args) => {
          const fnName = `ipfs.pubsub.subscribe.handler.${shortid()}`
 
          sub = {
            topic,
            handler,
            rpc: {
              fnName,
              exposedFn: expose(fnName, pre(
                (...args) => {
                  if (args[0]) {
                    if (isBufferJson(args[0].data)) {
                      args[0].data = bufferFromJson(args[0].data)
                    }
 
                    if (isBufferJson(args[0].seqno)) {
                      args[0].seqno = bufferFromJson(args[0].seqno)
                    }
                  }
 
                  return args
                },
                (...args) => {
                  process.nextTick(() => handler(...args))
                  return Promise.resolve()
                }
              ), opts)
            }
          }
 
          subs.push(sub)
 
          args[1] = functionToJson(fnName)
 
          return args
        },
        // If error, then remove subscription handler
        (...args) => {
          return caller('ipfs.pubsub.subscribe', opts)(...args)
            .catch((err) => {
              sub.rpc.exposedFn.close()
              subs.splice(subs.indexOf(sub), 1)
              throw err
            })
        }
      )
 
      if (cb) {
        stub(topic, handler, options)
          .then((res) => process.nextTick(() => cb(null, res)))
          .catch((err) => process.nextTick(() => cb(err)))
      } else {
        return stub(topic, handler, options)
      }
    },
    unsubscribe: callbackify.variadic(
      pre(
        (...args) => {
          const topic = args[0]
          const sub = subs.find((s) => s.topic === topic && s.handler === args[1])
 
          if (sub) {
            args[1] = functionToJson(sub.rpc.fnName)
            sub.rpc.exposedFn.close()
            subs.splice(subs.indexOf(sub), 1)
          }
 
          return args
        },
        caller('ipfs.pubsub.unsubscribe', opts)
      )
    ),
    peers: callbackify.variadic(caller('ipfs.pubsub.peers', opts)),
    ls: callbackify.variadic(caller('ipfs.pubsub.ls', opts)),
    // interface-ipfs-core tests use this function
    // noop since we're not an EventEmitter
    setMaxListeners: () => api
  }
 
  return api
}