@zenfs/core 1.1.0 → 1.1.2
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.
- package/dist/devices.d.ts +1 -1
- package/dist/emulation/dir.d.ts +1 -1
- package/dist/emulation/promises.d.ts +13 -9
- package/dist/emulation/promises.js +51 -21
- package/dist/emulation/streams.d.ts +1 -1
- package/dist/emulation/sync.d.ts +5 -0
- package/dist/emulation/sync.js +30 -8
- package/dist/stats.d.ts +1 -1
- package/package.json +5 -1
- package/src/devices.ts +1 -1
- package/src/emulation/dir.ts +1 -1
- package/src/emulation/promises.ts +76 -37
- package/src/emulation/streams.ts +1 -1
- package/src/emulation/sync.ts +33 -9
- package/src/stats.ts +1 -1
package/dist/devices.d.ts
CHANGED
package/dist/emulation/dir.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import type * as fs from 'node:fs';
|
|
3
3
|
import type * as promises from 'node:fs/promises';
|
|
4
|
-
import type { CreateReadStreamOptions, CreateWriteStreamOptions, FileChangeInfo, FileReadResult, FlagAndOpenMode } from 'node:fs/promises';
|
|
5
4
|
import type { Stream } from 'node:stream';
|
|
6
5
|
import type { ReadableStream as TReadableStream } from 'node:stream/web';
|
|
7
6
|
import type { Interface as ReadlineInterface } from 'readline';
|
|
@@ -60,7 +59,7 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
60
59
|
* - `mode` defaults to `0o666`.
|
|
61
60
|
* - `flag` defaults to `'a'`.
|
|
62
61
|
*/
|
|
63
|
-
appendFile(data: string | Uint8Array, _options?: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding): Promise<void>;
|
|
62
|
+
appendFile(data: string | Uint8Array, _options?: (fs.ObjectEncodingOptions & promises.FlagAndOpenMode) | BufferEncoding): Promise<void>;
|
|
64
63
|
/**
|
|
65
64
|
* Asynchronously reads data from the file.
|
|
66
65
|
* The `FileHandle` must have been opened for reading.
|
|
@@ -69,7 +68,7 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
69
68
|
* @param length The number of bytes to read.
|
|
70
69
|
* @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position.
|
|
71
70
|
*/
|
|
72
|
-
read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number | null): Promise<FileReadResult<TBuffer>>;
|
|
71
|
+
read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number | null): Promise<promises.FileReadResult<TBuffer>>;
|
|
73
72
|
/**
|
|
74
73
|
* Asynchronously reads the entire contents of a file. The underlying file will _not_ be closed automatically.
|
|
75
74
|
* The `FileHandle` must have been opened for reading.
|
|
@@ -79,7 +78,7 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
79
78
|
readFile(_options?: {
|
|
80
79
|
flag?: fs.OpenMode;
|
|
81
80
|
}): Promise<Buffer>;
|
|
82
|
-
readFile(_options: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding): Promise<string>;
|
|
81
|
+
readFile(_options: (fs.ObjectEncodingOptions & promises.FlagAndOpenMode) | BufferEncoding): Promise<string>;
|
|
83
82
|
/**
|
|
84
83
|
* Returns a `ReadableStream` that may be used to read the files data.
|
|
85
84
|
*
|
|
@@ -153,12 +152,12 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
153
152
|
* Creates a stream for reading from the file.
|
|
154
153
|
* @param options Options for the readable stream
|
|
155
154
|
*/
|
|
156
|
-
createReadStream(options?: CreateReadStreamOptions): ReadStream;
|
|
155
|
+
createReadStream(options?: promises.CreateReadStreamOptions): ReadStream;
|
|
157
156
|
/**
|
|
158
157
|
* Creates a stream for writing to the file.
|
|
159
158
|
* @param options Options for the writeable stream.
|
|
160
159
|
*/
|
|
161
|
-
createWriteStream(options?: CreateWriteStreamOptions): WriteStream;
|
|
160
|
+
createWriteStream(options?: promises.CreateWriteStreamOptions): WriteStream;
|
|
162
161
|
}
|
|
163
162
|
export declare function rename(oldPath: fs.PathLike, newPath: fs.PathLike): Promise<void>;
|
|
164
163
|
/**
|
|
@@ -265,6 +264,11 @@ export declare function readdir(path: fs.PathLike, options: fs.ObjectEncodingOpt
|
|
|
265
264
|
withFileTypes: true;
|
|
266
265
|
recursive?: boolean;
|
|
267
266
|
}): Promise<Dirent[]>;
|
|
267
|
+
export declare function readdir(path: fs.PathLike, options?: {
|
|
268
|
+
withFileTypes?: boolean;
|
|
269
|
+
recursive?: boolean;
|
|
270
|
+
encoding?: BufferEncoding | 'buffer' | null;
|
|
271
|
+
} | BufferEncoding | 'buffer' | null): Promise<string[] | Dirent[] | Buffer[]>;
|
|
268
272
|
export declare function link(targetPath: fs.PathLike, linkPath: fs.PathLike): Promise<void>;
|
|
269
273
|
/**
|
|
270
274
|
* `symlink`.
|
|
@@ -295,9 +299,9 @@ export declare function lutimes(path: fs.PathLike, atime: fs.TimeLike, mtime: fs
|
|
|
295
299
|
*/
|
|
296
300
|
export declare function realpath(path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
|
|
297
301
|
export declare function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
|
|
298
|
-
export declare function watch(filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<FileChangeInfo<string>>;
|
|
299
|
-
export declare function watch(filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<FileChangeInfo<Buffer>>;
|
|
300
|
-
export declare function watch(filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<FileChangeInfo<string>> | AsyncIterable<FileChangeInfo<Buffer>>;
|
|
302
|
+
export declare function watch(filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<promises.FileChangeInfo<string>>;
|
|
303
|
+
export declare function watch(filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<promises.FileChangeInfo<Buffer>>;
|
|
304
|
+
export declare function watch(filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<promises.FileChangeInfo<string>> | AsyncIterable<promises.FileChangeInfo<Buffer>>;
|
|
301
305
|
export declare function access(path: fs.PathLike, mode?: number): Promise<void>;
|
|
302
306
|
/**
|
|
303
307
|
* Asynchronous `rm`. Removes files or directories (recursively).
|
|
@@ -51,7 +51,7 @@ import { Errno, ErrnoError } from '../error.js';
|
|
|
51
51
|
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
52
52
|
import '../polyfills.js';
|
|
53
53
|
import { BigIntStats } from '../stats.js';
|
|
54
|
-
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
54
|
+
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
55
55
|
import * as constants from './constants.js';
|
|
56
56
|
import { Dir, Dirent } from './dir.js';
|
|
57
57
|
import { dirname, join, parse } from './path.js';
|
|
@@ -627,19 +627,16 @@ export async function mkdir(path, options) {
|
|
|
627
627
|
}
|
|
628
628
|
mkdir;
|
|
629
629
|
export async function readdir(path, options) {
|
|
630
|
+
options = typeof options === 'object' ? options : { encoding: options };
|
|
630
631
|
path = normalizePath(path);
|
|
631
632
|
if (!(await stat(path)).hasAccess(constants.R_OK)) {
|
|
632
633
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
633
634
|
}
|
|
634
635
|
path = (await exists(path)) ? await realpath(path) : path;
|
|
635
636
|
const { fs, path: resolved } = resolveMount(path);
|
|
636
|
-
|
|
637
|
-
try {
|
|
638
|
-
entries = await fs.readdir(resolved);
|
|
639
|
-
}
|
|
640
|
-
catch (e) {
|
|
637
|
+
const entries = await fs.readdir(resolved).catch((e) => {
|
|
641
638
|
throw fixError(e, { [resolved]: path });
|
|
642
|
-
}
|
|
639
|
+
});
|
|
643
640
|
for (const point of mounts.keys()) {
|
|
644
641
|
if (point.startsWith(path)) {
|
|
645
642
|
const entry = point.slice(path.length);
|
|
@@ -652,12 +649,37 @@ export async function readdir(path, options) {
|
|
|
652
649
|
}
|
|
653
650
|
const values = [];
|
|
654
651
|
for (const entry of entries) {
|
|
655
|
-
|
|
652
|
+
const fullPath = join(path, entry);
|
|
653
|
+
const stats = options?.recursive || options?.withFileTypes ? await stat(fullPath) : null;
|
|
654
|
+
if (options?.withFileTypes) {
|
|
655
|
+
values.push(new Dirent(entry, stats));
|
|
656
|
+
}
|
|
657
|
+
else if (options?.encoding === 'buffer') {
|
|
658
|
+
values.push(Buffer.from(entry));
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
values.push(entry);
|
|
662
|
+
}
|
|
663
|
+
if (!options?.recursive || !stats?.isDirectory()) {
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
for (const subEntry of await readdir(fullPath, options)) {
|
|
667
|
+
if (subEntry instanceof Dirent) {
|
|
668
|
+
subEntry.path = join(entry, subEntry.path);
|
|
669
|
+
values.push(subEntry);
|
|
670
|
+
}
|
|
671
|
+
else if (Buffer.isBuffer(subEntry)) {
|
|
672
|
+
// Convert Buffer to string, prefix with the full path
|
|
673
|
+
values.push(Buffer.from(join(entry, decodeUTF8(subEntry))));
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
values.push(join(entry, subEntry));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
656
679
|
}
|
|
657
680
|
return values;
|
|
658
681
|
}
|
|
659
682
|
readdir;
|
|
660
|
-
// SYMLINK METHODS
|
|
661
683
|
export async function link(targetPath, linkPath) {
|
|
662
684
|
targetPath = normalizePath(targetPath);
|
|
663
685
|
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK)) {
|
|
@@ -849,20 +871,28 @@ realpath;
|
|
|
849
871
|
export function watch(filename, options = {}) {
|
|
850
872
|
return {
|
|
851
873
|
[Symbol.asyncIterator]() {
|
|
852
|
-
const watcher = new FSWatcher(filename.toString(), typeof options
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
874
|
+
const watcher = new FSWatcher(filename.toString(), typeof options !== 'string' ? options : { encoding: options });
|
|
875
|
+
// A queue to hold change events, since we need to resolve them in the async iterator
|
|
876
|
+
const eventQueue = [];
|
|
877
|
+
watcher.on('change', (eventType, filename) => {
|
|
878
|
+
eventQueue.shift()?.({ value: { eventType, filename }, done: false });
|
|
879
|
+
});
|
|
880
|
+
function cleanup() {
|
|
881
|
+
watcher.close();
|
|
882
|
+
for (const resolve of eventQueue) {
|
|
883
|
+
resolve({ value: null, done: true });
|
|
884
|
+
}
|
|
885
|
+
eventQueue.length = 0; // Clear the queue
|
|
886
|
+
return Promise.resolve({ value: null, done: true });
|
|
861
887
|
}
|
|
862
888
|
return {
|
|
863
|
-
next
|
|
864
|
-
|
|
865
|
-
|
|
889
|
+
async next() {
|
|
890
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
891
|
+
eventQueue.push(resolve);
|
|
892
|
+
return promise;
|
|
893
|
+
},
|
|
894
|
+
return: cleanup,
|
|
895
|
+
throw: cleanup,
|
|
866
896
|
};
|
|
867
897
|
},
|
|
868
898
|
};
|
package/dist/emulation/sync.d.ts
CHANGED
|
@@ -128,6 +128,11 @@ export declare function readdirSync(path: fs.PathLike, options?: (fs.ObjectEncod
|
|
|
128
128
|
withFileTypes?: false;
|
|
129
129
|
recursive?: boolean;
|
|
130
130
|
}) | BufferEncoding | null): string[] | Buffer[];
|
|
131
|
+
export declare function readdirSync(path: fs.PathLike, options?: {
|
|
132
|
+
withFileTypes?: boolean;
|
|
133
|
+
recursive?: boolean;
|
|
134
|
+
encoding?: BufferEncoding | 'buffer' | null;
|
|
135
|
+
} | BufferEncoding | 'buffer' | null): string[] | Dirent[] | Buffer[];
|
|
131
136
|
export declare function linkSync(targetPath: fs.PathLike, linkPath: fs.PathLike): void;
|
|
132
137
|
/**
|
|
133
138
|
* Synchronous `symlink`.
|
package/dist/emulation/sync.js
CHANGED
|
@@ -49,7 +49,7 @@ import { Buffer } from 'buffer';
|
|
|
49
49
|
import { Errno, ErrnoError } from '../error.js';
|
|
50
50
|
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
51
51
|
import { BigIntStats } from '../stats.js';
|
|
52
|
-
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
52
|
+
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
53
53
|
import * as constants from './constants.js';
|
|
54
54
|
import { Dir, Dirent } from './dir.js';
|
|
55
55
|
import { dirname, join, parse } from './path.js';
|
|
@@ -431,6 +431,7 @@ export function mkdirSync(path, options) {
|
|
|
431
431
|
}
|
|
432
432
|
mkdirSync;
|
|
433
433
|
export function readdirSync(path, options) {
|
|
434
|
+
options = typeof options === 'object' ? options : { encoding: options };
|
|
434
435
|
path = normalizePath(path);
|
|
435
436
|
const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
|
|
436
437
|
let entries;
|
|
@@ -454,15 +455,36 @@ export function readdirSync(path, options) {
|
|
|
454
455
|
}
|
|
455
456
|
entries.push(entry);
|
|
456
457
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
458
|
+
// Iterate over entries and handle recursive case if needed
|
|
459
|
+
const values = [];
|
|
460
|
+
for (const entry of entries) {
|
|
461
|
+
const fullPath = join(path, entry);
|
|
462
|
+
const entryStat = statSync(fullPath);
|
|
463
|
+
if (options?.withFileTypes) {
|
|
464
|
+
values.push(new Dirent(entry, entryStat));
|
|
460
465
|
}
|
|
461
|
-
if (options
|
|
462
|
-
|
|
466
|
+
else if (options?.encoding === 'buffer') {
|
|
467
|
+
values.push(Buffer.from(entry));
|
|
463
468
|
}
|
|
464
|
-
|
|
465
|
-
|
|
469
|
+
else {
|
|
470
|
+
values.push(entry);
|
|
471
|
+
}
|
|
472
|
+
if (!entryStat.isDirectory() || !options?.recursive)
|
|
473
|
+
continue;
|
|
474
|
+
for (const subEntry of readdirSync(fullPath, options)) {
|
|
475
|
+
if (subEntry instanceof Dirent) {
|
|
476
|
+
subEntry.path = join(entry, subEntry.path);
|
|
477
|
+
values.push(subEntry);
|
|
478
|
+
}
|
|
479
|
+
else if (Buffer.isBuffer(subEntry)) {
|
|
480
|
+
values.push(Buffer.from(join(entry, decodeUTF8(subEntry))));
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
values.push(join(entry, subEntry));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return values;
|
|
466
488
|
}
|
|
467
489
|
readdirSync;
|
|
468
490
|
// SYMLINK METHODS
|
package/dist/stats.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
|
+
"funding": {
|
|
6
|
+
"type": "individual",
|
|
7
|
+
"url": "https://github.com/sponsors/james-pre"
|
|
8
|
+
},
|
|
5
9
|
"main": "dist/index.js",
|
|
6
10
|
"types": "dist/index.d.ts",
|
|
7
11
|
"keywords": [
|
package/src/devices.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileReadResult } from 'fs/promises';
|
|
1
|
+
import type { FileReadResult } from 'node:fs/promises';
|
|
2
2
|
import { InMemoryStore } from './backends/memory.js';
|
|
3
3
|
import { StoreFS } from './backends/store/fs.js';
|
|
4
4
|
import { S_IFBLK, S_IFCHR } from './emulation/constants.js';
|
package/src/emulation/dir.ts
CHANGED
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
import { Buffer } from 'buffer';
|
|
3
3
|
import type * as fs from 'node:fs';
|
|
4
4
|
import type * as promises from 'node:fs/promises';
|
|
5
|
-
import type { CreateReadStreamOptions, CreateWriteStreamOptions, FileChangeInfo, FileReadResult, FlagAndOpenMode } from 'node:fs/promises';
|
|
6
5
|
import type { Stream } from 'node:stream';
|
|
7
|
-
import type { ReadableStream as TReadableStream } from 'node:stream/web';
|
|
6
|
+
import type { ReadableStreamController, ReadableStream as TReadableStream } from 'node:stream/web';
|
|
8
7
|
import type { Interface as ReadlineInterface } from 'readline';
|
|
9
|
-
import type { ReadableStreamController } from 'stream/web';
|
|
10
8
|
import { Errno, ErrnoError } from '../error.js';
|
|
11
9
|
import type { File } from '../file.js';
|
|
12
10
|
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
13
11
|
import type { FileContents } from '../filesystem.js';
|
|
14
12
|
import '../polyfills.js';
|
|
15
13
|
import { BigIntStats, type Stats } from '../stats.js';
|
|
16
|
-
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
14
|
+
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
17
15
|
import * as constants from './constants.js';
|
|
18
16
|
import { Dir, Dirent } from './dir.js';
|
|
19
17
|
import { dirname, join, parse } from './path.js';
|
|
@@ -107,7 +105,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
107
105
|
* - `mode` defaults to `0o666`.
|
|
108
106
|
* - `flag` defaults to `'a'`.
|
|
109
107
|
*/
|
|
110
|
-
public async appendFile(data: string | Uint8Array, _options: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding = {}): Promise<void> {
|
|
108
|
+
public async appendFile(data: string | Uint8Array, _options: (fs.ObjectEncodingOptions & promises.FlagAndOpenMode) | BufferEncoding = {}): Promise<void> {
|
|
111
109
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
112
110
|
const flag = parseFlag(options.flag);
|
|
113
111
|
if (!isAppendable(flag)) {
|
|
@@ -129,7 +127,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
129
127
|
* @param length The number of bytes to read.
|
|
130
128
|
* @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position.
|
|
131
129
|
*/
|
|
132
|
-
public read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number | null): Promise<FileReadResult<TBuffer>> {
|
|
130
|
+
public read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number | null): Promise<promises.FileReadResult<TBuffer>> {
|
|
133
131
|
if (isNaN(+position!)) {
|
|
134
132
|
position = this.file.position;
|
|
135
133
|
}
|
|
@@ -143,8 +141,8 @@ export class FileHandle implements promises.FileHandle {
|
|
|
143
141
|
* If a flag is not provided, it defaults to `'r'`.
|
|
144
142
|
*/
|
|
145
143
|
public async readFile(_options?: { flag?: fs.OpenMode }): Promise<Buffer>;
|
|
146
|
-
public async readFile(_options: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding): Promise<string>;
|
|
147
|
-
public async readFile(_options?: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding): Promise<string | Buffer> {
|
|
144
|
+
public async readFile(_options: (fs.ObjectEncodingOptions & promises.FlagAndOpenMode) | BufferEncoding): Promise<string>;
|
|
145
|
+
public async readFile(_options?: (fs.ObjectEncodingOptions & promises.FlagAndOpenMode) | BufferEncoding): Promise<string | Buffer> {
|
|
148
146
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
149
147
|
const flag = parseFlag(options.flag);
|
|
150
148
|
if (!isReadable(flag)) {
|
|
@@ -334,7 +332,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
334
332
|
* Creates a stream for reading from the file.
|
|
335
333
|
* @param options Options for the readable stream
|
|
336
334
|
*/
|
|
337
|
-
public createReadStream(options?: CreateReadStreamOptions): ReadStream {
|
|
335
|
+
public createReadStream(options?: promises.CreateReadStreamOptions): ReadStream {
|
|
338
336
|
const stream = new ReadStream({
|
|
339
337
|
highWaterMark: options?.highWaterMark || 64 * 1024,
|
|
340
338
|
encoding: options!.encoding!,
|
|
@@ -359,7 +357,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
359
357
|
* Creates a stream for writing to the file.
|
|
360
358
|
* @param options Options for the writeable stream.
|
|
361
359
|
*/
|
|
362
|
-
public createWriteStream(options?: CreateWriteStreamOptions): WriteStream {
|
|
360
|
+
public createWriteStream(options?: promises.CreateWriteStreamOptions): WriteStream {
|
|
363
361
|
const streamOptions = {
|
|
364
362
|
highWaterMark: options?.highWaterMark,
|
|
365
363
|
encoding: options?.encoding,
|
|
@@ -692,22 +690,28 @@ export async function readdir(
|
|
|
692
690
|
options?: (fs.ObjectEncodingOptions & { withFileTypes?: false; recursive?: boolean }) | BufferEncoding | null
|
|
693
691
|
): Promise<string[] | Buffer[]>;
|
|
694
692
|
export async function readdir(path: fs.PathLike, options: fs.ObjectEncodingOptions & { withFileTypes: true; recursive?: boolean }): Promise<Dirent[]>;
|
|
693
|
+
export async function readdir(
|
|
694
|
+
path: fs.PathLike,
|
|
695
|
+
options?: { withFileTypes?: boolean; recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null } | BufferEncoding | 'buffer' | null
|
|
696
|
+
): Promise<string[] | Dirent[] | Buffer[]>;
|
|
695
697
|
export async function readdir(
|
|
696
698
|
path: fs.PathLike,
|
|
697
699
|
options?: { withFileTypes?: boolean; recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null } | BufferEncoding | 'buffer' | null
|
|
698
700
|
): Promise<string[] | Dirent[] | Buffer[]> {
|
|
701
|
+
options = typeof options === 'object' ? options : { encoding: options };
|
|
699
702
|
path = normalizePath(path);
|
|
703
|
+
|
|
700
704
|
if (!(await stat(path)).hasAccess(constants.R_OK)) {
|
|
701
705
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
702
706
|
}
|
|
707
|
+
|
|
703
708
|
path = (await exists(path)) ? await realpath(path) : path;
|
|
704
709
|
const { fs, path: resolved } = resolveMount(path);
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
entries = await fs.readdir(resolved);
|
|
708
|
-
} catch (e) {
|
|
710
|
+
|
|
711
|
+
const entries = await fs.readdir(resolved).catch((e: Error) => {
|
|
709
712
|
throw fixError(e as Error, { [resolved]: path });
|
|
710
|
-
}
|
|
713
|
+
});
|
|
714
|
+
|
|
711
715
|
for (const point of mounts.keys()) {
|
|
712
716
|
if (point.startsWith(path)) {
|
|
713
717
|
const entry = point.slice(path.length);
|
|
@@ -718,16 +722,41 @@ export async function readdir(
|
|
|
718
722
|
entries.push(entry);
|
|
719
723
|
}
|
|
720
724
|
}
|
|
721
|
-
|
|
725
|
+
|
|
726
|
+
const values: (string | Dirent | Buffer)[] = [];
|
|
722
727
|
for (const entry of entries) {
|
|
723
|
-
|
|
728
|
+
const fullPath = join(path, entry);
|
|
729
|
+
|
|
730
|
+
const stats = options?.recursive || options?.withFileTypes ? await stat(fullPath) : null;
|
|
731
|
+
if (options?.withFileTypes) {
|
|
732
|
+
values.push(new Dirent(entry, stats!));
|
|
733
|
+
} else if (options?.encoding === 'buffer') {
|
|
734
|
+
values.push(Buffer.from(entry));
|
|
735
|
+
} else {
|
|
736
|
+
values.push(entry);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (!options?.recursive || !stats?.isDirectory()) {
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
for (const subEntry of await readdir(fullPath, options)) {
|
|
744
|
+
if (subEntry instanceof Dirent) {
|
|
745
|
+
subEntry.path = join(entry, subEntry.path);
|
|
746
|
+
values.push(subEntry);
|
|
747
|
+
} else if (Buffer.isBuffer(subEntry)) {
|
|
748
|
+
// Convert Buffer to string, prefix with the full path
|
|
749
|
+
values.push(Buffer.from(join(entry, decodeUTF8(subEntry))));
|
|
750
|
+
} else {
|
|
751
|
+
values.push(join(entry, subEntry));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
724
754
|
}
|
|
755
|
+
|
|
725
756
|
return values as string[] | Dirent[];
|
|
726
757
|
}
|
|
727
758
|
readdir satisfies typeof promises.readdir;
|
|
728
759
|
|
|
729
|
-
// SYMLINK METHODS
|
|
730
|
-
|
|
731
760
|
export async function link(targetPath: fs.PathLike, linkPath: fs.PathLike): Promise<void> {
|
|
732
761
|
targetPath = normalizePath(targetPath);
|
|
733
762
|
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK)) {
|
|
@@ -856,28 +885,38 @@ export async function realpath(path: fs.PathLike, options?: fs.EncodingOption |
|
|
|
856
885
|
}
|
|
857
886
|
realpath satisfies typeof promises.realpath;
|
|
858
887
|
|
|
859
|
-
export function watch(filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<FileChangeInfo<string>>;
|
|
860
|
-
export function watch(filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<FileChangeInfo<Buffer>>;
|
|
861
|
-
export function watch(filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<FileChangeInfo<string>> | AsyncIterable<FileChangeInfo<Buffer>>;
|
|
862
|
-
export function watch<T extends string | Buffer>(filename: fs.PathLike, options: fs.WatchOptions | string = {}): AsyncIterable<FileChangeInfo<T>> {
|
|
888
|
+
export function watch(filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<promises.FileChangeInfo<string>>;
|
|
889
|
+
export function watch(filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<promises.FileChangeInfo<Buffer>>;
|
|
890
|
+
export function watch(filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<promises.FileChangeInfo<string>> | AsyncIterable<promises.FileChangeInfo<Buffer>>;
|
|
891
|
+
export function watch<T extends string | Buffer>(filename: fs.PathLike, options: fs.WatchOptions | string = {}): AsyncIterable<promises.FileChangeInfo<T>> {
|
|
863
892
|
return {
|
|
864
|
-
[Symbol.asyncIterator](): AsyncIterator<FileChangeInfo<T>> {
|
|
865
|
-
const watcher = new FSWatcher<T>(filename.toString(), typeof options
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
893
|
+
[Symbol.asyncIterator](): AsyncIterator<promises.FileChangeInfo<T>> {
|
|
894
|
+
const watcher = new FSWatcher<T>(filename.toString(), typeof options !== 'string' ? options : { encoding: options as BufferEncoding | 'buffer' });
|
|
895
|
+
|
|
896
|
+
// A queue to hold change events, since we need to resolve them in the async iterator
|
|
897
|
+
const eventQueue: ((value: IteratorResult<promises.FileChangeInfo<T>>) => void)[] = [];
|
|
898
|
+
|
|
899
|
+
watcher.on('change', (eventType: promises.FileChangeInfo<T>['eventType'], filename: T) => {
|
|
900
|
+
eventQueue.shift()?.({ value: { eventType, filename }, done: false });
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
function cleanup() {
|
|
904
|
+
watcher.close();
|
|
905
|
+
for (const resolve of eventQueue) {
|
|
906
|
+
resolve({ value: null, done: true });
|
|
907
|
+
}
|
|
908
|
+
eventQueue.length = 0; // Clear the queue
|
|
909
|
+
return Promise.resolve({ value: null, done: true as const });
|
|
875
910
|
}
|
|
876
911
|
|
|
877
912
|
return {
|
|
878
|
-
next
|
|
879
|
-
|
|
880
|
-
|
|
913
|
+
async next() {
|
|
914
|
+
const { promise, resolve } = Promise.withResolvers<IteratorResult<promises.FileChangeInfo<T>>>();
|
|
915
|
+
eventQueue.push(resolve);
|
|
916
|
+
return promise;
|
|
917
|
+
},
|
|
918
|
+
return: cleanup,
|
|
919
|
+
throw: cleanup,
|
|
881
920
|
};
|
|
882
921
|
},
|
|
883
922
|
};
|
package/src/emulation/streams.ts
CHANGED
package/src/emulation/sync.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { File } from '../file.js';
|
|
|
5
5
|
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
6
6
|
import type { FileContents } from '../filesystem.js';
|
|
7
7
|
import { BigIntStats, type Stats } from '../stats.js';
|
|
8
|
-
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
8
|
+
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
9
9
|
import * as constants from './constants.js';
|
|
10
10
|
import { Dir, Dirent } from './dir.js';
|
|
11
11
|
import { dirname, join, parse } from './path.js';
|
|
@@ -437,10 +437,15 @@ export function readdirSync(path: fs.PathLike, options?: { recursive?: boolean;
|
|
|
437
437
|
export function readdirSync(path: fs.PathLike, options: { recursive?: boolean; encoding: 'buffer'; withFileTypes?: false } | 'buffer'): Buffer[];
|
|
438
438
|
export function readdirSync(path: fs.PathLike, options: { recursive?: boolean; withFileTypes: true }): Dirent[];
|
|
439
439
|
export function readdirSync(path: fs.PathLike, options?: (fs.ObjectEncodingOptions & { withFileTypes?: false; recursive?: boolean }) | BufferEncoding | null): string[] | Buffer[];
|
|
440
|
+
export function readdirSync(
|
|
441
|
+
path: fs.PathLike,
|
|
442
|
+
options?: { withFileTypes?: boolean; recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null } | BufferEncoding | 'buffer' | null
|
|
443
|
+
): string[] | Dirent[] | Buffer[];
|
|
440
444
|
export function readdirSync(
|
|
441
445
|
path: fs.PathLike,
|
|
442
446
|
options?: { recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null; withFileTypes?: boolean } | BufferEncoding | 'buffer' | null
|
|
443
447
|
): string[] | Dirent[] | Buffer[] {
|
|
448
|
+
options = typeof options === 'object' ? options : { encoding: options };
|
|
444
449
|
path = normalizePath(path);
|
|
445
450
|
const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
|
|
446
451
|
let entries: string[];
|
|
@@ -452,6 +457,7 @@ export function readdirSync(
|
|
|
452
457
|
} catch (e) {
|
|
453
458
|
throw fixError(e as Error, { [resolved]: path });
|
|
454
459
|
}
|
|
460
|
+
|
|
455
461
|
for (const mount of mounts.keys()) {
|
|
456
462
|
if (!mount.startsWith(path)) {
|
|
457
463
|
continue;
|
|
@@ -463,17 +469,35 @@ export function readdirSync(
|
|
|
463
469
|
}
|
|
464
470
|
entries.push(entry);
|
|
465
471
|
}
|
|
466
|
-
return entries.map((entry: string) => {
|
|
467
|
-
if (typeof options == 'object' && options?.withFileTypes) {
|
|
468
|
-
return new Dirent(entry, statSync(join(path.toString(), entry)));
|
|
469
|
-
}
|
|
470
472
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
+
// Iterate over entries and handle recursive case if needed
|
|
474
|
+
const values: (string | Dirent | Buffer)[] = [];
|
|
475
|
+
for (const entry of entries) {
|
|
476
|
+
const fullPath = join(path, entry);
|
|
477
|
+
const entryStat = statSync(fullPath);
|
|
478
|
+
|
|
479
|
+
if (options?.withFileTypes) {
|
|
480
|
+
values.push(new Dirent(entry, entryStat));
|
|
481
|
+
} else if (options?.encoding === 'buffer') {
|
|
482
|
+
values.push(Buffer.from(entry));
|
|
483
|
+
} else {
|
|
484
|
+
values.push(entry);
|
|
485
|
+
}
|
|
486
|
+
if (!entryStat.isDirectory() || !options?.recursive) continue;
|
|
487
|
+
|
|
488
|
+
for (const subEntry of readdirSync(fullPath, options)) {
|
|
489
|
+
if (subEntry instanceof Dirent) {
|
|
490
|
+
subEntry.path = join(entry, subEntry.path);
|
|
491
|
+
values.push(subEntry);
|
|
492
|
+
} else if (Buffer.isBuffer(subEntry)) {
|
|
493
|
+
values.push(Buffer.from(join(entry, decodeUTF8(subEntry))));
|
|
494
|
+
} else {
|
|
495
|
+
values.push(join(entry, subEntry));
|
|
496
|
+
}
|
|
473
497
|
}
|
|
498
|
+
}
|
|
474
499
|
|
|
475
|
-
|
|
476
|
-
}) as string[] | Dirent[] | Buffer[];
|
|
500
|
+
return values as string[] | Dirent[] | Buffer[];
|
|
477
501
|
}
|
|
478
502
|
readdirSync satisfies typeof fs.readdirSync;
|
|
479
503
|
|
package/src/stats.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type * as Node from 'fs';
|
|
1
|
+
import type * as Node from 'node:fs';
|
|
2
2
|
import { credentials, type Credentials } from './credentials.js';
|
|
3
3
|
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';
|
|
4
4
|
|