UNPKG

@jsonjoy.com/reactive-rpc

Version:

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

150 lines 5.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamingRpcClient = void 0; const tslib_1 = require("tslib"); const rxjs_1 = require("rxjs"); const msg = tslib_1.__importStar(require("../../messages")); const subscribeCompleteObserver_1 = require("../../util/subscribeCompleteObserver"); const TimedQueue_1 = require("../../util/TimedQueue"); const Value_1 = require("../../messages/Value"); class StreamingRpcClient { constructor({ send, bufferSize = 100, bufferTime = 10 }) { this.id = 1; this.calls = new Map(); this.buffer = new TimedQueue_1.TimedQueue(); this.buffer.itemLimit = bufferSize; this.buffer.timeLimit = bufferTime; this.buffer.onFlush = send; } getInflightCallCount() { return this.calls.size; } onMessages(messages) { const length = messages.length; for (let i = 0; i < length; i++) this.onMessage(messages[i]); } onMessage(message) { if (message instanceof msg.ResponseCompleteMessage) { this.onResponseComplete(message); return; } else if (message instanceof msg.ResponseDataMessage) { this.onResponseData(message); return; } else if (message instanceof msg.ResponseErrorMessage) { this.onResponseError(message); return; } this.onRequestUnsubscribe(message); } onResponseComplete(message) { const { id, value } = message; const call = this.calls.get(id); if (!call) return; call.resFinalized = true; const data = value ? value.data : undefined; if (data !== undefined) call.res$.next(data); call.res$.complete(); } onResponseData(message) { const { id, value } = message; const call = this.calls.get(id); if (!call) return; call.res$.next(value.data); } onResponseError(message) { const { id, value } = message; const call = this.calls.get(id); if (!call) return; call.resFinalized = true; call.res$.error(value.data); } onRequestUnsubscribe(message) { const { id } = message; const call = this.calls.get(id); if (!call) return; call.req$.complete(); } call$(method, data) { const id = this.id++; if (this.id >= 0xffff) this.id = 1; if (this.calls.has(id)) return this.call$(method, data); const req$ = new rxjs_1.Subject(); const res$ = new rxjs_1.Subject(); let finalizedStreams = 0; const cleanup = () => { finalizedStreams++; if (finalizedStreams === 2) this.calls.delete(id); }; res$.subscribe({ error: cleanup, complete: cleanup }); const entry = { req$, res$ }; this.calls.set(id, entry); if ((0, rxjs_1.isObservable)(data)) { let firstMessageSent = false; (0, subscribeCompleteObserver_1.subscribeCompleteObserver)(req$, { next: (value) => { const messageMethod = firstMessageSent ? '' : method; firstMessageSent = true; const message = new msg.RequestDataMessage(id, messageMethod, new Value_1.RpcValue(value, undefined)); this.buffer.push(message); }, error: (error) => { cleanup(); const messageMethod = firstMessageSent ? '' : method; const message = new msg.RequestErrorMessage(id, messageMethod, new Value_1.RpcValue(error, undefined)); this.buffer.push(message); }, complete: (value) => { cleanup(); const messageMethod = firstMessageSent ? '' : method; const message = new msg.RequestCompleteMessage(id, messageMethod, new Value_1.RpcValue(value, undefined)); this.buffer.push(message); }, }); data.subscribe(req$); } else { this.buffer.push(new msg.RequestCompleteMessage(id, method, new Value_1.RpcValue(data, undefined))); req$.complete(); cleanup(); } return new rxjs_1.Observable((observer) => { res$.subscribe(observer); return () => { if (!entry.resFinalized) this.buffer.push(new msg.ResponseUnsubscribeMessage(id)); res$.complete(); }; }); } async call(method, request) { return await (0, rxjs_1.firstValueFrom)(this.call$(method, request)); } notify(method, data) { const value = new Value_1.RpcValue(data, undefined); this.buffer.push(new msg.NotificationMessage(method, value)); } stop(reason = 'STOP') { this.buffer.onFlush = () => { }; for (const call of this.calls.values()) { call.req$.error(new Error(reason)); call.req$.error(new Error(reason)); } this.calls.clear(); } disconnect() { this.stop('DISCONNECT'); } } exports.StreamingRpcClient = StreamingRpcClient; //# sourceMappingURL=StreamingRpcClient.js.map