--- a/src/main/ssh/ssh-filesystem-stream-reader.ts +++ b/src/main/ssh/ssh-filesystem-stream-reader.ts @@ -77,7 +77 @@ - // Why: chunk/end/error frames may arrive in the same dispatch tick as the - // metadata response. Queue them until streamIdRef is set, then drain. - type PendingFrame = - | { kind: 'chunk'; params: Record } - | { kind: 'end'; params: Record } - | { kind: 'error'; params: Record } - const pending: PendingFrame[] = [] + // Install metadata during response dispatch, before adjacent stream frames. @@ -232,13 +225,0 @@ - const drainPending = (): void => { - while (!settled && pending.length > 0) { - const frame = pending.shift()! - if (frame.kind === 'chunk') { - handleChunk(frame.params) - } else if (frame.kind === 'end') { - handleEnd(frame.params) - } else { - handleStreamError(frame.params) - } - } - } - @@ -248 +228,0 @@ - pending.push({ kind: 'chunk', params }) @@ -257 +236,0 @@ - pending.push({ kind: 'end', params }) @@ -266 +244,0 @@ - pending.push({ kind: 'error', params }) @@ -287,51 +265,55 @@ - .request('fs.readFileStream', { filePath, flowControl: 'ack' }) - .then((rawMetadata) => { - if (settled) { - return - } - const metadata = rawMetadata as StreamMetadataResponse - isBinary = metadata.isBinary - isImage = metadata.isImage - mimeType = metadata.mimeType - resultEncoding = metadata.resultEncoding ?? RESULT_ENCODING_BASE64 - - if (metadata.empty) { - succeed({ - content: '', - isBinary: metadata.isBinary, - ...(metadata.isImage !== undefined ? { isImage: metadata.isImage } : {}), - ...(metadata.mimeType !== undefined ? { mimeType: metadata.mimeType } : {}) - }) - return - } - - if (typeof metadata.streamId !== 'number') { - fail(new StreamProtocolError('Metadata missing streamId for non-empty stream')) - return - } - - const cap = sshFileStreamReadCap(metadata.isBinary, limits) - if (metadata.totalSize < 0 || metadata.totalSize > cap) { - streamIdRef.current = metadata.streamId - fail( - new FileReadCapExceededError( - `Reported totalSize ${metadata.totalSize} exceeds client cap ${cap}` - ) - ) - return - } - - totalSize = metadata.totalSize - totalChunks = totalSize === 0 ? 0 : Math.ceil(totalSize / STREAM_CHUNK_SIZE) - try { - buffer = Buffer.alloc(totalSize) - } catch (err) { - streamIdRef.current = metadata.streamId - fail(new Error(`Failed to allocate ${totalSize} bytes: ${(err as Error).message}`)) - return - } - streamIdRef.current = metadata.streamId - metadataReady = true - inactivity.reset() - drainPending() - }) + .request( + 'fs.readFileStream', + { filePath, flowControl: 'ack' }, + { + beforeResolve: (rawMetadata) => { + if (settled) { + return + } + const metadata = rawMetadata as StreamMetadataResponse + isBinary = metadata.isBinary + isImage = metadata.isImage + mimeType = metadata.mimeType + resultEncoding = metadata.resultEncoding ?? RESULT_ENCODING_BASE64 + + if (metadata.empty) { + succeed({ + content: '', + isBinary: metadata.isBinary, + ...(metadata.isImage !== undefined ? { isImage: metadata.isImage } : {}), + ...(metadata.mimeType !== undefined ? { mimeType: metadata.mimeType } : {}) + }) + return + } + + if (typeof metadata.streamId !== 'number') { + fail(new StreamProtocolError('Metadata missing streamId for non-empty stream')) + return + } + + const cap = sshFileStreamReadCap(metadata.isBinary, limits) + if (metadata.totalSize < 0 || metadata.totalSize > cap) { + streamIdRef.current = metadata.streamId + fail( + new FileReadCapExceededError( + `Reported totalSize ${metadata.totalSize} exceeds client cap ${cap}` + ) + ) + return + } + + totalSize = metadata.totalSize + totalChunks = totalSize === 0 ? 0 : Math.ceil(totalSize / STREAM_CHUNK_SIZE) + try { + buffer = Buffer.alloc(totalSize) + } catch (err) { + streamIdRef.current = metadata.streamId + fail(new Error(`Failed to allocate ${totalSize} bytes: ${(err as Error).message}`)) + return + } + streamIdRef.current = metadata.streamId + metadataReady = true + inactivity.reset() + } + } + )