UNPKG

simpleddp-node

Version:

The aim of this library is to simplify the process of working with meteor server over DDP protocol using external JS environments

97 lines (96 loc) 3.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __importDefault(require("events")); const ejson_1 = __importDefault(require("ejson")); const utils_1 = require("./utils"); class Socket extends events_1.default { endpoint; SocketConstructor; rawSocket; constructor(SocketConstructor, endpoint) { super(); this.SocketConstructor = SocketConstructor; this.endpoint = endpoint; this.rawSocket = null; this.setMaxListeners(100); } send(object) { const message = ejson_1.default.stringify(object); this.rawSocket.send(message); // Emit a copy of the object, as the listener might mutate it. this.emit('message:out', ejson_1.default.parse(message)); } open() { /* * Makes `open` a no-op if there's already a `rawSocket`. This avoids * memory / socket leaks if `open` is called twice (e.g. by a user * calling `ddp.connect` twice) without properly disposing of the * socket connection. `rawSocket` gets automatically set to `null` only * when it goes into a closed or error state. This way `rawSocket` is * disposed of correctly: the socket connection is closed, and the * object can be garbage collected. */ if (this.rawSocket) { return; } this.rawSocket = new this.SocketConstructor(this.endpoint); /* * Calls to `onopen` and `onclose` directly trigger the `open` and * `close` events on the `Socket` instance. */ this.rawSocket.onopen = () => this.emit('open'); this.rawSocket.onclose = () => { this.rawSocket = null; this.emit('close'); }; /* * Calls to `onerror` trigger the `close` event on the `Socket` * instance, and cause the `rawSocket` object to be disposed of. * Since it's not clear what conditions could cause the error and if * it's possible to recover from it, we prefer to always close the * connection (if it isn't already) and dispose of the socket object. */ this.rawSocket.onerror = () => { // It's not clear what the socket lifecycle is when errors occurr. // Hence, to avoid the `close` event to be emitted twice, before // manually closing the socket we de-register the `onclose` // callback. if (this.rawSocket && this.rawSocket.onclose) { // @ts-ignore delete this.rawSocket.onclose; } // Safe to perform even if the socket is already closed this.rawSocket.close(); this.rawSocket = null; this.emit('close'); }; /* * Calls to `onmessage` trigger a `message:in` event on the `Socket` * instance only once the message (first parameter to `onmessage`) has * been successfully parsed into a javascript object. */ this.rawSocket.onmessage = (message) => { const [object, error] = (0, utils_1.run)(() => ejson_1.default.parse(message.data)); if (error) { // Simply ignore the malformed message and return return; } // Outside the try-catch block as it must only catch EJSON parsing // errors, not errors that may occur inside a "message:in" event // handler this.emit('message:in', object); }; } close() { /* * Avoid throwing an error if `rawSocket === null` */ if (this.rawSocket) { this.rawSocket.close(); } } } exports.default = Socket;