@zenfs/core 0.9.7 → 0.10.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.
- package/dist/backends/AsyncStore.js +29 -29
- package/dist/backends/Fetch.d.ts +84 -0
- package/dist/backends/Fetch.js +171 -0
- package/dist/backends/Index.js +19 -19
- package/dist/backends/Locked.d.ts +11 -11
- package/dist/backends/Locked.js +50 -49
- package/dist/backends/Overlay.js +21 -21
- package/dist/backends/SyncStore.js +27 -27
- package/dist/backends/backend.js +4 -4
- package/dist/backends/port/fs.d.ts +124 -0
- package/dist/backends/port/fs.js +241 -0
- package/dist/backends/port/rpc.d.ts +60 -0
- package/dist/backends/port/rpc.js +71 -0
- package/dist/backends/port/store.d.ts +30 -0
- package/dist/backends/port/store.js +142 -0
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +8 -10
- package/dist/config.js +11 -11
- package/dist/emulation/async.js +6 -6
- package/dist/emulation/dir.js +2 -2
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/path.d.ts +3 -2
- package/dist/emulation/path.js +19 -45
- package/dist/emulation/promises.d.ts +7 -12
- package/dist/emulation/promises.js +144 -146
- package/dist/emulation/shared.d.ts +5 -10
- package/dist/emulation/shared.js +8 -8
- package/dist/emulation/streams.js +3 -3
- package/dist/emulation/sync.js +25 -25
- package/dist/{ApiError.d.ts → error.d.ts} +13 -14
- package/dist/error.js +292 -0
- package/dist/file.d.ts +2 -0
- package/dist/file.js +10 -4
- package/dist/filesystem.js +15 -15
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/mutex.js +2 -1
- package/dist/utils.d.ts +8 -7
- package/dist/utils.js +11 -12
- package/package.json +3 -3
- package/readme.md +17 -9
- package/src/backends/AsyncStore.ts +29 -29
- package/src/backends/Fetch.ts +230 -0
- package/src/backends/Index.ts +19 -19
- package/src/backends/Locked.ts +50 -49
- package/src/backends/Overlay.ts +23 -23
- package/src/backends/SyncStore.ts +27 -27
- package/src/backends/backend.ts +6 -6
- package/src/backends/port/fs.ts +308 -0
- package/src/backends/port/readme.md +59 -0
- package/src/backends/port/rpc.ts +144 -0
- package/src/backends/port/store.ts +187 -0
- package/src/config.ts +20 -24
- package/src/emulation/async.ts +6 -6
- package/src/emulation/dir.ts +2 -2
- package/src/emulation/index.ts +1 -1
- package/src/emulation/path.ts +25 -49
- package/src/emulation/promises.ts +150 -159
- package/src/emulation/shared.ts +12 -14
- package/src/emulation/streams.ts +3 -3
- package/src/emulation/sync.ts +28 -28
- package/src/{ApiError.ts → error.ts} +89 -89
- package/src/file.ts +12 -4
- package/src/filesystem.ts +15 -15
- package/src/index.ts +4 -1
- package/src/mutex.ts +3 -1
- package/src/utils.ts +16 -18
- package/tsconfig.json +2 -2
- package/dist/ApiError.js +0 -292
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
2
|
+
import { NoSyncFile } from '../file.js';
|
|
3
|
+
import type { FileSystemMetadata } from '../filesystem.js';
|
|
4
|
+
import { Stats } from '../stats.js';
|
|
5
|
+
import { type ListingTree, FileIndex, type IndexFileInode, AsyncIndexFS } from './Index.js';
|
|
6
|
+
import type { Backend } from './backend.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @hidden
|
|
10
|
+
*/
|
|
11
|
+
function convertError(e: Error): never {
|
|
12
|
+
throw new ErrnoError(Errno.EIO, e.message);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Asynchronously download a file as a buffer or a JSON object.
|
|
17
|
+
* Note that the third function signature with a non-specialized type is
|
|
18
|
+
* invalid, but TypeScript requires it when you specialize string arguments to
|
|
19
|
+
* constants.
|
|
20
|
+
* @hidden
|
|
21
|
+
*/
|
|
22
|
+
async function fetchFile(p: string, type: 'buffer'): Promise<Uint8Array>;
|
|
23
|
+
async function fetchFile<T extends object>(p: string, type: 'json'): Promise<T>;
|
|
24
|
+
async function fetchFile<T extends object>(p: string, type: 'buffer' | 'json'): Promise<T | Uint8Array>;
|
|
25
|
+
async function fetchFile<T extends object>(p: string, type: 'buffer' | 'json'): Promise<T | Uint8Array> {
|
|
26
|
+
const response = await fetch(p).catch(convertError);
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status);
|
|
29
|
+
}
|
|
30
|
+
switch (type) {
|
|
31
|
+
case 'buffer':
|
|
32
|
+
const arrayBuffer = await response.arrayBuffer().catch(convertError);
|
|
33
|
+
return new Uint8Array(arrayBuffer);
|
|
34
|
+
case 'json':
|
|
35
|
+
return response.json().catch(convertError) as Promise<T>;
|
|
36
|
+
default:
|
|
37
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid download type: ' + type);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Asynchronously retrieves the size of the given file in bytes.
|
|
43
|
+
* @hidden
|
|
44
|
+
*/
|
|
45
|
+
async function fetchSize(p: string): Promise<number> {
|
|
46
|
+
const response = await fetch(p, { method: 'HEAD' }).catch(convertError);
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new ErrnoError(Errno.EIO, 'fetch failed: HEAD response returned code ' + response.status);
|
|
49
|
+
}
|
|
50
|
+
return parseInt(response.headers.get('Content-Length') || '-1', 10);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Configuration options for FetchFS.
|
|
55
|
+
*/
|
|
56
|
+
export interface FetchOptions {
|
|
57
|
+
/**
|
|
58
|
+
* URL to a file index as a JSON file or the file index object itself.
|
|
59
|
+
* Defaults to `index.json`.
|
|
60
|
+
*/
|
|
61
|
+
index?: string | ListingTree;
|
|
62
|
+
|
|
63
|
+
/** Used as the URL prefix for fetched files.
|
|
64
|
+
* Default: Fetch files relative to the index.
|
|
65
|
+
*/
|
|
66
|
+
baseUrl?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A simple filesystem backed by HTTP using the fetch API.
|
|
71
|
+
*
|
|
72
|
+
*
|
|
73
|
+
* Listings objects look like the following:
|
|
74
|
+
*
|
|
75
|
+
* ```json
|
|
76
|
+
* {
|
|
77
|
+
* "home": {
|
|
78
|
+
* "jvilk": {
|
|
79
|
+
* "someFile.txt": null,
|
|
80
|
+
* "someDir": {
|
|
81
|
+
* // Empty directory
|
|
82
|
+
* }
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.
|
|
89
|
+
*/
|
|
90
|
+
export class FetchFS extends AsyncIndexFS<Stats> {
|
|
91
|
+
public readonly prefixUrl: string;
|
|
92
|
+
|
|
93
|
+
protected _init: Promise<void>;
|
|
94
|
+
|
|
95
|
+
protected async _initialize(index: string | ListingTree): Promise<void> {
|
|
96
|
+
if (typeof index != 'string') {
|
|
97
|
+
this._index = FileIndex.FromListing(index);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(index);
|
|
103
|
+
this._index = FileIndex.FromListing((await response.json()) as ListingTree);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid or unavailable file listing tree');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public async ready(): Promise<this> {
|
|
110
|
+
await this._init;
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
constructor({ index = 'index.json', baseUrl = '' }: FetchOptions) {
|
|
115
|
+
super({});
|
|
116
|
+
|
|
117
|
+
// prefix url must end in a directory separator.
|
|
118
|
+
if (baseUrl.at(-1) != '/') {
|
|
119
|
+
baseUrl += '/';
|
|
120
|
+
}
|
|
121
|
+
this.prefixUrl = baseUrl;
|
|
122
|
+
|
|
123
|
+
this._init = this._initialize(index);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public metadata(): FileSystemMetadata {
|
|
127
|
+
return {
|
|
128
|
+
...super.metadata(),
|
|
129
|
+
name: FetchFS.name,
|
|
130
|
+
readonly: true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public empty(): void {
|
|
135
|
+
for (const file of this._index.files()) {
|
|
136
|
+
delete file.data!.fileData;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Special function: Preload the given file into the index.
|
|
142
|
+
* @param path
|
|
143
|
+
* @param buffer
|
|
144
|
+
*/
|
|
145
|
+
public preloadFile(path: string, buffer: Uint8Array): void {
|
|
146
|
+
const inode = this._index.get(path)!;
|
|
147
|
+
if (!inode) {
|
|
148
|
+
throw ErrnoError.With('ENOENT', path, 'preloadFile');
|
|
149
|
+
}
|
|
150
|
+
if (!inode.isFile()) {
|
|
151
|
+
throw ErrnoError.With('EISDIR', path, 'preloadFile');
|
|
152
|
+
}
|
|
153
|
+
const stats = inode.data!;
|
|
154
|
+
stats.size = buffer.length;
|
|
155
|
+
stats.fileData = buffer;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
protected async statFileInode(inode: IndexFileInode<Stats>, path: string): Promise<Stats> {
|
|
159
|
+
const stats = inode.data!;
|
|
160
|
+
// At this point, a non-opened file will still have default stats from the listing.
|
|
161
|
+
if (stats.size < 0) {
|
|
162
|
+
stats.size = await this._fetchSize(path);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return stats;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected async openFileInode(inode: IndexFileInode<Stats>, path: string, flag: string): Promise<NoSyncFile<this>> {
|
|
169
|
+
const stats = inode.data!;
|
|
170
|
+
// Use existing file contents. This maintains the previously-used flag.
|
|
171
|
+
if (stats.fileData) {
|
|
172
|
+
return new NoSyncFile(this, path, flag, new Stats(stats), stats.fileData);
|
|
173
|
+
}
|
|
174
|
+
// @todo be lazier about actually requesting the file
|
|
175
|
+
const data = await this._fetchFile(path, 'buffer');
|
|
176
|
+
// we don't initially have file sizes
|
|
177
|
+
stats.size = data.length;
|
|
178
|
+
stats.fileData = data;
|
|
179
|
+
return new NoSyncFile(this, path, flag, new Stats(stats), data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private _getRemotePath(filePath: string): string {
|
|
183
|
+
if (filePath.charAt(0) === '/') {
|
|
184
|
+
filePath = filePath.slice(1);
|
|
185
|
+
}
|
|
186
|
+
return this.prefixUrl + filePath;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Asynchronously download the given file.
|
|
191
|
+
*/
|
|
192
|
+
protected _fetchFile(p: string, type: 'buffer'): Promise<Uint8Array>;
|
|
193
|
+
protected _fetchFile(p: string, type: 'json'): Promise<object>;
|
|
194
|
+
protected _fetchFile(p: string, type: 'buffer' | 'json'): Promise<object>;
|
|
195
|
+
protected _fetchFile(p: string, type: 'buffer' | 'json'): Promise<object> {
|
|
196
|
+
return fetchFile(this._getRemotePath(p), type);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Only requests the HEAD content, for the file size.
|
|
201
|
+
*/
|
|
202
|
+
protected _fetchSize(path: string): Promise<number> {
|
|
203
|
+
return fetchSize(this._getRemotePath(path));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const Fetch = {
|
|
208
|
+
name: 'Fetch',
|
|
209
|
+
|
|
210
|
+
options: {
|
|
211
|
+
index: {
|
|
212
|
+
type: ['string', 'object'],
|
|
213
|
+
required: false,
|
|
214
|
+
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make_http_index script. Defaults to `index.json`.',
|
|
215
|
+
},
|
|
216
|
+
baseUrl: {
|
|
217
|
+
type: 'string',
|
|
218
|
+
required: false,
|
|
219
|
+
description: 'Used as the URL prefix for fetched files. Default: Fetch files relative to the index.',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
isAvailable(): boolean {
|
|
224
|
+
return typeof globalThis.fetch == 'function';
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
create(options: FetchOptions) {
|
|
228
|
+
return new FetchFS(options);
|
|
229
|
+
},
|
|
230
|
+
} as const satisfies Backend<FetchFS, FetchOptions>;
|
package/src/backends/Index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
2
2
|
import type { Cred } from '../cred.js';
|
|
3
3
|
import { basename, dirname, join } from '../emulation/path.js';
|
|
4
4
|
import { NoSyncFile, flagToMode, isWriteable } from '../file.js';
|
|
@@ -113,10 +113,10 @@ export class FileIndex<TData> {
|
|
|
113
113
|
*/
|
|
114
114
|
public add(path: string, inode: IndexInode<TData>): boolean {
|
|
115
115
|
if (!inode) {
|
|
116
|
-
throw new
|
|
116
|
+
throw new ErrnoError(Errno.EINVAL, 'Inode must be specified', path, 'FileIndex.add');
|
|
117
117
|
}
|
|
118
118
|
if (!path.startsWith('/')) {
|
|
119
|
-
throw new
|
|
119
|
+
throw new ErrnoError(Errno.EINVAL, 'Path not absolute', path, 'FileIndex.add');
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// Check if it already exists.
|
|
@@ -365,7 +365,7 @@ export abstract class IndexFS<TData> extends Readonly(FileSystem) {
|
|
|
365
365
|
public async stat(path: string): Promise<Stats> {
|
|
366
366
|
const inode = this._index.get(path);
|
|
367
367
|
if (!inode) {
|
|
368
|
-
throw
|
|
368
|
+
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
if (inode.isDirectory()) {
|
|
@@ -376,13 +376,13 @@ export abstract class IndexFS<TData> extends Readonly(FileSystem) {
|
|
|
376
376
|
return this.statFileInode(inode, path);
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
throw new
|
|
379
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid inode.');
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
public statSync(path: string): Stats {
|
|
383
383
|
const inode = this._index.get(path);
|
|
384
384
|
if (!inode) {
|
|
385
|
-
throw
|
|
385
|
+
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
if (inode.isDirectory()) {
|
|
@@ -393,24 +393,24 @@ export abstract class IndexFS<TData> extends Readonly(FileSystem) {
|
|
|
393
393
|
return this.statFileInodeSync(inode, path);
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
throw new
|
|
396
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid inode.');
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
public async openFile(path: string, flag: string, cred: Cred): Promise<NoSyncFile<this>> {
|
|
400
400
|
if (isWriteable(flag)) {
|
|
401
401
|
// You can't write to files on this file system.
|
|
402
|
-
throw new
|
|
402
|
+
throw new ErrnoError(Errno.EPERM, path);
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
// Check if the path exists, and is a file.
|
|
406
406
|
const inode = this._index.get(path);
|
|
407
407
|
|
|
408
408
|
if (!inode) {
|
|
409
|
-
throw
|
|
409
|
+
throw ErrnoError.With('ENOENT', path, 'openFile');
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
if (!inode.toStats().hasAccess(flagToMode(flag), cred)) {
|
|
413
|
-
throw
|
|
413
|
+
throw ErrnoError.With('EACCES', path, 'openFile');
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
if (inode.isDirectory()) {
|
|
@@ -424,18 +424,18 @@ export abstract class IndexFS<TData> extends Readonly(FileSystem) {
|
|
|
424
424
|
public openFileSync(path: string, flag: string, cred: Cred): NoSyncFile<this> {
|
|
425
425
|
if (isWriteable(flag)) {
|
|
426
426
|
// You can't write to files on this file system.
|
|
427
|
-
throw new
|
|
427
|
+
throw new ErrnoError(Errno.EPERM, path);
|
|
428
428
|
}
|
|
429
429
|
|
|
430
430
|
// Check if the path exists, and is a file.
|
|
431
431
|
const inode = this._index.get(path);
|
|
432
432
|
|
|
433
433
|
if (!inode) {
|
|
434
|
-
throw
|
|
434
|
+
throw ErrnoError.With('ENOENT', path, 'openFile');
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
if (!inode.toStats().hasAccess(flagToMode(flag), cred)) {
|
|
438
|
-
throw
|
|
438
|
+
throw ErrnoError.With('EACCES', path, 'openFile');
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
if (inode.isDirectory()) {
|
|
@@ -450,28 +450,28 @@ export abstract class IndexFS<TData> extends Readonly(FileSystem) {
|
|
|
450
450
|
// Check if it exists.
|
|
451
451
|
const inode = this._index.get(path);
|
|
452
452
|
if (!inode) {
|
|
453
|
-
throw
|
|
453
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
454
454
|
}
|
|
455
455
|
|
|
456
456
|
if (inode.isDirectory()) {
|
|
457
457
|
return inode.listing;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
throw
|
|
460
|
+
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
public readdirSync(path: string): string[] {
|
|
464
464
|
// Check if it exists.
|
|
465
465
|
const inode = this._index.get(path);
|
|
466
466
|
if (!inode) {
|
|
467
|
-
throw
|
|
467
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
468
468
|
}
|
|
469
469
|
|
|
470
470
|
if (inode.isDirectory()) {
|
|
471
471
|
return inode.listing;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
throw
|
|
474
|
+
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
protected abstract statFileInode(inode: IndexFileInode<TData>, path: string): Promise<Stats>;
|
|
@@ -495,10 +495,10 @@ export abstract class SyncIndexFS<TData> extends IndexFS<TData> {
|
|
|
495
495
|
|
|
496
496
|
export abstract class AsyncIndexFS<TData> extends IndexFS<TData> {
|
|
497
497
|
protected statFileInodeSync(inode: IndexFileInode<TData>, path: string): Stats {
|
|
498
|
-
throw
|
|
498
|
+
throw ErrnoError.With('ENOSYS', path, 'AsyncIndexFS.statFileInodeSync');
|
|
499
499
|
}
|
|
500
500
|
|
|
501
501
|
protected openFileInodeSync(inode: IndexFileInode<TData>, path: string, flag: string): NoSyncFile<this> {
|
|
502
|
-
throw
|
|
502
|
+
throw ErrnoError.With('ENOSYS', path, 'AsyncIndexFS.openFileInodeSync');
|
|
503
503
|
}
|
|
504
504
|
}
|
package/src/backends/Locked.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ErrnoError } from '../error.js';
|
|
1
2
|
import type { Cred } from '../cred.js';
|
|
2
3
|
import type { File } from '../file.js';
|
|
3
4
|
import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
|
|
@@ -39,23 +40,23 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
39
40
|
|
|
40
41
|
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
41
42
|
if (this._mu.isLocked(oldPath)) {
|
|
42
|
-
throw
|
|
43
|
+
throw ErrnoError.With('EBUSY', oldPath, 'rename');
|
|
43
44
|
}
|
|
44
45
|
return this.fs.renameSync(oldPath, newPath, cred);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
public async stat(
|
|
48
|
-
await this._mu.lock(
|
|
49
|
-
const stats = await this.fs.stat(
|
|
50
|
-
this._mu.unlock(
|
|
48
|
+
public async stat(path: string, cred: Cred): Promise<Stats> {
|
|
49
|
+
await this._mu.lock(path);
|
|
50
|
+
const stats = await this.fs.stat(path, cred);
|
|
51
|
+
this._mu.unlock(path);
|
|
51
52
|
return stats;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
public statSync(
|
|
55
|
-
if (this._mu.isLocked(
|
|
56
|
-
throw
|
|
55
|
+
public statSync(path: string, cred: Cred): Stats {
|
|
56
|
+
if (this._mu.isLocked(path)) {
|
|
57
|
+
throw ErrnoError.With('EBUSY', path, 'stat');
|
|
57
58
|
}
|
|
58
|
-
return this.fs.statSync(
|
|
59
|
+
return this.fs.statSync(path, cred);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
|
|
@@ -67,7 +68,7 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
67
68
|
|
|
68
69
|
public openFileSync(path: string, flag: string, cred: Cred): File {
|
|
69
70
|
if (this._mu.isLocked(path)) {
|
|
70
|
-
throw
|
|
71
|
+
throw ErrnoError.With('EBUSY', path, 'openFile');
|
|
71
72
|
}
|
|
72
73
|
return this.fs.openFileSync(path, flag, cred);
|
|
73
74
|
}
|
|
@@ -81,7 +82,7 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
81
82
|
|
|
82
83
|
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
|
|
83
84
|
if (this._mu.isLocked(path)) {
|
|
84
|
-
throw
|
|
85
|
+
throw ErrnoError.With('EBUSY', path, 'createFile');
|
|
85
86
|
}
|
|
86
87
|
return this.fs.createFileSync(path, flag, mode, cred);
|
|
87
88
|
}
|
|
@@ -92,65 +93,65 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
92
93
|
this._mu.unlock(p);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
public unlinkSync(
|
|
96
|
-
if (this._mu.isLocked(
|
|
97
|
-
throw
|
|
96
|
+
public unlinkSync(path: string, cred: Cred): void {
|
|
97
|
+
if (this._mu.isLocked(path)) {
|
|
98
|
+
throw ErrnoError.With('EBUSY', path, 'unlink');
|
|
98
99
|
}
|
|
99
|
-
return this.fs.unlinkSync(
|
|
100
|
+
return this.fs.unlinkSync(path, cred);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
public async rmdir(
|
|
103
|
-
await this._mu.lock(
|
|
104
|
-
await this.fs.rmdir(
|
|
105
|
-
this._mu.unlock(
|
|
103
|
+
public async rmdir(path: string, cred: Cred): Promise<void> {
|
|
104
|
+
await this._mu.lock(path);
|
|
105
|
+
await this.fs.rmdir(path, cred);
|
|
106
|
+
this._mu.unlock(path);
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
public rmdirSync(
|
|
109
|
-
if (this._mu.isLocked(
|
|
110
|
-
throw
|
|
109
|
+
public rmdirSync(path: string, cred: Cred): void {
|
|
110
|
+
if (this._mu.isLocked(path)) {
|
|
111
|
+
throw ErrnoError.With('EBUSY', path, 'rmdir');
|
|
111
112
|
}
|
|
112
|
-
return this.fs.rmdirSync(
|
|
113
|
+
return this.fs.rmdirSync(path, cred);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
public async mkdir(
|
|
116
|
-
await this._mu.lock(
|
|
117
|
-
await this.fs.mkdir(
|
|
118
|
-
this._mu.unlock(
|
|
116
|
+
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
|
|
117
|
+
await this._mu.lock(path);
|
|
118
|
+
await this.fs.mkdir(path, mode, cred);
|
|
119
|
+
this._mu.unlock(path);
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
public mkdirSync(
|
|
122
|
-
if (this._mu.isLocked(
|
|
123
|
-
throw
|
|
122
|
+
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
123
|
+
if (this._mu.isLocked(path)) {
|
|
124
|
+
throw ErrnoError.With('EBUSY', path, 'mkdir');
|
|
124
125
|
}
|
|
125
|
-
return this.fs.mkdirSync(
|
|
126
|
+
return this.fs.mkdirSync(path, mode, cred);
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
public async readdir(
|
|
129
|
-
await this._mu.lock(
|
|
130
|
-
const files = await this.fs.readdir(
|
|
131
|
-
this._mu.unlock(
|
|
129
|
+
public async readdir(path: string, cred: Cred): Promise<string[]> {
|
|
130
|
+
await this._mu.lock(path);
|
|
131
|
+
const files = await this.fs.readdir(path, cred);
|
|
132
|
+
this._mu.unlock(path);
|
|
132
133
|
return files;
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
public readdirSync(
|
|
136
|
-
if (this._mu.isLocked(
|
|
137
|
-
throw
|
|
136
|
+
public readdirSync(path: string, cred: Cred): string[] {
|
|
137
|
+
if (this._mu.isLocked(path)) {
|
|
138
|
+
throw ErrnoError.With('EBUSY', path, 'readdir');
|
|
138
139
|
}
|
|
139
|
-
return this.fs.readdirSync(
|
|
140
|
+
return this.fs.readdirSync(path, cred);
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
public async exists(
|
|
143
|
-
await this._mu.lock(
|
|
144
|
-
const exists = await this.fs.exists(
|
|
145
|
-
this._mu.unlock(
|
|
143
|
+
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
144
|
+
await this._mu.lock(path);
|
|
145
|
+
const exists = await this.fs.exists(path, cred);
|
|
146
|
+
this._mu.unlock(path);
|
|
146
147
|
return exists;
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
public existsSync(
|
|
150
|
-
if (this._mu.isLocked(
|
|
151
|
-
throw
|
|
150
|
+
public existsSync(path: string, cred: Cred): boolean {
|
|
151
|
+
if (this._mu.isLocked(path)) {
|
|
152
|
+
throw ErrnoError.With('EBUSY', path, 'exists');
|
|
152
153
|
}
|
|
153
|
-
return this.fs.existsSync(
|
|
154
|
+
return this.fs.existsSync(path, cred);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
|
|
@@ -161,7 +162,7 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
161
162
|
|
|
162
163
|
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
|
|
163
164
|
if (this._mu.isLocked(srcpath)) {
|
|
164
|
-
throw
|
|
165
|
+
throw ErrnoError.With('EBUSY', srcpath, 'link');
|
|
165
166
|
}
|
|
166
167
|
return this.fs.linkSync(srcpath, dstpath, cred);
|
|
167
168
|
}
|
|
@@ -174,7 +175,7 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
174
175
|
|
|
175
176
|
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
176
177
|
if (this._mu.isLocked(path)) {
|
|
177
|
-
throw
|
|
178
|
+
throw ErrnoError.With('EBUSY', path, 'sync');
|
|
178
179
|
}
|
|
179
180
|
return this.fs.syncSync(path, data, stats);
|
|
180
181
|
}
|