UNPKG

@litert/televoke

Version:
220 lines 6.46 kB
"use strict"; /** * Copyright 2025 Angus.Fenying <fenying@litert.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createServer = createServer; exports.createConnector = createConnector; const Shared = require("../../shared"); const node_events_1 = require("node:events"); const Crypto = require("node:crypto"); function createRandomName() { return Crypto.randomBytes(8).toString('hex'); } class MemoryExchange extends node_events_1.EventEmitter { constructor(name) { super(); this.name = name; this._aEnded = false; this._bEnded = false; } send(targetEndpoint, data) { setImmediate(() => this.emit('data_' + targetEndpoint, data)); } isEnded(endpoint) { if (endpoint === 'a') { return this._aEnded; } return this._bEnded; } end(endpoint) { if (this._aEnded && this._bEnded) { return; } if (endpoint === 'a') { if (this._aEnded) { return; } this._aEnded = true; setImmediate(() => this.emit('end_a')); } else { if (this._bEnded) { return; } this._bEnded = true; setImmediate(() => this.emit('end_b')); } if (this._aEnded && this._bEnded) { setImmediate(() => this.emit('close')); } } close() { this.end('a'); this.end('b'); } } class MemorySocket extends node_events_1.EventEmitter { constructor(_exchange, _endpoint) { super(); this._exchange = _exchange; this._endpoint = _endpoint; this.protocol = 'memory'; this._targetEndpoint = _endpoint === 'a' ? 'b' : 'a'; this._exchange .on('end_' + this._targetEndpoint, () => { try { this.emit('end'); } catch (e) { this.emit('error', e); } }) .on('data_' + this._endpoint, (data) => { try { this.emit('frame', data); } catch (e) { this.emit('error', e); } }) .on('error', (e) => this.emit('error', e)) .on('close', () => this.emit('close')); } get writable() { return !this._exchange.isEnded(this._endpoint); } getProperty(name) { switch (name) { case 'remoteAddress': return 'localhost'; case 'remotePort': return 0; case 'localAddress': return 'localhost'; case 'localPort': return 0; default: return null; } } getPropertyNames() { return ['remoteAddress', 'remotePort', 'localAddress', 'localPort']; } getAllProperties() { return { 'remoteAddress': 'localhost', 'remotePort': 0, 'localAddress': 'localhost', 'localPort': 0, }; } write(data) { if (this._exchange.isEnded(this._endpoint)) { throw new Shared.errors.network_error({ reason: 'conn_lost' }); } for (let i = 0; i < data.length; ++i) { if (!(data[i] instanceof Buffer)) { data[i] = Buffer.from(data[i]); } } this._exchange.send(this._targetEndpoint, data); } destroy() { this._exchange.close(); } end() { this._exchange.end(this._endpoint); } } class MemoryGateway extends node_events_1.EventEmitter { constructor(name, _server) { super(); this.name = name; this._server = _server; this._running = false; this._exchanges = {}; } get running() { return this._running; } _createExchange() { const ex = new MemoryExchange(this._generateExchangeName()); return this._exchanges[ex.name] = ex; } _generateExchangeName() { let name; do { name = createRandomName(); } while (this._exchanges[name]); return name; } connect() { if (!this._running) { throw new Shared.errors.network_error({ reason: 'conn_refused' }); } const ex = this._createExchange(); const serverSocket = new MemorySocket(ex, 'a'); const clientSocket = new MemorySocket(ex, 'b'); ex.on('close', () => { delete this._exchanges[ex.name]; }); this._server.registerChannel(serverSocket); return clientSocket; } start() { if (!this._running) { this._running = true; this.emit('listening'); } return Promise.resolve(); } stop() { if (!this._running) { return Promise.resolve(); } for (const ex of Object.values(this._exchanges)) { ex.close(); } this.emit('close'); return Promise.resolve(); } } const servers = {}; function createServer(opts, listener) { opts.name ?? (opts.name = createRandomName()); if (servers[opts.name]) { throw new Shared.errors.network_error({ reason: 'dup_listen' }); } const server = new MemoryGateway(opts.name, opts.server); servers[opts.name] = server; if (listener) { server.on('connection', listener); } server.on('close', () => { delete servers[opts.name]; }); return server; } class MemoryConnector { constructor(_name) { this._name = _name; } connect() { if (!servers[this._name]) { throw new Shared.errors.network_error({ reason: 'unknown_dest' }); } return Promise.resolve(servers[this._name].connect()); } } function createConnector(name) { return new MemoryConnector(name); } //# sourceMappingURL=Memory.Impl.js.map