@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
118 lines • 4.88 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetronWritableStream = void 0;
const stream_1 = require("stream");
const uid_1 = require("./uid");
const packet_1 = require("./packet");
const uid = new uid_1.Uid();
class NetronWritableStream extends stream_1.Writable {
constructor({ peer, streamId, isLive = false, ...opts }) {
super({ ...opts, objectMode: true });
this.index = 0;
this.isClosed = false;
this.cleanup = () => {
this.peer.logger.debug('Cleaning up stream resources', { streamId: this.id });
this.peer.writableStreams.delete(this.id);
};
this.handleError = (err) => {
this.peer.logger.error('Stream error occurred', { streamId: this.id, error: err });
this.cleanup();
};
this.peer = peer;
this.isLive = isLive;
this.id = streamId ?? uid.next();
this.peer.logger.info('Creating writable stream', { streamId: this.id, isLive });
this.peer.writableStreams.set(this.id, this);
this.once('close', this.cleanup);
this.once('error', this.handleError);
}
async pipeFrom(source) {
this.peer.logger.debug('Starting pipe operation', { streamId: this.id });
try {
for await (const chunk of source) {
if (!this.write(chunk)) {
this.peer.logger.debug('Stream backpressure detected', { streamId: this.id });
await new Promise((resolve) => this.once('drain', resolve));
}
}
this.end();
this.peer.logger.debug('Pipe operation completed', { streamId: this.id });
}
catch (error) {
this.peer.logger.error('Pipe operation failed', { streamId: this.id, error });
this.destroy(error instanceof Error ? error : new Error(String(error)));
}
}
_write(chunk, _, callback) {
if (this.isClosed) {
this.peer.logger.warn('Attempt to write to closed stream', { streamId: this.id });
callback(new Error('Stream is already closed'));
return;
}
this.peer.logger.debug('Writing chunk', { streamId: this.id, index: this.index });
this.peer.sendStreamChunk(this.id, chunk, this.index++, false, this.isLive)
.then(() => callback())
.catch((err) => {
this.peer.logger.error('Error sending stream chunk', { streamId: this.id, error: err });
this.peer.sendPacket((0, packet_1.createPacket)(packet_1.Packet.nextId(), 1, packet_1.TYPE_STREAM_ERROR, {
streamId: this.id,
message: err.message,
})).finally(() => {
callback(err);
this.destroy(err);
});
});
}
_final(callback) {
if (this.isClosed) {
this.peer.logger.warn('Attempt to finalize closed stream', { streamId: this.id });
callback(new Error('Stream is already closed'));
return;
}
this.peer.logger.debug('Sending final chunk', { streamId: this.id, index: this.index });
this.peer.sendStreamChunk(this.id, null, this.index, true, this.isLive)
.then(() => callback())
.catch((err) => {
this.peer.logger.error('Error sending final chunk', { streamId: this.id, error: err });
callback(err);
this.destroy(err);
})
.finally(() => this.closeStream());
}
closeStream() {
if (this.isClosed) {
this.peer.logger.warn('Attempt to close already closed stream', { streamId: this.id });
return;
}
this.peer.logger.info('Closing stream', { streamId: this.id });
this.isClosed = true;
this.end();
this.cleanup();
}
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;
this.peer.sendStreamChunk(this.id, null, this.index, true, this.isLive)
.catch((sendError) => {
this.peer.logger.error('Failed to send final stream chunk', { streamId: this.id, error: sendError });
})
.finally(() => {
super.destroy(error);
this.cleanup();
});
return this;
}
static create(peer, source, isLive = false, streamId) {
const stream = new NetronWritableStream({ peer, streamId, isLive });
if (source) {
stream.pipeFrom(source);
}
return stream;
}
}
exports.NetronWritableStream = NetronWritableStream;
//# sourceMappingURL=writable-stream.js.map