OverviewFilesCommitsIssuesDocsReleasesPeers

pearbrowser-desktop / backend / pear-adapter.cjs

83 lines · javascript ref:
/**
 * Pear adapter for PearBrowser backend.
 *
 * The original backend assumes a mobile BareKit host that injects
 * `BareKit.IPC` (a duplex stream) before the backend loads. For the
 * desktop worker adapter we use an in-memory pipe pair.
 *
 * The v3 Electron host owns the worker lifecycle. If it later provides a
 * native duplex, only the two sides returned here need replacing; the backend
 * and renderer RPC contracts remain unchanged.
 */

const EventEmitter = require('bare-events')

class InMemoryPipe extends EventEmitter {
  constructor (name) {
    super()
    this.name = name
    this._peer = null
    this._closed = false
  }

  _link (peer) {
    this._peer = peer
  }

  write (chunk) {
    if (this._closed) return false
    if (!this._peer) return false
    queueMicrotask(() => {
      if (!this._peer._closed) this._peer.emit('data', chunk)
    })
    return true
  }

  end () {
    if (this._closed) return
    this._closed = true
    queueMicrotask(() => {
      if (this._peer && !this._peer._closed) this._peer.emit('close')
    })
  }

  ref () {}
  unref () {}
}

function createPipePair () {
  const a = new InMemoryPipe('ui')
  const b = new InMemoryPipe('backend')
  a._link(b)
  b._link(a)
  return [a, b]
}

function bootBackend ({ storagePath, esmModules }) {
  const [uiSide, backendSide] = createPipePair()

  // Shim the BareKit global the backend expects.
  globalThis.BareKit = { IPC: backendSide }

  // Stash ESM modules the root `index.js` loaded statically so the
  // CJS backend can use them without paying Bare's CJS→ESM
  // dynamic-import cost. See AnongptBuyer for the consumer.
  if (esmModules) globalThis._pearbrowserEsmModules = esmModules

  // Backend reads `Bare.argv[0]` for its storage path.
  try {
    if (typeof Bare !== 'undefined') {
      if (!Bare.argv) Bare.argv = []
      Bare.argv[0] = storagePath
    }
  } catch {}

  // Load the backend. It immediately starts `boot()` which
  // talks to BareKit.IPC — which is `backendSide`.
  require('./index.js')

  return uiSide
}

module.exports = { bootBackend }