@zenfs/core 2.6.1 → 2.6.3

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.
@@ -1,7 +1,8 @@
1
1
  import { pick } from 'utilium';
2
- import { _inode_fields, hasAccess } from '../internal/inode.js';
3
2
  import * as c from '../constants.js';
4
- const n1000 = BigInt(1000);
3
+ import { _inode_fields, hasAccess } from '../internal/inode.js';
4
+ import { _temporal } from '../utils.js';
5
+ const _nsMultiplier = BigInt(1_000_000);
5
6
  /**
6
7
  * Provides information about a particular entry in the file system.
7
8
  * Common code used by both Stats and BigIntStats.
@@ -56,6 +57,12 @@ export class StatsCommon {
56
57
  set atime(value) {
57
58
  this.atimeMs = this._convert(value.getTime());
58
59
  }
60
+ get atimeInstant() {
61
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.atimeMs));
62
+ }
63
+ set atimeInstant(value) {
64
+ this.atimeMs = this._convert(value.epochMilliseconds);
65
+ }
59
66
  /**
60
67
  * Time of last modification, since epoch
61
68
  */
@@ -66,6 +73,12 @@ export class StatsCommon {
66
73
  set mtime(value) {
67
74
  this.mtimeMs = this._convert(value.getTime());
68
75
  }
76
+ get mtimeInstant() {
77
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.mtimeMs));
78
+ }
79
+ set mtimeInstant(value) {
80
+ this.mtimeMs = this._convert(value.epochMilliseconds);
81
+ }
69
82
  /**
70
83
  * Time of last time file status was changed, since epoch
71
84
  */
@@ -76,6 +89,12 @@ export class StatsCommon {
76
89
  set ctime(value) {
77
90
  this.ctimeMs = this._convert(value.getTime());
78
91
  }
92
+ get ctimeInstant() {
93
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.ctimeMs));
94
+ }
95
+ set ctimeInstant(value) {
96
+ this.ctimeMs = this._convert(value.epochMilliseconds);
97
+ }
79
98
  /**
80
99
  * Time of file creation, since epoch
81
100
  */
@@ -86,6 +105,12 @@ export class StatsCommon {
86
105
  set birthtime(value) {
87
106
  this.birthtimeMs = this._convert(value.getTime());
88
107
  }
108
+ get birthtimeInstant() {
109
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.birthtimeMs));
110
+ }
111
+ set birthtimeInstant(value) {
112
+ this.birthtimeMs = this._convert(value.epochMilliseconds);
113
+ }
89
114
  /**
90
115
  * Size of the item in bytes.
91
116
  * For directories/symlinks, this is normally the size of the struct that represents the item.
@@ -156,16 +181,16 @@ export class StatsCommon {
156
181
  return hasAccess(context, this._isBigint ? new Stats(this) : this, mode);
157
182
  }
158
183
  get atimeNs() {
159
- return BigInt(this.atimeMs) * n1000;
184
+ return BigInt(this.atimeMs) * _nsMultiplier;
160
185
  }
161
186
  get mtimeNs() {
162
- return BigInt(this.mtimeMs) * n1000;
187
+ return BigInt(this.mtimeMs) * _nsMultiplier;
163
188
  }
164
189
  get ctimeNs() {
165
- return BigInt(this.ctimeMs) * n1000;
190
+ return BigInt(this.ctimeMs) * _nsMultiplier;
166
191
  }
167
192
  get birthtimeNs() {
168
- return BigInt(this.birthtimeMs) * n1000;
193
+ return BigInt(this.birthtimeMs) * _nsMultiplier;
169
194
  }
170
195
  }
171
196
  /**
@@ -218,6 +243,8 @@ export class StatsFs {
218
243
  bavail = 0;
219
244
  /** Total file nodes in file system. */
220
245
  files = c.size_max;
246
+ /** Fundamental file system block size. */
247
+ frsize = 4096;
221
248
  /** Free file nodes in file system. */
222
249
  ffree = c.size_max;
223
250
  }
@@ -237,6 +264,8 @@ export class BigIntStatsFs {
237
264
  bavail = BigInt(0);
238
265
  /** Total file nodes in file system. */
239
266
  files = BigInt(c.size_max);
267
+ /** Fundamental file system block size. */
268
+ frsize = BigInt(4096);
240
269
  /** Free file nodes in file system. */
241
270
  ffree = BigInt(c.size_max);
242
271
  }
@@ -10,23 +10,51 @@ export declare function renameSync(this: V_Context, oldPath: fs.PathLike, newPat
10
10
  * Test whether or not `path` exists by checking with the file system.
11
11
  */
12
12
  export declare function existsSync(this: V_Context, path: fs.PathLike): boolean;
13
- export declare function statSync(this: V_Context, path: fs.PathLike, options?: {
14
- bigint?: boolean;
13
+ export declare function statSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions & {
14
+ bigint?: false;
15
+ throwIfNoEntry?: true;
15
16
  }): Stats;
16
- export declare function statSync(this: V_Context, path: fs.PathLike, options: {
17
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
17
18
  bigint: true;
19
+ throwIfNoEntry?: true;
18
20
  }): BigIntStats;
21
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
22
+ bigint?: false;
23
+ throwIfNoEntry: false;
24
+ }): Stats | undefined;
25
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
26
+ bigint: true;
27
+ throwIfNoEntry: false;
28
+ }): BigIntStats | undefined;
29
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
30
+ throwIfNoEntry?: true;
31
+ }): Stats | BigIntStats;
32
+ export declare function statSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats | undefined;
19
33
  /**
20
34
  * Synchronous `lstat`.
21
35
  * `lstat()` is identical to `stat()`, except that if path is a symbolic link,
22
36
  * then the link itself is stat-ed, not the file that it refers to.
23
37
  */
24
- export declare function lstatSync(this: V_Context, path: fs.PathLike, options?: {
25
- bigint?: boolean;
38
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions & {
39
+ bigint?: false;
40
+ throwIfNoEntry?: true;
26
41
  }): Stats;
27
- export declare function lstatSync(this: V_Context, path: fs.PathLike, options: {
42
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
28
43
  bigint: true;
44
+ throwIfNoEntry?: true;
29
45
  }): BigIntStats;
46
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
47
+ bigint?: false;
48
+ throwIfNoEntry: false;
49
+ }): Stats | undefined;
50
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
51
+ bigint: true;
52
+ throwIfNoEntry: false;
53
+ }): BigIntStats | undefined;
54
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
55
+ throwIfNoEntry?: true;
56
+ }): Stats | BigIntStats;
57
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats | undefined;
30
58
  export declare function truncateSync(this: V_Context, path: fs.PathLike, len?: number | null): void;
31
59
  export declare function unlinkSync(this: V_Context, path: fs.PathLike): void;
32
60
  /**
@@ -94,7 +122,7 @@ export declare function fdatasyncSync(this: V_Context, fd: number): void;
94
122
  */
95
123
  export declare function writeSync(this: V_Context, fd: number, data: ArrayBufferView, offset?: number | null, length?: number | null, position?: number | null): number;
96
124
  export declare function writeSync(this: V_Context, fd: number, data: string, position?: number | null, encoding?: BufferEncoding | null): number;
97
- export declare function readSync(this: V_Context, fd: number, buffer: ArrayBufferView, options?: fs.ReadSyncOptions): number;
125
+ export declare function readSync(this: V_Context, fd: number, buffer: ArrayBufferView, options?: fs.ReadOptions): number;
98
126
  export declare function readSync(this: V_Context, fd: number, buffer: ArrayBufferView, offset: number, length: number, position?: fs.ReadPosition | null): number;
99
127
  export declare function fchownSync(this: V_Context, fd: number, uid: number, gid: number): void;
100
128
  export declare function fchmodSync(this: V_Context, fd: number, mode: number | string): void;
@@ -246,7 +274,7 @@ export declare function statfsSync(this: V_Context, path: fs.PathLike, options?:
246
274
  /**
247
275
  * Retrieves the files matching the specified pattern.
248
276
  */
249
- export declare function globSync(pattern: string | readonly string[]): string[];
250
- export declare function globSync(pattern: string | readonly string[], options: fs.GlobOptionsWithFileTypes): Dirent[];
251
- export declare function globSync(pattern: string | readonly string[], options: fs.GlobOptionsWithoutFileTypes): string[];
252
- export declare function globSync(pattern: string | readonly string[], options: fs.GlobOptions): Dirent[] | string[];
277
+ export declare function globSync(this: V_Context, pattern: string | readonly string[]): string[];
278
+ export declare function globSync(this: V_Context, pattern: string | readonly string[], options: fs.GlobOptionsWithFileTypes): Dirent[];
279
+ export declare function globSync(this: V_Context, pattern: string | readonly string[], options: fs.GlobOptionsWithoutFileTypes): string[];
280
+ export declare function globSync(this: V_Context, pattern: string | readonly string[], options: fs.GlobOptions): Dirent[] | string[];
package/dist/node/sync.js CHANGED
@@ -54,10 +54,11 @@ import { Buffer } from 'buffer';
54
54
  import { Errno, Exception, setUVMessage, UV } from 'kerium';
55
55
  import { encodeUTF8 } from 'utilium';
56
56
  import * as constants from '../constants.js';
57
+ import { contextOf } from '../internal/contexts.js';
57
58
  import { wrap } from '../internal/error.js';
58
59
  import { hasAccess, isDirectory } from '../internal/inode.js';
59
- import { dirname, join, matchesGlob } from '../path.js';
60
- import { _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
60
+ import { dirname, join, matchesGlob, relative, resolve } from '../path.js';
61
+ import { _isNoEntry, _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
61
62
  import { checkAccess } from '../vfs/config.js';
62
63
  import { deleteFD, fromFD, toFD } from '../vfs/file.js';
63
64
  import * as flags from '../vfs/flags.js';
@@ -88,12 +89,28 @@ export function existsSync(path) {
88
89
  }
89
90
  existsSync;
90
91
  export function statSync(path, options) {
91
- const stats = _sync.stat.call(this, path, false);
92
+ let stats;
93
+ try {
94
+ stats = _sync.stat.call(this, path, false);
95
+ }
96
+ catch (e) {
97
+ if (options?.throwIfNoEntry === false && _isNoEntry(e))
98
+ return;
99
+ throw e;
100
+ }
92
101
  return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
93
102
  }
94
103
  statSync;
95
104
  export function lstatSync(path, options) {
96
- const stats = _sync.stat.call(this, path, true);
105
+ let stats;
106
+ try {
107
+ stats = _sync.stat.call(this, path, true);
108
+ }
109
+ catch (e) {
110
+ if (options?.throwIfNoEntry === false && _isNoEntry(e))
111
+ return;
112
+ throw e;
113
+ }
97
114
  return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
98
115
  }
99
116
  lstatSync;
@@ -202,7 +219,6 @@ export function writeFileSync(path, data, _options = {}) {
202
219
  : _sync.open.call(this, path.toString(), {
203
220
  flag,
204
221
  mode: options.mode,
205
- preserveSymlinks: true,
206
222
  }), false);
207
223
  file.writeSync(encodedData, 0, encodedData.byteLength, 0);
208
224
  emitChange(this, 'change', path.toString());
@@ -237,7 +253,6 @@ export function appendFileSync(filename, data, _options = {}) {
237
253
  : _sync.open.call(this, normalizePath(filename), {
238
254
  flag,
239
255
  mode: options.mode,
240
- preserveSymlinks: true,
241
256
  });
242
257
  file.writeSync(encodedData, 0, encodedData.byteLength);
243
258
  if (typeof file != 'number')
@@ -567,9 +582,9 @@ export function mkdtempDisposableSync(prefix, options) {
567
582
  export function copyFileSync(source, destination, flags) {
568
583
  source = normalizePath(source);
569
584
  destination = normalizePath(destination);
570
- if (flags && flags & constants.COPYFILE_EXCL && existsSync(destination))
585
+ if (flags && flags & constants.COPYFILE_EXCL && existsSync.call(this, destination))
571
586
  throw UV('EEXIST', 'copyFile', destination);
572
- writeFileSync.call(this, destination, readFileSync(source));
587
+ writeFileSync.call(this, destination, readFileSync.call(this, source));
573
588
  emitChange(this, 'rename', destination.toString());
574
589
  }
575
590
  copyFileSync;
@@ -647,8 +662,16 @@ export function cpSync(source, destination, opts) {
647
662
  cpSync.call(this, join(source, dirent.name), join(destination, dirent.name), opts);
648
663
  }
649
664
  break;
650
- case constants.S_IFREG:
651
665
  case constants.S_IFLNK:
666
+ if (opts?.dereference) {
667
+ copyFileSync.call(this, source, destination);
668
+ }
669
+ else {
670
+ const link = readlinkSync.call(this, source, 'utf8');
671
+ symlinkSync.call(this, opts?.verbatimSymlinks ? link : resolve.call(this, dirname(source), link), destination);
672
+ }
673
+ break;
674
+ case constants.S_IFREG:
652
675
  copyFileSync.call(this, source, destination);
653
676
  break;
654
677
  case constants.S_IFBLK:
@@ -671,8 +694,10 @@ export function statfsSync(path, options) {
671
694
  }
672
695
  export function globSync(pattern, options = {}) {
673
696
  pattern = Array.isArray(pattern) ? pattern : [pattern];
674
- const { cwd = '/', withFileTypes = false, exclude = () => false } = options;
675
- const normalizedPatterns = pattern.map(p => p.replace(/^\/+/g, ''));
697
+ const $ = contextOf(this);
698
+ const { cwd = $.pwd, withFileTypes = false, exclude = () => false, followSymlinks = false } = options;
699
+ const base = resolve.call($, cwd instanceof URL ? cwd.pathname : cwd);
700
+ const normalizedPatterns = pattern.map(p => (p.startsWith('/') ? relative.call($, base, p) : p));
676
701
  const hasGlobStar = normalizedPatterns.some(p => p.includes('**'));
677
702
  const patternBases = normalizedPatterns.map(p => {
678
703
  const firstGlob = p.search(/[*?[\]{]/);
@@ -682,25 +707,30 @@ export function globSync(pattern, options = {}) {
682
707
  return lastSlash === -1 ? '' : p.slice(0, lastSlash);
683
708
  });
684
709
  const regexPatterns = normalizedPatterns.map(globToRegex);
710
+ function shouldDescend(path, relativePath) {
711
+ const isNamed = patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'));
712
+ const inScope = hasGlobStar || isNamed;
713
+ const stats = lstatSync.call($, path);
714
+ if (!stats.isSymbolicLink())
715
+ return inScope && stats.isDirectory();
716
+ return inScope && (followSymlinks || isNamed) && !!statSync.call($, path, { throwIfNoEntry: false })?.isDirectory();
717
+ }
685
718
  const results = [];
686
719
  function recursiveList(dir) {
687
- const entries = readdirSync(dir, { withFileTypes, encoding: 'utf8' });
720
+ const entries = readdirSync.call($, dir, { withFileTypes, encoding: 'utf8' });
688
721
  for (const entry of entries) {
689
722
  const fullPath = join(dir, withFileTypes ? entry.name : entry);
690
- if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
723
+ const relPath = relative.call($, base, fullPath);
724
+ if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, relPath)) : exclude((withFileTypes ? entry : relPath)))
691
725
  continue;
692
- const relativePath = fullPath.replace(/^\/+/g, '');
693
- if (statSync(fullPath).isDirectory()) {
694
- if (hasGlobStar || patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'))) {
695
- recursiveList(fullPath);
696
- }
697
- }
698
- if (regexPatterns.some(rx => rx.test(relativePath))) {
699
- results.push(withFileTypes ? entry : relativePath);
726
+ if (shouldDescend(fullPath, relPath))
727
+ recursiveList(fullPath);
728
+ if (regexPatterns.some(rx => rx.test(relPath))) {
729
+ results.push(withFileTypes ? entry : relPath);
700
730
  }
701
731
  }
702
732
  }
703
- recursiveList(cwd instanceof URL ? cwd.pathname : cwd);
733
+ recursiveList(base);
704
734
  return results;
705
735
  }
706
736
  globSync;
@@ -0,0 +1,99 @@
1
+ import { Buffer } from 'buffer';
2
+ import { EventEmitter } from 'eventemitter3';
3
+ import type { EventEmitter as NodeEventEmitter } from 'node:events';
4
+ import type * as fs from 'node:fs';
5
+ import { type FSContext } from '../internal/contexts.js';
6
+ interface FS {
7
+ open(path: string, flags: string, mode: fs.Mode, cb: (err: Error | null, fd: number) => void): void;
8
+ openSync(path: string, flags: string, mode: fs.Mode): number;
9
+ close(fd: number, cb: (err: Error | null) => void): void;
10
+ write(fd: number, data: Buffer, cb: (err: Error | null, n: number) => void): void;
11
+ write(fd: number, data: string, encoding: BufferEncoding, cb: (err: Error | null, n: number) => void): void;
12
+ writeSync(fd: number, data: Buffer): number;
13
+ writeSync(fd: number, data: string, encoding: BufferEncoding): number;
14
+ fsync(fd: number, cb: (err: Error | null) => void): void;
15
+ fsyncSync(fd: number): void;
16
+ mkdir(path: string, options: {
17
+ recursive: true;
18
+ }, cb: (err: Error | null) => void): void;
19
+ mkdirSync(path: string, options: {
20
+ recursive: true;
21
+ }): void;
22
+ }
23
+ declare const kUtf8StreamFS: unique symbol;
24
+ /**
25
+ * An optimized UTF-8 stream writer that allows for flushing all of the internal buffering on demand.
26
+ */
27
+ export declare class Utf8Stream extends EventEmitter<fs.Utf8StreamEventMap> implements NodeEventEmitter, fs.Utf8Stream {
28
+ #private;
29
+ ['constructor']: typeof Utf8Stream;
30
+ static [kUtf8StreamFS]: FS;
31
+ /** @hidden @internal */
32
+ static _withContext(ctx: FSContext): typeof Utf8Stream;
33
+ constructor(options?: fs.Utf8StreamOptions);
34
+ /**
35
+ * When `contentMode` is `utf8`, `data` must be a string. When it is `buffer`, `data` must be a `Buffer`.
36
+ * @returns Whether the internal buffer is below the high water mark
37
+ */
38
+ write(data: string | Buffer): boolean;
39
+ /**
40
+ * Writes the current buffer to the file if a write was not in progress.
41
+ * Does nothing if `minLength` is zero or if it is already writing.
42
+ */
43
+ flush(cb?: ((err: Error | null) => void) | null): void;
44
+ /**
45
+ * Flushes the buffered data synchronously. This is a costly operation.
46
+ */
47
+ flushSync(): void;
48
+ /**
49
+ * Reopen the file in place, useful for log rotation.
50
+ */
51
+ reopen(file?: fs.PathLike): void;
52
+ /**
53
+ * Close the stream gracefully, flushing the internal buffer before closing.
54
+ */
55
+ end(): void;
56
+ /**
57
+ * Close the stream immediately, without flushing the internal buffer.
58
+ */
59
+ destroy(): void;
60
+ /** The mode of the file that is being written to */
61
+ get mode(): fs.Mode;
62
+ /** The file that is being written to */
63
+ get file(): string;
64
+ /** The file descriptor that is being written to */
65
+ get fd(): number;
66
+ /** The minimum length of the internal buffer that is required to be full before flushing */
67
+ get minLength(): number;
68
+ /**
69
+ * The maximum length of the internal buffer.
70
+ * If a write would cause the buffer to exceed this, the data is dropped and a `drop` event is emitted with it.
71
+ */
72
+ get maxLength(): number;
73
+ /** Whether the stream is currently writing data to the file */
74
+ get writing(): boolean;
75
+ /** Whether the stream is writing synchronously or asynchronously */
76
+ get sync(): boolean;
77
+ /** Whether the stream performs a `fsyncSync` after every write */
78
+ get fsync(): boolean;
79
+ /** Whether the stream is appending to the file or truncating it */
80
+ get append(): boolean;
81
+ /** The number of milliseconds between flushes. `0` means no periodic flushes are performed. */
82
+ get periodicFlush(): number;
83
+ /** The type of data that can be written to the stream */
84
+ get contentMode(): 'utf8' | 'buffer';
85
+ /** Whether the directory for `file` is created if it does not exist */
86
+ get mkdir(): boolean;
87
+ [Symbol.dispose](): void;
88
+ on<T extends keyof fs.Utf8StreamEventMap>(event: T, fn: (...args: fs.Utf8StreamEventMap[T]) => void, context?: any): this;
89
+ addListener<T extends keyof fs.Utf8StreamEventMap>(event: T, fn: (...args: fs.Utf8StreamEventMap[T]) => void, context?: any): this;
90
+ once<T extends keyof fs.Utf8StreamEventMap>(event: T, fn: (...args: fs.Utf8StreamEventMap[T]) => void, context?: any): this;
91
+ off<T extends keyof fs.Utf8StreamEventMap>(event: T, fn?: (...args: any[]) => void, context?: any, once?: boolean): this;
92
+ removeListener<T extends keyof fs.Utf8StreamEventMap>(event: T, fn?: (...args: any[]) => void, context?: any, once?: boolean): this;
93
+ setMaxListeners(): never;
94
+ getMaxListeners(): never;
95
+ prependListener(): never;
96
+ prependOnceListener(): never;
97
+ rawListeners(): never;
98
+ }
99
+ export {};