initial release
Release / release (push) Successful in 23s

This commit is contained in:
2025-12-30 11:56:21 -08:00
parent 82cacf484c
commit f2783b2587
12 changed files with 316 additions and 104 deletions
-35
View File
@@ -1,35 +0,0 @@
import * as fs from "node:fs";
import * as fsp from "node:fs/promises";
import * as path from "node:path";
import mime from 'mime'
export default class NodeFile implements File {
lastModified: number;
name: string;
webkitRelativePath: string;
size: number;
type: string;
constructor(name: string) {
this.name = path.normalize(name)
const stats = fs.lstatSync(name)
this.lastModified = stats.mtimeMs
this.size = stats.size
this.type = mime.getType(name.split('.').pop())
}
arrayBuffer(): Promise<ArrayBuffer> {
throw new Error("Method not implemented.");
}
bytes(): Promise<Uint8Array<ArrayBuffer>> {
return fsp.readFile(this.name)
}
slice(start?: number, end?: number, contentType?: string): Blob {
throw new Error("Method not implemented.");
}
stream(): ReadableStream<Uint8Array<ArrayBuffer>> {
throw new Error("Method not implemented.");
}
text(): Promise<string> {
return fsp.readFile(this.name,'utf8')
}
}
+33 -29
View File
@@ -1,63 +1,67 @@
import * as fs from 'node:fs';
import * as fsp from 'node:fs/promises';
import * as asyncfs from 'node:fs/promises';
import * as path from 'node:path';
import NodeFileSystemHandle from './NodeFileSystemHandle';
import NodeFileSystemFileHandle from './NodeFileSystemFileHandle';
import NodeFileSystemHandle from './NodeFileSystemHandle.js';
import NodeFileSystemFileHandle from './NodeFileSystemFileHandle.js';
export default class NodeFileSystemDirectoryHandle extends NodeFileSystemHandle implements FileSystemDirectoryHandle {
declare kind: "directory";
declare name: string;
constructor(name: string, options?: FileSystemGetDirectoryOptions) {
constructor(fpath: string, options: FileSystemGetDirectoryOptions = {}) {
super()
name=path.normalize(name)
if (!fs.existsSync(name) && options.create) fs.mkdirSync(name)
if (!fs.lstatSync(name).isDirectory()) throw new Error('not a directory')
this.name=name
fpath = path.normalize(fpath)
if (!fs.existsSync(fpath) && options.create) fs.mkdirSync(fpath)
if (!fs.lstatSync(fpath).isDirectory()) throw new Error('not a directory')
this.path = fpath
}
entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemDirectoryHandle | FileSystemFileHandle]> {
const entries: Array<[string, FileSystemDirectoryHandle | FileSystemFileHandle]>=[]
fs.readdirSync(this.name).forEach(name => {
entries(): FileSystemDirectoryHandleAsyncIterator<[string, NodeFileSystemDirectoryHandle | NodeFileSystemFileHandle]> {
const entries: Array<[string, NodeFileSystemDirectoryHandle | NodeFileSystemFileHandle]> = []
fs.readdirSync(this.path).forEach(name => {
if (fs.lstatSync(name).isDirectory()) {
entries.push([name,new NodeFileSystemDirectoryHandle(path.join(this.name, name))])
entries.push([name, new NodeFileSystemDirectoryHandle(path.join(this.path, name))])
} else {
entries.push([name, new NodeFileSystemFileHandle(path.join(this.name, name))])
entries.push([name, new NodeFileSystemFileHandle(path.join(this.path, name))])
}
})
return entries[Symbol.asyncIterator]
}
keys(): FileSystemDirectoryHandleAsyncIterator<string> {
return fs.readdirSync(this.name)[Symbol.asyncIterator]
return fs.readdirSync(this.path)[Symbol.asyncIterator]
}
values(): FileSystemDirectoryHandleAsyncIterator<FileSystemDirectoryHandle | FileSystemFileHandle> {
const values: Array<FileSystemDirectoryHandle | FileSystemFileHandle> = [];
fs.readdirSync(this.name).forEach(name => {
values(): FileSystemDirectoryHandleAsyncIterator<NodeFileSystemDirectoryHandle | NodeFileSystemFileHandle> {
const values: Array<NodeFileSystemDirectoryHandle | NodeFileSystemFileHandle> = [];
fs.readdirSync(this.path).forEach(name => {
if (fs.lstatSync(name).isDirectory()) {
values.push(new NodeFileSystemDirectoryHandle(path.join(this.name,name)))
values.push(new NodeFileSystemDirectoryHandle(path.join(this.path, name)))
} else {
values.push(new NodeFileSystemFileHandle(path.join(this.name, name)))
values.push(new NodeFileSystemFileHandle(path.join(this.path, name)))
}
})
return values[Symbol.asyncIterator]
}
async getDirectoryHandle(name: string, options?: FileSystemGetDirectoryOptions): Promise<FileSystemDirectoryHandle> {
return new NodeFileSystemDirectoryHandle(path.join(this.name, name),options)
#getValidatedPath(name: string) {
name = path.normalize(name)
if (name.includes(path.sep)) throw new TypeError('Invalid Filename')
return path.join(this.path, name)
}
async getFileHandle(name: string, options?: FileSystemGetFileOptions): Promise<FileSystemFileHandle> {
return new NodeFileSystemFileHandle(path.join(this.name,name),options)
async getDirectoryHandle(name: string, options: FileSystemGetDirectoryOptions = {}): Promise<NodeFileSystemDirectoryHandle> {
return new NodeFileSystemDirectoryHandle(this.#getValidatedPath(name), options)
}
removeEntry(name: string, options?: FileSystemRemoveOptions): Promise<void> {
return fsp.rm(name,{'recursive':options.recursive,"force":options.recursive})
async getFileHandle(name: string, options: FileSystemGetFileOptions = {}): Promise<NodeFileSystemFileHandle> {
return new NodeFileSystemFileHandle(this.#getValidatedPath(name), options)
}
async removeEntry(name: string, options: FileSystemRemoveOptions = { recursive: false }): Promise<void> {
return asyncfs.rm(this.#getValidatedPath(name), { recursive: options.recursive, force: options.recursive })
}
async resolve(possibleDescendant: FileSystemHandle): Promise<string[] | null> {
if (typeof possibleDescendant !== typeof this) return null
let thisName = this.name
let thisName = this.path
let possibleDescendantName = possibleDescendant.name
if (!path.isAbsolute(this.name)) thisName = path.join(process.cwd(), this.name)
if (!path.isAbsolute(this.path)) thisName = path.join(process.cwd(), this.path)
if (!path.isAbsolute(possibleDescendant.name)) possibleDescendantName = path.join(process.cwd(), possibleDescendant.name)
if (!possibleDescendantName.startsWith(thisName)) return null
possibleDescendantName = possibleDescendantName.substring(thisName.length - 1)
if (possibleDescendantName[0] == path.sep) possibleDescendantName = possibleDescendantName.substring(1)
return possibleDescendantName.split(path.sep)
}
[Symbol.asyncIterator]=this.entries
[Symbol.asyncIterator] = this.entries
}
+16 -14
View File
@@ -1,25 +1,27 @@
import * as fs from 'node:fs';
import * as fsp from 'node:fs/promises';
import * as asyncfs from 'node:fs/promises';
import * as path from 'node:path';
import NodeFileSystemHandle from './NodeFileSystemHandle';
import NodeFileSystemWriteableFileStream from './NodeFileSystemWriteableFileStream';
import NodeFile from './NodeFile';
import NodeFileSystemHandle from './NodeFileSystemHandle.js';
import NodeFileSystemWritableFileStream from './NodeFileSystemWriteableFileStream.js';
export default class NodeFileSystemFileHandle extends NodeFileSystemHandle implements FileSystemFileHandle {
declare kind: "file";
declare name: string;
constructor(name: string, options?: FileSystemGetFileOptions) {
constructor(fpath: string, options: FileSystemGetFileOptions={}) {
super()
name=path.normalize(name)
if (!fs.existsSync(name) && options.create) fs.writeFileSync(name,'')
if (fs.lstatSync(name).isDirectory()) new Error('not a file')
this.name=name
fpath = path.normalize(fpath)
if (!fs.existsSync(fpath) && options.create) fs.writeFileSync(fpath, '')
if (fs.lstatSync(fpath).isDirectory()) new Error('not a file')
this.path = fpath
}
async createWritable(options?: FileSystemCreateWritableOptions): Promise<FileSystemWritableFileStream> {
return new NodeFileSystemWriteableFileStream(this.name,options)
async createWritable(options: FileSystemCreateWritableOptions = {}): Promise<FileSystemWritableFileStream> {
return new NodeFileSystemWritableFileStream(this.path,options)
}
async getFile(): Promise<File> {
return new NodeFile(this.name)
const stats = await asyncfs.stat(this.path)
return asyncfs.readFile(this.path).then(buffer => Object.assign(new Blob([buffer]), {
lastModified: stats.mtimeMs,
name: this.path,
webkitRelativePath: this.path
}))
}
}
+2 -1
View File
@@ -2,7 +2,8 @@ import * as path from 'node:path'
export default class NodeFileSystemHandle implements FileSystemHandle {
kind: FileSystemHandleKind;
name: string;
path: string;
get name() { return path.basename(this.path) }
async isSameEntry(other: FileSystemHandle): Promise<boolean> {
if (typeof other != typeof this) return false
let thisName = this.name
+30 -23
View File
@@ -1,31 +1,38 @@
import * as fs from 'node:fs'
import * as fsp from 'node:fs/promises'
import * as asyncfs from 'node:fs/promises'
import * as path from 'node:path'
import { Writable } from 'node:stream'
export default class NodeFileSystemWriteableFileStream implements FileSystemWritableFileStream {
#name:string
constructor(name: string, options?: FileSystemCreateWritableOptions) {
if (options.keepExistingData) {
}
this.#name=name
export default class NodeFileSystemWritableFileStream extends WritableStream implements FileSystemWritableFileStream {
#seek_position = 0
#name: string
constructor(name: string, options: FileSystemCreateWritableOptions={}) {
name = path.normalize(name)
super(Writable.toWeb(fs.createWriteStream(name)))
this.#name = name
if(!options.keepExistingData) this.truncate(0)
}
seek(position: number): Promise<void> {
throw new Error("Method not implemented.");
async seek(position: number): Promise<void> {
this.#seek_position = position
}
truncate(size: number): Promise<void> {
return fsp.truncate(this.#name,size)
return asyncfs.truncate(this.#name, size)
}
async write(data: FileSystemWriteChunkType): Promise<void> {
return fsp.writeFile(this.#name,(data as string))
}
locked: boolean;
abort(reason?: any): Promise<void> {
throw new Error("Method not implemented.");
}
close(): Promise<void> {
throw new Error("Method not implemented.");
}
getWriter(): WritableStreamDefaultWriter<any> {
throw new Error("Method not implemented")
async write(wdata: FileSystemWriteChunkType): Promise<void> {
if (wdata instanceof Blob) wdata = await wdata.bytes()
if (typeof wdata === 'string') wdata = new TextEncoder().encode(wdata)
let wsize: number = null
let pseek_position = this.#seek_position
if ((wdata as WriteParams).type) {
const { type, position, size, data } = (wdata as WriteParams)
if (type === 'truncate') return asyncfs.truncate(this.#name, size)
if (position) this.#seek_position = position
if (type === 'seek') return
if (size) wsize = size
wdata = data
}
const file = await asyncfs.open(this.#name,'r+')
await file.write((wdata as Uint8Array), null, wsize, this.#seek_position)
this.#seek_position = pseek_position
}
}