@zenfs/core 1.8.8 → 1.9.1
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/backend.js +7 -4
- package/dist/backends/fetch.d.ts +23 -32
- package/dist/backends/fetch.js +94 -134
- package/dist/backends/index.d.ts +1 -4
- package/dist/backends/index.js +1 -4
- package/dist/backends/memory.d.ts +7 -5
- package/dist/backends/memory.js +6 -4
- package/dist/backends/overlay.d.ts +4 -5
- package/dist/backends/overlay.js +16 -20
- package/dist/backends/passthrough.d.ts +3 -3
- package/dist/backends/passthrough.js +4 -6
- package/dist/backends/port/fs.d.ts +4 -5
- package/dist/backends/port/fs.js +7 -12
- package/dist/backends/port/rpc.d.ts +1 -1
- package/dist/backends/port/rpc.js +15 -13
- package/dist/backends/store/fs.d.ts +51 -40
- package/dist/backends/store/fs.js +347 -241
- package/dist/backends/store/map.d.ts +41 -0
- package/dist/backends/store/map.js +45 -0
- package/dist/backends/store/simple.d.ts +10 -58
- package/dist/backends/store/simple.js +8 -115
- package/dist/backends/store/store.d.ts +111 -44
- package/dist/backends/store/store.js +230 -38
- package/dist/config.d.ts +7 -3
- package/dist/config.js +17 -14
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +1 -5
- package/dist/{devices.d.ts → internal/devices.d.ts} +4 -4
- package/dist/{devices.js → internal/devices.js} +18 -14
- package/dist/{file.d.ts → internal/file.d.ts} +3 -2
- package/dist/{file.js → internal/file.js} +17 -12
- package/dist/{backends/store → internal}/file_index.d.ts +13 -3
- package/dist/{backends/store → internal}/file_index.js +28 -5
- package/dist/{filesystem.d.ts → internal/filesystem.d.ts} +99 -32
- package/dist/internal/filesystem.js +83 -0
- package/dist/internal/index.d.ts +9 -0
- package/dist/internal/index.js +9 -0
- package/dist/internal/index_fs.d.ts +56 -0
- package/dist/internal/index_fs.js +188 -0
- package/dist/{backends/store → internal}/inode.d.ts +6 -1
- package/dist/{backends/store → internal}/inode.js +14 -6
- package/dist/internal/log.d.ts +132 -0
- package/dist/internal/log.js +177 -0
- package/dist/mixins/async.d.ts +2 -2
- package/dist/mixins/async.js +19 -16
- package/dist/mixins/mutexed.d.ts +9 -3
- package/dist/mixins/mutexed.js +22 -3
- package/dist/mixins/readonly.d.ts +2 -2
- package/dist/mixins/readonly.js +4 -3
- package/dist/mixins/shared.d.ts +1 -1
- package/dist/mixins/sync.d.ts +2 -2
- package/dist/stats.d.ts +2 -3
- package/dist/stats.js +7 -5
- package/dist/utils.d.ts +2 -15
- package/dist/utils.js +10 -47
- package/dist/vfs/async.d.ts +2 -2
- package/dist/vfs/async.js +3 -3
- package/dist/vfs/dir.js +1 -1
- package/dist/vfs/promises.d.ts +6 -6
- package/dist/vfs/promises.js +54 -49
- package/dist/vfs/shared.d.ts +3 -3
- package/dist/vfs/shared.js +16 -10
- package/dist/vfs/streams.js +1 -1
- package/dist/vfs/sync.d.ts +1 -2
- package/dist/vfs/sync.js +14 -15
- package/dist/vfs/types.d.ts +1 -0
- package/dist/vfs/watchers.d.ts +5 -1
- package/dist/vfs/watchers.js +16 -19
- package/package.json +3 -3
- package/readme.md +12 -12
- package/scripts/test.js +15 -3
- package/tests/backend/fetch.test.ts +49 -0
- package/tests/backend/port.test.ts +130 -0
- package/tests/common/context.test.ts +9 -4
- package/tests/common.ts +21 -3
- package/tests/data/image.jpg +0 -0
- package/tests/data/utf8.txt +1 -0
- package/tests/fetch/config.js +40 -0
- package/tests/fetch/fetch.ts +20 -0
- package/tests/fetch/run.sh +3 -3
- package/tests/fetch/{server.ts → server.js} +15 -11
- package/tests/fs/directory.test.ts +1 -1
- package/tests/fs/errors.test.ts +1 -1
- package/tests/fs/links.test.ts +1 -1
- package/tests/fs/open.test.ts +1 -1
- package/tests/fs/permissions.test.ts +2 -3
- package/tests/fs/rename.test.ts +1 -1
- package/tests/fs/stat.test.ts +1 -1
- package/tests/fs/times.test.ts +1 -1
- package/tests/fs/watch.test.ts +21 -22
- package/tests/fs/writeFile.test.ts +8 -7
- package/tests/readme.md +3 -3
- package/tests/setup/_overlay.ts +7 -0
- package/tests/setup/context.ts +2 -2
- package/tests/setup/index.ts +3 -3
- package/tests/setup/memory.ts +2 -2
- package/tests/setup/port.ts +2 -2
- package/tests/setup.ts +25 -5
- package/tests/tsconfig.json +3 -2
- package/dist/backends/store/index_fs.d.ts +0 -34
- package/dist/backends/store/index_fs.js +0 -67
- package/dist/filesystem.js +0 -52
- package/tests/fetch/cow+fetch.ts +0 -13
- package/tests/port/channel.test.ts +0 -39
- package/tests/port/config.test.ts +0 -30
- package/tests/port/remote.test.ts +0 -32
- package/tests/port/timeout.test.ts +0 -48
- /package/dist/{credentials.d.ts → internal/credentials.d.ts} +0 -0
- /package/dist/{credentials.js → internal/credentials.js} +0 -0
- /package/dist/{error.d.ts → internal/error.d.ts} +0 -0
- /package/dist/{error.js → internal/error.js} +0 -0
- /package/tests/{port → backend}/config.worker.js +0 -0
- /package/tests/{port → backend}/remote.worker.js +0 -0
|
@@ -13,8 +13,7 @@ export declare const version = 1;
|
|
|
13
13
|
* An index of files
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
|
-
export declare class Index extends Map<string,
|
|
17
|
-
protected _directories?: Map<string, Record<string, number>>;
|
|
16
|
+
export declare class Index extends Map<string, Inode> {
|
|
18
17
|
/**
|
|
19
18
|
* Converts the index to JSON
|
|
20
19
|
*/
|
|
@@ -23,7 +22,18 @@ export declare class Index extends Map<string, Readonly<Inode>> {
|
|
|
23
22
|
* Converts the index to a string
|
|
24
23
|
*/
|
|
25
24
|
toString(): string;
|
|
25
|
+
pathOf(id: number): string | undefined;
|
|
26
|
+
getByID(id: number): Inode | undefined;
|
|
27
|
+
entryByID(id: number): {
|
|
28
|
+
path: string;
|
|
29
|
+
inode: Inode;
|
|
30
|
+
} | undefined;
|
|
26
31
|
directoryEntries(path: string): Record<string, number>;
|
|
32
|
+
/**
|
|
33
|
+
* Get the next available ID in the index
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
_alloc(): number;
|
|
27
37
|
/**
|
|
28
38
|
* Gets a list of entries for each directory in the index.
|
|
29
39
|
* Use
|
|
@@ -32,7 +42,7 @@ export declare class Index extends Map<string, Readonly<Inode>> {
|
|
|
32
42
|
/**
|
|
33
43
|
* Loads the index from JSON data
|
|
34
44
|
*/
|
|
35
|
-
fromJSON(json: IndexData):
|
|
45
|
+
fromJSON(json: IndexData): this;
|
|
36
46
|
/**
|
|
37
47
|
* Parses an index from a string
|
|
38
48
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
|
|
2
2
|
import { isJSON, randomInt } from 'utilium';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { S_IFDIR, S_IFMT, size_max } from '../vfs/constants.js';
|
|
4
|
+
import { basename, dirname } from '../vfs/path.js';
|
|
5
|
+
import { Errno, ErrnoError } from './error.js';
|
|
6
6
|
import { Inode } from './inode.js';
|
|
7
7
|
export const version = 1;
|
|
8
8
|
/**
|
|
@@ -25,6 +25,22 @@ export class Index extends Map {
|
|
|
25
25
|
toString() {
|
|
26
26
|
return JSON.stringify(this.toJSON());
|
|
27
27
|
}
|
|
28
|
+
pathOf(id) {
|
|
29
|
+
for (const [path, inode] of this) {
|
|
30
|
+
if (inode.ino == id || inode.data == id)
|
|
31
|
+
return path;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
getByID(id) {
|
|
35
|
+
var _a;
|
|
36
|
+
return (_a = this.entryByID(id)) === null || _a === void 0 ? void 0 : _a.inode;
|
|
37
|
+
}
|
|
38
|
+
entryByID(id) {
|
|
39
|
+
for (const [path, inode] of this) {
|
|
40
|
+
if (inode.ino == id || inode.data == id)
|
|
41
|
+
return { path, inode };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
28
44
|
directoryEntries(path) {
|
|
29
45
|
const node = this.get(path);
|
|
30
46
|
if (!node)
|
|
@@ -39,6 +55,13 @@ export class Index extends Map {
|
|
|
39
55
|
}
|
|
40
56
|
return entries;
|
|
41
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the next available ID in the index
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
_alloc() {
|
|
63
|
+
return Math.max(...[...this.values()].flatMap(i => [i.ino, i.data])) + 1;
|
|
64
|
+
}
|
|
42
65
|
/**
|
|
43
66
|
* Gets a list of entries for each directory in the index.
|
|
44
67
|
* Use
|
|
@@ -72,14 +95,14 @@ export class Index extends Map {
|
|
|
72
95
|
node.ino = 0;
|
|
73
96
|
this.set(path, new Inode(node));
|
|
74
97
|
}
|
|
98
|
+
return this;
|
|
75
99
|
}
|
|
76
100
|
/**
|
|
77
101
|
* Parses an index from a string
|
|
78
102
|
*/
|
|
79
103
|
static parse(data) {
|
|
80
|
-
if (!isJSON(data))
|
|
104
|
+
if (!isJSON(data))
|
|
81
105
|
throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
|
|
82
|
-
}
|
|
83
106
|
const json = JSON.parse(data);
|
|
84
107
|
const index = new Index();
|
|
85
108
|
index.fromJSON(json);
|
|
@@ -1,29 +1,51 @@
|
|
|
1
|
+
import type { ConstMap } from 'utilium';
|
|
2
|
+
import type { Stats, StatsLike } from '../stats.js';
|
|
1
3
|
import type { File } from './file.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Usage information about a file system
|
|
6
|
+
*/
|
|
7
|
+
export interface UsageInfo {
|
|
8
|
+
/**
|
|
9
|
+
* The total space
|
|
10
|
+
*/
|
|
11
|
+
totalSpace: number;
|
|
12
|
+
/**
|
|
13
|
+
* The available space
|
|
14
|
+
*/
|
|
15
|
+
freeSpace: number;
|
|
16
|
+
/**
|
|
17
|
+
* The optimal block size to use with the file system
|
|
18
|
+
* @default 4096
|
|
19
|
+
*/
|
|
20
|
+
blockSize?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Total number of nodes available
|
|
23
|
+
*/
|
|
24
|
+
totalNodes?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Number of free nodes available
|
|
27
|
+
*/
|
|
28
|
+
freeNodes?: number;
|
|
29
|
+
}
|
|
4
30
|
/**
|
|
5
31
|
* Metadata about a FileSystem
|
|
32
|
+
* @deprecated
|
|
6
33
|
*/
|
|
7
|
-
export interface FileSystemMetadata {
|
|
34
|
+
export interface FileSystemMetadata extends UsageInfo {
|
|
8
35
|
/**
|
|
9
36
|
* The name of the FS
|
|
37
|
+
* @deprecated Use `FileSystem#name`
|
|
10
38
|
*/
|
|
11
39
|
name: string;
|
|
12
40
|
/**
|
|
13
41
|
* Whether the FS is readonly or not
|
|
42
|
+
* @deprecated Use `FileSystem#attributes
|
|
14
43
|
*/
|
|
15
44
|
readonly: boolean;
|
|
16
|
-
/**
|
|
17
|
-
* The total space
|
|
18
|
-
*/
|
|
19
|
-
totalSpace: number;
|
|
20
|
-
/**
|
|
21
|
-
* The available space
|
|
22
|
-
*/
|
|
23
|
-
freeSpace: number;
|
|
24
45
|
/**
|
|
25
46
|
* If set, disables File from using a resizable array buffer.
|
|
26
47
|
* @default false
|
|
48
|
+
* @deprecated Use `FileSystem#attributes`
|
|
27
49
|
*/
|
|
28
50
|
noResizableBuffers: boolean;
|
|
29
51
|
/**
|
|
@@ -31,32 +53,45 @@ export interface FileSystemMetadata {
|
|
|
31
53
|
* This means *sync operations will not work*.
|
|
32
54
|
* It has no affect on sync file systems.
|
|
33
55
|
* @default false
|
|
56
|
+
* @deprecated Use `FileSystem#attributes
|
|
34
57
|
*/
|
|
35
58
|
noAsyncCache: boolean;
|
|
36
59
|
/**
|
|
37
|
-
* The
|
|
38
|
-
* @default 4096
|
|
60
|
+
* The type of the FS
|
|
39
61
|
*/
|
|
40
|
-
|
|
62
|
+
type: number;
|
|
41
63
|
/**
|
|
42
|
-
*
|
|
64
|
+
* Various features the file system supports.
|
|
65
|
+
* @deprecated Use `FileSystem#attributes`
|
|
43
66
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
67
|
+
features?: unknown[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Attributes that control how the file system interacts with the VFS.
|
|
71
|
+
* No options are set by default.
|
|
72
|
+
*/
|
|
73
|
+
export type FileSystemAttributes = {
|
|
74
|
+
/** The FS supports setuid and setgid when creating files and directories. */
|
|
75
|
+
setid: void;
|
|
76
|
+
/** If set, disables `PreloadFile` from using a resizable array buffer. */
|
|
77
|
+
no_buffer_resize: void;
|
|
78
|
+
/**
|
|
79
|
+
* If set disables async file systems from preloading their contents.
|
|
80
|
+
* This means *sync operations will not work* (unless the contents are cached)
|
|
81
|
+
* It has no affect on sync file systems.
|
|
47
82
|
*/
|
|
48
|
-
|
|
83
|
+
no_async: void;
|
|
49
84
|
/**
|
|
50
|
-
*
|
|
85
|
+
* Currently unused. In the future, this will disable caching.
|
|
86
|
+
* Not recommended due to performance impact.
|
|
51
87
|
*/
|
|
52
|
-
|
|
88
|
+
no_cache: void;
|
|
53
89
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* - setid: The FS supports setuid and setgid when creating files and directories.
|
|
90
|
+
* If set, the file system should not be written to.
|
|
91
|
+
* This should be set for read-only file systems.
|
|
57
92
|
*/
|
|
58
|
-
|
|
59
|
-
}
|
|
93
|
+
no_write: void;
|
|
94
|
+
};
|
|
60
95
|
/**
|
|
61
96
|
* Options used when creating files and directories.
|
|
62
97
|
* This weird naming and such is to preserve backward compatibility.
|
|
@@ -96,16 +131,48 @@ export interface PureCreationOptions extends CreationOptions {
|
|
|
96
131
|
*/
|
|
97
132
|
export declare abstract class FileSystem {
|
|
98
133
|
/**
|
|
99
|
-
*
|
|
134
|
+
* A unique ID for this kind of file system.
|
|
135
|
+
* Currently unused internally, but could be used for partition tables or something
|
|
100
136
|
*/
|
|
101
|
-
|
|
137
|
+
readonly id: number;
|
|
138
|
+
/**
|
|
139
|
+
* The name for this file system.
|
|
140
|
+
* For example, tmpfs for an in memory one
|
|
141
|
+
*/
|
|
142
|
+
readonly name: string;
|
|
143
|
+
label?: string;
|
|
102
144
|
/**
|
|
103
|
-
*
|
|
104
|
-
* Only affects async things.
|
|
145
|
+
* The last place this file system was mounted
|
|
105
146
|
* @internal @protected
|
|
106
147
|
*/
|
|
107
|
-
|
|
108
|
-
|
|
148
|
+
_mountPoint?: string;
|
|
149
|
+
/**
|
|
150
|
+
* @see FileSystemAttributes
|
|
151
|
+
*/
|
|
152
|
+
readonly attributes: ConstMap<FileSystemAttributes>;
|
|
153
|
+
constructor(
|
|
154
|
+
/**
|
|
155
|
+
* A unique ID for this kind of file system.
|
|
156
|
+
* Currently unused internally, but could be used for partition tables or something
|
|
157
|
+
*/
|
|
158
|
+
id: number,
|
|
159
|
+
/**
|
|
160
|
+
* The name for this file system.
|
|
161
|
+
* For example, tmpfs for an in memory one
|
|
162
|
+
*/
|
|
163
|
+
name: string);
|
|
164
|
+
toString(): string;
|
|
165
|
+
/**
|
|
166
|
+
* Default implementation.
|
|
167
|
+
* @todo Implement
|
|
168
|
+
* @experimental
|
|
169
|
+
*/
|
|
170
|
+
usage(): UsageInfo;
|
|
171
|
+
/**
|
|
172
|
+
* Get metadata about the current file system
|
|
173
|
+
* @deprecated
|
|
174
|
+
*/
|
|
175
|
+
metadata(): FileSystemMetadata;
|
|
109
176
|
ready(): Promise<void>;
|
|
110
177
|
abstract rename(oldPath: string, newPath: string): Promise<void>;
|
|
111
178
|
abstract renameSync(oldPath: string, newPath: string): void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a consistent and easy to use internal API.
|
|
3
|
+
* Default implementations for `exists` and `existsSync` are included.
|
|
4
|
+
* If you are extending this class, note that every path is an absolute path and all arguments are present.
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export class FileSystem {
|
|
8
|
+
constructor(
|
|
9
|
+
/**
|
|
10
|
+
* A unique ID for this kind of file system.
|
|
11
|
+
* Currently unused internally, but could be used for partition tables or something
|
|
12
|
+
*/
|
|
13
|
+
id,
|
|
14
|
+
/**
|
|
15
|
+
* The name for this file system.
|
|
16
|
+
* For example, tmpfs for an in memory one
|
|
17
|
+
*/
|
|
18
|
+
name) {
|
|
19
|
+
this.id = id;
|
|
20
|
+
this.name = name;
|
|
21
|
+
/**
|
|
22
|
+
* @see FileSystemAttributes
|
|
23
|
+
*/
|
|
24
|
+
this.attributes = new Map();
|
|
25
|
+
}
|
|
26
|
+
toString() {
|
|
27
|
+
var _a;
|
|
28
|
+
return `${this.name} ${(_a = this.label) !== null && _a !== void 0 ? _a : ''} (${this._mountPoint ? 'mounted on ' + this._mountPoint : 'unmounted'})`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Default implementation.
|
|
32
|
+
* @todo Implement
|
|
33
|
+
* @experimental
|
|
34
|
+
*/
|
|
35
|
+
usage() {
|
|
36
|
+
return {
|
|
37
|
+
totalSpace: 0,
|
|
38
|
+
freeSpace: 0,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/* node:coverage disable */
|
|
42
|
+
/**
|
|
43
|
+
* Get metadata about the current file system
|
|
44
|
+
* @deprecated
|
|
45
|
+
*/
|
|
46
|
+
metadata() {
|
|
47
|
+
return {
|
|
48
|
+
...this.usage(),
|
|
49
|
+
name: this.name,
|
|
50
|
+
readonly: this.attributes.has('no_write'),
|
|
51
|
+
noResizableBuffers: this.attributes.has('no_buffer_resize'),
|
|
52
|
+
noAsyncCache: this.attributes.has('no_async'),
|
|
53
|
+
features: Array.from(this.attributes.keys()),
|
|
54
|
+
type: this.id,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/* node:coverage enable */
|
|
58
|
+
async ready() { }
|
|
59
|
+
/**
|
|
60
|
+
* Test whether or not `path` exists.
|
|
61
|
+
*/
|
|
62
|
+
async exists(path) {
|
|
63
|
+
try {
|
|
64
|
+
await this.stat(path);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
return e.code != 'ENOENT';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Test whether or not `path` exists.
|
|
73
|
+
*/
|
|
74
|
+
existsSync(path) {
|
|
75
|
+
try {
|
|
76
|
+
this.statSync(path);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return e.code != 'ENOENT';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './credentials.js';
|
|
2
|
+
export * from './devices.js';
|
|
3
|
+
export * from './error.js';
|
|
4
|
+
export * from './file_index.js';
|
|
5
|
+
export * from './file.js';
|
|
6
|
+
export * from './filesystem.js';
|
|
7
|
+
export * from './index_fs.js';
|
|
8
|
+
export * from './inode.js';
|
|
9
|
+
export * as log from './log.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './credentials.js';
|
|
2
|
+
export * from './devices.js';
|
|
3
|
+
export * from './error.js';
|
|
4
|
+
export * from './file_index.js';
|
|
5
|
+
export * from './file.js';
|
|
6
|
+
export * from './filesystem.js';
|
|
7
|
+
export * from './index_fs.js';
|
|
8
|
+
export * from './inode.js';
|
|
9
|
+
export * as log from './log.js';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Stats } from '../stats.js';
|
|
2
|
+
import { type File } from './file.js';
|
|
3
|
+
import { Index } from './file_index.js';
|
|
4
|
+
import { FileSystem, type CreationOptions, type PureCreationOptions } from './filesystem.js';
|
|
5
|
+
import { Inode, type InodeLike } from './inode.js';
|
|
6
|
+
/**
|
|
7
|
+
* Uses an `Index` for metadata.
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class IndexFS extends FileSystem {
|
|
10
|
+
readonly index: Index;
|
|
11
|
+
constructor(id: number, name: string, index?: Index);
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated
|
|
14
|
+
*/
|
|
15
|
+
reloadFiles(): never;
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated
|
|
18
|
+
*/
|
|
19
|
+
reloadFilesSync(): never;
|
|
20
|
+
/**
|
|
21
|
+
* Finds all the paths in the index that need to be moved for a rename
|
|
22
|
+
*/
|
|
23
|
+
private pathsForRename;
|
|
24
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
25
|
+
renameSync(oldPath: string, newPath: string): void;
|
|
26
|
+
stat(path: string): Promise<Stats>;
|
|
27
|
+
statSync(path: string): Stats;
|
|
28
|
+
openFile(path: string, flag: string): Promise<File>;
|
|
29
|
+
openFileSync(path: string, flag: string): File;
|
|
30
|
+
protected _remove(path: string, isUnlink: boolean): void;
|
|
31
|
+
protected abstract remove(path: string): Promise<void>;
|
|
32
|
+
protected abstract removeSync(path: string): void;
|
|
33
|
+
unlink(path: string): Promise<void>;
|
|
34
|
+
unlinkSync(path: string): void;
|
|
35
|
+
rmdir(path: string): Promise<void>;
|
|
36
|
+
rmdirSync(path: string): void;
|
|
37
|
+
protected create(path: string, options: PureCreationOptions): Inode;
|
|
38
|
+
createFile(path: string, flag: string, mode: number, options: CreationOptions): Promise<File>;
|
|
39
|
+
createFileSync(path: string, flag: string, mode: number, options: CreationOptions): File;
|
|
40
|
+
mkdir(path: string, mode: number, options: CreationOptions): Promise<void>;
|
|
41
|
+
mkdirSync(path: string, mode: number, options: CreationOptions): void;
|
|
42
|
+
link(target: string, link: string): Promise<void>;
|
|
43
|
+
linkSync(target: string, link: string): void;
|
|
44
|
+
readdir(path: string): Promise<string[]>;
|
|
45
|
+
readdirSync(path: string): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Optional hook for implementations to support updating metadata
|
|
48
|
+
*/
|
|
49
|
+
protected syncMetadata?(path: string, metadata: Readonly<InodeLike>): Promise<void>;
|
|
50
|
+
sync(path: string, data?: Uint8Array, stats?: Readonly<InodeLike>): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Optional hook for implementations to support updating metadata
|
|
53
|
+
*/
|
|
54
|
+
protected syncMetadataSync?(path: string, metadata: Readonly<InodeLike>): void;
|
|
55
|
+
syncSync(path: string, data?: Uint8Array, stats?: Readonly<InodeLike>): void;
|
|
56
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
2
|
+
import { _throw } from 'utilium';
|
|
3
|
+
import { Stats } from '../stats.js';
|
|
4
|
+
import { S_IFDIR, S_IFMT, S_IFREG, S_ISGID, S_ISUID } from '../vfs/constants.js';
|
|
5
|
+
import { dirname, join, relative } from '../vfs/path.js';
|
|
6
|
+
import { ErrnoError } from './error.js';
|
|
7
|
+
import { LazyFile } from './file.js';
|
|
8
|
+
import { Index } from './file_index.js';
|
|
9
|
+
import { FileSystem } from './filesystem.js';
|
|
10
|
+
import { Inode } from './inode.js';
|
|
11
|
+
/**
|
|
12
|
+
* Uses an `Index` for metadata.
|
|
13
|
+
*/
|
|
14
|
+
export class IndexFS extends FileSystem {
|
|
15
|
+
constructor(id, name, index = new Index()) {
|
|
16
|
+
super(id, name);
|
|
17
|
+
this.index = index;
|
|
18
|
+
}
|
|
19
|
+
/* node:coverage disable */
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated
|
|
22
|
+
*/
|
|
23
|
+
reloadFiles() {
|
|
24
|
+
throw ErrnoError.With('ENOTSUP');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated
|
|
28
|
+
*/
|
|
29
|
+
reloadFilesSync() {
|
|
30
|
+
throw ErrnoError.With('ENOTSUP');
|
|
31
|
+
}
|
|
32
|
+
/* node:coverage enable */
|
|
33
|
+
/**
|
|
34
|
+
* Finds all the paths in the index that need to be moved for a rename
|
|
35
|
+
*/
|
|
36
|
+
pathsForRename(oldPath, newPath) {
|
|
37
|
+
if (newPath === oldPath)
|
|
38
|
+
return [];
|
|
39
|
+
if (!this.index.has(oldPath))
|
|
40
|
+
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
41
|
+
if ((dirname(newPath) + '/').startsWith(oldPath + '/'))
|
|
42
|
+
throw ErrnoError.With('EBUSY', dirname(oldPath), 'rename');
|
|
43
|
+
const toRename = [];
|
|
44
|
+
for (const [from, inode] of this.index.entries()) {
|
|
45
|
+
const rel = relative(oldPath, from);
|
|
46
|
+
if (rel.startsWith('..'))
|
|
47
|
+
continue;
|
|
48
|
+
let to = join(newPath, rel);
|
|
49
|
+
if (to.endsWith('/'))
|
|
50
|
+
to = to.slice(0, -1);
|
|
51
|
+
toRename.push({ from, to, inode });
|
|
52
|
+
}
|
|
53
|
+
return toRename;
|
|
54
|
+
}
|
|
55
|
+
async rename(oldPath, newPath) {
|
|
56
|
+
for (const { from, to, inode } of this.pathsForRename(oldPath, newPath)) {
|
|
57
|
+
const data = new Uint8Array(inode.size);
|
|
58
|
+
await this.read(from, data, 0, inode.size);
|
|
59
|
+
this.index.delete(from);
|
|
60
|
+
this.index.set(to, inode);
|
|
61
|
+
await this.write(to, data, 0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
renameSync(oldPath, newPath) {
|
|
65
|
+
for (const { from, to, inode } of this.pathsForRename(oldPath, newPath)) {
|
|
66
|
+
const data = new Uint8Array(inode.size);
|
|
67
|
+
this.readSync(from, data, 0, inode.size);
|
|
68
|
+
this.index.delete(from);
|
|
69
|
+
this.index.set(to, inode);
|
|
70
|
+
this.writeSync(to, data, 0);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async stat(path) {
|
|
74
|
+
const inode = this.index.get(path);
|
|
75
|
+
if (!inode)
|
|
76
|
+
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
77
|
+
return new Stats(inode);
|
|
78
|
+
}
|
|
79
|
+
statSync(path) {
|
|
80
|
+
const inode = this.index.get(path);
|
|
81
|
+
if (!inode)
|
|
82
|
+
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
83
|
+
return new Stats(inode);
|
|
84
|
+
}
|
|
85
|
+
async openFile(path, flag) {
|
|
86
|
+
var _a;
|
|
87
|
+
const stats = (_a = this.index.get(path)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', path, 'openFile'));
|
|
88
|
+
return new LazyFile(this, path, flag, stats);
|
|
89
|
+
}
|
|
90
|
+
openFileSync(path, flag) {
|
|
91
|
+
var _a;
|
|
92
|
+
const stats = (_a = this.index.get(path)) !== null && _a !== void 0 ? _a : _throw(ErrnoError.With('ENOENT', path, 'openFile'));
|
|
93
|
+
return new LazyFile(this, path, flag, stats);
|
|
94
|
+
}
|
|
95
|
+
_remove(path, isUnlink) {
|
|
96
|
+
const syscall = isUnlink ? 'unlink' : 'rmdir';
|
|
97
|
+
const inode = this.index.get(path);
|
|
98
|
+
if (!inode)
|
|
99
|
+
throw ErrnoError.With('ENOENT', path, syscall);
|
|
100
|
+
const isDir = (inode.mode & S_IFMT) == S_IFDIR;
|
|
101
|
+
if (!isDir && !isUnlink)
|
|
102
|
+
throw ErrnoError.With('ENOTDIR', path, syscall);
|
|
103
|
+
if (isDir && isUnlink)
|
|
104
|
+
throw ErrnoError.With('EISDIR', path, syscall);
|
|
105
|
+
this.index.delete(path);
|
|
106
|
+
}
|
|
107
|
+
async unlink(path) {
|
|
108
|
+
this._remove(path, true);
|
|
109
|
+
await this.remove(path);
|
|
110
|
+
}
|
|
111
|
+
unlinkSync(path) {
|
|
112
|
+
this._remove(path, true);
|
|
113
|
+
this.removeSync(path);
|
|
114
|
+
}
|
|
115
|
+
async rmdir(path) {
|
|
116
|
+
this._remove(path, false);
|
|
117
|
+
await this.remove(path);
|
|
118
|
+
}
|
|
119
|
+
rmdirSync(path) {
|
|
120
|
+
this._remove(path, false);
|
|
121
|
+
this.removeSync(path);
|
|
122
|
+
}
|
|
123
|
+
create(path, options) {
|
|
124
|
+
const syscall = (options.mode & S_IFMT) == S_IFDIR ? 'mkdir' : 'createFile';
|
|
125
|
+
if (this.index.has(path))
|
|
126
|
+
throw ErrnoError.With('EEXIST', path, syscall);
|
|
127
|
+
const parent = this.index.get(dirname(path));
|
|
128
|
+
if (!parent)
|
|
129
|
+
throw ErrnoError.With('ENOENT', dirname(path), syscall);
|
|
130
|
+
const id = this.index._alloc();
|
|
131
|
+
const inode = new Inode({
|
|
132
|
+
ino: id,
|
|
133
|
+
data: id + 1,
|
|
134
|
+
mode: options.mode,
|
|
135
|
+
size: 0,
|
|
136
|
+
uid: parent.mode & S_ISUID ? parent.uid : options.uid,
|
|
137
|
+
gid: parent.mode & S_ISGID ? parent.gid : options.gid,
|
|
138
|
+
});
|
|
139
|
+
this.index.set(path, inode);
|
|
140
|
+
return inode;
|
|
141
|
+
}
|
|
142
|
+
async createFile(path, flag, mode, options) {
|
|
143
|
+
const node = this.create(path, { mode: mode | S_IFREG, ...options });
|
|
144
|
+
return new LazyFile(this, path, flag, node.toStats());
|
|
145
|
+
}
|
|
146
|
+
createFileSync(path, flag, mode, options) {
|
|
147
|
+
const node = this.create(path, { mode: mode | S_IFREG, ...options });
|
|
148
|
+
return new LazyFile(this, path, flag, node.toStats());
|
|
149
|
+
}
|
|
150
|
+
async mkdir(path, mode, options) {
|
|
151
|
+
this.create(path, { mode: mode | S_IFDIR, ...options });
|
|
152
|
+
}
|
|
153
|
+
mkdirSync(path, mode, options) {
|
|
154
|
+
this.create(path, { mode: mode | S_IFDIR, ...options });
|
|
155
|
+
}
|
|
156
|
+
link(target, link) {
|
|
157
|
+
throw ErrnoError.With('ENOSYS', link, 'link');
|
|
158
|
+
}
|
|
159
|
+
linkSync(target, link) {
|
|
160
|
+
throw ErrnoError.With('ENOSYS', link, 'link');
|
|
161
|
+
}
|
|
162
|
+
async readdir(path) {
|
|
163
|
+
return Object.keys(this.index.directoryEntries(path));
|
|
164
|
+
}
|
|
165
|
+
readdirSync(path) {
|
|
166
|
+
return Object.keys(this.index.directoryEntries(path));
|
|
167
|
+
}
|
|
168
|
+
async sync(path, data, stats) {
|
|
169
|
+
var _a;
|
|
170
|
+
const inode = this.index.get(path);
|
|
171
|
+
if (!inode)
|
|
172
|
+
throw ErrnoError.With('ENOENT', path, 'sync');
|
|
173
|
+
if (inode.update(stats))
|
|
174
|
+
await ((_a = this.syncMetadata) === null || _a === void 0 ? void 0 : _a.call(this, path, stats));
|
|
175
|
+
if (data)
|
|
176
|
+
await this.write(path, data, 0);
|
|
177
|
+
}
|
|
178
|
+
syncSync(path, data, stats) {
|
|
179
|
+
var _a;
|
|
180
|
+
const inode = this.index.get(path);
|
|
181
|
+
if (!inode)
|
|
182
|
+
throw ErrnoError.With('ENOENT', path, 'sync');
|
|
183
|
+
if (inode.update(stats))
|
|
184
|
+
(_a = this.syncMetadataSync) === null || _a === void 0 ? void 0 : _a.call(this, path, stats);
|
|
185
|
+
if (data)
|
|
186
|
+
this.writeSync(path, data, 0);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Stats, type StatsLike } from '
|
|
1
|
+
import { Stats, type StatsLike } from '../stats.js';
|
|
2
2
|
/**
|
|
3
3
|
* Root inode
|
|
4
4
|
* @hidden
|
|
@@ -39,6 +39,7 @@ export declare class Inode implements InodeLike {
|
|
|
39
39
|
flags: number;
|
|
40
40
|
/** For future use */
|
|
41
41
|
__padding: number;
|
|
42
|
+
toString(): string;
|
|
42
43
|
toJSON(): InodeLike;
|
|
43
44
|
/**
|
|
44
45
|
* Handy function that converts the Inode to a Node Stats object.
|
|
@@ -56,3 +57,7 @@ export declare class Inode implements InodeLike {
|
|
|
56
57
|
*/
|
|
57
58
|
update(data?: Partial<Readonly<InodeLike>>): boolean;
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* @internal @hidden
|
|
62
|
+
*/
|
|
63
|
+
export declare const __inode_sz: number;
|