@zenfs/core 1.9.2 → 1.9.4

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 (60) hide show
  1. package/dist/backends/backend.d.ts +16 -2
  2. package/dist/backends/backend.js +9 -2
  3. package/dist/backends/fetch.d.ts +9 -0
  4. package/dist/backends/fetch.js +13 -3
  5. package/dist/backends/memory.d.ts +2 -0
  6. package/dist/backends/memory.js +2 -0
  7. package/dist/backends/overlay.d.ts +2 -0
  8. package/dist/backends/overlay.js +1 -0
  9. package/dist/backends/passthrough.d.ts +4 -0
  10. package/dist/backends/port/fs.d.ts +8 -2
  11. package/dist/backends/port/fs.js +6 -0
  12. package/dist/backends/store/fs.d.ts +2 -4
  13. package/dist/backends/store/fs.js +2 -4
  14. package/dist/backends/store/map.d.ts +6 -0
  15. package/dist/backends/store/map.js +4 -0
  16. package/dist/backends/store/simple.d.ts +3 -0
  17. package/dist/backends/store/simple.js +1 -0
  18. package/dist/backends/store/store.d.ts +12 -1
  19. package/dist/backends/store/store.js +5 -1
  20. package/dist/config.d.ts +9 -15
  21. package/dist/config.js +8 -5
  22. package/dist/context.d.ts +3 -4
  23. package/dist/context.js +1 -1
  24. package/dist/internal/credentials.d.ts +9 -0
  25. package/dist/internal/credentials.js +7 -0
  26. package/dist/internal/devices.d.ts +14 -0
  27. package/dist/internal/devices.js +10 -0
  28. package/dist/internal/error.d.ts +6 -0
  29. package/dist/internal/error.js +3 -0
  30. package/dist/internal/file.d.ts +23 -0
  31. package/dist/internal/file.js +23 -1
  32. package/dist/internal/file_index.d.ts +2 -1
  33. package/dist/internal/file_index.js +2 -1
  34. package/dist/internal/filesystem.d.ts +9 -0
  35. package/dist/internal/filesystem.js +1 -0
  36. package/dist/internal/index_fs.d.ts +3 -1
  37. package/dist/internal/index_fs.js +7 -3
  38. package/dist/internal/inode.d.ts +8 -0
  39. package/dist/internal/inode.js +1 -0
  40. package/dist/mixins/async.d.ts +6 -2
  41. package/dist/mixins/async.js +1 -1
  42. package/dist/mixins/mutexed.d.ts +6 -0
  43. package/dist/mixins/mutexed.js +6 -0
  44. package/dist/mixins/readonly.d.ts +1 -0
  45. package/dist/mixins/readonly.js +1 -0
  46. package/dist/mixins/shared.d.ts +3 -0
  47. package/dist/mixins/sync.d.ts +1 -0
  48. package/dist/mixins/sync.js +1 -0
  49. package/dist/vfs/promises.d.ts +2 -2
  50. package/dist/vfs/promises.js +71 -78
  51. package/dist/vfs/shared.d.ts +14 -1
  52. package/dist/vfs/shared.js +4 -6
  53. package/dist/vfs/sync.d.ts +2 -2
  54. package/dist/vfs/sync.js +48 -48
  55. package/dist/vfs/types.d.ts +1 -13
  56. package/package.json +1 -1
  57. package/readme.md +4 -0
  58. package/tests/backend/fetch.test.ts +3 -2
  59. package/dist/vfs/cache.d.ts +0 -46
  60. package/dist/vfs/cache.js +0 -75
package/dist/vfs/sync.js CHANGED
@@ -56,7 +56,6 @@ import { Errno, ErrnoError } from '../internal/error.js';
56
56
  import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../internal/file.js';
57
57
  import { BigIntStats } from '../stats.js';
58
58
  import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
59
- import * as cache from './cache.js';
60
59
  import { config } from './config.js';
61
60
  import * as constants from './constants.js';
62
61
  import { Dir, Dirent } from './dir.js';
@@ -154,7 +153,7 @@ export function unlinkSync(path) {
154
153
  path = normalizePath(path);
155
154
  const { fs, path: resolved } = resolveMount(path, this);
156
155
  try {
157
- if (config.checkAccess && !(cache.stats.get(path) || fs.statSync(resolved)).hasAccess(constants.W_OK, this)) {
156
+ if (config.checkAccess && !fs.statSync(resolved).hasAccess(constants.W_OK, this)) {
158
157
  throw ErrnoError.With('EACCES', resolved, 'unlink');
159
158
  }
160
159
  fs.unlinkSync(resolved);
@@ -426,7 +425,7 @@ export function rmdirSync(path) {
426
425
  path = normalizePath(path);
427
426
  const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
428
427
  try {
429
- const stats = cache.stats.get(path) || fs.statSync(resolved);
428
+ const stats = fs.statSync(resolved);
430
429
  if (!stats.isDirectory()) {
431
430
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
432
431
  }
@@ -484,8 +483,7 @@ export function readdirSync(path, options) {
484
483
  const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
485
484
  let entries;
486
485
  try {
487
- const stats = cache.stats.get(path) || fs.statSync(resolved);
488
- cache.stats.set(path, stats);
486
+ const stats = fs.statSync(resolved);
489
487
  if (config.checkAccess && !stats.hasAccess(constants.R_OK, this)) {
490
488
  throw ErrnoError.With('EACCES', resolved, 'readdir');
491
489
  }
@@ -500,8 +498,7 @@ export function readdirSync(path, options) {
500
498
  // Iterate over entries and handle recursive case if needed
501
499
  const values = [];
502
500
  for (const entry of entries) {
503
- const entryStat = cache.stats.get(join(path, entry)) || fs.statSync(join(resolved, entry));
504
- cache.stats.set(join(path, entry), entryStat);
501
+ const entryStat = fs.statSync(join(resolved, entry));
505
502
  if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
506
503
  values.push(new Dirent(entry, entryStat));
507
504
  }
@@ -513,7 +510,7 @@ export function readdirSync(path, options) {
513
510
  }
514
511
  if (!entryStat.isDirectory() || !(options === null || options === void 0 ? void 0 : options.recursive))
515
512
  continue;
516
- for (const subEntry of readdirSync.call(this, join(path, entry), { ...options, _isIndirect: true })) {
513
+ for (const subEntry of readdirSync.call(this, join(path, entry), options)) {
517
514
  if (subEntry instanceof Dirent) {
518
515
  subEntry.path = join(entry, subEntry.path);
519
516
  values.push(subEntry);
@@ -526,9 +523,6 @@ export function readdirSync(path, options) {
526
523
  }
527
524
  }
528
525
  }
529
- if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
530
- cache.stats.clear();
531
- }
532
526
  return values;
533
527
  }
534
528
  readdirSync;
@@ -627,52 +621,64 @@ export function lutimesSync(path, atime, mtime) {
627
621
  closeSync(fd);
628
622
  }
629
623
  lutimesSync;
630
- export function realpathSync(path, options) {
631
- path = normalizePath(path);
632
- const ctx_path = ((this === null || this === void 0 ? void 0 : this.root) || '') + path;
633
- if (cache.paths.has(ctx_path))
634
- return cache.paths.get(ctx_path);
624
+ /**
625
+ * Resolves the mount and real path for a path.
626
+ * Additionally, any stats fetched will be returned for de-duplication
627
+ * @internal @hidden
628
+ */
629
+ function _resolveSync($, path, preserveSymlinks) {
630
+ if (preserveSymlinks) {
631
+ const resolved = resolveMount(path, $);
632
+ const stats = resolved.fs.statSync(resolved.path);
633
+ return { ...resolved, fullPath: path, stats };
634
+ }
635
635
  /* Try to resolve it directly. If this works,
636
636
  that means we don't need to perform any resolution for parent directories. */
637
637
  try {
638
- const { fs, path: resolvedPath } = resolveMount(path, this);
638
+ const resolved = resolveMount(path, $);
639
639
  // Stat it to make sure it exists
640
- const stats = fs.statSync(resolvedPath);
641
- let real = path;
642
- if (stats.isSymbolicLink()) {
643
- const target = resolve(dirname(resolvedPath), readlinkSync.call(this, resolvedPath, options).toString());
644
- real = cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + target) || realpathSync.call(this, target);
645
- cache.paths.set(ctx_path, real);
640
+ const stats = resolved.fs.statSync(resolved.path);
641
+ if (!stats.isSymbolicLink()) {
642
+ return { ...resolved, fullPath: path, stats };
646
643
  }
647
- cache.paths.set(path, real);
648
- return real;
644
+ const target = resolve(dirname(path), readlinkSync.call($, path).toString());
645
+ return _resolveSync($, target);
649
646
  }
650
647
  catch {
651
648
  // Go the long way
652
649
  }
653
650
  const { base, dir } = parse(path);
654
- const realDir = dir == '/' ? '/' : cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + dir) || realpathSync.call(this, dir);
655
- const lpath = join(realDir, base);
656
- const { fs, path: resolvedPath } = resolveMount(lpath, this);
651
+ const realDir = dir == '/' ? '/' : realpathSync.call($, dir);
652
+ const maybePath = join(realDir, base);
653
+ const resolved = resolveMount(maybePath, $);
657
654
  try {
658
- const stats = cache.stats.get(lpath) || fs.statSync(resolvedPath);
659
- cache.stats.set(lpath, stats);
655
+ const stats = resolved.fs.statSync(resolved.path);
660
656
  if (!stats.isSymbolicLink()) {
661
- cache.paths.set(path, lpath);
662
- return lpath;
657
+ return { ...resolved, fullPath: maybePath, stats };
663
658
  }
664
- const target = resolve(realDir, readlinkSync.call(this, lpath, options).toString());
665
- const real = cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + target) || realpathSync.call(this, target);
666
- cache.paths.set(ctx_path, real);
667
- return real;
659
+ const target = resolve(realDir, readlinkSync.call($, maybePath).toString());
660
+ const real = realpathSync.call($, target);
661
+ return { ...resolved, fullPath: real, stats };
668
662
  }
669
663
  catch (e) {
670
664
  if (e.code == 'ENOENT') {
671
- return path;
665
+ return { ...resolved, fullPath: path };
672
666
  }
673
- throw fixError(e, { [resolvedPath]: lpath });
667
+ throw fixError(e, { [resolved.path]: maybePath });
674
668
  }
675
669
  }
670
+ export function realpathSync(path, options) {
671
+ var _a;
672
+ const encoding = typeof options == 'string' ? options : ((_a = options === null || options === void 0 ? void 0 : options.encoding) !== null && _a !== void 0 ? _a : 'utf8');
673
+ path = normalizePath(path);
674
+ const { fullPath } = _resolveSync(this, path);
675
+ if (encoding == 'utf8' || encoding == 'utf-8')
676
+ return fullPath;
677
+ const buf = Buffer.from(fullPath, 'utf-8');
678
+ if (encoding == 'buffer')
679
+ return buf;
680
+ return buf.toString(encoding);
681
+ }
676
682
  realpathSync;
677
683
  export function accessSync(path, mode = 0o600) {
678
684
  if (!config.checkAccess)
@@ -690,21 +696,19 @@ export function rmSync(path, options) {
690
696
  path = normalizePath(path);
691
697
  let stats;
692
698
  try {
693
- stats = cache.stats.get(path) || lstatSync.bind(this)(path);
699
+ stats = lstatSync.bind(this)(path);
694
700
  }
695
701
  catch (error) {
696
702
  if (error.code != 'ENOENT' || !(options === null || options === void 0 ? void 0 : options.force))
697
703
  throw error;
698
704
  }
699
- if (!stats) {
705
+ if (!stats)
700
706
  return;
701
- }
702
- cache.stats.set(path, stats);
703
707
  switch (stats.mode & constants.S_IFMT) {
704
708
  case constants.S_IFDIR:
705
709
  if (options === null || options === void 0 ? void 0 : options.recursive) {
706
- for (const entry of readdirSync.call(this, path, { _isIndirect: true })) {
707
- rmSync.call(this, join(path, entry), { ...options, _isIndirect: true });
710
+ for (const entry of readdirSync.call(this, path)) {
711
+ rmSync.call(this, join(path, entry), options);
708
712
  }
709
713
  }
710
714
  rmdirSync.call(this, path);
@@ -718,12 +722,8 @@ export function rmSync(path, options) {
718
722
  case constants.S_IFIFO:
719
723
  case constants.S_IFSOCK:
720
724
  default:
721
- cache.stats.clear();
722
725
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
723
726
  }
724
- if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
725
- cache.stats.clear();
726
- }
727
727
  }
728
728
  rmSync;
729
729
  export function mkdtempSync(prefix, options) {
@@ -1,17 +1,5 @@
1
1
  import type * as fs from 'node:fs';
2
2
  export type FileContents = ArrayBufferView | string;
3
- /**
4
- * Options used for caching, among other things.
5
- * @internal @hidden *UNSTABLE*
6
- */
7
- export interface InternalOptions {
8
- /**
9
- * If true, then this readdir was called from another function.
10
- * In this case, don't clear the cache when done.
11
- * @internal *UNSTABLE*
12
- */
13
- _isIndirect?: boolean;
14
- }
15
3
  /**
16
4
  * @internal @hidden Used for the internal `_open` functions
17
5
  */
@@ -27,7 +15,7 @@ export interface OpenOptions {
27
15
  */
28
16
  allowDirectory?: boolean;
29
17
  }
30
- export interface ReaddirOptions extends InternalOptions {
18
+ export interface ReaddirOptions {
31
19
  withFileTypes?: boolean;
32
20
  recursive?: boolean;
33
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
package/readme.md CHANGED
@@ -1,3 +1,7 @@
1
+ ---
2
+ title: Overview
3
+ ---
4
+
1
5
  # ZenFS
2
6
 
3
7
  ZenFS is a cross-platform library that emulates the [NodeJS filesystem API](http://nodejs.org/api/fs.html). It works using a system of backends, which are used by ZenFS to store and retrieve data. ZenFS can also integrate with other tools.
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
2
2
  import { join } from 'node:path';
3
3
  import { suite, test } from 'node:test';
4
4
  import { Worker } from 'node:worker_threads';
5
- import { Fetch, configureSingle, fs } from '../../dist/index.js';
5
+ import { Fetch, configureSingle, fs, mounts, type FetchFS } from '../../dist/index.js';
6
6
  import { baseUrl, defaultEntries, indexPath, whenServerReady } from '../fetch/config.js';
7
7
 
8
8
  const server = new Worker(join(import.meta.dirname, '../fetch/server.js'));
@@ -40,8 +40,9 @@ await suite('Fetch with `disableAsyncCache`', () => {
40
40
  assert.deepEqual(entries, [...defaultEntries, 'example', 'duck']);
41
41
  });
42
42
 
43
- test('Uncached synchronous operations throw', () => {
43
+ test('Uncached synchronous operations throw', async () => {
44
44
  assert.throws(() => fs.readFileSync('/x.txt', 'utf8'), { code: 'EAGAIN' });
45
+ await (mounts.get('/') as FetchFS)._asyncDone;
45
46
  });
46
47
  });
47
48
 
@@ -1,46 +0,0 @@
1
- import type { Stats } from '../stats.js';
2
- /**
3
- * Used for caching data
4
- * @internal
5
- */
6
- export declare class Cache<T> {
7
- isEnabled: boolean;
8
- protected sync: Map<string, T>;
9
- protected async: Map<string, Promise<T>>;
10
- /**
11
- * Whether the data exists in the cache
12
- */
13
- has(path: string): boolean;
14
- /**
15
- * Gets data from the cache, if is exists and the cache is enabled.
16
- */
17
- get(path: string): T | undefined;
18
- /**
19
- * Adds data if the cache is enabled
20
- */
21
- set(path: string, value: T): void;
22
- /**
23
- * Whether the data exists in the cache
24
- */
25
- hasAsync(path: string): boolean;
26
- /**
27
- * Gets data from the cache, if it exists and the cache is enabled.
28
- */
29
- getAsync(path: string): Promise<T> | undefined;
30
- /**
31
- * Adds data if the cache is enabled
32
- */
33
- setAsync(path: string, value: Promise<T>): void;
34
- /**
35
- * Clears the cache if it is enabled
36
- */
37
- clear(): void;
38
- }
39
- /**
40
- * Used to cache
41
- */
42
- export declare const stats: Cache<Stats>;
43
- /**
44
- * Used to cache realpath lookups
45
- */
46
- export declare const paths: Cache<string>;
package/dist/vfs/cache.js DELETED
@@ -1,75 +0,0 @@
1
- /* Experimental caching */
2
- /**
3
- * Used for caching data
4
- * @internal
5
- */
6
- export class Cache {
7
- constructor() {
8
- this.isEnabled = false;
9
- this.sync = new Map();
10
- this.async = new Map();
11
- }
12
- /**
13
- * Whether the data exists in the cache
14
- */
15
- has(path) {
16
- return this.isEnabled && this.sync.has(path);
17
- }
18
- /**
19
- * Gets data from the cache, if is exists and the cache is enabled.
20
- */
21
- get(path) {
22
- if (!this.isEnabled)
23
- return;
24
- return this.sync.get(path);
25
- }
26
- /**
27
- * Adds data if the cache is enabled
28
- */
29
- set(path, value) {
30
- if (!this.isEnabled)
31
- return;
32
- this.sync.set(path, value);
33
- this.async.set(path, Promise.resolve(value));
34
- }
35
- /**
36
- * Whether the data exists in the cache
37
- */
38
- hasAsync(path) {
39
- return this.isEnabled && this.async.has(path);
40
- }
41
- /**
42
- * Gets data from the cache, if it exists and the cache is enabled.
43
- */
44
- getAsync(path) {
45
- if (!this.isEnabled)
46
- return;
47
- return this.async.get(path);
48
- }
49
- /**
50
- * Adds data if the cache is enabled
51
- */
52
- setAsync(path, value) {
53
- if (!this.isEnabled)
54
- return;
55
- this.async.set(path, value);
56
- void value.then(v => this.sync.set(path, v));
57
- }
58
- /**
59
- * Clears the cache if it is enabled
60
- */
61
- clear() {
62
- if (!this.isEnabled)
63
- return;
64
- this.sync.clear();
65
- this.async.clear();
66
- }
67
- }
68
- /**
69
- * Used to cache
70
- */
71
- export const stats = new Cache();
72
- /**
73
- * Used to cache realpath lookups
74
- */
75
- export const paths = new Cache();