@ch1/rpc
Version:
JavaScript Remote Procedure Call (RPC)
166 lines (121 loc) • 5.35 kB
Markdown
# CH1 RPC
[](https://circleci.com/gh/bennett000/ch1-rpc)
_This is not well maintained_
_This is not traditional RPC, but it is like it_
## Installation
`yarn add /rpc`
## Usage
Convenient usage would be to use another library that consumes this library. Two examples are:
- [/rpc-worker](https://www.npmjs.com/package/@ch1/rpc-worker 'RPC Worker')
- [/rpc-web-socket](https://www.npmjs.com/package/@ch1/rpc-web-socket 'RPC Web Socket')
However this library can work over pretty much any stringifiable interface. Maybe binary one day.
### Simplest Pure JS Example
_Ideally process A/B would be literal separate processes like workers
or sockets. However the usage can be "faked" in a single process as
is done in [the tests]:(./spec/js-rpc.spec.ts "Testing Source Code")_
Process A
```js
const b = rpc.create({ /* config */}, {
sayOnA: (arg) => console.log(`Process B says ${arg}`);
});
b.ready.then(() => b.remote.sayOnB('hello world');
// will call sayOnB on process B
```
Process B
```js
const a = rpc.create({ /* config */}, {
sayOnB: (arg) => console.log(`Process A says ${arg}`);
});
a.ready.then(() => a.remote.sayOnA('hello world');
// will call sayOnA on process A
```
## How It Works
/rpc can work across any transport provided that the transport can be
simplified into `on` and `emit` functions.
What do we mean by `on` and `emit` functions? Let's imagine for a moment the
[Web Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers 'MDN: Using Web Workers')
_note: /rpc-worker contains convenience functions that provide WebWorker support out
of the box. The examples here are educational and *do not* need to be
implemented_
In our first process we might have a Web Worker:
```js
// make a new worker
const myWorker = new Worker('worker.js');
// js-rpc only wants/needs to use the first parameter of emit consequently the
// WebWorker post message can be used out of the box
const emit = myWorker.postMessage.bind(myWorker);
// js-rpc's on message wants/needs data to be its first parameter. Since
// WebWorker.onmessage boxes passed data into an even we need to extract it
const on = myWorker.onmessage(event => event.data);
// make a RPC Object:
const worker = create({ on, emit } /* object to expose on worker process */);
```
In our second WebWorker process we have a slightly different API:
```js
// WebWorkers use a variable called "self" to register their messages:
// self.postMessage has the same api as in our previous example
const emit = self.postMessage.bind(self);
// self.onmessage also boxes data into events
const on = self.onmessage(event => event.data);
// make a RPC Object:
const parentWindow = create(
{ on, emit } /* object to expose on window process */,
);
```
## API
The `create` call kicks everything off:
```ts
// RemoteType is a user defined type meant to inform TypeScript
// what the function signatures will be of `RPC.remote`
export function create<RemoteType>(
// The config object is more like the internal state of the system
// There are many config options
// This object will be changing in the future to better separate concerns
// Relevant config options will be documented below
config: RPCConfig,
// The optional "remote" object _in this context_ are the functions "local"
// to the `create` call that you wish to expose on the other side
// of the transport (worker, server, whatever)
remote?: Remote<any>,
// ignore this optional argument for now
remoteDesc?: RemoteDesc,
): RPC<RemoteType>;
```
The `RPC<RemoteType>` interface is the full object you get back from the `create` call:
```ts
export interface RPC<RemoteType> {
// The config object is more like the internal state of the system
// There are many config options
// This object will be changing in the future to better separate concerns
// Relevant config options will be documented below
config: RPCConfig;
// promises to free up the resources, takes an optional reason that
// propagates through to error messages received by any pending async
// results
destroy: (reason?: string) => Promise<void>;
// registers a callback and calls it in the event the object's `destroy`
// method is called. `onDestroy` returns a de-registration function
onDestroy: (callback: (reason?: string) => any) => () => void;
// this is a promise that resolves if/when the rpc system is ready
// it will time out and fail at a configurable threshold
ready: Promise<void>;
// this is the object that will expose all of the remote functions
// this object can have nested objects with functions
remote: RemoteType;
}
```
The RPCConfig Object is vast and it will be shrinking soon. The parts that
will stay consistent are the mandatory configurations:
```ts
export interface RPCConfig {
/*************** Required ***************/
// the `emit` method must be defined by the user of `rpc`
// it is what sends stuff to the worker/socket/whatever
emit: (payload: any): any;
// the `on` method must be defined by the user of `rpc`
// it is what receives stuff from the worker/socket/whatever
on: (callback: (payload: any) => any): () => any;
}
```
## License
[LGPL](./LICENSE 'Lesser GNU Public License')