@devgrid/netron
Version:
A powerful TypeScript library for building distributed systems with event bus, streaming capabilities, and remote object invocation. Features WebSocket-based bidirectional communication between Node.js and browser environments, service discovery, and type
109 lines • 4.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetronReadableStream = void 0;
const stream_1 = require("stream");
const MAX_BUFFER_SIZE = 10_000;
class NetronReadableStream extends stream_1.Readable {
constructor({ peer, streamId, isLive = false, ...opts }) {
super({ ...opts, objectMode: true });
this.buffer = new Map();
this.expectedIndex = 0;
this.isClosed = false;
this.isComplete = false;
this.cleanup = () => {
this.peer.logger.debug('Cleaning up stream resources', { streamId: this.id });
if (this.timeout)
clearTimeout(this.timeout);
this.peer.readableStreams.delete(this.id);
this.buffer.clear();
};
this.handleError = (error) => {
this.peer.logger.error('Stream error occurred', { streamId: this.id, error });
this.cleanup();
};
this.peer = peer;
this.id = streamId;
this.isLive = isLive;
this.peer.logger.info('Creating readable stream', { streamId: this.id, isLive });
this.peer.readableStreams.set(this.id, this);
if (!this.isLive) {
this.resetTimeout();
}
this.on('close', this.cleanup);
this.on('error', this.handleError);
}
onPacket(packet) {
if (this.isClosed) {
this.peer.logger.warn('Received packet for closed stream', { streamId: this.id });
return;
}
this.resetTimeout();
if (this.buffer.size > MAX_BUFFER_SIZE) {
this.peer.logger.error('Stream buffer overflow', { streamId: this.id, size: this.buffer.size });
this.destroy(new Error(`Buffer overflow: more than ${MAX_BUFFER_SIZE} packets buffered`));
return;
}
this.peer.logger.debug('Processing packet', { streamId: this.id, index: packet.streamIndex });
this.buffer.set(packet.streamIndex, packet.data);
while (this.buffer.has(this.expectedIndex)) {
const chunk = this.buffer.get(this.expectedIndex);
this.buffer.delete(this.expectedIndex);
this.expectedIndex++;
if (!this.push(chunk)) {
this.peer.logger.debug('Stream backpressure detected', { streamId: this.id });
break;
}
}
if (packet.isLastChunk()) {
this.peer.logger.info('Received last chunk', { streamId: this.id });
this.isComplete = true;
this.closeStream(true);
}
}
_read() {
}
resetTimeout() {
if (this.isLive)
return;
if (this.timeout)
clearTimeout(this.timeout);
const timeoutDuration = this.peer.netron.options?.streamTimeout ?? 60000;
this.peer.logger.debug('Resetting stream timeout', { streamId: this.id, timeoutDuration });
this.timeout = setTimeout(() => {
const message = `Stream ${this.id} inactive for ${timeoutDuration}ms, closing.`;
this.peer.logger.warn(message);
this.destroy(new Error(message));
}, timeoutDuration);
}
closeStream(force = false) {
if (this.isClosed) {
this.peer.logger.warn('Attempt to close already closed stream', { streamId: this.id });
return;
}
if (this.isLive && !force) {
this.peer.logger.warn('Attempt to close live stream', { streamId: this.id });
return;
}
this.peer.logger.info('Closing stream', { streamId: this.id, force });
this.push(null);
if (this.isLive && force) {
this.destroy();
}
}
destroy(error) {
if (this.isClosed) {
this.peer.logger.warn('Attempt to destroy already closed stream', { streamId: this.id });
return this;
}
this.peer.logger.info('Destroying stream', { streamId: this.id, error });
this.isClosed = true;
super.destroy(error);
this.cleanup();
return this;
}
static create(peer, streamId, isLive = false) {
return new NetronReadableStream({ peer, streamId, isLive });
}
}
exports.NetronReadableStream = NetronReadableStream;
//# sourceMappingURL=readable-stream.js.map