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