@zenfs/core 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/backend.d.ts +1 -1
- package/dist/backends/fetch.d.ts +1 -1
- package/dist/backends/index/fs.d.ts +2 -2
- package/dist/backends/index/index.d.ts +2 -1
- package/dist/backends/overlay.d.ts +4 -3
- package/dist/backends/port/fs.d.ts +5 -2
- package/dist/backends/port/fs.js +13 -10
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +3 -3
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/cred.d.ts +2 -1
- package/dist/emulation/async.d.ts +2 -1
- package/dist/emulation/promises.d.ts +1 -1
- package/dist/emulation/promises.js +27 -35
- package/dist/emulation/shared.d.ts +2 -2
- package/dist/emulation/shared.js +1 -2
- package/dist/emulation/streams.d.ts +1 -1
- package/dist/emulation/sync.d.ts +1 -1
- package/dist/emulation/sync.js +24 -32
- package/dist/error.d.ts +1 -1
- package/dist/error.js +1 -1
- package/dist/file.d.ts +2 -13
- package/dist/file.js +3 -33
- package/dist/filesystem.d.ts +18 -36
- package/dist/filesystem.js +9 -13
- package/dist/stats.d.ts +1 -1
- package/dist/utils.d.ts +2 -2
- package/license.md +2 -26
- package/package.json +2 -2
- package/readme.md +1 -1
- package/src/backends/backend.ts +1 -1
- package/src/backends/fetch.ts +1 -1
- package/src/backends/index/fs.ts +2 -1
- package/src/backends/index/index.ts +2 -1
- package/src/backends/overlay.ts +6 -3
- package/src/backends/port/fs.ts +14 -10
- package/src/config.ts +1 -1
- package/src/cred.ts +2 -1
- package/src/emulation/async.ts +2 -1
- package/src/emulation/promises.ts +28 -33
- package/src/emulation/shared.ts +4 -4
- package/src/emulation/streams.ts +1 -1
- package/src/emulation/sync.ts +28 -33
- package/src/error.ts +1 -1
- package/src/file.ts +5 -39
- package/src/filesystem.ts +19 -38
- package/src/stats.ts +1 -1
- package/src/utils.ts +2 -2
package/readme.md
CHANGED
|
@@ -4,7 +4,7 @@ ZenFS is a file system that emulates the [NodeJS filesystem API](http://nodejs.o
|
|
|
4
4
|
|
|
5
5
|
It works using a system of backends, which are used by ZenFS to store and retrieve data. ZenFS can also integrate with other tools.
|
|
6
6
|
|
|
7
|
-
ZenFS is a fork of [BrowserFS](https://github.com/jvilk/BrowserFS). If you are using ZenFS in a research paper, you may want to [cite BrowserFS](https://github.com/jvilk/BrowserFS
|
|
7
|
+
ZenFS is a fork of [BrowserFS](https://github.com/jvilk/BrowserFS). If you are using ZenFS in a research paper, you may want to [cite BrowserFS](https://github.com/jvilk/BrowserFS#citing).
|
|
8
8
|
|
|
9
9
|
## Backends
|
|
10
10
|
|
package/src/backends/backend.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RequiredKeys } from 'utilium';
|
|
2
2
|
import { ErrnoError, Errno } from '../error.js';
|
|
3
|
-
import { FileSystem } from '../filesystem.js';
|
|
3
|
+
import type { FileSystem } from '../filesystem.js';
|
|
4
4
|
import { levenshtein } from '../utils.js';
|
|
5
5
|
|
|
6
6
|
type OptionType = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function';
|
package/src/backends/fetch.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Errno, ErrnoError } from '../error.js';
|
|
2
2
|
import type { FileSystemMetadata } from '../filesystem.js';
|
|
3
|
-
import { Stats } from '../stats.js';
|
|
3
|
+
import type { Stats } from '../stats.js';
|
|
4
4
|
import type { Backend } from './backend.js';
|
|
5
5
|
import { IndexFS } from './index/fs.js';
|
|
6
6
|
import type { IndexData } from './index/index.js';
|
package/src/backends/index/fs.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { NoSyncFile, isWriteable, flagToMode } from '../../file.js';
|
|
|
4
4
|
import { Readonly, FileSystem } from '../../filesystem.js';
|
|
5
5
|
import type { Stats } from '../../stats.js';
|
|
6
6
|
import { decode } from '../../utils.js';
|
|
7
|
-
import {
|
|
7
|
+
import type { IndexData } from './index.js';
|
|
8
|
+
import { Index } from './index.js';
|
|
8
9
|
|
|
9
10
|
export abstract class IndexFS extends Readonly(FileSystem) {
|
|
10
11
|
protected index: Index = new Index();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isJSON } from 'utilium';
|
|
2
2
|
import { Errno, ErrnoError } from '../../error.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { StatsLike } from '../../stats.js';
|
|
4
|
+
import { Stats } from '../../stats.js';
|
|
4
5
|
import { encode } from '../../utils.js';
|
|
5
6
|
import { basename, dirname } from '../../emulation/path.js';
|
|
6
7
|
|
package/src/backends/overlay.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FileSystemMetadata } from '../filesystem.js';
|
|
2
|
+
import { FileSystem } from '../filesystem.js';
|
|
2
3
|
import { ErrnoError, Errno } from '../error.js';
|
|
3
|
-
import { File
|
|
4
|
+
import type { File } from '../file.js';
|
|
5
|
+
import { PreloadFile, parseFlag } from '../file.js';
|
|
4
6
|
import { Stats } from '../stats.js';
|
|
5
7
|
import { LockedFS } from './locked.js';
|
|
6
8
|
import { dirname } from '../emulation/path.js';
|
|
7
|
-
import { Cred
|
|
9
|
+
import type { Cred } from '../cred.js';
|
|
10
|
+
import { rootCred } from '../cred.js';
|
|
8
11
|
import { decode, encode } from '../utils.js';
|
|
9
12
|
import type { Backend } from './backend.js';
|
|
10
13
|
/**
|
package/src/backends/port/fs.ts
CHANGED
|
@@ -38,12 +38,16 @@ export class PortFile extends File {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
protected _throwNoSync(syscall: string): never {
|
|
42
|
+
throw new ErrnoError(Errno.ENOTSUP, 'Syncrohnous operations not support on PortFile', this.path, syscall);
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
public stat(): Promise<Stats> {
|
|
42
46
|
return this.rpc('stat');
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
public statSync(): Stats {
|
|
46
|
-
|
|
50
|
+
this._throwNoSync('stat');
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
public truncate(len: number): Promise<void> {
|
|
@@ -51,7 +55,7 @@ export class PortFile extends File {
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
public truncateSync(): void {
|
|
54
|
-
|
|
58
|
+
this._throwNoSync('truncate');
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
public write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number> {
|
|
@@ -59,7 +63,7 @@ export class PortFile extends File {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
public writeSync(): number {
|
|
62
|
-
|
|
66
|
+
this._throwNoSync('write');
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>> {
|
|
@@ -68,7 +72,7 @@ export class PortFile extends File {
|
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
public readSync(): number {
|
|
71
|
-
|
|
75
|
+
this._throwNoSync('read');
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
public chown(uid: number, gid: number): Promise<void> {
|
|
@@ -76,7 +80,7 @@ export class PortFile extends File {
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
public chownSync(): void {
|
|
79
|
-
|
|
83
|
+
this._throwNoSync('chown');
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
public chmod(mode: number): Promise<void> {
|
|
@@ -84,7 +88,7 @@ export class PortFile extends File {
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
public chmodSync(): void {
|
|
87
|
-
|
|
91
|
+
this._throwNoSync('chmod');
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
public utimes(atime: Date, mtime: Date): Promise<void> {
|
|
@@ -92,7 +96,7 @@ export class PortFile extends File {
|
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
public utimesSync(): void {
|
|
95
|
-
|
|
99
|
+
this._throwNoSync('utimes');
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
public _setType(type: FileType): Promise<void> {
|
|
@@ -100,7 +104,7 @@ export class PortFile extends File {
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
public _setTypeSync(): void {
|
|
103
|
-
|
|
107
|
+
this._throwNoSync('_setType');
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
public close(): Promise<void> {
|
|
@@ -108,7 +112,7 @@ export class PortFile extends File {
|
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
public closeSync(): void {
|
|
111
|
-
|
|
115
|
+
this._throwNoSync('close');
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
public sync(): Promise<void> {
|
|
@@ -116,7 +120,7 @@ export class PortFile extends File {
|
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
public syncSync(): void {
|
|
119
|
-
|
|
123
|
+
this._throwNoSync('sync');
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
|
package/src/config.ts
CHANGED
|
@@ -18,7 +18,7 @@ function isMountConfig<T extends Backend>(arg: unknown): arg is MountConfigurati
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Retrieve a file system with the given configuration.
|
|
21
|
-
* @
|
|
21
|
+
* @see MountConfiguration
|
|
22
22
|
*/
|
|
23
23
|
export async function resolveMountConfig<T extends Backend>(config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
|
|
24
24
|
if (typeof config !== 'object' || config == null) {
|
package/src/cred.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Credentials used for various operations.
|
|
3
|
-
* Similar to Linux's cred struct.
|
|
3
|
+
* Similar to Linux's cred struct.
|
|
4
|
+
* @see https://github.com/torvalds/linux/blob/master/include/linux/cred.h
|
|
4
5
|
*/
|
|
5
6
|
export interface Cred {
|
|
6
7
|
uid: number;
|
package/src/emulation/async.ts
CHANGED
|
@@ -5,7 +5,8 @@ import type { FileContents } from '../filesystem.js';
|
|
|
5
5
|
import { BigIntStats, type Stats } from '../stats.js';
|
|
6
6
|
import { nop, normalizeMode, type Callback } from '../utils.js';
|
|
7
7
|
import { R_OK } from './constants.js';
|
|
8
|
-
import { Dirent
|
|
8
|
+
import type { Dirent } from './dir.js';
|
|
9
|
+
import { type Dir } from './dir.js';
|
|
9
10
|
import * as promises from './promises.js';
|
|
10
11
|
import { fd2file } from './shared.js';
|
|
11
12
|
import { ReadStream, WriteStream } from './streams.js';
|
|
@@ -7,7 +7,8 @@ import type { ReadableStream as TReadableStream } from 'node:stream/web';
|
|
|
7
7
|
import type { Interface as ReadlineInterface } from 'readline';
|
|
8
8
|
import type { ReadableStreamController } from 'stream/web';
|
|
9
9
|
import { Errno, ErrnoError } from '../error.js';
|
|
10
|
-
import {
|
|
10
|
+
import type { File } from '../file.js';
|
|
11
|
+
import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
11
12
|
import type { FileContents } from '../filesystem.js';
|
|
12
13
|
import { BigIntStats, FileType, type Stats } from '../stats.js';
|
|
13
14
|
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
@@ -110,7 +111,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
110
111
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
111
112
|
}
|
|
112
113
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : data;
|
|
113
|
-
await this.file.write(encodedData, 0, encodedData.length
|
|
114
|
+
await this.file.write(encodedData, 0, encodedData.length);
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
/**
|
|
@@ -492,41 +493,35 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
|
|
|
492
493
|
const { fs, path: resolved } = resolveMount(path);
|
|
493
494
|
|
|
494
495
|
if (!(await fs.exists(path, cred))) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
|
|
503
|
-
case ActionType.THROW:
|
|
504
|
-
throw ErrnoError.With('ENOENT', path, '_open');
|
|
505
|
-
default:
|
|
506
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
496
|
+
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
|
|
497
|
+
throw ErrnoError.With('ENOENT', path, '_open');
|
|
498
|
+
}
|
|
499
|
+
// Create the file
|
|
500
|
+
const parentStats: Stats = await fs.stat(dirname(resolved), cred);
|
|
501
|
+
if (parentStats && !parentStats.isDirectory()) {
|
|
502
|
+
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
507
503
|
}
|
|
504
|
+
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
|
|
508
505
|
}
|
|
509
506
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
throw ErrnoError.With('EEXIST', path, '_open');
|
|
513
|
-
case ActionType.TRUNCATE:
|
|
514
|
-
/*
|
|
515
|
-
In a previous implementation, we deleted the file and
|
|
516
|
-
re-created it. However, this created a race condition if another
|
|
517
|
-
asynchronous request was trying to read the file, as the file
|
|
518
|
-
would not exist for a small period of time.
|
|
519
|
-
*/
|
|
520
|
-
const file: File = await fs.openFile(resolved, flag, cred);
|
|
521
|
-
await file.truncate(0);
|
|
522
|
-
await file.sync();
|
|
523
|
-
return new FileHandle(file);
|
|
524
|
-
case ActionType.NOP:
|
|
525
|
-
// Must await so thrown errors are caught by the catch below
|
|
526
|
-
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
527
|
-
default:
|
|
528
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
507
|
+
if (isExclusive(flag)) {
|
|
508
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
529
509
|
}
|
|
510
|
+
|
|
511
|
+
if (!isTruncating(flag)) {
|
|
512
|
+
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/*
|
|
516
|
+
In a previous implementation, we deleted the file and
|
|
517
|
+
re-created it. However, this created a race condition if another
|
|
518
|
+
asynchronous request was trying to read the file, as the file
|
|
519
|
+
would not exist for a small period of time.
|
|
520
|
+
*/
|
|
521
|
+
const file: File = await fs.openFile(resolved, flag, cred);
|
|
522
|
+
await file.truncate(0);
|
|
523
|
+
await file.sync();
|
|
524
|
+
return new FileHandle(file);
|
|
530
525
|
}
|
|
531
526
|
|
|
532
527
|
/**
|
package/src/emulation/shared.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import type { BigIntStatsFs, StatsFs } from 'node:fs';
|
|
4
4
|
import { InMemory } from '../backends/memory.js';
|
|
5
|
-
import { Cred
|
|
5
|
+
import type { Cred } from '../cred.js';
|
|
6
|
+
import { rootCred } from '../cred.js';
|
|
6
7
|
import { Errno, ErrnoError } from '../error.js';
|
|
7
8
|
import type { File } from '../file.js';
|
|
8
|
-
import { FileSystem } from '../filesystem.js';
|
|
9
|
+
import type { FileSystem } from '../filesystem.js';
|
|
9
10
|
import { size_max } from '../inode.js';
|
|
10
|
-
import { ZenFsType } from '../stats.js';
|
|
11
11
|
import { normalizePath } from '../utils.js';
|
|
12
12
|
import { resolve, type AbsolutePath } from './path.js';
|
|
13
13
|
|
|
@@ -129,7 +129,7 @@ export function _statfs<const T extends boolean>(fs: FileSystem, bigint?: T): T
|
|
|
129
129
|
const bs = md.blockSize || 4096;
|
|
130
130
|
|
|
131
131
|
return {
|
|
132
|
-
type: (bigint ? BigInt : Number)(md.type
|
|
132
|
+
type: (bigint ? BigInt : Number)(md.type),
|
|
133
133
|
bsize: (bigint ? BigInt : Number)(bs),
|
|
134
134
|
ffree: (bigint ? BigInt : Number)(md.freeNodes || size_max),
|
|
135
135
|
files: (bigint ? BigInt : Number)(md.totalNodes || size_max),
|
package/src/emulation/streams.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as Node from 'fs';
|
|
2
2
|
import { Readable, Writable } from 'readable-stream';
|
|
3
|
-
import { Callback } from '../utils.js';
|
|
3
|
+
import type { Callback } from '../utils.js';
|
|
4
4
|
import { ErrnoError, Errno } from '../error.js';
|
|
5
5
|
|
|
6
6
|
export class ReadStream extends Readable implements Node.ReadStream {
|
package/src/emulation/sync.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import type * as fs from 'node:fs';
|
|
3
3
|
import { Errno, ErrnoError } from '../error.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import type { File } from '../file.js';
|
|
5
|
+
import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
6
|
+
import type { FileContents } from '../filesystem.js';
|
|
6
7
|
import { BigIntStats, FileType, type Stats } from '../stats.js';
|
|
7
8
|
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
8
9
|
import { COPYFILE_EXCL, F_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } from './constants.js';
|
|
@@ -131,19 +132,15 @@ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null
|
|
|
131
132
|
const { fs, path: resolved } = resolveMount(path);
|
|
132
133
|
|
|
133
134
|
if (!fs.existsSync(resolved, cred)) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return fs.createFileSync(resolved, flag, mode, cred);
|
|
142
|
-
case ActionType.THROW:
|
|
143
|
-
throw ErrnoError.With('ENOENT', path, '_open');
|
|
144
|
-
default:
|
|
145
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
|
|
135
|
+
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
|
|
136
|
+
throw ErrnoError.With('ENOENT', path, '_open');
|
|
137
|
+
}
|
|
138
|
+
// Create the file
|
|
139
|
+
const parentStats: Stats = fs.statSync(dirname(resolved), cred);
|
|
140
|
+
if (!parentStats.isDirectory()) {
|
|
141
|
+
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
146
142
|
}
|
|
143
|
+
return fs.createFileSync(resolved, flag, mode, cred);
|
|
147
144
|
}
|
|
148
145
|
|
|
149
146
|
const stats: Stats = fs.statSync(resolved, cred);
|
|
@@ -152,25 +149,23 @@ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null
|
|
|
152
149
|
throw ErrnoError.With('EACCES', path, '_open');
|
|
153
150
|
}
|
|
154
151
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
case ActionType.THROW:
|
|
158
|
-
throw ErrnoError.With('EEXIST', path, '_open');
|
|
159
|
-
case ActionType.TRUNCATE:
|
|
160
|
-
// Delete file.
|
|
161
|
-
fs.unlinkSync(resolved, cred);
|
|
162
|
-
/*
|
|
163
|
-
Create file. Use the same mode as the old file.
|
|
164
|
-
Node itself modifies the ctime when this occurs, so this action
|
|
165
|
-
will preserve that behavior if the underlying file system
|
|
166
|
-
supports those properties.
|
|
167
|
-
*/
|
|
168
|
-
return fs.createFileSync(resolved, flag, stats.mode, cred);
|
|
169
|
-
case ActionType.NOP:
|
|
170
|
-
return fs.openFileSync(resolved, flag, cred);
|
|
171
|
-
default:
|
|
172
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
|
|
152
|
+
if (isExclusive(flag)) {
|
|
153
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
173
154
|
}
|
|
155
|
+
|
|
156
|
+
if (!isTruncating(flag)) {
|
|
157
|
+
return fs.openFileSync(resolved, flag, cred);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Delete file.
|
|
161
|
+
fs.unlinkSync(resolved, cred);
|
|
162
|
+
/*
|
|
163
|
+
Create file. Use the same mode as the old file.
|
|
164
|
+
Node itself modifies the ctime when this occurs, so this action
|
|
165
|
+
will preserve that behavior if the underlying file system
|
|
166
|
+
supports those properties.
|
|
167
|
+
*/
|
|
168
|
+
return fs.createFileSync(resolved, flag, stats.mode, cred);
|
|
174
169
|
}
|
|
175
170
|
|
|
176
171
|
/**
|
|
@@ -292,7 +287,7 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
|
|
|
292
287
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
293
288
|
const file = _openSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), flag, options.mode, true);
|
|
294
289
|
try {
|
|
295
|
-
file.writeSync(encodedData, 0, encodedData.byteLength
|
|
290
|
+
file.writeSync(encodedData, 0, encodedData.byteLength);
|
|
296
291
|
} finally {
|
|
297
292
|
file.closeSync();
|
|
298
293
|
}
|
package/src/error.ts
CHANGED
package/src/file.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FileReadResult } from 'node:fs/promises';
|
|
2
|
-
import { ErrnoError, Errno } from './error.js';
|
|
3
2
|
import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT } from './emulation/constants.js';
|
|
3
|
+
import { Errno, ErrnoError } from './error.js';
|
|
4
4
|
import type { FileSystem } from './filesystem.js';
|
|
5
5
|
import { size_max } from './inode.js';
|
|
6
6
|
import { Stats, type FileType } from './stats.js';
|
|
@@ -32,20 +32,6 @@ declare global {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/**
|
|
36
|
-
* @hidden
|
|
37
|
-
*/
|
|
38
|
-
export enum ActionType {
|
|
39
|
-
// Indicates that the code should not do anything.
|
|
40
|
-
NOP = 0,
|
|
41
|
-
// Indicates that the code should throw an exception.
|
|
42
|
-
THROW = 1,
|
|
43
|
-
// Indicates that the code should truncate the file, but only if it is a file.
|
|
44
|
-
TRUNCATE = 2,
|
|
45
|
-
// Indicates that the code should create the file.
|
|
46
|
-
CREATE = 3,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
35
|
const validFlags = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
|
|
50
36
|
|
|
51
37
|
export function parseFlag(flag: string | number): string {
|
|
@@ -158,25 +144,6 @@ export function isExclusive(flag: string): boolean {
|
|
|
158
144
|
return flag.indexOf('x') !== -1;
|
|
159
145
|
}
|
|
160
146
|
|
|
161
|
-
export function pathExistsAction(flag: string): ActionType {
|
|
162
|
-
if (isExclusive(flag)) {
|
|
163
|
-
return ActionType.THROW;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (isTruncating(flag)) {
|
|
167
|
-
return ActionType.TRUNCATE;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return ActionType.NOP;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function pathNotExistsAction(flag: string): ActionType {
|
|
174
|
-
if ((isWriteable(flag) || isAppendable(flag)) && flag !== 'r+') {
|
|
175
|
-
return ActionType.CREATE;
|
|
176
|
-
}
|
|
177
|
-
return ActionType.THROW;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
147
|
export abstract class File {
|
|
181
148
|
/**
|
|
182
149
|
* Get the current file position.
|
|
@@ -249,7 +216,7 @@ export abstract class File {
|
|
|
249
216
|
* the current position.
|
|
250
217
|
* @returns Promise resolving to the new length of the buffer
|
|
251
218
|
*/
|
|
252
|
-
public abstract write(buffer: Uint8Array, offset?: number, length?: number, position?: number
|
|
219
|
+
public abstract write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number>;
|
|
253
220
|
|
|
254
221
|
/**
|
|
255
222
|
* Write buffer to the file.
|
|
@@ -263,7 +230,7 @@ export abstract class File {
|
|
|
263
230
|
* data should be written. If position is null, the data will be written at
|
|
264
231
|
* the current position.
|
|
265
232
|
*/
|
|
266
|
-
public abstract writeSync(buffer: Uint8Array, offset?: number, length?: number, position?: number
|
|
233
|
+
public abstract writeSync(buffer: Uint8Array, offset?: number, length?: number, position?: number): number;
|
|
267
234
|
|
|
268
235
|
/**
|
|
269
236
|
* Read data from the file.
|
|
@@ -515,7 +482,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
515
482
|
* data should be written. If position is null, the data will be written at
|
|
516
483
|
* the current position.
|
|
517
484
|
*/
|
|
518
|
-
public async write(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number =
|
|
485
|
+
public async write(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = this.position): Promise<number> {
|
|
519
486
|
const bytesWritten = this.writeSync(buffer, offset, length, position);
|
|
520
487
|
await this.sync();
|
|
521
488
|
return bytesWritten;
|
|
@@ -534,9 +501,8 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
534
501
|
* the current position.
|
|
535
502
|
* @returns bytes written
|
|
536
503
|
*/
|
|
537
|
-
public writeSync(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number =
|
|
504
|
+
public writeSync(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = this.position): number {
|
|
538
505
|
this.dirty = true;
|
|
539
|
-
position ??= this.position;
|
|
540
506
|
if (!isWriteable(this.flag)) {
|
|
541
507
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
542
508
|
}
|
package/src/filesystem.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { ErrnoError, Errno } from './error.js';
|
|
|
3
3
|
import { rootCred, type Cred } from './cred.js';
|
|
4
4
|
import { join } from './emulation/path.js';
|
|
5
5
|
import { PreloadFile, parseFlag, type File } from './file.js';
|
|
6
|
-
import type
|
|
6
|
+
import { ZenFsType, type Stats } from './stats.js';
|
|
7
7
|
|
|
8
8
|
export type FileContents = ArrayBufferView | string;
|
|
9
9
|
|
|
@@ -64,26 +64,17 @@ export interface FileSystemMetadata {
|
|
|
64
64
|
/**
|
|
65
65
|
* The type of the FS
|
|
66
66
|
*/
|
|
67
|
-
type
|
|
67
|
+
type: number;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Structure for a filesystem. All ZenFS backends must extend this.
|
|
72
72
|
*
|
|
73
|
-
* This class includes
|
|
73
|
+
* This class includes default implementations for `exists` and `existsSync`
|
|
74
74
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* - Every path is an absolute path. `.`, `..`, and other items are resolved into an absolute form.
|
|
78
|
-
* - All arguments are present. Any optional arguments at the Node API level have been passed in with their default values.
|
|
75
|
+
* If you are extending this class, note that every path is an absolute path and all arguments are present.
|
|
79
76
|
*/
|
|
80
77
|
export abstract class FileSystem {
|
|
81
|
-
/**
|
|
82
|
-
* Numeric type, used for statfs
|
|
83
|
-
* @internal @protected
|
|
84
|
-
*/
|
|
85
|
-
_type?: number;
|
|
86
|
-
|
|
87
78
|
/**
|
|
88
79
|
* Get metadata about the current file system
|
|
89
80
|
*/
|
|
@@ -95,7 +86,7 @@ export abstract class FileSystem {
|
|
|
95
86
|
freeSpace: 0,
|
|
96
87
|
noResizableBuffers: false,
|
|
97
88
|
noAsyncCache: false,
|
|
98
|
-
type:
|
|
89
|
+
type: ZenFsType,
|
|
99
90
|
};
|
|
100
91
|
}
|
|
101
92
|
|
|
@@ -104,8 +95,7 @@ export abstract class FileSystem {
|
|
|
104
95
|
public async ready(): Promise<void> {}
|
|
105
96
|
|
|
106
97
|
/**
|
|
107
|
-
* Asynchronous rename.
|
|
108
|
-
* are given to the completion callback.
|
|
98
|
+
* Asynchronous rename.
|
|
109
99
|
*/
|
|
110
100
|
public abstract rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
111
101
|
/**
|
|
@@ -124,29 +114,27 @@ export abstract class FileSystem {
|
|
|
124
114
|
public abstract statSync(path: string, cred: Cred): Stats;
|
|
125
115
|
|
|
126
116
|
/**
|
|
127
|
-
* Opens the file at path
|
|
128
|
-
* @param
|
|
117
|
+
* Opens the file at `path` with the given flag. The file must exist.
|
|
118
|
+
* @param path The path to open.
|
|
129
119
|
* @param flag The flag to use when opening the file.
|
|
130
120
|
*/
|
|
131
121
|
public abstract openFile(path: string, flag: string, cred: Cred): Promise<File>;
|
|
132
122
|
|
|
133
123
|
/**
|
|
134
|
-
* Opens the file at path
|
|
135
|
-
* @param
|
|
124
|
+
* Opens the file at `path` with the given flag. The file must exist.
|
|
125
|
+
* @param path The path to open.
|
|
136
126
|
* @param flag The flag to use when opening the file.
|
|
137
127
|
* @return A File object corresponding to the opened file.
|
|
138
128
|
*/
|
|
139
129
|
public abstract openFileSync(path: string, flag: string, cred: Cred): File;
|
|
140
130
|
|
|
141
131
|
/**
|
|
142
|
-
* Create the file at path
|
|
143
|
-
* flag.
|
|
132
|
+
* Create the file at `path` with the given mode. Then, open it with the given flag.
|
|
144
133
|
*/
|
|
145
134
|
public abstract createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
|
|
146
135
|
|
|
147
136
|
/**
|
|
148
|
-
* Create the file at path
|
|
149
|
-
* flag.
|
|
137
|
+
* Create the file at `path` with the given mode. Then, open it with the given flag.
|
|
150
138
|
*/
|
|
151
139
|
public abstract createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
|
|
152
140
|
|
|
@@ -169,21 +157,16 @@ export abstract class FileSystem {
|
|
|
169
157
|
public abstract rmdirSync(path: string, cred: Cred): void;
|
|
170
158
|
/**
|
|
171
159
|
* Asynchronous `mkdir`.
|
|
172
|
-
* @param mode Mode to make the directory using.
|
|
173
|
-
* the filesystem doesn't support permissions.
|
|
160
|
+
* @param mode Mode to make the directory using.
|
|
174
161
|
*/
|
|
175
162
|
public abstract mkdir(path: string, mode: number, cred: Cred): Promise<void>;
|
|
176
163
|
/**
|
|
177
164
|
* Synchronous `mkdir`.
|
|
178
|
-
* @param mode Mode to make the directory using.
|
|
179
|
-
* the filesystem doesn't support permissions.
|
|
165
|
+
* @param mode Mode to make the directory using.
|
|
180
166
|
*/
|
|
181
167
|
public abstract mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
182
168
|
/**
|
|
183
169
|
* Asynchronous `readdir`. Reads the contents of a directory.
|
|
184
|
-
*
|
|
185
|
-
* The callback gets two arguments `(err, files)` where `files` is an array of
|
|
186
|
-
* the names of the files in the directory excluding `'.'` and `'..'`.
|
|
187
170
|
*/
|
|
188
171
|
public abstract readdir(path: string, cred: Cred): Promise<string[]>;
|
|
189
172
|
/**
|
|
@@ -352,13 +335,11 @@ type AsyncOperation = {
|
|
|
352
335
|
/**
|
|
353
336
|
* Async() implements synchronous methods on an asynchronous file system
|
|
354
337
|
*
|
|
355
|
-
* Implementing classes must define
|
|
356
|
-
* by:
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
* - During application loading, the contents of the async file system can be reloaded into
|
|
361
|
-
* the synchronous store, if desired.
|
|
338
|
+
* Implementing classes must define `_sync` for the synchronous file system used as a cache.
|
|
339
|
+
* Synchronous methods on an asynchronous FS are implemented by:
|
|
340
|
+
* - Performing operations over the in-memory copy,
|
|
341
|
+
* while asynchronously pipelining them to the backing store.
|
|
342
|
+
* - During loading, the contents of the async file system are eloaded into the synchronous store.
|
|
362
343
|
*
|
|
363
344
|
*/
|
|
364
345
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/stats.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as Node from 'fs';
|
|
2
|
-
import { Cred } from './cred.js';
|
|
2
|
+
import type { Cred } from './cred.js';
|
|
3
3
|
import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';
|
|
4
4
|
import { size_max } from './inode.js';
|
|
5
5
|
|