protoobject
Version:
A universal class for creating any JSON objects and simple manipulations with them.
317 lines • 12.2 kB
JavaScript
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
import { createServer, createConnection } from "node:net";
import { EventEmitter } from "node:events";
import { ProtoObject } from "./proto-object.js";
import { StaticImplements } from "../decorators/static-implements.js";
/* eslint-disable no-unused-vars */
/**
* Message types for TCP communication
*/
export var MessageType;
(function (MessageType) {
MessageType["REQUEST"] = "REQUEST";
MessageType["RESPONSE"] = "RESPONSE";
MessageType["NOTIFICATION"] = "NOTIFICATION";
MessageType["ERROR"] = "ERROR";
})(MessageType || (MessageType = {}));
/* eslint-enable no-unused-vars */
/**
* TCP Server for ProtoObject communication
*/
export class ProtoObjectTCPServer extends EventEmitter {
/* eslint-disable no-unused-vars */
constructor(port = 3000) {
super();
this.port = port;
this.clients = new Map();
this.server = createServer();
this.setupServer();
}
/* eslint-enable no-unused-vars */
setupServer() {
this.server.on("connection", (socket) => {
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
this.clients.set(clientId, socket);
console.log(`Client connected: ${clientId}`);
this.emit("clientConnected", clientId, socket);
socket.on("data", (buffer) => {
try {
const messages = this.parseMessages(buffer);
messages.forEach((message) => {
this.emit("message", clientId, message);
});
}
catch (error) {
this.emit("error", error);
}
});
socket.on("close", () => {
this.clients.delete(clientId);
console.log(`Client disconnected: ${clientId}`);
this.emit("clientDisconnected", clientId);
});
socket.on("error", (error) => {
this.emit("error", error);
});
});
}
parseMessages(buffer) {
const messages = [];
let offset = 0;
while (offset < buffer.length) {
// Read message length (4 bytes)
if (offset + 4 > buffer.length)
break;
const messageLength = buffer.readUInt32BE(offset);
offset += 4;
// Read message data
if (offset + messageLength > buffer.length)
break;
const messageData = buffer.subarray(offset, offset + messageLength);
offset += messageLength;
try {
const message = JSON.parse(messageData.toString());
messages.push(message);
}
catch (error) {
console.error("Error parsing message:", error);
}
}
return messages;
}
start() {
return new Promise((resolve, reject) => {
this.server.listen(this.port, (error) => {
if (error) {
reject(error);
}
else {
console.log(`TCP Server listening on port ${this.port}`);
resolve();
}
});
});
}
stop() {
return new Promise((resolve) => {
this.server.close(() => {
console.log("TCP Server stopped");
resolve();
});
});
}
sendToClient(clientId, object, type = MessageType.NOTIFICATION) {
const socket = this.clients.get(clientId);
if (!socket)
return false;
const message = {
id: this.generateId(),
type,
timestamp: Date.now(),
data: object.toJSON(),
};
return this.sendMessage(socket, message);
}
broadcast(object, type = MessageType.NOTIFICATION) {
const message = {
id: this.generateId(),
type,
timestamp: Date.now(),
data: object.toJSON(),
};
this.clients.forEach((socket) => {
this.sendMessage(socket, message);
});
}
sendMessage(socket, message) {
try {
const messageData = Buffer.from(JSON.stringify(message));
const lengthBuffer = Buffer.allocUnsafe(4);
lengthBuffer.writeUInt32BE(messageData.length, 0);
socket.write(Buffer.concat([lengthBuffer, messageData]));
return true;
}
catch (error) {
console.error("Error sending message:", error);
return false;
}
}
generateId() {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
}
getConnectedClients() {
return Array.from(this.clients.keys());
}
}
/**
* TCP Client for ProtoObject communication
*/
export class ProtoObjectTCPClient extends EventEmitter {
/* eslint-disable no-unused-vars */
constructor(host = "localhost", port = 3000) {
super();
this.host = host;
this.port = port;
this.socket = null;
this.messageBuffer = Buffer.alloc(0);
}
/* eslint-enable no-unused-vars */
connect() {
return new Promise((resolve, reject) => {
this.socket = createConnection(this.port, this.host);
this.socket.on("connect", () => {
console.log(`Connected to server ${this.host}:${this.port}`);
this.emit("connected");
resolve();
});
this.socket.on("data", (buffer) => {
this.messageBuffer = Buffer.concat([this.messageBuffer, buffer]);
this.processMessages();
});
this.socket.on("close", () => {
console.log("Disconnected from server");
this.emit("disconnected");
});
this.socket.on("error", (error) => {
this.emit("error", error);
reject(error);
});
});
}
processMessages() {
let offset = 0;
while (offset + 4 <= this.messageBuffer.length) {
const messageLength = this.messageBuffer.readUInt32BE(offset);
if (offset + 4 + messageLength > this.messageBuffer.length) {
break; // Wait for more data
}
const messageData = this.messageBuffer.subarray(offset + 4, offset + 4 + messageLength);
try {
const message = JSON.parse(messageData.toString());
this.emit("message", message);
}
catch (error) {
console.error("Error parsing message:", error);
}
offset += 4 + messageLength;
}
this.messageBuffer = this.messageBuffer.subarray(offset);
}
send(object, type = MessageType.REQUEST) {
if (!this.socket)
return false;
const message = {
id: this.generateId(),
type,
timestamp: Date.now(),
data: object.toJSON(),
};
try {
const messageData = Buffer.from(JSON.stringify(message));
const lengthBuffer = Buffer.allocUnsafe(4);
lengthBuffer.writeUInt32BE(messageData.length, 0);
this.socket.write(Buffer.concat([lengthBuffer, messageData]));
return true;
}
catch (error) {
console.error("Error sending message:", error);
return false;
}
}
disconnect() {
if (this.socket) {
this.socket.end();
this.socket = null;
}
}
generateId() {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
}
}
/**
* Base class for TCP-enabled ProtoObjects
*/
let ProtoObjectTCP = (() => {
let _classDecorators = [StaticImplements()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = ProtoObject;
var ProtoObjectTCP = _classThis = class extends _classSuper {
constructor(data) {
super(data);
}
/**
* Send this object through TCP client
*/
sendTCP(client, type = MessageType.REQUEST) {
return client.send(this, type);
}
/**
* Broadcast this object through TCP server
*/
broadcastTCP(server, type = MessageType.NOTIFICATION) {
server.broadcast(this, type);
}
/**
* Send this object to specific client through TCP server
*/
sendToClientTCP(server, clientId, type = MessageType.RESPONSE) {
return server.sendToClient(clientId, this, type);
}
/**
* Create object from TCP message
*/
static fromTCPMessage(message) {
return this.fromJSON(message.data);
}
};
__setFunctionName(_classThis, "ProtoObjectTCP");
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
ProtoObjectTCP = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
__runInitializers(_classThis, _classExtraInitializers);
})();
return ProtoObjectTCP = _classThis;
})();
export { ProtoObjectTCP };
//# sourceMappingURL=proto-object-tcp.js.map