@zenfs/core 0.1.0 → 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 (44) hide show
  1. package/dist/ApiError.d.ts +51 -14
  2. package/dist/ApiError.js +60 -34
  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 +146 -133
  7. package/dist/backends/AsyncStore.d.ts +29 -28
  8. package/dist/backends/AsyncStore.js +139 -189
  9. package/dist/backends/InMemory.d.ts +16 -13
  10. package/dist/backends/InMemory.js +29 -14
  11. package/dist/backends/Locked.d.ts +8 -28
  12. package/dist/backends/Locked.js +44 -148
  13. package/dist/backends/OverlayFS.d.ts +26 -34
  14. package/dist/backends/OverlayFS.js +208 -371
  15. package/dist/backends/SyncStore.d.ts +54 -72
  16. package/dist/backends/SyncStore.js +159 -161
  17. package/dist/backends/backend.d.ts +45 -29
  18. package/dist/backends/backend.js +83 -13
  19. package/dist/backends/index.d.ts +6 -7
  20. package/dist/backends/index.js +5 -6
  21. package/dist/browser.min.js +5 -7
  22. package/dist/browser.min.js.map +4 -4
  23. package/dist/emulation/callbacks.d.ts +36 -67
  24. package/dist/emulation/callbacks.js +90 -46
  25. package/dist/emulation/constants.js +1 -1
  26. package/dist/emulation/promises.d.ts +228 -129
  27. package/dist/emulation/promises.js +414 -172
  28. package/dist/emulation/shared.d.ts +10 -10
  29. package/dist/emulation/shared.js +18 -20
  30. package/dist/emulation/sync.d.ts +25 -25
  31. package/dist/emulation/sync.js +187 -73
  32. package/dist/file.d.ts +166 -170
  33. package/dist/file.js +199 -218
  34. package/dist/filesystem.d.ts +68 -241
  35. package/dist/filesystem.js +59 -383
  36. package/dist/index.d.ts +7 -44
  37. package/dist/index.js +13 -52
  38. package/dist/inode.d.ts +37 -28
  39. package/dist/inode.js +123 -65
  40. package/dist/stats.d.ts +21 -19
  41. package/dist/stats.js +35 -56
  42. package/dist/utils.d.ts +26 -9
  43. package/dist/utils.js +73 -102
  44. package/package.json +4 -3
package/dist/file.js CHANGED
@@ -1,33 +1,33 @@
1
1
  import { ApiError, ErrorCode } from './ApiError.js';
2
2
  import { Stats } from './stats.js';
3
- import { getMount } from './emulation/shared.js';
4
- import { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_SYNC } from './emulation/constants.js';
3
+ import { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_SYNC, S_IFMT } from './emulation/constants.js';
4
+ import { size_max } from './inode.js';
5
5
  export var ActionType;
6
6
  (function (ActionType) {
7
7
  // Indicates that the code should not do anything.
8
8
  ActionType[ActionType["NOP"] = 0] = "NOP";
9
9
  // Indicates that the code should throw an exception.
10
- ActionType[ActionType["THROW_EXCEPTION"] = 1] = "THROW_EXCEPTION";
10
+ ActionType[ActionType["THROW"] = 1] = "THROW";
11
11
  // Indicates that the code should truncate the file, but only if it is a file.
12
- ActionType[ActionType["TRUNCATE_FILE"] = 2] = "TRUNCATE_FILE";
12
+ ActionType[ActionType["TRUNCATE"] = 2] = "TRUNCATE";
13
13
  // Indicates that the code should create the file.
14
- ActionType[ActionType["CREATE_FILE"] = 3] = "CREATE_FILE";
14
+ ActionType[ActionType["CREATE"] = 3] = "CREATE";
15
15
  })(ActionType = ActionType || (ActionType = {}));
16
16
  /**
17
17
  * Represents one of the following file flags. A convenience object.
18
18
  *
19
- * * `'r'` - Open file for reading. An exception occurs if the file does not exist.
20
- * * `'r+'` - Open file for reading and writing. An exception occurs if the file does not exist.
21
- * * `'rs'` - Open file for reading in synchronous mode. Instructs the filesystem to not cache writes.
22
- * * `'rs+'` - Open file for reading and writing, and opens the file in synchronous mode.
23
- * * `'w'` - Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
24
- * * `'wx'` - Like 'w' but opens the file in exclusive mode.
25
- * * `'w+'` - Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists).
26
- * * `'wx+'` - Like 'w+' but opens the file in exclusive mode.
27
- * * `'a'` - Open file for appending. The file is created if it does not exist.
28
- * * `'ax'` - Like 'a' but opens the file in exclusive mode.
29
- * * `'a+'` - Open file for reading and appending. The file is created if it does not exist.
30
- * * `'ax+'` - Like 'a+' but opens the file in exclusive mode.
19
+ * - r: Open file for reading. An exception occurs if the file does not exist.
20
+ * - r+: Open file for reading and writing. An exception occurs if the file does not exist.
21
+ * - rs: Open file for reading in synchronous mode. Instructs the filesystem to not cache writes.
22
+ * - rs+: Open file for reading and writing, and opens the file in synchronous mode.
23
+ * - w: Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
24
+ * - wx: Like 'w' but opens the file in exclusive mode.
25
+ * - w+: Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists).
26
+ * - wx+: Like 'w+' but opens the file in exclusive mode.
27
+ * - a: Open file for appending. The file is created if it does not exist.
28
+ * - ax: Like 'a' but opens the file in exclusive mode.
29
+ * - a+: Open file for reading and appending. The file is created if it does not exist.
30
+ * - ax+: Like 'a+' but opens the file in exclusive mode.
31
31
  *
32
32
  * Exclusive mode ensures that the file path is newly created.
33
33
  */
@@ -38,7 +38,7 @@ export class FileFlag {
38
38
  * @return The FileFlag object representing the flag
39
39
  * @throw when the flag string is invalid
40
40
  */
41
- static getFileFlag(flag) {
41
+ static FromString(flag) {
42
42
  // Check cache first.
43
43
  if (!FileFlag.flagCache.has(flag)) {
44
44
  FileFlag.flagCache.set(flag, new FileFlag(flag));
@@ -52,7 +52,7 @@ export class FileFlag {
52
52
  */
53
53
  constructor(flag) {
54
54
  if (typeof flag === 'number') {
55
- flag = FileFlag.StringFromNumber(flag);
55
+ flag = FileFlag.NumberToString(flag);
56
56
  }
57
57
  if (FileFlag.validFlagStrs.indexOf(flag) < 0) {
58
58
  throw new ApiError(ErrorCode.EINVAL, 'Invalid flag string: ' + flag);
@@ -64,7 +64,7 @@ export class FileFlag {
64
64
  * @return The string representing the flag
65
65
  * @throw when the flag number is invalid
66
66
  */
67
- static StringFromNumber(flag) {
67
+ static NumberToString(flag) {
68
68
  // based on https://github.com/nodejs/node/blob/abbdc3efaa455e6c907ebef5409ac8b0f222f969/lib/internal/fs/utils.js#L619
69
69
  switch (flag) {
70
70
  case O_RDONLY:
@@ -98,14 +98,14 @@ export class FileFlag {
98
98
  /**
99
99
  * Get the underlying flag string for this flag.
100
100
  */
101
- getFlagString() {
101
+ toString() {
102
102
  return this.flagStr;
103
103
  }
104
104
  /**
105
105
  * Get the equivalent mode (0b0xxx: read, write, execute)
106
106
  * Note: Execute will always be 0
107
107
  */
108
- getMode() {
108
+ get mode() {
109
109
  let mode = 0;
110
110
  mode <<= 1;
111
111
  mode += +this.isReadable();
@@ -156,14 +156,12 @@ export class FileFlag {
156
156
  */
157
157
  pathExistsAction() {
158
158
  if (this.isExclusive()) {
159
- return ActionType.THROW_EXCEPTION;
159
+ return ActionType.THROW;
160
160
  }
161
- else if (this.isTruncating()) {
162
- return ActionType.TRUNCATE_FILE;
163
- }
164
- else {
165
- return ActionType.NOP;
161
+ if (this.isTruncating()) {
162
+ return ActionType.TRUNCATE;
166
163
  }
164
+ return ActionType.NOP;
167
165
  }
168
166
  /**
169
167
  * Returns one of the static fields on this object that indicates the
@@ -171,10 +169,10 @@ export class FileFlag {
171
169
  */
172
170
  pathNotExistsAction() {
173
171
  if ((this.isWriteable() || this.isAppendable()) && this.flagStr !== 'r+') {
174
- return ActionType.CREATE_FILE;
172
+ return ActionType.CREATE;
175
173
  }
176
174
  else {
177
- return ActionType.THROW_EXCEPTION;
175
+ return ActionType.THROW;
178
176
  }
179
177
  }
180
178
  }
@@ -182,41 +180,23 @@ export class FileFlag {
182
180
  FileFlag.flagCache = new Map();
183
181
  // Array of valid mode strings.
184
182
  FileFlag.validFlagStrs = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
185
- /**
186
- * Base class that contains shared implementations of functions for the file
187
- * object.
188
- */
189
- export class BaseFile {
190
- async sync() {
191
- throw new ApiError(ErrorCode.ENOTSUP);
192
- }
193
- syncSync() {
194
- throw new ApiError(ErrorCode.ENOTSUP);
195
- }
196
- async datasync() {
183
+ export class File {
184
+ /**
185
+ * Asynchronous `datasync`.
186
+ *
187
+ * Default implementation maps to `sync`.
188
+ */
189
+ datasync() {
197
190
  return this.sync();
198
191
  }
192
+ /**
193
+ * Synchronous `datasync`.
194
+ *
195
+ * Default implementation maps to `syncSync`.
196
+ */
199
197
  datasyncSync() {
200
198
  return this.syncSync();
201
199
  }
202
- async chown(uid, gid) {
203
- throw new ApiError(ErrorCode.ENOTSUP);
204
- }
205
- chownSync(uid, gid) {
206
- throw new ApiError(ErrorCode.ENOTSUP);
207
- }
208
- async chmod(mode) {
209
- throw new ApiError(ErrorCode.ENOTSUP);
210
- }
211
- chmodSync(mode) {
212
- throw new ApiError(ErrorCode.ENOTSUP);
213
- }
214
- async utimes(atime, mtime) {
215
- throw new ApiError(ErrorCode.ENOTSUP);
216
- }
217
- utimesSync(atime, mtime) {
218
- throw new ApiError(ErrorCode.ENOTSUP);
219
- }
220
200
  }
221
201
  /**
222
202
  * An implementation of the File interface that operates on a file that is
@@ -227,59 +207,54 @@ export class BaseFile {
227
207
  * extend this class and implement those two methods.
228
208
  * @todo 'close' lever that disables functionality once closed.
229
209
  */
230
- export class PreloadFile extends BaseFile {
210
+ export class PreloadFile extends File {
231
211
  /**
232
212
  * Creates a file with the given path and, optionally, the given contents. Note
233
213
  * that, if contents is specified, it will be mutated by the file!
234
- * @param _fs The file system that created the file.
235
- * @param _path
236
214
  * @param _mode The mode that the file was opened using.
237
215
  * Dictates permissions and where the file pointer starts.
238
- * @param _stat The stats object for the given file.
216
+ * @param stats The stats object for the given file.
239
217
  * PreloadFile will mutate this object. Note that this object must contain
240
218
  * the appropriate mode that the file was opened as.
241
- * @param contents A buffer containing the entire
219
+ * @param buffer A buffer containing the entire
242
220
  * contents of the file. PreloadFile will mutate this buffer. If not
243
221
  * specified, we assume it is a new file.
244
222
  */
245
- constructor(_fs, _path, _flag, _stat, contents) {
246
- super();
247
- this._pos = 0;
248
- this._dirty = false;
249
- this._fs = _fs;
250
- this._path = _path;
251
- this._flag = _flag;
252
- this._stat = _stat;
253
- this._buffer = contents ? contents : new Uint8Array(0);
254
- // Note: This invariant is *not* maintained once the file starts getting
255
- // modified.
256
- // Note: Only actually matters if file is readable, as writeable modes may
257
- // truncate/append to file.
258
- if (this._stat.size !== this._buffer.length && this._flag.isReadable()) {
259
- throw new Error(`Invalid buffer: Uint8Array is ${this._buffer.length} long, yet Stats object specifies that file is ${this._stat.size} long.`);
260
- }
261
- }
223
+ constructor(
262
224
  /**
263
- * NONSTANDARD: Get the underlying buffer for this file. !!DO NOT MUTATE!! Will mess up dirty tracking.
225
+ * The file system that created the file.
264
226
  */
265
- getBuffer() {
266
- return this._buffer;
267
- }
227
+ fs,
268
228
  /**
269
- * NONSTANDARD: Get underlying stats for this file. !!DO NOT MUTATE!!
229
+ * Path to the file
270
230
  */
271
- getStats() {
272
- return this._stat;
273
- }
274
- getFlag() {
275
- return this._flag;
231
+ path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, { maxByteLength: size_max }))) {
232
+ super();
233
+ this.fs = fs;
234
+ this.path = path;
235
+ this.flag = flag;
236
+ this.stats = stats;
237
+ this._buffer = _buffer;
238
+ this._position = 0;
239
+ this._dirty = false;
240
+ /*
241
+ Note:
242
+ This invariant is *not* maintained once the file starts getting modified.
243
+ It only actually matters if file is readable, as writeable modes may truncate/append to file.
244
+ */
245
+ if (this.stats.size == _buffer.byteLength) {
246
+ return;
247
+ }
248
+ if (this.flag.isReadable()) {
249
+ throw new Error(`Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`);
250
+ }
251
+ this._dirty = true;
276
252
  }
277
253
  /**
278
- * Get the path to this file.
279
- * @return [String] The path to the file.
254
+ * Get the underlying buffer for this file. Mutating not recommended and will mess up dirty tracking.
280
255
  */
281
- getPath() {
282
- return this._path;
256
+ get buffer() {
257
+ return this._buffer;
283
258
  }
284
259
  /**
285
260
  * Get the current file position.
@@ -288,103 +263,66 @@ export class PreloadFile extends BaseFile {
288
263
  * > On Linux, positional writes don't work when the file is opened in append
289
264
  * mode. The kernel ignores the position argument and always appends the data
290
265
  * to the end of the file.
291
- * @return [Number] The current file position.
266
+ * @return The current file position.
292
267
  */
293
- getPos() {
294
- if (this._flag.isAppendable()) {
295
- return this._stat.size;
268
+ get position() {
269
+ if (this.flag.isAppendable()) {
270
+ return this.stats.size;
296
271
  }
297
- return this._pos;
298
- }
299
- /**
300
- * Advance the current file position by the indicated number of positions.
301
- * @param [Number] delta
302
- */
303
- advancePos(delta) {
304
- return (this._pos += delta);
272
+ return this._position;
305
273
  }
306
274
  /**
307
275
  * Set the file position.
308
- * @param [Number] newPos
309
- */
310
- setPos(newPos) {
311
- return (this._pos = newPos);
312
- }
313
- /**
314
- * **Core**: Asynchronous sync. Must be implemented by subclasses of this
315
- * class.
316
- * @param [Function(ZenFS.ApiError)] cb
276
+ * @param newPos new position
317
277
  */
318
- async sync() {
319
- this.syncSync();
320
- }
321
- /**
322
- * **Core**: Synchronous sync.
323
- */
324
- syncSync() {
325
- throw new ApiError(ErrorCode.ENOTSUP);
326
- }
327
- /**
328
- * **Core**: Asynchronous close. Must be implemented by subclasses of this
329
- * class.
330
- * @param [Function(ZenFS.ApiError)] cb
331
- */
332
- async close() {
333
- this.closeSync();
334
- }
335
- /**
336
- * **Core**: Synchronous close.
337
- */
338
- closeSync() {
339
- throw new ApiError(ErrorCode.ENOTSUP);
278
+ set position(newPos) {
279
+ this._position = newPos;
340
280
  }
341
281
  /**
342
282
  * Asynchronous `stat`.
343
- * @param [Function(ZenFS.ApiError, ZenFS.node.fs.Stats)] cb
344
283
  */
345
284
  async stat() {
346
- return Stats.clone(this._stat);
285
+ return Stats.clone(this.stats);
347
286
  }
348
287
  /**
349
288
  * Synchronous `stat`.
350
289
  */
351
290
  statSync() {
352
- return Stats.clone(this._stat);
291
+ return Stats.clone(this.stats);
353
292
  }
354
293
  /**
355
294
  * Asynchronous truncate.
356
- * @param [Number] len
357
- * @param [Function(ZenFS.ApiError)] cb
295
+ * @param len
358
296
  */
359
297
  truncate(len) {
360
298
  this.truncateSync(len);
361
- if (this._flag.isSynchronous() && !getMount('/').metadata.synchronous) {
299
+ if (this.flag.isSynchronous() && !this.fs.metadata.synchronous) {
362
300
  return this.sync();
363
301
  }
364
302
  }
365
303
  /**
366
304
  * Synchronous truncate.
367
- * @param [Number] len
305
+ * @param len
368
306
  */
369
307
  truncateSync(len) {
370
308
  this._dirty = true;
371
- if (!this._flag.isWriteable()) {
309
+ if (!this.flag.isWriteable()) {
372
310
  throw new ApiError(ErrorCode.EPERM, 'File not opened with a writeable mode.');
373
311
  }
374
- this._stat.mtimeMs = Date.now();
312
+ this.stats.mtimeMs = Date.now();
375
313
  if (len > this._buffer.length) {
376
314
  const buf = new Uint8Array(len - this._buffer.length);
377
- // Write will set @_stat.size for us.
315
+ // Write will set stats.size for us.
378
316
  this.writeSync(buf, 0, buf.length, this._buffer.length);
379
- if (this._flag.isSynchronous() && getMount('/').metadata.synchronous) {
317
+ if (this.flag.isSynchronous() && this.fs.metadata.synchronous) {
380
318
  this.syncSync();
381
319
  }
382
320
  return;
383
321
  }
384
- this._stat.size = len;
322
+ this.stats.size = len;
385
323
  // Truncate buffer to 'len'.
386
324
  this._buffer = this._buffer.subarray(0, len);
387
- if (this._flag.isSynchronous() && getMount('/').metadata.synchronous) {
325
+ if (this.flag.isSynchronous() && this.fs.metadata.synchronous) {
388
326
  this.syncSync();
389
327
  }
390
328
  }
@@ -392,142 +330,155 @@ export class PreloadFile extends BaseFile {
392
330
  * Write buffer to the file.
393
331
  * Note that it is unsafe to use fs.write multiple times on the same file
394
332
  * without waiting for the callback.
395
- * @param [ZenFS.node.Uint8Array] buffer Uint8Array containing the data to write to
333
+ * @param buffer Uint8Array containing the data to write to
396
334
  * the file.
397
- * @param [Number] offset Offset in the buffer to start reading data from.
398
- * @param [Number] length The amount of bytes to write to the file.
399
- * @param [Number] position Offset from the beginning of the file where this
335
+ * @param offset Offset in the buffer to start reading data from.
336
+ * @param length The amount of bytes to write to the file.
337
+ * @param position Offset from the beginning of the file where this
400
338
  * data should be written. If position is null, the data will be written at
401
339
  * the current position.
402
- * @param [Function(ZenFS.ApiError, Number, ZenFS.node.Uint8Array)]
403
- * cb The number specifies the number of bytes written into the file.
404
340
  */
405
- async write(buffer, offset, length, position) {
341
+ async write(buffer, offset = 0, length = this.stats.size, position = 0) {
406
342
  return this.writeSync(buffer, offset, length, position);
407
343
  }
408
344
  /**
409
345
  * Write buffer to the file.
410
346
  * Note that it is unsafe to use fs.writeSync multiple times on the same file
411
347
  * without waiting for the callback.
412
- * @param [ZenFS.node.Uint8Array] buffer Uint8Array containing the data to write to
348
+ * @param buffer Uint8Array containing the data to write to
413
349
  * the file.
414
- * @param [Number] offset Offset in the buffer to start reading data from.
415
- * @param [Number] length The amount of bytes to write to the file.
416
- * @param [Number] position Offset from the beginning of the file where this
350
+ * @param offset Offset in the buffer to start reading data from.
351
+ * @param length The amount of bytes to write to the file.
352
+ * @param position Offset from the beginning of the file where this
417
353
  * data should be written. If position is null, the data will be written at
418
354
  * the current position.
419
- * @return [Number]
355
+ * @returns bytes written
420
356
  */
421
- writeSync(buffer, offset, length, position) {
357
+ writeSync(buffer, offset = 0, length = this.stats.size, position = 0) {
422
358
  this._dirty = true;
423
- if (position === undefined || position === null) {
424
- position = this.getPos();
425
- }
426
- if (!this._flag.isWriteable()) {
359
+ position ?? (position = this.position);
360
+ if (!this.flag.isWriteable()) {
427
361
  throw new ApiError(ErrorCode.EPERM, 'File not opened with a writeable mode.');
428
362
  }
429
363
  const endFp = position + length;
430
- if (endFp > this._stat.size) {
431
- this._stat.size = endFp;
432
- if (endFp > this._buffer.length) {
433
- // Extend the buffer!
434
- const newBuffer = new Uint8Array(endFp);
435
- newBuffer.set(this._buffer);
436
- this._buffer = newBuffer;
364
+ if (endFp > this.stats.size) {
365
+ this.stats.size = endFp;
366
+ if (endFp > this._buffer.byteLength) {
367
+ if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength <= endFp) {
368
+ this._buffer.buffer.resize(endFp);
369
+ }
370
+ else {
371
+ // Extend the buffer!
372
+ const newBuffer = new Uint8Array(new ArrayBuffer(endFp, { maxByteLength: size_max }));
373
+ newBuffer.set(this._buffer);
374
+ this._buffer = newBuffer;
375
+ }
437
376
  }
438
377
  }
439
378
  this._buffer.set(buffer.slice(offset, offset + length), position);
440
- const len = this._buffer.length;
441
- this._stat.mtimeMs = Date.now();
442
- if (this._flag.isSynchronous()) {
379
+ const len = this._buffer.byteOffset;
380
+ this.stats.mtimeMs = Date.now();
381
+ if (this.flag.isSynchronous()) {
443
382
  this.syncSync();
444
383
  return len;
445
384
  }
446
- this.setPos(position + len);
385
+ this.position = position + len;
447
386
  return len;
448
387
  }
449
388
  /**
450
389
  * Read data from the file.
451
- * @param [ZenFS.node.Uint8Array] buffer The buffer that the data will be
390
+ * @param buffer The buffer that the data will be
452
391
  * written to.
453
- * @param [Number] offset The offset within the buffer where writing will
392
+ * @param offset The offset within the buffer where writing will
454
393
  * start.
455
- * @param [Number] length An integer specifying the number of bytes to read.
456
- * @param [Number] position An integer specifying where to begin reading from
394
+ * @param length An integer specifying the number of bytes to read.
395
+ * @param position An integer specifying where to begin reading from
457
396
  * in the file. If position is null, data will be read from the current file
458
397
  * position.
459
- * @param [Function(ZenFS.ApiError, Number, ZenFS.node.Uint8Array)] cb The
460
- * number is the number of bytes read
461
398
  */
462
- async read(buffer, offset, length, position) {
399
+ async read(buffer, offset = 0, length = this.stats.size, position = 0) {
463
400
  return { bytesRead: this.readSync(buffer, offset, length, position), buffer };
464
401
  }
465
402
  /**
466
403
  * Read data from the file.
467
- * @param [ZenFS.node.Uint8Array] buffer The buffer that the data will be
404
+ * @param buffer The buffer that the data will be
468
405
  * written to.
469
- * @param [Number] offset The offset within the buffer where writing will
470
- * start.
471
- * @param [Number] length An integer specifying the number of bytes to read.
472
- * @param [Number] position An integer specifying where to begin reading from
406
+ * @param offset The offset within the buffer where writing will start.
407
+ * @param length An integer specifying the number of bytes to read.
408
+ * @param position An integer specifying where to begin reading from
473
409
  * in the file. If position is null, data will be read from the current file
474
410
  * position.
475
- * @return [Number]
411
+ * @returns number of bytes written
476
412
  */
477
- readSync(buffer, offset, length, position) {
478
- if (!this._flag.isReadable()) {
413
+ readSync(buffer, offset = 0, length = this.stats.size, position = 0) {
414
+ if (!this.flag.isReadable()) {
479
415
  throw new ApiError(ErrorCode.EPERM, 'File not opened with a readable mode.');
480
416
  }
481
- if (position === undefined || position === null) {
482
- position = this.getPos();
417
+ position ?? (position = this.position);
418
+ let end = position + length;
419
+ if (end > this.stats.size) {
420
+ end = position + Math.max(this.stats.size - position, 0);
483
421
  }
484
- const endRead = position + length;
485
- if (endRead > this._stat.size) {
486
- length = this._stat.size - position;
422
+ this.stats.atimeMs = Date.now();
423
+ this._position = end;
424
+ const bytesRead = end - position;
425
+ if (bytesRead == 0) {
426
+ // No copy/read. Return immediatly for better performance
427
+ return bytesRead;
487
428
  }
488
- this._buffer.set(buffer.slice(offset, offset + length), position);
489
- this._stat.atimeMs = Date.now();
490
- this._pos = position + length;
491
- return this._buffer.length;
429
+ buffer.set(this._buffer.slice(position, end), offset);
430
+ return bytesRead;
492
431
  }
493
432
  /**
494
433
  * Asynchronous `fchmod`.
495
- * @param [Number|String] mode
434
+ * @param mode the mode
496
435
  */
497
436
  async chmod(mode) {
498
437
  this.chmodSync(mode);
499
438
  }
500
439
  /**
501
440
  * Synchronous `fchmod`.
502
- * @param [Number] mode
441
+ * @param mode
503
442
  */
504
443
  chmodSync(mode) {
505
- if (!this._fs.metadata.supportsProperties) {
444
+ if (!this.fs.metadata.supportsProperties) {
506
445
  throw new ApiError(ErrorCode.ENOTSUP);
507
446
  }
508
447
  this._dirty = true;
509
- this._stat.chmod(mode);
448
+ this.stats.chmod(mode);
510
449
  this.syncSync();
511
450
  }
512
451
  /**
513
452
  * Asynchronous `fchown`.
514
- * @param [Number] uid
515
- * @param [Number] gid
453
+ * @param uid
454
+ * @param gid
516
455
  */
517
456
  async chown(uid, gid) {
518
457
  this.chownSync(uid, gid);
519
458
  }
520
459
  /**
521
460
  * Synchronous `fchown`.
522
- * @param [Number] uid
523
- * @param [Number] gid
461
+ * @param uid
462
+ * @param gid
524
463
  */
525
464
  chownSync(uid, gid) {
526
- if (!this._fs.metadata.supportsProperties) {
465
+ if (!this.fs.metadata.supportsProperties) {
466
+ throw new ApiError(ErrorCode.ENOTSUP);
467
+ }
468
+ this._dirty = true;
469
+ this.stats.chown(uid, gid);
470
+ this.syncSync();
471
+ }
472
+ async utimes(atime, mtime) {
473
+ this.utimesSync(atime, mtime);
474
+ }
475
+ utimesSync(atime, mtime) {
476
+ if (!this.fs.metadata.supportsProperties) {
527
477
  throw new ApiError(ErrorCode.ENOTSUP);
528
478
  }
529
479
  this._dirty = true;
530
- this._stat.chown(uid, gid);
480
+ this.stats.atime = atime;
481
+ this.stats.mtime = mtime;
531
482
  this.syncSync();
532
483
  }
533
484
  isDirty() {
@@ -539,10 +490,42 @@ export class PreloadFile extends BaseFile {
539
490
  resetDirty() {
540
491
  this._dirty = false;
541
492
  }
493
+ _setType(type) {
494
+ this._dirty = true;
495
+ this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
496
+ return this.sync();
497
+ }
498
+ _setTypeSync(type) {
499
+ this._dirty = true;
500
+ this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
501
+ this.syncSync();
502
+ }
503
+ }
504
+ /**
505
+ * For synchronous file systems
506
+ */
507
+ export class SyncFile extends PreloadFile {
508
+ constructor(_fs, _path, _flag, _stat, contents) {
509
+ super(_fs, _path, _flag, _stat, contents);
510
+ }
511
+ async sync() {
512
+ this.syncSync();
513
+ }
514
+ syncSync() {
515
+ if (this.isDirty()) {
516
+ this.fs.syncSync(this.path, this._buffer, this.stats);
517
+ this.resetDirty();
518
+ }
519
+ }
520
+ async close() {
521
+ this.closeSync();
522
+ }
523
+ closeSync() {
524
+ this.syncSync();
525
+ }
542
526
  }
543
527
  /**
544
- * File class for the InMemory and XHR file systems.
545
- * Doesn't sync to anything, so it works nicely for memory-only files.
528
+ * For the filesystems which do not sync to anything..
546
529
  */
547
530
  export class NoSyncFile extends PreloadFile {
548
531
  constructor(_fs, _path, _flag, _stat, contents) {
@@ -550,7 +533,6 @@ export class NoSyncFile extends PreloadFile {
550
533
  }
551
534
  /**
552
535
  * Asynchronous sync. Doesn't do anything, simply calls the cb.
553
- * @param [Function(ZenFS.ApiError)] cb
554
536
  */
555
537
  async sync() {
556
538
  return;
@@ -563,7 +545,6 @@ export class NoSyncFile extends PreloadFile {
563
545
  }
564
546
  /**
565
547
  * Asynchronous close. Doesn't do anything, simply calls the cb.
566
- * @param [Function(ZenFS.ApiError)] cb
567
548
  */
568
549
  async close() {
569
550
  return;