npm.io
7.0.2 • Published 6 months ago

@delight-rpc/electron

Licence
MIT
Version
7.0.2
Deps
7
Size
48 kB
Vulns
0
Weekly
0
Stars
4

@delight-rpc/electron

Install

npm install --save @delight-rpc/electron
# or
yarn add @delight-rpc/electron

Usage

If your application sends RPC requests immediately after creating the RPC client, you need to create the RPC server as soon as possible, which means your corresponding code needs to be as synchronized as possible.

Main as Client, Renderer as Server
api.d.ts
interface IAPI {
  echo(message: string): string
}
main.ts
import { app, ipcMain } from 'electron'
import { createClientInMain } from '@delight-rpc/electron'

await app.whenReady()

ipcMain.on('message-port', async event => {
  const [port] = event.ports

  port.start()
  const [client] = createClientInMain<IAPI>(port)
  await client.echo('hello world')
})

const window = new BrowserWindow({
  webPreferences: { preload: 'preload.js' }
})
window.loadFile('renderer.html')
preload.ts
import { ipcRenderer } from 'electron'

window.addEventListener('message', event => {
  if (event.data === 'message-port') {
    const [port] = event.ports
    ipcRenderer.postMessage('message-port', null, [port])
  }
})
renderer.ts
import { createServerInRenderer } from '@delight-rpc/electron'

const api: IAPI = {
  echo(message) {
    return message
  }
}

// create the MessageChannel in the renderer,
// because its script file is always executed last.
const channel = new MessageChannel()

channel.port1.start()
createServerInRenderer(api, channel.port1)
window.postMessage('message-port', '*', [channel.port2])
Renderer as Client, Main as Server
api.d.ts
interface IAPI {
  echo(message: string): string
}
main.ts
import { app, ipcMain } from 'electron'
import { createClientInMain } from '@delight-rpc/electron'

const api: IAPI = {
  echo(message) {
    return message
  }
}

await app.whenReady()

ipcMain.on('message-port', event => {
  const [port] = event.ports

  port.start()
  createServerInMain(api, port)
})

const window = new BrowserWindow({
  webPreferences: { preload: 'preload.js' }
})
window.loadFile('renderer.html')
preload.ts
import { ipcRenderer } from 'electron'

window.addEventListener('message', event => {
  if (event.data === 'message-port') {
    const [port] = event.ports
    ipcRenderer.postMessage('message-port', null, [port])
  }
})
renderer.ts
import { createClientInRenderer } from '@delight-rpc/electron'

const api: IAPI = {
  echo(message) {
    return message
  }
}

// create the MessageChannel in the renderer,
// because its script file is always executed last.
const channel = new MessageChannel()
window.postMessage('message-port', '*', [channel.port2])

channel.port1.start()
const [client] = createClientInRenderer(api, channel.port1)
await client.echo('hello world')
Renderer as Client, Renderer as Server
api.d.ts
interface IAPI {
  echo(message: string): string
}
main.ts
import { app, ipcMain, MessageChannelMain } from 'electron'

await app.whenReady()

const windowA = new BrowserWindow({
  webPreferences: { preload: 'preload.js' }
})
windowA.loadFile('renderer-a.html')

const windowB = new BrowserWindow({
  webPreferences: { preload: 'preload.js' }
})
windowB.loadFile('renderer-b.html')

const channel = new MessageChannelMain()
windowA.webContents.postMessage('message-port', null, [channel.port1])
windowB.webContents.postMessage('message-port', null, [channel.port2])
preload.ts
import { ipcRenderer, contextBridge } from 'electron'
import { Deferred } from 'extra-promise'

const ready = new Deferred<void>()
contextBridge.exposeInMainWorld('ready', () => {
  ready.resolve()
})

ipcRenderer.on('message-port', async event => {
  const [port] = event.ports
  await ready
  window.postMessage('message-port', '*', [port])
})
renderer-a.ts
import { createServerInRenderer } from '@delight-rpc/electron'

const api: IAPI = {
  echo(message) {
    return message
  }
}

window.addEventListener('message', async event => {
  if (event.data === 'message-port') {
    const [port] = event.ports

    port.start()
    createServerInRenderer(api, port)
  }
})

window.ready()
renderer-b.ts
import { createClientInRenderer } from '@delight-rpc/electron'

window.addEventListener('message', async event => {
  if (event.data === 'message-port') {
    const [port] = event.ports

    port.start()
    const [client] = createClientInRenderer<IAPI>(port)
    await client.echo('hello world')
  }
})

window.ready()

API

createClientInMain
function createClientInMain<IAPI extends object>(
  port: Electron.MessagePortMain
, options?: {
    parameterValidators?: DelightRPC.ParameterValidators<IAPI>
    expectedVersion?: string
    channel?: string
  }
): [client: DelightRPC.ClientProxy<IAPI>, close: () => void]
createClientInRenderer
function createClientInRenderer<IAPI extends object>(
  port: MessagePort
, options?: {
    parameterValidators?: DelightRPC.ParameterValidators<IAPI>
    expectedVersion?: string
    channel?: string
  }
): [client: DelightRPC.ClientProxy<IAPI>, close: () => void]
createBatchClientInMain
function createBatchClientInMain<DataType>(
  port: Electron.MessagePortMain
, options?: {
    expectedVersion?: string
    channel?: string
  }
): [client: DelightRPC.BatchClient<DataType>, close: () => void]
createBatchClientInRenderer
function createBatchClientInRenderer<DataType>(
  port: MessagePort
, options?: {
    expectedVersion?: string
    channel?: string
  }
): [client: DelightRPC.BatchClient<DataType>, close: () => void]
createServerInMain
function createServerInMain<IAPI extends object>(
  api: DelightRPC.ImplementationOf<IAPI>
, options?: {
    port: Electron.MessagePortMain
    parameterValidators?: DelightRPC.ParameterValidators<IAPI>
    version?: `${number}.${number}.${number}`
    channel?: string | RegExp | AnyChannel
    ownPropsOnly?: boolean
  }
): () => void
createServerInRenderer
function createServerInRenderer<IAPI extends object>(
  api: DelightRPC.ImplementationOf<IAPI>
, port: MessagePort
, options?: {
    parameterValidators?: DelightRPC.ParameterValidators<IAPI>
    version?: `${number}.${number}.${number}`
    channel?: string | RegExp | AnyChannel
    ownPropsOnly?: boolean
  }
): () => void