fix(fs): Improve fs and stream handling, enhance SmartFile/StreamFile, update tests and CI configs

This commit is contained in:
2025-08-18 00:13:03 +00:00
parent 9b0d89b9ef
commit cd147ca38e
25 changed files with 3003 additions and 1221 deletions

View File

@@ -3,7 +3,7 @@ import * as smartfileFs from './fs.js';
import * as smartfileFsStream from './fsstream.js';
import { Readable } from 'stream';
type TStreamSource = (streamFile: StreamFile) => Promise<Readable>;
type TStreamSource = (streamFile: StreamFile) => Promise<Readable | ReadableStream>;
/**
* The StreamFile class represents a file as a stream.
@@ -13,28 +13,41 @@ export class StreamFile {
// STATIC
public static async fromPath(filePath: string): Promise<StreamFile> {
const streamSource: TStreamSource = async (streamFileArg) => smartfileFsStream.createReadStream(filePath);
const streamSource: TStreamSource = async (streamFileArg) =>
smartfileFsStream.createReadStream(filePath);
const streamFile = new StreamFile(streamSource, filePath);
streamFile.multiUse = true;
streamFile.byteLengthComputeFunction = async () => {
const stats = await smartfileFs.stat(filePath);
return stats.size;
}
};
return streamFile;
}
public static async fromUrl(url: string): Promise<StreamFile> {
const streamSource: TStreamSource = async (streamFileArg) => plugins.smartrequest.getStream(url); // Replace with actual plugin method
const streamSource: TStreamSource = async (streamFileArg) => {
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.get();
return response.stream();
};
const streamFile = new StreamFile(streamSource);
streamFile.multiUse = true;
streamFile.byteLengthComputeFunction = async () => {
const response = await plugins.smartrequest.getBinary(url); // TODO: switch to future .getBinaryByteLength()
return response.body.length;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.accept('binary')
.get();
const buffer = Buffer.from(await response.arrayBuffer());
return buffer.length;
};
return streamFile;
}
public static fromBuffer(buffer: Buffer, relativeFilePath?: string): StreamFile {
public static fromBuffer(
buffer: Buffer,
relativeFilePath?: string,
): StreamFile {
const streamSource: TStreamSource = async (streamFileArg) => {
const stream = new Readable();
stream.push(buffer);
@@ -54,7 +67,11 @@ export class StreamFile {
* @param multiUse If true, the stream can be read multiple times, caching its content.
* @returns A StreamFile instance.
*/
public static fromStream(stream: Readable, relativeFilePath?: string, multiUse: boolean = false): StreamFile {
public static fromStream(
stream: Readable,
relativeFilePath?: string,
multiUse: boolean = false,
): StreamFile {
const streamSource: TStreamSource = (streamFileArg) => {
if (streamFileArg.multiUse) {
// If multi-use is enabled and we have cached content, create a new readable stream from the buffer
@@ -86,7 +103,6 @@ export class StreamFile {
return streamFile;
}
// INSTANCE
relativeFilePath?: string;
private streamSource: TStreamSource;
@@ -115,7 +131,16 @@ export class StreamFile {
* Creates a new readable stream from the source.
*/
public async createReadStream(): Promise<Readable> {
return this.streamSource(this);
const stream = await this.streamSource(this);
// Check if it's a Web ReadableStream and convert to Node.js Readable
if (stream && typeof (stream as any).getReader === 'function') {
// This is a Web ReadableStream, convert it to Node.js Readable
return Readable.fromWeb(stream as any);
}
// It's already a Node.js Readable stream
return stream as Readable;
}
/**
@@ -171,4 +196,4 @@ export class StreamFile {
return null;
}
}
}
}