@zenfs/core 0.2.3 → 0.3.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/FileIndex.d.ts +117 -34
- package/dist/FileIndex.js +166 -84
- package/dist/backends/AsyncMirror.d.ts +28 -2
- package/dist/backends/AsyncMirror.js +3 -11
- package/dist/backends/AsyncStore.d.ts +16 -2
- package/dist/backends/AsyncStore.js +3 -2
- package/dist/backends/Locked.d.ts +1 -1
- package/dist/backends/Locked.js +1 -1
- package/dist/backends/Overlay.d.ts +1 -1
- package/dist/backends/Overlay.js +1 -1
- package/dist/backends/SyncStore.d.ts +29 -2
- package/dist/backends/SyncStore.js +3 -2
- package/dist/browser.min.js +5 -5
- package/dist/browser.min.js.map +3 -3
- package/dist/file.d.ts +2 -2
- package/dist/file.js +2 -2
- package/dist/filesystem.d.ts +27 -15
- package/dist/filesystem.js +133 -118
- package/package.json +1 -1
- package/readme.md +48 -70
- package/dist/backends/FolderAdapter.d.ts +0 -52
- package/dist/backends/FolderAdapter.js +0 -173
- package/dist/backends/OverlayFS.d.ts +0 -112
- package/dist/backends/OverlayFS.js +0 -542
- package/dist/emulation/fs.d.ts +0 -6
- package/dist/emulation/fs.js +0 -4
package/dist/FileIndex.d.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
|
+
import type { Cred } from './cred.js';
|
|
2
|
+
import { NoSyncFile, type FileFlag } from './file.js';
|
|
3
|
+
import { FileSystem } from './filesystem.js';
|
|
1
4
|
import { Stats } from './stats.js';
|
|
2
|
-
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export type ListingTree = {
|
|
3
9
|
[key: string]: ListingTree | null;
|
|
4
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export interface ListingQueueNode<T> {
|
|
15
|
+
pwd: string;
|
|
16
|
+
tree: ListingTree;
|
|
17
|
+
parent: IndexDirInode<T>;
|
|
18
|
+
}
|
|
5
19
|
/**
|
|
6
20
|
* A simple class for storing a filesystem index. Assumes that all paths passed
|
|
7
21
|
* to it are *absolute* paths.
|
|
8
22
|
*
|
|
9
23
|
* Can be used as a partial or a full index, although care must be taken if used
|
|
10
24
|
* for the former purpose, especially when directories are concerned.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
11
27
|
*/
|
|
12
28
|
export declare class FileIndex<T> {
|
|
13
29
|
/**
|
|
@@ -15,16 +31,13 @@ export declare class FileIndex<T> {
|
|
|
15
31
|
* @param listing Directory listing generated by tools
|
|
16
32
|
* @return A new FileIndex object.
|
|
17
33
|
*/
|
|
18
|
-
static
|
|
34
|
+
static FromListing<T>(listing: ListingTree): FileIndex<T>;
|
|
19
35
|
protected _index: Map<string, IndexDirInode<T>>;
|
|
20
36
|
/**
|
|
21
37
|
* Constructs a new FileIndex.
|
|
22
38
|
*/
|
|
23
39
|
constructor();
|
|
24
|
-
|
|
25
|
-
* Runs the given function over all files in the index.
|
|
26
|
-
*/
|
|
27
|
-
forEachFile(cb: (file?: T) => void): void;
|
|
40
|
+
files(): IndexFileInode<T>[];
|
|
28
41
|
/**
|
|
29
42
|
* Adds the given absolute path to the index if it is not already in the index.
|
|
30
43
|
* Creates any needed parent directories.
|
|
@@ -37,27 +50,25 @@ export declare class FileIndex<T> {
|
|
|
37
50
|
* @todo If adding fails and implicitly creates directories, we do not clean up
|
|
38
51
|
* the new empty directories.
|
|
39
52
|
*/
|
|
40
|
-
|
|
53
|
+
add(path: string, inode: IndexInode<T>): boolean;
|
|
41
54
|
/**
|
|
42
55
|
* Adds the given absolute path to the index if it is not already in the index.
|
|
43
56
|
* The path is added without special treatment (no joining of adjacent separators, etc).
|
|
44
57
|
* Creates any needed parent directories.
|
|
45
58
|
* @param path The path to add to the index.
|
|
46
|
-
* @param inode The inode for the
|
|
47
|
-
* path to add.
|
|
59
|
+
* @param inode The inode for the path to add.
|
|
48
60
|
* @return 'True' if it was added or already exists, 'false' if there
|
|
49
61
|
* was an issue adding it (e.g. item in path is a file, item exists but is
|
|
50
62
|
* different).
|
|
51
|
-
* @todo If adding fails and implicitly creates directories, we do not clean up
|
|
52
|
-
* the new empty directories.
|
|
63
|
+
* @todo If adding fails and implicitly creates directories, we do not clean up the new empty directories.
|
|
53
64
|
*/
|
|
54
|
-
|
|
65
|
+
addFast(path: string, inode: IndexInode<T>): boolean;
|
|
55
66
|
/**
|
|
56
67
|
* Removes the given path. Can be a file or a directory.
|
|
57
68
|
* @return The removed item,
|
|
58
69
|
* or null if it did not exist.
|
|
59
70
|
*/
|
|
60
|
-
|
|
71
|
+
remove(path: string): IndexInode<T> | null;
|
|
61
72
|
/**
|
|
62
73
|
* Retrieves the directory listing of the given path.
|
|
63
74
|
* @return An array of files in the given path, or 'null' if it does not exist.
|
|
@@ -67,11 +78,7 @@ export declare class FileIndex<T> {
|
|
|
67
78
|
* Returns the inode of the given item.
|
|
68
79
|
* @return Returns null if the item does not exist.
|
|
69
80
|
*/
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Split into a (directory path, item name) pair
|
|
73
|
-
*/
|
|
74
|
-
protected splitPath(p: string): string[];
|
|
81
|
+
get(path: string): IndexInode<T> | null;
|
|
75
82
|
}
|
|
76
83
|
/**
|
|
77
84
|
* Generic interface for file/directory inodes.
|
|
@@ -80,8 +87,14 @@ export declare class FileIndex<T> {
|
|
|
80
87
|
export declare abstract class IndexInode<T> {
|
|
81
88
|
data?: T;
|
|
82
89
|
constructor(data?: T);
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Whether this inode is for a file
|
|
92
|
+
*/
|
|
93
|
+
abstract isFile(): this is IndexFileInode<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Whether this inode is for a directory
|
|
96
|
+
*/
|
|
97
|
+
abstract isDirectory(): this is IndexDirInode<T>;
|
|
85
98
|
abstract toStats(): Stats;
|
|
86
99
|
}
|
|
87
100
|
/**
|
|
@@ -89,7 +102,7 @@ export declare abstract class IndexInode<T> {
|
|
|
89
102
|
*/
|
|
90
103
|
export declare class IndexFileInode<T> extends IndexInode<T> {
|
|
91
104
|
isFile(): boolean;
|
|
92
|
-
|
|
105
|
+
isDirectory(): boolean;
|
|
93
106
|
toStats(): Stats;
|
|
94
107
|
}
|
|
95
108
|
/**
|
|
@@ -101,7 +114,7 @@ export declare class IndexDirInode<T> extends IndexInode<T> {
|
|
|
101
114
|
*/
|
|
102
115
|
_listing: Map<string, IndexInode<T>>;
|
|
103
116
|
isFile(): boolean;
|
|
104
|
-
|
|
117
|
+
isDirectory(): boolean;
|
|
105
118
|
/**
|
|
106
119
|
* Return a Stats object for this inode.
|
|
107
120
|
* @todo Should probably remove this at some point. This isn't the responsibility of the FileIndex.
|
|
@@ -120,18 +133,18 @@ export declare class IndexDirInode<T> extends IndexInode<T> {
|
|
|
120
133
|
get listing(): string[];
|
|
121
134
|
/**
|
|
122
135
|
* Returns the inode for the indicated item, or null if it does not exist.
|
|
123
|
-
* @param
|
|
136
|
+
* @param path Name of item in this directory.
|
|
124
137
|
*/
|
|
125
|
-
get(
|
|
138
|
+
get(path: string): IndexInode<T> | null;
|
|
126
139
|
/**
|
|
127
140
|
* Add the given item to the directory listing. Note that the given inode is
|
|
128
141
|
* not copied, and will be mutated by the DirInode if it is a DirInode.
|
|
129
|
-
* @param
|
|
142
|
+
* @param path Item name to add to the directory listing.
|
|
130
143
|
* @param inode The inode for the
|
|
131
144
|
* item to add to the directory inode.
|
|
132
145
|
* @return True if it was added, false if it already existed.
|
|
133
146
|
*/
|
|
134
|
-
add(
|
|
147
|
+
add(path: string, inode: IndexInode<T>): boolean;
|
|
135
148
|
/**
|
|
136
149
|
* Removes the given item from the directory listing.
|
|
137
150
|
* @param p Name of item to remove from the directory listing.
|
|
@@ -140,12 +153,82 @@ export declare class IndexDirInode<T> extends IndexInode<T> {
|
|
|
140
153
|
*/
|
|
141
154
|
remove(p: string): IndexInode<T> | null;
|
|
142
155
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
declare const FileIndexFS_base: (abstract new (...args: any[]) => {
|
|
157
|
+
readonly metadata: import("./filesystem.js").FileSystemMetadata;
|
|
158
|
+
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
159
|
+
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
160
|
+
createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<import("./file.js").File>;
|
|
161
|
+
createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): import("./file.js").File;
|
|
162
|
+
unlink(path: string, cred: Cred): Promise<void>;
|
|
163
|
+
unlinkSync(path: string, cred: Cred): void;
|
|
164
|
+
rmdir(path: string, cred: Cred): Promise<void>;
|
|
165
|
+
rmdirSync(path: string, cred: Cred): void;
|
|
166
|
+
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
|
|
167
|
+
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
168
|
+
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
|
|
169
|
+
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
170
|
+
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
|
|
171
|
+
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
172
|
+
}) & typeof FileSystem;
|
|
173
|
+
export declare abstract class FileIndexFS<TIndex> extends FileIndexFS_base {
|
|
174
|
+
protected _index: FileIndex<TIndex>;
|
|
175
|
+
constructor(index: ListingTree);
|
|
176
|
+
stat(path: string): Promise<Stats>;
|
|
177
|
+
statSync(path: string): Stats;
|
|
178
|
+
openFile(path: string, flag: FileFlag, cred: Cred): Promise<NoSyncFile<this>>;
|
|
179
|
+
openFileSync(path: string, flag: FileFlag, cred: Cred): NoSyncFile<this>;
|
|
180
|
+
readdir(path: string): Promise<string[]>;
|
|
181
|
+
readdirSync(path: string): string[];
|
|
182
|
+
protected abstract statFileInode(inode: IndexFileInode<TIndex>): Promise<Stats>;
|
|
183
|
+
protected abstract fileForFileInode(inode: IndexFileInode<TIndex>): Promise<NoSyncFile<this>>;
|
|
184
|
+
protected abstract statFileInodeSync(inode: IndexFileInode<TIndex>): Stats;
|
|
185
|
+
protected abstract fileForFileInodeSync(inode: IndexFileInode<TIndex>): NoSyncFile<this>;
|
|
186
|
+
}
|
|
187
|
+
declare const SyncFileIndexFS_base: (abstract new (...args: any[]) => {
|
|
188
|
+
readonly metadata: import("./filesystem.js").FileSystemMetadata;
|
|
189
|
+
ready(): Promise<any>;
|
|
190
|
+
exists(path: string, cred: Cred): Promise<boolean>;
|
|
191
|
+
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
192
|
+
stat(path: string, cred: Cred): Promise<Stats>;
|
|
193
|
+
createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<import("./file.js").File>;
|
|
194
|
+
openFile(path: string, flag: FileFlag, cred: Cred): Promise<import("./file.js").File>;
|
|
195
|
+
unlink(path: string, cred: Cred): Promise<void>;
|
|
196
|
+
rmdir(path: string, cred: Cred): Promise<void>;
|
|
197
|
+
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
|
|
198
|
+
readdir(path: string, cred: Cred): Promise<string[]>;
|
|
199
|
+
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
|
|
200
|
+
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
|
|
201
|
+
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
202
|
+
statSync(path: string, cred: Cred): Stats;
|
|
203
|
+
openFileSync(path: string, flag: FileFlag, cred: Cred): import("./file.js").File;
|
|
204
|
+
createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): import("./file.js").File;
|
|
205
|
+
unlinkSync(path: string, cred: Cred): void;
|
|
206
|
+
rmdirSync(path: string, cred: Cred): void;
|
|
207
|
+
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
208
|
+
readdirSync(path: string, cred: Cred): string[];
|
|
209
|
+
existsSync(path: string, cred: Cred): boolean;
|
|
210
|
+
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
211
|
+
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
212
|
+
}) & (abstract new (index: ListingTree) => FileIndexFS<unknown>);
|
|
213
|
+
export declare abstract class SyncFileIndexFS<TIndex> extends SyncFileIndexFS_base {
|
|
214
|
+
protected statFileInode(inode: IndexFileInode<TIndex>): Promise<Stats>;
|
|
215
|
+
protected fileForFileInode(inode: IndexFileInode<TIndex>): Promise<NoSyncFile<this>>;
|
|
216
|
+
}
|
|
217
|
+
declare const AsyncFileIndexFS_base: (abstract new (...args: any[]) => {
|
|
218
|
+
readonly metadata: import("./filesystem.js").FileSystemMetadata;
|
|
219
|
+
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
220
|
+
statSync(path: string, cred: Cred): Stats;
|
|
221
|
+
createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): import("./file.js").File;
|
|
222
|
+
openFileSync(path: string, flag: FileFlag, cred: Cred): import("./file.js").File;
|
|
223
|
+
unlinkSync(path: string, cred: Cred): void;
|
|
224
|
+
rmdirSync(path: string, cred: Cred): void;
|
|
225
|
+
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
226
|
+
readdirSync(path: string, cred: Cred): string[];
|
|
227
|
+
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
228
|
+
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
229
|
+
}) & (abstract new (index: ListingTree) => FileIndexFS<unknown>);
|
|
230
|
+
export declare abstract class AsyncFileIndexFS<TIndex> extends AsyncFileIndexFS_base {
|
|
231
|
+
protected statFileInodeSync(): Stats;
|
|
232
|
+
protected fileForFileInodeSync(): NoSyncFile<this>;
|
|
233
|
+
}
|
|
151
234
|
export {};
|
package/dist/FileIndex.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { ApiError, ErrorCode } from './ApiError.js';
|
|
2
|
+
import { basename, dirname, join } from './emulation/path.js';
|
|
3
|
+
import { NoSyncFile } from './file.js';
|
|
4
|
+
import { FileSystem, Sync, Async, Readonly } from './filesystem.js';
|
|
5
|
+
import { FileType, Stats } from './stats.js';
|
|
3
6
|
/**
|
|
4
7
|
* A simple class for storing a filesystem index. Assumes that all paths passed
|
|
5
8
|
* to it are *absolute* paths.
|
|
6
9
|
*
|
|
7
10
|
* Can be used as a partial or a full index, although care must be taken if used
|
|
8
11
|
* for the former purpose, especially when directories are concerned.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
9
14
|
*/
|
|
10
15
|
export class FileIndex {
|
|
11
16
|
/**
|
|
@@ -13,7 +18,7 @@ export class FileIndex {
|
|
|
13
18
|
* @param listing Directory listing generated by tools
|
|
14
19
|
* @return A new FileIndex object.
|
|
15
20
|
*/
|
|
16
|
-
static
|
|
21
|
+
static FromListing(listing) {
|
|
17
22
|
const index = new FileIndex();
|
|
18
23
|
// Add a root DirNode.
|
|
19
24
|
const rootInode = new IndexDirInode();
|
|
@@ -28,7 +33,7 @@ export class FileIndex {
|
|
|
28
33
|
}
|
|
29
34
|
const children = tree[node];
|
|
30
35
|
if (children) {
|
|
31
|
-
const path =
|
|
36
|
+
const path = pwd + '/' + node;
|
|
32
37
|
inode = new IndexDirInode();
|
|
33
38
|
index._index.set(path, inode);
|
|
34
39
|
queue.push({ pwd: path, tree: children, parent: inode });
|
|
@@ -54,21 +59,20 @@ export class FileIndex {
|
|
|
54
59
|
// _index is a single-level key,value store that maps *directory* paths to
|
|
55
60
|
// DirInodes. File information is only contained in DirInodes themselves.
|
|
56
61
|
// Create the root directory.
|
|
57
|
-
this.
|
|
62
|
+
this.add('/', new IndexDirInode());
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*/
|
|
62
|
-
forEachFile(cb) {
|
|
64
|
+
files() {
|
|
65
|
+
const files = [];
|
|
63
66
|
for (const dir of this._index.values()) {
|
|
64
67
|
for (const file of dir.listing) {
|
|
65
68
|
const item = dir.get(file);
|
|
66
69
|
if (!item?.isFile()) {
|
|
67
70
|
continue;
|
|
68
71
|
}
|
|
69
|
-
|
|
72
|
+
files.push(item);
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
return files;
|
|
72
76
|
}
|
|
73
77
|
/**
|
|
74
78
|
* Adds the given absolute path to the index if it is not already in the index.
|
|
@@ -82,38 +86,34 @@ export class FileIndex {
|
|
|
82
86
|
* @todo If adding fails and implicitly creates directories, we do not clean up
|
|
83
87
|
* the new empty directories.
|
|
84
88
|
*/
|
|
85
|
-
|
|
89
|
+
add(path, inode) {
|
|
86
90
|
if (!inode) {
|
|
87
91
|
throw new Error('Inode must be specified');
|
|
88
92
|
}
|
|
89
|
-
if (path
|
|
93
|
+
if (!path.startsWith('/')) {
|
|
90
94
|
throw new Error('Path must be absolute, got: ' + path);
|
|
91
95
|
}
|
|
92
96
|
// Check if it already exists.
|
|
93
97
|
if (this._index.has(path)) {
|
|
94
98
|
return this._index.get(path) === inode;
|
|
95
99
|
}
|
|
96
|
-
const
|
|
97
|
-
const dirpath = splitPath[0];
|
|
98
|
-
const itemname = splitPath[1];
|
|
100
|
+
const dirpath = dirname(path);
|
|
99
101
|
// Try to add to its parent directory first.
|
|
100
102
|
let parent = this._index.get(dirpath);
|
|
101
|
-
if (!parent && path
|
|
103
|
+
if (!parent && path != '/') {
|
|
102
104
|
// Create parent.
|
|
103
105
|
parent = new IndexDirInode();
|
|
104
|
-
if (!this.
|
|
106
|
+
if (!this.add(dirpath, parent)) {
|
|
105
107
|
return false;
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
|
-
// Add
|
|
109
|
-
if (path
|
|
110
|
-
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
110
|
+
// Add to parent.
|
|
111
|
+
if (path != '/' && !parent.add(basename(path), inode)) {
|
|
112
|
+
return false;
|
|
113
113
|
}
|
|
114
|
-
// If
|
|
115
|
-
if (
|
|
116
|
-
this._index
|
|
114
|
+
// If a directory, add to the index.
|
|
115
|
+
if (inode.isDirectory()) {
|
|
116
|
+
this._index.set(path, inode);
|
|
117
117
|
}
|
|
118
118
|
return true;
|
|
119
119
|
}
|
|
@@ -122,31 +122,28 @@ export class FileIndex {
|
|
|
122
122
|
* The path is added without special treatment (no joining of adjacent separators, etc).
|
|
123
123
|
* Creates any needed parent directories.
|
|
124
124
|
* @param path The path to add to the index.
|
|
125
|
-
* @param inode The inode for the
|
|
126
|
-
* path to add.
|
|
125
|
+
* @param inode The inode for the path to add.
|
|
127
126
|
* @return 'True' if it was added or already exists, 'false' if there
|
|
128
127
|
* was an issue adding it (e.g. item in path is a file, item exists but is
|
|
129
128
|
* different).
|
|
130
|
-
* @todo If adding fails and implicitly creates directories, we do not clean up
|
|
131
|
-
* the new empty directories.
|
|
129
|
+
* @todo If adding fails and implicitly creates directories, we do not clean up the new empty directories.
|
|
132
130
|
*/
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const itemName = path.substring(itemNameMark + 1);
|
|
131
|
+
addFast(path, inode) {
|
|
132
|
+
const parentPath = dirname(path);
|
|
133
|
+
const itemName = basename(path);
|
|
137
134
|
// Try to add to its parent directory first.
|
|
138
135
|
let parent = this._index.get(parentPath);
|
|
139
136
|
if (!parent) {
|
|
140
137
|
// Create parent.
|
|
141
138
|
parent = new IndexDirInode();
|
|
142
|
-
this.
|
|
139
|
+
this.addFast(parentPath, parent);
|
|
143
140
|
}
|
|
144
141
|
if (!parent.add(itemName, inode)) {
|
|
145
142
|
return false;
|
|
146
143
|
}
|
|
147
144
|
// If adding a directory, add to the index as well.
|
|
148
|
-
if (inode.
|
|
149
|
-
this._index
|
|
145
|
+
if (inode.isDirectory()) {
|
|
146
|
+
this._index.set(path, inode);
|
|
150
147
|
}
|
|
151
148
|
return true;
|
|
152
149
|
}
|
|
@@ -155,30 +152,28 @@ export class FileIndex {
|
|
|
155
152
|
* @return The removed item,
|
|
156
153
|
* or null if it did not exist.
|
|
157
154
|
*/
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const dirpath = splitPath[0];
|
|
161
|
-
const itemname = splitPath[1];
|
|
155
|
+
remove(path) {
|
|
156
|
+
const dirpath = dirname(path);
|
|
162
157
|
// Try to remove it from its parent directory first.
|
|
163
|
-
const parent = this._index
|
|
158
|
+
const parent = this._index.get(dirpath);
|
|
164
159
|
if (!parent) {
|
|
165
160
|
return;
|
|
166
161
|
}
|
|
167
|
-
// Remove
|
|
168
|
-
const inode = parent.remove(
|
|
162
|
+
// Remove from parent.
|
|
163
|
+
const inode = parent.remove(basename(path));
|
|
169
164
|
if (!inode) {
|
|
170
165
|
return;
|
|
171
166
|
}
|
|
172
|
-
|
|
173
|
-
if (!isIndexDirInode(inode)) {
|
|
167
|
+
if (!inode.isDirectory()) {
|
|
174
168
|
return inode;
|
|
175
169
|
}
|
|
170
|
+
// If a directory, remove from the index, and remove children.
|
|
176
171
|
const children = inode.listing;
|
|
177
172
|
for (const child of children) {
|
|
178
|
-
this.
|
|
173
|
+
this.remove(join(path, child));
|
|
179
174
|
}
|
|
180
175
|
// Remove the directory from the index, unless it's the root.
|
|
181
|
-
if (path
|
|
176
|
+
if (path != '/') {
|
|
182
177
|
this._index.delete(path);
|
|
183
178
|
}
|
|
184
179
|
}
|
|
@@ -193,23 +188,15 @@ export class FileIndex {
|
|
|
193
188
|
* Returns the inode of the given item.
|
|
194
189
|
* @return Returns null if the item does not exist.
|
|
195
190
|
*/
|
|
196
|
-
|
|
197
|
-
const
|
|
191
|
+
get(path) {
|
|
192
|
+
const dirpath = dirname(path);
|
|
198
193
|
// Retrieve from its parent directory.
|
|
199
194
|
const parent = this._index.get(dirpath);
|
|
200
195
|
// Root case
|
|
201
|
-
if (dirpath
|
|
196
|
+
if (dirpath == path) {
|
|
202
197
|
return parent;
|
|
203
198
|
}
|
|
204
|
-
return parent?.get(
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Split into a (directory path, item name) pair
|
|
208
|
-
*/
|
|
209
|
-
splitPath(p) {
|
|
210
|
-
const dirpath = path.dirname(p);
|
|
211
|
-
const itemname = p.slice(dirpath.length + (dirpath === '/' ? 0 : 1));
|
|
212
|
-
return [dirpath, itemname];
|
|
199
|
+
return parent?.get(basename(path));
|
|
213
200
|
}
|
|
214
201
|
}
|
|
215
202
|
/**
|
|
@@ -228,7 +215,7 @@ export class IndexFileInode extends IndexInode {
|
|
|
228
215
|
isFile() {
|
|
229
216
|
return true;
|
|
230
217
|
}
|
|
231
|
-
|
|
218
|
+
isDirectory() {
|
|
232
219
|
return false;
|
|
233
220
|
}
|
|
234
221
|
toStats() {
|
|
@@ -249,7 +236,7 @@ export class IndexDirInode extends IndexInode {
|
|
|
249
236
|
isFile() {
|
|
250
237
|
return false;
|
|
251
238
|
}
|
|
252
|
-
|
|
239
|
+
isDirectory() {
|
|
253
240
|
return true;
|
|
254
241
|
}
|
|
255
242
|
/**
|
|
@@ -272,29 +259,28 @@ export class IndexDirInode extends IndexInode {
|
|
|
272
259
|
* @return The directory listing for this directory.
|
|
273
260
|
*/
|
|
274
261
|
get listing() {
|
|
275
|
-
return
|
|
262
|
+
return [...this._listing.keys()];
|
|
276
263
|
}
|
|
277
264
|
/**
|
|
278
265
|
* Returns the inode for the indicated item, or null if it does not exist.
|
|
279
|
-
* @param
|
|
266
|
+
* @param path Name of item in this directory.
|
|
280
267
|
*/
|
|
281
|
-
get(
|
|
282
|
-
|
|
283
|
-
return item ? item : null;
|
|
268
|
+
get(path) {
|
|
269
|
+
return this._listing.get(path);
|
|
284
270
|
}
|
|
285
271
|
/**
|
|
286
272
|
* Add the given item to the directory listing. Note that the given inode is
|
|
287
273
|
* not copied, and will be mutated by the DirInode if it is a DirInode.
|
|
288
|
-
* @param
|
|
274
|
+
* @param path Item name to add to the directory listing.
|
|
289
275
|
* @param inode The inode for the
|
|
290
276
|
* item to add to the directory inode.
|
|
291
277
|
* @return True if it was added, false if it already existed.
|
|
292
278
|
*/
|
|
293
|
-
add(
|
|
294
|
-
if (
|
|
279
|
+
add(path, inode) {
|
|
280
|
+
if (this._listing.has(path)) {
|
|
295
281
|
return false;
|
|
296
282
|
}
|
|
297
|
-
this._listing
|
|
283
|
+
this._listing.set(path, inode);
|
|
298
284
|
return true;
|
|
299
285
|
}
|
|
300
286
|
/**
|
|
@@ -304,23 +290,119 @@ export class IndexDirInode extends IndexInode {
|
|
|
304
290
|
* removed, or null if the item did not exist.
|
|
305
291
|
*/
|
|
306
292
|
remove(p) {
|
|
307
|
-
const item = this._listing
|
|
308
|
-
if (item
|
|
309
|
-
return
|
|
293
|
+
const item = this._listing.get(p);
|
|
294
|
+
if (!item) {
|
|
295
|
+
return;
|
|
310
296
|
}
|
|
311
|
-
|
|
297
|
+
this._listing.delete(p);
|
|
312
298
|
return item;
|
|
313
299
|
}
|
|
314
300
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
301
|
+
export class FileIndexFS extends Readonly(FileSystem) {
|
|
302
|
+
constructor(index) {
|
|
303
|
+
super();
|
|
304
|
+
this._index = FileIndex.FromListing(index);
|
|
305
|
+
}
|
|
306
|
+
async stat(path) {
|
|
307
|
+
const inode = this._index.get(path);
|
|
308
|
+
if (!inode) {
|
|
309
|
+
throw ApiError.ENOENT(path);
|
|
310
|
+
}
|
|
311
|
+
if (inode.isDirectory()) {
|
|
312
|
+
return inode.stats;
|
|
313
|
+
}
|
|
314
|
+
if (inode.isFile()) {
|
|
315
|
+
return this.statFileInode(inode);
|
|
316
|
+
}
|
|
317
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid inode.');
|
|
318
|
+
}
|
|
319
|
+
statSync(path) {
|
|
320
|
+
const inode = this._index.get(path);
|
|
321
|
+
if (!inode) {
|
|
322
|
+
throw ApiError.ENOENT(path);
|
|
323
|
+
}
|
|
324
|
+
if (inode.isDirectory()) {
|
|
325
|
+
return inode.stats;
|
|
326
|
+
}
|
|
327
|
+
if (inode.isFile()) {
|
|
328
|
+
return this.statFileInodeSync(inode);
|
|
329
|
+
}
|
|
330
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid inode.');
|
|
331
|
+
}
|
|
332
|
+
async openFile(path, flag, cred) {
|
|
333
|
+
if (flag.isWriteable()) {
|
|
334
|
+
// You can't write to files on this file system.
|
|
335
|
+
throw new ApiError(ErrorCode.EPERM, path);
|
|
336
|
+
}
|
|
337
|
+
// Check if the path exists, and is a file.
|
|
338
|
+
const inode = this._index.get(path);
|
|
339
|
+
if (!inode) {
|
|
340
|
+
throw ApiError.ENOENT(path);
|
|
341
|
+
}
|
|
342
|
+
if (!inode.toStats().hasAccess(flag.mode, cred)) {
|
|
343
|
+
throw ApiError.EACCES(path);
|
|
344
|
+
}
|
|
345
|
+
if (inode.isDirectory()) {
|
|
346
|
+
const stats = inode.stats;
|
|
347
|
+
return new NoSyncFile(this, path, flag, stats, stats.fileData);
|
|
348
|
+
}
|
|
349
|
+
return this.fileForFileInode(inode);
|
|
350
|
+
}
|
|
351
|
+
openFileSync(path, flag, cred) {
|
|
352
|
+
if (flag.isWriteable()) {
|
|
353
|
+
// You can't write to files on this file system.
|
|
354
|
+
throw new ApiError(ErrorCode.EPERM, path);
|
|
355
|
+
}
|
|
356
|
+
// Check if the path exists, and is a file.
|
|
357
|
+
const inode = this._index.get(path);
|
|
358
|
+
if (!inode) {
|
|
359
|
+
throw ApiError.ENOENT(path);
|
|
360
|
+
}
|
|
361
|
+
if (!inode.toStats().hasAccess(flag.mode, cred)) {
|
|
362
|
+
throw ApiError.EACCES(path);
|
|
363
|
+
}
|
|
364
|
+
if (inode.isDirectory()) {
|
|
365
|
+
const stats = inode.stats;
|
|
366
|
+
return new NoSyncFile(this, path, flag, stats, stats.fileData);
|
|
367
|
+
}
|
|
368
|
+
return this.fileForFileInodeSync(inode);
|
|
369
|
+
}
|
|
370
|
+
async readdir(path) {
|
|
371
|
+
// Check if it exists.
|
|
372
|
+
const inode = this._index.get(path);
|
|
373
|
+
if (!inode) {
|
|
374
|
+
throw ApiError.ENOENT(path);
|
|
375
|
+
}
|
|
376
|
+
if (inode.isDirectory()) {
|
|
377
|
+
return inode.listing;
|
|
378
|
+
}
|
|
379
|
+
throw ApiError.ENOTDIR(path);
|
|
380
|
+
}
|
|
381
|
+
readdirSync(path) {
|
|
382
|
+
// Check if it exists.
|
|
383
|
+
const inode = this._index.get(path);
|
|
384
|
+
if (!inode) {
|
|
385
|
+
throw ApiError.ENOENT(path);
|
|
386
|
+
}
|
|
387
|
+
if (inode.isDirectory()) {
|
|
388
|
+
return inode.listing;
|
|
389
|
+
}
|
|
390
|
+
throw ApiError.ENOTDIR(path);
|
|
391
|
+
}
|
|
320
392
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
393
|
+
export class SyncFileIndexFS extends Sync((FileIndexFS)) {
|
|
394
|
+
async statFileInode(inode) {
|
|
395
|
+
return this.statFileInodeSync(inode);
|
|
396
|
+
}
|
|
397
|
+
async fileForFileInode(inode) {
|
|
398
|
+
return this.fileForFileInodeSync(inode);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
export class AsyncFileIndexFS extends Async((FileIndexFS)) {
|
|
402
|
+
statFileInodeSync() {
|
|
403
|
+
throw new ApiError(ErrorCode.ENOTSUP);
|
|
404
|
+
}
|
|
405
|
+
fileForFileInodeSync() {
|
|
406
|
+
throw new ApiError(ErrorCode.ENOTSUP);
|
|
407
|
+
}
|
|
326
408
|
}
|