UNPKG

@ch1/rpc

Version:

JavaScript Remote Procedure Call (RPC)

301 lines (246 loc) 6.28 kB
/** * Utility functions */ import { Dictionary, RPC, RPCDefer, RPCNodeCallback, RPCEvent, RPCError, RPCErrorPayload, RPCInvocationPayload, RPCNotify, RPCReturnPayload, } from './interfaces'; import { DEFAULT_ASYNC_TYPE } from './constants'; /** * This function is for creating new instances of functions. This is handy for * "enhancing" and enforcing correctness with certain event emitter libraries * * __Note__ this function will likely not work with arrow functions * * __Note__ this function uses `new Function`. Consequently functions will be * created in the global context. In other words, make sure your function is * pure and does not rely on external variables, unless those variables are * global. */ export function createNewFunctionFrom(func: Function): Function { const rawFunctionString = func.toString(); /** Eliminate references to Istanbul code coverage */ const functionString = rawFunctionString.replace(/__cov_(.+?)\+\+;?/g, ''); // proceed with normal logic const firstCurly = functionString.indexOf('{') + 1; const lastCurly = functionString.lastIndexOf('}'); const firstBracket = functionString.indexOf('(') + 1; const lastBracket = functionString.indexOf(')'); const functionContents = functionString.slice(firstCurly, lastCurly); const args = functionString .slice(firstBracket, lastBracket) .split(',') .filter(Boolean) .map(s => s.trim()); return new Function(...args, functionContents); } export function isRPC<T>(arg: any): arg is RPC<T> { if (!arg) { return false; } if (!isFunction(arg.destroy)) { return false; } if (!arg.remote) { return false; } return true; } export const isRPCDefaultAsync = arg => { if (arg === DEFAULT_ASYNC_TYPE) { return true; } return false; }; export function isDefer<T>(thing: any): thing is RPCDefer<T> { if (!thing) { return false; } if (!isFunction(thing.resolve)) { return false; } if (!isFunction(thing.reject)) { return false; } if (!isPromise(thing.promise)) { return false; } return true; } export function isError(err: any): err is Error { return err instanceof Error; } export function isFunction(fn: any): fn is Function { return typeof fn === 'function'; } export function isObject(obj: any): obj is Object { if (!obj) { return false; } return typeof obj === 'object'; } export function isString(arg: any): arg is string { return typeof arg === 'string'; } export function isDictionary<T>(dict: any): dict is Dictionary<T> { return isObject(dict); } export function isPromise<T>(promise: any): promise is Promise<T> { if (!promise) { return false; } if (!isFunction(promise.then)) { return false; } if (!isFunction(promise.catch)) { return false; } return true; } export function isRPCNodeCallback<T>(arg: any): arg is RPCNodeCallback<T> { return isFunction(arg); } export function isRPCNotify<T>(arg: any): arg is RPCNotify<T> { return isFunction(arg); } export function isRPCEvent(event: any): event is RPCEvent { if (!event) { return false; } if (typeof event.uid !== 'string') { return false; } if (typeof event.type !== 'number') { return false; } if ( isRPCErrorPayload(event.payload) || isRPCInvocationPayload(event.payload) || isRPCReturnPayload(event.payload) ) { return true; } return false; } export function isRPCError(error: any): error is RPCError { if (!error) { return false; } if (!error.message) { return false; } return true; } export function isRPCErrorPayload(payload: any): payload is RPCErrorPayload { if (!payload) { return false; } return isRPCError(payload.error); } export function isRPCInvocationPayload( payload: any, ): payload is RPCInvocationPayload { if (!payload) { return false; } if (!Array.isArray(payload.args)) { return false; } if (typeof payload.fn !== 'string') { return false; } return true; } export function isRPCReturnPayload( payload: any, ): payload is RPCReturnPayload { if (!payload) { return false; } return Array.isArray(payload.result); } export function noop(): void {} export const pnoop: () => Promise<void> = () => new Promise<void>(resolve => resolve()); export function createUidGenerator(): () => string { let uidCount = 0; const rpcId = Math.floor(Math.random() * 100000).toString(32); return () => { // increment the counter uidCount += 1; // return a uid return [rpcId, uidCount.toString(32)].join('-'); }; } export function defer<T>(): RPCDefer<T> { let pass = noop; let fail = noop; const promise = new Promise<T>((resolve, reject) => { pass = resolve; fail = reject; }); return { promise, reject: fail, resolve: pass, }; } export function createRangeError(message) { return new RangeError('js-rpc: ' + message); } export function createTypeError(message) { return new TypeError('js-rpc: ' + message); } export function rangeError(message) { throw createRangeError(message); } export function safeInstantiate(fn: Function, args: any[]) { try { return new (Function.prototype.bind.apply(fn, arguments))(); } catch (err) { return err; } } export function safeInvoke(fn: Function, args: any[]) { try { return fn.apply(null, args); } catch (err) { return err; } } export function throwIfNotDefer(d: any, message?: string) { if (!isDefer(d)) { typeError(message || 'given value is not a defer'); } } export function throwIfNotError(err: any, message?: string) { if (!isError(err)) { typeError(message || 'given value is not an Error'); } } export function throwIfNotFunction(fn: any, message?: string) { if (!isFunction(fn)) { typeError(message || 'given value is not a function'); } } export function throwIfNotRPCEvent(event: any, message?: string) { if (!isRPCEvent(event)) { typeError(message || 'given value is not an RPCEvent'); } } export function throwIfNotObject(obj: any, message?: string) { if (!isObject(obj)) { typeError(message || 'given value is not an object'); } } export function typeError(message) { throw createTypeError(message); }