@zenfs/core 1.2.10 → 1.3.1

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 (56) hide show
  1. package/dist/backends/fetch.js +1 -1
  2. package/dist/backends/memory.d.ts +1 -2
  3. package/dist/backends/overlay.js +2 -3
  4. package/dist/backends/port/fs.d.ts +2 -15
  5. package/dist/backends/store/fs.d.ts +17 -28
  6. package/dist/backends/store/fs.js +169 -191
  7. package/dist/backends/store/simple.d.ts +20 -21
  8. package/dist/backends/store/simple.js +24 -24
  9. package/dist/backends/store/store.d.ts +22 -23
  10. package/dist/backends/store/store.js +6 -6
  11. package/dist/config.d.ts +8 -0
  12. package/dist/config.js +20 -20
  13. package/dist/devices.d.ts +2 -3
  14. package/dist/emulation/cache.d.ts +40 -31
  15. package/dist/emulation/cache.js +62 -53
  16. package/dist/emulation/index.d.ts +1 -1
  17. package/dist/emulation/index.js +1 -1
  18. package/dist/emulation/promises.js +18 -17
  19. package/dist/emulation/shared.d.ts +6 -0
  20. package/dist/emulation/shared.js +13 -1
  21. package/dist/emulation/sync.js +14 -13
  22. package/dist/file.d.ts +3 -15
  23. package/dist/file.js +6 -18
  24. package/dist/index.d.ts +7 -0
  25. package/dist/index.js +1 -0
  26. package/dist/inode.d.ts +5 -13
  27. package/dist/inode.js +38 -29
  28. package/dist/mixins/async.d.ts +15 -10
  29. package/dist/mixins/async.js +3 -1
  30. package/dist/utils.d.ts +5 -7
  31. package/dist/utils.js +11 -20
  32. package/package.json +1 -1
  33. package/src/backends/fetch.ts +1 -1
  34. package/src/backends/memory.ts +1 -2
  35. package/src/backends/overlay.ts +2 -2
  36. package/src/backends/store/fs.ts +187 -220
  37. package/src/backends/store/simple.ts +36 -37
  38. package/src/backends/store/store.ts +25 -26
  39. package/src/config.ts +31 -23
  40. package/src/devices.ts +2 -3
  41. package/src/emulation/cache.ts +68 -60
  42. package/src/emulation/index.ts +1 -1
  43. package/src/emulation/promises.ts +20 -19
  44. package/src/emulation/shared.ts +13 -1
  45. package/src/emulation/sync.ts +14 -13
  46. package/src/file.ts +8 -20
  47. package/src/index.ts +10 -0
  48. package/src/inode.ts +27 -31
  49. package/src/mixins/async.ts +27 -24
  50. package/src/utils.ts +11 -23
  51. package/tests/fs/dir.test.ts +21 -31
  52. package/tests/fs/directory.test.ts +6 -4
  53. package/tests/fs/links.test.ts +9 -2
  54. package/tests/fs/permissions.test.ts +2 -2
  55. package/tests/fs/times.test.ts +22 -11
  56. package/tests/setup/cow+fetch.ts +4 -2
@@ -435,7 +435,7 @@ export async function unlink(path) {
435
435
  path = normalizePath(path);
436
436
  const { fs, path: resolved } = resolveMount(path);
437
437
  try {
438
- if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
438
+ if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
439
439
  throw ErrnoError.With('EACCES', resolved, 'unlink');
440
440
  }
441
441
  await fs.unlink(resolved);
@@ -583,7 +583,7 @@ export async function rmdir(path) {
583
583
  path = await realpath(path);
584
584
  const { fs, path: resolved } = resolveMount(path);
585
585
  try {
586
- const stats = await (cache.getStats(path) || fs.stat(resolved));
586
+ const stats = await (cache.stats.get(path) || fs.stat(resolved));
587
587
  if (!stats) {
588
588
  throw ErrnoError.With('ENOENT', path, 'rmdir');
589
589
  }
@@ -642,8 +642,8 @@ export async function readdir(path, options) {
642
642
  throw fixError(e, { [resolved]: path });
643
643
  };
644
644
  const { fs, path: resolved } = resolveMount(path);
645
- const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
646
- cache.setStats(path, _stats);
645
+ const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
646
+ cache.stats.set(path, _stats);
647
647
  const stats = await _stats;
648
648
  if (!stats) {
649
649
  throw ErrnoError.With('ENOENT', path, 'readdir');
@@ -659,8 +659,8 @@ export async function readdir(path, options) {
659
659
  const addEntry = async (entry) => {
660
660
  let entryStats;
661
661
  if (options?.recursive || options?.withFileTypes) {
662
- const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
663
- cache.setStats(join(path, entry), _entryStats);
662
+ const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
663
+ cache.stats.set(join(path, entry), _entryStats);
664
664
  entryStats = await _entryStats;
665
665
  }
666
666
  if (options?.withFileTypes) {
@@ -690,7 +690,7 @@ export async function readdir(path, options) {
690
690
  };
691
691
  await Promise.all(entries.map(addEntry));
692
692
  if (!options?._isIndirect) {
693
- cache.clearStats();
693
+ cache.stats.clear();
694
694
  }
695
695
  return values;
696
696
  }
@@ -881,14 +881,16 @@ lutimes;
881
881
  export async function realpath(path, options) {
882
882
  path = normalizePath(path);
883
883
  const { base, dir } = parse(path);
884
- const lpath = join(dir == '/' ? '/' : await realpath(dir), base);
884
+ const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
885
885
  const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
886
886
  try {
887
- const stats = await fs.stat(resolvedPath);
888
- if (!stats.isSymbolicLink()) {
887
+ const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
888
+ cache.stats.set(lpath, _stats);
889
+ if (!(await _stats).isSymbolicLink()) {
889
890
  return lpath;
890
891
  }
891
- return await realpath(mountPoint + (await readlink(lpath)));
892
+ const target = mountPoint + (await readlink(lpath));
893
+ return await (cache.paths.get(target) || realpath(target));
892
894
  }
893
895
  catch (e) {
894
896
  if (e.code == 'ENOENT') {
@@ -943,17 +945,16 @@ access;
943
945
  */
944
946
  export async function rm(path, options) {
945
947
  path = normalizePath(path);
946
- const _stats = cache.getStats(path) ||
948
+ const stats = await (cache.stats.get(path) ||
947
949
  stat(path).catch((error) => {
948
950
  if (error.code == 'ENOENT' && options?.force)
949
951
  return undefined;
950
952
  throw error;
951
- });
952
- cache.setStats(path, _stats);
953
- const stats = await _stats;
953
+ }));
954
954
  if (!stats) {
955
955
  return;
956
956
  }
957
+ cache.stats.setSync(path, stats);
957
958
  switch (stats.mode & constants.S_IFMT) {
958
959
  case constants.S_IFDIR:
959
960
  if (options?.recursive) {
@@ -972,11 +973,11 @@ export async function rm(path, options) {
972
973
  case constants.S_IFIFO:
973
974
  case constants.S_IFSOCK:
974
975
  default:
975
- cache.clearStats();
976
+ cache.stats.clear();
976
977
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
977
978
  }
978
979
  if (!options?._isIndirect) {
979
- cache.clearStats();
980
+ cache.stats.clear();
980
981
  }
981
982
  }
982
983
  rm;
@@ -28,6 +28,12 @@ export declare function resolveMount(path: string): {
28
28
  path: string;
29
29
  mountPoint: string;
30
30
  };
31
+ /**
32
+ * Wait for all file systems to be ready and synced.
33
+ * May be removed at some point.
34
+ * @experimental @internal
35
+ */
36
+ export declare function _synced(): Promise<void>;
31
37
  /**
32
38
  * Reverse maps the paths in text from the mounted FileSystem to the global path
33
39
  * @hidden
@@ -69,6 +69,15 @@ export function resolveMount(path) {
69
69
  }
70
70
  throw new ErrnoError(Errno.EIO, 'ZenFS not initialized with a file system');
71
71
  }
72
+ /**
73
+ * Wait for all file systems to be ready and synced.
74
+ * May be removed at some point.
75
+ * @experimental @internal
76
+ */
77
+ export async function _synced() {
78
+ await Promise.all([...mounts.values()].map(m => m.ready()));
79
+ return;
80
+ }
72
81
  /**
73
82
  * Reverse maps the paths in text from the mounted FileSystem to the global path
74
83
  * @hidden
@@ -87,7 +96,10 @@ export function fixError(e, paths) {
87
96
  if (typeof e.stack == 'string') {
88
97
  e.stack = fixPaths(e.stack, paths);
89
98
  }
90
- e.message = fixPaths(e.message, paths);
99
+ try {
100
+ e.message = fixPaths(e.message, paths);
101
+ }
102
+ catch { }
91
103
  return e;
92
104
  }
93
105
  /**
@@ -147,7 +147,7 @@ export function unlinkSync(path) {
147
147
  path = normalizePath(path);
148
148
  const { fs, path: resolved } = resolveMount(path);
149
149
  try {
150
- if (config.checkAccess && !(cache.getStatsSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
150
+ if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
151
151
  throw ErrnoError.With('EACCES', resolved, 'unlink');
152
152
  }
153
153
  fs.unlinkSync(resolved);
@@ -393,7 +393,7 @@ export function rmdirSync(path) {
393
393
  path = normalizePath(path);
394
394
  const { fs, path: resolved } = resolveMount(realpathSync(path));
395
395
  try {
396
- const stats = cache.getStatsSync(path) || fs.statSync(resolved);
396
+ const stats = cache.stats.getSync(path) || fs.statSync(resolved);
397
397
  if (!stats.isDirectory()) {
398
398
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
399
399
  }
@@ -446,8 +446,8 @@ export function readdirSync(path, options) {
446
446
  const { fs, path: resolved } = resolveMount(realpathSync(path));
447
447
  let entries;
448
448
  try {
449
- const stats = cache.getStatsSync(path) || fs.statSync(resolved);
450
- cache.setStatsSync(path, stats);
449
+ const stats = cache.stats.getSync(path) || fs.statSync(resolved);
450
+ cache.stats.setSync(path, stats);
451
451
  if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
452
452
  throw ErrnoError.With('EACCES', resolved, 'readdir');
453
453
  }
@@ -462,8 +462,8 @@ export function readdirSync(path, options) {
462
462
  // Iterate over entries and handle recursive case if needed
463
463
  const values = [];
464
464
  for (const entry of entries) {
465
- const entryStat = cache.getStatsSync(join(path, entry)) || fs.statSync(join(resolved, entry));
466
- cache.setStatsSync(join(path, entry), entryStat);
465
+ const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry));
466
+ cache.stats.setSync(join(path, entry), entryStat);
467
467
  if (options?.withFileTypes) {
468
468
  values.push(new Dirent(entry, entryStat));
469
469
  }
@@ -489,7 +489,7 @@ export function readdirSync(path, options) {
489
489
  }
490
490
  }
491
491
  if (!options?._isIndirect) {
492
- cache.clearStatsSync();
492
+ cache.stats.clearSync();
493
493
  }
494
494
  return values;
495
495
  }
@@ -593,14 +593,15 @@ lutimesSync;
593
593
  export function realpathSync(path, options) {
594
594
  path = normalizePath(path);
595
595
  const { base, dir } = parse(path);
596
- const lpath = join(dir == '/' ? '/' : realpathSync(dir), base);
596
+ const lpath = join(dir == '/' ? '/' : cache.paths.getSync(dir) || realpathSync(dir), base);
597
597
  const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
598
598
  try {
599
599
  const stats = fs.statSync(resolvedPath);
600
600
  if (!stats.isSymbolicLink()) {
601
601
  return lpath;
602
602
  }
603
- return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
603
+ const target = mountPoint + readlinkSync(lpath, options).toString();
604
+ return cache.paths.getSync(target) || realpathSync(target);
604
605
  }
605
606
  catch (e) {
606
607
  if (e.code == 'ENOENT') {
@@ -626,7 +627,7 @@ export function rmSync(path, options) {
626
627
  path = normalizePath(path);
627
628
  let stats;
628
629
  try {
629
- stats = cache.getStatsSync(path) || statSync(path);
630
+ stats = cache.stats.getSync(path) || statSync(path);
630
631
  }
631
632
  catch (error) {
632
633
  if (error.code != 'ENOENT' || !options?.force)
@@ -635,7 +636,7 @@ export function rmSync(path, options) {
635
636
  if (!stats) {
636
637
  return;
637
638
  }
638
- cache.setStatsSync(path, stats);
639
+ cache.stats.setSync(path, stats);
639
640
  switch (stats.mode & constants.S_IFMT) {
640
641
  case constants.S_IFDIR:
641
642
  if (options?.recursive) {
@@ -654,11 +655,11 @@ export function rmSync(path, options) {
654
655
  case constants.S_IFIFO:
655
656
  case constants.S_IFSOCK:
656
657
  default:
657
- cache.clearStatsSync();
658
+ cache.stats.clearSync();
658
659
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
659
660
  }
660
661
  if (!options?._isIndirect) {
661
- cache.clearStatsSync();
662
+ cache.stats.clearSync();
662
663
  }
663
664
  }
664
665
  rmSync;
package/dist/file.d.ts CHANGED
@@ -38,7 +38,7 @@ export declare function isTruncating(flag: string): boolean;
38
38
  export declare function isAppendable(flag: string): boolean;
39
39
  export declare function isSynchronous(flag: string): boolean;
40
40
  export declare function isExclusive(flag: string): boolean;
41
- export declare abstract class File {
41
+ export declare abstract class File<FS extends FileSystem = FileSystem> {
42
42
  /**
43
43
  * @internal
44
44
  * The file system that created the file
@@ -138,12 +138,7 @@ export declare abstract class File {
138
138
  * An implementation of `File` that operates completely in-memory.
139
139
  * `PreloadFile`s are backed by a `Uint8Array`.
140
140
  */
141
- export declare class PreloadFile<FS extends FileSystem> extends File {
142
- /**
143
- * The file system that created the file.
144
- * @internal
145
- */
146
- fs: FS;
141
+ export declare class PreloadFile<FS extends FileSystem> extends File<FS> {
147
142
  readonly flag: string;
148
143
  readonly stats: Stats;
149
144
  /**
@@ -166,12 +161,7 @@ export declare class PreloadFile<FS extends FileSystem> extends File {
166
161
  * Creates a file with `path` and, optionally, the given contents.
167
162
  * Note that, if contents is specified, it will be mutated by the file.
168
163
  */
169
- constructor(
170
- /**
171
- * The file system that created the file.
172
- * @internal
173
- */
174
- fs: FS, path: string, flag: string, stats: Stats,
164
+ constructor(fs: FS, path: string, flag: string, stats: Stats,
175
165
  /**
176
166
  * A buffer containing the entire contents of the file.
177
167
  */
@@ -255,8 +245,6 @@ export declare class PreloadFile<FS extends FileSystem> extends File {
255
245
  utimesSync(atime: Date, mtime: Date): void;
256
246
  _setType(type: FileType): Promise<void>;
257
247
  _setTypeSync(type: FileType): void;
258
- [Symbol.asyncDispose](): Promise<void>;
259
- [Symbol.dispose](): void;
260
248
  }
261
249
  /**
262
250
  * For the file systems which do not sync to anything.
package/dist/file.js CHANGED
@@ -114,11 +114,11 @@ export class File {
114
114
  this.fs = fs;
115
115
  this.path = path;
116
116
  }
117
- [Symbol.asyncDispose]() {
118
- return this.close();
117
+ async [Symbol.asyncDispose]() {
118
+ await this.close();
119
119
  }
120
120
  [Symbol.dispose]() {
121
- return this.closeSync();
121
+ this.closeSync();
122
122
  }
123
123
  /**
124
124
  * Default implementation maps to `sync`.
@@ -142,18 +142,12 @@ export class PreloadFile extends File {
142
142
  * Creates a file with `path` and, optionally, the given contents.
143
143
  * Note that, if contents is specified, it will be mutated by the file.
144
144
  */
145
- constructor(
146
- /**
147
- * The file system that created the file.
148
- * @internal
149
- */
150
- fs, path, flag, stats,
145
+ constructor(fs, path, flag, stats,
151
146
  /**
152
147
  * A buffer containing the entire contents of the file.
153
148
  */
154
149
  _buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
155
150
  super(fs, path);
156
- this.fs = fs;
157
151
  this.flag = flag;
158
152
  this.stats = stats;
159
153
  this._buffer = _buffer;
@@ -280,12 +274,12 @@ export class PreloadFile extends File {
280
274
  if (length > this._buffer.length) {
281
275
  const data = new Uint8Array(length - this._buffer.length);
282
276
  // Write will set stats.size and handle syncing.
283
- this.writeSync(data, 0, data.length, this._buffer.length);
277
+ this._write(data, 0, data.length, this._buffer.length);
284
278
  return;
285
279
  }
286
280
  this.stats.size = length;
287
281
  // Truncate.
288
- this._buffer = this._buffer.slice(0, length);
282
+ this._buffer = length ? this._buffer.slice(0, length) : new Uint8Array();
289
283
  }
290
284
  async truncate(length) {
291
285
  this._truncate(length);
@@ -484,12 +478,6 @@ export class PreloadFile extends File {
484
478
  this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
485
479
  this.syncSync();
486
480
  }
487
- async [Symbol.asyncDispose]() {
488
- await this.close();
489
- }
490
- [Symbol.dispose]() {
491
- this.closeSync();
492
- }
493
481
  }
494
482
  /**
495
483
  * For the file systems which do not sync to anything.
package/dist/index.d.ts CHANGED
@@ -22,3 +22,10 @@ export * from './emulation/index.js';
22
22
  import * as fs from './emulation/index.js';
23
23
  export { fs };
24
24
  export default fs;
25
+ declare global {
26
+ /**
27
+ * Global FS emulation. Do not use unless absolutely needed.
28
+ * @hidden
29
+ */
30
+ var __zenfs__: typeof fs;
31
+ }
package/dist/index.js CHANGED
@@ -22,3 +22,4 @@ export * from './emulation/index.js';
22
22
  import * as fs from './emulation/index.js';
23
23
  export { fs };
24
24
  export default fs;
25
+ globalThis.__zenfs__ = fs;
package/dist/inode.d.ts CHANGED
@@ -1,26 +1,17 @@
1
1
  import { Stats, type StatsLike } from './stats.js';
2
- /**
3
- * Alias for an ino.
4
- * This will be helpful if in the future inode numbers/IDs are changed to strings or numbers.
5
- */
6
- export type Ino = bigint;
7
2
  /**
8
3
  * Room inode
9
4
  * @hidden
10
5
  */
11
6
  export declare const rootIno = 0n;
12
- /**
13
- * Generate a random ino
14
- * @internal
15
- */
16
- export declare function randomIno(): Ino;
17
7
  /**
18
8
  * Generic inode definition that can easily be serialized.
9
+ * @internal
10
+ * @todo [BREAKING]
19
11
  */
20
12
  export declare class Inode implements StatsLike {
21
- get data(): Uint8Array;
22
- constructor(buffer?: ArrayBufferLike);
23
- ino: Ino;
13
+ constructor(buffer?: ArrayBufferLike | ArrayBufferView);
14
+ data: bigint;
24
15
  size: number;
25
16
  mode: number;
26
17
  nlink: number;
@@ -30,6 +21,7 @@ export declare class Inode implements StatsLike {
30
21
  birthtimeMs: number;
31
22
  mtimeMs: number;
32
23
  ctimeMs: number;
24
+ ino: bigint;
33
25
  /**
34
26
  * Handy function that converts the Inode to a Node Stats object.
35
27
  */
package/dist/inode.js CHANGED
@@ -36,38 +36,28 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
36
36
  if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
37
37
  return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
38
38
  };
39
- import { deserialize, serialize, sizeof, struct, types as t } from 'utilium';
39
+ import { deserialize, sizeof, struct, types as t } from 'utilium';
40
40
  import { Stats } from './stats.js';
41
+ import { randomBigInt } from './utils.js';
41
42
  /**
42
43
  * Room inode
43
44
  * @hidden
44
45
  */
45
46
  export const rootIno = 0n;
46
- /**
47
- * Generates a random 32 bit integer, then converts to a hex string
48
- */
49
- function _random() {
50
- return Math.round(Math.random() * 2 ** 32).toString(16);
51
- }
52
- /**
53
- * Generate a random ino
54
- * @internal
55
- */
56
- export function randomIno() {
57
- return BigInt('0x' + _random() + _random());
58
- }
59
47
  /**
60
48
  * Generic inode definition that can easily be serialized.
49
+ * @internal
50
+ * @todo [BREAKING]
61
51
  */
62
52
  let Inode = (() => {
63
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
53
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
64
54
  let _classDecorators = [struct()];
65
55
  let _classDescriptor;
66
56
  let _classExtraInitializers = [];
67
57
  let _classThis;
68
- let _ino_decorators;
69
- let _ino_initializers = [];
70
- let _ino_extraInitializers = [];
58
+ let _data_decorators;
59
+ let _data_initializers = [];
60
+ let _data_extraInitializers = [];
71
61
  let _size_decorators;
72
62
  let _size_initializers = [];
73
63
  let _size_extraInitializers = [];
@@ -95,13 +85,13 @@ let Inode = (() => {
95
85
  let _ctimeMs_decorators;
96
86
  let _ctimeMs_initializers = [];
97
87
  let _ctimeMs_extraInitializers = [];
88
+ let _ino_decorators;
89
+ let _ino_initializers = [];
90
+ let _ino_extraInitializers = [];
98
91
  var Inode = _classThis = class {
99
- get data() {
100
- return serialize(this);
101
- }
102
92
  constructor(buffer) {
103
- this.ino = __runInitializers(this, _ino_initializers, void 0);
104
- this.size = (__runInitializers(this, _ino_extraInitializers), __runInitializers(this, _size_initializers, void 0));
93
+ this.data = __runInitializers(this, _data_initializers, void 0);
94
+ this.size = (__runInitializers(this, _data_extraInitializers), __runInitializers(this, _size_initializers, void 0));
105
95
  this.mode = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _mode_initializers, void 0));
106
96
  this.nlink = (__runInitializers(this, _mode_extraInitializers), __runInitializers(this, _nlink_initializers, void 0));
107
97
  this.uid = (__runInitializers(this, _nlink_extraInitializers), __runInitializers(this, _uid_initializers, void 0));
@@ -110,16 +100,33 @@ let Inode = (() => {
110
100
  this.birthtimeMs = (__runInitializers(this, _atimeMs_extraInitializers), __runInitializers(this, _birthtimeMs_initializers, void 0));
111
101
  this.mtimeMs = (__runInitializers(this, _birthtimeMs_extraInitializers), __runInitializers(this, _mtimeMs_initializers, void 0));
112
102
  this.ctimeMs = (__runInitializers(this, _mtimeMs_extraInitializers), __runInitializers(this, _ctimeMs_initializers, void 0));
113
- __runInitializers(this, _ctimeMs_extraInitializers);
103
+ this.ino = (__runInitializers(this, _ctimeMs_extraInitializers), __runInitializers(this, _ino_initializers, void 0));
104
+ __runInitializers(this, _ino_extraInitializers);
114
105
  if (buffer) {
115
- if (buffer.byteLength < sizeof(Inode)) {
116
- throw new RangeError(`Can not create an inode from a buffer less than ${sizeof(Inode)} bytes`);
106
+ const sz_inode = sizeof(Inode);
107
+ const oldSize = sz_inode - sizeof('uint64');
108
+ if (buffer.byteLength < oldSize) {
109
+ throw new RangeError(`Can not create an inode from a buffer less than ${oldSize} bytes`);
110
+ }
111
+ // Expand the buffer so it is the right size
112
+ if (buffer.byteLength < sz_inode) {
113
+ const newBuffer = new Uint32Array(sz_inode);
114
+ // Fill the new buffer with current data
115
+ newBuffer.set(new Uint32Array(ArrayBuffer.isView(buffer) ? buffer.buffer : buffer));
116
+ /* Add a random ino.
117
+ This will be different from the actual one,
118
+ but `ino` isn't used anywhere so it should be fine.
119
+ */
120
+ const randomIno = crypto.getRandomValues(new Uint32Array(2));
121
+ newBuffer.set(randomIno, sz_inode - 2);
122
+ buffer = newBuffer;
117
123
  }
118
124
  deserialize(this, buffer);
119
125
  return;
120
126
  }
121
127
  // set defaults on a fresh inode
122
- this.ino = randomIno();
128
+ this.ino = randomBigInt();
129
+ this.data = randomBigInt();
123
130
  this.nlink = 1;
124
131
  this.size = 4096;
125
132
  const now = Date.now();
@@ -184,7 +191,7 @@ let Inode = (() => {
184
191
  __setFunctionName(_classThis, "Inode");
185
192
  (() => {
186
193
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
187
- _ino_decorators = [(_a = t).uint64.bind(_a)];
194
+ _data_decorators = [(_a = t).uint64.bind(_a)];
188
195
  _size_decorators = [(_b = t).uint32.bind(_b)];
189
196
  _mode_decorators = [(_c = t).uint16.bind(_c)];
190
197
  _nlink_decorators = [(_d = t).uint32.bind(_d)];
@@ -194,7 +201,8 @@ let Inode = (() => {
194
201
  _birthtimeMs_decorators = [(_h = t).float64.bind(_h)];
195
202
  _mtimeMs_decorators = [(_j = t).float64.bind(_j)];
196
203
  _ctimeMs_decorators = [(_k = t).float64.bind(_k)];
197
- __esDecorate(null, null, _ino_decorators, { kind: "field", name: "ino", static: false, private: false, access: { has: obj => "ino" in obj, get: obj => obj.ino, set: (obj, value) => { obj.ino = value; } }, metadata: _metadata }, _ino_initializers, _ino_extraInitializers);
204
+ _ino_decorators = [(_l = t).uint64.bind(_l)];
205
+ __esDecorate(null, null, _data_decorators, { kind: "field", name: "data", static: false, private: false, access: { has: obj => "data" in obj, get: obj => obj.data, set: (obj, value) => { obj.data = value; } }, metadata: _metadata }, _data_initializers, _data_extraInitializers);
198
206
  __esDecorate(null, null, _size_decorators, { kind: "field", name: "size", static: false, private: false, access: { has: obj => "size" in obj, get: obj => obj.size, set: (obj, value) => { obj.size = value; } }, metadata: _metadata }, _size_initializers, _size_extraInitializers);
199
207
  __esDecorate(null, null, _mode_decorators, { kind: "field", name: "mode", static: false, private: false, access: { has: obj => "mode" in obj, get: obj => obj.mode, set: (obj, value) => { obj.mode = value; } }, metadata: _metadata }, _mode_initializers, _mode_extraInitializers);
200
208
  __esDecorate(null, null, _nlink_decorators, { kind: "field", name: "nlink", static: false, private: false, access: { has: obj => "nlink" in obj, get: obj => obj.nlink, set: (obj, value) => { obj.nlink = value; } }, metadata: _metadata }, _nlink_initializers, _nlink_extraInitializers);
@@ -204,6 +212,7 @@ let Inode = (() => {
204
212
  __esDecorate(null, null, _birthtimeMs_decorators, { kind: "field", name: "birthtimeMs", static: false, private: false, access: { has: obj => "birthtimeMs" in obj, get: obj => obj.birthtimeMs, set: (obj, value) => { obj.birthtimeMs = value; } }, metadata: _metadata }, _birthtimeMs_initializers, _birthtimeMs_extraInitializers);
205
213
  __esDecorate(null, null, _mtimeMs_decorators, { kind: "field", name: "mtimeMs", static: false, private: false, access: { has: obj => "mtimeMs" in obj, get: obj => obj.mtimeMs, set: (obj, value) => { obj.mtimeMs = value; } }, metadata: _metadata }, _mtimeMs_initializers, _mtimeMs_extraInitializers);
206
214
  __esDecorate(null, null, _ctimeMs_decorators, { kind: "field", name: "ctimeMs", static: false, private: false, access: { has: obj => "ctimeMs" in obj, get: obj => obj.ctimeMs, set: (obj, value) => { obj.ctimeMs = value; } }, metadata: _metadata }, _ctimeMs_initializers, _ctimeMs_extraInitializers);
215
+ __esDecorate(null, null, _ino_decorators, { kind: "field", name: "ino", static: false, private: false, access: { has: obj => "ino" in obj, get: obj => obj.ino, set: (obj, value) => { obj.ino = value; } }, metadata: _metadata }, _ino_initializers, _ino_extraInitializers);
207
216
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
208
217
  Inode = _classThis = _classDescriptor.value;
209
218
  if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -7,16 +7,9 @@ export type AsyncOperation = {
7
7
  [K in keyof AsyncFSMethods]: [K, ...Parameters<FileSystem[K]>];
8
8
  }[keyof AsyncFSMethods];
9
9
  /**
10
- * Async() implements synchronous methods on an asynchronous file system
11
- *
12
- * Implementing classes must define `_sync` for the synchronous file system used as a cache.
13
- *
14
- * Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
15
- * while asynchronously pipelining them to the backing store.
16
- * During loading, the contents of the async file system are preloaded into the synchronous store.
17
- *
10
+ * @internal
18
11
  */
19
- export declare function Async<T extends typeof FileSystem>(FS: T): Mixin<T, {
12
+ export interface Async {
20
13
  /**
21
14
  * @internal @protected
22
15
  */
@@ -33,4 +26,16 @@ export declare function Async<T extends typeof FileSystem>(FS: T): Mixin<T, {
33
26
  readdirSync(path: string): string[];
34
27
  linkSync(srcpath: string, dstpath: string): void;
35
28
  syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
36
- }>;
29
+ }
30
+ /**
31
+ * Async() implements synchronous methods on an asynchronous file system
32
+ *
33
+ * Implementing classes must define `_sync` for the synchronous file system used as a cache.
34
+ *
35
+ * Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
36
+ * while asynchronously pipelining them to the backing store.
37
+ * During loading, the contents of the async file system are preloaded into the synchronous store.
38
+ *
39
+ */
40
+ export declare function Async<const T extends typeof FileSystem>(FS: T): Mixin<T, Async>;
41
+ export declare function asyncPatch<T extends typeof FileSystem>(fs: Mixin<T, Async>): void;
@@ -80,6 +80,7 @@ export function Async(FS) {
80
80
  }
81
81
  async ready() {
82
82
  await super.ready();
83
+ await this.queueDone();
83
84
  if (this._isInitialized || this._disableSync) {
84
85
  return;
85
86
  }
@@ -131,7 +132,7 @@ export function Async(FS) {
131
132
  }
132
133
  openFileSync(path, flag) {
133
134
  this.checkSync(path, 'openFile');
134
- const file = this._sync.openFileSync(path, flag);
135
+ const file = this._sync.openFileSync(path, flag + '+');
135
136
  const stats = file.statSync();
136
137
  const buffer = new Uint8Array(stats.size);
137
138
  file.readSync(buffer);
@@ -228,3 +229,4 @@ export function Async(FS) {
228
229
  }
229
230
  return AsyncFS;
230
231
  }
232
+ export function asyncPatch(fs) { }
package/dist/utils.d.ts CHANGED
@@ -50,13 +50,6 @@ export declare function decodeDirListing(data: Uint8Array): Record<string, bigin
50
50
  */
51
51
  export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
52
52
  export type Callback<Args extends unknown[] = []> = (e?: ErrnoError, ...args: OptionalTuple<Args>) => unknown;
53
- /**
54
- * converts Date or number to a integer UNIX timestamp
55
- * Grabbed from NodeJS sources (lib/fs.js)
56
- *
57
- * @internal
58
- */
59
- export declare function _toUnixTimestamp(time: Date | number): number;
60
53
  /**
61
54
  * Normalizes a mode
62
55
  * @internal
@@ -88,3 +81,8 @@ export declare function normalizeOptions(options: fs.WriteFileOptions | (fs.Enco
88
81
  mode: number;
89
82
  };
90
83
  export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: any[]) => InstanceType<T>);
84
+ /**
85
+ * Generate a random ino
86
+ * @internal
87
+ */
88
+ export declare function randomBigInt(): bigint;