@zenfs/core 1.2.9 → 1.3.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 (57) 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 +2 -1
  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.d.ts +1 -1
  22. package/dist/emulation/sync.js +16 -15
  23. package/dist/file.d.ts +3 -15
  24. package/dist/file.js +7 -19
  25. package/dist/inode.d.ts +4 -13
  26. package/dist/inode.js +22 -29
  27. package/dist/mixins/async.d.ts +15 -10
  28. package/dist/mixins/async.js +3 -1
  29. package/dist/stats.js +30 -5
  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 +11 -1
  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 +16 -15
  46. package/src/file.ts +9 -21
  47. package/src/inode.ts +10 -31
  48. package/src/mixins/async.ts +27 -24
  49. package/src/stats.ts +47 -5
  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/stat.test.ts +42 -0
  56. package/tests/fs/times.test.ts +28 -28
  57. package/tests/setup/cow+fetch.ts +4 -2
@@ -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
  /**
@@ -64,7 +64,7 @@ export declare function writeFileSync(path: fs.PathOrFileDescriptor, data: FileC
64
64
  * Asynchronously append data to a file, creating the file if it not yet exists.
65
65
  * @option encoding Defaults to `'utf8'`.
66
66
  * @option mode Defaults to `0644`.
67
- * @option flag Defaults to `'a'`.
67
+ * @option flag Defaults to `'a+'`.
68
68
  */
69
69
  export declare function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileContents, _options?: fs.WriteFileOptions): void;
70
70
  /**
@@ -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);
@@ -272,12 +272,12 @@ writeFileSync;
272
272
  * Asynchronously append data to a file, creating the file if it not yet exists.
273
273
  * @option encoding Defaults to `'utf8'`.
274
274
  * @option mode Defaults to `0644`.
275
- * @option flag Defaults to `'a'`.
275
+ * @option flag Defaults to `'a+'`.
276
276
  */
277
277
  export function appendFileSync(filename, data, _options = {}) {
278
278
  const env_4 = { stack: [], error: void 0, hasError: false };
279
279
  try {
280
- const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
280
+ const options = normalizeOptions(_options, 'utf8', 'a+', 0o644);
281
281
  const flag = parseFlag(options.flag);
282
282
  if (!isAppendable(flag)) {
283
283
  throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
@@ -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
@@ -1,5 +1,5 @@
1
- import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT, size_max } from './emulation/constants.js';
2
1
  import { config } from './emulation/config.js';
2
+ import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT, size_max } from './emulation/constants.js';
3
3
  import { Errno, ErrnoError } from './error.js';
4
4
  import './polyfills.js';
5
5
  import { Stats } from './stats.js';
@@ -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/inode.d.ts CHANGED
@@ -1,26 +1,16 @@
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
19
10
  */
20
11
  export declare class Inode implements StatsLike {
21
- get data(): Uint8Array;
22
- constructor(buffer?: ArrayBufferLike);
23
- ino: Ino;
12
+ constructor(buffer?: ArrayBufferLike | ArrayBufferView);
13
+ data: bigint;
24
14
  size: number;
25
15
  mode: number;
26
16
  nlink: number;
@@ -30,6 +20,7 @@ export declare class Inode implements StatsLike {
30
20
  birthtimeMs: number;
31
21
  mtimeMs: number;
32
22
  ctimeMs: number;
23
+ ino: bigint;
33
24
  /**
34
25
  * Handy function that converts the Inode to a Node Stats object.
35
26
  */
package/dist/inode.js CHANGED
@@ -36,38 +36,27 @@ 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, sizeof, struct, types as t } from 'utilium';
39
40
  import { Stats } from './stats.js';
40
- import { types as t, struct, sizeof, serialize, deserialize } from 'utilium';
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
61
50
  */
62
51
  let Inode = (() => {
63
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
52
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
64
53
  let _classDecorators = [struct()];
65
54
  let _classDescriptor;
66
55
  let _classExtraInitializers = [];
67
56
  let _classThis;
68
- let _ino_decorators;
69
- let _ino_initializers = [];
70
- let _ino_extraInitializers = [];
57
+ let _data_decorators;
58
+ let _data_initializers = [];
59
+ let _data_extraInitializers = [];
71
60
  let _size_decorators;
72
61
  let _size_initializers = [];
73
62
  let _size_extraInitializers = [];
@@ -95,13 +84,13 @@ let Inode = (() => {
95
84
  let _ctimeMs_decorators;
96
85
  let _ctimeMs_initializers = [];
97
86
  let _ctimeMs_extraInitializers = [];
87
+ let _ino_decorators;
88
+ let _ino_initializers = [];
89
+ let _ino_extraInitializers = [];
98
90
  var Inode = _classThis = class {
99
- get data() {
100
- return serialize(this);
101
- }
102
91
  constructor(buffer) {
103
- this.ino = __runInitializers(this, _ino_initializers, void 0);
104
- this.size = (__runInitializers(this, _ino_extraInitializers), __runInitializers(this, _size_initializers, void 0));
92
+ this.data = __runInitializers(this, _data_initializers, void 0);
93
+ this.size = (__runInitializers(this, _data_extraInitializers), __runInitializers(this, _size_initializers, void 0));
105
94
  this.mode = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _mode_initializers, void 0));
106
95
  this.nlink = (__runInitializers(this, _mode_extraInitializers), __runInitializers(this, _nlink_initializers, void 0));
107
96
  this.uid = (__runInitializers(this, _nlink_extraInitializers), __runInitializers(this, _uid_initializers, void 0));
@@ -110,7 +99,8 @@ let Inode = (() => {
110
99
  this.birthtimeMs = (__runInitializers(this, _atimeMs_extraInitializers), __runInitializers(this, _birthtimeMs_initializers, void 0));
111
100
  this.mtimeMs = (__runInitializers(this, _birthtimeMs_extraInitializers), __runInitializers(this, _mtimeMs_initializers, void 0));
112
101
  this.ctimeMs = (__runInitializers(this, _mtimeMs_extraInitializers), __runInitializers(this, _ctimeMs_initializers, void 0));
113
- __runInitializers(this, _ctimeMs_extraInitializers);
102
+ this.ino = (__runInitializers(this, _ctimeMs_extraInitializers), __runInitializers(this, _ino_initializers, void 0));
103
+ __runInitializers(this, _ino_extraInitializers);
114
104
  if (buffer) {
115
105
  if (buffer.byteLength < sizeof(Inode)) {
116
106
  throw new RangeError(`Can not create an inode from a buffer less than ${sizeof(Inode)} bytes`);
@@ -119,7 +109,8 @@ let Inode = (() => {
119
109
  return;
120
110
  }
121
111
  // set defaults on a fresh inode
122
- this.ino = randomIno();
112
+ this.ino = randomBigInt();
113
+ this.data = randomBigInt();
123
114
  this.nlink = 1;
124
115
  this.size = 4096;
125
116
  const now = Date.now();
@@ -162,8 +153,8 @@ let Inode = (() => {
162
153
  this.uid = stats.uid;
163
154
  hasChanged = true;
164
155
  }
165
- if (this.uid !== stats.uid) {
166
- this.uid = stats.uid;
156
+ if (this.gid !== stats.gid) {
157
+ this.gid = stats.gid;
167
158
  hasChanged = true;
168
159
  }
169
160
  if (this.atimeMs !== stats.atimeMs) {
@@ -184,7 +175,7 @@ let Inode = (() => {
184
175
  __setFunctionName(_classThis, "Inode");
185
176
  (() => {
186
177
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
187
- _ino_decorators = [(_a = t).uint64.bind(_a)];
178
+ _data_decorators = [(_a = t).uint64.bind(_a)];
188
179
  _size_decorators = [(_b = t).uint32.bind(_b)];
189
180
  _mode_decorators = [(_c = t).uint16.bind(_c)];
190
181
  _nlink_decorators = [(_d = t).uint32.bind(_d)];
@@ -194,7 +185,8 @@ let Inode = (() => {
194
185
  _birthtimeMs_decorators = [(_h = t).float64.bind(_h)];
195
186
  _mtimeMs_decorators = [(_j = t).float64.bind(_j)];
196
187
  _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);
188
+ _ino_decorators = [(_l = t).uint64.bind(_l)];
189
+ __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
190
  __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
191
  __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
192
  __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 +196,7 @@ let Inode = (() => {
204
196
  __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
197
  __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
198
  __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);
199
+ __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
200
  __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
208
201
  Inode = _classThis = _classDescriptor.value;
209
202
  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/stats.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { credentials } from './credentials.js';
2
- import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRWXG, S_IRWXO, S_IRWXU, size_max } from './emulation/constants.js';
2
+ import { R_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, size_max, W_OK, X_OK, } from './emulation/constants.js';
3
3
  /**
4
4
  * Provides information about a particular entry in the file system.
5
5
  * Common code used by both Stats and BigIntStats.
@@ -110,12 +110,37 @@ export class StatsCommon {
110
110
  */
111
111
  hasAccess(mode) {
112
112
  if (credentials.euid === 0 || credentials.egid === 0) {
113
- //Running as root
113
+ // Running as root
114
114
  return true;
115
115
  }
116
- // Mask for
117
- const adjusted = (credentials.uid == this.uid ? S_IRWXU : 0) | (credentials.gid == this.gid ? S_IRWXG : 0) | S_IRWXO;
118
- return (mode & this.mode & adjusted) == mode;
116
+ let perm = 0;
117
+ // Owner permissions
118
+ if (credentials.uid === this.uid) {
119
+ if (this.mode & S_IRUSR)
120
+ perm |= R_OK;
121
+ if (this.mode & S_IWUSR)
122
+ perm |= W_OK;
123
+ if (this.mode & S_IXUSR)
124
+ perm |= X_OK;
125
+ }
126
+ // Group permissions
127
+ if (credentials.gid === this.gid) {
128
+ if (this.mode & S_IRGRP)
129
+ perm |= R_OK;
130
+ if (this.mode & S_IWGRP)
131
+ perm |= W_OK;
132
+ if (this.mode & S_IXGRP)
133
+ perm |= X_OK;
134
+ }
135
+ // Others permissions
136
+ if (this.mode & S_IROTH)
137
+ perm |= R_OK;
138
+ if (this.mode & S_IWOTH)
139
+ perm |= W_OK;
140
+ if (this.mode & S_IXOTH)
141
+ perm |= X_OK;
142
+ // Perform the access check
143
+ return (perm & mode) === mode;
119
144
  }
120
145
  /**
121
146
  * Convert the current stats object into a credentials object
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;
package/dist/utils.js CHANGED
@@ -143,21 +143,6 @@ export function decodeDirListing(data) {
143
143
  export function encodeDirListing(data) {
144
144
  return encodeUTF8(JSON.stringify(data, (k, v) => (k == '' ? v : v.toString())));
145
145
  }
146
- /**
147
- * converts Date or number to a integer UNIX timestamp
148
- * Grabbed from NodeJS sources (lib/fs.js)
149
- *
150
- * @internal
151
- */
152
- export function _toUnixTimestamp(time) {
153
- if (typeof time === 'number') {
154
- return Math.floor(time);
155
- }
156
- if (time instanceof Date) {
157
- return Math.floor(time.getTime() / 1000);
158
- }
159
- throw new Error('Cannot parse time');
160
- }
161
146
  /**
162
147
  * Normalizes a mode
163
148
  * @internal
@@ -185,13 +170,12 @@ export function normalizeTime(time) {
185
170
  if (time instanceof Date) {
186
171
  return time;
187
172
  }
188
- if (typeof time == 'number') {
189
- return new Date(time * 1000);
190
- }
191
- if (typeof time == 'string') {
173
+ try {
192
174
  return new Date(time);
193
175
  }
194
- throw new ErrnoError(Errno.EINVAL, 'Invalid time.');
176
+ catch {
177
+ throw new ErrnoError(Errno.EINVAL, 'Invalid time.');
178
+ }
195
179
  }
196
180
  /**
197
181
  * Normalizes a path
@@ -229,3 +213,10 @@ export function normalizeOptions(options, encoding = 'utf8', flag, mode = 0) {
229
213
  mode: normalizeMode('mode' in options ? options?.mode : null, mode),
230
214
  };
231
215
  }
216
+ /**
217
+ * Generate a random ino
218
+ * @internal
219
+ */
220
+ export function randomBigInt() {
221
+ return crypto.getRandomValues(new BigUint64Array(1))[0];
222
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.2.9",
3
+ "version": "1.3.0",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -106,7 +106,7 @@ export class FetchFS extends IndexFS {
106
106
  baseUrl += '/';
107
107
  }
108
108
 
109
- super(typeof index != 'string' ? index : fetchFile<IndexData>(baseUrl + index, 'json', requestInit));
109
+ super(typeof index != 'string' ? index : fetchFile<IndexData>(index, 'json', requestInit));
110
110
 
111
111
  this.baseUrl = baseUrl;
112
112
  this.requestInit = requestInit;
@@ -1,4 +1,3 @@
1
- import type { Ino } from '../inode.js';
2
1
  import type { Backend } from './backend.js';
3
2
  import { StoreFS } from './store/fs.js';
4
3
  import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
@@ -6,7 +5,7 @@ import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
6
5
  /**
7
6
  * A simple in-memory store
8
7
  */
9
- export class InMemoryStore extends Map<Ino, Uint8Array> implements SimpleSyncStore {
8
+ export class InMemoryStore extends Map<bigint, Uint8Array> implements SimpleSyncStore {
10
9
  public constructor(public name: string = 'tmp') {
11
10
  super();
12
11
  }
@@ -159,7 +159,7 @@ export class UnmutexedOverlayFS extends FileSystem {
159
159
  if (this._deletedFiles.has(path)) {
160
160
  throw ErrnoError.With('ENOENT', path, 'stat');
161
161
  }
162
- const oldStat = new Stats(await this.readable.stat(path));
162
+ const oldStat = await this.readable.stat(path);
163
163
  // Make the oldStat's mode writable.
164
164
  oldStat.mode |= 0o222;
165
165
  return oldStat;
@@ -174,7 +174,7 @@ export class UnmutexedOverlayFS extends FileSystem {
174
174
  if (this._deletedFiles.has(path)) {
175
175
  throw ErrnoError.With('ENOENT', path, 'stat');
176
176
  }
177
- const oldStat = new Stats(this.readable.statSync(path));
177
+ const oldStat = this.readable.statSync(path);
178
178
  // Make the oldStat's mode writable.
179
179
  oldStat.mode |= 0o222;
180
180
  return oldStat;