UNPKG

@jsonjoy.com/reactive-rpc

Version:

Reactive-RPC is a library for building reactive APIs over WebSocket, HTTP, and other RPCs.

220 lines 8.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RpcMessageStreamProcessor = void 0; const tslib_1 = require("tslib"); const msg = tslib_1.__importStar(require("../messages")); const TimedQueue_1 = require("../util/TimedQueue"); const RpcError_1 = require("./caller/error/RpcError"); const subscribeCompleteObserver_1 = require("../util/subscribeCompleteObserver"); const typed_1 = require("./caller/error/typed"); class RpcMessageStreamProcessor { constructor({ caller, send, bufferSize = 10, bufferTime = 1 }) { this.activeStreamCalls = new Map(); this.onStreamError = (id, error) => { this.sendErrorMessage(id, error); this.activeStreamCalls.delete(id); }; this.caller = caller; this.onSend = send; if (bufferTime) { const buffer = new TimedQueue_1.TimedQueue(); buffer.itemLimit = bufferSize; buffer.timeLimit = bufferTime; buffer.onFlush = (messages) => this.onSend(messages); this.send = (message) => { buffer.push(message); }; } else { this.send = (message) => { this.onSend([message]); }; } } onMessage(message, ctx) { if (message instanceof msg.RequestDataMessage) this.onRequestDataMessage(message, ctx); else if (message instanceof msg.RequestCompleteMessage) this.onRequestCompleteMessage(message, ctx); else if (message instanceof msg.RequestErrorMessage) this.onRequestErrorMessage(message, ctx); else if (message instanceof msg.NotificationMessage) this.onNotificationMessage(message, ctx); else if (message instanceof msg.ResponseUnsubscribeMessage) this.onUnsubscribeMessage(message); } onMessages(messages, ctx) { const length = messages.length; for (let i = 0; i < length; i++) this.onMessage(messages[i], ctx); } sendNotification(method, value) { const message = new msg.NotificationMessage(method, value); this.send(message); } sendCompleteMessage(id, value) { const message = new msg.ResponseCompleteMessage(id, value); this.send(message); } sendDataMessage(id, value) { const message = new msg.ResponseDataMessage(id, value); this.send(message); } sendErrorMessage(id, value) { const message = new msg.ResponseErrorMessage(id, value); this.send(message); } sendUnsubscribeMessage(id) { const message = new msg.RequestUnsubscribeMessage(id); this.send(message); } execStaticCall(id, name, request, ctx) { this.caller .call(name, request, ctx) .then((value) => this.sendCompleteMessage(id, value)) .catch((value) => this.sendErrorMessage(id, value)); } stop(reason = RpcError_1.RpcErrorCodes.STOP) { this.send = (() => { }); for (const call of this.activeStreamCalls.values()) { try { call.req$.error(typed_1.TypedRpcError.valueFromCode(reason)); call.stop$.next(null); } catch (error) { console.error('STOPPING_ERROR', error); } } this.activeStreamCalls.clear(); } disconnect() { this.stop(RpcError_1.RpcErrorCodes.DISCONNECT); } sendError(id, code) { const data = typed_1.TypedRpcError.valueFromCode(code); this.sendErrorMessage(id, data); } createStreamCall(id, name, ctx) { const call = this.caller.createCall(name, ctx); this.activeStreamCalls.set(id, call); (0, subscribeCompleteObserver_1.subscribeCompleteObserver)(call.res$, { next: (value) => { this.sendDataMessage(id, value); }, error: (error) => this.onStreamError(id, error), complete: (value) => { this.activeStreamCalls.delete(id); this.sendCompleteMessage(id, value); }, }); call.reqUnsubscribe$.subscribe(() => { if (this.activeStreamCalls.has(id)) this.sendUnsubscribeMessage(id); }); return call; } onRequestDataMessage(message, ctx) { const { id, method, value } = message; let call = this.activeStreamCalls.get(id); if (!call) { if (!method) { this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_INV); return; } const info = this.caller.info(method); if (!info) { this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_UNK); return; } if (info.isStreaming) { call = this.createStreamCall(id, method, ctx); } else { this.execStaticCall(id, method, value ? value.data : undefined, ctx); return; } } if (call) { const data = value ? value.data : undefined; if (data !== undefined) { call.req$.next(data); } } } onRequestCompleteMessage(message, ctx) { const { id, method, value } = message; const call = this.activeStreamCalls.get(id); if (call) { const { req$ } = call; const data = value ? value.data : undefined; if (data !== undefined) req$.next(data); req$.complete(); return; } if (!method) { this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_INV); return; } const caller = this.caller; if (!caller.exists(method)) { this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_UNK); return; } const { isStreaming } = caller.info(method); const data = value ? value.data : undefined; if (isStreaming) { const newCall = this.createStreamCall(id, method, ctx); if (newCall) { if (data !== undefined) { newCall.req$.next(data); newCall.req$.complete(); } } } else this.execStaticCall(id, method, data, ctx); } onRequestErrorMessage(message, ctx) { const { id, method, value } = message; const call = this.activeStreamCalls.get(id); if (call) { call.req$.error(value.data); return; } if (!method) { this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_INV); return; } if (!this.caller.exists(method)) { this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_UNK); return; } const { isStreaming } = this.caller.info(method); if (!isStreaming) { void this.sendError(id, RpcError_1.RpcErrorCodes.METHOD_UNK); return; } const streamCall = this.createStreamCall(id, method, ctx); if (!streamCall) return; streamCall.req$.error(value.data); } onUnsubscribeMessage(message) { const { id } = message; const call = this.activeStreamCalls.get(id); if (!call) return; this.activeStreamCalls.delete(id); call.req$.complete(); } onNotificationMessage(message, ctx) { const { method, value } = message; if (!method || method.length > 128) throw RpcError_1.RpcError.fromErrno(RpcError_1.RpcErrorCodes.METHOD_INV); const request = value && typeof value === 'object' ? value?.data : undefined; this.caller.notification(method, request, ctx).catch(() => { }); } } exports.RpcMessageStreamProcessor = RpcMessageStreamProcessor; //# sourceMappingURL=RpcMessageStreamProcessor.js.map