@zenfs/core 0.2.3 → 0.3.0

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.
@@ -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
- type ListingTree = {
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 fromListing<T>(listing: ListingTree): FileIndex<T>;
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
- addPath(path: string, inode: IndexInode<T>): boolean;
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
- addPathFast(path: string, inode: IndexInode<T>): boolean;
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
- removePath(path: string): IndexInode<T> | null;
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
- getInode(path: string): IndexInode<T> | null;
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
- abstract isFile(): boolean;
84
- abstract isDir(): boolean;
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
- isDir(): boolean;
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
- isDir(): boolean;
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 p Name of item in this directory.
136
+ * @param path Name of item in this directory.
124
137
  */
125
- get(p: string): IndexInode<T> | null;
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 p Item name to add to the directory listing.
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(p: string, inode: IndexInode<T>): boolean;
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,104 @@ export declare class IndexDirInode<T> extends IndexInode<T> {
140
153
  */
141
154
  remove(p: string): IndexInode<T> | null;
142
155
  }
143
- /**
144
- * @hidden
145
- */
146
- export declare function isIndexFileInode<T>(inode?: IndexInode<T>): inode is IndexFileInode<T>;
147
- /**
148
- * @hidden
149
- */
150
- export declare function isIndexDirInode<T>(inode?: IndexInode<T>): inode is IndexDirInode<T>;
156
+ declare const FileIndexFS_base: (abstract new (...args: any[]) => {
157
+ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
158
+ renameSync(oldPath: string, newPath: string, cred: Cred): void;
159
+ createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<import("./file.js").File>;
160
+ createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): import("./file.js").File;
161
+ unlink(path: string, cred: Cred): Promise<void>;
162
+ unlinkSync(path: string, cred: Cred): void;
163
+ rmdir(path: string, cred: Cred): Promise<void>;
164
+ rmdirSync(path: string, cred: Cred): void;
165
+ mkdir(path: string, mode: number, cred: Cred): Promise<void>;
166
+ mkdirSync(path: string, mode: number, cred: Cred): void;
167
+ link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
168
+ linkSync(srcpath: string, dstpath: string, cred: Cred): void;
169
+ sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
170
+ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
171
+ readonly metadata: import("./filesystem.js").FileSystemMetadata;
172
+ ready(): Promise<any>;
173
+ stat(path: string, cred: Cred): Promise<Stats>;
174
+ statSync(path: string, cred: Cred): Stats;
175
+ openFile(path: string, flag: FileFlag, cred: Cred): Promise<import("./file.js").File>;
176
+ openFileSync(path: string, flag: FileFlag, cred: Cred): import("./file.js").File;
177
+ readdir(path: string, cred: Cred): Promise<string[]>;
178
+ readdirSync(path: string, cred: Cred): string[];
179
+ exists(path: string, cred: Cred): Promise<boolean>;
180
+ existsSync(path: string, cred: Cred): boolean;
181
+ }) & typeof FileSystem;
182
+ export declare abstract class FileIndexFS<TIndex> extends FileIndexFS_base {
183
+ protected _index: FileIndex<TIndex>;
184
+ constructor(index: ListingTree);
185
+ stat(path: string): Promise<Stats>;
186
+ statSync(path: string): Stats;
187
+ openFile(path: string, flag: FileFlag, cred: Cred): Promise<NoSyncFile<this>>;
188
+ openFileSync(path: string, flag: FileFlag, cred: Cred): NoSyncFile<this>;
189
+ readdir(path: string): Promise<string[]>;
190
+ readdirSync(path: string): string[];
191
+ protected abstract statFileInode(inode: IndexFileInode<TIndex>): Promise<Stats>;
192
+ protected abstract fileForFileInode(inode: IndexFileInode<TIndex>): Promise<NoSyncFile<this>>;
193
+ protected abstract statFileInodeSync(inode: IndexFileInode<TIndex>): Stats;
194
+ protected abstract fileForFileInodeSync(inode: IndexFileInode<TIndex>): NoSyncFile<this>;
195
+ }
196
+ declare const SyncFileIndexFS_base: (abstract new (...args: any[]) => {
197
+ readonly metadata: import("./filesystem.js").FileSystemMetadata;
198
+ ready(): Promise<any>;
199
+ exists(path: string, cred: Cred): Promise<boolean>;
200
+ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
201
+ stat(path: string, cred: Cred): Promise<Stats>;
202
+ createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<import("./file.js").File>;
203
+ openFile(path: string, flag: FileFlag, cred: Cred): Promise<import("./file.js").File>;
204
+ unlink(path: string, cred: Cred): Promise<void>;
205
+ rmdir(path: string, cred: Cred): Promise<void>;
206
+ mkdir(path: string, mode: number, cred: Cred): Promise<void>;
207
+ readdir(path: string, cred: Cred): Promise<string[]>;
208
+ link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
209
+ sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
210
+ renameSync(oldPath: string, newPath: string, cred: Cred): void;
211
+ statSync(path: string, cred: Cred): Stats;
212
+ openFileSync(path: string, flag: FileFlag, cred: Cred): import("./file.js").File;
213
+ createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): import("./file.js").File;
214
+ unlinkSync(path: string, cred: Cred): void;
215
+ rmdirSync(path: string, cred: Cred): void;
216
+ mkdirSync(path: string, mode: number, cred: Cred): void;
217
+ readdirSync(path: string, cred: Cred): string[];
218
+ existsSync(path: string, cred: Cred): boolean;
219
+ linkSync(srcpath: string, dstpath: string, cred: Cred): void;
220
+ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
221
+ }) & (abstract new (index: ListingTree) => FileIndexFS<unknown>);
222
+ export declare abstract class SyncFileIndexFS<TIndex> extends SyncFileIndexFS_base {
223
+ protected statFileInode(inode: IndexFileInode<TIndex>): Promise<Stats>;
224
+ protected fileForFileInode(inode: IndexFileInode<TIndex>): Promise<NoSyncFile<this>>;
225
+ }
226
+ declare const AsyncFileIndexFS_base: (abstract new (...args: any[]) => {
227
+ renameSync(oldPath: string, newPath: string, cred: Cred): void;
228
+ statSync(path: string, cred: Cred): Stats;
229
+ createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): import("./file.js").File;
230
+ openFileSync(path: string, flag: FileFlag, cred: Cred): import("./file.js").File;
231
+ unlinkSync(path: string, cred: Cred): void;
232
+ rmdirSync(path: string, cred: Cred): void;
233
+ mkdirSync(path: string, mode: number, cred: Cred): void;
234
+ readdirSync(path: string, cred: Cred): string[];
235
+ linkSync(srcpath: string, dstpath: string, cred: Cred): void;
236
+ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
237
+ readonly metadata: import("./filesystem.js").FileSystemMetadata;
238
+ ready(): Promise<any>;
239
+ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
240
+ stat(path: string, cred: Cred): Promise<Stats>;
241
+ openFile(path: string, flag: FileFlag, cred: Cred): Promise<import("./file.js").File>;
242
+ createFile(path: string, flag: FileFlag, mode: number, cred: Cred): Promise<import("./file.js").File>;
243
+ unlink(path: string, cred: Cred): Promise<void>;
244
+ rmdir(path: string, cred: Cred): Promise<void>;
245
+ mkdir(path: string, mode: number, cred: Cred): Promise<void>;
246
+ readdir(path: string, cred: Cred): Promise<string[]>;
247
+ exists(path: string, cred: Cred): Promise<boolean>;
248
+ existsSync(path: string, cred: Cred): boolean;
249
+ link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
250
+ sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
251
+ }) & (abstract new (index: ListingTree) => FileIndexFS<unknown>);
252
+ export declare abstract class AsyncFileIndexFS<TIndex> extends AsyncFileIndexFS_base {
253
+ protected statFileInodeSync(): Stats;
254
+ protected fileForFileInodeSync(): NoSyncFile<this>;
255
+ }
151
256
  export {};
package/dist/FileIndex.js CHANGED
@@ -1,11 +1,16 @@
1
- import { Stats, FileType } from './stats.js';
2
- import * as path from './emulation/path.js';
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 fromListing(listing) {
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 = `${pwd}/${node}`;
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.addPath('/', new IndexDirInode());
62
+ this.add('/', new IndexDirInode());
58
63
  }
59
- /**
60
- * Runs the given function over all files in the index.
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
- cb(item.data);
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
- addPath(path, inode) {
89
+ add(path, inode) {
86
90
  if (!inode) {
87
91
  throw new Error('Inode must be specified');
88
92
  }
89
- if (path[0] !== '/') {
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 splitPath = this.splitPath(path);
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.addPath(dirpath, parent)) {
106
+ if (!this.add(dirpath, parent)) {
105
107
  return false;
106
108
  }
107
109
  }
108
- // Add myself to my parent.
109
- if (path !== '/') {
110
- if (!parent.add(itemname, inode)) {
111
- return false;
112
- }
110
+ // Add to parent.
111
+ if (path != '/' && !parent.add(basename(path), inode)) {
112
+ return false;
113
113
  }
114
- // If I'm a directory, add myself to the index.
115
- if (isIndexDirInode(inode)) {
116
- this._index[path] = inode;
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
- addPathFast(path, inode) {
134
- const itemNameMark = path.lastIndexOf('/');
135
- const parentPath = itemNameMark === 0 ? '/' : path.substring(0, itemNameMark);
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.addPathFast(parentPath, parent);
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.isDir()) {
149
- this._index[path] = inode;
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
- removePath(path) {
159
- const splitPath = this.splitPath(path);
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[dirpath];
158
+ const parent = this._index.get(dirpath);
164
159
  if (!parent) {
165
160
  return;
166
161
  }
167
- // Remove myself from my parent.
168
- const inode = parent.remove(itemname);
162
+ // Remove from parent.
163
+ const inode = parent.remove(basename(path));
169
164
  if (!inode) {
170
165
  return;
171
166
  }
172
- // If I'm a directory, remove myself from the index, and remove my children.
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.removePath(path + '/' + child);
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
- getInode(path) {
197
- const [dirpath, itemname] = this.splitPath(path);
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 === path) {
196
+ if (dirpath == path) {
202
197
  return parent;
203
198
  }
204
- return parent?.get(itemname);
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
- isDir() {
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
- isDir() {
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 Object.keys(this._listing);
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 p Name of item in this directory.
266
+ * @param path Name of item in this directory.
280
267
  */
281
- get(p) {
282
- const item = this._listing[p];
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 p Item name to add to the directory listing.
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(p, inode) {
294
- if (p in this._listing) {
279
+ add(path, inode) {
280
+ if (this._listing.has(path)) {
295
281
  return false;
296
282
  }
297
- this._listing[p] = inode;
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[p];
308
- if (item === undefined) {
309
- return null;
293
+ const item = this._listing.get(p);
294
+ if (!item) {
295
+ return;
310
296
  }
311
- delete this._listing[p];
297
+ this._listing.delete(p);
312
298
  return item;
313
299
  }
314
300
  }
315
- /**
316
- * @hidden
317
- */
318
- export function isIndexFileInode(inode) {
319
- return inode?.isFile();
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
- * @hidden
323
- */
324
- export function isIndexDirInode(inode) {
325
- return inode?.isDir();
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
  }