@zenfs/core 0.9.6 → 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.d.ts +3 -2
- package/dist/backends/AsyncStore.js +40 -33
- package/dist/backends/Fetch.d.ts +84 -0
- package/dist/backends/Fetch.js +171 -0
- package/dist/backends/InMemory.d.ts +1 -1
- package/dist/backends/Index.d.ts +7 -10
- package/dist/backends/Index.js +26 -24
- package/dist/backends/Locked.d.ts +11 -11
- package/dist/backends/Locked.js +50 -49
- package/dist/backends/Overlay.js +22 -22
- package/dist/backends/SyncStore.d.ts +6 -6
- package/dist/backends/SyncStore.js +30 -30
- package/dist/backends/backend.d.ts +5 -4
- package/dist/backends/backend.js +6 -6
- 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 +9 -11
- package/dist/config.js +13 -13
- package/dist/emulation/async.d.ts +76 -77
- package/dist/emulation/async.js +45 -45
- package/dist/emulation/dir.js +8 -7
- 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 +112 -113
- package/dist/emulation/promises.js +167 -173
- package/dist/emulation/shared.d.ts +6 -17
- package/dist/emulation/shared.js +9 -9
- package/dist/emulation/streams.js +3 -2
- package/dist/emulation/sync.d.ts +71 -64
- package/dist/emulation/sync.js +62 -63
- package/dist/{ApiError.d.ts → error.d.ts} +15 -15
- package/dist/error.js +292 -0
- package/dist/file.d.ts +6 -4
- package/dist/file.js +17 -9
- package/dist/filesystem.d.ts +1 -1
- package/dist/filesystem.js +18 -15
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/mutex.js +4 -3
- package/dist/stats.d.ts +7 -7
- package/dist/stats.js +50 -10
- package/dist/utils.d.ts +10 -9
- package/dist/utils.js +15 -15
- package/package.json +5 -5
- package/readme.md +19 -11
- package/src/backends/AsyncStore.ts +42 -36
- package/src/backends/Fetch.ts +230 -0
- package/src/backends/Index.ts +33 -29
- package/src/backends/Locked.ts +50 -49
- package/src/backends/Overlay.ts +24 -24
- package/src/backends/SyncStore.ts +34 -34
- package/src/backends/backend.ts +13 -11
- 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 +25 -29
- package/src/emulation/async.ts +191 -199
- package/src/emulation/dir.ts +8 -8
- package/src/emulation/index.ts +1 -1
- package/src/emulation/path.ts +25 -49
- package/src/emulation/promises.ts +286 -287
- package/src/emulation/shared.ts +14 -23
- package/src/emulation/streams.ts +9 -8
- package/src/emulation/sync.ts +182 -182
- package/src/{ApiError.ts → error.ts} +91 -89
- package/src/file.ts +23 -13
- package/src/filesystem.ts +26 -22
- package/src/index.ts +4 -1
- package/src/mutex.ts +6 -4
- package/src/stats.ts +32 -23
- package/src/utils.ts +23 -24
- package/tsconfig.json +4 -3
- package/dist/ApiError.js +0 -292
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
|
-
import {
|
|
2
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
3
3
|
import { ActionType, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
|
|
4
4
|
import { BigIntStats, FileType } from '../stats.js';
|
|
5
5
|
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
6
6
|
import * as constants from './constants.js';
|
|
7
7
|
import { Dir, Dirent } from './dir.js';
|
|
8
8
|
import { dirname, join, parse } from './path.js';
|
|
9
|
-
import { cred, fd2file, fdMap,
|
|
9
|
+
import { cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
|
|
10
10
|
import { ReadStream, WriteStream } from './streams.js';
|
|
11
11
|
export * as constants from './constants.js';
|
|
12
12
|
export class FileHandle {
|
|
13
|
-
constructor(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
fd) {
|
|
18
|
-
this.fd = fd;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* @internal
|
|
22
|
-
*/
|
|
23
|
-
get file() {
|
|
24
|
-
return fd2file(this.fd);
|
|
13
|
+
constructor(fdOrFile) {
|
|
14
|
+
const isFile = typeof fdOrFile != 'number';
|
|
15
|
+
this.fd = isFile ? file2fd(fdOrFile) : fdOrFile;
|
|
16
|
+
this.file = isFile ? fdOrFile : fd2file(fdOrFile);
|
|
25
17
|
}
|
|
26
18
|
/**
|
|
27
19
|
* Asynchronous fchown(2) - Change ownership of a file.
|
|
@@ -36,7 +28,7 @@ export class FileHandle {
|
|
|
36
28
|
chmod(mode) {
|
|
37
29
|
const numMode = normalizeMode(mode, -1);
|
|
38
30
|
if (numMode < 0) {
|
|
39
|
-
throw new
|
|
31
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid mode.');
|
|
40
32
|
}
|
|
41
33
|
return this.file.chmod(numMode);
|
|
42
34
|
}
|
|
@@ -57,8 +49,9 @@ export class FileHandle {
|
|
|
57
49
|
* @param len If not specified, defaults to `0`.
|
|
58
50
|
*/
|
|
59
51
|
truncate(len) {
|
|
52
|
+
len || (len = 0);
|
|
60
53
|
if (len < 0) {
|
|
61
|
-
throw new
|
|
54
|
+
throw new ErrnoError(Errno.EINVAL);
|
|
62
55
|
}
|
|
63
56
|
return this.file.truncate(len);
|
|
64
57
|
}
|
|
@@ -80,14 +73,14 @@ export class FileHandle {
|
|
|
80
73
|
* If `mode` is a string, it is parsed as an octal integer.
|
|
81
74
|
* If `flag` is not supplied, the default of `'a'` is used.
|
|
82
75
|
*/
|
|
83
|
-
async appendFile(data, _options) {
|
|
76
|
+
async appendFile(data, _options = {}) {
|
|
84
77
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
85
78
|
const flag = parseFlag(options.flag);
|
|
86
79
|
if (!isAppendable(flag)) {
|
|
87
|
-
throw new
|
|
80
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
88
81
|
}
|
|
89
82
|
if (typeof data != 'string' && !options.encoding) {
|
|
90
|
-
throw new
|
|
83
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
91
84
|
}
|
|
92
85
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
93
86
|
await this.file.write(encodedData, 0, encodedData.length, null);
|
|
@@ -110,7 +103,7 @@ export class FileHandle {
|
|
|
110
103
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
111
104
|
const flag = parseFlag(options.flag);
|
|
112
105
|
if (!isReadable(flag)) {
|
|
113
|
-
throw new
|
|
106
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading.');
|
|
114
107
|
}
|
|
115
108
|
const { size } = await this.stat();
|
|
116
109
|
const data = new Uint8Array(size);
|
|
@@ -129,14 +122,14 @@ export class FileHandle {
|
|
|
129
122
|
* @since v17.0.0
|
|
130
123
|
* @experimental
|
|
131
124
|
*/
|
|
132
|
-
readableWebStream(options) {
|
|
125
|
+
readableWebStream(options = {}) {
|
|
133
126
|
// Note: using an arrow function to preserve `this`
|
|
134
127
|
const start = async ({ close, enqueue, error }) => {
|
|
135
128
|
try {
|
|
136
129
|
const chunkSize = 64 * 1024, maxChunks = 1e7;
|
|
137
|
-
let i = 0, position = 0,
|
|
138
|
-
while (
|
|
139
|
-
result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
|
|
130
|
+
let i = 0, position = 0, bytesRead = NaN;
|
|
131
|
+
while (bytesRead > 0) {
|
|
132
|
+
const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
|
|
140
133
|
if (!result.bytesRead) {
|
|
141
134
|
close();
|
|
142
135
|
return;
|
|
@@ -144,8 +137,9 @@ export class FileHandle {
|
|
|
144
137
|
enqueue(result.buffer.slice(0, result.bytesRead));
|
|
145
138
|
position += result.bytesRead;
|
|
146
139
|
if (++i >= maxChunks) {
|
|
147
|
-
throw new
|
|
140
|
+
throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
|
|
148
141
|
}
|
|
142
|
+
bytesRead = result.bytesRead;
|
|
149
143
|
}
|
|
150
144
|
}
|
|
151
145
|
catch (e) {
|
|
@@ -155,7 +149,7 @@ export class FileHandle {
|
|
|
155
149
|
return new globalThis.ReadableStream({ start, type: options.type });
|
|
156
150
|
}
|
|
157
151
|
readLines(options) {
|
|
158
|
-
throw
|
|
152
|
+
throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
|
|
159
153
|
}
|
|
160
154
|
[Symbol.asyncDispose]() {
|
|
161
155
|
return this.close();
|
|
@@ -165,7 +159,7 @@ export class FileHandle {
|
|
|
165
159
|
return opts?.bigint ? new BigIntStats(stats) : stats;
|
|
166
160
|
}
|
|
167
161
|
async write(data, posOrOff, lenOrEnc, position) {
|
|
168
|
-
let buffer, offset
|
|
162
|
+
let buffer, offset, length;
|
|
169
163
|
if (typeof data === 'string') {
|
|
170
164
|
// Signature 1: (fd, string, [position?, [encoding?]])
|
|
171
165
|
position = typeof posOrOff === 'number' ? posOrOff : null;
|
|
@@ -176,7 +170,7 @@ export class FileHandle {
|
|
|
176
170
|
}
|
|
177
171
|
else {
|
|
178
172
|
// Signature 2: (fd, buffer, offset, length, position?)
|
|
179
|
-
buffer = data;
|
|
173
|
+
buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
180
174
|
offset = posOrOff;
|
|
181
175
|
length = lenOrEnc;
|
|
182
176
|
position = typeof position === 'number' ? position : null;
|
|
@@ -196,14 +190,14 @@ export class FileHandle {
|
|
|
196
190
|
* If `mode` is a string, it is parsed as an octal integer.
|
|
197
191
|
* If `flag` is not supplied, the default of `'w'` is used.
|
|
198
192
|
*/
|
|
199
|
-
async writeFile(data, _options) {
|
|
193
|
+
async writeFile(data, _options = {}) {
|
|
200
194
|
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
201
195
|
const flag = parseFlag(options.flag);
|
|
202
196
|
if (!isWriteable(flag)) {
|
|
203
|
-
throw new
|
|
197
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing.');
|
|
204
198
|
}
|
|
205
199
|
if (typeof data != 'string' && !options.encoding) {
|
|
206
|
-
throw new
|
|
200
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
207
201
|
}
|
|
208
202
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
209
203
|
await this.file.write(encodedData, 0, encodedData.length, 0);
|
|
@@ -248,9 +242,9 @@ export class FileHandle {
|
|
|
248
242
|
* @returns A `ReadStream` object.
|
|
249
243
|
*/
|
|
250
244
|
createReadStream(options) {
|
|
251
|
-
const
|
|
245
|
+
const stream = new ReadStream({
|
|
252
246
|
highWaterMark: options?.highWaterMark || 64 * 1024,
|
|
253
|
-
encoding: options
|
|
247
|
+
encoding: options.encoding,
|
|
254
248
|
read: async (size) => {
|
|
255
249
|
try {
|
|
256
250
|
const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
|
|
@@ -261,8 +255,7 @@ export class FileHandle {
|
|
|
261
255
|
stream.destroy(error);
|
|
262
256
|
}
|
|
263
257
|
},
|
|
264
|
-
};
|
|
265
|
-
const stream = new ReadStream(streamOptions);
|
|
258
|
+
});
|
|
266
259
|
stream.path = this.file.path;
|
|
267
260
|
return stream;
|
|
268
261
|
}
|
|
@@ -291,31 +284,6 @@ export class FileHandle {
|
|
|
291
284
|
return stream;
|
|
292
285
|
}
|
|
293
286
|
}
|
|
294
|
-
/**
|
|
295
|
-
* Utility for FS ops. It handles
|
|
296
|
-
* - path normalization (for the first parameter to the FS op)
|
|
297
|
-
* - path translation for errors
|
|
298
|
-
* - FS/mount point resolution
|
|
299
|
-
*
|
|
300
|
-
* It can't be used for functions which may operate on multiple mounted FSs or paths (e.g. `rename`)
|
|
301
|
-
* @param name the function name
|
|
302
|
-
* @param resolveSymlinks whether to resolve symlinks
|
|
303
|
-
* @param args the rest of the parameters are passed to the FS function. Note that the first parameter is required to be a path
|
|
304
|
-
* @returns
|
|
305
|
-
*/
|
|
306
|
-
async function doOp(...[name, resolveSymlinks, rawPath, ...args]) {
|
|
307
|
-
rawPath = normalizePath(rawPath);
|
|
308
|
-
const _path = resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath;
|
|
309
|
-
const { fs, path } = resolveMount(_path);
|
|
310
|
-
try {
|
|
311
|
-
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
312
|
-
return fs[name](path, ...args);
|
|
313
|
-
}
|
|
314
|
-
catch (e) {
|
|
315
|
-
throw fixError(e, { [path]: rawPath });
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
// fs.promises
|
|
319
287
|
/**
|
|
320
288
|
* Renames a file
|
|
321
289
|
* @param oldPath
|
|
@@ -341,28 +309,42 @@ export async function rename(oldPath, newPath) {
|
|
|
341
309
|
rename;
|
|
342
310
|
/**
|
|
343
311
|
* Test whether or not the given path exists by checking with the file system.
|
|
344
|
-
* @param
|
|
312
|
+
* @param path
|
|
345
313
|
*/
|
|
346
|
-
export async function exists(
|
|
314
|
+
export async function exists(path) {
|
|
347
315
|
try {
|
|
348
|
-
const { fs, path } = resolveMount(await realpath(
|
|
349
|
-
return await fs.exists(
|
|
316
|
+
const { fs, path: resolved } = resolveMount(await realpath(path));
|
|
317
|
+
return await fs.exists(resolved, cred);
|
|
350
318
|
}
|
|
351
319
|
catch (e) {
|
|
352
|
-
if (e.
|
|
320
|
+
if (e instanceof ErrnoError && e.code == 'ENOENT') {
|
|
353
321
|
return false;
|
|
354
322
|
}
|
|
355
323
|
throw e;
|
|
356
324
|
}
|
|
357
325
|
}
|
|
358
326
|
export async function stat(path, options) {
|
|
359
|
-
|
|
360
|
-
|
|
327
|
+
path = normalizePath(path);
|
|
328
|
+
const { fs, path: resolved } = resolveMount((await exists(path)) ? await realpath(path) : path);
|
|
329
|
+
try {
|
|
330
|
+
const stats = await fs.stat(resolved, cred);
|
|
331
|
+
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
332
|
+
}
|
|
333
|
+
catch (e) {
|
|
334
|
+
throw fixError(e, { [resolved]: path });
|
|
335
|
+
}
|
|
361
336
|
}
|
|
362
337
|
stat;
|
|
363
338
|
export async function lstat(path, options) {
|
|
364
|
-
|
|
365
|
-
|
|
339
|
+
path = normalizePath(path);
|
|
340
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
341
|
+
try {
|
|
342
|
+
const stats = await fs.stat(resolved, cred);
|
|
343
|
+
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
throw fixError(e, { [resolved]: path });
|
|
347
|
+
}
|
|
366
348
|
}
|
|
367
349
|
lstat;
|
|
368
350
|
// FILE-ONLY METHODS
|
|
@@ -386,19 +368,29 @@ truncate;
|
|
|
386
368
|
* @param path
|
|
387
369
|
*/
|
|
388
370
|
export async function unlink(path) {
|
|
389
|
-
|
|
371
|
+
path = normalizePath(path);
|
|
372
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
373
|
+
try {
|
|
374
|
+
await fs.unlink(resolved, cred);
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
throw fixError(e, { [resolved]: path });
|
|
378
|
+
}
|
|
390
379
|
}
|
|
391
380
|
unlink;
|
|
392
381
|
/**
|
|
393
382
|
* Opens a file. This helper handles the complexity of file flags.
|
|
394
383
|
* @internal
|
|
395
384
|
*/
|
|
396
|
-
async function _open(
|
|
397
|
-
|
|
385
|
+
async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
386
|
+
path = normalizePath(path);
|
|
387
|
+
const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
|
|
388
|
+
path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
|
|
389
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
398
390
|
try {
|
|
399
391
|
switch (pathExistsAction(flag)) {
|
|
400
392
|
case ActionType.THROW:
|
|
401
|
-
throw
|
|
393
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
402
394
|
case ActionType.TRUNCATE:
|
|
403
395
|
/*
|
|
404
396
|
In a previous implementation, we deleted the file and
|
|
@@ -406,33 +398,30 @@ async function _open(_path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
|
406
398
|
asynchronous request was trying to read the file, as the file
|
|
407
399
|
would not exist for a small period of time.
|
|
408
400
|
*/
|
|
409
|
-
const file = await
|
|
410
|
-
if (!file) {
|
|
411
|
-
throw new ApiError(ErrorCode.EIO, 'Impossible code path reached');
|
|
412
|
-
}
|
|
401
|
+
const file = await fs.openFile(resolved, flag, cred);
|
|
413
402
|
await file.truncate(0);
|
|
414
403
|
await file.sync();
|
|
415
|
-
return file;
|
|
404
|
+
return new FileHandle(file);
|
|
416
405
|
case ActionType.NOP:
|
|
417
406
|
// Must await so thrown errors are caught by the catch below
|
|
418
|
-
return await
|
|
407
|
+
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
419
408
|
default:
|
|
420
|
-
throw new
|
|
409
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
421
410
|
}
|
|
422
411
|
}
|
|
423
412
|
catch (e) {
|
|
424
413
|
switch (pathNotExistsAction(flag)) {
|
|
425
414
|
case ActionType.CREATE:
|
|
426
415
|
// Ensure parent exists.
|
|
427
|
-
const parentStats = await
|
|
416
|
+
const parentStats = await fs.stat(dirname(resolved), cred);
|
|
428
417
|
if (parentStats && !parentStats.isDirectory()) {
|
|
429
|
-
throw
|
|
418
|
+
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
430
419
|
}
|
|
431
|
-
return await
|
|
420
|
+
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
|
|
432
421
|
case ActionType.THROW:
|
|
433
|
-
throw
|
|
422
|
+
throw ErrnoError.With('ENOENT', path, '_open');
|
|
434
423
|
default:
|
|
435
|
-
throw new
|
|
424
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
436
425
|
}
|
|
437
426
|
}
|
|
438
427
|
}
|
|
@@ -442,102 +431,74 @@ async function _open(_path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
|
442
431
|
* @param flags Handles the complexity of the various file modes. See its API for more details.
|
|
443
432
|
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
444
433
|
*/
|
|
445
|
-
export async function open(path, flag, mode = 0o644) {
|
|
446
|
-
|
|
447
|
-
return new FileHandle(getFdForFile(file));
|
|
434
|
+
export async function open(path, flag = 'r', mode = 0o644) {
|
|
435
|
+
return await _open(path, flag, mode, true);
|
|
448
436
|
}
|
|
449
437
|
open;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
*/
|
|
454
|
-
export async function lopen(path, flag, mode = 0o644) {
|
|
455
|
-
const file = await _open(path, flag, mode, false);
|
|
456
|
-
return new FileHandle(getFdForFile(file));
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Asynchronously reads the entire contents of a file.
|
|
460
|
-
*/
|
|
461
|
-
async function _readFile(fname, flag, resolveSymlinks) {
|
|
462
|
-
const file = await _open(normalizePath(fname), flag, 0o644, resolveSymlinks);
|
|
438
|
+
export async function readFile(path, _options) {
|
|
439
|
+
const options = normalizeOptions(_options, null, 'r', 0o644);
|
|
440
|
+
const handle = typeof path == 'object' && 'fd' in path ? path : await open(path, options.flag, options.mode);
|
|
463
441
|
try {
|
|
464
|
-
|
|
465
|
-
const data = new Uint8Array(stat.size);
|
|
466
|
-
await file.read(data, 0, stat.size, 0);
|
|
467
|
-
await file.close();
|
|
468
|
-
return data;
|
|
469
|
-
}
|
|
470
|
-
catch (e) {
|
|
471
|
-
await file.close();
|
|
472
|
-
throw e;
|
|
442
|
+
return await handle.readFile(options);
|
|
473
443
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const options = normalizeOptions(_options, null, 'r', 0);
|
|
477
|
-
const flag = parseFlag(options.flag);
|
|
478
|
-
if (!isReadable(flag)) {
|
|
479
|
-
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for reading.');
|
|
444
|
+
finally {
|
|
445
|
+
await handle.close();
|
|
480
446
|
}
|
|
481
|
-
const data = Buffer.from(await _readFile(filename, options.flag, true));
|
|
482
|
-
return options.encoding ? data.toString(options.encoding) : data;
|
|
483
447
|
}
|
|
484
448
|
readFile;
|
|
485
449
|
/**
|
|
486
450
|
* Asynchronously writes data to a file, replacing the file if it already exists.
|
|
487
451
|
*
|
|
488
452
|
* The encoding option is ignored if data is a buffer.
|
|
489
|
-
* @param
|
|
490
|
-
* @param data
|
|
453
|
+
* @param path
|
|
454
|
+
* @param data Note:
|
|
491
455
|
* @param _options
|
|
492
456
|
* @option options encoding Defaults to `'utf8'`.
|
|
493
457
|
* @option options mode Defaults to `0644`.
|
|
494
458
|
* @option options flag Defaults to `'w'`.
|
|
495
459
|
*/
|
|
496
|
-
export async function writeFile(
|
|
460
|
+
export async function writeFile(path, data, _options) {
|
|
497
461
|
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
|
|
498
|
-
const handle = await open(
|
|
462
|
+
const handle = path instanceof FileHandle ? path : await open(path.toString(), options.flag, options.mode);
|
|
499
463
|
try {
|
|
500
|
-
|
|
464
|
+
const _data = typeof data == 'string' ? data : data;
|
|
465
|
+
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
|
|
466
|
+
throw new ErrnoError(Errno.EINVAL, 'Iterables and streams not supported', handle.file.path, 'writeFile');
|
|
467
|
+
}
|
|
468
|
+
await handle.writeFile(_data, options);
|
|
501
469
|
}
|
|
502
470
|
finally {
|
|
503
471
|
await handle.close();
|
|
504
472
|
}
|
|
505
473
|
}
|
|
506
474
|
writeFile;
|
|
507
|
-
/**
|
|
508
|
-
* Asynchronously append data to a file, creating the file if
|
|
509
|
-
* it not yet exists.
|
|
510
|
-
*/
|
|
511
|
-
async function _appendFile(fname, data, flag, mode, resolveSymlinks) {
|
|
512
|
-
const file = await _open(fname, flag, mode, resolveSymlinks);
|
|
513
|
-
try {
|
|
514
|
-
await file.write(data, 0, data.length, null);
|
|
515
|
-
}
|
|
516
|
-
finally {
|
|
517
|
-
await file.close();
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
475
|
/**
|
|
521
476
|
* Asynchronously append data to a file, creating the file if it not yet
|
|
522
477
|
* exists.
|
|
523
|
-
* @param
|
|
478
|
+
* @param path
|
|
524
479
|
* @param data
|
|
525
480
|
* @param options
|
|
526
481
|
* @option options encoding Defaults to `'utf8'`.
|
|
527
482
|
* @option options mode Defaults to `0644`.
|
|
528
483
|
* @option options flag Defaults to `'a'`.
|
|
529
484
|
*/
|
|
530
|
-
export async function appendFile(
|
|
485
|
+
export async function appendFile(path, data, _options) {
|
|
531
486
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
532
487
|
const flag = parseFlag(options.flag);
|
|
533
488
|
if (!isAppendable(flag)) {
|
|
534
|
-
throw new
|
|
489
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
535
490
|
}
|
|
536
491
|
if (typeof data != 'string' && !options.encoding) {
|
|
537
|
-
throw new
|
|
492
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
493
|
+
}
|
|
494
|
+
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
495
|
+
const handle = typeof path == 'object' && 'fd' in path ? path : await open(path, options.flag, options.mode);
|
|
496
|
+
try {
|
|
497
|
+
await handle.appendFile(encodedData, options);
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
await handle.close();
|
|
538
501
|
}
|
|
539
|
-
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
|
|
540
|
-
await _appendFile(filename, encodedData, options.flag, options.mode, true);
|
|
541
502
|
}
|
|
542
503
|
appendFile;
|
|
543
504
|
// DIRECTORY-ONLY METHODS
|
|
@@ -546,18 +507,41 @@ appendFile;
|
|
|
546
507
|
* @param path
|
|
547
508
|
*/
|
|
548
509
|
export async function rmdir(path) {
|
|
549
|
-
|
|
510
|
+
path = normalizePath(path);
|
|
511
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
512
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
513
|
+
try {
|
|
514
|
+
await fs.rmdir(resolved, cred);
|
|
515
|
+
}
|
|
516
|
+
catch (e) {
|
|
517
|
+
throw fixError(e, { [resolved]: path });
|
|
518
|
+
}
|
|
550
519
|
}
|
|
551
520
|
rmdir;
|
|
552
|
-
export async function mkdir(path,
|
|
553
|
-
|
|
521
|
+
export async function mkdir(path, options) {
|
|
522
|
+
path = normalizePath(path);
|
|
523
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
524
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
525
|
+
try {
|
|
526
|
+
await fs.mkdir(resolved, normalizeMode(typeof options == 'object' ? options?.mode : options, 0o777), cred);
|
|
527
|
+
}
|
|
528
|
+
catch (e) {
|
|
529
|
+
throw fixError(e, { [resolved]: path });
|
|
530
|
+
}
|
|
554
531
|
}
|
|
555
532
|
mkdir;
|
|
556
533
|
export async function readdir(path, options) {
|
|
557
534
|
path = normalizePath(path);
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
|
|
535
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
536
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
537
|
+
let entries;
|
|
538
|
+
try {
|
|
539
|
+
entries = await fs.readdir(resolved, cred);
|
|
540
|
+
}
|
|
541
|
+
catch (e) {
|
|
542
|
+
throw fixError(e, { [resolved]: path });
|
|
543
|
+
}
|
|
544
|
+
for (const point of mounts.keys()) {
|
|
561
545
|
if (point.startsWith(path)) {
|
|
562
546
|
const entry = point.slice(path.length);
|
|
563
547
|
if (entry.includes('/') || entry.length == 0) {
|
|
@@ -581,8 +565,15 @@ readdir;
|
|
|
581
565
|
* @param newpath
|
|
582
566
|
*/
|
|
583
567
|
export async function link(existing, newpath) {
|
|
568
|
+
existing = normalizePath(existing);
|
|
584
569
|
newpath = normalizePath(newpath);
|
|
585
|
-
|
|
570
|
+
const { fs, path: resolved } = resolveMount(newpath);
|
|
571
|
+
try {
|
|
572
|
+
return await fs.link(existing, newpath, cred);
|
|
573
|
+
}
|
|
574
|
+
catch (e) {
|
|
575
|
+
throw fixError(e, { [resolved]: newpath });
|
|
576
|
+
}
|
|
586
577
|
}
|
|
587
578
|
link;
|
|
588
579
|
/**
|
|
@@ -593,23 +584,26 @@ link;
|
|
|
593
584
|
*/
|
|
594
585
|
export async function symlink(target, path, type = 'file') {
|
|
595
586
|
if (!['file', 'dir', 'junction'].includes(type)) {
|
|
596
|
-
throw new
|
|
587
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
|
|
597
588
|
}
|
|
598
589
|
if (await exists(path)) {
|
|
599
|
-
throw
|
|
590
|
+
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
600
591
|
}
|
|
601
|
-
await writeFile(path, target);
|
|
602
|
-
const
|
|
603
|
-
await file._setType(FileType.SYMLINK);
|
|
592
|
+
await writeFile(path, target.toString());
|
|
593
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
594
|
+
await handle.file._setType(FileType.SYMLINK);
|
|
604
595
|
}
|
|
605
596
|
symlink;
|
|
606
597
|
export async function readlink(path, options) {
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
598
|
+
const handle = await _open(normalizePath(path), 'r', 0o644, false);
|
|
599
|
+
try {
|
|
600
|
+
const value = await handle.readFile();
|
|
601
|
+
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
602
|
+
return encoding == 'buffer' ? value : value.toString(encoding);
|
|
603
|
+
}
|
|
604
|
+
finally {
|
|
605
|
+
await handle.close();
|
|
611
606
|
}
|
|
612
|
-
return value.toString(encoding);
|
|
613
607
|
}
|
|
614
608
|
readlink;
|
|
615
609
|
// PROPERTY OPERATIONS
|
|
@@ -636,7 +630,7 @@ chown;
|
|
|
636
630
|
* @param gid
|
|
637
631
|
*/
|
|
638
632
|
export async function lchown(path, uid, gid) {
|
|
639
|
-
const handle = await
|
|
633
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
640
634
|
try {
|
|
641
635
|
await handle.chown(uid, gid);
|
|
642
636
|
}
|
|
@@ -666,7 +660,7 @@ chmod;
|
|
|
666
660
|
* @param mode
|
|
667
661
|
*/
|
|
668
662
|
export async function lchmod(path, mode) {
|
|
669
|
-
const handle = await
|
|
663
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
670
664
|
try {
|
|
671
665
|
await handle.chmod(mode);
|
|
672
666
|
}
|
|
@@ -698,9 +692,9 @@ utimes;
|
|
|
698
692
|
* @param mtime
|
|
699
693
|
*/
|
|
700
694
|
export async function lutimes(path, atime, mtime) {
|
|
701
|
-
const handle = await
|
|
695
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
702
696
|
try {
|
|
703
|
-
await handle.utimes(atime, mtime);
|
|
697
|
+
await handle.utimes(new Date(atime), new Date(mtime));
|
|
704
698
|
}
|
|
705
699
|
finally {
|
|
706
700
|
await handle.close();
|
|
@@ -724,8 +718,8 @@ export async function realpath(path, options) {
|
|
|
724
718
|
}
|
|
725
719
|
}
|
|
726
720
|
realpath;
|
|
727
|
-
export function watch(filename, options) {
|
|
728
|
-
throw
|
|
721
|
+
export function watch(filename, options = {}) {
|
|
722
|
+
throw ErrnoError.With('ENOSYS', filename.toString(), 'watch');
|
|
729
723
|
}
|
|
730
724
|
watch;
|
|
731
725
|
/**
|
|
@@ -736,7 +730,7 @@ watch;
|
|
|
736
730
|
export async function access(path, mode = constants.F_OK) {
|
|
737
731
|
const stats = await stat(path);
|
|
738
732
|
if (!stats.hasAccess(mode, cred)) {
|
|
739
|
-
throw new
|
|
733
|
+
throw new ErrnoError(Errno.EACCES);
|
|
740
734
|
}
|
|
741
735
|
}
|
|
742
736
|
access;
|
|
@@ -765,12 +759,12 @@ export async function rm(path, options) {
|
|
|
765
759
|
case constants.S_IFIFO:
|
|
766
760
|
case constants.S_IFSOCK:
|
|
767
761
|
default:
|
|
768
|
-
throw new
|
|
762
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
769
763
|
}
|
|
770
764
|
}
|
|
771
765
|
rm;
|
|
772
766
|
export async function mkdtemp(prefix, options) {
|
|
773
|
-
const encoding = typeof options === 'object' ? options
|
|
767
|
+
const encoding = typeof options === 'object' ? options?.encoding : options || 'utf8';
|
|
774
768
|
const fsName = `${prefix}${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
775
769
|
const resolvedPath = '/tmp/' + fsName;
|
|
776
770
|
await mkdir(resolvedPath);
|
|
@@ -788,7 +782,7 @@ export async function copyFile(src, dest, mode) {
|
|
|
788
782
|
src = normalizePath(src);
|
|
789
783
|
dest = normalizePath(dest);
|
|
790
784
|
if (mode && mode & constants.COPYFILE_EXCL && (await exists(dest))) {
|
|
791
|
-
throw new
|
|
785
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
|
|
792
786
|
}
|
|
793
787
|
await writeFile(dest, await readFile(src));
|
|
794
788
|
}
|
|
@@ -821,12 +815,12 @@ export async function cp(source, destination, opts) {
|
|
|
821
815
|
destination = normalizePath(destination);
|
|
822
816
|
const srcStats = await lstat(source); // Use lstat to follow symlinks if not dereferencing
|
|
823
817
|
if (opts?.errorOnExist && (await exists(destination))) {
|
|
824
|
-
throw new
|
|
818
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists.', destination, 'cp');
|
|
825
819
|
}
|
|
826
820
|
switch (srcStats.mode & constants.S_IFMT) {
|
|
827
821
|
case constants.S_IFDIR:
|
|
828
822
|
if (!opts?.recursive) {
|
|
829
|
-
throw new
|
|
823
|
+
throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');
|
|
830
824
|
}
|
|
831
825
|
await mkdir(destination, { recursive: true }); // Ensure the destination directory exists
|
|
832
826
|
for (const dirent of await readdir(source, { withFileTypes: true })) {
|
|
@@ -845,7 +839,7 @@ export async function cp(source, destination, opts) {
|
|
|
845
839
|
case constants.S_IFIFO:
|
|
846
840
|
case constants.S_IFSOCK:
|
|
847
841
|
default:
|
|
848
|
-
throw new
|
|
842
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');
|
|
849
843
|
}
|
|
850
844
|
// Optionally preserve timestamps
|
|
851
845
|
if (opts?.preserveTimestamps) {
|
|
@@ -854,5 +848,5 @@ export async function cp(source, destination, opts) {
|
|
|
854
848
|
}
|
|
855
849
|
cp;
|
|
856
850
|
export async function statfs(path, opts) {
|
|
857
|
-
throw
|
|
851
|
+
throw ErrnoError.With('ENOSYS', path.toString(), 'statfs');
|
|
858
852
|
}
|