@zenfs/core 2.3.1 → 2.3.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.
@@ -102,7 +102,7 @@ const { format } = new Intl.NumberFormat('en-US', {
102
102
  });
103
103
  let MetadataEntry = (() => {
104
104
  var _a, _b, _c, _d;
105
- let _classDecorators = [struct(packed)];
105
+ let _classDecorators = [struct(packed, { name: 'MetadataEntry' })];
106
106
  let _classDescriptor;
107
107
  let _classExtraInitializers = [];
108
108
  let _classThis;
@@ -49,7 +49,7 @@ export const rootIno = 0;
49
49
  const maxDynamicData = 3968;
50
50
  let Attribute = (() => {
51
51
  var _a, _b;
52
- let _classDecorators = [struct(packed)];
52
+ let _classDecorators = [struct(packed, { name: 'Attribute' })];
53
53
  let _classDescriptor;
54
54
  let _classExtraInitializers = [];
55
55
  let _classThis;
package/dist/vfs/dir.d.ts CHANGED
@@ -2,13 +2,56 @@ import type { Dir as _Dir, Dirent as _Dirent } from 'node:fs';
2
2
  import type { V_Context } from '../context.js';
3
3
  import type { InodeLike } from '../internal/inode.js';
4
4
  import type { Callback } from '../utils.js';
5
- export declare class Dirent<Name extends string | Buffer = string> implements _Dirent<Name> {
6
- path: string;
7
- protected stats: InodeLike;
8
- protected readonly encoding?: (BufferEncoding | "buffer" | null) | undefined;
5
+ import { Buffer } from 'buffer';
6
+ import { BufferView } from 'utilium/buffer.js';
7
+ /**
8
+ * @see `DT_*` in `dirent.h`
9
+ */
10
+ export declare enum DirType {
11
+ UNKNOWN = 0,
12
+ FIFO = 1,
13
+ CHR = 2,
14
+ DIR = 4,
15
+ BLK = 6,
16
+ REG = 8,
17
+ LNK = 10,
18
+ SOCK = 12,
19
+ WHT = 14
20
+ }
21
+ /**
22
+ * Converts a file mode to a directory type.
23
+ * @see `IFTODT` in `dirent.h`
24
+ */
25
+ export declare function ifToDt(mode: number): DirType;
26
+ /**
27
+ * Converts a directory type to a file mode.
28
+ * @see `DTTOIF` in `dirent.h`
29
+ */
30
+ export declare function dtToIf(dt: DirType): number;
31
+ export declare class Dirent<Name extends string | Buffer = string, TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> extends BufferView<TArrayBuffer> implements _Dirent<Name> {
32
+ protected accessor ino: number;
33
+ /** Reserved for 64-bit inodes */
34
+ private accessor _ino;
35
+ protected accessor type: DirType;
36
+ protected accessor _name: Uint8Array;
9
37
  get name(): Name;
10
- constructor(path: string, stats: InodeLike, encoding?: (BufferEncoding | "buffer" | null) | undefined);
38
+ /**
39
+ * @internal @protected
40
+ */
41
+ _encoding?: BufferEncoding | 'buffer' | null;
42
+ /**
43
+ * @internal @protected
44
+ */
45
+ _parentPath: string;
11
46
  get parentPath(): string;
47
+ /**
48
+ * @deprecated Removed in Node v24, use `parentPath` instead.
49
+ */
50
+ get path(): string;
51
+ /**
52
+ * @internal
53
+ */
54
+ static from(path: string, stats: InodeLike, encoding?: BufferEncoding | 'buffer' | null): Dirent;
12
55
  isFile(): boolean;
13
56
  isDirectory(): boolean;
14
57
  isBlockDevice(): boolean;
package/dist/vfs/dir.js CHANGED
@@ -1,46 +1,184 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { Buffer } from 'buffer';
1
36
  import { withErrno } from 'kerium';
2
- import { isBlockDevice, isCharacterDevice, isDirectory, isFIFO, isFile, isSocket, isSymbolicLink } from '../internal/inode.js';
3
- import { basename } from '../path.js';
37
+ import { packed, sizeof, struct, types as t } from 'memium';
38
+ import { warn } from 'kerium/log';
39
+ import { encodeUTF8 } from 'utilium';
40
+ import { BufferView } from 'utilium/buffer.js';
41
+ import { basename, dirname } from '../path.js';
4
42
  import { readdir } from './promises.js';
5
43
  import { readdirSync } from './sync.js';
6
- export class Dirent {
7
- path;
8
- stats;
9
- encoding;
10
- get name() {
11
- const name = Buffer.from(basename(this.path));
12
- return (this.encoding == 'buffer' ? name : name.toString(this.encoding));
13
- }
14
- constructor(path, stats, encoding) {
15
- this.path = path;
16
- this.stats = stats;
17
- this.encoding = encoding;
18
- }
19
- get parentPath() {
20
- return this.path;
21
- }
22
- isFile() {
23
- return isFile(this.stats);
24
- }
25
- isDirectory() {
26
- return isDirectory(this.stats);
27
- }
28
- isBlockDevice() {
29
- return isBlockDevice(this.stats);
30
- }
31
- isCharacterDevice() {
32
- return isCharacterDevice(this.stats);
33
- }
34
- isSymbolicLink() {
35
- return isSymbolicLink(this.stats);
36
- }
37
- isFIFO() {
38
- return isFIFO(this.stats);
39
- }
40
- isSocket() {
41
- return isSocket(this.stats);
42
- }
44
+ /**
45
+ * @see `DT_*` in `dirent.h`
46
+ */
47
+ export var DirType;
48
+ (function (DirType) {
49
+ DirType[DirType["UNKNOWN"] = 0] = "UNKNOWN";
50
+ DirType[DirType["FIFO"] = 1] = "FIFO";
51
+ DirType[DirType["CHR"] = 2] = "CHR";
52
+ DirType[DirType["DIR"] = 4] = "DIR";
53
+ DirType[DirType["BLK"] = 6] = "BLK";
54
+ DirType[DirType["REG"] = 8] = "REG";
55
+ DirType[DirType["LNK"] = 10] = "LNK";
56
+ DirType[DirType["SOCK"] = 12] = "SOCK";
57
+ DirType[DirType["WHT"] = 14] = "WHT";
58
+ })(DirType || (DirType = {}));
59
+ /**
60
+ * Converts a file mode to a directory type.
61
+ * @see `IFTODT` in `dirent.h`
62
+ */
63
+ export function ifToDt(mode) {
64
+ return ((mode & 0o170000) >> 12);
65
+ }
66
+ /**
67
+ * Converts a directory type to a file mode.
68
+ * @see `DTTOIF` in `dirent.h`
69
+ */
70
+ export function dtToIf(dt) {
71
+ return dt << 12;
43
72
  }
73
+ let Dirent = (() => {
74
+ var _a, _b, _c;
75
+ let _classDecorators = [struct(packed, { name: 'Dirent' })];
76
+ let _classDescriptor;
77
+ let _classExtraInitializers = [];
78
+ let _classThis;
79
+ let _classSuper = BufferView;
80
+ let _ino_decorators;
81
+ let _ino_initializers = [];
82
+ let _ino_extraInitializers = [];
83
+ let __ino_decorators;
84
+ let __ino_initializers = [];
85
+ let __ino_extraInitializers = [];
86
+ let _type_decorators;
87
+ let _type_initializers = [];
88
+ let _type_extraInitializers = [];
89
+ let __name_decorators;
90
+ let __name_initializers = [];
91
+ let __name_extraInitializers = [];
92
+ var Dirent = class extends _classSuper {
93
+ static { _classThis = this; }
94
+ static {
95
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
96
+ _ino_decorators = [(_a = t).uint32.bind(_a)];
97
+ __ino_decorators = [(_b = t).uint32.bind(_b)];
98
+ _type_decorators = [(_c = t).uint8.bind(_c)];
99
+ __name_decorators = [t.char(256)];
100
+ __esDecorate(this, null, _ino_decorators, { kind: "accessor", 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);
101
+ __esDecorate(this, null, __ino_decorators, { kind: "accessor", 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);
102
+ __esDecorate(this, null, _type_decorators, { kind: "accessor", name: "type", static: false, private: false, access: { has: obj => "type" in obj, get: obj => obj.type, set: (obj, value) => { obj.type = value; } }, metadata: _metadata }, _type_initializers, _type_extraInitializers);
103
+ __esDecorate(this, null, __name_decorators, { kind: "accessor", name: "_name", static: false, private: false, access: { has: obj => "_name" in obj, get: obj => obj._name, set: (obj, value) => { obj._name = value; } }, metadata: _metadata }, __name_initializers, __name_extraInitializers);
104
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
105
+ Dirent = _classThis = _classDescriptor.value;
106
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
107
+ __runInitializers(_classThis, _classExtraInitializers);
108
+ }
109
+ #ino_accessor_storage = __runInitializers(this, _ino_initializers, void 0);
110
+ get ino() { return this.#ino_accessor_storage; }
111
+ set ino(value) { this.#ino_accessor_storage = value; }
112
+ #_ino_accessor_storage = (__runInitializers(this, _ino_extraInitializers), __runInitializers(this, __ino_initializers, void 0));
113
+ /** Reserved for 64-bit inodes */
114
+ get _ino() { return this.#_ino_accessor_storage; }
115
+ set _ino(value) { this.#_ino_accessor_storage = value; }
116
+ #type_accessor_storage = (__runInitializers(this, __ino_extraInitializers), __runInitializers(this, _type_initializers, void 0));
117
+ get type() { return this.#type_accessor_storage; }
118
+ set type(value) { this.#type_accessor_storage = value; }
119
+ #_name_accessor_storage = (__runInitializers(this, _type_extraInitializers), __runInitializers(this, __name_initializers, void 0));
120
+ get _name() { return this.#_name_accessor_storage; }
121
+ set _name(value) { this.#_name_accessor_storage = value; }
122
+ get name() {
123
+ const end = (this._name.indexOf(0) + 1 || 256) - 1;
124
+ const name = Buffer.from(this._name.subarray(0, end));
125
+ return (this._encoding == 'buffer' ? name : name.toString(this._encoding));
126
+ }
127
+ /**
128
+ * @internal @protected
129
+ */
130
+ _encoding = __runInitializers(this, __name_extraInitializers);
131
+ /**
132
+ * @internal @protected
133
+ */
134
+ _parentPath;
135
+ get parentPath() {
136
+ return this._parentPath;
137
+ }
138
+ /**
139
+ * @deprecated Removed in Node v24, use `parentPath` instead.
140
+ */
141
+ get path() {
142
+ warn('Dirent.path was removed in Node v24, use parentPath instead');
143
+ return this._parentPath;
144
+ }
145
+ /**
146
+ * @internal
147
+ */
148
+ static from(path, stats, encoding) {
149
+ const dirent = new Dirent(new ArrayBuffer(sizeof(Dirent) + 1));
150
+ dirent._parentPath = dirname(path);
151
+ dirent._name = encodeUTF8(basename(path));
152
+ dirent.ino = stats.ino;
153
+ dirent.type = ifToDt(stats.mode);
154
+ dirent._encoding = encoding;
155
+ return dirent;
156
+ }
157
+ isFile() {
158
+ return this.type === DirType.REG;
159
+ }
160
+ isDirectory() {
161
+ return this.type === DirType.DIR;
162
+ }
163
+ isBlockDevice() {
164
+ return this.type === DirType.BLK;
165
+ }
166
+ isCharacterDevice() {
167
+ return this.type === DirType.CHR;
168
+ }
169
+ isSymbolicLink() {
170
+ return this.type === DirType.LNK;
171
+ }
172
+ isFIFO() {
173
+ return this.type === DirType.FIFO;
174
+ }
175
+ isSocket() {
176
+ return this.type === DirType.SOCK;
177
+ }
178
+ };
179
+ return Dirent = _classThis;
180
+ })();
181
+ export { Dirent };
44
182
  /**
45
183
  * A class representing a directory stream.
46
184
  */
package/dist/vfs/ioctl.js CHANGED
@@ -87,7 +87,7 @@ var XFlag;
87
87
  })(XFlag || (XFlag = {}));
88
88
  let fsxattr = (() => {
89
89
  var _a, _b, _c, _d, _e;
90
- let _classDecorators = [struct()];
90
+ let _classDecorators = [struct({ name: 'fsxattr' })];
91
91
  let _classDescriptor;
92
92
  let _classExtraInitializers = [];
93
93
  let _classThis;
@@ -11,6 +11,7 @@ import type { FileContents, ReaddirOptions } from './types.js';
11
11
  import { Buffer } from 'buffer';
12
12
  import '../polyfills.js';
13
13
  import { Dir, Dirent } from './dir.js';
14
+ import { SyncHandle } from './file.js';
14
15
  import { BigIntStats, Stats } from './stats.js';
15
16
  import { ReadStream, WriteStream } from './streams.js';
16
17
  export * as constants from './constants.js';
@@ -18,10 +19,6 @@ export declare class FileHandle implements promises.FileHandle {
18
19
  protected context: V_Context;
19
20
  readonly fd: number;
20
21
  protected _buffer?: Uint8Array;
21
- /**
22
- * Current position
23
- */
24
- protected _position: number;
25
22
  /**
26
23
  * Get the current file position.
27
24
  *
@@ -42,15 +39,16 @@ export declare class FileHandle implements promises.FileHandle {
42
39
  */
43
40
  protected closed: boolean;
44
41
  /** The path relative to the context's root */
45
- readonly path: string;
42
+ get path(): string;
46
43
  /** The internal FS associated with the handle */
47
- protected readonly fs: FileSystem;
44
+ protected get fs(): FileSystem;
48
45
  /** The path relative to the `FileSystem`'s root */
49
- readonly internalPath: string;
46
+ get internalPath(): string;
50
47
  /** The flag the handle was opened with */
51
- readonly flag: number;
48
+ get flag(): number;
52
49
  /** Stats for the handle */
53
- readonly inode: InodeLike;
50
+ get inode(): InodeLike;
51
+ protected _sync: SyncHandle;
54
52
  constructor(context: V_Context, fd: number);
55
53
  private get _isSync();
56
54
  private _emitChange;
@@ -52,10 +52,9 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
52
52
  });
53
53
  import { Buffer } from 'buffer';
54
54
  import { Exception, rethrow, setUVMessage, UV } from 'kerium';
55
- import { decodeUTF8, pick } from 'utilium';
56
55
  import { defaultContext } from '../internal/contexts.js';
57
56
  import { hasAccess, InodeFlags, isBlockDevice, isCharacterDevice, isDirectory, isSymbolicLink } from '../internal/inode.js';
58
- import { dirname, join, matchesGlob, parse, resolve } from '../path.js';
57
+ import { basename, dirname, join, matchesGlob, parse, resolve } from '../path.js';
59
58
  import '../polyfills.js';
60
59
  import { createInterface } from '../readline.js';
61
60
  import { __assertType, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
@@ -73,10 +72,6 @@ export class FileHandle {
73
72
  context;
74
73
  fd;
75
74
  _buffer;
76
- /**
77
- * Current position
78
- */
79
- _position = 0;
80
75
  /**
81
76
  * Get the current file position.
82
77
  *
@@ -87,10 +82,10 @@ export class FileHandle {
87
82
  * @returns The current file position.
88
83
  */
89
84
  get position() {
90
- return this.flag & constants.O_APPEND ? this.inode.size : this._position;
85
+ return this._sync.position;
91
86
  }
92
87
  set position(value) {
93
- this._position = value;
88
+ this._sync.position = value;
94
89
  }
95
90
  /**
96
91
  * Whether the file has changes which have not been written to the FS
@@ -101,20 +96,30 @@ export class FileHandle {
101
96
  */
102
97
  closed = false;
103
98
  /** The path relative to the context's root */
104
- path;
99
+ get path() {
100
+ return this._sync.path;
101
+ }
105
102
  /** The internal FS associated with the handle */
106
- fs;
103
+ get fs() {
104
+ return this._sync.fs;
105
+ }
107
106
  /** The path relative to the `FileSystem`'s root */
108
- internalPath;
107
+ get internalPath() {
108
+ return this._sync.internalPath;
109
+ }
109
110
  /** The flag the handle was opened with */
110
- flag;
111
+ get flag() {
112
+ return this._sync.flag;
113
+ }
111
114
  /** Stats for the handle */
112
- inode;
115
+ get inode() {
116
+ return this._sync.inode;
117
+ }
118
+ _sync;
113
119
  constructor(context, fd) {
114
120
  this.context = context;
115
121
  this.fd = fd;
116
- const sync = fromFD(context, fd);
117
- Object.assign(this, pick(sync, 'path', 'fs', 'internalPath', 'flag', 'inode'));
122
+ this._sync = fromFD(context, fd);
118
123
  }
119
124
  get _isSync() {
120
125
  return !!(this.flag & constants.O_SYNC || this.inode.flags & InodeFlags.Sync);
@@ -246,7 +251,7 @@ export class FileHandle {
246
251
  if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size) {
247
252
  end = position + Math.max(this.inode.size - position, 0);
248
253
  }
249
- this._position = end;
254
+ this._sync.position = end;
250
255
  const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
251
256
  await this.fs.read(this.internalPath, uint8.subarray(offset, offset + length), position, end);
252
257
  if (this._isSync)
@@ -348,7 +353,7 @@ export class FileHandle {
348
353
  this.inode.size = end;
349
354
  this.inode.mtimeMs = Date.now();
350
355
  this.inode.ctimeMs = Date.now();
351
- this._position = position + slice.byteLength;
356
+ this._sync.position = position + slice.byteLength;
352
357
  await this.fs.write(this.internalPath, slice, position);
353
358
  if (this._isSync)
354
359
  await this.sync();
@@ -534,8 +539,9 @@ export async function stat(path, options) {
534
539
  stat;
535
540
  export async function lstat(path, options) {
536
541
  path = normalizePath(path);
537
- const { fs, path: resolved } = resolveMount(path, this);
538
542
  const $ex = { syscall: 'lstat', path };
543
+ path = join(await realpath.call(this, dirname(path)), basename(path));
544
+ const { fs, path: resolved } = resolveMount(path, this);
539
545
  const stats = await fs.stat(resolved).catch(rethrow($ex));
540
546
  if (checkAccess && !hasAccess(this, stats, constants.R_OK))
541
547
  throw UV('EACCES', $ex);
@@ -747,9 +753,9 @@ export async function mkdir(path, options) {
747
753
  return dirs[0][0];
748
754
  }
749
755
  mkdir;
750
- export async function readdir(path, options) {
756
+ export async function readdir(_path, options) {
751
757
  const opt = typeof options === 'object' && options != null ? options : { encoding: options, withFileTypes: false, recursive: false };
752
- path = await realpath.call(this, path);
758
+ const path = await realpath.call(this, _path);
753
759
  const { fs, path: resolved } = resolveMount(path, this);
754
760
  const $ex = { syscall: 'readdir', path };
755
761
  const stats = await fs.stat(resolved).catch(rethrow({ syscall: 'stat', path }));
@@ -773,7 +779,7 @@ export async function readdir(path, options) {
773
779
  return;
774
780
  }
775
781
  if (opt.withFileTypes) {
776
- values.push(new Dirent(entry, entryStats, opt.encoding));
782
+ values.push(Dirent.from(join(_path.toString(), entry), entryStats, opt.encoding));
777
783
  }
778
784
  else if (opt.encoding == 'buffer') {
779
785
  values.push(Buffer.from(entry));
@@ -783,19 +789,9 @@ export async function readdir(path, options) {
783
789
  }
784
790
  if (!opt.recursive || !isDirectory(entryStats))
785
791
  return;
786
- for (const subEntry of await readdir.call(this, join(path, entry), opt)) {
787
- if (subEntry instanceof Dirent) {
788
- subEntry.path = join(entry, subEntry.path);
789
- values.push(subEntry);
790
- }
791
- else if (Buffer.isBuffer(subEntry)) {
792
- // Convert Buffer to string, prefix with the full path
793
- values.push(Buffer.from(join(entry, decodeUTF8(subEntry))));
794
- }
795
- else {
796
- values.push(join(entry, subEntry));
797
- }
798
- }
792
+ const children = await fs.readdir(join(resolved, entry)).catch(rethrow({ syscall: 'readdir', path: join(path, entry) }));
793
+ for (const child of children)
794
+ await addEntry(join(entry, child));
799
795
  };
800
796
  await Promise.all(entries.map(addEntry));
801
797
  return values;
@@ -1232,7 +1228,7 @@ export function glob(pattern, opt) {
1232
1228
  async function* recursiveList(dir) {
1233
1229
  const entries = await readdir(dir, { withFileTypes, encoding: 'utf8' });
1234
1230
  for (const entry of entries) {
1235
- const fullPath = withFileTypes ? entry.path : dir + '/' + entry;
1231
+ const fullPath = withFileTypes ? join(entry.parentPath, entry.name) : dir + '/' + entry;
1236
1232
  if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
1237
1233
  continue;
1238
1234
  /**
package/dist/vfs/sync.js CHANGED
@@ -52,11 +52,11 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
52
52
  });
53
53
  import { Buffer } from 'buffer';
54
54
  import { Errno, Exception, setUVMessage, UV } from 'kerium';
55
- import { decodeUTF8, encodeUTF8 } from 'utilium';
55
+ import { encodeUTF8 } from 'utilium';
56
56
  import { defaultContext } from '../internal/contexts.js';
57
57
  import { wrap } from '../internal/error.js';
58
58
  import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
59
- import { dirname, join, matchesGlob, parse, resolve } from '../path.js';
59
+ import { basename, dirname, join, matchesGlob, parse, resolve } from '../path.js';
60
60
  import { __assertType, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
61
61
  import { checkAccess } from './config.js';
62
62
  import * as constants from './constants.js';
@@ -139,7 +139,8 @@ export function statSync(path, options) {
139
139
  statSync;
140
140
  export function lstatSync(path, options) {
141
141
  path = normalizePath(path);
142
- const { fs, path: resolved } = resolveMount(path, this);
142
+ const real = join(realpathSync.call(this, dirname(path)), basename(path));
143
+ const { fs, path: resolved } = resolveMount(real, this);
143
144
  const stats = wrap(fs, 'statSync', path)(resolved);
144
145
  if (checkAccess && !hasAccess(this, stats, constants.R_OK))
145
146
  throw UV('EACCES', { syscall: 'lstat', path });
@@ -485,16 +486,18 @@ export function readdirSync(path, options) {
485
486
  const entries = wrap(fs, 'readdirSync', path)(resolved);
486
487
  // Iterate over entries and handle recursive case if needed
487
488
  const values = [];
488
- for (const entry of entries) {
489
+ const addEntry = (entry) => {
489
490
  let entryStat;
490
491
  try {
491
492
  entryStat = fs.statSync(join(resolved, entry));
492
493
  }
493
- catch {
494
- continue;
494
+ catch (e) {
495
+ if (e.code == 'ENOENT')
496
+ return;
497
+ throw setUVMessage(Object.assign(e, { syscall: 'stat', path: join(path, entry) }));
495
498
  }
496
499
  if (options?.withFileTypes) {
497
- values.push(new Dirent(entry, entryStat, options.encoding));
500
+ values.push(Dirent.from(entry, entryStat, options.encoding));
498
501
  }
499
502
  else if (options?.encoding == 'buffer') {
500
503
  values.push(Buffer.from(entry));
@@ -503,20 +506,13 @@ export function readdirSync(path, options) {
503
506
  values.push(entry);
504
507
  }
505
508
  if (!isDirectory(entryStat) || !options?.recursive)
506
- continue;
507
- for (const subEntry of readdirSync.call(this, join(path, entry), options)) {
508
- if (subEntry instanceof Dirent) {
509
- subEntry.path = join(entry, subEntry.path);
510
- values.push(subEntry);
511
- }
512
- else if (Buffer.isBuffer(subEntry)) {
513
- values.push(Buffer.from(join(entry, decodeUTF8(subEntry))));
514
- }
515
- else {
516
- values.push(join(entry, subEntry));
517
- }
518
- }
519
- }
509
+ return;
510
+ const children = wrap(fs, 'readdirSync', join(path, entry))(join(resolved, entry));
511
+ for (const child of children)
512
+ addEntry(join(entry, child));
513
+ };
514
+ for (const entry of entries)
515
+ addEntry(entry);
520
516
  return values;
521
517
  }
522
518
  readdirSync;
@@ -862,7 +858,7 @@ export function globSync(pattern, options = {}) {
862
858
  function recursiveList(dir) {
863
859
  const entries = readdirSync(dir, { withFileTypes, encoding: 'utf8' });
864
860
  for (const entry of entries) {
865
- const fullPath = withFileTypes ? entry.path : dir + '/' + entry;
861
+ const fullPath = withFileTypes ? join(entry.parentPath, entry.name) : dir + '/' + entry;
866
862
  if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
867
863
  continue;
868
864
  /**
@@ -872,7 +868,7 @@ export function globSync(pattern, options = {}) {
872
868
  recursiveList(fullPath);
873
869
  }
874
870
  if (regexPatterns.some(pattern => pattern.test(fullPath.replace(/^\/+/g, '')))) {
875
- results.push(withFileTypes ? entry.path : fullPath.replace(/^\/+/g, ''));
871
+ results.push(withFileTypes ? entry : fullPath.replace(/^\/+/g, ''));
876
872
  }
877
873
  }
878
874
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
package/scripts/test.js CHANGED
@@ -40,13 +40,14 @@ if (options.help) {
40
40
  Paths: The setup files to run tests on
41
41
 
42
42
  Behavior:
43
- -a, --auto Automatically detect setup files
44
- -b, --build Run the npm build script prior to running tests
45
- -c, --common Also run tests not specific to any backend
46
- -e, --exit-on-fail If any tests suites fail, exit immediately
47
- -t, --test <glob> Which FS test suite(s) to run
48
- -f, --force Whether to use --test-force-exit
49
- -I, --inspect Use the inspector for debugging
43
+ -a, --auto Automatically detect setup files
44
+ -b, --build Run the npm build script prior to running tests
45
+ -c, --common Also run tests not specific to any backend
46
+ -e, --exit-on-fail If any tests suites fail, exit immediately
47
+ -t, --test <glob> Which FS test suite(s) to run
48
+ -f, --force Whether to use --test-force-exit
49
+ -I, --inspect Use the inspector for debugging
50
+ -s, --skip <pattern> Skip tests with names matching the given pattern.
50
51
 
51
52
  Output:
52
53
  -h, --help Outputs this help message
@@ -211,11 +212,11 @@ for (const setupFile of positionals) {
211
212
  execSync(
212
213
  [
213
214
  'tsx --trace-deprecation',
214
- options.inspect ? 'inspect' : '',
215
+ options.inspect ? '--inspect' : '',
215
216
  '--test --experimental-test-coverage',
216
217
  options.force ? '--test-force-exit' : '',
217
218
  options.skip ? `--test-skip-pattern=${options.skip}` : '',
218
- testsGlob,
219
+ `'${testsGlob.replaceAll("'", "\\'")}'`,
219
220
  process.env.CMD,
220
221
  ].join(' '),
221
222
  {
@@ -16,15 +16,15 @@ for (const file of testFiles) {
16
16
  suite('Dirent', () => {
17
17
  test('name and parentPath getters', async () => {
18
18
  const stats = await fs.promises.lstat(testFile);
19
- const dirent = new fs.Dirent(testFile, stats);
19
+ const dirent = fs.Dirent.from(testFile, stats);
20
20
 
21
21
  assert.equal(dirent.name, testFile);
22
- assert.equal(dirent.parentPath, testFile);
22
+ assert.equal(dirent.parentPath, '.');
23
23
  });
24
24
 
25
25
  test('isFile', async () => {
26
26
  const fileStats = await fs.promises.lstat(testFile);
27
- const fileDirent = new fs.Dirent(testFile, fileStats);
27
+ const fileDirent = fs.Dirent.from(testFile, fileStats);
28
28
 
29
29
  assert(fileDirent.isFile());
30
30
  assert(!fileDirent.isDirectory());
@@ -32,7 +32,7 @@ suite('Dirent', () => {
32
32
 
33
33
  test('isDirectory', async () => {
34
34
  const dirStats = await fs.promises.lstat('test-directory');
35
- const dirDirent = new fs.Dirent('test-directory', dirStats);
35
+ const dirDirent = fs.Dirent.from('test-directory', dirStats);
36
36
 
37
37
  assert(!dirDirent.isFile());
38
38
  assert(dirDirent.isDirectory());
@@ -40,14 +40,14 @@ suite('Dirent', () => {
40
40
 
41
41
  test('isSymbolicLink', async () => {
42
42
  const symlinkStats = await fs.promises.lstat('test-symlink');
43
- const symlinkDirent = new fs.Dirent('test-symlink', symlinkStats);
43
+ const symlinkDirent = fs.Dirent.from('test-symlink', symlinkStats);
44
44
 
45
45
  assert(symlinkDirent.isSymbolicLink());
46
46
  });
47
47
 
48
48
  test('other methods return false', async () => {
49
49
  const fileStats = await fs.promises.lstat(testFile);
50
- const fileDirent = new fs.Dirent(testFile, fileStats);
50
+ const fileDirent = fs.Dirent.from(testFile, fileStats);
51
51
 
52
52
  assert(!fileDirent.isBlockDevice());
53
53
  assert(!fileDirent.isCharacterDevice());
@@ -1,4 +1,5 @@
1
1
  import assert from 'node:assert/strict';
2
+ import { join } from 'node:path/posix';
2
3
  import { suite, test } from 'node:test';
3
4
  import { fs } from '../common.js';
4
5
 
@@ -143,10 +144,12 @@ suite('Directories', () => {
143
144
 
144
145
  test('readdir returns Dirent recursively', async () => {
145
146
  const entries = await fs.promises.readdir(testDir, { recursive: true, withFileTypes: true });
146
- const paths = entries.map(entry => entry.path).sort();
147
- assert.equal(paths[0], 'file1.txt');
148
- assert.equal(paths[4], 'subdir1/file4.txt');
149
- assert.equal(paths[8], 'subdir2/file5.txt');
147
+ entries.sort((a, b) => join(a.parentPath, a.name).localeCompare(join(b.parentPath, b.name)));
148
+ const values = entries.map(entry => [entry.parentPath, entry.name]);
149
+
150
+ assert.deepEqual(values[0], [testDir, 'file1.txt']);
151
+ assert.deepEqual(values[4], [join(testDir, 'subdir1'), 'file4.txt']);
152
+ assert.deepEqual(values[8], [join(testDir, 'subdir2'), 'file5.txt']);
150
153
  });
151
154
 
152
155
  test('readdirSync returns files recursively', () => {
@@ -18,6 +18,16 @@ suite('Links', () => {
18
18
  assert(stats.isSymbolicLink());
19
19
  });
20
20
 
21
+ test('lstat file inside symlinked directory', async () => {
22
+ // @zenfs/core#241
23
+ await fs.promises.mkdir('/a');
24
+ await fs.promises.writeFile('/a/hello.txt', 'hello world');
25
+ await fs.promises.symlink('/a', '/b');
26
+
27
+ const stat = await fs.promises.lstat('/b/hello.txt');
28
+ assert(stat.isFile());
29
+ });
30
+
21
31
  test('readlink', async () => {
22
32
  const destination = await fs.promises.readlink(symlink);
23
33
  assert.equal(destination, target);
@@ -1,7 +1,9 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import assert from 'node:assert/strict';
3
+ import type { OpenMode, PathLike } from 'node:fs';
3
4
  import { suite, test } from 'node:test';
4
- import { fs } from '../common.js';
5
+ import { promisify } from 'node:util';
6
+ import { fs, type Callback } from '../common.js';
5
7
 
6
8
  const filepath = 'x.txt';
7
9
  const expected = 'xyz\n';
@@ -65,4 +67,25 @@ suite('read', () => {
65
67
  assert.equal(buffer.subarray(10, buffer.length).toString(), expected);
66
68
  assert.equal(bytesRead, expected.length);
67
69
  });
70
+
71
+ test('read using callback API', async () => {
72
+ // @zenfs/core#239
73
+ const path = '/text.txt';
74
+
75
+ fs.writeFileSync(path, 'hello world');
76
+ const fd: number = (await promisify<PathLike, OpenMode, number | string>(fs.open)(path, 0, 0)) as any;
77
+
78
+ const read = promisify(fs.read);
79
+
80
+ const buf = Buffer.alloc(1024);
81
+ const n0 = await read(fd, buf, 0, 1024, undefined);
82
+ assert.equal(n0, 11);
83
+ assert.equal(buf.subarray(0, n0).toString('utf8'), 'hello world');
84
+
85
+ const n1 = await read(fd, buf, 0, 1024, undefined);
86
+ assert.equal(n1, 0);
87
+ assert.equal(buf.subarray(0, n1).toString('utf8'), '');
88
+
89
+ await promisify(fs.close)(fd);
90
+ });
68
91
  });