Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbd975feb6 | ||
|
|
1bd153de5b | ||
|
|
a1838bc153 | ||
|
|
65e8e64079 | ||
|
|
579c4bf131 | ||
|
|
1cc6906cc9 |
Generated
+19
-14
@@ -1,36 +1,41 @@
|
|||||||
{
|
{
|
||||||
"name": "node-file-system-adapter",
|
"name": "@sugoidogo/node-file-system-adapter",
|
||||||
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "node-file-system-adapter",
|
"name": "@sugoidogo/node-file-system-adapter",
|
||||||
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime": "^4.1.0"
|
"mime": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.0.3",
|
"@sugoidogo/importable-types-web": "^1.0.0",
|
||||||
"@types/web": "^0.0.310",
|
"@types/node": "^25.0.9",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sugoidogo/importable-types-web": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://gitea.sugoidogo.com/api/packages/sugoidogo/npm/%40sugoidogo%2Fimportable-types-web/-/1.0.0/importable-types-web-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-MqWkQ/WsllO7GiQjqWiFB3ZzsDwJwGPXIWkyd0E24d3CMcI0FKGgsOn6G/RVjCO9k2d/aRlzxqVmyuPAYOZ8tQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "^7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.0.3",
|
"version": "25.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.9.tgz",
|
||||||
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
"integrity": "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/web": {
|
|
||||||
"version": "0.0.310",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.310.tgz",
|
|
||||||
"integrity": "sha512-XN++V7UZGEeRg8MXJBkzMqmcAWoCRN1UV/zpQxg9YnJQ9pIPSuzEG7oidC+nXi/J8pmfb3UgFv+TjEDzMiNnRg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
|
||||||
"node_modules/mime": {
|
"node_modules/mime": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sugoidogo/node-file-system-adapter",
|
"name": "@sugoidogo/node-file-system-adapter",
|
||||||
|
"version": "0.0.0",
|
||||||
"main": "dist/NodeFileSystemDirectoryHandle.js",
|
"main": "dist/NodeFileSystemDirectoryHandle.js",
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@@ -15,8 +16,8 @@
|
|||||||
"mime": "^4.1.0"
|
"mime": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.0.3",
|
"@sugoidogo/importable-types-web": "^1.0.0",
|
||||||
"@types/web": "^0.0.310",
|
"@types/node": "^25.0.9",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -1,14 +1,17 @@
|
|||||||
import NodeFileSystemDirectoryHandle from '../dist/NodeFileSystemDirectoryHandle.js'
|
import NodeFileSystemDirectoryHandle from '../src/NodeFileSystemDirectoryHandle.ts'
|
||||||
|
|
||||||
|
const textText="Hello World!"
|
||||||
const cwd_directory_handle = new NodeFileSystemDirectoryHandle('.')
|
const cwd_directory_handle = new NodeFileSystemDirectoryHandle('.')
|
||||||
const test_directory_handle = await cwd_directory_handle.getDirectoryHandle('test', { 'create': true })
|
const test_directory_handle = await cwd_directory_handle.getDirectoryHandle('test', { 'create': true })
|
||||||
const file_handle = await test_directory_handle.getFileHandle('test.txt', { 'create': true })
|
for (let i = 0; i < 10000; i++) {
|
||||||
|
const file_handle = await test_directory_handle.getFileHandle(i+'.txt', { 'create': true })
|
||||||
const writable = await file_handle.createWritable()
|
const writable = await file_handle.createWritable()
|
||||||
writable.write('Hello World!')
|
await writable.write('Hello World!')
|
||||||
writable.close()
|
await writable.close()
|
||||||
const file = await file_handle.getFile()
|
const file = await file_handle.getFile()
|
||||||
console.log(file.type)
|
const text = await file.text()
|
||||||
console.log(await file.text())
|
if(text!==textText) throw new Error("failed to read back written data")
|
||||||
|
}
|
||||||
const result = await cwd_directory_handle.getFileHandle('test/test.txt').catch(error => 'blocked path traversal')
|
const result = await cwd_directory_handle.getFileHandle('test/test.txt').catch(error => 'blocked path traversal')
|
||||||
if (typeof result === 'string') console.log(result)
|
if (typeof result === 'string') console.log(result)
|
||||||
else console.error('path traversal not blocked')
|
else console.error('path traversal not blocked')
|
||||||
|
|||||||
@@ -3,6 +3,14 @@ import * as asyncfs from 'node:fs/promises';
|
|||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import NodeFileSystemHandle from './NodeFileSystemHandle.ts';
|
import NodeFileSystemHandle from './NodeFileSystemHandle.ts';
|
||||||
import NodeFileSystemFileHandle from './NodeFileSystemFileHandle.ts';
|
import NodeFileSystemFileHandle from './NodeFileSystemFileHandle.ts';
|
||||||
|
import type {
|
||||||
|
FileSystemDirectoryHandle,
|
||||||
|
FileSystemGetDirectoryOptions,
|
||||||
|
FileSystemDirectoryHandleAsyncIterator,
|
||||||
|
FileSystemGetFileOptions,
|
||||||
|
FileSystemRemoveOptions,
|
||||||
|
FileSystemHandle
|
||||||
|
} from '@sugoidogo/importable-types-web'
|
||||||
|
|
||||||
export default class NodeFileSystemDirectoryHandle extends NodeFileSystemHandle implements FileSystemDirectoryHandle {
|
export default class NodeFileSystemDirectoryHandle extends NodeFileSystemHandle implements FileSystemDirectoryHandle {
|
||||||
declare kind: "directory";
|
declare kind: "directory";
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ import * as path from 'node:path';
|
|||||||
import NodeFileSystemHandle from './NodeFileSystemHandle.ts';
|
import NodeFileSystemHandle from './NodeFileSystemHandle.ts';
|
||||||
import NodeFileSystemWritableFileStream from './NodeFileSystemWriteableFileStream.ts';
|
import NodeFileSystemWritableFileStream from './NodeFileSystemWriteableFileStream.ts';
|
||||||
import mime from 'mime'
|
import mime from 'mime'
|
||||||
|
import type {
|
||||||
|
FileSystemFileHandle,
|
||||||
|
FileSystemGetFileOptions,
|
||||||
|
FileSystemCreateWritableOptions,
|
||||||
|
FileSystemWritableFileStream,
|
||||||
|
File as WebFile
|
||||||
|
} from '@sugoidogo/importable-types-web'
|
||||||
|
|
||||||
export default class NodeFileSystemFileHandle extends NodeFileSystemHandle implements FileSystemFileHandle {
|
export default class NodeFileSystemFileHandle extends NodeFileSystemHandle implements FileSystemFileHandle {
|
||||||
declare kind: "file";
|
declare kind: "file";
|
||||||
@@ -17,13 +24,10 @@ export default class NodeFileSystemFileHandle extends NodeFileSystemHandle imple
|
|||||||
async createWritable(options: FileSystemCreateWritableOptions = {}): Promise<FileSystemWritableFileStream> {
|
async createWritable(options: FileSystemCreateWritableOptions = {}): Promise<FileSystemWritableFileStream> {
|
||||||
return new NodeFileSystemWritableFileStream(this.path,options)
|
return new NodeFileSystemWritableFileStream(this.path,options)
|
||||||
}
|
}
|
||||||
async getFile(): Promise<File> {
|
async getFile(): Promise<WebFile> {
|
||||||
const stats = await asyncfs.stat(this.path)
|
const stats = await asyncfs.stat(this.path)
|
||||||
const type = mime.getType(this.name.split('.').pop())
|
const type = mime.getType(this.name.split('.').pop())
|
||||||
return asyncfs.readFile(this.path).then(buffer => Object.assign(new Blob([buffer],{type}), {
|
const buffer = await asyncfs.readFile(this.path)
|
||||||
lastModified: stats.mtimeMs,
|
return new File([buffer], this.path, { "lastModified": stats.mtimeMs, "type": type }) as any //TODO Node File and Web File have some incompatibilities
|
||||||
name: this.path,
|
|
||||||
webkitRelativePath: this.path
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
import * as path from 'node:path'
|
import * as path from 'node:path'
|
||||||
|
import type {
|
||||||
|
FileSystemHandle,
|
||||||
|
FileSystemHandleKind
|
||||||
|
} from '@sugoidogo/importable-types-web'
|
||||||
|
|
||||||
export default class NodeFileSystemHandle implements FileSystemHandle {
|
export default class NodeFileSystemHandle implements FileSystemHandle {
|
||||||
kind: FileSystemHandleKind;
|
kind: FileSystemHandleKind;
|
||||||
|
|||||||
@@ -1,38 +1,47 @@
|
|||||||
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 {
|
||||||
|
FileSystemWritableFileStream,
|
||||||
|
FileSystemCreateWritableOptions,
|
||||||
|
FileSystemWriteChunkType,
|
||||||
|
WriteParams
|
||||||
|
} from '@sugoidogo/importable-types-web'
|
||||||
|
|
||||||
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)
|
|
||||||
this.#seek_position = pseek_position
|
this.#seek_position = pseek_position
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user