@zenfs/core 1.5.1 → 1.6.1
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/config.d.ts +2 -0
- package/dist/config.js +7 -0
- package/dist/devices.d.ts +31 -22
- package/dist/devices.js +75 -12
- package/dist/emulation/async.d.ts +9 -0
- package/dist/emulation/async.js +18 -0
- package/dist/emulation/index.d.ts +2 -1
- package/dist/emulation/index.js +2 -1
- package/dist/emulation/promises.d.ts +25 -22
- package/dist/emulation/promises.js +64 -19
- package/dist/emulation/shared.d.ts +2 -24
- package/dist/emulation/shared.js +0 -9
- package/dist/emulation/streams.d.ts +5 -5
- package/dist/emulation/streams.js +2 -2
- package/dist/emulation/sync.d.ts +15 -8
- package/dist/emulation/sync.js +34 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/utils.d.ts +1 -1
- package/package.json +5 -3
- package/readme.md +10 -10
- package/tests/fs/dir.test.ts +0 -2
- package/tests/fs/directory.test.ts +0 -2
- package/tests/fs/streams.test.ts +6 -6
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
|
|
2
|
+
import { type Device, type DeviceDriver } from './devices.js';
|
|
2
3
|
/**
|
|
3
4
|
* Configuration for a specific mount point
|
|
4
5
|
*/
|
|
@@ -85,6 +86,7 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
85
86
|
* Configures ZenFS with single mount point /
|
|
86
87
|
*/
|
|
87
88
|
export declare function configureSingle<T extends Backend>(configuration: MountConfiguration<T>): Promise<void>;
|
|
89
|
+
export declare function addDevice(driver: DeviceDriver, options?: object): Device;
|
|
88
90
|
/**
|
|
89
91
|
* Configures ZenFS with `configuration`
|
|
90
92
|
* @see Configuration
|
package/dist/config.js
CHANGED
|
@@ -4,6 +4,7 @@ import { DeviceFS } from './devices.js';
|
|
|
4
4
|
import * as cache from './emulation/cache.js';
|
|
5
5
|
import { config } from './emulation/config.js';
|
|
6
6
|
import * as fs from './emulation/index.js';
|
|
7
|
+
import { mounts } from './emulation/shared.js';
|
|
7
8
|
import { Errno, ErrnoError } from './error.js';
|
|
8
9
|
import { FileSystem } from './filesystem.js';
|
|
9
10
|
function isMountConfig(arg) {
|
|
@@ -80,6 +81,12 @@ async function mount(path, mount) {
|
|
|
80
81
|
}
|
|
81
82
|
fs.mount(path, mount);
|
|
82
83
|
}
|
|
84
|
+
export function addDevice(driver, options) {
|
|
85
|
+
const devfs = mounts.get('/dev');
|
|
86
|
+
if (!(devfs instanceof DeviceFS))
|
|
87
|
+
throw new ErrnoError(Errno.ENOTSUP, '/dev does not exist or is not a device file system');
|
|
88
|
+
return devfs._createDevice(driver, options);
|
|
89
|
+
}
|
|
83
90
|
/**
|
|
84
91
|
* Configures ZenFS with `configuration`
|
|
85
92
|
* @see Configuration
|
package/dist/devices.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Stats } from './stats.js';
|
|
|
8
8
|
* A device
|
|
9
9
|
* @todo Maybe add major/minor number or some other device information, like a UUID?
|
|
10
10
|
* @privateRemarks
|
|
11
|
-
* UUIDs were considered, however they don't make sense without an easy mechanism for
|
|
11
|
+
* UUIDs were considered, however they don't make sense without an easy mechanism for persistence
|
|
12
12
|
*/
|
|
13
13
|
export interface Device<TData = any> {
|
|
14
14
|
/**
|
|
@@ -22,17 +22,14 @@ export interface Device<TData = any> {
|
|
|
22
22
|
/**
|
|
23
23
|
* Data associated with a device.
|
|
24
24
|
* This is meant to be used by device drivers.
|
|
25
|
-
* @experimental
|
|
26
25
|
*/
|
|
27
26
|
data: TData;
|
|
28
27
|
/**
|
|
29
28
|
* Major device number
|
|
30
|
-
* @experimental
|
|
31
29
|
*/
|
|
32
30
|
major: number;
|
|
33
31
|
/**
|
|
34
32
|
* Minor device number
|
|
35
|
-
* @experimental
|
|
36
33
|
*/
|
|
37
34
|
minor: number;
|
|
38
35
|
}
|
|
@@ -44,6 +41,11 @@ export interface DeviceDriver<TData = any> {
|
|
|
44
41
|
* The name of the device driver
|
|
45
42
|
*/
|
|
46
43
|
name: string;
|
|
44
|
+
/**
|
|
45
|
+
* If true, only a single device can exist per device FS.
|
|
46
|
+
* Note that if this is unset or false, auto-named devices will have a number suffix
|
|
47
|
+
*/
|
|
48
|
+
singleton?: boolean;
|
|
47
49
|
/**
|
|
48
50
|
* Whether the device is buffered (a "block" device) or unbuffered (a "character" device)
|
|
49
51
|
* @default false
|
|
@@ -52,33 +54,33 @@ export interface DeviceDriver<TData = any> {
|
|
|
52
54
|
/**
|
|
53
55
|
* Initializes a new device.
|
|
54
56
|
* @returns `Device.data`
|
|
55
|
-
* @experimental
|
|
56
57
|
*/
|
|
57
|
-
init?(ino: bigint): {
|
|
58
|
+
init?(ino: bigint, options: object): {
|
|
58
59
|
data?: TData;
|
|
59
60
|
minor?: number;
|
|
60
61
|
major?: number;
|
|
62
|
+
name?: string;
|
|
61
63
|
};
|
|
62
64
|
/**
|
|
63
65
|
* Synchronously read from the device
|
|
64
66
|
* @group File operations
|
|
65
67
|
*/
|
|
66
|
-
read(file: DeviceFile
|
|
68
|
+
read(file: DeviceFile<TData>, buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number;
|
|
67
69
|
/**
|
|
68
70
|
* Synchronously write to the device
|
|
69
71
|
* @group File operations
|
|
70
72
|
*/
|
|
71
|
-
write(file: DeviceFile
|
|
73
|
+
write(file: DeviceFile<TData>, buffer: Uint8Array, offset: number, length: number, position?: number): number;
|
|
72
74
|
/**
|
|
73
75
|
* Sync the device
|
|
74
76
|
* @group File operations
|
|
75
77
|
*/
|
|
76
|
-
sync?(file: DeviceFile): void;
|
|
78
|
+
sync?(file: DeviceFile<TData>): void;
|
|
77
79
|
/**
|
|
78
80
|
* Close the device
|
|
79
81
|
* @group File operations
|
|
80
82
|
*/
|
|
81
|
-
close?(file: DeviceFile): void;
|
|
83
|
+
close?(file: DeviceFile<TData>): void;
|
|
82
84
|
}
|
|
83
85
|
/**
|
|
84
86
|
* The base class for device files
|
|
@@ -86,12 +88,12 @@ export interface DeviceDriver<TData = any> {
|
|
|
86
88
|
* It implements `truncate` using `write` and it has non-device methods throw.
|
|
87
89
|
* It is up to device drivers to implement the rest of the functionality.
|
|
88
90
|
*/
|
|
89
|
-
export declare class DeviceFile extends File {
|
|
91
|
+
export declare class DeviceFile<TData = any> extends File {
|
|
90
92
|
fs: DeviceFS;
|
|
91
|
-
readonly device: Device
|
|
93
|
+
readonly device: Device<TData>;
|
|
92
94
|
position: number;
|
|
93
|
-
constructor(fs: DeviceFS, path: string, device: Device);
|
|
94
|
-
get driver(): DeviceDriver
|
|
95
|
+
constructor(fs: DeviceFS, path: string, device: Device<TData>);
|
|
96
|
+
get driver(): DeviceDriver<TData>;
|
|
95
97
|
protected get stats(): Partial<StatsLike>;
|
|
96
98
|
stat(): Promise<Stats>;
|
|
97
99
|
statSync(): Stats;
|
|
@@ -121,8 +123,14 @@ export declare class DeviceFS extends StoreFS<InMemoryStore> {
|
|
|
121
123
|
protected readonly devices: Map<string, Device<any>>;
|
|
122
124
|
/**
|
|
123
125
|
* Creates a new device at `path` relative to the `DeviceFS` root.
|
|
126
|
+
* @deprecated
|
|
127
|
+
*/
|
|
128
|
+
createDevice<TData = any>(path: string, driver: DeviceDriver<TData>, options?: object): Device<TData | Record<string, never>>;
|
|
129
|
+
protected devicesWithDriver(driver: DeviceDriver<unknown> | string, forceIdentity?: boolean): Device[];
|
|
130
|
+
/**
|
|
131
|
+
* @internal
|
|
124
132
|
*/
|
|
125
|
-
|
|
133
|
+
_createDevice<TData = any>(driver: DeviceDriver<TData>, options?: object): Device<TData | Record<string, never>>;
|
|
126
134
|
/**
|
|
127
135
|
* Adds default devices
|
|
128
136
|
*/
|
|
@@ -153,7 +161,7 @@ export declare class DeviceFS extends StoreFS<InMemoryStore> {
|
|
|
153
161
|
* Simulates the `/dev/null` device.
|
|
154
162
|
* - Reads return 0 bytes (EOF).
|
|
155
163
|
* - Writes discard data, advancing the file position.
|
|
156
|
-
* @
|
|
164
|
+
* @internal
|
|
157
165
|
*/
|
|
158
166
|
export declare const nullDevice: DeviceDriver;
|
|
159
167
|
/**
|
|
@@ -164,31 +172,32 @@ export declare const nullDevice: DeviceDriver;
|
|
|
164
172
|
* - Reads fill the buffer with zeroes.
|
|
165
173
|
* - Writes discard data but update the file position.
|
|
166
174
|
* - Provides basic file metadata, treating it as a character device.
|
|
167
|
-
* @
|
|
175
|
+
* @internal
|
|
168
176
|
*/
|
|
169
177
|
export declare const zeroDevice: DeviceDriver;
|
|
170
178
|
/**
|
|
171
179
|
* Simulates the `/dev/full` device.
|
|
172
180
|
* - Reads behave like `/dev/zero` (returns zeroes).
|
|
173
181
|
* - Writes always fail with ENOSPC (no space left on device).
|
|
174
|
-
* @
|
|
182
|
+
* @internal
|
|
175
183
|
*/
|
|
176
184
|
export declare const fullDevice: DeviceDriver;
|
|
177
185
|
/**
|
|
178
186
|
* Simulates the `/dev/random` device.
|
|
179
187
|
* - Reads return random bytes.
|
|
180
188
|
* - Writes discard data, advancing the file position.
|
|
181
|
-
* @
|
|
189
|
+
* @internal
|
|
182
190
|
*/
|
|
183
191
|
export declare const randomDevice: DeviceDriver;
|
|
184
192
|
/**
|
|
185
193
|
* Shortcuts for importing.
|
|
186
|
-
* @experimental
|
|
187
194
|
*/
|
|
188
|
-
declare const
|
|
195
|
+
export declare const devices: {
|
|
189
196
|
null: DeviceDriver<any>;
|
|
190
197
|
zero: DeviceDriver<any>;
|
|
191
198
|
full: DeviceDriver<any>;
|
|
192
199
|
random: DeviceDriver<any>;
|
|
200
|
+
console: DeviceDriver<{
|
|
201
|
+
output: (text: string) => unknown;
|
|
202
|
+
}>;
|
|
193
203
|
};
|
|
194
|
-
export default _default;
|
package/dist/devices.js
CHANGED
|
@@ -60,6 +60,7 @@ import { Errno, ErrnoError } from './error.js';
|
|
|
60
60
|
import { File } from './file.js';
|
|
61
61
|
import { Stats } from './stats.js';
|
|
62
62
|
import { basename, dirname } from './emulation/path.js';
|
|
63
|
+
import { decodeUTF8 } from './utils.js';
|
|
63
64
|
/**
|
|
64
65
|
* The base class for device files
|
|
65
66
|
* This class only does some simple things:
|
|
@@ -154,8 +155,9 @@ export class DeviceFile extends File {
|
|
|
154
155
|
export class DeviceFS extends StoreFS {
|
|
155
156
|
/**
|
|
156
157
|
* Creates a new device at `path` relative to the `DeviceFS` root.
|
|
158
|
+
* @deprecated
|
|
157
159
|
*/
|
|
158
|
-
createDevice(path, driver) {
|
|
160
|
+
createDevice(path, driver, options = {}) {
|
|
159
161
|
if (this.existsSync(path)) {
|
|
160
162
|
throw ErrnoError.With('EEXIST', path, 'mknod');
|
|
161
163
|
}
|
|
@@ -168,19 +170,56 @@ export class DeviceFS extends StoreFS {
|
|
|
168
170
|
data: {},
|
|
169
171
|
minor: 0,
|
|
170
172
|
major: 0,
|
|
171
|
-
...driver.init?.(ino),
|
|
173
|
+
...driver.init?.(ino, options),
|
|
172
174
|
};
|
|
173
175
|
this.devices.set(path, dev);
|
|
174
176
|
return dev;
|
|
175
177
|
}
|
|
178
|
+
devicesWithDriver(driver, forceIdentity) {
|
|
179
|
+
if (forceIdentity && typeof driver == 'string') {
|
|
180
|
+
throw new ErrnoError(Errno.EINVAL, 'Can not fetch devices using only a driver name');
|
|
181
|
+
}
|
|
182
|
+
const devs = [];
|
|
183
|
+
for (const device of this.devices.values()) {
|
|
184
|
+
if (forceIdentity && device.driver != driver)
|
|
185
|
+
continue;
|
|
186
|
+
const name = typeof driver == 'string' ? driver : driver.name;
|
|
187
|
+
if (name == device.driver.name)
|
|
188
|
+
devs.push(device);
|
|
189
|
+
}
|
|
190
|
+
return devs;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
_createDevice(driver, options = {}) {
|
|
196
|
+
let ino = 1n;
|
|
197
|
+
while (this.store.has(ino))
|
|
198
|
+
ino++;
|
|
199
|
+
const dev = {
|
|
200
|
+
driver,
|
|
201
|
+
ino,
|
|
202
|
+
data: {},
|
|
203
|
+
minor: 0,
|
|
204
|
+
major: 0,
|
|
205
|
+
...driver.init?.(ino, options),
|
|
206
|
+
};
|
|
207
|
+
const path = '/' + (dev.name || driver.name) + (driver.singleton ? '' : this.devicesWithDriver(driver).length);
|
|
208
|
+
if (this.existsSync(path)) {
|
|
209
|
+
throw ErrnoError.With('EEXIST', path, 'mknod');
|
|
210
|
+
}
|
|
211
|
+
this.devices.set(path, dev);
|
|
212
|
+
return dev;
|
|
213
|
+
}
|
|
176
214
|
/**
|
|
177
215
|
* Adds default devices
|
|
178
216
|
*/
|
|
179
217
|
addDefaults() {
|
|
180
|
-
this.
|
|
181
|
-
this.
|
|
182
|
-
this.
|
|
183
|
-
this.
|
|
218
|
+
this._createDevice(nullDevice);
|
|
219
|
+
this._createDevice(zeroDevice);
|
|
220
|
+
this._createDevice(fullDevice);
|
|
221
|
+
this._createDevice(randomDevice);
|
|
222
|
+
this._createDevice(consoleDevice);
|
|
184
223
|
}
|
|
185
224
|
constructor() {
|
|
186
225
|
super(new InMemoryStore('devfs'));
|
|
@@ -351,10 +390,11 @@ function defaultWrite(file, buffer, offset, length) {
|
|
|
351
390
|
* Simulates the `/dev/null` device.
|
|
352
391
|
* - Reads return 0 bytes (EOF).
|
|
353
392
|
* - Writes discard data, advancing the file position.
|
|
354
|
-
* @
|
|
393
|
+
* @internal
|
|
355
394
|
*/
|
|
356
395
|
export const nullDevice = {
|
|
357
396
|
name: 'null',
|
|
397
|
+
singleton: true,
|
|
358
398
|
init() {
|
|
359
399
|
return { major: 1, minor: 3 };
|
|
360
400
|
},
|
|
@@ -371,10 +411,11 @@ export const nullDevice = {
|
|
|
371
411
|
* - Reads fill the buffer with zeroes.
|
|
372
412
|
* - Writes discard data but update the file position.
|
|
373
413
|
* - Provides basic file metadata, treating it as a character device.
|
|
374
|
-
* @
|
|
414
|
+
* @internal
|
|
375
415
|
*/
|
|
376
416
|
export const zeroDevice = {
|
|
377
417
|
name: 'zero',
|
|
418
|
+
singleton: true,
|
|
378
419
|
init() {
|
|
379
420
|
return { major: 1, minor: 5 };
|
|
380
421
|
},
|
|
@@ -392,10 +433,11 @@ export const zeroDevice = {
|
|
|
392
433
|
* Simulates the `/dev/full` device.
|
|
393
434
|
* - Reads behave like `/dev/zero` (returns zeroes).
|
|
394
435
|
* - Writes always fail with ENOSPC (no space left on device).
|
|
395
|
-
* @
|
|
436
|
+
* @internal
|
|
396
437
|
*/
|
|
397
438
|
export const fullDevice = {
|
|
398
439
|
name: 'full',
|
|
440
|
+
singleton: true,
|
|
399
441
|
init() {
|
|
400
442
|
return { major: 1, minor: 7 };
|
|
401
443
|
},
|
|
@@ -415,10 +457,11 @@ export const fullDevice = {
|
|
|
415
457
|
* Simulates the `/dev/random` device.
|
|
416
458
|
* - Reads return random bytes.
|
|
417
459
|
* - Writes discard data, advancing the file position.
|
|
418
|
-
* @
|
|
460
|
+
* @internal
|
|
419
461
|
*/
|
|
420
462
|
export const randomDevice = {
|
|
421
463
|
name: 'random',
|
|
464
|
+
singleton: true,
|
|
422
465
|
init() {
|
|
423
466
|
return { major: 1, minor: 8 };
|
|
424
467
|
},
|
|
@@ -432,13 +475,33 @@ export const randomDevice = {
|
|
|
432
475
|
},
|
|
433
476
|
write: defaultWrite,
|
|
434
477
|
};
|
|
478
|
+
/**
|
|
479
|
+
* Simulates the `/dev/console` device.
|
|
480
|
+
* @experimental @internal
|
|
481
|
+
*/
|
|
482
|
+
const consoleDevice = {
|
|
483
|
+
name: 'console',
|
|
484
|
+
singleton: true,
|
|
485
|
+
init(ino, { output = console.log } = {}) {
|
|
486
|
+
return { major: 5, minor: 1, data: { output } };
|
|
487
|
+
},
|
|
488
|
+
read() {
|
|
489
|
+
return 0;
|
|
490
|
+
},
|
|
491
|
+
write(file, buffer, offset, length) {
|
|
492
|
+
const text = decodeUTF8(buffer.slice(offset, offset + length));
|
|
493
|
+
file.device.data.output(text);
|
|
494
|
+
file.position += length;
|
|
495
|
+
return length;
|
|
496
|
+
},
|
|
497
|
+
};
|
|
435
498
|
/**
|
|
436
499
|
* Shortcuts for importing.
|
|
437
|
-
* @experimental
|
|
438
500
|
*/
|
|
439
|
-
export
|
|
501
|
+
export const devices = {
|
|
440
502
|
null: nullDevice,
|
|
441
503
|
zero: zeroDevice,
|
|
442
504
|
full: fullDevice,
|
|
443
505
|
random: randomDevice,
|
|
506
|
+
console: consoleDevice,
|
|
444
507
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import type * as fs from 'node:fs';
|
|
3
|
+
import { ErrnoError } from '../error.js';
|
|
3
4
|
import type { FileContents } from '../filesystem.js';
|
|
4
5
|
import { BigIntStats, type Stats } from '../stats.js';
|
|
5
6
|
import { type Callback } from '../utils.js';
|
|
@@ -309,4 +310,12 @@ export declare function statfs(this: V_Context, path: fs.PathLike, options: fs.S
|
|
|
309
310
|
bigint: true;
|
|
310
311
|
}, callback: Callback<[fs.BigIntStatsFs]>): void;
|
|
311
312
|
export declare function openAsBlob(this: V_Context, path: fs.PathLike, options?: fs.OpenAsBlobOptions): Promise<Blob>;
|
|
313
|
+
type GlobCallback<Args extends unknown[]> = (e: ErrnoError | null, ...args: Args) => unknown;
|
|
314
|
+
/**
|
|
315
|
+
* Retrieves the files matching the specified pattern.
|
|
316
|
+
*/
|
|
317
|
+
export declare function glob(this: V_Context, pattern: string | string[], callback: GlobCallback<[string[]]>): void;
|
|
318
|
+
export declare function glob(this: V_Context, pattern: string | string[], options: fs.GlobOptionsWithFileTypes, callback: GlobCallback<[Dirent[]]>): void;
|
|
319
|
+
export declare function glob(this: V_Context, pattern: string | string[], options: fs.GlobOptionsWithoutFileTypes, callback: GlobCallback<[string[]]>): void;
|
|
320
|
+
export declare function glob(this: V_Context, pattern: string | string[], options: fs.GlobOptions, callback: GlobCallback<[Dirent[] | string[]]>): void;
|
|
312
321
|
export {};
|
package/dist/emulation/async.js
CHANGED
|
@@ -8,6 +8,16 @@ import { fd2file, fdMap } from './shared.js';
|
|
|
8
8
|
import { ReadStream, WriteStream } from './streams.js';
|
|
9
9
|
import { FSWatcher, StatWatcher } from './watchers.js';
|
|
10
10
|
const nop = () => { };
|
|
11
|
+
/**
|
|
12
|
+
* Helper to collect an async iterator into an array
|
|
13
|
+
*/
|
|
14
|
+
async function collectAsyncIterator(it) {
|
|
15
|
+
const results = [];
|
|
16
|
+
for await (const result of it) {
|
|
17
|
+
results.push(result);
|
|
18
|
+
}
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Asynchronous rename. No arguments other than a possible exception are given to the completion callback.
|
|
13
23
|
*/
|
|
@@ -556,3 +566,11 @@ export async function openAsBlob(path, options) {
|
|
|
556
566
|
return new Blob([buffer], options);
|
|
557
567
|
}
|
|
558
568
|
openAsBlob;
|
|
569
|
+
export function glob(pattern, options, callback = nop) {
|
|
570
|
+
callback = typeof options == 'function' ? options : callback;
|
|
571
|
+
const it = promises.glob.call(this, pattern, typeof options === 'function' ? undefined : options);
|
|
572
|
+
collectAsyncIterator(it)
|
|
573
|
+
.then(results => callback(null, results ?? []))
|
|
574
|
+
.catch((e) => callback(e));
|
|
575
|
+
}
|
|
576
|
+
glob;
|
|
@@ -4,5 +4,6 @@ export * as promises from './promises.js';
|
|
|
4
4
|
export * as constants from './constants.js';
|
|
5
5
|
export * from './streams.js';
|
|
6
6
|
export * from './dir.js';
|
|
7
|
-
export {
|
|
7
|
+
export { mount, umount, chroot, mountObject } from './shared.js';
|
|
8
|
+
export { /** @deprecated security */ mounts } from './shared.js';
|
|
8
9
|
export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
|
package/dist/emulation/index.js
CHANGED
|
@@ -4,5 +4,6 @@ export * as promises from './promises.js';
|
|
|
4
4
|
export * as constants from './constants.js';
|
|
5
5
|
export * from './streams.js';
|
|
6
6
|
export * from './dir.js';
|
|
7
|
-
export {
|
|
7
|
+
export { mount, umount, chroot, mountObject } from './shared.js';
|
|
8
|
+
export { /** @deprecated security */ mounts } from './shared.js';
|
|
8
9
|
export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
|
|
@@ -10,8 +10,8 @@ import type { FileContents } from '../filesystem.js';
|
|
|
10
10
|
import '../polyfills.js';
|
|
11
11
|
import { BigIntStats, type Stats } from '../stats.js';
|
|
12
12
|
import { Dir, Dirent } from './dir.js';
|
|
13
|
-
import { type InternalOptions, type ReaddirOptions } from './shared.js';
|
|
14
13
|
import { ReadStream, WriteStream } from './streams.js';
|
|
14
|
+
import type { InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
|
|
15
15
|
export * as constants from './constants.js';
|
|
16
16
|
export declare class FileHandle implements promises.FileHandle {
|
|
17
17
|
protected context?: V_Context | undefined;
|
|
@@ -71,7 +71,9 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
71
71
|
* @param length The number of bytes to read.
|
|
72
72
|
* @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.
|
|
73
73
|
*/
|
|
74
|
-
read<
|
|
74
|
+
read<T extends NodeJS.ArrayBufferView>(buffer: T, offset?: number, length?: number, position?: number | null): Promise<promises.FileReadResult<T>>;
|
|
75
|
+
read<T extends NodeJS.ArrayBufferView = Buffer>(buffer: T, options?: promises.FileReadOptions<T>): Promise<promises.FileReadResult<T>>;
|
|
76
|
+
read<T extends NodeJS.ArrayBufferView = Buffer>(options?: promises.FileReadOptions<T>): Promise<promises.FileReadResult<T>>;
|
|
75
77
|
/**
|
|
76
78
|
* Asynchronously reads the entire contents of a file. The underlying file will _not_ be closed automatically.
|
|
77
79
|
* The `FileHandle` must have been opened for reading.
|
|
@@ -90,9 +92,6 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
90
92
|
* While the `ReadableStream` will read the file to completion,
|
|
91
93
|
* it will not close the `FileHandle` automatically.
|
|
92
94
|
* User code must still call the `fileHandle.close()` method.
|
|
93
|
-
*
|
|
94
|
-
* @since v17.0.0
|
|
95
|
-
* @experimental
|
|
96
95
|
*/
|
|
97
96
|
readableWebStream(options?: promises.ReadableWebStreamOptions): TReadableStream<Uint8Array>;
|
|
98
97
|
/**
|
|
@@ -113,17 +112,13 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
113
112
|
* It is unsafe to call `write()` multiple times on the same file without waiting for the `Promise`
|
|
114
113
|
* to be resolved (or rejected). For this scenario, `fs.createWriteStream` is strongly recommended.
|
|
115
114
|
*/
|
|
116
|
-
write(data:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
write<T extends FileContents>(data: T, options?: number | null | {
|
|
116
|
+
offset?: number;
|
|
117
|
+
length?: number;
|
|
118
|
+
position?: number;
|
|
119
|
+
}, lenOrEnc?: BufferEncoding | number | null, position?: number | null): Promise<{
|
|
121
120
|
bytesWritten: number;
|
|
122
|
-
buffer:
|
|
123
|
-
}>;
|
|
124
|
-
write(data: string, position?: number, encoding?: BufferEncoding): Promise<{
|
|
125
|
-
bytesWritten: number;
|
|
126
|
-
buffer: string;
|
|
121
|
+
buffer: T;
|
|
127
122
|
}>;
|
|
128
123
|
/**
|
|
129
124
|
* Asynchronously writes data to a file, replacing the file if it already exists. The underlying file will _not_ be closed automatically.
|
|
@@ -256,19 +251,19 @@ export declare function mkdir(this: V_Context, path: fs.PathLike, options?: fs.M
|
|
|
256
251
|
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
257
252
|
* @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'`.
|
|
258
253
|
*/
|
|
259
|
-
export declare function readdir(this: V_Context, path: fs.PathLike, options?:
|
|
254
|
+
export declare function readdir(this: V_Context, path: fs.PathLike, options?: ReaddirOptsI<{
|
|
260
255
|
withFileTypes?: false;
|
|
261
|
-
}
|
|
256
|
+
}> | NullEnc): Promise<string[]>;
|
|
262
257
|
export declare function readdir(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption & ReaddirOptions & {
|
|
263
258
|
withFileTypes?: false;
|
|
264
259
|
}): Promise<Buffer[]>;
|
|
265
|
-
export declare function readdir(this: V_Context, path: fs.PathLike, options?:
|
|
260
|
+
export declare function readdir(this: V_Context, path: fs.PathLike, options?: ReaddirOptsI<{
|
|
266
261
|
withFileTypes?: false;
|
|
267
|
-
}
|
|
268
|
-
export declare function readdir(this: V_Context, path: fs.PathLike, options:
|
|
262
|
+
}> | NullEnc): Promise<string[] | Buffer[]>;
|
|
263
|
+
export declare function readdir(this: V_Context, path: fs.PathLike, options: ReaddirOptsI<{
|
|
269
264
|
withFileTypes: true;
|
|
270
|
-
}): Promise<Dirent[]>;
|
|
271
|
-
export declare function readdir(this: V_Context, path: fs.PathLike, options?:
|
|
265
|
+
}>): Promise<Dirent[]>;
|
|
266
|
+
export declare function readdir(this: V_Context, path: fs.PathLike, options?: ReaddirOptsU<fs.BufferEncodingOption> | NullEnc): Promise<string[] | Dirent[] | Buffer[]>;
|
|
272
267
|
export declare function link(this: V_Context, targetPath: fs.PathLike, linkPath: fs.PathLike): Promise<void>;
|
|
273
268
|
/**
|
|
274
269
|
* `symlink`.
|
|
@@ -357,3 +352,11 @@ export declare function statfs(this: V_Context, path: fs.PathLike, opts: fs.Stat
|
|
|
357
352
|
bigint: true;
|
|
358
353
|
}): Promise<fs.BigIntStatsFs>;
|
|
359
354
|
export declare function statfs(this: V_Context, path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs>;
|
|
355
|
+
/**
|
|
356
|
+
* Retrieves the files matching the specified pattern.
|
|
357
|
+
* @todo Implement
|
|
358
|
+
*/
|
|
359
|
+
export declare function glob(this: V_Context, pattern: string | string[]): NodeJS.AsyncIterator<string>;
|
|
360
|
+
export declare function glob(this: V_Context, pattern: string | string[], opt: fs.GlobOptionsWithFileTypes): NodeJS.AsyncIterator<Dirent>;
|
|
361
|
+
export declare function glob(this: V_Context, pattern: string | string[], opt: fs.GlobOptionsWithoutFileTypes): NodeJS.AsyncIterator<string>;
|
|
362
|
+
export declare function glob(this: V_Context, pattern: string | string[], opt: fs.GlobOptions): NodeJS.AsyncIterator<Dirent | string>;
|
|
@@ -147,19 +147,23 @@ export class FileHandle {
|
|
|
147
147
|
await this.file.write(encodedData, 0, encodedData.length);
|
|
148
148
|
emitChange('change', this.file.path);
|
|
149
149
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
150
|
+
async read(buffer, offset, length, position) {
|
|
151
|
+
if (typeof offset == 'object' && offset != null) {
|
|
152
|
+
position = offset.position;
|
|
153
|
+
length = offset.length;
|
|
154
|
+
offset = offset.offset;
|
|
155
|
+
}
|
|
156
|
+
if (!ArrayBuffer.isView(buffer) && typeof buffer == 'object') {
|
|
157
|
+
position = buffer.position;
|
|
158
|
+
length = buffer.length;
|
|
159
|
+
offset = buffer.offset;
|
|
160
|
+
buffer = buffer.buffer;
|
|
161
|
+
}
|
|
159
162
|
if (isNaN(+position)) {
|
|
160
163
|
position = this.file.position;
|
|
161
164
|
}
|
|
162
|
-
|
|
165
|
+
buffer || (buffer = new Uint8Array((await this.file.stat()).size));
|
|
166
|
+
return this.file.read(buffer, offset ?? undefined, length ?? undefined, position ?? undefined);
|
|
163
167
|
}
|
|
164
168
|
async readFile(_options) {
|
|
165
169
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
@@ -180,9 +184,6 @@ export class FileHandle {
|
|
|
180
184
|
* While the `ReadableStream` will read the file to completion,
|
|
181
185
|
* it will not close the `FileHandle` automatically.
|
|
182
186
|
* User code must still call the `fileHandle.close()` method.
|
|
183
|
-
*
|
|
184
|
-
* @since v17.0.0
|
|
185
|
-
* @experimental
|
|
186
187
|
*/
|
|
187
188
|
readableWebStream(options = {}) {
|
|
188
189
|
// Note: using an arrow function to preserve `this`
|
|
@@ -230,27 +231,36 @@ export class FileHandle {
|
|
|
230
231
|
}
|
|
231
232
|
return opts?.bigint ? new BigIntStats(stats) : stats;
|
|
232
233
|
}
|
|
233
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Asynchronously writes `string` to the file.
|
|
236
|
+
* The `FileHandle` must have been opened for writing.
|
|
237
|
+
* It is unsafe to call `write()` multiple times on the same file without waiting for the `Promise`
|
|
238
|
+
* to be resolved (or rejected). For this scenario, `fs.createWriteStream` is strongly recommended.
|
|
239
|
+
*/
|
|
240
|
+
async write(data, options, lenOrEnc, position) {
|
|
234
241
|
let buffer, offset, length;
|
|
242
|
+
if (typeof options == 'object') {
|
|
243
|
+
lenOrEnc = options?.length;
|
|
244
|
+
position = options?.position;
|
|
245
|
+
options = options?.offset;
|
|
246
|
+
}
|
|
235
247
|
if (typeof data === 'string') {
|
|
236
|
-
|
|
237
|
-
position = typeof posOrOff === 'number' ? posOrOff : null;
|
|
248
|
+
position = typeof options === 'number' ? options : null;
|
|
238
249
|
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8';
|
|
239
250
|
offset = 0;
|
|
240
251
|
buffer = Buffer.from(data, encoding);
|
|
241
252
|
length = buffer.length;
|
|
242
253
|
}
|
|
243
254
|
else {
|
|
244
|
-
// Signature 2: (fd, buffer, offset, length, position?)
|
|
245
255
|
buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
246
|
-
offset =
|
|
256
|
+
offset = options;
|
|
247
257
|
length = lenOrEnc;
|
|
248
258
|
position = typeof position === 'number' ? position : null;
|
|
249
259
|
}
|
|
250
260
|
position ?? (position = this.file.position);
|
|
251
261
|
const bytesWritten = await this.file.write(buffer, offset, length, position);
|
|
252
262
|
emitChange('change', this.file.path);
|
|
253
|
-
return { buffer, bytesWritten };
|
|
263
|
+
return { buffer: data, bytesWritten };
|
|
254
264
|
}
|
|
255
265
|
/**
|
|
256
266
|
* Asynchronously writes data to a file, replacing the file if it already exists. The underlying file will _not_ be closed automatically.
|
|
@@ -938,6 +948,9 @@ export function watch(filename, options = {}) {
|
|
|
938
948
|
},
|
|
939
949
|
return: cleanup,
|
|
940
950
|
throw: cleanup,
|
|
951
|
+
[Symbol.asyncDispose]() {
|
|
952
|
+
return Promise.resolve();
|
|
953
|
+
},
|
|
941
954
|
};
|
|
942
955
|
},
|
|
943
956
|
};
|
|
@@ -1088,3 +1101,35 @@ export async function statfs(path, opts) {
|
|
|
1088
1101
|
const { fs } = resolveMount(path, this);
|
|
1089
1102
|
return Promise.resolve(_statfs(fs, opts?.bigint));
|
|
1090
1103
|
}
|
|
1104
|
+
export function glob(pattern, opt) {
|
|
1105
|
+
pattern = Array.isArray(pattern) ? pattern : [pattern];
|
|
1106
|
+
const { cwd = '/', withFileTypes = false, exclude = () => false } = opt || {};
|
|
1107
|
+
// Escape special characters in pattern
|
|
1108
|
+
const regexPatterns = pattern.map(p => {
|
|
1109
|
+
p = p
|
|
1110
|
+
.replace(/([.?+^$(){}|[\]/])/g, '$1')
|
|
1111
|
+
.replace(/\*\*/g, '.*')
|
|
1112
|
+
.replace(/\*/g, '[^/]*')
|
|
1113
|
+
.replace(/\?/g, '.');
|
|
1114
|
+
return new RegExp(`^${p}$`);
|
|
1115
|
+
});
|
|
1116
|
+
async function* recursiveList(dir) {
|
|
1117
|
+
const entries = await readdir(dir, { withFileTypes, encoding: 'utf8' });
|
|
1118
|
+
for (const entry of entries) {
|
|
1119
|
+
const fullPath = withFileTypes ? entry.path : dir + '/' + entry;
|
|
1120
|
+
if (exclude((withFileTypes ? entry : fullPath)))
|
|
1121
|
+
continue;
|
|
1122
|
+
/**
|
|
1123
|
+
* @todo it the pattern.source check correct?
|
|
1124
|
+
*/
|
|
1125
|
+
if ((await stat(fullPath)).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
|
|
1126
|
+
yield* recursiveList(fullPath);
|
|
1127
|
+
}
|
|
1128
|
+
if (regexPatterns.some(pattern => pattern.test(fullPath.replace(/^\/+/g, '')))) {
|
|
1129
|
+
yield withFileTypes ? entry : fullPath.replace(/^\/+/g, '');
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return recursiveList(cwd);
|
|
1134
|
+
}
|
|
1135
|
+
glob;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type * as fs from 'node:fs';
|
|
2
2
|
import { type BoundContext, type V_Context } from '../context.js';
|
|
3
3
|
import { ErrnoError } from '../error.js';
|
|
4
4
|
import type { File } from '../file.js';
|
|
@@ -48,12 +48,6 @@ export interface ResolvedMount {
|
|
|
48
48
|
* @internal @hidden
|
|
49
49
|
*/
|
|
50
50
|
export declare function resolveMount(path: string, ctx: V_Context): ResolvedMount;
|
|
51
|
-
/**
|
|
52
|
-
* Wait for all file systems to be ready and synced.
|
|
53
|
-
* May be removed at some point.
|
|
54
|
-
* @experimental @internal
|
|
55
|
-
*/
|
|
56
|
-
export declare function _synced(): Promise<void>;
|
|
57
51
|
/**
|
|
58
52
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
59
53
|
* @internal @hidden
|
|
@@ -71,23 +65,7 @@ export declare function mountObject(mounts: MountObject): void;
|
|
|
71
65
|
/**
|
|
72
66
|
* @internal @hidden
|
|
73
67
|
*/
|
|
74
|
-
export declare function _statfs<const T extends boolean>(fs: FileSystem, bigint?: T): T extends true ? BigIntStatsFs : StatsFs;
|
|
75
|
-
/**
|
|
76
|
-
* Options used for caching, among other things.
|
|
77
|
-
* @internal @hidden *UNSTABLE*
|
|
78
|
-
*/
|
|
79
|
-
export interface InternalOptions {
|
|
80
|
-
/**
|
|
81
|
-
* If true, then this readdir was called from another function.
|
|
82
|
-
* In this case, don't clear the cache when done.
|
|
83
|
-
* @internal *UNSTABLE*
|
|
84
|
-
*/
|
|
85
|
-
_isIndirect?: boolean;
|
|
86
|
-
}
|
|
87
|
-
export interface ReaddirOptions extends InternalOptions {
|
|
88
|
-
withFileTypes?: boolean;
|
|
89
|
-
recursive?: boolean;
|
|
90
|
-
}
|
|
68
|
+
export declare function _statfs<const T extends boolean>(fs: FileSystem, bigint?: T): T extends true ? fs.BigIntStatsFs : fs.StatsFs;
|
|
91
69
|
/**
|
|
92
70
|
* Change the root path
|
|
93
71
|
* @param inPlace if true, this changes the root for the current context instead of creating a new one (if associated with a context).
|
package/dist/emulation/shared.js
CHANGED
|
@@ -85,15 +85,6 @@ export function resolveMount(path, ctx) {
|
|
|
85
85
|
}
|
|
86
86
|
throw new ErrnoError(Errno.EIO, 'No file system');
|
|
87
87
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Wait for all file systems to be ready and synced.
|
|
90
|
-
* May be removed at some point.
|
|
91
|
-
* @experimental @internal
|
|
92
|
-
*/
|
|
93
|
-
export async function _synced() {
|
|
94
|
-
await Promise.all([...mounts.values()].map(m => m.ready()));
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
88
|
/**
|
|
98
89
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
99
90
|
* @internal @hidden
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type * as
|
|
1
|
+
import type * as fs from 'node:fs';
|
|
2
2
|
import { Readable, Writable } from 'readable-stream';
|
|
3
3
|
import type { Callback } from '../utils.js';
|
|
4
|
-
export declare class ReadStream extends Readable implements
|
|
5
|
-
close(callback?: Callback): void;
|
|
4
|
+
export declare class ReadStream extends Readable implements fs.ReadStream {
|
|
5
|
+
close(callback?: Callback<[void], null>): void;
|
|
6
6
|
wrap(oldStream: NodeJS.ReadableStream): this;
|
|
7
7
|
bytesRead: number;
|
|
8
8
|
path: string | Buffer;
|
|
9
9
|
pending: boolean;
|
|
10
10
|
}
|
|
11
|
-
export declare class WriteStream extends Writable implements
|
|
12
|
-
close(callback?: Callback): void;
|
|
11
|
+
export declare class WriteStream extends Writable implements fs.WriteStream {
|
|
12
|
+
close(callback?: Callback<[void], null>): void;
|
|
13
13
|
bytesWritten: number;
|
|
14
14
|
path: string | Buffer;
|
|
15
15
|
pending: boolean;
|
|
@@ -5,7 +5,7 @@ export class ReadStream extends Readable {
|
|
|
5
5
|
try {
|
|
6
6
|
super.destroy();
|
|
7
7
|
super.emit('close');
|
|
8
|
-
callback();
|
|
8
|
+
callback(null);
|
|
9
9
|
}
|
|
10
10
|
catch (err) {
|
|
11
11
|
callback(new ErrnoError(Errno.EIO, err.toString()));
|
|
@@ -21,7 +21,7 @@ export class WriteStream extends Writable {
|
|
|
21
21
|
try {
|
|
22
22
|
super.destroy();
|
|
23
23
|
super.emit('close');
|
|
24
|
-
callback();
|
|
24
|
+
callback(null);
|
|
25
25
|
}
|
|
26
26
|
catch (err) {
|
|
27
27
|
callback(new ErrnoError(Errno.EIO, err.toString()));
|
package/dist/emulation/sync.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type * as fs from 'node:fs';
|
|
|
3
3
|
import type { FileContents } from '../filesystem.js';
|
|
4
4
|
import { BigIntStats, type Stats } from '../stats.js';
|
|
5
5
|
import { Dir, Dirent } from './dir.js';
|
|
6
|
-
import { type InternalOptions, type ReaddirOptions } from './shared.js';
|
|
7
6
|
import type { V_Context } from '../context.js';
|
|
7
|
+
import type { ReaddirOptsI, ReaddirOptsU, InternalOptions, ReaddirOptions, NullEnc } from './types.js';
|
|
8
8
|
export declare function renameSync(this: V_Context, oldPath: fs.PathLike, newPath: fs.PathLike): void;
|
|
9
9
|
/**
|
|
10
10
|
* Test whether or not `path` exists by checking with the file system.
|
|
@@ -112,19 +112,19 @@ export declare function mkdirSync(this: V_Context, path: fs.PathLike, options?:
|
|
|
112
112
|
recursive?: false;
|
|
113
113
|
}) | null): void;
|
|
114
114
|
export declare function mkdirSync(this: V_Context, path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): string | undefined;
|
|
115
|
-
export declare function readdirSync(this: V_Context, path: fs.PathLike, options?:
|
|
115
|
+
export declare function readdirSync(this: V_Context, path: fs.PathLike, options?: ReaddirOptsI<{
|
|
116
116
|
withFileTypes?: false;
|
|
117
|
-
}
|
|
117
|
+
}> | NullEnc): string[];
|
|
118
118
|
export declare function readdirSync(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption & ReaddirOptions & {
|
|
119
119
|
withFileTypes?: false;
|
|
120
120
|
}): Buffer[];
|
|
121
|
-
export declare function readdirSync(this: V_Context, path: fs.PathLike, options?:
|
|
121
|
+
export declare function readdirSync(this: V_Context, path: fs.PathLike, options?: ReaddirOptsI<{
|
|
122
122
|
withFileTypes?: false;
|
|
123
|
-
}
|
|
124
|
-
export declare function readdirSync(this: V_Context, path: fs.PathLike, options:
|
|
123
|
+
}> | NullEnc): string[] | Buffer[];
|
|
124
|
+
export declare function readdirSync(this: V_Context, path: fs.PathLike, options: ReaddirOptsI<{
|
|
125
125
|
withFileTypes: true;
|
|
126
|
-
}): Dirent[];
|
|
127
|
-
export declare function readdirSync(this: V_Context, path: fs.PathLike, options?:
|
|
126
|
+
}>): Dirent[];
|
|
127
|
+
export declare function readdirSync(this: V_Context, path: fs.PathLike, options?: ReaddirOptsU<fs.BufferEncodingOption> | NullEnc): string[] | Dirent[] | Buffer[];
|
|
128
128
|
export declare function linkSync(this: V_Context, targetPath: fs.PathLike, linkPath: fs.PathLike): void;
|
|
129
129
|
/**
|
|
130
130
|
* Synchronous `symlink`.
|
|
@@ -219,3 +219,10 @@ export declare function statfsSync(this: V_Context, path: fs.PathLike, options:
|
|
|
219
219
|
bigint: true;
|
|
220
220
|
}): fs.BigIntStatsFs;
|
|
221
221
|
export declare function statfsSync(this: V_Context, path: fs.PathLike, options?: fs.StatFsOptions): fs.StatsFs | fs.BigIntStatsFs;
|
|
222
|
+
/**
|
|
223
|
+
* Retrieves the files matching the specified pattern.
|
|
224
|
+
*/
|
|
225
|
+
export declare function globSync(pattern: string | string[]): string[];
|
|
226
|
+
export declare function globSync(pattern: string | string[], options: fs.GlobOptionsWithFileTypes): Dirent[];
|
|
227
|
+
export declare function globSync(pattern: string | string[], options: fs.GlobOptionsWithoutFileTypes): string[];
|
|
228
|
+
export declare function globSync(pattern: string | string[], options: fs.GlobOptions): Dirent[] | string[];
|
package/dist/emulation/sync.js
CHANGED
|
@@ -796,3 +796,37 @@ export function statfsSync(path, options) {
|
|
|
796
796
|
const { fs } = resolveMount(path, this);
|
|
797
797
|
return _statfs(fs, options?.bigint);
|
|
798
798
|
}
|
|
799
|
+
export function globSync(pattern, options = {}) {
|
|
800
|
+
pattern = Array.isArray(pattern) ? pattern : [pattern];
|
|
801
|
+
const { cwd = '/', withFileTypes = false, exclude = () => false } = options;
|
|
802
|
+
// Escape special characters in pattern
|
|
803
|
+
const regexPatterns = pattern.map(p => {
|
|
804
|
+
p = p
|
|
805
|
+
.replace(/([.?+^$(){}|[\]/])/g, '\\$1')
|
|
806
|
+
.replace(/\*\*/g, '.*')
|
|
807
|
+
.replace(/\*/g, '[^/]*')
|
|
808
|
+
.replace(/\?/g, '.');
|
|
809
|
+
return new RegExp(`^${p}$`);
|
|
810
|
+
});
|
|
811
|
+
const results = [];
|
|
812
|
+
function recursiveList(dir) {
|
|
813
|
+
const entries = readdirSync(dir, { withFileTypes, encoding: 'utf8' });
|
|
814
|
+
for (const entry of entries) {
|
|
815
|
+
const fullPath = withFileTypes ? entry.path : dir + '/' + entry;
|
|
816
|
+
if (exclude((withFileTypes ? entry : fullPath)))
|
|
817
|
+
continue;
|
|
818
|
+
/**
|
|
819
|
+
* @todo it the pattern.source check correct?
|
|
820
|
+
*/
|
|
821
|
+
if (statSync(fullPath).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
|
|
822
|
+
recursiveList(fullPath);
|
|
823
|
+
}
|
|
824
|
+
if (regexPatterns.some(pattern => pattern.test(fullPath.replace(/^\/+/g, '')))) {
|
|
825
|
+
results.push(withFileTypes ? entry.path : fullPath.replace(/^\/+/g, ''));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
recursiveList(cwd);
|
|
830
|
+
return results;
|
|
831
|
+
}
|
|
832
|
+
globSync;
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ export * from './config.js';
|
|
|
12
12
|
export * from './context.js';
|
|
13
13
|
export * from './credentials.js';
|
|
14
14
|
export * from './devices.js';
|
|
15
|
-
export { default as devices } from './devices.js';
|
|
16
15
|
export * from './file.js';
|
|
17
16
|
export * from './filesystem.js';
|
|
18
17
|
export * from './inode.js';
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,6 @@ export * from './config.js';
|
|
|
12
12
|
export * from './context.js';
|
|
13
13
|
export * from './credentials.js';
|
|
14
14
|
export * from './devices.js';
|
|
15
|
-
export { default as devices } from './devices.js';
|
|
16
15
|
export * from './file.js';
|
|
17
16
|
export * from './filesystem.js';
|
|
18
17
|
export * from './inode.js';
|
package/dist/utils.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export declare function decodeDirListing(data: Uint8Array): Record<string, bigin
|
|
|
49
49
|
* @hidden
|
|
50
50
|
*/
|
|
51
51
|
export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
|
|
52
|
-
export type Callback<Args extends unknown[] = []> = (e
|
|
52
|
+
export type Callback<Args extends unknown[] = [], NoError = undefined | void> = (e: ErrnoError | NoError, ...args: OptionalTuple<Args>) => unknown;
|
|
53
53
|
/**
|
|
54
54
|
* Normalizes a mode
|
|
55
55
|
* @internal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"build": "tsc -p tsconfig.json",
|
|
59
59
|
"build:docs": "typedoc",
|
|
60
60
|
"dev": "npm run build -- --watch",
|
|
61
|
-
"prepublishOnly": "npm run build"
|
|
61
|
+
"prepublishOnly": "npm run build",
|
|
62
|
+
"install": "patch-package"
|
|
62
63
|
},
|
|
63
64
|
"lint-staged": {
|
|
64
65
|
"*": [
|
|
@@ -66,7 +67,7 @@
|
|
|
66
67
|
]
|
|
67
68
|
},
|
|
68
69
|
"dependencies": {
|
|
69
|
-
"@types/node": "^
|
|
70
|
+
"@types/node": "^22.10.1",
|
|
70
71
|
"@types/readable-stream": "^4.0.10",
|
|
71
72
|
"buffer": "^6.0.3",
|
|
72
73
|
"eventemitter3": "^5.0.1",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"eslint": "^9.15.0",
|
|
84
85
|
"globals": "^15.9.0",
|
|
85
86
|
"lint-staged": "^15.2.7",
|
|
87
|
+
"patch-package": "^8.0.0",
|
|
86
88
|
"prettier": "^3.2.5",
|
|
87
89
|
"tsx": "^4.19.1",
|
|
88
90
|
"typedoc": "^0.27.1",
|
package/readme.md
CHANGED
|
@@ -174,31 +174,31 @@ You can create your own devices by implementing a `DeviceDriver`. For example, t
|
|
|
174
174
|
```ts
|
|
175
175
|
const customNullDevice = {
|
|
176
176
|
name: 'custom_null',
|
|
177
|
+
// only 1 can exist per DeviceFS
|
|
178
|
+
singleton: true,
|
|
179
|
+
// optional if false
|
|
177
180
|
isBuffered: false,
|
|
178
181
|
read() {
|
|
179
182
|
return 0;
|
|
180
183
|
},
|
|
181
|
-
write() {
|
|
184
|
+
write() {
|
|
185
|
+
return 0;
|
|
186
|
+
},
|
|
182
187
|
};
|
|
183
188
|
```
|
|
184
189
|
|
|
185
190
|
Note the actual implementation's write is slightly more complicated since it adds to the file position. You can find more information on the docs.
|
|
186
191
|
|
|
187
|
-
Finally, if you'd like to use your custom device with the file system
|
|
192
|
+
Finally, if you'd like to use your custom device with the file system:
|
|
188
193
|
|
|
189
194
|
```ts
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
import { addDevice, fs } from '@zenfs/core';
|
|
196
|
+
|
|
197
|
+
addDevice(customNullDevice);
|
|
192
198
|
|
|
193
199
|
fs.writeFileSync('/dev/custom', 'This gets discarded.');
|
|
194
200
|
```
|
|
195
201
|
|
|
196
|
-
In the above example, `createDevice` works relative to the `DeviceFS` mount point.
|
|
197
|
-
|
|
198
|
-
Additionally, a type assertion (` as ...`) is used since `fs.mounts` does not keep track of which file system type is mapped to which mount point. Doing so would create significant maintenance costs due to the complexity of implementing it.
|
|
199
|
-
|
|
200
|
-
If you would like to see a more intuitive way adding custom devices (e.g. `fs.mknod`), please feel free to open an issue for a feature request.
|
|
201
|
-
|
|
202
202
|
## Using with bundlers
|
|
203
203
|
|
|
204
204
|
ZenFS exports a drop-in for Node's `fs` module (up to the version of `@types/node` in package.json), so you can use it for your bundler of preference using the default export.
|
package/tests/fs/dir.test.ts
CHANGED
|
@@ -13,8 +13,6 @@ for (const file of testFiles) {
|
|
|
13
13
|
fs.writeFileSync(`${testDirPath}/${file}`, 'Sample content');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
await fs._synced();
|
|
17
|
-
|
|
18
16
|
suite('Dirent', () => {
|
|
19
17
|
test('name and parentPath getters', async () => {
|
|
20
18
|
const stats = await fs.promises.lstat(testFile);
|
package/tests/fs/streams.test.ts
CHANGED
|
@@ -33,7 +33,7 @@ suite('ReadStream', () => {
|
|
|
33
33
|
closed = true;
|
|
34
34
|
});
|
|
35
35
|
readStream.close(err => {
|
|
36
|
-
assert.
|
|
36
|
+
assert.equal(err, null);
|
|
37
37
|
assert(closed);
|
|
38
38
|
done();
|
|
39
39
|
});
|
|
@@ -58,10 +58,10 @@ suite('ReadStream', () => {
|
|
|
58
58
|
test('ReadStream close method can be called multiple times', (_, done) => {
|
|
59
59
|
const readStream = new fs.ReadStream();
|
|
60
60
|
readStream.close(err => {
|
|
61
|
-
assert.
|
|
61
|
+
assert.equal(err, null);
|
|
62
62
|
// Call close again
|
|
63
63
|
readStream.close(err2 => {
|
|
64
|
-
assert.
|
|
64
|
+
assert.equal(err2, null);
|
|
65
65
|
done();
|
|
66
66
|
});
|
|
67
67
|
});
|
|
@@ -94,7 +94,7 @@ suite('WriteStream', () => {
|
|
|
94
94
|
closed = true;
|
|
95
95
|
});
|
|
96
96
|
writeStream.close(err => {
|
|
97
|
-
assert.
|
|
97
|
+
assert.equal(err, null);
|
|
98
98
|
assert(closed);
|
|
99
99
|
done();
|
|
100
100
|
});
|
|
@@ -119,10 +119,10 @@ suite('WriteStream', () => {
|
|
|
119
119
|
test('WriteStream close method can be called multiple times', (_, done) => {
|
|
120
120
|
const writeStream = new fs.WriteStream();
|
|
121
121
|
writeStream.close(err => {
|
|
122
|
-
assert.
|
|
122
|
+
assert.equal(err, null);
|
|
123
123
|
// Call close again
|
|
124
124
|
writeStream.close(err2 => {
|
|
125
|
-
assert.
|
|
125
|
+
assert.equal(err2, null);
|
|
126
126
|
done();
|
|
127
127
|
});
|
|
128
128
|
});
|