Consumer-invisible modernization pass — no public API or runtime-behavior
change; dist for the override-only files is byte-identical.
Toolchain:
- CI: GitHub Actions matrix — Node 22/24 (test:unit) + Bun latest (test:bun).
- engines: node ">=22" (was "22.x"), bun ">=1.1.0".
- tsconfig: isolatedModules + noImplicitOverride; add the 33 `override`
modifiers the flag requires across storage/integrations/vfs/transaction.
- deps: @types/node ^22; add prettier; drop dead standard-version,
@rollup/plugin-* and the redundant embedded eslintConfig (flat
eslint.config.js is the active config — verified identical lint output).
paramValidation: replace the top-level `await import('node:os'/'node:fs')`
with static ESM imports. The top-level-await form poisoned the module graph;
static imports also drop the browser/edge fallback branches no supported
runtime reaches (8.0 is Node/Bun/Deno-only).
Bun positioning: recommend Bun as a runtime (`bun add` / `bun run`), which is
green (test:bun 8/8). Drop single-binary `bun build --compile` as a target —
native addons cannot embed into it, and Bun 1.3.10 has a `--compile` codegen
regression around top-level await. Rename the Bun test to bun-runtime-test.ts
and correct docs that overclaimed single-binary support.
Gates: typecheck 0, build 0, test:unit 1743/1743, test:bun 8/8.
87 lines
No EOL
2 KiB
TypeScript
87 lines
No EOL
2 KiB
TypeScript
/**
|
|
* VFS Write Stream Implementation
|
|
*
|
|
* Real streaming write support for large files
|
|
*/
|
|
|
|
import { Writable } from 'stream'
|
|
import { VirtualFileSystem } from '../VirtualFileSystem.js'
|
|
import { WriteStreamOptions } from '../types.js'
|
|
|
|
export class VFSWriteStream extends Writable {
|
|
private chunks: Buffer[] = []
|
|
private size = 0
|
|
private _closed = false
|
|
|
|
constructor(
|
|
private vfs: VirtualFileSystem,
|
|
private path: string,
|
|
private options: WriteStreamOptions = {}
|
|
) {
|
|
super({
|
|
highWaterMark: 64 * 1024 // 64KB chunks
|
|
})
|
|
|
|
// Handle autoClose option
|
|
if (options.autoClose !== false) {
|
|
this.once('finish', () => this._flush())
|
|
}
|
|
}
|
|
|
|
override async _write(
|
|
chunk: any,
|
|
encoding: BufferEncoding,
|
|
callback: (error?: Error | null) => void
|
|
): Promise<void> {
|
|
try {
|
|
// Convert to buffer if needed
|
|
const buffer = Buffer.isBuffer(chunk)
|
|
? chunk
|
|
: Buffer.from(chunk, encoding)
|
|
|
|
// Store chunk
|
|
this.chunks.push(buffer)
|
|
this.size += buffer.length
|
|
|
|
// For very large files, we could flush periodically
|
|
// to avoid memory issues, but for now we accumulate
|
|
|
|
callback()
|
|
} catch (error: any) {
|
|
callback(error)
|
|
}
|
|
}
|
|
|
|
override async _final(callback: (error?: Error | null) => void): Promise<void> {
|
|
try {
|
|
await this._flush()
|
|
callback()
|
|
} catch (error: any) {
|
|
callback(error)
|
|
}
|
|
}
|
|
|
|
private async _flush(): Promise<void> {
|
|
if (this._closed) return
|
|
this._closed = true
|
|
|
|
// Combine all chunks
|
|
const data = Buffer.concat(this.chunks, this.size)
|
|
|
|
// Write to VFS
|
|
await this.vfs.writeFile(this.path, data, {
|
|
mode: this.options.mode,
|
|
encoding: this.options.encoding
|
|
})
|
|
|
|
// Clear chunks to free memory
|
|
this.chunks = []
|
|
}
|
|
|
|
override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {
|
|
// Clean up resources
|
|
this.chunks = []
|
|
this._closed = true
|
|
callback(error)
|
|
}
|
|
} |