@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
JavaScript
"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