@ch1/rpc
Version:
JavaScript Remote Procedure Call (RPC)
270 lines (238 loc) • 5.83 kB
text/typescript
/**
* Project interfaces
*/
/**
* Generic Dictionary
*/
export interface Dictionary<T> {
[key: string]: T;
}
/**
* Where `T` is the type of the complete interface you wish to expose
*/
export interface Remote<T> extends Dictionary<Function | Object> {}
/**
* Specify async types on the remote
*/
export interface RemoteDesc extends Dictionary<number | Dictionary<number>> {}
/**
* Async style function union
*/
export type RPCAsync<T> =
| RPCDefer<T>
| RPCNodeCallback<T>
| RPCNodeEventInternal
| RPCNotify<T>;
/**
* Ensures correct async response
*/
export interface RPCAsyncContainer<T> {
type: number;
async: RPCAsync<T> | Dictionary<RPCNodeEventInternal>;
}
export interface RPCNodeEventInternal {
listener: Function;
uid: string;
}
export type RPCAsyncContainerDictionary = Dictionary<RPCAsyncContainer<any>>;
export interface RPCNodeCallback<T> {
(error: Error, param?: T);
(error: Error, ...rest: any[]);
}
/**
* tl;dr these are used in `RemoteDesc` to describe given API functions
*
* js-rpc uses these strings to identify types of "real world" functions that
* it can interface with.
*
* These types are actually used by consumers of the library since they are the
* specific strings enforced by `RemoteDesc`
*
* - ### "nodeCallback"
* nodeCallback refers to JavaScript callback functions that strictly follow
* two rules:
*
* - the _last_ parameter passed to a function is the callback
* - a "Nullable Error" is the _first_ parameter passed to the callback
*
* Example:
*
* ```js
* function add(a, b, callback) {
* setTimeout(() => {
* if (isNumber(a) && (isNumber(b)) {
* callback(null, result);
* } else {
* callback(new TypeError('add is for numbers'));
* }
* }, 0);
* }
*
* add(1, 2, (err, result) => expect(result).toBe(3);
* add(1, 2, (err, result) => expect(error).toBeFalsey();
* add('1', 2), (erro, result) => expect(error instanceof Error).toBe(true);
* ```
*
* - ### "nodeEvent"/"nodeEventInternal"
* nodeEvents are a _limited_ interface to *
* [node EventEmitters](https://nodejs.org/api/events.html "docs")
*
* - ### "observable"
* In this case observables refer to
* [RxJS5](https://github.com/ReactiveX/rxjs, "RxJS")
*
* - ### "promise"
* promises are es6 promises or any A+ promises that implement `.then` and
* `.catch` along with the global `Promise`
*/
// export type RPCAsyncType =
// | 'nodeCallback'
// | 'nodeEvent'
// | 'nodeEventInternal'
// | 'observable'
// | 'promise';
export const RPCAsyncType = {
observable: 100,
promise: 200,
};
export interface RPCDefer<T> {
resolve: (any) => any;
reject: (any) => any;
promise: Promise<T>;
}
/**
* The Different Types of RPC Events
*/
// export type RPCEventType =
// | 'ack'
// | 'addEventListener'
// | 'create'
// | 'createReturn'
// | 'destroy'
// | 'destroyReturn'
// | 'fnReturn'
// | 'invoke'
// | 'nodeCallback'
// | 'nodeOn'
// | 'nodeRemoveListener'
// | 'browserRemoveListener'
// | 'observe'
// | 'promise'
// | 'subscribe'
// | 'subscribeReturn'
// | 'unSubscribeReturn';
export const RPCEventType = Object.freeze({
ack: 1,
create: 2,
createReturn: 3,
destroy: 4,
destroyReturn: 5,
fnReturn: 6,
invoke: 7,
promise: 9,
subscribe: 10,
unsubscribe: 11,
});
export interface RPCEmit {
(message: string, payload: RPCEvent): any;
}
export interface ConfiguredRPCEmit {
(payload: RPCEvent): any;
}
export interface RPCNotify<T> {
(param: T, ...args: any[]);
(...args: any[]);
}
export interface RPCObservable<T> {
subscribe: (
next: (param: T) => any,
onError: (error: Error) => any,
onComplete: () => any,
) => () => void;
}
export interface RPCOn {
(message: string, callback: (payload: RPCEvent) => void): () => void;
}
export interface ConfiguredRPCOn {
(callback: (payload: RPCEvent) => any): () => any;
}
export interface RPCOptions {
defaultAsyncType?: number;
defaultCreateRetry?: number;
defaultCreateRetryCurve?: number;
defaultCreateWait?: number;
enableStackTrace?: boolean;
maxAckDelay?: number;
useAcks?: Dictionary<number>;
}
export interface FunctionalState {
responders: RPCEventRegistry;
successHandlers: RPCEventRegistry;
errorHandlers: RPCEventRegistry;
}
export interface RPCAbstractConfig extends RPCOptions {
message?: string;
remote?: Object;
emit?: ConfiguredRPCEmit;
on?: ConfiguredRPCOn;
functionalState?: FunctionalState;
callbacks?: RPCAsyncContainerDictionary;
uid?: () => string;
}
export interface RPCConfig extends RPCAbstractConfig {
emit: ConfiguredRPCEmit;
on: ConfiguredRPCOn;
}
export type RPCEventRegistry = {
[key: number]: (...args: any[]) => any;
};
export type RPCAsyncRegistry = {
[key: number]: (...args: any[]) => any;
};
export type RPCRegister = (
key: number,
value: (...args: any[]) => any,
) => void;
/**
* JavaScript error with a code number
*/
export interface CodedError extends Error {
code?: number;
stack?: string;
}
/**
* Serializable Error
*/
export interface RPCError {
code?: number;
message: string;
stack?: string;
type?: string;
}
export interface RPCInvocationPayload {
fn: string;
args: any[];
}
export interface RPCErrorPayload {
error: RPCError;
}
export interface RPCReturnPayload {
result: any[];
}
export type RPCPayload =
| RPCInvocationPayload
| RPCReturnPayload
| RPCErrorPayload;
export interface RPCEvent {
type: number;
payload: RPCPayload;
uid: string;
useAcks?: boolean;
}
export interface RPC<T> {
config: RPCConfig;
destroy: (reason?: string) => Promise<void>;
onDestroy: (callback: (reason?: string) => any) => () => void;
ready: Promise<void>;
remote: T;
}