UNPKG

@cliz/inlets

Version:
178 lines (177 loc) 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StreamManager = exports.Stream = exports.StreamState = void 0; var StreamState; (function (StreamState) { StreamState["INITIALIZING"] = "initializing"; StreamState["ACTIVE"] = "active"; StreamState["PAUSED"] = "paused"; StreamState["COMPLETED"] = "completed"; StreamState["ERROR"] = "error"; })(StreamState = exports.StreamState || (exports.StreamState = {})); class Stream { constructor(id) { this.state = StreamState.INITIALIZING; this.chunks = new Map(); this.expectedSequence = 0; this.totalChunks = -1; this.id = id; } addChunk(sequence, data, isLast = false) { if (this.state === StreamState.COMPLETED || this.state === StreamState.ERROR) { return; } this.chunks.set(sequence, { sequence, data, isLast }); if (isLast) { this.totalChunks = sequence + 1; } this.tryReassemble(); } tryReassemble() { while (this.chunks.has(this.expectedSequence)) { const chunk = this.chunks.get(this.expectedSequence); this.chunks.delete(this.expectedSequence); if (!this._completedData) { this._completedData = chunk.data; } else { const previous = this._completedData; const mergedArray = new Uint8Array(previous.length + chunk.data.length); mergedArray.set(previous, 0); mergedArray.set(chunk.data, previous.length); this._completedData = Buffer.from(mergedArray); } this.completedData = this._completedData; this.expectedSequence++; if (chunk.isLast) { this.state = StreamState.COMPLETED; if (this.onComplete && this._completedData) { this.onComplete(this._completedData); } return; } if (this.totalChunks > 0 && this.expectedSequence >= this.totalChunks) { this.state = StreamState.COMPLETED; if (this.onComplete && this._completedData) { this.onComplete(this._completedData); } return; } } } markComplete() { if (this.state === StreamState.COMPLETED || this.state === StreamState.ERROR) { return; } this.state = StreamState.COMPLETED; if (this.onComplete && this._completedData) { this.onComplete(this._completedData); } } markError(error) { this.state = StreamState.ERROR; if (this.onError) { this.onError(error); } } pause() { if (this.state === StreamState.ACTIVE) { this.state = StreamState.PAUSED; } } resume() { if (this.state === StreamState.PAUSED) { this.state = StreamState.ACTIVE; this.tryReassemble(); } } getPartialData() { return this._completedData || null; } destroy() { this.chunks.clear(); this._completedData = undefined; this.completedData = undefined; this.onComplete = undefined; this.onError = undefined; } } exports.Stream = Stream; class StreamManager { constructor(defaultChunkSize) { this.streams = new Map(); this.defaultChunkSize = 64 * 1024; this.maxStreamAge = 5 * 60 * 1000; this.cleanupInterval = null; if (defaultChunkSize) { this.defaultChunkSize = defaultChunkSize; } this.startCleanupTimer(); } splitIntoChunks(data, chunkSize) { const size = chunkSize || this.defaultChunkSize; const chunks = []; for (let i = 0; i < data.length; i += size) { const chunk = data.slice(i, Math.min(i + size, data.length)); chunks.push(chunk); } return chunks; } createStream(streamId, onComplete, onError) { const stream = new Stream(streamId); stream.state = StreamState.ACTIVE; stream.onComplete = onComplete; stream.onError = onError; this.streams.set(streamId, stream); return stream; } getStream(streamId) { return this.streams.get(streamId); } addChunk(streamId, sequence, data, isLast = false) { const stream = this.getStream(streamId); if (!stream) { const newStream = this.createStream(streamId); newStream.addChunk(sequence, data, isLast); } else { stream.addChunk(sequence, data, isLast); } } removeStream(streamId) { const stream = this.streams.get(streamId); if (stream) { stream.destroy(); this.streams.delete(streamId); } } startCleanupTimer() { this.cleanupInterval = setInterval(() => { const now = Date.now(); const streamsToRemove = []; for (const [streamId, stream] of this.streams.entries()) { if ((stream.state === StreamState.COMPLETED || stream.state === StreamState.ERROR) && stream.completedData) { streamsToRemove.push(streamId); } } for (const streamId of streamsToRemove) { this.removeStream(streamId); } }, 60 * 1000); } stopCleanupTimer() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } } destroy() { this.stopCleanupTimer(); for (const stream of this.streams.values()) { stream.destroy(); } this.streams.clear(); } } exports.StreamManager = StreamManager;