@zenfs/core 0.11.0 → 0.11.2
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 -2
- package/dist/backends/backend.js +0 -6
- package/dist/backends/fetch.d.ts +1 -1
- package/dist/backends/fetch.js +13 -11
- package/dist/backends/locked.d.ts +1 -1
- package/dist/backends/locked.js +34 -34
- package/dist/backends/memory.d.ts +1 -1
- package/dist/backends/port/fs.d.ts +1 -1
- package/dist/backends/port/fs.js +2 -1
- package/dist/backends/port/rpc.js +3 -1
- package/dist/backends/store/fs.d.ts +5 -5
- package/dist/backends/store/fs.js +1 -1
- package/dist/browser.min.js +3 -3
- package/dist/browser.min.js.map +3 -3
- package/dist/config.js +1 -1
- package/dist/emulation/promises.js +1 -2
- package/dist/emulation/sync.js +24 -40
- package/dist/filesystem.d.ts +6 -6
- package/dist/filesystem.js +11 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mutex.d.ts +1 -1
- package/dist/mutex.js +11 -11
- package/package.json +1 -1
- package/scripts/make-index.js +15 -12
- package/src/backends/backend.ts +3 -8
- package/src/backends/fetch.ts +13 -12
- package/src/backends/locked.ts +34 -34
- package/src/backends/memory.ts +1 -1
- package/src/backends/port/fs.ts +2 -1
- package/src/backends/port/rpc.ts +2 -1
- package/src/backends/store/fs.ts +5 -5
- package/src/config.ts +1 -1
- package/src/emulation/promises.ts +1 -2
- package/src/emulation/sync.ts +23 -41
- package/src/filesystem.ts +18 -17
- package/src/index.ts +2 -0
- package/src/mutex.ts +11 -11
package/src/backends/locked.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { Stats } from '../stats.js';
|
|
|
16
16
|
* @internal
|
|
17
17
|
*/
|
|
18
18
|
export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
19
|
-
private
|
|
19
|
+
private mutex: Mutex = new Mutex();
|
|
20
20
|
|
|
21
21
|
constructor(public readonly fs: FS) {}
|
|
22
22
|
|
|
@@ -32,148 +32,148 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
35
|
-
await this.
|
|
35
|
+
await this.mutex.lock(oldPath);
|
|
36
36
|
await this.fs.rename(oldPath, newPath, cred);
|
|
37
|
-
this.
|
|
37
|
+
this.mutex.unlock(oldPath);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
41
|
-
if (this.
|
|
41
|
+
if (this.mutex.isLocked(oldPath)) {
|
|
42
42
|
throw ErrnoError.With('EBUSY', oldPath, 'rename');
|
|
43
43
|
}
|
|
44
44
|
return this.fs.renameSync(oldPath, newPath, cred);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
public async stat(path: string, cred: Cred): Promise<Stats> {
|
|
48
|
-
await this.
|
|
48
|
+
await this.mutex.lock(path);
|
|
49
49
|
const stats = await this.fs.stat(path, cred);
|
|
50
|
-
this.
|
|
50
|
+
this.mutex.unlock(path);
|
|
51
51
|
return stats;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
public statSync(path: string, cred: Cred): Stats {
|
|
55
|
-
if (this.
|
|
55
|
+
if (this.mutex.isLocked(path)) {
|
|
56
56
|
throw ErrnoError.With('EBUSY', path, 'stat');
|
|
57
57
|
}
|
|
58
58
|
return this.fs.statSync(path, cred);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
|
|
62
|
-
await this.
|
|
62
|
+
await this.mutex.lock(path);
|
|
63
63
|
const fd = await this.fs.openFile(path, flag, cred);
|
|
64
|
-
this.
|
|
64
|
+
this.mutex.unlock(path);
|
|
65
65
|
return fd;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
public openFileSync(path: string, flag: string, cred: Cred): File {
|
|
69
|
-
if (this.
|
|
69
|
+
if (this.mutex.isLocked(path)) {
|
|
70
70
|
throw ErrnoError.With('EBUSY', path, 'openFile');
|
|
71
71
|
}
|
|
72
72
|
return this.fs.openFileSync(path, flag, cred);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
|
|
76
|
-
await this.
|
|
76
|
+
await this.mutex.lock(path);
|
|
77
77
|
const fd = await this.fs.createFile(path, flag, mode, cred);
|
|
78
|
-
this.
|
|
78
|
+
this.mutex.unlock(path);
|
|
79
79
|
return fd;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
|
|
83
|
-
if (this.
|
|
83
|
+
if (this.mutex.isLocked(path)) {
|
|
84
84
|
throw ErrnoError.With('EBUSY', path, 'createFile');
|
|
85
85
|
}
|
|
86
86
|
return this.fs.createFileSync(path, flag, mode, cred);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
public async unlink(path: string, cred: Cred): Promise<void> {
|
|
90
|
-
await this.
|
|
90
|
+
await this.mutex.lock(path);
|
|
91
91
|
await this.fs.unlink(path, cred);
|
|
92
|
-
this.
|
|
92
|
+
this.mutex.unlock(path);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
public unlinkSync(path: string, cred: Cred): void {
|
|
96
|
-
if (this.
|
|
96
|
+
if (this.mutex.isLocked(path)) {
|
|
97
97
|
throw ErrnoError.With('EBUSY', path, 'unlink');
|
|
98
98
|
}
|
|
99
99
|
return this.fs.unlinkSync(path, cred);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
public async rmdir(path: string, cred: Cred): Promise<void> {
|
|
103
|
-
await this.
|
|
103
|
+
await this.mutex.lock(path);
|
|
104
104
|
await this.fs.rmdir(path, cred);
|
|
105
|
-
this.
|
|
105
|
+
this.mutex.unlock(path);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
public rmdirSync(path: string, cred: Cred): void {
|
|
109
|
-
if (this.
|
|
109
|
+
if (this.mutex.isLocked(path)) {
|
|
110
110
|
throw ErrnoError.With('EBUSY', path, 'rmdir');
|
|
111
111
|
}
|
|
112
112
|
return this.fs.rmdirSync(path, cred);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
|
|
116
|
-
await this.
|
|
116
|
+
await this.mutex.lock(path);
|
|
117
117
|
await this.fs.mkdir(path, mode, cred);
|
|
118
|
-
this.
|
|
118
|
+
this.mutex.unlock(path);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
122
|
-
if (this.
|
|
122
|
+
if (this.mutex.isLocked(path)) {
|
|
123
123
|
throw ErrnoError.With('EBUSY', path, 'mkdir');
|
|
124
124
|
}
|
|
125
125
|
return this.fs.mkdirSync(path, mode, cred);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
public async readdir(path: string, cred: Cred): Promise<string[]> {
|
|
129
|
-
await this.
|
|
129
|
+
await this.mutex.lock(path);
|
|
130
130
|
const files = await this.fs.readdir(path, cred);
|
|
131
|
-
this.
|
|
131
|
+
this.mutex.unlock(path);
|
|
132
132
|
return files;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
public readdirSync(path: string, cred: Cred): string[] {
|
|
136
|
-
if (this.
|
|
136
|
+
if (this.mutex.isLocked(path)) {
|
|
137
137
|
throw ErrnoError.With('EBUSY', path, 'readdir');
|
|
138
138
|
}
|
|
139
139
|
return this.fs.readdirSync(path, cred);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
143
|
-
await this.
|
|
143
|
+
await this.mutex.lock(path);
|
|
144
144
|
const exists = await this.fs.exists(path, cred);
|
|
145
|
-
this.
|
|
145
|
+
this.mutex.unlock(path);
|
|
146
146
|
return exists;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
public existsSync(path: string, cred: Cred): boolean {
|
|
150
|
-
if (this.
|
|
150
|
+
if (this.mutex.isLocked(path)) {
|
|
151
151
|
throw ErrnoError.With('EBUSY', path, 'exists');
|
|
152
152
|
}
|
|
153
153
|
return this.fs.existsSync(path, cred);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
|
|
157
|
-
await this.
|
|
157
|
+
await this.mutex.lock(srcpath);
|
|
158
158
|
await this.fs.link(srcpath, dstpath, cred);
|
|
159
|
-
this.
|
|
159
|
+
this.mutex.unlock(srcpath);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
|
|
163
|
-
if (this.
|
|
163
|
+
if (this.mutex.isLocked(srcpath)) {
|
|
164
164
|
throw ErrnoError.With('EBUSY', srcpath, 'link');
|
|
165
165
|
}
|
|
166
166
|
return this.fs.linkSync(srcpath, dstpath, cred);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
170
|
-
await this.
|
|
170
|
+
await this.mutex.lock(path);
|
|
171
171
|
await this.fs.sync(path, data, stats);
|
|
172
|
-
this.
|
|
172
|
+
this.mutex.unlock(path);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
176
|
-
if (this.
|
|
176
|
+
if (this.mutex.isLocked(path)) {
|
|
177
177
|
throw ErrnoError.With('EBUSY', path, 'sync');
|
|
178
178
|
}
|
|
179
179
|
return this.fs.syncSync(path, data, stats);
|
package/src/backends/memory.ts
CHANGED
package/src/backends/port/fs.ts
CHANGED
|
@@ -63,7 +63,8 @@ export class PortFile extends File {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>> {
|
|
66
|
-
|
|
66
|
+
const result = await this.rpc('read', buffer, offset, length, position);
|
|
67
|
+
return result as FileReadResult<TBuffer>;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
public readSync(): number {
|
package/src/backends/port/rpc.ts
CHANGED
|
@@ -85,10 +85,11 @@ export function request<const TRequest extends Request, TValue>(
|
|
|
85
85
|
const id = Math.random().toString(16).slice(10);
|
|
86
86
|
executors.set(id, { resolve, reject, fs });
|
|
87
87
|
port.postMessage({ ...request, _zenfs: true, id, stack });
|
|
88
|
-
setTimeout(() => {
|
|
88
|
+
const _ = setTimeout(() => {
|
|
89
89
|
const error = new ErrnoError(Errno.EIO, 'RPC Failed');
|
|
90
90
|
error.stack += stack;
|
|
91
91
|
reject(error);
|
|
92
|
+
if (typeof _ == 'object') _.unref();
|
|
92
93
|
}, timeout);
|
|
93
94
|
});
|
|
94
95
|
}
|
package/src/backends/store/fs.ts
CHANGED
|
@@ -12,22 +12,22 @@ import type { Store, Transaction } from './store.js';
|
|
|
12
12
|
const maxInodeAllocTries = 5;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* A
|
|
15
|
+
* A file system which uses a key-value store.
|
|
16
16
|
*
|
|
17
17
|
* We use a unique ID for each node in the file system. The root node has a fixed ID.
|
|
18
18
|
* @todo Introduce Node ID caching.
|
|
19
19
|
* @todo Check modes.
|
|
20
20
|
* @internal
|
|
21
21
|
*/
|
|
22
|
-
export class StoreFS extends FileSystem {
|
|
23
|
-
protected get store():
|
|
22
|
+
export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
23
|
+
protected get store(): T {
|
|
24
24
|
if (!this._store) {
|
|
25
25
|
throw new ErrnoError(Errno.ENODATA, 'No store attached');
|
|
26
26
|
}
|
|
27
27
|
return this._store;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
protected _store?:
|
|
30
|
+
protected _store?: T;
|
|
31
31
|
|
|
32
32
|
private _initialized: boolean = false;
|
|
33
33
|
|
|
@@ -40,7 +40,7 @@ export class StoreFS extends FileSystem {
|
|
|
40
40
|
this._store = await this.$store;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
constructor(private $store:
|
|
43
|
+
constructor(private $store: T | Promise<T>) {
|
|
44
44
|
super();
|
|
45
45
|
|
|
46
46
|
if (!($store instanceof Promise)) {
|
package/src/config.ts
CHANGED
|
@@ -58,7 +58,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
|
|
|
58
58
|
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
|
|
59
59
|
}
|
|
60
60
|
checkOptions(backend, config);
|
|
61
|
-
const mount = backend.create(config);
|
|
61
|
+
const mount = await backend.create(config);
|
|
62
62
|
await mount.ready();
|
|
63
63
|
return mount;
|
|
64
64
|
}
|
|
@@ -144,8 +144,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
const { size } = await this.stat();
|
|
147
|
-
const data = new Uint8Array(size);
|
|
148
|
-
await this.file.read(data, 0, size, 0);
|
|
147
|
+
const { buffer: data } = await this.file.read(new Uint8Array(size), 0, size, 0);
|
|
149
148
|
const buffer = Buffer.from(data);
|
|
150
149
|
return options.encoding ? buffer.toString(options.encoding) : buffer;
|
|
151
150
|
}
|
package/src/emulation/sync.ts
CHANGED
|
@@ -16,7 +16,7 @@ type FileSystemMethod = {
|
|
|
16
16
|
: never;
|
|
17
17
|
}[keyof FileSystem]; // https://stackoverflow.com/a/76335220/17637456
|
|
18
18
|
|
|
19
|
-
function
|
|
19
|
+
function wrap<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, resolveSymlinks, path, ...args]: Parameters<M>): RT {
|
|
20
20
|
path = normalizePath(path!);
|
|
21
21
|
const { fs, path: resolvedPath } = resolveMount(resolveSymlinks && existsSync(path) ? realpathSync(path) : path);
|
|
22
22
|
try {
|
|
@@ -78,7 +78,7 @@ existsSync satisfies typeof fs.existsSync;
|
|
|
78
78
|
export function statSync(path: fs.PathLike, options?: { bigint?: boolean }): Stats;
|
|
79
79
|
export function statSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
|
|
80
80
|
export function statSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
|
|
81
|
-
const stats: Stats =
|
|
81
|
+
const stats: Stats = wrap('statSync', true, path.toString(), cred);
|
|
82
82
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
83
83
|
}
|
|
84
84
|
statSync satisfies typeof fs.statSync;
|
|
@@ -92,7 +92,7 @@ statSync satisfies typeof fs.statSync;
|
|
|
92
92
|
export function lstatSync(path: fs.PathLike, options?: { bigint?: boolean }): Stats;
|
|
93
93
|
export function lstatSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
|
|
94
94
|
export function lstatSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
|
|
95
|
-
const stats: Stats =
|
|
95
|
+
const stats: Stats = wrap('statSync', false, path.toString(), cred);
|
|
96
96
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
97
97
|
}
|
|
98
98
|
lstatSync satisfies typeof fs.lstatSync;
|
|
@@ -117,7 +117,7 @@ truncateSync satisfies typeof fs.truncateSync;
|
|
|
117
117
|
* @param path
|
|
118
118
|
*/
|
|
119
119
|
export function unlinkSync(path: fs.PathLike): void {
|
|
120
|
-
return
|
|
120
|
+
return wrap('unlinkSync', false, path.toString(), cred);
|
|
121
121
|
}
|
|
122
122
|
unlinkSync satisfies typeof fs.unlinkSync;
|
|
123
123
|
|
|
@@ -128,17 +128,17 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
|
|
|
128
128
|
// Check if the path exists, and is a file.
|
|
129
129
|
let stats: Stats;
|
|
130
130
|
try {
|
|
131
|
-
stats =
|
|
131
|
+
stats = wrap('statSync', resolveSymlinks, path, cred);
|
|
132
132
|
} catch (e) {
|
|
133
133
|
// File does not exist.
|
|
134
134
|
switch (pathNotExistsAction(flag)) {
|
|
135
135
|
case ActionType.CREATE:
|
|
136
136
|
// Ensure parent exists.
|
|
137
|
-
const parentStats: Stats =
|
|
137
|
+
const parentStats: Stats = wrap('statSync', resolveSymlinks, dirname(path), cred);
|
|
138
138
|
if (!parentStats.isDirectory()) {
|
|
139
139
|
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
140
140
|
}
|
|
141
|
-
return
|
|
141
|
+
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
|
|
142
142
|
case ActionType.THROW:
|
|
143
143
|
throw ErrnoError.With('ENOENT', path, '_open');
|
|
144
144
|
default:
|
|
@@ -155,16 +155,16 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
|
|
|
155
155
|
throw ErrnoError.With('EEXIST', path, '_open');
|
|
156
156
|
case ActionType.TRUNCATE:
|
|
157
157
|
// Delete file.
|
|
158
|
-
|
|
158
|
+
wrap('unlinkSync', resolveSymlinks, path, cred);
|
|
159
159
|
/*
|
|
160
160
|
Create file. Use the same mode as the old file.
|
|
161
161
|
Node itself modifies the ctime when this occurs, so this action
|
|
162
162
|
will preserve that behavior if the underlying file system
|
|
163
163
|
supports those properties.
|
|
164
164
|
*/
|
|
165
|
-
return
|
|
165
|
+
return wrap('createFileSync', resolveSymlinks, path, flag, stats.mode, cred);
|
|
166
166
|
case ActionType.NOP:
|
|
167
|
-
return
|
|
167
|
+
return wrap('openFileSync', resolveSymlinks, path, flag, cred);
|
|
168
168
|
default:
|
|
169
169
|
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
|
|
170
170
|
}
|
|
@@ -230,21 +230,6 @@ export function readFileSync(path: fs.PathOrFileDescriptor, _options: fs.WriteFi
|
|
|
230
230
|
}
|
|
231
231
|
readFileSync satisfies typeof fs.readFileSync;
|
|
232
232
|
|
|
233
|
-
/**
|
|
234
|
-
* Synchronously writes data to a file, replacing the file
|
|
235
|
-
* if it already exists.
|
|
236
|
-
*
|
|
237
|
-
* The encoding option is ignored if data is a buffer.
|
|
238
|
-
*/
|
|
239
|
-
function _writeFileSync(fname: string, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): void {
|
|
240
|
-
const file = _openSync(fname, flag, mode, resolveSymlinks);
|
|
241
|
-
try {
|
|
242
|
-
file.writeSync(data, 0, data.byteLength, 0);
|
|
243
|
-
} finally {
|
|
244
|
-
file.closeSync();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
233
|
/**
|
|
249
234
|
* Synchronously writes data to a file, replacing the file if it already
|
|
250
235
|
* exists.
|
|
@@ -272,22 +257,14 @@ export function writeFileSync(path: fs.PathOrFileDescriptor, data: FileContents,
|
|
|
272
257
|
if (!encodedData) {
|
|
273
258
|
throw new ErrnoError(Errno.EINVAL, 'Data not specified');
|
|
274
259
|
}
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
writeFileSync satisfies typeof fs.writeFileSync;
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Synchronously append data to a file, creating the file if
|
|
281
|
-
* it not yet exists.
|
|
282
|
-
*/
|
|
283
|
-
function _appendFileSync(fname: string, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): void {
|
|
284
|
-
const file = _openSync(fname, flag, mode, resolveSymlinks);
|
|
260
|
+
const file = _openSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), flag, options.mode, true);
|
|
285
261
|
try {
|
|
286
|
-
file.writeSync(
|
|
262
|
+
file.writeSync(encodedData, 0, encodedData.byteLength, 0);
|
|
287
263
|
} finally {
|
|
288
264
|
file.closeSync();
|
|
289
265
|
}
|
|
290
266
|
}
|
|
267
|
+
writeFileSync satisfies typeof fs.writeFileSync;
|
|
291
268
|
|
|
292
269
|
/**
|
|
293
270
|
* Asynchronously append data to a file, creating the file if it not yet
|
|
@@ -310,7 +287,12 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
|
|
|
310
287
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
311
288
|
}
|
|
312
289
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
313
|
-
|
|
290
|
+
const file = _openSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), flag, options.mode, true);
|
|
291
|
+
try {
|
|
292
|
+
file.writeSync(encodedData, 0, encodedData.byteLength, null);
|
|
293
|
+
} finally {
|
|
294
|
+
file.closeSync();
|
|
295
|
+
}
|
|
314
296
|
}
|
|
315
297
|
appendFileSync satisfies typeof fs.appendFileSync;
|
|
316
298
|
|
|
@@ -481,7 +463,7 @@ futimesSync satisfies typeof fs.futimesSync;
|
|
|
481
463
|
* @param path
|
|
482
464
|
*/
|
|
483
465
|
export function rmdirSync(path: fs.PathLike): void {
|
|
484
|
-
return
|
|
466
|
+
return wrap('rmdirSync', true, path.toString(), cred);
|
|
485
467
|
}
|
|
486
468
|
rmdirSync satisfies typeof fs.rmdirSync;
|
|
487
469
|
|
|
@@ -497,7 +479,7 @@ export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirector
|
|
|
497
479
|
export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): string | undefined | void {
|
|
498
480
|
const mode: fs.Mode | undefined = typeof options == 'number' || typeof options == 'string' ? options : options?.mode;
|
|
499
481
|
const recursive = typeof options == 'object' && options?.recursive;
|
|
500
|
-
|
|
482
|
+
wrap('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred);
|
|
501
483
|
}
|
|
502
484
|
mkdirSync satisfies typeof fs.mkdirSync;
|
|
503
485
|
|
|
@@ -514,7 +496,7 @@ export function readdirSync(
|
|
|
514
496
|
options?: { recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null; withFileTypes?: boolean } | BufferEncoding | 'buffer' | null
|
|
515
497
|
): string[] | Dirent[] | Buffer[] {
|
|
516
498
|
path = normalizePath(path);
|
|
517
|
-
const entries: string[] =
|
|
499
|
+
const entries: string[] = wrap('readdirSync', true, path, cred);
|
|
518
500
|
for (const mount of mounts.keys()) {
|
|
519
501
|
if (!mount.startsWith(path)) {
|
|
520
502
|
continue;
|
|
@@ -549,7 +531,7 @@ readdirSync satisfies typeof fs.readdirSync;
|
|
|
549
531
|
*/
|
|
550
532
|
export function linkSync(existing: fs.PathLike, newpath: fs.PathLike): void {
|
|
551
533
|
newpath = normalizePath(newpath);
|
|
552
|
-
return
|
|
534
|
+
return wrap('linkSync', false, existing.toString(), newpath.toString(), cred);
|
|
553
535
|
}
|
|
554
536
|
linkSync satisfies typeof fs.linkSync;
|
|
555
537
|
|
package/src/filesystem.ts
CHANGED
|
@@ -197,7 +197,7 @@ export abstract class FileSystem {
|
|
|
197
197
|
/**
|
|
198
198
|
* @internal
|
|
199
199
|
*/
|
|
200
|
-
declare abstract class
|
|
200
|
+
declare abstract class SyncFS extends FileSystem {
|
|
201
201
|
metadata(): FileSystemMetadata;
|
|
202
202
|
ready(): Promise<void>;
|
|
203
203
|
exists(path: string, cred: Cred): Promise<boolean>;
|
|
@@ -217,8 +217,8 @@ declare abstract class SyncFileSystem extends FileSystem {
|
|
|
217
217
|
* Implements the asynchronous API in terms of the synchronous API.
|
|
218
218
|
*/
|
|
219
219
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
220
|
-
export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) =>
|
|
221
|
-
abstract class
|
|
220
|
+
export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFS) & T {
|
|
221
|
+
abstract class _SyncFS extends FS implements SyncFS {
|
|
222
222
|
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
223
223
|
return this.existsSync(path, cred);
|
|
224
224
|
}
|
|
@@ -263,13 +263,13 @@ export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS:
|
|
|
263
263
|
return this.syncSync(path, data, stats);
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
-
return
|
|
266
|
+
return _SyncFS;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
/**
|
|
270
270
|
* @internal
|
|
271
271
|
*/
|
|
272
|
-
declare abstract class
|
|
272
|
+
declare abstract class AsyncFS extends FileSystem {
|
|
273
273
|
/**
|
|
274
274
|
* @hidden
|
|
275
275
|
*/
|
|
@@ -312,8 +312,8 @@ type AsyncOperation = {
|
|
|
312
312
|
*
|
|
313
313
|
*/
|
|
314
314
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
315
|
-
export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) =>
|
|
316
|
-
abstract class
|
|
315
|
+
export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFS) & T {
|
|
316
|
+
abstract class _AsyncFS extends FS implements AsyncFS {
|
|
317
317
|
/**
|
|
318
318
|
* Queue of pending asynchronous operations.
|
|
319
319
|
*/
|
|
@@ -359,18 +359,19 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
|
|
362
|
-
|
|
362
|
+
this._sync.createFileSync(path, flag, mode, cred);
|
|
363
363
|
this.queue('createFile', path, flag, mode, cred);
|
|
364
|
+
return this.openFileSync(path, flag, cred);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
|
|
368
|
+
const file = this._sync.openFileSync(path, flag, cred);
|
|
364
369
|
const stats = file.statSync();
|
|
365
370
|
const buffer = new Uint8Array(stats.size);
|
|
366
371
|
file.readSync(buffer);
|
|
367
372
|
return new PreloadFile(this, path, flag, stats, buffer);
|
|
368
373
|
}
|
|
369
374
|
|
|
370
|
-
public openFileSync(path: string, flag: string, cred: Cred): File {
|
|
371
|
-
return this._sync.openFileSync(path, flag, cred);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
375
|
public unlinkSync(path: string, cred: Cred): void {
|
|
375
376
|
this._sync.unlinkSync(path, cred);
|
|
376
377
|
this.queue('unlink', path, cred);
|
|
@@ -455,13 +456,13 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
|
|
|
455
456
|
}
|
|
456
457
|
}
|
|
457
458
|
|
|
458
|
-
return
|
|
459
|
+
return _AsyncFS;
|
|
459
460
|
}
|
|
460
461
|
|
|
461
462
|
/**
|
|
462
463
|
* @internal
|
|
463
464
|
*/
|
|
464
|
-
declare abstract class
|
|
465
|
+
declare abstract class ReadonlyFS extends FileSystem {
|
|
465
466
|
metadata(): FileSystemMetadata;
|
|
466
467
|
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
467
468
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
@@ -483,8 +484,8 @@ declare abstract class ReadonlyFileSystem extends FileSystem {
|
|
|
483
484
|
* Implements the non-readonly methods to throw `EROFS`
|
|
484
485
|
*/
|
|
485
486
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
486
|
-
export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) =>
|
|
487
|
-
abstract class
|
|
487
|
+
export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFS) & T {
|
|
488
|
+
abstract class _ReadonlyFS extends FS implements ReadonlyFS {
|
|
488
489
|
public metadata(): FileSystemMetadata {
|
|
489
490
|
return { ...super.metadata(), readonly: true };
|
|
490
491
|
}
|
|
@@ -546,5 +547,5 @@ export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(
|
|
|
546
547
|
}
|
|
547
548
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
548
549
|
}
|
|
549
|
-
return
|
|
550
|
+
return _ReadonlyFS;
|
|
550
551
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from './backends/Index.js';
|
|
|
6
6
|
export * from './backends/locked.js';
|
|
7
7
|
export * from './backends/overlay.js';
|
|
8
8
|
export * from './backends/store/fs.js';
|
|
9
|
+
export * from './backends/store/simple.js';
|
|
10
|
+
export * from './backends/store/store.js';
|
|
9
11
|
export * from './backends/backend.js';
|
|
10
12
|
export * from './config.js';
|
|
11
13
|
export * from './cred.js';
|
package/src/mutex.ts
CHANGED
|
@@ -5,24 +5,24 @@ import { ErrnoError, Errno } from './error.js';
|
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
7
|
export class Mutex {
|
|
8
|
-
|
|
8
|
+
protected locks: Map<string, (() => void)[]> = new Map();
|
|
9
9
|
|
|
10
10
|
public lock(path: string): Promise<void> {
|
|
11
11
|
return new Promise(resolve => {
|
|
12
|
-
if (this.
|
|
13
|
-
this.
|
|
12
|
+
if (this.locks.has(path)) {
|
|
13
|
+
this.locks.get(path)!.push(resolve);
|
|
14
14
|
} else {
|
|
15
|
-
this.
|
|
15
|
+
this.locks.set(path, [resolve]);
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
public unlock(path: string): void {
|
|
21
|
-
if (!this.
|
|
21
|
+
if (!this.locks.has(path)) {
|
|
22
22
|
throw new ErrnoError(Errno.EPERM, 'Can not unlock an already unlocked path', path);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const next = this.
|
|
25
|
+
const next = this.locks.get(path)?.shift();
|
|
26
26
|
/*
|
|
27
27
|
don't unlock - we want to queue up next for the
|
|
28
28
|
end of the current task execution, but we don't
|
|
@@ -32,23 +32,23 @@ export class Mutex {
|
|
|
32
32
|
lock won't cause starvation.
|
|
33
33
|
*/
|
|
34
34
|
if (next) {
|
|
35
|
-
setTimeout(next
|
|
35
|
+
setTimeout(next);
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
this.
|
|
39
|
+
this.locks.delete(path);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
public tryLock(path: string): boolean {
|
|
43
|
-
if (this.
|
|
43
|
+
if (this.locks.has(path)) {
|
|
44
44
|
return false;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
this.
|
|
47
|
+
this.locks.set(path, []);
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
public isLocked(path: string): boolean {
|
|
52
|
-
return this.
|
|
52
|
+
return this.locks.has(path);
|
|
53
53
|
}
|
|
54
54
|
}
|