@devgrid/netron
Version:
Event bus, streams and remote object invocation.
351 lines • 12.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemotePeer = void 0;
const ws_1 = require("ws");
const common_1 = require("@devgrid/common");
const service_stub_1 = require("./service-stub");
const abstract_peer_1 = require("./abstract-peer");
const predicates_1 = require("./predicates");
const common_2 = require("./common");
const packet_1 = require("./packet");
class RemotePeer extends abstract_peer_1.AbstractPeer {
constructor(socket, netron, id = '') {
super(netron, id);
this.socket = socket;
this.responseHandlers = new common_1.TimedMap(this.netron.options?.requestTimeout ?? common_2.REQUEST_TIMEOUT, (packetId) => {
const handlers = this.deleteResponseHandler(packetId);
if (handlers?.errorHandler) {
handlers.errorHandler(new Error('Request timeout exceeded'));
}
});
this.writableStreams = new Map();
this.readableStreams = new Map();
this.eventSubscribers = new Map();
this.remoteSubscriptions = new Map();
this.abilities = {};
this.services = new Map();
this.definitions = new Map();
}
async init(isConnector, abilities) {
this.socket.on('message', (data, isBinary) => {
if (isBinary) {
this.handlePacket((0, packet_1.decodePacket)(data));
}
else {
console.warn('Received non-binary message:', data);
}
});
if (isConnector) {
this.abilities = (await this.runTask('abilities', abilities));
if (this.abilities.services) {
for (const [name, definition] of this.abilities.services) {
this.definitions.set(definition.id, definition);
this.services.set(name, definition);
}
}
if (this.abilities.subsribeForServices) {
await this.subscribe(common_2.NETRON_EVENT_SERVICE_EXPOSE, (event) => {
this.definitions.set(event.definition.id, event.definition);
this.services.set(event.name, event.definition);
});
await this.subscribe(common_2.NETRON_EVENT_SERVICE_UNEXPOSE, (event) => {
this.definitions.delete(event.defId);
this.services.delete(event.name);
});
}
}
}
async exposeService(instance) {
const meta = Reflect.getMetadata(common_2.SERVICE_ANNOTATION, instance.constructor);
if (!meta) {
throw new Error('Invalid service');
}
if (this.services.has(meta.name)) {
throw new Error(`Service already exposed: ${meta.name}`);
}
const def = await this.runTask('expose_service', meta);
const stub = new service_stub_1.ServiceStub(this.netron.peer, instance, meta);
this.netron.peer.stubs.set(def.id, stub);
this.netron.peer.serviceInstances.set(instance, stub);
return def;
}
async unexposeService(serviceName) {
const defId = await this.runTask('unexpose_service', serviceName);
for (const i of this.interfaces.values()) {
if (i.instance.$def?.parentId === defId) {
this.releaseInterface(i.instance);
}
}
const stub = this.netron.peer.stubs.get(defId);
if (stub) {
this.netron.peer.serviceInstances.delete(stub.instance);
this.netron.peer.stubs.delete(defId);
}
}
async subscribe(eventName, handler) {
const handlers = this.eventSubscribers.get(eventName);
if (!handlers) {
this.eventSubscribers.set(eventName, [handler]);
await this.runTask('subscribe', eventName);
}
else if (!handlers.includes(handler)) {
handlers.push(handler);
}
}
async unsubscribe(eventName, handler) {
const handlers = this.eventSubscribers.get(eventName);
if (handlers) {
const index = handlers.indexOf(handler);
if (index >= 0) {
handlers.splice(index, 1);
if (handlers.length === 0) {
this.eventSubscribers.delete(eventName);
await this.runTask('unsubscribe', eventName);
}
}
}
}
getServiceNames() {
return [...this.services.keys()];
}
get(defId, name) {
const def = this.definitions.get(defId);
if (!def) {
throw new Error(`Unknown definition: ${defId}`);
}
return new Promise((resolve, reject) => {
this.sendRequest(packet_1.TYPE_GET, [defId, name], (result) => {
resolve(this.processResult(def, result));
}, reject).catch(reject);
});
}
set(defId, name, value) {
const def = this.definitions.get(defId);
if (!def) {
throw new Error(`Unknown definition: ${defId}`);
}
return new Promise((resolve, reject) => {
this.sendRequest(packet_1.TYPE_SET, [defId, name, value], () => {
resolve();
}, reject).catch(reject);
});
}
call(defId, method, args) {
const def = this.definitions.get(defId);
if (!def) {
throw new Error(`Unknown definition: ${defId}`);
}
args = this.processArgs(def, args);
return new Promise((resolve, reject) => {
this.sendRequest(packet_1.TYPE_CALL, [defId, method, ...args], (result) => {
resolve(this.processResult(def, result));
}, reject).catch(reject);
});
}
disconnect() {
this.socket.close();
this.responseHandlers.clear();
this.writableStreams.clear();
this.readableStreams.clear();
this.eventSubscribers.clear();
this.remoteSubscriptions.clear();
this.services.clear();
this.definitions.clear();
}
runTask(name, ...args) {
return new Promise((resolve, reject) => {
this.sendRequest(packet_1.TYPE_TASK, [name, ...args], (result) => {
resolve(result);
}, reject).catch(reject);
});
}
sendRequest(type, data, successHandler, errorHandler) {
const packet = (0, packet_1.createPacket)(packet_1.Packet.nextId(), 1, type, data);
this.responseHandlers.set(packet.id, {
successHandler,
errorHandler,
});
return this.sendPacket(packet);
}
sendResponse(packet, data) {
packet.setImpulse(0);
packet.data = data;
return this.sendPacket(packet);
}
sendErrorResponse(packet, error) {
packet.setImpulse(0);
packet.setError(1);
packet.data = error;
return this.sendPacket(packet);
}
sendPacket(packet) {
return new Promise((resolve, reject) => {
if (this.socket.readyState === ws_1.WebSocket.OPEN) {
this.socket.send((0, packet_1.encodePacket)(packet), { binary: true }, (err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
}
else {
reject(new Error('Socket closed'));
}
});
}
sendStreamChunk(streamId, chunk, index, isLast, isLive) {
return this.sendPacket((0, packet_1.createStreamPacket)(packet_1.Packet.nextId(), streamId, index, isLast, isLive, chunk));
}
handleResponse(packet) {
const id = packet.id;
const handlers = this.deleteResponseHandler(id);
if (handlers) {
const data = packet.data;
if (packet.getError() === 0) {
handlers.successHandler(data);
}
else {
handlers.errorHandler?.(data);
}
}
}
async handlePacket(packet) {
const pType = packet.getType();
if (packet.getImpulse() === 0) {
this.handleResponse(packet);
return;
}
switch (pType) {
case packet_1.TYPE_SET: {
const [defId, name, value] = packet.data;
try {
const stub = this.netron.peer.getStubByDefinitionId(defId);
await stub.set(name, value);
await this.sendResponse(packet, undefined);
}
catch (err) {
console.error('Error setting value:', err);
try {
await this.sendErrorResponse(packet, err);
}
catch (err_) {
console.error('Error sending error response:', err_);
}
}
break;
}
case packet_1.TYPE_GET: {
const [defId, name] = packet.data;
try {
const stub = this.netron.peer.getStubByDefinitionId(defId);
await this.sendResponse(packet, await stub.get(name));
}
catch (err) {
try {
await this.sendErrorResponse(packet, err);
}
catch (err_) {
console.error('Error sending error response:', err_);
}
}
break;
}
case packet_1.TYPE_CALL: {
const [defId, method, ...args] = packet.data;
try {
const stub = this.netron.peer.getStubByDefinitionId(defId);
await this.sendResponse(packet, await stub.call(method, args));
}
catch (err) {
try {
await this.sendErrorResponse(packet, err);
}
catch (err_) {
console.error('Error sending error response:', err_);
}
}
break;
}
case packet_1.TYPE_TASK: {
const [name, ...args] = packet.data;
try {
await this.sendResponse(packet, await this.netron.runTask(this, name, ...args));
}
catch (err) {
try {
await this.sendErrorResponse(packet, err);
}
catch (err_) {
console.error('Error sending error response:', err_);
}
}
break;
}
case packet_1.TYPE_STREAM: {
if (!packet.streamId || packet.streamIndex === undefined)
return;
const stream = this.readableStreams.get(packet.streamId);
if (!stream)
return;
stream.onPacket(packet);
break;
}
default: {
console.warn('Unknown packet type:', pType);
}
}
}
async releaseInterfaceInternal(iInstance) {
await this.runTask('unref_service', iInstance.$def?.id);
this.unrefService(iInstance.$def?.id);
}
refService(def, parentDef) {
const existingDef = this.definitions.get(def.id);
if (existingDef) {
return existingDef;
}
def.parentId = parentDef.id;
this.definitions.set(def.id, def);
return def;
}
unrefService(defId) {
if (defId) {
this.definitions.delete(defId);
}
}
processResult(parentDef, result) {
if ((0, predicates_1.isServiceDefinition)(result)) {
const def = this.refService(result, parentDef);
return this.queryInterfaceByDefId(def.id, def);
}
return result;
}
processArgs(ctxDef, args) {
return args;
}
deleteResponseHandler(packetId) {
const handlers = this.responseHandlers.get(packetId);
if (handlers) {
this.responseHandlers.delete(packetId);
}
return handlers;
}
getDefinitionById(defId) {
const def = this.definitions.get(defId);
if (!def) {
throw new Error(`Unknown definition: ${defId}.`);
}
return def;
}
getDefinitionByServiceName(name) {
const def = this.services.get(name);
if (def === void 0) {
throw new Error(`Unknown service: ${name}.`);
}
return def;
}
}
exports.RemotePeer = RemotePeer;
//# sourceMappingURL=remote-peer.js.map