use single fd for all operations

This commit is contained in:
2026-02-15 19:57:19 -08:00
parent a1838bc153
commit 1bd153de5b
+27 -12
View File
@@ -1,5 +1,4 @@
import * as fs from 'node:fs' import * as fs from 'node:fs'
import * as asyncfs from 'node:fs/promises'
import * as path from 'node:path' import * as path from 'node:path'
import { Writable } from 'node:stream' import { Writable } from 'node:stream'
import type { import type {
@@ -11,35 +10,51 @@ import type {
export default class NodeFileSystemWritableFileStream extends WritableStream implements FileSystemWritableFileStream { export default class NodeFileSystemWritableFileStream extends WritableStream implements FileSystemWritableFileStream {
#seek_position = 0 #seek_position = 0
#name: string #fd: number
constructor(name: string, options: FileSystemCreateWritableOptions={}) { constructor(name: string, options: FileSystemCreateWritableOptions = {}) {
name = path.normalize(name) name = path.normalize(name)
super(Writable.toWeb(fs.createWriteStream(name))) let flags = 'w'
this.#name = name if (!options.keepExistingData) flags = 'r+'
if(!options.keepExistingData) this.truncate(0) const fd = fs.openSync(name, flags)
super(Writable.toWeb(fs.createWriteStream(name, { "fd": fd })))
this.#fd = fd
} }
async seek(position: number): Promise<void> { async seek(position: number): Promise<void> {
if (this.locked) throw new Error("stream is locked")
this.#seek_position = position this.#seek_position = position
} }
truncate(size: number): Promise<void> { async truncate(size: number): Promise<void> {
return asyncfs.truncate(this.#name, size) if (this.locked) throw new Error("stream is locked")
return fs.ftruncateSync(this.#fd, size)
} }
async write(wdata: FileSystemWriteChunkType): Promise<void> { async write(wdata: FileSystemWriteChunkType): Promise<void> {
if (this.locked) throw new Error("stream is locked")
if (wdata instanceof Blob) wdata = await wdata.bytes() if (wdata instanceof Blob) wdata = await wdata.bytes()
if (typeof wdata === 'string') wdata = new TextEncoder().encode(wdata) if (typeof wdata === 'string') wdata = new TextEncoder().encode(wdata)
let wsize: number = null let wsize: number = null
let pseek_position = this.#seek_position let pseek_position = this.#seek_position
if ((wdata as WriteParams).type) { if ((wdata as WriteParams).type) {
const { type, position, size, data } = (wdata as WriteParams) const { type, position, size, data } = (wdata as WriteParams)
if (type === 'truncate') return asyncfs.truncate(this.#name, size) if (type === 'truncate') return this.truncate(size)
if (position) this.#seek_position = position if (position) this.#seek_position = position
if (type === 'seek') return if (type === 'seek') return
if (size) wsize = size if (size) wsize = size
wdata = data wdata = data
} }
const file = await asyncfs.open(this.#name,'r+') fs.writeSync(this.#fd, (wdata as Uint8Array), null, wsize, this.#seek_position)
await file.write((wdata as Uint8Array), null, wsize, this.#seek_position)
await file.close()
this.#seek_position = pseek_position this.#seek_position = pseek_position
} }
async #_close() {
try {
fs.closeSync(this.#fd)
} catch { }
}
async close(): Promise<void> {
await super.close()
await this.#_close()
}
async abort(reason?: any): Promise<void> {
await super.abort(reason)
await this.#_close()
}
} }