@zenfs/core 0.13.0 → 0.14.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/locked.d.ts +50 -2
- package/dist/backends/locked.js +407 -74
- package/dist/backends/overlay.js +4 -1
- package/dist/backends/store/store.d.ts +1 -0
- package/dist/backends/store/store.js +1 -0
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/emulation/promises.d.ts +1 -0
- package/dist/emulation/promises.js +156 -33
- package/dist/emulation/sync.js +100 -31
- package/dist/file.d.ts +1 -0
- package/dist/file.js +1 -0
- package/dist/filesystem.js +55 -4
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/symbol-dispose.d.ts +1 -0
- package/dist/symbol-dispose.js +10 -0
- package/package.json +4 -4
- package/scripts/make-index.js +2 -1
- package/src/backends/locked.ts +152 -70
- package/src/backends/overlay.ts +4 -1
- package/src/backends/store/store.ts +1 -0
- package/src/emulation/promises.ts +29 -72
- package/src/emulation/sync.ts +16 -28
- package/src/file.ts +1 -0
- package/src/filesystem.ts +5 -10
- package/src/index.ts +0 -1
- package/src/symbol-dispose.ts +9 -0
- package/dist/mutex.d.ts +0 -11
- package/dist/mutex.js +0 -49
- package/src/mutex.ts +0 -54
package/scripts/make-index.js
CHANGED
|
@@ -78,9 +78,9 @@ function computeEntries(path) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const stats = statSync(path);
|
|
81
|
-
entries.set('/' + relative(resolvedRoot, path), stats);
|
|
82
81
|
|
|
83
82
|
if (stats.isFile()) {
|
|
83
|
+
entries.set('/' + relative(resolvedRoot, path), stats);
|
|
84
84
|
if (options.verbose) {
|
|
85
85
|
console.log(`${color('green', 'file')} ${path}`);
|
|
86
86
|
}
|
|
@@ -90,6 +90,7 @@ function computeEntries(path) {
|
|
|
90
90
|
for (const file of readdirSync(path)) {
|
|
91
91
|
computeEntries(join(path, file));
|
|
92
92
|
}
|
|
93
|
+
entries.set('/' + relative(resolvedRoot, path), stats);
|
|
93
94
|
if (options.verbose) {
|
|
94
95
|
console.log(`${color('bright_green', ' dir')} ${path}`);
|
|
95
96
|
}
|
package/src/backends/locked.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { ErrnoError } from '../error.js';
|
|
2
1
|
import type { Cred } from '../cred.js';
|
|
2
|
+
import { Errno, ErrnoError } from '../error.js';
|
|
3
3
|
import type { File } from '../file.js';
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
4
|
+
import type { FileSystemMetadata } from '../filesystem.js';
|
|
5
|
+
import { FileSystem } from '../filesystem.js';
|
|
6
6
|
import type { Stats } from '../stats.js';
|
|
7
|
+
import type { Backend } from './backend.js';
|
|
8
|
+
|
|
9
|
+
export interface MutexLock extends PromiseWithResolvers<void> {
|
|
10
|
+
[Symbol.dispose](): void;
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* This class serializes access to an underlying async filesystem.
|
|
@@ -16,10 +21,81 @@ import type { Stats } from '../stats.js';
|
|
|
16
21
|
* @internal
|
|
17
22
|
*/
|
|
18
23
|
export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
19
|
-
private mutex: Mutex = new Mutex();
|
|
20
|
-
|
|
21
24
|
constructor(public readonly fs: FS) {}
|
|
22
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The current locks
|
|
28
|
+
*/
|
|
29
|
+
private locks: Map<string, MutexLock> = new Map();
|
|
30
|
+
|
|
31
|
+
protected addLock(path: string): MutexLock {
|
|
32
|
+
const lock: MutexLock = {
|
|
33
|
+
...Promise.withResolvers(),
|
|
34
|
+
[Symbol.dispose]: () => {
|
|
35
|
+
this.unlock(path);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
this.locks.set(path, lock);
|
|
39
|
+
return lock;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Locks `path` asynchronously.
|
|
44
|
+
* If the path is currently locked, waits for it to be unlocked.
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
public async lock(path: string): Promise<MutexLock> {
|
|
48
|
+
if (this.locks.has(path)) {
|
|
49
|
+
// Non-null assertion: we already checked locks has path
|
|
50
|
+
await this.locks.get(path)!.promise;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this.addLock(path);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Locks `path` asynchronously.
|
|
58
|
+
* If the path is currently locked, an error will be thrown
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
public lockSync(path: string): MutexLock {
|
|
62
|
+
if (this.locks.has(path)) {
|
|
63
|
+
// Non-null assertion: we already checked locks has path
|
|
64
|
+
throw ErrnoError.With('EBUSY', path, 'lockSync');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this.addLock(path);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Unlocks a path
|
|
72
|
+
* @param path The path to lock
|
|
73
|
+
* @param noThrow If true, an error will not be thrown if the path is already unlocked
|
|
74
|
+
* @returns Whether the path was unlocked
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
public unlock(path: string, noThrow: boolean = false): boolean {
|
|
78
|
+
if (!this.locks.has(path)) {
|
|
79
|
+
if (noThrow) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
throw new ErrnoError(Errno.EPERM, 'Can not unlock an already unlocked path', path);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Non-null assertion: we already checked locks has path
|
|
86
|
+
this.locks.get(path)!.resolve();
|
|
87
|
+
this.locks.delete(path);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether `path` is locked
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
public isLocked(path: string): boolean {
|
|
96
|
+
return this.locks.has(path);
|
|
97
|
+
}
|
|
98
|
+
|
|
23
99
|
public async ready(): Promise<void> {
|
|
24
100
|
await this.fs.ready();
|
|
25
101
|
}
|
|
@@ -32,150 +108,156 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
|
|
|
32
108
|
}
|
|
33
109
|
|
|
34
110
|
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
35
|
-
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
112
|
+
using _ = await this.lock(oldPath);
|
|
36
113
|
await this.fs.rename(oldPath, newPath, cred);
|
|
37
|
-
this.mutex.unlock(oldPath);
|
|
38
114
|
}
|
|
39
115
|
|
|
40
116
|
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
118
|
+
using _ = this.lockSync(oldPath);
|
|
44
119
|
return this.fs.renameSync(oldPath, newPath, cred);
|
|
45
120
|
}
|
|
46
121
|
|
|
47
122
|
public async stat(path: string, cred: Cred): Promise<Stats> {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
return stats;
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
124
|
+
using _ = await this.lock(path);
|
|
125
|
+
return await this.fs.stat(path, cred);
|
|
52
126
|
}
|
|
53
127
|
|
|
54
128
|
public statSync(path: string, cred: Cred): Stats {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
130
|
+
using _ = this.lockSync(path);
|
|
58
131
|
return this.fs.statSync(path, cred);
|
|
59
132
|
}
|
|
60
133
|
|
|
61
134
|
public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
return fd;
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
136
|
+
using _ = await this.lock(path);
|
|
137
|
+
return await this.fs.openFile(path, flag, cred);
|
|
66
138
|
}
|
|
67
139
|
|
|
68
140
|
public openFileSync(path: string, flag: string, cred: Cred): File {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
142
|
+
using _ = this.lockSync(path);
|
|
72
143
|
return this.fs.openFileSync(path, flag, cred);
|
|
73
144
|
}
|
|
74
145
|
|
|
75
146
|
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.
|
|
79
|
-
return fd;
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
148
|
+
using _ = await this.lock(path);
|
|
149
|
+
return await this.fs.createFile(path, flag, mode, cred);
|
|
80
150
|
}
|
|
81
151
|
|
|
82
152
|
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
154
|
+
using _ = this.lockSync(path);
|
|
86
155
|
return this.fs.createFileSync(path, flag, mode, cred);
|
|
87
156
|
}
|
|
88
157
|
|
|
89
158
|
public async unlink(path: string, cred: Cred): Promise<void> {
|
|
90
|
-
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
160
|
+
using _ = await this.lock(path);
|
|
91
161
|
await this.fs.unlink(path, cred);
|
|
92
|
-
this.mutex.unlock(path);
|
|
93
162
|
}
|
|
94
163
|
|
|
95
164
|
public unlinkSync(path: string, cred: Cred): void {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
166
|
+
using _ = this.lockSync(path);
|
|
99
167
|
return this.fs.unlinkSync(path, cred);
|
|
100
168
|
}
|
|
101
169
|
|
|
102
170
|
public async rmdir(path: string, cred: Cred): Promise<void> {
|
|
103
|
-
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
172
|
+
using _ = await this.lock(path);
|
|
104
173
|
await this.fs.rmdir(path, cred);
|
|
105
|
-
this.mutex.unlock(path);
|
|
106
174
|
}
|
|
107
175
|
|
|
108
176
|
public rmdirSync(path: string, cred: Cred): void {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
178
|
+
using _ = this.lockSync(path);
|
|
112
179
|
return this.fs.rmdirSync(path, cred);
|
|
113
180
|
}
|
|
114
181
|
|
|
115
182
|
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
|
|
116
|
-
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
184
|
+
using _ = await this.lock(path);
|
|
117
185
|
await this.fs.mkdir(path, mode, cred);
|
|
118
|
-
this.mutex.unlock(path);
|
|
119
186
|
}
|
|
120
187
|
|
|
121
188
|
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
190
|
+
using _ = this.lockSync(path);
|
|
125
191
|
return this.fs.mkdirSync(path, mode, cred);
|
|
126
192
|
}
|
|
127
193
|
|
|
128
194
|
public async readdir(path: string, cred: Cred): Promise<string[]> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.
|
|
132
|
-
return files;
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
196
|
+
using _ = await this.lock(path);
|
|
197
|
+
return await this.fs.readdir(path, cred);
|
|
133
198
|
}
|
|
134
199
|
|
|
135
200
|
public readdirSync(path: string, cred: Cred): string[] {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
202
|
+
using _ = this.lockSync(path);
|
|
139
203
|
return this.fs.readdirSync(path, cred);
|
|
140
204
|
}
|
|
141
205
|
|
|
142
206
|
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.
|
|
146
|
-
return exists;
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
208
|
+
using _ = await this.lock(path);
|
|
209
|
+
return await this.fs.exists(path, cred);
|
|
147
210
|
}
|
|
148
211
|
|
|
149
212
|
public existsSync(path: string, cred: Cred): boolean {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
213
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
214
|
+
using _ = this.lockSync(path);
|
|
153
215
|
return this.fs.existsSync(path, cred);
|
|
154
216
|
}
|
|
155
217
|
|
|
156
218
|
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
|
|
157
|
-
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
220
|
+
using _ = await this.lock(srcpath);
|
|
158
221
|
await this.fs.link(srcpath, dstpath, cred);
|
|
159
|
-
this.mutex.unlock(srcpath);
|
|
160
222
|
}
|
|
161
223
|
|
|
162
224
|
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
226
|
+
using _ = this.lockSync(srcpath);
|
|
166
227
|
return this.fs.linkSync(srcpath, dstpath, cred);
|
|
167
228
|
}
|
|
168
229
|
|
|
169
230
|
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
170
|
-
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
232
|
+
using _ = await this.lock(path);
|
|
171
233
|
await this.fs.sync(path, data, stats);
|
|
172
|
-
this.mutex.unlock(path);
|
|
173
234
|
}
|
|
174
235
|
|
|
175
236
|
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
238
|
+
using _ = this.lockSync(path);
|
|
179
239
|
return this.fs.syncSync(path, data, stats);
|
|
180
240
|
}
|
|
181
241
|
}
|
|
242
|
+
|
|
243
|
+
export const Locked = {
|
|
244
|
+
name: 'Locked',
|
|
245
|
+
options: {
|
|
246
|
+
fs: {
|
|
247
|
+
type: 'object',
|
|
248
|
+
required: true,
|
|
249
|
+
description: '',
|
|
250
|
+
validator(fs) {
|
|
251
|
+
if (!(fs instanceof FileSystem)) {
|
|
252
|
+
throw new ErrnoError(Errno.EINVAL, 'fs passed to LockedFS must be a FileSystem');
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
isAvailable() {
|
|
258
|
+
return true;
|
|
259
|
+
},
|
|
260
|
+
create({ fs }) {
|
|
261
|
+
return new LockedFS(fs);
|
|
262
|
+
},
|
|
263
|
+
} satisfies Backend<LockedFS<FileSystem>, { fs: FileSystem }>;
|
package/src/backends/overlay.ts
CHANGED
|
@@ -86,6 +86,9 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
86
86
|
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
87
87
|
const cred = stats.cred(0, 0);
|
|
88
88
|
await this.createParentDirectories(path, cred);
|
|
89
|
+
if (!(await this._writable.exists(path, cred))) {
|
|
90
|
+
await this._writable.createFile(path, 'w', 0o644, cred);
|
|
91
|
+
}
|
|
89
92
|
await this._writable.sync(path, data, stats);
|
|
90
93
|
}
|
|
91
94
|
|
|
@@ -160,7 +163,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
160
163
|
public async stat(path: string, cred: Cred): Promise<Stats> {
|
|
161
164
|
this.checkInitialized();
|
|
162
165
|
try {
|
|
163
|
-
return this._writable.stat(path, cred);
|
|
166
|
+
return await this._writable.stat(path, cred);
|
|
164
167
|
} catch (e) {
|
|
165
168
|
if (this._deletedFiles.has(path)) {
|
|
166
169
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
@@ -18,6 +18,7 @@ import { dirname, join, parse } from './path.js';
|
|
|
18
18
|
import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
|
|
19
19
|
import { ReadStream, WriteStream } from './streams.js';
|
|
20
20
|
export * as constants from './constants.js';
|
|
21
|
+
import '../symbol-dispose.js';
|
|
21
22
|
|
|
22
23
|
export class FileHandle implements promises.FileHandle {
|
|
23
24
|
/**
|
|
@@ -456,12 +457,8 @@ lstat satisfies typeof promises.lstat;
|
|
|
456
457
|
* @param len
|
|
457
458
|
*/
|
|
458
459
|
export async function truncate(path: fs.PathLike, len: number = 0): Promise<void> {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
await handle.truncate(len);
|
|
462
|
-
} finally {
|
|
463
|
-
await handle.close();
|
|
464
|
-
}
|
|
460
|
+
await using handle = await open(path, 'r+');
|
|
461
|
+
await handle.truncate(len);
|
|
465
462
|
}
|
|
466
463
|
truncate satisfies typeof promises.truncate;
|
|
467
464
|
|
|
@@ -554,13 +551,8 @@ export async function readFile(
|
|
|
554
551
|
_options?: (fs.ObjectEncodingOptions & { flag?: fs.OpenMode }) | BufferEncoding | null
|
|
555
552
|
): Promise<Buffer | string> {
|
|
556
553
|
const options = normalizeOptions(_options, null, 'r', 0o644);
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
try {
|
|
560
|
-
return await handle.readFile(options);
|
|
561
|
-
} finally {
|
|
562
|
-
await handle.close();
|
|
563
|
-
}
|
|
554
|
+
await using handle: FileHandle | promises.FileHandle = typeof path == 'object' && 'fd' in path ? path : await open(path as string, options.flag, options.mode);
|
|
555
|
+
return await handle.readFile(options);
|
|
564
556
|
}
|
|
565
557
|
readFile satisfies typeof promises.readFile;
|
|
566
558
|
|
|
@@ -581,16 +573,13 @@ export async function writeFile(
|
|
|
581
573
|
_options?: (fs.ObjectEncodingOptions & { mode?: fs.Mode; flag?: fs.OpenMode; flush?: boolean }) | BufferEncoding | null
|
|
582
574
|
): Promise<void> {
|
|
583
575
|
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
590
|
-
await handle.writeFile(_data, options);
|
|
591
|
-
} finally {
|
|
592
|
-
await handle.close();
|
|
576
|
+
await using handle = path instanceof FileHandle ? path : await open(path.toString(), options.flag, options.mode);
|
|
577
|
+
|
|
578
|
+
const _data = typeof data == 'string' ? data : data;
|
|
579
|
+
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
|
|
580
|
+
throw new ErrnoError(Errno.EINVAL, 'Iterables and streams not supported', handle.file.path, 'writeFile');
|
|
593
581
|
}
|
|
582
|
+
await handle.writeFile(_data, options);
|
|
594
583
|
}
|
|
595
584
|
writeFile satisfies typeof promises.writeFile;
|
|
596
585
|
|
|
@@ -618,13 +607,9 @@ export async function appendFile(
|
|
|
618
607
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
619
608
|
}
|
|
620
609
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
621
|
-
|
|
610
|
+
await using handle: FileHandle | promises.FileHandle = typeof path == 'object' && 'fd' in path ? path : await open(path as string, options.flag, options.mode);
|
|
622
611
|
|
|
623
|
-
|
|
624
|
-
await handle.appendFile(encodedData, options);
|
|
625
|
-
} finally {
|
|
626
|
-
await handle.close();
|
|
627
|
-
}
|
|
612
|
+
await handle.appendFile(encodedData, options);
|
|
628
613
|
}
|
|
629
614
|
appendFile satisfies typeof promises.appendFile;
|
|
630
615
|
|
|
@@ -775,14 +760,10 @@ export async function readlink(path: fs.PathLike, options: fs.BufferEncodingOpti
|
|
|
775
760
|
export async function readlink(path: fs.PathLike, options?: fs.EncodingOption | null): Promise<string>;
|
|
776
761
|
export async function readlink(path: fs.PathLike, options?: fs.BufferEncodingOption | fs.EncodingOption | string | null): Promise<string | Buffer>;
|
|
777
762
|
export async function readlink(path: fs.PathLike, options?: fs.BufferEncodingOption | fs.EncodingOption | string | null): Promise<string | Buffer> {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
return encoding == 'buffer' ? value : value.toString(encoding! as BufferEncoding);
|
|
783
|
-
} finally {
|
|
784
|
-
await handle.close();
|
|
785
|
-
}
|
|
763
|
+
await using handle = await _open(normalizePath(path), 'r', 0o644, false);
|
|
764
|
+
const value = await handle.readFile();
|
|
765
|
+
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
766
|
+
return encoding == 'buffer' ? value : value.toString(encoding! as BufferEncoding);
|
|
786
767
|
}
|
|
787
768
|
readlink satisfies typeof promises.readlink;
|
|
788
769
|
|
|
@@ -795,12 +776,8 @@ readlink satisfies typeof promises.readlink;
|
|
|
795
776
|
* @param gid
|
|
796
777
|
*/
|
|
797
778
|
export async function chown(path: fs.PathLike, uid: number, gid: number): Promise<void> {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
await handle.chown(uid, gid);
|
|
801
|
-
} finally {
|
|
802
|
-
await handle.close();
|
|
803
|
-
}
|
|
779
|
+
await using handle = await open(path, 'r+');
|
|
780
|
+
await handle.chown(uid, gid);
|
|
804
781
|
}
|
|
805
782
|
chown satisfies typeof promises.chown;
|
|
806
783
|
|
|
@@ -811,12 +788,8 @@ chown satisfies typeof promises.chown;
|
|
|
811
788
|
* @param gid
|
|
812
789
|
*/
|
|
813
790
|
export async function lchown(path: fs.PathLike, uid: number, gid: number): Promise<void> {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
await handle.chown(uid, gid);
|
|
817
|
-
} finally {
|
|
818
|
-
await handle.close();
|
|
819
|
-
}
|
|
791
|
+
await using handle: FileHandle = await _open(path, 'r+', 0o644, false);
|
|
792
|
+
await handle.chown(uid, gid);
|
|
820
793
|
}
|
|
821
794
|
lchown satisfies typeof promises.lchown;
|
|
822
795
|
|
|
@@ -826,12 +799,8 @@ lchown satisfies typeof promises.lchown;
|
|
|
826
799
|
* @param mode
|
|
827
800
|
*/
|
|
828
801
|
export async function chmod(path: fs.PathLike, mode: fs.Mode): Promise<void> {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
await handle.chmod(mode);
|
|
832
|
-
} finally {
|
|
833
|
-
await handle.close();
|
|
834
|
-
}
|
|
802
|
+
await using handle = await open(path, 'r+');
|
|
803
|
+
await handle.chmod(mode);
|
|
835
804
|
}
|
|
836
805
|
chmod satisfies typeof promises.chmod;
|
|
837
806
|
|
|
@@ -841,12 +810,8 @@ chmod satisfies typeof promises.chmod;
|
|
|
841
810
|
* @param mode
|
|
842
811
|
*/
|
|
843
812
|
export async function lchmod(path: fs.PathLike, mode: fs.Mode): Promise<void> {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
await handle.chmod(mode);
|
|
847
|
-
} finally {
|
|
848
|
-
await handle.close();
|
|
849
|
-
}
|
|
813
|
+
await using handle: FileHandle = await _open(path, 'r+', 0o644, false);
|
|
814
|
+
await handle.chmod(mode);
|
|
850
815
|
}
|
|
851
816
|
lchmod satisfies typeof promises.lchmod;
|
|
852
817
|
|
|
@@ -857,12 +822,8 @@ lchmod satisfies typeof promises.lchmod;
|
|
|
857
822
|
* @param mtime
|
|
858
823
|
*/
|
|
859
824
|
export async function utimes(path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date): Promise<void> {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
await handle.utimes(atime, mtime);
|
|
863
|
-
} finally {
|
|
864
|
-
await handle.close();
|
|
865
|
-
}
|
|
825
|
+
await using handle = await open(path, 'r+');
|
|
826
|
+
await handle.utimes(atime, mtime);
|
|
866
827
|
}
|
|
867
828
|
utimes satisfies typeof promises.utimes;
|
|
868
829
|
|
|
@@ -873,12 +834,8 @@ utimes satisfies typeof promises.utimes;
|
|
|
873
834
|
* @param mtime
|
|
874
835
|
*/
|
|
875
836
|
export async function lutimes(path: fs.PathLike, atime: fs.TimeLike, mtime: fs.TimeLike): Promise<void> {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
await handle.utimes(new Date(atime), new Date(mtime));
|
|
879
|
-
} finally {
|
|
880
|
-
await handle.close();
|
|
881
|
-
}
|
|
837
|
+
await using handle: FileHandle = await _open(path, 'r+', 0o644, false);
|
|
838
|
+
await handle.utimes(new Date(atime), new Date(mtime));
|
|
882
839
|
}
|
|
883
840
|
lutimes satisfies typeof promises.lutimes;
|
|
884
841
|
|
package/src/emulation/sync.ts
CHANGED
|
@@ -99,12 +99,12 @@ lstatSync satisfies typeof fs.lstatSync;
|
|
|
99
99
|
* @param len
|
|
100
100
|
*/
|
|
101
101
|
export function truncateSync(path: fs.PathLike, len: number | null = 0): void {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
closeSync(fd);
|
|
102
|
+
using file = _openSync(path, 'r+');
|
|
103
|
+
len ||= 0;
|
|
104
|
+
if (len < 0) {
|
|
105
|
+
throw new ErrnoError(Errno.EINVAL);
|
|
107
106
|
}
|
|
107
|
+
file.truncateSync(len);
|
|
108
108
|
}
|
|
109
109
|
truncateSync satisfies typeof fs.truncateSync;
|
|
110
110
|
|
|
@@ -194,17 +194,13 @@ export function lopenSync(path: fs.PathLike, flag: string, mode?: fs.Mode | null
|
|
|
194
194
|
*/
|
|
195
195
|
function _readFileSync(fname: string, flag: string, resolveSymlinks: boolean): Uint8Array {
|
|
196
196
|
// Get file.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return data;
|
|
205
|
-
} finally {
|
|
206
|
-
file.closeSync();
|
|
207
|
-
}
|
|
197
|
+
using file = _openSync(fname, flag, 0o644, resolveSymlinks);
|
|
198
|
+
const stat = file.statSync();
|
|
199
|
+
// Allocate buffer.
|
|
200
|
+
const data = new Uint8Array(stat.size);
|
|
201
|
+
file.readSync(data, 0, stat.size, 0);
|
|
202
|
+
file.closeSync();
|
|
203
|
+
return data;
|
|
208
204
|
}
|
|
209
205
|
|
|
210
206
|
/**
|
|
@@ -255,12 +251,8 @@ export function writeFileSync(path: fs.PathOrFileDescriptor, data: FileContents,
|
|
|
255
251
|
if (!encodedData) {
|
|
256
252
|
throw new ErrnoError(Errno.EINVAL, 'Data not specified');
|
|
257
253
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
file.writeSync(encodedData, 0, encodedData.byteLength, 0);
|
|
261
|
-
} finally {
|
|
262
|
-
file.closeSync();
|
|
263
|
-
}
|
|
254
|
+
using file = _openSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), flag, options.mode, true);
|
|
255
|
+
file.writeSync(encodedData, 0, encodedData.byteLength, 0);
|
|
264
256
|
}
|
|
265
257
|
writeFileSync satisfies typeof fs.writeFileSync;
|
|
266
258
|
|
|
@@ -285,12 +277,8 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
|
|
|
285
277
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
286
278
|
}
|
|
287
279
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
file.writeSync(encodedData, 0, encodedData.byteLength);
|
|
291
|
-
} finally {
|
|
292
|
-
file.closeSync();
|
|
293
|
-
}
|
|
280
|
+
using file = _openSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), flag, options.mode, true);
|
|
281
|
+
file.writeSync(encodedData, 0, encodedData.byteLength);
|
|
294
282
|
}
|
|
295
283
|
appendFileSync satisfies typeof fs.appendFileSync;
|
|
296
284
|
|
package/src/file.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Errno, ErrnoError } from './error.js';
|
|
|
4
4
|
import type { FileSystem } from './filesystem.js';
|
|
5
5
|
import { size_max } from './inode.js';
|
|
6
6
|
import { Stats, type FileType } from './stats.js';
|
|
7
|
+
import './symbol-dispose.js';
|
|
7
8
|
|
|
8
9
|
/*
|
|
9
10
|
Typescript does not include a type declaration for resizable array buffers.
|
package/src/filesystem.ts
CHANGED
|
@@ -463,16 +463,11 @@ export function Async<T extends typeof FileSystem>(
|
|
|
463
463
|
await this.crossCopy(join(path, file));
|
|
464
464
|
}
|
|
465
465
|
} else {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
syncFile.writeSync(buffer, 0, stats.size);
|
|
472
|
-
} finally {
|
|
473
|
-
await asyncFile.close();
|
|
474
|
-
syncFile.closeSync();
|
|
475
|
-
}
|
|
466
|
+
await using asyncFile = await this.openFile(path, parseFlag('r'), rootCred);
|
|
467
|
+
using syncFile = this._sync.createFileSync(path, parseFlag('w'), stats.mode, stats.cred());
|
|
468
|
+
const buffer = new Uint8Array(stats.size);
|
|
469
|
+
await asyncFile.read(buffer);
|
|
470
|
+
syncFile.writeSync(buffer, 0, stats.size);
|
|
476
471
|
}
|
|
477
472
|
}
|
|
478
473
|
|