// StorageService.proto
service StorageService {
rpc readKey(ReadRequest) returns (ReadReply) {}
}
message ReadRequest {
string type;
string key;
}
message ReadReply {
string res;
}
// client.ts
import { ipcRenderer } from 'electron';
import { createClientMessageChannelInRenderer } from 'protobufjs-electron-ipc-message-channel';
import { createStub } from 'protobufjs-utils';
// StorageService.proto is compiled with protobuff to `StorageService.ts`
import StorageService from './StorageService'
// ipcChannel implememts ClientMessageChannel and use electron ipc as wire
// can use ipc multiplexing
const ipcChannel = createClientMessageChannelInRenderer(ipcRenderer);
// we create a stub from StorageService and use ipcChannel to wire requests
const storageStub = createStub(ipcChannel, StorageService);
const { res } = await storageService.readKey({type: 'sync', key: 'key-123'})
// server.ts
import { webContents } from 'electron';
import { createServerMessageChannelFromWebContents } from 'protobufjs-electron-ipc-message-channel';
import { ServiceImplCollection } from 'protobufjs-utils';
// StorageService.proto is compiled with protobuff to `StorageService.ts`
import StorageService from './StorageService'
// inspired from https://grpc.io/docs/tutorials/basic/c.html
class StorageServiceImpl implements ServiceImpl<StorageService, Context> {
$service: StorageService;
constructor(db) {
this.db = db;
}
async readKey(readRequest, context: Context) {
// context can contain elements relative to underlying communication channel: webContents incase of electron, etc..
const val = await this.db.read(readRequest.key);
return { res: val }
}
}
const collection = new ServiceImplCollection();
const storageServiceImpl = new StorageServiceImpl(db);
collection.registerService(StorageServiceImpl);
// let's take a webContents that corresponds to the renderer used in client.ts
const wc = webContents.fromId(1);
// ipcChannel implememts ServerMessageChannel and use electron ipc as wire
const ipcChannel = createServerMessageChannelFromWebContents(wc);
ipcChannel.on('new-stream', (messageStream: Duplex<Message, Message>) => {
collection.submit(messageStream, { channel: ipcChannel, webContens: wc }: Context);
})
import { Stream, Duplex } from 'stream';
import { EventEmitter } from 'events';
import { Message } from 'protobufjs'
interface AbstractMessageChannel extends EventEmitter {
on(event: 'close', listener: () => void): this;
}
interface ClientMessageChannel extends AbstractMessageChannel {
createStream(): Duplex<Message, Message>;
};
interface ServerMessageChannel extends AbstractMessageChannel {
on(event: 'new-stream', listener: (messageStram: Duplex<Message, Message>, streamId: number) => void): this
};
interface DuplexMessageChannel extends ClientMessageChannel, ServerMessageChannel {
};