@zenfs/core 0.0.12 → 0.2.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.
Files changed (54) hide show
  1. package/dist/ApiError.d.ts +52 -15
  2. package/dist/ApiError.js +77 -50
  3. package/dist/FileIndex.d.ts +32 -35
  4. package/dist/FileIndex.js +93 -109
  5. package/dist/backends/AsyncMirror.d.ts +42 -43
  6. package/dist/backends/AsyncMirror.js +154 -147
  7. package/dist/backends/AsyncStore.d.ts +29 -28
  8. package/dist/backends/AsyncStore.js +375 -482
  9. package/dist/backends/FolderAdapter.js +8 -19
  10. package/dist/backends/InMemory.d.ts +16 -13
  11. package/dist/backends/InMemory.js +29 -14
  12. package/dist/backends/Locked.d.ts +8 -28
  13. package/dist/backends/Locked.js +74 -224
  14. package/dist/backends/OverlayFS.d.ts +26 -34
  15. package/dist/backends/OverlayFS.js +303 -511
  16. package/dist/backends/SyncStore.d.ts +54 -72
  17. package/dist/backends/SyncStore.js +159 -161
  18. package/dist/backends/backend.d.ts +45 -29
  19. package/dist/backends/backend.js +83 -13
  20. package/dist/backends/index.d.ts +6 -7
  21. package/dist/backends/index.js +5 -6
  22. package/dist/browser.min.js +21 -6
  23. package/dist/browser.min.js.map +4 -4
  24. package/dist/emulation/callbacks.d.ts +119 -113
  25. package/dist/emulation/callbacks.js +129 -92
  26. package/dist/emulation/constants.js +1 -1
  27. package/dist/emulation/dir.d.ts +55 -0
  28. package/dist/emulation/dir.js +104 -0
  29. package/dist/emulation/fs.d.ts +1 -2
  30. package/dist/emulation/fs.js +0 -1
  31. package/dist/emulation/index.d.ts +3 -0
  32. package/dist/emulation/index.js +3 -0
  33. package/dist/emulation/promises.d.ts +265 -145
  34. package/dist/emulation/promises.js +526 -383
  35. package/dist/emulation/shared.d.ts +20 -6
  36. package/dist/emulation/shared.js +22 -23
  37. package/dist/emulation/streams.d.ts +102 -0
  38. package/dist/emulation/streams.js +55 -0
  39. package/dist/emulation/sync.d.ts +98 -69
  40. package/dist/emulation/sync.js +280 -133
  41. package/dist/file.d.ts +175 -173
  42. package/dist/file.js +257 -273
  43. package/dist/filesystem.d.ts +71 -244
  44. package/dist/filesystem.js +67 -472
  45. package/dist/index.d.ts +7 -44
  46. package/dist/index.js +22 -75
  47. package/dist/inode.d.ts +37 -28
  48. package/dist/inode.js +123 -65
  49. package/dist/stats.d.ts +91 -36
  50. package/dist/stats.js +138 -110
  51. package/dist/utils.d.ts +26 -13
  52. package/dist/utils.js +79 -107
  53. package/package.json +7 -4
  54. package/readme.md +2 -40
@@ -1,18 +1,154 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { ApiError, ErrorCode } from '../ApiError.js';
11
- import * as constants from './constants.js';
12
- export { constants };
13
- import { FileFlag } from '../file.js';
2
+ export * as constants from './constants.js';
3
+ import { ActionType, FileFlag } from '../file.js';
14
4
  import { normalizePath, normalizeMode, getFdForFile, normalizeOptions, fd2file, fdMap, normalizeTime, cred, nop, resolveFS, fixError, mounts } from './shared.js';
5
+ import { BigIntStats, FileType } from '../stats.js';
15
6
  import { decode, encode } from '../utils.js';
7
+ import { Dirent } from './dir.js';
8
+ import { dirname, join } from './path.js';
9
+ export class FileHandle {
10
+ constructor(
11
+ /**
12
+ * Gets the file descriptor for this file handle.
13
+ */
14
+ fd) {
15
+ this.fd = fd;
16
+ }
17
+ /**
18
+ * Asynchronous fchown(2) - Change ownership of a file.
19
+ */
20
+ chown(uid, gid) {
21
+ return fd2file(this.fd).chown(uid, gid);
22
+ }
23
+ /**
24
+ * Asynchronous fchmod(2) - Change permissions of a file.
25
+ * @param mode A file mode. If a string is passed, it is parsed as an octal integer.
26
+ */
27
+ chmod(mode) {
28
+ const numMode = normalizeMode(mode, -1);
29
+ if (numMode < 0) {
30
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid mode.');
31
+ }
32
+ return fd2file(this.fd).chmod(numMode);
33
+ }
34
+ /**
35
+ * Asynchronous fdatasync(2) - synchronize a file's in-core state with storage device.
36
+ */
37
+ datasync() {
38
+ return fd2file(this.fd).datasync();
39
+ }
40
+ /**
41
+ * Asynchronous fsync(2) - synchronize a file's in-core state with the underlying storage device.
42
+ */
43
+ sync() {
44
+ return fd2file(this.fd).sync();
45
+ }
46
+ /**
47
+ * Asynchronous ftruncate(2) - Truncate a file to a specified length.
48
+ * @param len If not specified, defaults to `0`.
49
+ */
50
+ truncate(len) {
51
+ if (len < 0) {
52
+ throw new ApiError(ErrorCode.EINVAL);
53
+ }
54
+ return fd2file(this.fd).truncate(len);
55
+ }
56
+ /**
57
+ * Asynchronously change file timestamps of the file.
58
+ * @param atime The last access time. If a string is provided, it will be coerced to number.
59
+ * @param mtime The last modified time. If a string is provided, it will be coerced to number.
60
+ */
61
+ utimes(atime, mtime) {
62
+ return fd2file(this.fd).utimes(normalizeTime(atime), normalizeTime(mtime));
63
+ }
64
+ /**
65
+ * Asynchronously append data to a file, creating the file if it does not exist. The underlying file will _not_ be closed automatically.
66
+ * The `FileHandle` must have been opened for appending.
67
+ * @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string.
68
+ * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag.
69
+ * If `encoding` is not supplied, the default of `'utf8'` is used.
70
+ * If `mode` is not supplied, the default of `0o666` is used.
71
+ * If `mode` is a string, it is parsed as an octal integer.
72
+ * If `flag` is not supplied, the default of `'a'` is used.
73
+ */
74
+ appendFile(data, options) {
75
+ return appendFile(fd2file(this.fd).path, data, options);
76
+ }
77
+ /**
78
+ * Asynchronously reads data from the file.
79
+ * The `FileHandle` must have been opened for reading.
80
+ * @param buffer The buffer that the data will be written to.
81
+ * @param offset The offset in the buffer at which to start writing.
82
+ * @param length The number of bytes to read.
83
+ * @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position.
84
+ */
85
+ read(buffer, offset, length, position) {
86
+ if (isNaN(+position)) {
87
+ position = fd2file(this.fd).position;
88
+ }
89
+ return fd2file(this.fd).read(buffer, offset, length, position);
90
+ }
91
+ readFile(options) {
92
+ return readFile(fd2file(this.fd).path, options);
93
+ }
94
+ stat(opts) {
95
+ return stat(fd2file(this.fd).path, opts);
96
+ }
97
+ async write(data, posOrOff, lenOrEnc, position) {
98
+ let buffer, offset = 0, length;
99
+ if (typeof data === 'string') {
100
+ // Signature 1: (fd, string, [position?, [encoding?]])
101
+ position = typeof posOrOff === 'number' ? posOrOff : null;
102
+ const encoding = (typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
103
+ offset = 0;
104
+ buffer = encode(data, encoding);
105
+ length = buffer.length;
106
+ }
107
+ else {
108
+ // Signature 2: (fd, buffer, offset, length, position?)
109
+ buffer = data;
110
+ offset = posOrOff;
111
+ length = lenOrEnc;
112
+ position = typeof position === 'number' ? position : null;
113
+ }
114
+ position ?? (position = fd2file(this.fd).position);
115
+ const bytesWritten = await fd2file(this.fd).write(buffer, offset, length, position);
116
+ return { buffer, bytesWritten };
117
+ }
118
+ /**
119
+ * Asynchronously writes data to a file, replacing the file if it already exists. The underlying file will _not_ be closed automatically.
120
+ * The `FileHandle` must have been opened for writing.
121
+ * It is unsafe to call `writeFile()` multiple times on the same file without waiting for the `Promise` to be resolved (or rejected).
122
+ * @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string.
123
+ * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag.
124
+ * If `encoding` is not supplied, the default of `'utf8'` is used.
125
+ * If `mode` is not supplied, the default of `0o666` is used.
126
+ * If `mode` is a string, it is parsed as an octal integer.
127
+ * If `flag` is not supplied, the default of `'w'` is used.
128
+ */
129
+ writeFile(data, options) {
130
+ return writeFile(fd2file(this.fd).path, data, options);
131
+ }
132
+ /**
133
+ * See `fs.writev` promisified version.
134
+ */
135
+ writev(buffers, position) {
136
+ throw new ApiError(ErrorCode.ENOTSUP);
137
+ }
138
+ /**
139
+ * See `fs.readv` promisified version.
140
+ */
141
+ readv(buffers, position) {
142
+ throw new ApiError(ErrorCode.ENOTSUP);
143
+ }
144
+ /**
145
+ * Asynchronous close(2) - close a `FileHandle`.
146
+ */
147
+ async close() {
148
+ await fd2file(this.fd).close();
149
+ fdMap.delete(this.fd);
150
+ }
151
+ }
16
152
  /**
17
153
  * Utility for FS ops. It handles
18
154
  * - path normalization (for the first parameter to the FS op)
@@ -25,18 +161,16 @@ import { decode, encode } from '../utils.js';
25
161
  * @param args the rest of the parameters are passed to the FS function. Note that the first parameter is required to be a path
26
162
  * @returns
27
163
  */
28
- function doOp(...[name, resolveSymlinks, path, ...args]) {
29
- return __awaiter(this, void 0, void 0, function* () {
30
- path = normalizePath(path);
31
- const { fs, path: resolvedPath } = resolveFS(resolveSymlinks && (yield exists(path)) ? yield realpath(path) : path);
32
- try {
33
- // @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
34
- return fs[name](resolvedPath, ...args);
35
- }
36
- catch (e) {
37
- throw fixError(e, { [resolvedPath]: path });
38
- }
39
- });
164
+ async function doOp(...[name, resolveSymlinks, rawPath, ...args]) {
165
+ rawPath = normalizePath(rawPath);
166
+ const { fs, path } = resolveFS(resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath);
167
+ try {
168
+ // @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
169
+ return fs[name](path, ...args);
170
+ }
171
+ catch (e) {
172
+ throw fixError(e, { [path]: rawPath });
173
+ }
40
174
  }
41
175
  // fs.promises
42
176
  /**
@@ -44,232 +178,245 @@ function doOp(...[name, resolveSymlinks, path, ...args]) {
44
178
  * @param oldPath
45
179
  * @param newPath
46
180
  */
47
- export function rename(oldPath, newPath) {
48
- return __awaiter(this, void 0, void 0, function* () {
49
- oldPath = normalizePath(oldPath);
50
- newPath = normalizePath(newPath);
51
- const _old = resolveFS(oldPath);
52
- const _new = resolveFS(newPath);
53
- const paths = { [_old.path]: oldPath, [_new.path]: newPath };
54
- try {
55
- if (_old === _new) {
56
- return _old.fs.rename(_old.path, _new.path, cred);
57
- }
58
- const data = yield readFile(oldPath);
59
- yield writeFile(newPath, data);
60
- yield unlink(oldPath);
61
- }
62
- catch (e) {
63
- throw fixError(e, paths);
64
- }
65
- });
66
- }
181
+ export async function rename(oldPath, newPath) {
182
+ oldPath = normalizePath(oldPath);
183
+ newPath = normalizePath(newPath);
184
+ const { path: old } = resolveFS(oldPath);
185
+ const { fs, path } = resolveFS(newPath);
186
+ try {
187
+ const data = await readFile(oldPath);
188
+ await writeFile(newPath, data);
189
+ await unlink(oldPath);
190
+ }
191
+ catch (e) {
192
+ throw fixError(e, { [old]: oldPath, [path]: newPath });
193
+ }
194
+ }
195
+ rename;
67
196
  /**
68
197
  * Test whether or not the given path exists by checking with the file system.
69
- * @param path
198
+ * @param _path
70
199
  */
71
- export function exists(path) {
72
- return __awaiter(this, void 0, void 0, function* () {
73
- path = normalizePath(path);
74
- try {
75
- const { fs, path: resolvedPath } = resolveFS(path);
76
- return fs.exists(resolvedPath, cred);
77
- }
78
- catch (e) {
79
- if (e.errno == ErrorCode.ENOENT) {
80
- return false;
81
- }
82
- throw e;
200
+ export async function exists(_path) {
201
+ try {
202
+ const { fs, path } = resolveFS(_path);
203
+ return fs.exists(path, cred);
204
+ }
205
+ catch (e) {
206
+ if (e.errno == ErrorCode.ENOENT) {
207
+ return false;
83
208
  }
84
- });
209
+ throw e;
210
+ }
85
211
  }
86
- /**
87
- * `stat`.
88
- * @param path
89
- * @returns Stats
90
- */
91
- export function stat(path) {
92
- return __awaiter(this, void 0, void 0, function* () {
93
- return doOp('stat', true, path, cred);
94
- });
212
+ export async function stat(path, options) {
213
+ const stats = await doOp('stat', true, path, cred);
214
+ return options?.bigint ? BigIntStats.clone(stats) : stats;
95
215
  }
96
- /**
97
- * `lstat`.
98
- * `lstat()` is identical to `stat()`, except that if path is a symbolic link,
99
- * then the link itself is stat-ed, not the file that it refers to.
100
- * @param path
101
- * @return [ZenFS.node.fs.Stats]
102
- */
103
- export function lstat(path) {
104
- return __awaiter(this, void 0, void 0, function* () {
105
- return doOp('stat', false, path, cred);
106
- });
216
+ stat;
217
+ export async function lstat(path, options) {
218
+ const stats = await doOp('stat', false, path, cred);
219
+ return options?.bigint ? BigIntStats.clone(stats) : stats;
107
220
  }
221
+ lstat;
108
222
  // FILE-ONLY METHODS
109
223
  /**
110
224
  * `truncate`.
111
225
  * @param path
112
226
  * @param len
113
227
  */
114
- export function truncate(path, len = 0) {
115
- return __awaiter(this, void 0, void 0, function* () {
116
- if (len < 0) {
117
- throw new ApiError(ErrorCode.EINVAL);
118
- }
119
- return doOp('truncate', true, path, len, cred);
120
- });
121
- }
228
+ export async function truncate(path, len = 0) {
229
+ const handle = await open(path, 'r+');
230
+ try {
231
+ await handle.truncate(len);
232
+ }
233
+ finally {
234
+ await handle.close();
235
+ }
236
+ }
237
+ truncate;
122
238
  /**
123
239
  * `unlink`.
124
240
  * @param path
125
241
  */
126
- export function unlink(path) {
127
- return __awaiter(this, void 0, void 0, function* () {
128
- return doOp('unlink', false, path, cred);
129
- });
242
+ export async function unlink(path) {
243
+ return doOp('unlink', false, path, cred);
130
244
  }
245
+ unlink;
131
246
  /**
132
- * file open.
133
- * @see http://www.manpagez.com/man/2/open/
134
- * @param path
135
- * @param flags
136
- * @param mode defaults to `0644`
247
+ * Opens a file. This helper handles the complexity of file flags.
248
+ * @internal
137
249
  */
138
- export function open(path, flag, mode = 0o644) {
139
- return __awaiter(this, void 0, void 0, function* () {
140
- const file = yield doOp('open', true, path, FileFlag.getFileFlag(flag), normalizeMode(mode, 0o644), cred);
141
- return getFdForFile(file);
142
- });
143
- }
144
- export function readFile(filename, arg2 = {}) {
145
- return __awaiter(this, void 0, void 0, function* () {
146
- const options = normalizeOptions(arg2, null, 'r', null);
147
- const flag = FileFlag.getFileFlag(options.flag);
148
- if (!flag.isReadable()) {
149
- throw new ApiError(ErrorCode.EINVAL, 'Flag passed to readFile must allow for reading.');
150
- }
151
- const data = yield doOp('readFile', true, filename, flag, cred);
152
- switch (options.encoding) {
153
- case 'utf8':
154
- case 'utf-8':
155
- return decode(data);
250
+ async function _open(_path, _flag, _mode = 0o644, resolveSymlinks) {
251
+ const path = normalizePath(_path), mode = normalizeMode(_mode, 0o644), flag = FileFlag.FromString(_flag);
252
+ try {
253
+ switch (flag.pathExistsAction()) {
254
+ case ActionType.THROW:
255
+ throw ApiError.EEXIST(path);
256
+ case ActionType.TRUNCATE:
257
+ /*
258
+ In a previous implementation, we deleted the file and
259
+ re-created it. However, this created a race condition if another
260
+ asynchronous request was trying to read the file, as the file
261
+ would not exist for a small period of time.
262
+ */
263
+ const file = await doOp('openFile', resolveSymlinks, path, flag, cred);
264
+ if (!file) {
265
+ throw new ApiError(ErrorCode.EIO, 'Impossible code path reached');
266
+ }
267
+ await file.truncate(0);
268
+ await file.sync();
269
+ return file;
270
+ case ActionType.NOP:
271
+ // Must await so thrown errors are caught by the catch below
272
+ return await doOp('openFile', resolveSymlinks, path, flag, cred);
156
273
  default:
157
- return data;
274
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid file flag');
158
275
  }
159
- });
160
- }
161
- export function writeFile(filename, data, arg3) {
162
- return __awaiter(this, void 0, void 0, function* () {
163
- const options = normalizeOptions(arg3, 'utf8', 'w', 0o644);
164
- const flag = FileFlag.getFileFlag(options.flag);
165
- if (!flag.isWriteable()) {
166
- throw new ApiError(ErrorCode.EINVAL, 'Flag passed to writeFile must allow for writing.');
167
- }
168
- if (typeof data != 'string' && !options.encoding) {
169
- throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
170
- }
171
- const encodedData = typeof data == 'string' ? encode(data) : data;
172
- return doOp('writeFile', true, filename, encodedData, flag, options.mode, cred);
173
- });
174
- }
175
- export function appendFile(filename, data, arg3) {
176
- return __awaiter(this, void 0, void 0, function* () {
177
- const options = normalizeOptions(arg3, 'utf8', 'a', 0o644);
178
- const flag = FileFlag.getFileFlag(options.flag);
179
- if (!flag.isAppendable()) {
180
- throw new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.');
181
- }
182
- if (typeof data != 'string' && !options.encoding) {
183
- throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
276
+ }
277
+ catch (e) {
278
+ switch (flag.pathNotExistsAction()) {
279
+ case ActionType.CREATE:
280
+ // Ensure parent exists.
281
+ const parentStats = await doOp('stat', resolveSymlinks, dirname(path), cred);
282
+ if (parentStats && !parentStats.isDirectory()) {
283
+ throw ApiError.ENOTDIR(dirname(path));
284
+ }
285
+ return await doOp('createFile', resolveSymlinks, path, flag, mode, cred);
286
+ case ActionType.THROW:
287
+ throw ApiError.ENOENT(path);
288
+ default:
289
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid file flag');
184
290
  }
185
- const encodedData = typeof data == 'string' ? encode(data) : data;
186
- return doOp('appendFile', true, filename, encodedData, flag, options.mode, cred);
187
- });
291
+ }
188
292
  }
189
- // FILE DESCRIPTOR METHODS
190
293
  /**
191
- * `fstat`.
192
- * `fstat()` is identical to `stat()`, except that the file to be stat-ed is
193
- * specified by the file descriptor `fd`.
194
- * @param fd
195
- * @return [ZenFS.node.fs.Stats]
294
+ * Asynchronous file open.
295
+ * @see http://www.manpagez.com/man/2/open/
296
+ * @param flags Handles the complexity of the various file modes. See its API for more details.
297
+ * @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
196
298
  */
197
- export function fstat(fd) {
198
- return __awaiter(this, void 0, void 0, function* () {
199
- return fd2file(fd).stat();
200
- });
299
+ export async function open(path, flag, mode = 0o644) {
300
+ const file = await _open(path, flag, mode, true);
301
+ return new FileHandle(getFdForFile(file));
201
302
  }
303
+ open;
202
304
  /**
203
- * close.
204
- * @param fd
305
+ * Opens a file without resolving symlinks
306
+ * @internal
205
307
  */
206
- export function close(fd) {
207
- return __awaiter(this, void 0, void 0, function* () {
208
- yield fd2file(fd).close();
209
- fdMap.delete(fd);
210
- return;
211
- });
308
+ export async function lopen(path, flag, mode = 0o644) {
309
+ const file = await _open(path, flag, mode, false);
310
+ return new FileHandle(getFdForFile(file));
212
311
  }
213
312
  /**
214
- * ftruncate.
215
- * @param fd
216
- * @param len
313
+ * Asynchronously reads the entire contents of a file.
217
314
  */
218
- export function ftruncate(fd, len = 0) {
219
- return __awaiter(this, void 0, void 0, function* () {
220
- const file = fd2file(fd);
221
- if (len < 0) {
222
- throw new ApiError(ErrorCode.EINVAL);
223
- }
224
- return file.truncate(len);
225
- });
226
- }
315
+ async function _readFile(fname, flag, resolveSymlinks) {
316
+ const file = await _open(normalizePath(fname), flag, 0o644, resolveSymlinks);
317
+ try {
318
+ const stat = await file.stat();
319
+ const data = new Uint8Array(stat.size);
320
+ await file.read(data, 0, stat.size, 0);
321
+ await file.close();
322
+ return data;
323
+ }
324
+ finally {
325
+ await file.close();
326
+ }
327
+ }
328
+ export async function readFile(filename, _options) {
329
+ const options = normalizeOptions(_options, null, 'r', null);
330
+ const flag = FileFlag.FromString(options.flag);
331
+ if (!flag.isReadable()) {
332
+ throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for reading.');
333
+ }
334
+ const data = await _readFile(filename, options.flag, true);
335
+ return options.encoding ? decode(data, options.encoding) : data;
336
+ }
337
+ readFile;
227
338
  /**
228
- * fsync.
229
- * @param fd
339
+ * Asynchronously writes data to a file, replacing the file
340
+ * if it already exists.
341
+ *
342
+ * The encoding option is ignored if data is a buffer.
230
343
  */
231
- export function fsync(fd) {
232
- return __awaiter(this, void 0, void 0, function* () {
233
- return fd2file(fd).sync();
234
- });
344
+ async function _writeFile(fname, data, flag, mode, resolveSymlinks) {
345
+ const file = await _open(fname, flag, mode, resolveSymlinks);
346
+ try {
347
+ await file.write(data, 0, data.length, 0);
348
+ }
349
+ finally {
350
+ await file.close();
351
+ }
235
352
  }
236
353
  /**
237
- * fdatasync.
238
- * @param fd
354
+ * Synchronously writes data to a file, replacing the file if it already exists.
355
+ *
356
+ * The encoding option is ignored if data is a buffer.
357
+ * @param filename
358
+ * @param data
359
+ * @param _options
360
+ * @option options encoding Defaults to `'utf8'`.
361
+ * @option options mode Defaults to `0644`.
362
+ * @option options flag Defaults to `'w'`.
363
+ */
364
+ export async function writeFile(filename, data, _options) {
365
+ const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
366
+ const flag = FileFlag.FromString(options.flag);
367
+ if (!flag.isWriteable()) {
368
+ throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for writing.');
369
+ }
370
+ if (typeof data != 'string' && !options.encoding) {
371
+ throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
372
+ }
373
+ const encodedData = typeof data == 'string' ? encode(data, options.encoding) : data;
374
+ await _writeFile(filename, encodedData, options.flag, options.mode, true);
375
+ }
376
+ writeFile;
377
+ /**
378
+ * Asynchronously append data to a file, creating the file if
379
+ * it not yet exists.
239
380
  */
240
- export function fdatasync(fd) {
241
- return __awaiter(this, void 0, void 0, function* () {
242
- return fd2file(fd).datasync();
243
- });
244
- }
245
- export function write(fd, arg2, arg3, arg4, arg5) {
246
- return __awaiter(this, void 0, void 0, function* () {
247
- let buffer, offset = 0, length, position;
248
- if (typeof arg2 === 'string') {
249
- // Signature 1: (fd, string, [position?, [encoding?]])
250
- position = typeof arg3 === 'number' ? arg3 : null;
251
- const encoding = (typeof arg4 === 'string' ? arg4 : 'utf8');
252
- offset = 0;
253
- buffer = encode(arg2);
254
- length = buffer.length;
255
- }
256
- else {
257
- // Signature 2: (fd, buffer, offset, length, position?)
258
- buffer = arg2;
259
- offset = arg3;
260
- length = arg4;
261
- position = typeof arg5 === 'number' ? arg5 : null;
262
- }
263
- const file = fd2file(fd);
264
- if (position === undefined || position === null) {
265
- position = file.getPos();
266
- }
267
- return file.write(buffer, offset, length, position);
268
- });
381
+ async function _appendFile(fname, data, flag, mode, resolveSymlinks) {
382
+ const file = await _open(fname, flag, mode, resolveSymlinks);
383
+ try {
384
+ await file.write(data, 0, data.length, null);
385
+ }
386
+ finally {
387
+ await file.close();
388
+ }
269
389
  }
390
+ /**
391
+ * Asynchronously append data to a file, creating the file if it not yet
392
+ * exists.
393
+ * @param filename
394
+ * @param data
395
+ * @param options
396
+ * @option options encoding Defaults to `'utf8'`.
397
+ * @option options mode Defaults to `0644`.
398
+ * @option options flag Defaults to `'a'`.
399
+ */
400
+ export async function appendFile(filename, data, _options) {
401
+ const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
402
+ const flag = FileFlag.FromString(options.flag);
403
+ if (!flag.isAppendable()) {
404
+ throw new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.');
405
+ }
406
+ if (typeof data != 'string' && !options.encoding) {
407
+ throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified');
408
+ }
409
+ const encodedData = typeof data == 'string' ? encode(data) : data;
410
+ await _appendFile(filename, encodedData, options.flag, options.mode, true);
411
+ }
412
+ appendFile;
413
+ export function write(handle, data, posOrOff, lenOrEnc, position) {
414
+ return handle.write(data, posOrOff, lenOrEnc, position);
415
+ }
416
+ write;
270
417
  /**
271
418
  * Read data from the file specified by `fd`.
272
- * @param fd
419
+ * @param handle
273
420
  * @param buffer The buffer that the data will be
274
421
  * written to.
275
422
  * @param offset The offset within the buffer where writing will
@@ -279,129 +426,112 @@ export function write(fd, arg2, arg3, arg4, arg5) {
279
426
  * in the file. If position is null, data will be read from the current file
280
427
  * position.
281
428
  */
282
- export function read(fd, buffer, offset, length, position) {
283
- return __awaiter(this, void 0, void 0, function* () {
284
- const file = fd2file(fd);
285
- if (isNaN(+position)) {
286
- position = file.getPos();
287
- }
288
- return file.read(buffer, offset, length, position);
289
- });
429
+ export function read(handle, buffer, offset, length, position) {
430
+ return handle.read(buffer, offset, length, position);
290
431
  }
432
+ read;
291
433
  /**
292
434
  * `fchown`.
293
- * @param fd
435
+ * @param handle
294
436
  * @param uid
295
437
  * @param gid
296
438
  */
297
- export function fchown(fd, uid, gid) {
298
- return __awaiter(this, void 0, void 0, function* () {
299
- return fd2file(fd).chown(uid, gid);
300
- });
439
+ export function fchown(handle, uid, gid) {
440
+ return handle.chown(uid, gid);
301
441
  }
442
+ fchown;
302
443
  /**
303
444
  * `fchmod`.
304
- * @param fd
445
+ * @param handle
305
446
  * @param mode
306
447
  */
307
- export function fchmod(fd, mode) {
308
- return __awaiter(this, void 0, void 0, function* () {
309
- const numMode = typeof mode === 'string' ? parseInt(mode, 8) : mode;
310
- return fd2file(fd).chmod(numMode);
311
- });
448
+ export function fchmod(handle, mode) {
449
+ return handle.chmod(mode);
312
450
  }
451
+ fchmod;
313
452
  /**
314
453
  * Change the file timestamps of a file referenced by the supplied file
315
454
  * descriptor.
316
- * @param fd
455
+ * @param handle
317
456
  * @param atime
318
457
  * @param mtime
319
458
  */
320
- export function futimes(fd, atime, mtime) {
321
- return __awaiter(this, void 0, void 0, function* () {
322
- return fd2file(fd).utimes(normalizeTime(atime), normalizeTime(mtime));
323
- });
459
+ export function futimes(handle, atime, mtime) {
460
+ return handle.utimes(atime, mtime);
324
461
  }
462
+ futimes;
325
463
  // DIRECTORY-ONLY METHODS
326
464
  /**
327
465
  * `rmdir`.
328
466
  * @param path
329
467
  */
330
- export function rmdir(path) {
331
- return __awaiter(this, void 0, void 0, function* () {
332
- return doOp('rmdir', true, path, cred);
333
- });
334
- }
335
- /**
336
- * `mkdir`.
337
- * @param path
338
- * @param mode defaults to `0777`
339
- */
340
- export function mkdir(path, mode) {
341
- return __awaiter(this, void 0, void 0, function* () {
342
- return doOp('mkdir', true, path, normalizeMode(mode, 0o777), cred);
343
- });
344
- }
345
- /**
346
- * `readdir`. Reads the contents of a directory.
347
- * @param path
348
- * @return [String[]]
349
- */
350
- export function readdir(path) {
351
- return __awaiter(this, void 0, void 0, function* () {
352
- path = normalizePath(path);
353
- const entries = yield doOp('readdir', true, path, cred);
354
- const points = [...mounts.keys()];
355
- for (const point of points) {
356
- if (point.startsWith(path)) {
357
- const entry = point.slice(path.length);
358
- if (entry.includes('/') || entry.length == 0) {
359
- // ignore FSs mounted in subdirectories and any FS mounted to `path`.
360
- continue;
361
- }
362
- entries.push(entry);
468
+ export async function rmdir(path) {
469
+ return doOp('rmdir', true, path, cred);
470
+ }
471
+ rmdir;
472
+ export async function mkdir(path, mode) {
473
+ await doOp('mkdir', true, path, normalizeMode(typeof mode == 'object' ? mode?.mode : mode, 0o777), cred);
474
+ }
475
+ mkdir;
476
+ export async function readdir(path, options) {
477
+ path = normalizePath(path);
478
+ const entries = await doOp('readdir', true, path, cred);
479
+ const points = [...mounts.keys()];
480
+ for (const point of points) {
481
+ if (point.startsWith(path)) {
482
+ const entry = point.slice(path.length);
483
+ if (entry.includes('/') || entry.length == 0) {
484
+ // ignore FSs mounted in subdirectories and any FS mounted to `path`.
485
+ continue;
363
486
  }
487
+ entries.push(entry);
364
488
  }
365
- return entries;
366
- });
367
- }
489
+ }
490
+ const values = [];
491
+ for (const entry of entries) {
492
+ values.push(typeof options == 'object' && options?.withFileTypes ? new Dirent(entry, await stat(join(path, entry))) : entry);
493
+ }
494
+ return values;
495
+ }
496
+ readdir;
368
497
  // SYMLINK METHODS
369
498
  /**
370
499
  * `link`.
371
- * @param srcpath
372
- * @param dstpath
500
+ * @param existing
501
+ * @param newpath
373
502
  */
374
- export function link(srcpath, dstpath) {
375
- return __awaiter(this, void 0, void 0, function* () {
376
- dstpath = normalizePath(dstpath);
377
- return doOp('link', false, srcpath, dstpath, cred);
378
- });
503
+ export async function link(existing, newpath) {
504
+ newpath = normalizePath(newpath);
505
+ return doOp('link', false, existing, newpath, cred);
379
506
  }
507
+ link;
380
508
  /**
381
509
  * `symlink`.
382
- * @param srcpath
383
- * @param dstpath
510
+ * @param target target path
511
+ * @param path link path
384
512
  * @param type can be either `'dir'` or `'file'` (default is `'file'`)
385
513
  */
386
- export function symlink(srcpath, dstpath, type = 'file') {
387
- return __awaiter(this, void 0, void 0, function* () {
388
- if (!['file', 'dir', 'junction'].includes(type)) {
389
- throw new ApiError(ErrorCode.EINVAL, 'Invalid type: ' + type);
390
- }
391
- dstpath = normalizePath(dstpath);
392
- return doOp('symlink', false, srcpath, dstpath, type, cred);
393
- });
394
- }
395
- /**
396
- * readlink.
397
- * @param path
398
- * @return [String]
399
- */
400
- export function readlink(path) {
401
- return __awaiter(this, void 0, void 0, function* () {
402
- return doOp('readlink', false, path, cred);
403
- });
404
- }
514
+ export async function symlink(target, path, type = 'file') {
515
+ if (!['file', 'dir', 'junction'].includes(type)) {
516
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid symlink type: ' + type);
517
+ }
518
+ if (await exists(path)) {
519
+ throw ApiError.EEXIST(path);
520
+ }
521
+ await writeFile(path, target);
522
+ const file = await _open(path, 'r+', 0o644, false);
523
+ await file._setType(FileType.SYMLINK);
524
+ }
525
+ symlink;
526
+ export async function readlink(path, options) {
527
+ const value = await _readFile(path, 'r', false);
528
+ const encoding = typeof options == 'object' ? options.encoding : options;
529
+ if (encoding == 'buffer') {
530
+ return value;
531
+ }
532
+ return decode(value, encoding);
533
+ }
534
+ readlink;
405
535
  // PROPERTY OPERATIONS
406
536
  /**
407
537
  * `chown`.
@@ -409,129 +539,142 @@ export function readlink(path) {
409
539
  * @param uid
410
540
  * @param gid
411
541
  */
412
- export function chown(path, uid, gid) {
413
- return __awaiter(this, void 0, void 0, function* () {
414
- return doOp('chown', true, path, uid, gid, cred);
415
- });
416
- }
542
+ export async function chown(path, uid, gid) {
543
+ const handle = await open(path, 'r+');
544
+ try {
545
+ await handle.chown(uid, gid);
546
+ }
547
+ finally {
548
+ await handle.close();
549
+ }
550
+ }
551
+ chown;
417
552
  /**
418
553
  * `lchown`.
419
554
  * @param path
420
555
  * @param uid
421
556
  * @param gid
422
557
  */
423
- export function lchown(path, uid, gid) {
424
- return __awaiter(this, void 0, void 0, function* () {
425
- return doOp('chown', false, path, uid, gid, cred);
426
- });
427
- }
558
+ export async function lchown(path, uid, gid) {
559
+ const handle = await lopen(path, 'r+');
560
+ try {
561
+ await handle.chown(uid, gid);
562
+ }
563
+ finally {
564
+ await handle.close();
565
+ }
566
+ }
567
+ lchown;
428
568
  /**
429
569
  * `chmod`.
430
570
  * @param path
431
571
  * @param mode
432
572
  */
433
- export function chmod(path, mode) {
434
- return __awaiter(this, void 0, void 0, function* () {
435
- const numMode = normalizeMode(mode, -1);
436
- if (numMode < 0) {
437
- throw new ApiError(ErrorCode.EINVAL, `Invalid mode.`);
438
- }
439
- return doOp('chmod', true, path, numMode, cred);
440
- });
441
- }
573
+ export async function chmod(path, mode) {
574
+ const handle = await open(path, 'r+');
575
+ try {
576
+ await handle.chmod(mode);
577
+ }
578
+ finally {
579
+ await handle.close();
580
+ }
581
+ }
582
+ chmod;
442
583
  /**
443
584
  * `lchmod`.
444
585
  * @param path
445
586
  * @param mode
446
587
  */
447
- export function lchmod(path, mode) {
448
- return __awaiter(this, void 0, void 0, function* () {
449
- const numMode = normalizeMode(mode, -1);
450
- if (numMode < 1) {
451
- throw new ApiError(ErrorCode.EINVAL, `Invalid mode.`);
452
- }
453
- return doOp('chmod', false, normalizePath(path), numMode, cred);
454
- });
455
- }
588
+ export async function lchmod(path, mode) {
589
+ const handle = await lopen(path, 'r+');
590
+ try {
591
+ await handle.chmod(mode);
592
+ }
593
+ finally {
594
+ await handle.close();
595
+ }
596
+ }
597
+ lchmod;
456
598
  /**
457
599
  * Change file timestamps of the file referenced by the supplied path.
458
600
  * @param path
459
601
  * @param atime
460
602
  * @param mtime
461
603
  */
462
- export function utimes(path, atime, mtime) {
463
- return __awaiter(this, void 0, void 0, function* () {
464
- return doOp('utimes', true, path, normalizeTime(atime), normalizeTime(mtime), cred);
465
- });
466
- }
604
+ export async function utimes(path, atime, mtime) {
605
+ const handle = await open(path, 'r+');
606
+ try {
607
+ await handle.utimes(atime, mtime);
608
+ }
609
+ finally {
610
+ await handle.close();
611
+ }
612
+ }
613
+ utimes;
467
614
  /**
468
615
  * Change file timestamps of the file referenced by the supplied path.
469
616
  * @param path
470
617
  * @param atime
471
618
  * @param mtime
472
619
  */
473
- export function lutimes(path, atime, mtime) {
474
- return __awaiter(this, void 0, void 0, function* () {
475
- return doOp('utimes', false, path, normalizeTime(atime), normalizeTime(mtime), cred);
476
- });
477
- }
478
- /**
479
- * `realpath`.
480
- * @param path
481
- * @param cache An object literal of mapped paths that can be used to
482
- * force a specific path resolution or avoid additional `fs.stat` calls for
483
- * known real paths.
484
- * @return [String]
485
- */
486
- export function realpath(path, cache = {}) {
487
- return __awaiter(this, void 0, void 0, function* () {
488
- path = normalizePath(path);
489
- const { fs, path: resolvedPath, mountPoint } = resolveFS(path);
490
- try {
491
- const stats = yield fs.stat(resolvedPath, cred);
492
- if (!stats.isSymbolicLink()) {
493
- return path;
494
- }
495
- const dst = mountPoint + normalizePath(yield fs.readlink(resolvedPath, cred));
496
- return realpath(dst);
620
+ export async function lutimes(path, atime, mtime) {
621
+ const handle = await lopen(path, 'r+');
622
+ try {
623
+ await handle.utimes(atime, mtime);
624
+ }
625
+ finally {
626
+ await handle.close();
627
+ }
628
+ }
629
+ lutimes;
630
+ export async function realpath(path, options) {
631
+ path = normalizePath(path);
632
+ const { fs, path: resolvedPath, mountPoint } = resolveFS(path);
633
+ try {
634
+ const stats = await fs.stat(resolvedPath, cred);
635
+ if (!stats.isSymbolicLink()) {
636
+ return path;
497
637
  }
498
- catch (e) {
499
- throw fixError(e, { [resolvedPath]: path });
500
- }
501
- });
638
+ const dst = mountPoint + normalizePath(decode(await _readFile(resolvedPath, 'r+', false)));
639
+ return realpath(dst);
640
+ }
641
+ catch (e) {
642
+ throw fixError(e, { [resolvedPath]: path });
643
+ }
502
644
  }
503
- export function watchFile(filename, arg2, listener = nop) {
504
- return __awaiter(this, void 0, void 0, function* () {
505
- throw new ApiError(ErrorCode.ENOTSUP);
506
- });
645
+ realpath;
646
+ export async function watchFile(filename, arg2, listener = nop) {
647
+ throw new ApiError(ErrorCode.ENOTSUP);
507
648
  }
508
- export function unwatchFile(filename, listener = nop) {
509
- return __awaiter(this, void 0, void 0, function* () {
510
- throw new ApiError(ErrorCode.ENOTSUP);
511
- });
649
+ export async function unwatchFile(filename, listener = nop) {
650
+ throw new ApiError(ErrorCode.ENOTSUP);
512
651
  }
513
- export function watch(filename, arg2, listener = nop) {
514
- return __awaiter(this, void 0, void 0, function* () {
515
- throw new ApiError(ErrorCode.ENOTSUP);
516
- });
652
+ export async function watch(filename, arg2, listener = nop) {
653
+ throw new ApiError(ErrorCode.ENOTSUP);
517
654
  }
518
655
  /**
519
656
  * `access`.
520
657
  * @param path
521
658
  * @param mode
522
659
  */
523
- export function access(path, mode = 0o600) {
524
- return __awaiter(this, void 0, void 0, function* () {
525
- return doOp('access', true, path, mode, cred);
526
- });
660
+ export async function access(path, mode = 0o600) {
661
+ const stats = await stat(path);
662
+ if (!stats.hasAccess(mode, cred)) {
663
+ throw new ApiError(ErrorCode.EACCES);
664
+ }
527
665
  }
528
- export function createReadStream(path, options) {
529
- return __awaiter(this, void 0, void 0, function* () {
530
- throw new ApiError(ErrorCode.ENOTSUP);
531
- });
666
+ export async function createReadStream(path, options) {
667
+ throw new ApiError(ErrorCode.ENOTSUP);
532
668
  }
533
- export function createWriteStream(path, options) {
534
- return __awaiter(this, void 0, void 0, function* () {
535
- throw new ApiError(ErrorCode.ENOTSUP);
536
- });
669
+ export async function createWriteStream(path, options) {
670
+ throw new ApiError(ErrorCode.ENOTSUP);
671
+ }
672
+ export async function rm(path) {
673
+ throw new ApiError(ErrorCode.ENOTSUP);
674
+ }
675
+ export async function mkdtemp(path) {
676
+ throw new ApiError(ErrorCode.ENOTSUP);
677
+ }
678
+ export async function copyFile(path) {
679
+ throw new ApiError(ErrorCode.ENOTSUP);
537
680
  }