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