@zenfs/core 1.11.3 → 2.0.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 (116) hide show
  1. package/dist/backends/backend.d.ts +19 -15
  2. package/dist/backends/backend.js +31 -15
  3. package/dist/backends/cow.d.ts +20 -30
  4. package/dist/backends/cow.js +52 -142
  5. package/dist/backends/fetch.d.ts +1 -0
  6. package/dist/backends/fetch.js +3 -1
  7. package/dist/backends/index.d.ts +1 -1
  8. package/dist/backends/index.js +1 -1
  9. package/dist/backends/memory.d.ts +5 -7
  10. package/dist/backends/memory.js +2 -3
  11. package/dist/backends/passthrough.d.ts +19 -22
  12. package/dist/backends/passthrough.js +85 -160
  13. package/dist/backends/port.d.ts +207 -0
  14. package/dist/backends/port.js +297 -0
  15. package/dist/backends/single_buffer.d.ts +11 -5
  16. package/dist/backends/single_buffer.js +18 -12
  17. package/dist/backends/store/fs.d.ts +11 -27
  18. package/dist/backends/store/fs.js +67 -91
  19. package/dist/backends/store/store.d.ts +7 -12
  20. package/dist/config.d.ts +1 -10
  21. package/dist/config.js +7 -8
  22. package/dist/context.d.ts +8 -21
  23. package/dist/context.js +33 -10
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.js +2 -1
  26. package/dist/internal/contexts.d.ts +63 -0
  27. package/dist/internal/contexts.js +15 -0
  28. package/dist/internal/credentials.d.ts +2 -11
  29. package/dist/internal/credentials.js +0 -19
  30. package/dist/internal/devices.d.ts +18 -80
  31. package/dist/internal/devices.js +76 -279
  32. package/dist/internal/file_index.js +3 -3
  33. package/dist/internal/filesystem.d.ts +31 -89
  34. package/dist/internal/filesystem.js +21 -20
  35. package/dist/internal/index.d.ts +0 -1
  36. package/dist/internal/index.js +0 -1
  37. package/dist/internal/index_fs.d.ts +12 -30
  38. package/dist/internal/index_fs.js +23 -55
  39. package/dist/internal/inode.d.ts +147 -9
  40. package/dist/internal/inode.js +333 -25
  41. package/dist/internal/log.d.ts +19 -13
  42. package/dist/internal/log.js +81 -80
  43. package/dist/mixins/async.js +26 -90
  44. package/dist/mixins/mutexed.d.ts +17 -16
  45. package/dist/mixins/mutexed.js +29 -31
  46. package/dist/mixins/readonly.d.ts +7 -6
  47. package/dist/mixins/readonly.js +6 -0
  48. package/dist/mixins/sync.js +8 -8
  49. package/dist/{vfs/path.d.ts → path.d.ts} +3 -4
  50. package/dist/{vfs/path.js → path.js} +6 -9
  51. package/dist/readline.d.ts +134 -0
  52. package/dist/readline.js +623 -0
  53. package/dist/utils.d.ts +4 -35
  54. package/dist/utils.js +8 -73
  55. package/dist/vfs/acl.d.ts +42 -0
  56. package/dist/vfs/acl.js +249 -0
  57. package/dist/vfs/async.d.ts +7 -21
  58. package/dist/vfs/async.js +19 -19
  59. package/dist/vfs/config.d.ts +6 -18
  60. package/dist/vfs/config.js +8 -18
  61. package/dist/vfs/dir.d.ts +3 -3
  62. package/dist/vfs/dir.js +9 -8
  63. package/dist/vfs/file.d.ts +106 -0
  64. package/dist/vfs/file.js +235 -0
  65. package/dist/vfs/flags.d.ts +19 -0
  66. package/dist/vfs/flags.js +62 -0
  67. package/dist/vfs/index.d.ts +4 -10
  68. package/dist/vfs/index.js +4 -13
  69. package/dist/vfs/ioctl.d.ts +87 -0
  70. package/dist/vfs/ioctl.js +304 -0
  71. package/dist/vfs/promises.d.ts +78 -16
  72. package/dist/vfs/promises.js +273 -122
  73. package/dist/vfs/shared.d.ts +7 -26
  74. package/dist/vfs/shared.js +25 -53
  75. package/dist/{stats.d.ts → vfs/stats.d.ts} +14 -28
  76. package/dist/{stats.js → vfs/stats.js} +11 -66
  77. package/dist/vfs/streams.d.ts +1 -0
  78. package/dist/vfs/streams.js +24 -19
  79. package/dist/vfs/sync.d.ts +4 -3
  80. package/dist/vfs/sync.js +143 -128
  81. package/dist/vfs/watchers.d.ts +2 -2
  82. package/dist/vfs/watchers.js +6 -6
  83. package/dist/vfs/xattr.d.ts +116 -0
  84. package/dist/vfs/xattr.js +218 -0
  85. package/package.json +3 -3
  86. package/readme.md +1 -1
  87. package/tests/backend/config.worker.js +4 -1
  88. package/tests/backend/fetch.test.ts +3 -0
  89. package/tests/backend/port.test.ts +21 -35
  90. package/tests/backend/remote.worker.js +4 -1
  91. package/tests/backend/single-buffer.test.ts +24 -0
  92. package/tests/common/context.test.ts +1 -1
  93. package/tests/common/handle.test.ts +17 -12
  94. package/tests/common/path.test.ts +1 -1
  95. package/tests/common/readline.test.ts +104 -0
  96. package/tests/common.ts +4 -19
  97. package/tests/fetch/fetch.ts +1 -1
  98. package/tests/fs/links.test.ts +1 -1
  99. package/tests/fs/permissions.test.ts +7 -6
  100. package/tests/fs/readFile.test.ts +3 -3
  101. package/tests/fs/stat.test.ts +6 -6
  102. package/tests/fs/streams.test.ts +2 -11
  103. package/tests/fs/times.test.ts +1 -1
  104. package/tests/fs/xattr.test.ts +85 -0
  105. package/tests/logs.js +22 -0
  106. package/tests/setup/context.ts +1 -1
  107. package/tests/setup/index.ts +3 -3
  108. package/tests/setup/port.ts +1 -1
  109. package/dist/backends/port/fs.d.ts +0 -84
  110. package/dist/backends/port/fs.js +0 -151
  111. package/dist/backends/port/rpc.d.ts +0 -77
  112. package/dist/backends/port/rpc.js +0 -100
  113. package/dist/backends/store/simple.d.ts +0 -20
  114. package/dist/backends/store/simple.js +0 -13
  115. package/dist/internal/file.d.ts +0 -351
  116. package/dist/internal/file.js +0 -739
@@ -1,739 +0,0 @@
1
- import { extendBuffer } from 'utilium/buffer.js';
2
- import { _chown, Stats } from '../stats.js';
3
- import { config } from '../vfs/config.js';
4
- import * as c from '../vfs/constants.js';
5
- import { Errno, ErrnoError } from './error.js';
6
- import { err, log_deprecated } from './log.js';
7
- import '../polyfills.js';
8
- const maxByteLength = 0xffff; // 64 KiB
9
- const validFlags = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
10
- /**
11
- * @internal @hidden
12
- */
13
- export function parseFlag(flag) {
14
- if (typeof flag === 'number') {
15
- return flagToString(flag);
16
- }
17
- if (!validFlags.includes(flag)) {
18
- throw new Error('Invalid flag string: ' + flag);
19
- }
20
- return flag;
21
- }
22
- /**
23
- * @internal @hidden
24
- */
25
- export function flagToString(flag) {
26
- switch (flag) {
27
- case c.O_RDONLY:
28
- return 'r';
29
- case c.O_RDONLY | c.O_SYNC:
30
- return 'rs';
31
- case c.O_RDWR:
32
- return 'r+';
33
- case c.O_RDWR | c.O_SYNC:
34
- return 'rs+';
35
- case c.O_TRUNC | c.O_CREAT | c.O_WRONLY:
36
- return 'w';
37
- case c.O_TRUNC | c.O_CREAT | c.O_WRONLY | c.O_EXCL:
38
- return 'wx';
39
- case c.O_TRUNC | c.O_CREAT | c.O_RDWR:
40
- return 'w+';
41
- case c.O_TRUNC | c.O_CREAT | c.O_RDWR | c.O_EXCL:
42
- return 'wx+';
43
- case c.O_APPEND | c.O_CREAT | c.O_WRONLY:
44
- return 'a';
45
- case c.O_APPEND | c.O_CREAT | c.O_WRONLY | c.O_EXCL:
46
- return 'ax';
47
- case c.O_APPEND | c.O_CREAT | c.O_RDWR:
48
- return 'a+';
49
- case c.O_APPEND | c.O_CREAT | c.O_RDWR | c.O_EXCL:
50
- return 'ax+';
51
- default:
52
- throw new Error('Invalid flag number: ' + flag);
53
- }
54
- }
55
- /**
56
- * @internal @hidden
57
- */
58
- export function flagToNumber(flag) {
59
- switch (flag) {
60
- case 'r':
61
- return c.O_RDONLY;
62
- case 'rs':
63
- return c.O_RDONLY | c.O_SYNC;
64
- case 'r+':
65
- return c.O_RDWR;
66
- case 'rs+':
67
- return c.O_RDWR | c.O_SYNC;
68
- case 'w':
69
- return c.O_TRUNC | c.O_CREAT | c.O_WRONLY;
70
- case 'wx':
71
- return c.O_TRUNC | c.O_CREAT | c.O_WRONLY | c.O_EXCL;
72
- case 'w+':
73
- return c.O_TRUNC | c.O_CREAT | c.O_RDWR;
74
- case 'wx+':
75
- return c.O_TRUNC | c.O_CREAT | c.O_RDWR | c.O_EXCL;
76
- case 'a':
77
- return c.O_APPEND | c.O_CREAT | c.O_WRONLY;
78
- case 'ax':
79
- return c.O_APPEND | c.O_CREAT | c.O_WRONLY | c.O_EXCL;
80
- case 'a+':
81
- return c.O_APPEND | c.O_CREAT | c.O_RDWR;
82
- case 'ax+':
83
- return c.O_APPEND | c.O_CREAT | c.O_RDWR | c.O_EXCL;
84
- default:
85
- throw new Error('Invalid flag string: ' + flag);
86
- }
87
- }
88
- /**
89
- * Parses a flag as a mode (W_OK, R_OK, and/or X_OK)
90
- * @param flag the flag to parse
91
- * @internal @hidden
92
- */
93
- export function flagToMode(flag) {
94
- let mode = 0;
95
- mode <<= 1;
96
- mode += +isReadable(flag);
97
- mode <<= 1;
98
- mode += +isWriteable(flag);
99
- mode <<= 1;
100
- return mode;
101
- }
102
- /** @hidden */
103
- export function isReadable(flag) {
104
- return flag.indexOf('r') !== -1 || flag.indexOf('+') !== -1;
105
- }
106
- /** @hidden */
107
- export function isWriteable(flag) {
108
- return flag.indexOf('w') !== -1 || flag.indexOf('a') !== -1 || flag.indexOf('+') !== -1;
109
- }
110
- /** @hidden */
111
- export function isTruncating(flag) {
112
- return flag.indexOf('w') !== -1;
113
- }
114
- /** @hidden */
115
- export function isAppendable(flag) {
116
- return flag.indexOf('a') !== -1;
117
- }
118
- /** @hidden */
119
- export function isSynchronous(flag) {
120
- return flag.indexOf('s') !== -1;
121
- }
122
- /** @hidden */
123
- export function isExclusive(flag) {
124
- return flag.indexOf('x') !== -1;
125
- }
126
- /**
127
- * @category Internals
128
- */
129
- export class File {
130
- constructor(
131
- /**
132
- * @internal
133
- * The file system that created the file
134
- */
135
- fs, path) {
136
- this.fs = fs;
137
- this.path = path;
138
- }
139
- async [Symbol.asyncDispose]() {
140
- await this.close();
141
- }
142
- [Symbol.dispose]() {
143
- this.closeSync();
144
- }
145
- /**
146
- * Default implementation maps to `sync`.
147
- */
148
- datasync() {
149
- return this.sync();
150
- }
151
- /**
152
- * Default implementation maps to `syncSync`.
153
- */
154
- datasyncSync() {
155
- return this.syncSync();
156
- }
157
- }
158
- /**
159
- * An implementation of `File` that operates completely in-memory.
160
- * `PreloadFile`s are backed by a `Uint8Array`.
161
- * @category Internals
162
- */
163
- export class PreloadFile extends File {
164
- /**
165
- * Creates a file with `path` and, optionally, the given contents.
166
- * Note that, if contents is specified, it will be mutated by the file.
167
- */
168
- constructor(fs, path, flag, stats,
169
- /**
170
- * A buffer containing the entire contents of the file.
171
- */
172
- _buffer = new Uint8Array(new ArrayBuffer(0, fs.attributes.has('no_buffer_resize') ? {} : { maxByteLength }))) {
173
- super(fs, path);
174
- this.flag = flag;
175
- this.stats = stats;
176
- this._buffer = _buffer;
177
- /**
178
- * Current position
179
- */
180
- this._position = 0;
181
- /**
182
- * Whether the file has changes which have not been written to the FS
183
- */
184
- this.dirty = false;
185
- /**
186
- * Whether the file is open or closed
187
- */
188
- this.closed = false;
189
- /*
190
- Note:
191
- This invariant is *not* maintained once the file starts getting modified.
192
- It only actually matters if file is readable, as writeable modes may truncate/append to file.
193
- */
194
- if (this.stats.size == _buffer.byteLength)
195
- return;
196
- if (!isWriteable(this.flag)) {
197
- throw err(new ErrnoError(Errno.EIO, `Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`, path));
198
- }
199
- this.stats.size = _buffer.byteLength;
200
- this.dirty = true;
201
- }
202
- /**
203
- * Get the underlying buffer for this file. Mutating not recommended and will mess up dirty tracking.
204
- */
205
- get buffer() {
206
- return this._buffer;
207
- }
208
- /**
209
- * Get the current file position.
210
- *
211
- * We emulate the following bug mentioned in the Node documentation:
212
- *
213
- * On Linux, positional writes don't work when the file is opened in append mode.
214
- * The kernel ignores the position argument and always appends the data to the end of the file.
215
- * @returns The current file position.
216
- */
217
- get position() {
218
- if (isAppendable(this.flag)) {
219
- return this.stats.size;
220
- }
221
- return this._position;
222
- }
223
- set position(value) {
224
- this._position = value;
225
- }
226
- async sync() {
227
- if (this.closed)
228
- throw ErrnoError.With('EBADF', this.path, 'sync');
229
- if (!this.dirty)
230
- return;
231
- if (!this.fs.attributes.has('no_write'))
232
- await this.fs.sync(this.path, this._buffer, this.stats);
233
- this.dirty = false;
234
- }
235
- syncSync() {
236
- if (this.closed)
237
- throw ErrnoError.With('EBADF', this.path, 'sync');
238
- if (!this.dirty)
239
- return;
240
- if (!this.fs.attributes.has('no_write'))
241
- this.fs.syncSync(this.path, this._buffer, this.stats);
242
- this.dirty = false;
243
- }
244
- async close() {
245
- if (this.closed)
246
- throw ErrnoError.With('EBADF', this.path, 'close');
247
- await this.sync();
248
- this.dispose();
249
- }
250
- closeSync() {
251
- if (this.closed)
252
- throw ErrnoError.With('EBADF', this.path, 'close');
253
- this.syncSync();
254
- this.dispose();
255
- }
256
- /**
257
- * Cleans up. This will *not* sync the file data to the FS
258
- */
259
- dispose(force) {
260
- if (this.closed)
261
- throw ErrnoError.With('EBADF', this.path, 'dispose');
262
- if (this.dirty && !force) {
263
- throw ErrnoError.With('EBUSY', this.path, 'dispose');
264
- }
265
- this.closed = true;
266
- }
267
- stat() {
268
- if (this.closed)
269
- throw ErrnoError.With('EBADF', this.path, 'stat');
270
- return Promise.resolve(new Stats(this.stats));
271
- }
272
- statSync() {
273
- if (this.closed)
274
- throw ErrnoError.With('EBADF', this.path, 'stat');
275
- return new Stats(this.stats);
276
- }
277
- _truncate(length) {
278
- if (this.closed)
279
- throw ErrnoError.With('EBADF', this.path, 'truncate');
280
- this.dirty = true;
281
- if (!isWriteable(this.flag)) {
282
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
283
- }
284
- this.stats.mtimeMs = Date.now();
285
- if (length > this._buffer.length) {
286
- const data = new Uint8Array(length - this._buffer.length);
287
- // Write will set stats.size and handle syncing.
288
- this._write(data, 0, data.length, this._buffer.length);
289
- return;
290
- }
291
- this.stats.size = length;
292
- // Truncate.
293
- this._buffer = length ? this._buffer.subarray(0, length) : new Uint8Array();
294
- }
295
- async truncate(length) {
296
- this._truncate(length);
297
- if (config.syncImmediately)
298
- await this.sync();
299
- }
300
- truncateSync(length) {
301
- this._truncate(length);
302
- if (config.syncImmediately)
303
- this.syncSync();
304
- }
305
- _write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
306
- if (this.closed)
307
- throw ErrnoError.With('EBADF', this.path, 'write');
308
- if (!isWriteable(this.flag)) {
309
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
310
- }
311
- this.dirty = true;
312
- const end = position + length;
313
- const slice = buffer.subarray(offset, offset + length);
314
- this._buffer = extendBuffer(this._buffer, end);
315
- if (end > this.stats.size)
316
- this.stats.size = end;
317
- this._buffer.set(slice, position);
318
- this.stats.mtimeMs = Date.now();
319
- this.position = position + slice.byteLength;
320
- return slice.byteLength;
321
- }
322
- /**
323
- * Write buffer to the file.
324
- * @param buffer Uint8Array containing the data to write to the file.
325
- * @param offset Offset in the buffer to start reading data from.
326
- * @param length The amount of bytes to write to the file.
327
- * @param position Offset from the beginning of the file where this data should be written.
328
- * If position is null, the data will be written at the current position.
329
- */
330
- async write(buffer, offset, length, position) {
331
- const bytesWritten = this._write(buffer, offset, length, position);
332
- if (config.syncImmediately)
333
- await this.sync();
334
- return bytesWritten;
335
- }
336
- /**
337
- * Write buffer to the file.
338
- * @param buffer Uint8Array containing the data to write to the file.
339
- * @param offset Offset in the buffer to start reading data from.
340
- * @param length The amount of bytes to write to the file.
341
- * @param position Offset from the beginning of the file where this data should be written.
342
- * If position is null, the data will be written at the current position.
343
- * @returns bytes written
344
- */
345
- writeSync(buffer, offset, length, position) {
346
- const bytesWritten = this._write(buffer, offset, length, position);
347
- if (config.syncImmediately)
348
- this.syncSync();
349
- return bytesWritten;
350
- }
351
- _read(buffer, offset = 0, length = buffer.byteLength - offset, position) {
352
- if (this.closed)
353
- throw ErrnoError.With('EBADF', this.path, 'read');
354
- if (!isReadable(this.flag)) {
355
- throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode');
356
- }
357
- if (config.updateOnRead) {
358
- this.dirty = true;
359
- }
360
- this.stats.atimeMs = Date.now();
361
- position !== null && position !== void 0 ? position : (position = this.position);
362
- let end = position + length;
363
- if (end > this.stats.size) {
364
- end = position + Math.max(this.stats.size - position, 0);
365
- }
366
- this._position = end;
367
- const bytesRead = end - position;
368
- if (bytesRead == 0) {
369
- // No copy/read. Return immediately for better performance
370
- return bytesRead;
371
- }
372
- const slice = this._buffer.subarray(position, end);
373
- new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength).set(slice, offset);
374
- return bytesRead;
375
- }
376
- /**
377
- * Read data from the file.
378
- * @param buffer The buffer that the data will be written to.
379
- * @param offset The offset within the buffer where writing will start.
380
- * @param length An integer specifying the number of bytes to read.
381
- * @param position An integer specifying where to begin reading from in the file.
382
- * If position is null, data will be read from the current file position.
383
- */
384
- async read(buffer, offset, length, position) {
385
- const bytesRead = this._read(buffer, offset, length, position);
386
- if (config.syncImmediately)
387
- await this.sync();
388
- return { bytesRead, buffer };
389
- }
390
- /**
391
- * Read data from the file.
392
- * @param buffer The buffer that the data will be written to.
393
- * @param offset The offset within the buffer where writing will start.
394
- * @param length An integer specifying the number of bytes to read.
395
- * @param position An integer specifying where to begin reading from in the file.
396
- * If position is null, data will be read from the current file position.
397
- * @returns number of bytes written
398
- */
399
- readSync(buffer, offset, length, position) {
400
- const bytesRead = this._read(buffer, offset, length, position);
401
- if (config.syncImmediately)
402
- this.syncSync();
403
- return bytesRead;
404
- }
405
- async chmod(mode) {
406
- if (this.closed)
407
- throw ErrnoError.With('EBADF', this.path, 'chmod');
408
- this.dirty = true;
409
- this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
410
- if (config.syncImmediately || mode > c.S_IFMT)
411
- await this.sync();
412
- }
413
- chmodSync(mode) {
414
- if (this.closed)
415
- throw ErrnoError.With('EBADF', this.path, 'chmod');
416
- this.dirty = true;
417
- this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
418
- if (config.syncImmediately || mode > c.S_IFMT)
419
- this.syncSync();
420
- }
421
- async chown(uid, gid) {
422
- if (this.closed)
423
- throw ErrnoError.With('EBADF', this.path, 'chown');
424
- this.dirty = true;
425
- _chown(this.stats, uid, gid);
426
- if (config.syncImmediately)
427
- await this.sync();
428
- }
429
- chownSync(uid, gid) {
430
- if (this.closed)
431
- throw ErrnoError.With('EBADF', this.path, 'chown');
432
- this.dirty = true;
433
- _chown(this.stats, uid, gid);
434
- if (config.syncImmediately)
435
- this.syncSync();
436
- }
437
- async utimes(atime, mtime) {
438
- if (this.closed)
439
- throw ErrnoError.With('EBADF', this.path, 'utimes');
440
- this.dirty = true;
441
- this.stats.atimeMs = atime;
442
- this.stats.mtimeMs = mtime;
443
- if (config.syncImmediately)
444
- await this.sync();
445
- }
446
- utimesSync(atime, mtime) {
447
- if (this.closed)
448
- throw ErrnoError.With('EBADF', this.path, 'utimes');
449
- this.dirty = true;
450
- this.stats.atimeMs = atime;
451
- this.stats.mtimeMs = mtime;
452
- if (config.syncImmediately)
453
- this.syncSync();
454
- }
455
- }
456
- /* node:coverage disable */
457
- /**
458
- * For the file systems which do not sync to anything.
459
- * @category Internals
460
- * @deprecated
461
- */
462
- export class NoSyncFile extends PreloadFile {
463
- constructor(...args) {
464
- log_deprecated('NoSyncFile');
465
- super(...args);
466
- }
467
- sync() {
468
- return Promise.resolve();
469
- }
470
- syncSync() { }
471
- close() {
472
- return Promise.resolve();
473
- }
474
- closeSync() { }
475
- }
476
- /* node:coverage enable */
477
- /**
478
- * An implementation of `File` that uses the FS
479
- * @category Internals
480
- */
481
- export class LazyFile extends File {
482
- /**
483
- * Get the current file position.
484
- *
485
- * We emulate the following bug mentioned in the Node documentation:
486
- *
487
- * On Linux, positional writes don't work when the file is opened in append mode.
488
- * The kernel ignores the position argument and always appends the data to the end of the file.
489
- * @returns The current file position.
490
- */
491
- get position() {
492
- return isAppendable(this.flag) ? this.stats.size : this._position;
493
- }
494
- set position(value) {
495
- this._position = value;
496
- }
497
- /**
498
- * Creates a file with `path` and, optionally, the given contents.
499
- * Note that, if contents is specified, it will be mutated by the file.
500
- */
501
- constructor(fs, path, flag, stats) {
502
- super(fs, path);
503
- this.flag = flag;
504
- this.stats = stats;
505
- /**
506
- * Current position
507
- */
508
- this._position = 0;
509
- /**
510
- * Whether the file has changes which have not been written to the FS
511
- */
512
- this.dirty = false;
513
- /**
514
- * Whether the file is open or closed
515
- */
516
- this.closed = false;
517
- }
518
- async sync() {
519
- if (this.closed)
520
- throw ErrnoError.With('EBADF', this.path, 'sync');
521
- if (!this.dirty)
522
- return;
523
- if (!this.fs.attributes.has('no_write'))
524
- await this.fs.sync(this.path, undefined, this.stats);
525
- this.dirty = false;
526
- }
527
- syncSync() {
528
- if (this.closed)
529
- throw ErrnoError.With('EBADF', this.path, 'sync');
530
- if (!this.dirty)
531
- return;
532
- if (!this.fs.attributes.has('no_write'))
533
- this.fs.syncSync(this.path, undefined, this.stats);
534
- this.dirty = false;
535
- }
536
- async close() {
537
- if (this.closed)
538
- throw ErrnoError.With('EBADF', this.path, 'close');
539
- await this.sync();
540
- this.dispose();
541
- }
542
- closeSync() {
543
- if (this.closed)
544
- throw ErrnoError.With('EBADF', this.path, 'close');
545
- this.syncSync();
546
- this.dispose();
547
- }
548
- /**
549
- * Cleans up. This will *not* sync the file data to the FS
550
- */
551
- dispose(force) {
552
- if (this.closed)
553
- throw ErrnoError.With('EBADF', this.path, 'dispose');
554
- if (this.dirty && !force)
555
- throw ErrnoError.With('EBUSY', this.path, 'dispose');
556
- this.closed = true;
557
- }
558
- stat() {
559
- if (this.closed)
560
- throw ErrnoError.With('EBADF', this.path, 'stat');
561
- return Promise.resolve(new Stats(this.stats));
562
- }
563
- statSync() {
564
- if (this.closed)
565
- throw ErrnoError.With('EBADF', this.path, 'stat');
566
- return new Stats(this.stats);
567
- }
568
- async truncate(length) {
569
- if (this.closed)
570
- throw ErrnoError.With('EBADF', this.path, 'truncate');
571
- this.dirty = true;
572
- if (!isWriteable(this.flag)) {
573
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
574
- }
575
- this.stats.mtimeMs = Date.now();
576
- this.stats.size = length;
577
- if (config.syncImmediately)
578
- await this.sync();
579
- }
580
- truncateSync(length) {
581
- if (this.closed)
582
- throw ErrnoError.With('EBADF', this.path, 'truncate');
583
- this.dirty = true;
584
- if (!isWriteable(this.flag)) {
585
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
586
- }
587
- this.stats.mtimeMs = Date.now();
588
- this.stats.size = length;
589
- if (config.syncImmediately)
590
- this.syncSync();
591
- }
592
- prepareWrite(buffer, offset, length, position) {
593
- if (this.closed)
594
- throw ErrnoError.With('EBADF', this.path, 'write');
595
- if (!isWriteable(this.flag)) {
596
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
597
- }
598
- this.dirty = true;
599
- const end = position + length;
600
- const slice = buffer.subarray(offset, offset + length);
601
- if (end > this.stats.size)
602
- this.stats.size = end;
603
- this.stats.mtimeMs = Date.now();
604
- this._position = position + slice.byteLength;
605
- return slice;
606
- }
607
- /**
608
- * Write buffer to the file.
609
- * @param buffer Uint8Array containing the data to write to the file.
610
- * @param offset Offset in the buffer to start reading data from.
611
- * @param length The amount of bytes to write to the file.
612
- * @param position Offset from the beginning of the file where this data should be written.
613
- * If position is null, the data will be written at the current position.
614
- */
615
- async write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
616
- const slice = this.prepareWrite(buffer, offset, length, position);
617
- await this.fs.write(this.path, slice, position);
618
- if (config.syncImmediately)
619
- await this.sync();
620
- return slice.byteLength;
621
- }
622
- /**
623
- * Write buffer to the file.
624
- * @param buffer Uint8Array containing the data to write to the file.
625
- * @param offset Offset in the buffer to start reading data from.
626
- * @param length The amount of bytes to write to the file.
627
- * @param position Offset from the beginning of the file where this data should be written.
628
- * If position is null, the data will be written at the current position.
629
- * @returns bytes written
630
- */
631
- writeSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
632
- const slice = this.prepareWrite(buffer, offset, length, position);
633
- this.fs.writeSync(this.path, slice, position);
634
- if (config.syncImmediately)
635
- this.syncSync();
636
- return slice.byteLength;
637
- }
638
- /**
639
- * Computes position information for reading
640
- */
641
- prepareRead(length, position) {
642
- if (this.closed)
643
- throw ErrnoError.With('EBADF', this.path, 'read');
644
- if (!isReadable(this.flag))
645
- throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode');
646
- if (config.updateOnRead)
647
- this.dirty = true;
648
- this.stats.atimeMs = Date.now();
649
- let end = position + length;
650
- if (end > this.stats.size) {
651
- end = position + Math.max(this.stats.size - position, 0);
652
- }
653
- this._position = end;
654
- return end;
655
- }
656
- /**
657
- * Read data from the file.
658
- * @param buffer The buffer that the data will be written to.
659
- * @param offset The offset within the buffer where writing will start.
660
- * @param length An integer specifying the number of bytes to read.
661
- * @param position An integer specifying where to begin reading from in the file.
662
- * If position is unset, data will be read from the current file position.
663
- */
664
- async read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
665
- const end = this.prepareRead(length, position);
666
- const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
667
- await this.fs.read(this.path, uint8.subarray(offset, offset + length), position, end);
668
- if (config.syncImmediately)
669
- await this.sync();
670
- return { bytesRead: end - position, buffer };
671
- }
672
- /**
673
- * Read data from the file.
674
- * @param buffer The buffer that the data will be written to.
675
- * @param offset The offset within the buffer where writing will start.
676
- * @param length An integer specifying the number of bytes to read.
677
- * @param position An integer specifying where to begin reading from in the file.
678
- * If position is null, data will be read from the current file position.
679
- * @returns number of bytes written
680
- */
681
- readSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
682
- const end = this.prepareRead(length, position);
683
- const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
684
- this.fs.readSync(this.path, uint8.subarray(offset, offset + length), position, end);
685
- if (config.syncImmediately)
686
- this.syncSync();
687
- return end - position;
688
- }
689
- async chmod(mode) {
690
- if (this.closed)
691
- throw ErrnoError.With('EBADF', this.path, 'chmod');
692
- this.dirty = true;
693
- this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
694
- if (config.syncImmediately || mode > c.S_IFMT)
695
- await this.sync();
696
- }
697
- chmodSync(mode) {
698
- if (this.closed)
699
- throw ErrnoError.With('EBADF', this.path, 'chmod');
700
- this.dirty = true;
701
- this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
702
- if (config.syncImmediately || mode > c.S_IFMT)
703
- this.syncSync();
704
- }
705
- async chown(uid, gid) {
706
- if (this.closed)
707
- throw ErrnoError.With('EBADF', this.path, 'chown');
708
- this.dirty = true;
709
- _chown(this.stats, uid, gid);
710
- if (config.syncImmediately)
711
- await this.sync();
712
- }
713
- chownSync(uid, gid) {
714
- if (this.closed)
715
- throw ErrnoError.With('EBADF', this.path, 'chown');
716
- this.dirty = true;
717
- _chown(this.stats, uid, gid);
718
- if (config.syncImmediately)
719
- this.syncSync();
720
- }
721
- async utimes(atime, mtime) {
722
- if (this.closed)
723
- throw ErrnoError.With('EBADF', this.path, 'utimes');
724
- this.dirty = true;
725
- this.stats.atimeMs = atime;
726
- this.stats.mtimeMs = mtime;
727
- if (config.syncImmediately)
728
- await this.sync();
729
- }
730
- utimesSync(atime, mtime) {
731
- if (this.closed)
732
- throw ErrnoError.With('EBADF', this.path, 'utimes');
733
- this.dirty = true;
734
- this.stats.atimeMs = atime;
735
- this.stats.mtimeMs = mtime;
736
- if (config.syncImmediately)
737
- this.syncSync();
738
- }
739
- }