@zenfs/core 1.10.2 → 1.10.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.
@@ -3,6 +3,7 @@ import type { CreationOptions, UsageInfo } from '../internal/filesystem.js';
3
3
  import type { InodeLike } from '../internal/inode.js';
4
4
  import type { Stats } from '../stats.js';
5
5
  import { FileSystem } from '../internal/filesystem.js';
6
+ import { EventEmitter } from 'eventemitter3';
6
7
  /**
7
8
  * Configuration options for CoW.
8
9
  * @category Backends and Configuration
@@ -12,22 +13,13 @@ export interface CopyOnWriteOptions {
12
13
  readable: FileSystem;
13
14
  /** The file system to write modified files to. */
14
15
  writable: FileSystem;
15
- /** Options for the journal */
16
- journal?: JournalOptions;
16
+ /** @see {@link Journal} */
17
+ journal?: Journal;
17
18
  }
18
19
  /**
19
20
  * @hidden @deprecated use `CopyOnWriteOptions`
20
21
  */
21
22
  export type OverlayOptions = CopyOnWriteOptions;
22
- /**
23
- * Configuration options for the journal used by CoW.
24
- * @category Backends and Configuration
25
- * @internal
26
- */
27
- export interface JournalOptions {
28
- /** The contents of the journal */
29
- contents?: string | JournalEntry[];
30
- }
31
23
  declare const journalOperations: readonly ["delete"];
32
24
  /**
33
25
  * @internal
@@ -44,15 +36,16 @@ export interface JournalEntry {
44
36
  * Tracks various operations for the CoW backend
45
37
  * @internal
46
38
  */
47
- export declare class Journal {
48
- readonly fs: CopyOnWriteFS;
49
- entries: JournalEntry[];
50
- constructor(fs: CopyOnWriteFS);
39
+ export declare class Journal extends EventEmitter<{
40
+ update: [op: JournalOperation, path: string];
41
+ delete: [path: string];
42
+ }> {
43
+ protected entries: JournalEntry[];
51
44
  toString(): string;
52
45
  /**
53
46
  * Parse a journal from a string
54
47
  */
55
- fromString(value: string): void;
48
+ fromString(value: string): this;
56
49
  add(op: JournalOperation, path: string): void;
57
50
  has(op: JournalOperation, path: string): boolean;
58
51
  isDeleted(path: string): boolean;
@@ -62,25 +55,20 @@ export declare class Journal {
62
55
  * @internal
63
56
  */
64
57
  export declare class CopyOnWriteFS extends FileSystem {
65
- /**
66
- * The file system that initially populates this file system.
67
- */
58
+ /** The file system that initially populates this file system. */
68
59
  readonly readable: FileSystem;
69
- /**
70
- * The file system to write modified files to.
71
- */
60
+ /** The file system to write modified files to. */
72
61
  readonly writable: FileSystem;
73
- ready(): Promise<void>;
62
+ /** The journal to use for persisting deletions */
74
63
  readonly journal: Journal;
64
+ ready(): Promise<void>;
75
65
  constructor(
76
- /**
77
- * The file system that initially populates this file system.
78
- */
66
+ /** The file system that initially populates this file system. */
79
67
  readable: FileSystem,
80
- /**
81
- * The file system to write modified files to.
82
- */
83
- writable: FileSystem);
68
+ /** The file system to write modified files to. */
69
+ writable: FileSystem,
70
+ /** The journal to use for persisting deletions */
71
+ journal?: Journal);
84
72
  isDeleted(path: string): boolean;
85
73
  /**
86
74
  * @todo Consider trying to track information on the writable as well
@@ -56,6 +56,7 @@ import { LazyFile } from '../internal/file.js';
56
56
  import { FileSystem } from '../internal/filesystem.js';
57
57
  import { debug, err, warn } from '../internal/log.js';
58
58
  import { dirname, join } from '../vfs/path.js';
59
+ import { EventEmitter } from 'eventemitter3';
59
60
  const journalOperations = ['delete'];
60
61
  /** Because TS doesn't work right w/o it */
61
62
  function isJournalOp(op) {
@@ -67,9 +68,9 @@ const journalMagicString = '#journal@v0\n';
67
68
  * Tracks various operations for the CoW backend
68
69
  * @internal
69
70
  */
70
- export class Journal {
71
- constructor(fs) {
72
- this.fs = fs;
71
+ export class Journal extends EventEmitter {
72
+ constructor() {
73
+ super(...arguments);
73
74
  this.entries = [];
74
75
  }
75
76
  toString() {
@@ -91,9 +92,12 @@ export class Journal {
91
92
  }
92
93
  this.entries.push({ op, path });
93
94
  }
95
+ return this;
94
96
  }
95
97
  add(op, path) {
96
98
  this.entries.push({ op, path });
99
+ this.emit('update', op, path);
100
+ this.emit(op, path);
97
101
  }
98
102
  has(op, path) {
99
103
  const test = JSON.stringify({ op, path });
@@ -125,18 +129,16 @@ export class CopyOnWriteFS extends FileSystem {
125
129
  await this.writable.ready();
126
130
  }
127
131
  constructor(
128
- /**
129
- * The file system that initially populates this file system.
130
- */
132
+ /** The file system that initially populates this file system. */
131
133
  readable,
132
- /**
133
- * The file system to write modified files to.
134
- */
135
- writable) {
134
+ /** The file system to write modified files to. */
135
+ writable,
136
+ /** The journal to use for persisting deletions */
137
+ journal = new Journal()) {
136
138
  super(0x62756c6c, readable.name);
137
139
  this.readable = readable;
138
140
  this.writable = writable;
139
- this.journal = new Journal(this);
141
+ this.journal = journal;
140
142
  if (writable.attributes.has('no_write')) {
141
143
  throw err(new ErrnoError(Errno.EINVAL, 'Writable file system can not be written to'));
142
144
  }
@@ -477,14 +479,7 @@ const _CopyOnWrite = {
477
479
  journal: { type: 'object', required: false },
478
480
  },
479
481
  create(options) {
480
- const fs = new CopyOnWriteFS(options.readable, options.writable);
481
- if (typeof options.journal == 'string') {
482
- fs.journal.fromString(options.journal);
483
- }
484
- if (Array.isArray(options.journal)) {
485
- fs.journal.entries.push(...options.journal);
486
- }
487
- return fs;
482
+ return new CopyOnWriteFS(options.readable, options.writable, options.journal);
488
483
  },
489
484
  };
490
485
  /**
@@ -3,7 +3,7 @@ import { _chown, Stats } from '../stats.js';
3
3
  import { config } from '../vfs/config.js';
4
4
  import * as c from '../vfs/constants.js';
5
5
  import { Errno, ErrnoError } from './error.js';
6
- import { log_deprecated } from './log.js';
6
+ import { err, log_deprecated } from './log.js';
7
7
  import '../polyfills.js';
8
8
  const maxByteLength = 0xffff; // 64 KiB
9
9
  const validFlags = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
@@ -191,11 +191,10 @@ export class PreloadFile extends File {
191
191
  This invariant is *not* maintained once the file starts getting modified.
192
192
  It only actually matters if file is readable, as writeable modes may truncate/append to file.
193
193
  */
194
- if (this.stats.size == _buffer.byteLength) {
194
+ if (this.stats.size == _buffer.byteLength)
195
195
  return;
196
- }
197
196
  if (!isWriteable(this.flag)) {
198
- throw new ErrnoError(Errno.EIO, `Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`, path);
197
+ throw err(new ErrnoError(Errno.EIO, `Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`, path));
199
198
  }
200
199
  this.stats.size = _buffer.byteLength;
201
200
  this.dirty = true;
@@ -515,7 +514,6 @@ export class LazyFile extends File {
515
514
  * Whether the file is open or closed
516
515
  */
517
516
  this.closed = false;
518
- this.dirty = true;
519
517
  }
520
518
  async sync() {
521
519
  if (this.closed)
@@ -64,6 +64,7 @@ import { dirname, join, parse, resolve } from './path.js';
64
64
  import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
65
65
  import { ReadStream, WriteStream } from './streams.js';
66
66
  import { FSWatcher, emitChange } from './watchers.js';
67
+ import { _throw } from 'utilium';
67
68
  export * as constants from './constants.js';
68
69
  export class FileHandle {
69
70
  constructor(fdOrFile, context) {
@@ -677,11 +678,8 @@ mkdir;
677
678
  export async function readdir(path, options) {
678
679
  options = typeof options === 'object' ? options : { encoding: options };
679
680
  path = await realpath.call(this, path);
680
- const handleError = (e) => {
681
- throw fixError(e, { [resolved]: path });
682
- };
683
681
  const { fs, path: resolved } = resolveMount(path, this);
684
- const stats = await fs.stat(resolved).catch(handleError);
682
+ const stats = await fs.stat(resolved).catch((e) => _throw(fixError(e, { [resolved]: path })));
685
683
  if (!stats) {
686
684
  throw ErrnoError.With('ENOENT', path, 'readdir');
687
685
  }
@@ -691,12 +689,18 @@ export async function readdir(path, options) {
691
689
  if (!stats.isDirectory()) {
692
690
  throw ErrnoError.With('ENOTDIR', path, 'readdir');
693
691
  }
694
- const entries = await fs.readdir(resolved).catch(handleError);
692
+ const entries = await fs.readdir(resolved).catch((e) => _throw(fixError(e, { [resolved]: path })));
695
693
  const values = [];
696
694
  const addEntry = async (entry) => {
697
695
  let entryStats;
698
696
  if ((options === null || options === void 0 ? void 0 : options.recursive) || (options === null || options === void 0 ? void 0 : options.withFileTypes)) {
699
- entryStats = await fs.stat(join(resolved, entry)).catch(handleError);
697
+ entryStats = await fs.stat(join(resolved, entry)).catch((e) => {
698
+ if (e.code == 'ENOENT')
699
+ return;
700
+ throw fixError(e, { [resolved]: path });
701
+ });
702
+ if (!entryStats)
703
+ return;
700
704
  }
701
705
  if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
702
706
  values.push(new Dirent(entry, entryStats));
package/dist/vfs/sync.js CHANGED
@@ -499,7 +499,13 @@ export function readdirSync(path, options) {
499
499
  // Iterate over entries and handle recursive case if needed
500
500
  const values = [];
501
501
  for (const entry of entries) {
502
- const entryStat = fs.statSync(join(resolved, entry));
502
+ let entryStat;
503
+ try {
504
+ entryStat = fs.statSync(join(resolved, entry));
505
+ }
506
+ catch {
507
+ continue;
508
+ }
503
509
  if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
504
510
  values.push(new Dirent(entry, entryStat));
505
511
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.10.2",
3
+ "version": "1.10.4",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",