@zenfs/core 0.12.0 → 0.12.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/backends/backend.d.ts +15 -5
- package/dist/backends/index/fs.d.ts +1 -0
- package/dist/backends/index/index.d.ts +5 -0
- package/dist/backends/index/index.js +1 -0
- package/dist/backends/overlay.js +1 -1
- package/dist/backends/port/fs.d.ts +3 -1
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +33 -11
- package/dist/config.js +16 -7
- package/dist/emulation/async.d.ts +5 -4
- package/dist/emulation/async.js +3 -2
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/path.d.ts +4 -1
- package/dist/emulation/promises.d.ts +4 -4
- package/dist/emulation/promises.js +27 -27
- package/dist/emulation/shared.d.ts +6 -0
- package/dist/emulation/shared.js +19 -1
- package/dist/emulation/sync.d.ts +4 -4
- package/dist/emulation/sync.js +76 -38
- package/dist/file.d.ts +5 -10
- package/dist/file.js +38 -55
- package/dist/filesystem.d.ts +45 -3
- package/dist/filesystem.js +37 -8
- package/dist/stats.d.ts +16 -16
- package/dist/stats.js +42 -49
- package/package.json +2 -2
- package/src/backends/backend.ts +15 -6
- package/src/backends/index/index.ts +5 -0
- package/src/backends/overlay.ts +1 -1
- package/src/config.ts +62 -21
- package/src/emulation/async.ts +19 -18
- package/src/emulation/index.ts +1 -1
- package/src/emulation/path.ts +4 -1
- package/src/emulation/promises.ts +33 -31
- package/src/emulation/shared.ts +22 -1
- package/src/emulation/sync.ts +83 -54
- package/src/error.ts +1 -1
- package/src/file.ts +39 -57
- package/src/filesystem.ts +133 -51
- package/src/stats.ts +48 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "A filesystem in your browser",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"buffer": "^6.0.3",
|
|
52
52
|
"minimatch": "^9.0.3",
|
|
53
53
|
"readable-stream": "^4.5.2",
|
|
54
|
-
"utilium": "^0.
|
|
54
|
+
"utilium": "^0.4.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
package/src/backends/backend.ts
CHANGED
|
@@ -8,7 +8,7 @@ type OptionType = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undef
|
|
|
8
8
|
/**
|
|
9
9
|
* Resolves the type of Backend.options from the options interface
|
|
10
10
|
*/
|
|
11
|
-
type OptionsConfig<T> = {
|
|
11
|
+
export type OptionsConfig<T> = {
|
|
12
12
|
[K in keyof T]: {
|
|
13
13
|
/**
|
|
14
14
|
* The basic JavaScript type(s) for this option.
|
|
@@ -66,9 +66,17 @@ export interface Backend<FS extends FileSystem = FileSystem, TOptions extends ob
|
|
|
66
66
|
isAvailable(): boolean | Promise<boolean>;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Gets the options type of a backend
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
export type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Gets the FileSystem type for a backend
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
export type FilesystemOf<T extends Backend> = T extends Backend<infer FS> ? FS : never;
|
|
72
80
|
|
|
73
81
|
/**
|
|
74
82
|
* @internal
|
|
@@ -81,7 +89,7 @@ export function isBackend(arg: unknown): arg is Backend {
|
|
|
81
89
|
* Checks that the given options object is valid for the file system options.
|
|
82
90
|
* @internal
|
|
83
91
|
*/
|
|
84
|
-
export async function checkOptions<T extends Backend>(backend: T, opts:
|
|
92
|
+
export async function checkOptions<T extends Backend>(backend: T, opts: Record<string, unknown>): Promise<void> {
|
|
85
93
|
if (typeof opts != 'object' || opts === null) {
|
|
86
94
|
throw new ErrnoError(Errno.EINVAL, 'Invalid options');
|
|
87
95
|
}
|
|
@@ -138,13 +146,14 @@ export async function checkOptions<T extends Backend>(backend: T, opts: Partial<
|
|
|
138
146
|
*
|
|
139
147
|
* The option object for each file system corresponds to that file system's option object passed to its `Create()` method.
|
|
140
148
|
*/
|
|
141
|
-
export type BackendConfiguration<T extends Backend
|
|
149
|
+
export type BackendConfiguration<T extends Backend> = OptionsOf<T> & {
|
|
142
150
|
backend: T;
|
|
151
|
+
disableAsyncCache?: boolean;
|
|
143
152
|
};
|
|
144
153
|
|
|
145
154
|
/**
|
|
146
155
|
* @internal
|
|
147
156
|
*/
|
|
148
|
-
export function isBackendConfig(arg: unknown): arg is BackendConfiguration {
|
|
157
|
+
export function isBackendConfig<T extends Backend>(arg: unknown): arg is BackendConfiguration<T> {
|
|
149
158
|
return arg != null && typeof arg == 'object' && 'backend' in arg && isBackend(arg.backend);
|
|
150
159
|
}
|
|
@@ -4,6 +4,10 @@ import { Stats, StatsLike } from '../../stats.js';
|
|
|
4
4
|
import { encode } from '../../utils.js';
|
|
5
5
|
import { basename, dirname } from '../../emulation/path.js';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* An Index in JSON form
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
7
11
|
export interface IndexData {
|
|
8
12
|
version: 1;
|
|
9
13
|
entries: Record<string, StatsLike<number>>;
|
|
@@ -13,6 +17,7 @@ export const version = 1;
|
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* An index of files
|
|
20
|
+
* @internal
|
|
16
21
|
*/
|
|
17
22
|
export class Index extends Map<string, Stats> {
|
|
18
23
|
public constructor() {
|
package/src/backends/overlay.ts
CHANGED
|
@@ -201,7 +201,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
201
201
|
}
|
|
202
202
|
// Create an OverlayFile.
|
|
203
203
|
const file = this._readable.openFileSync(path, parseFlag('r'), cred);
|
|
204
|
-
const stats = Stats
|
|
204
|
+
const stats = new Stats(file.statSync());
|
|
205
205
|
const data = new Uint8Array(stats.size);
|
|
206
206
|
file.readSync(data);
|
|
207
207
|
return new PreloadFile(this, path, flag, stats, data);
|
package/src/config.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Backend, BackendConfiguration } from './backends/backend.js';
|
|
1
|
+
import { type Entries } from 'utilium';
|
|
2
|
+
import type { Backend, BackendConfiguration, FilesystemOf } from './backends/backend.js';
|
|
3
3
|
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
|
|
4
4
|
import * as fs from './emulation/index.js';
|
|
5
|
-
import { setCred, type MountObject } from './emulation/shared.js';
|
|
6
|
-
import { FileSystem } from './filesystem.js';
|
|
7
5
|
import type { AbsolutePath } from './emulation/path.js';
|
|
6
|
+
import { setCred, type MountObject } from './emulation/shared.js';
|
|
7
|
+
import { Errno, ErrnoError } from './error.js';
|
|
8
|
+
import { FileSystem, type Async } from './filesystem.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Configuration for a specific mount point
|
|
11
12
|
*/
|
|
12
|
-
export type MountConfiguration<
|
|
13
|
+
export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendConfiguration<T> | T;
|
|
13
14
|
|
|
14
|
-
function isMountConfig(arg: unknown): arg is MountConfiguration {
|
|
15
|
+
function isMountConfig<T extends Backend>(arg: unknown): arg is MountConfiguration<T> {
|
|
15
16
|
return isBackendConfig(arg) || isBackend(arg) || arg instanceof FileSystem;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ function isMountConfig(arg: unknown): arg is MountConfiguration {
|
|
|
19
20
|
* Retrieve a file system with the given configuration.
|
|
20
21
|
* @param config A BackendConfig object.
|
|
21
22
|
*/
|
|
22
|
-
export async function resolveMountConfig<
|
|
23
|
+
export async function resolveMountConfig<T extends Backend>(config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
|
|
23
24
|
if (typeof config !== 'object' || config == null) {
|
|
24
25
|
throw new ErrnoError(Errno.EINVAL, 'Invalid options on mount configuration');
|
|
25
26
|
}
|
|
@@ -33,7 +34,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
if (isBackend(config)) {
|
|
36
|
-
config = { backend: config } as BackendConfiguration<
|
|
37
|
+
config = { backend: config } as BackendConfiguration<T>;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
for (const [key, value] of Object.entries(config)) {
|
|
@@ -49,7 +50,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
|
|
|
49
50
|
throw new ErrnoError(Errno.EINVAL, 'Invalid configuration, too deep and possibly infinite');
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
(
|
|
53
|
+
(config as Record<string, FileSystem>)[key] = await resolveMountConfig(value, ++_depth);
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
const { backend } = config;
|
|
@@ -58,40 +59,80 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
|
|
|
58
59
|
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
|
|
59
60
|
}
|
|
60
61
|
checkOptions(backend, config);
|
|
61
|
-
const mount = await backend.create(config)
|
|
62
|
+
const mount = (await backend.create(config)) as FilesystemOf<T>;
|
|
63
|
+
if ('_disableSync' in mount) {
|
|
64
|
+
type AsyncFS = InstanceType<ReturnType<typeof Async<new () => FilesystemOf<T>>>>;
|
|
65
|
+
(mount as AsyncFS)._disableSync = config.disableAsyncCache || false;
|
|
66
|
+
}
|
|
62
67
|
await mount.ready();
|
|
63
68
|
return mount;
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
type ConfigMounts = { [K in AbsolutePath]: Backend };
|
|
72
|
+
|
|
66
73
|
/**
|
|
67
74
|
* Configuration
|
|
68
75
|
*/
|
|
69
|
-
export interface Configuration {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
export interface Configuration<T extends ConfigMounts> {
|
|
77
|
+
/**
|
|
78
|
+
* An object mapping mount points to mount configuration
|
|
79
|
+
*/
|
|
80
|
+
mounts: { [K in keyof T & AbsolutePath]: MountConfiguration<T[K]> };
|
|
81
|
+
/**
|
|
82
|
+
* The uid to use
|
|
83
|
+
*/
|
|
84
|
+
uid: number;
|
|
85
|
+
/**
|
|
86
|
+
* The gid to use
|
|
87
|
+
*/
|
|
88
|
+
gid: number;
|
|
89
|
+
/**
|
|
90
|
+
* If set, disables the sync cache and sync operations on async file systems.
|
|
91
|
+
*/
|
|
92
|
+
disableAsyncCache: boolean;
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
/**
|
|
76
|
-
*
|
|
77
|
-
|
|
96
|
+
* Configures ZenFS with single mount point /
|
|
97
|
+
*/
|
|
98
|
+
export async function configure<T extends Backend>(config: MountConfiguration<T>): Promise<void>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Configures ZenFS with the given configuration
|
|
102
|
+
* @see Configuration
|
|
103
|
+
*/
|
|
104
|
+
export async function configure<T extends ConfigMounts>(config: Partial<Configuration<T>>): Promise<void>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Configures ZenFS with the given configuration
|
|
108
|
+
* @see Configuration
|
|
78
109
|
*/
|
|
79
|
-
export async function configure
|
|
110
|
+
export async function configure(config: MountConfiguration<Backend> | Partial<Configuration<ConfigMounts>>): Promise<void> {
|
|
80
111
|
const uid = 'uid' in config ? config.uid || 0 : 0;
|
|
81
112
|
const gid = 'gid' in config ? config.gid || 0 : 0;
|
|
82
113
|
|
|
83
114
|
if (isMountConfig(config)) {
|
|
84
115
|
// single FS
|
|
85
|
-
config = { mounts: { '/': config } }
|
|
116
|
+
config = { mounts: { '/': config } } as Partial<Configuration<ConfigMounts>>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
|
|
120
|
+
|
|
121
|
+
if (!config.mounts) {
|
|
122
|
+
return;
|
|
86
123
|
}
|
|
87
124
|
|
|
88
|
-
for (const [point,
|
|
125
|
+
for (const [point, mountConfig] of Object.entries(config.mounts) as Entries<typeof config.mounts>) {
|
|
89
126
|
if (!point.startsWith('/')) {
|
|
90
127
|
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
|
|
91
128
|
}
|
|
92
|
-
|
|
129
|
+
|
|
130
|
+
if (isBackendConfig(mountConfig)) {
|
|
131
|
+
mountConfig.disableAsyncCache ??= config.disableAsyncCache || false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
config.mounts[point] = await resolveMountConfig(mountConfig);
|
|
93
135
|
}
|
|
94
136
|
|
|
95
137
|
fs.mountObject(config.mounts as MountObject);
|
|
96
|
-
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
|
|
97
138
|
}
|
package/src/emulation/async.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
1
2
|
import type * as fs from 'node:fs';
|
|
2
3
|
import { ErrnoError, Errno } from '../error.js';
|
|
3
4
|
import type { FileContents } from '../filesystem.js';
|
|
4
|
-
import { BigIntStats, type
|
|
5
|
+
import { BigIntStats, type Stats } from '../stats.js';
|
|
5
6
|
import { nop, normalizeMode, type Callback } from '../utils.js';
|
|
6
7
|
import { R_OK } from './constants.js';
|
|
7
8
|
import { Dirent, type Dir } from './dir.js';
|
|
@@ -52,7 +53,7 @@ export function stat(path: fs.PathLike, options?: fs.StatOptions | Callback<[Sta
|
|
|
52
53
|
callback = typeof options == 'function' ? options : callback;
|
|
53
54
|
promises
|
|
54
55
|
.stat(path, typeof options != 'function' ? options : {})
|
|
55
|
-
.then(stats => (
|
|
56
|
+
.then(stats => (callback as Callback<[Stats] | [BigIntStats]>)(undefined, stats as any))
|
|
56
57
|
.catch(callback);
|
|
57
58
|
}
|
|
58
59
|
stat satisfies Omit<typeof fs.stat, '__promisify__'>;
|
|
@@ -71,8 +72,8 @@ export function lstat(path: fs.PathLike, options: fs.StatOptions, callback: Call
|
|
|
71
72
|
export function lstat(path: fs.PathLike, options?: fs.StatOptions | Callback<[Stats]>, callback: Callback<[Stats]> | Callback<[BigIntStats]> = nop): void {
|
|
72
73
|
callback = typeof options == 'function' ? options : callback;
|
|
73
74
|
promises
|
|
74
|
-
.lstat(path, typeof options != 'function' ? options :
|
|
75
|
-
.then(stats => (
|
|
75
|
+
.lstat(path, typeof options != 'function' ? options : ({} as object))
|
|
76
|
+
.then(stats => (callback as Callback<[Stats] | [BigIntStats]>)(undefined, stats))
|
|
76
77
|
.catch(callback);
|
|
77
78
|
}
|
|
78
79
|
lstat satisfies Omit<typeof fs.lstat, '__promisify__'>;
|
|
@@ -161,7 +162,7 @@ export function readFile(filename: fs.PathLike, options?: fs.WriteFileOptions |
|
|
|
161
162
|
|
|
162
163
|
promises
|
|
163
164
|
.readFile(filename, typeof options === 'function' ? null : options)
|
|
164
|
-
.then(data => (
|
|
165
|
+
.then(data => (cb as Callback<[string | Uint8Array]>)(undefined, data))
|
|
165
166
|
.catch(cb);
|
|
166
167
|
}
|
|
167
168
|
readFile satisfies Omit<typeof fs.readFile, '__promisify__'>;
|
|
@@ -231,7 +232,7 @@ export function fstat(fd: number, options?: fs.StatOptions | Callback<[Stats]>,
|
|
|
231
232
|
|
|
232
233
|
fd2file(fd)
|
|
233
234
|
.stat()
|
|
234
|
-
.then(stats => (
|
|
235
|
+
.then(stats => (cb as Callback<[Stats | BigIntStats]>)(undefined, typeof options == 'object' && options?.bigint ? new BigIntStats(stats) : stats))
|
|
235
236
|
.catch(cb);
|
|
236
237
|
}
|
|
237
238
|
fstat satisfies Omit<typeof fs.fstat, '__promisify__'>;
|
|
@@ -329,20 +330,20 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
|
|
|
329
330
|
case 'number':
|
|
330
331
|
// (fd, string, position, encoding?, cb?)
|
|
331
332
|
position = cbPosOff;
|
|
332
|
-
encoding =
|
|
333
|
+
encoding = typeof cbLenEnc === 'string' ? (cbLenEnc as BufferEncoding) : 'utf8';
|
|
333
334
|
cb = typeof cbPos === 'function' ? cbPos : cb;
|
|
334
335
|
break;
|
|
335
336
|
default:
|
|
336
337
|
// ...try to find the callback and get out of here!
|
|
337
338
|
cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPos === 'function' ? cbPos : cb;
|
|
338
|
-
(
|
|
339
|
+
(cb as Callback<[number, Uint8Array | string]>)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
|
|
339
340
|
return;
|
|
340
341
|
}
|
|
341
342
|
buffer = Buffer.from(data);
|
|
342
343
|
offset = 0;
|
|
343
344
|
length = buffer.length;
|
|
344
345
|
|
|
345
|
-
const _cb =
|
|
346
|
+
const _cb = cb as Callback<[number, string]>;
|
|
346
347
|
|
|
347
348
|
handle
|
|
348
349
|
.write(buffer, offset, length, position)
|
|
@@ -354,7 +355,7 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
|
|
|
354
355
|
offset = cbPosOff;
|
|
355
356
|
length = cbLenEnc;
|
|
356
357
|
position = typeof cbPos === 'number' ? cbPos : null;
|
|
357
|
-
const _cb =
|
|
358
|
+
const _cb = typeof cbPos === 'function' ? cbPos : (cb as Callback<[number, Uint8Array]>);
|
|
358
359
|
handle
|
|
359
360
|
.write(buffer, offset, length, position)
|
|
360
361
|
.then(({ bytesWritten }) => _cb(undefined, bytesWritten, buffer))
|
|
@@ -526,7 +527,7 @@ export function readlink(
|
|
|
526
527
|
callback = typeof options == 'function' ? options : callback;
|
|
527
528
|
promises
|
|
528
529
|
.readlink(path)
|
|
529
|
-
.then(result => (
|
|
530
|
+
.then(result => (callback as Callback<[string | Uint8Array]>)(undefined, result))
|
|
530
531
|
.catch(callback);
|
|
531
532
|
}
|
|
532
533
|
readlink satisfies Omit<typeof fs.readlink, '__promisify__'>;
|
|
@@ -816,8 +817,8 @@ export function mkdtemp(prefix: string, options: fs.BufferEncodingOption, callba
|
|
|
816
817
|
export function mkdtemp(prefix: string, options: fs.EncodingOption | fs.BufferEncodingOption | Callback<[string]>, callback: Callback<[Buffer]> | Callback<[string]> = nop): void {
|
|
817
818
|
callback = typeof options === 'function' ? options : callback;
|
|
818
819
|
promises
|
|
819
|
-
.mkdtemp(prefix, typeof options != 'function' ?
|
|
820
|
-
.then(result => (
|
|
820
|
+
.mkdtemp(prefix, typeof options != 'function' ? (options as fs.EncodingOption) : null)
|
|
821
|
+
.then(result => (callback as Callback<[string | Buffer]>)(undefined, result))
|
|
821
822
|
.catch(callback);
|
|
822
823
|
}
|
|
823
824
|
mkdtemp satisfies Omit<typeof fs.mkdtemp, '__promisify__'>;
|
|
@@ -881,14 +882,14 @@ export function cp(source: fs.PathLike, destination: fs.PathLike, opts: fs.CopyO
|
|
|
881
882
|
}
|
|
882
883
|
cp satisfies Omit<typeof fs.cp, '__promisify__'>;
|
|
883
884
|
|
|
884
|
-
export function statfs(path: fs.PathLike, callback: Callback<[StatsFs]>): void;
|
|
885
|
-
export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint?: false }, callback: Callback<[StatsFs]>): void;
|
|
886
|
-
export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }, callback: Callback<[BigIntStatsFs]>): void;
|
|
887
|
-
export function statfs(path: fs.PathLike, options?: fs.StatFsOptions | Callback<[StatsFs]>, callback: Callback<[StatsFs]> | Callback<[BigIntStatsFs]> = nop): void {
|
|
885
|
+
export function statfs(path: fs.PathLike, callback: Callback<[fs.StatsFs]>): void;
|
|
886
|
+
export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint?: false }, callback: Callback<[fs.StatsFs]>): void;
|
|
887
|
+
export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }, callback: Callback<[fs.BigIntStatsFs]>): void;
|
|
888
|
+
export function statfs(path: fs.PathLike, options?: fs.StatFsOptions | Callback<[fs.StatsFs]>, callback: Callback<[fs.StatsFs]> | Callback<[fs.BigIntStatsFs]> = nop): void {
|
|
888
889
|
callback = typeof options === 'function' ? options : callback;
|
|
889
890
|
promises
|
|
890
891
|
.statfs(path, typeof options === 'function' ? undefined : options)
|
|
891
|
-
.then(result => (
|
|
892
|
+
.then(result => (callback as Callback<[fs.StatsFs | fs.BigIntStatsFs]>)(undefined, result))
|
|
892
893
|
.catch(callback);
|
|
893
894
|
}
|
|
894
895
|
statfs satisfies Omit<typeof fs.statfs, '__promisify__'>;
|
package/src/emulation/index.ts
CHANGED
|
@@ -5,4 +5,4 @@ export * as constants from './constants.js';
|
|
|
5
5
|
export * from './streams.js';
|
|
6
6
|
export * from './dir.js';
|
|
7
7
|
export { mountObject, mounts, mount, umount } from './shared.js';
|
|
8
|
-
export { Stats,
|
|
8
|
+
export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
|
package/src/emulation/path.ts
CHANGED
|
@@ -28,6 +28,9 @@ https://raw.githubusercontent.com/nodejs/node/3907bd1/lib/path.js
|
|
|
28
28
|
|
|
29
29
|
import type { ParsedPath } from 'node:path';
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* An absolute path
|
|
33
|
+
*/
|
|
31
34
|
export type AbsolutePath = `/${string}`;
|
|
32
35
|
|
|
33
36
|
export let cwd = '/';
|
|
@@ -156,7 +159,7 @@ export function normalize(path: string): string {
|
|
|
156
159
|
return isAbsolute ? `/${path}` : path;
|
|
157
160
|
}
|
|
158
161
|
|
|
159
|
-
export function isAbsolute(path: string):
|
|
162
|
+
export function isAbsolute(path: string): path is AbsolutePath {
|
|
160
163
|
return path.startsWith('/');
|
|
161
164
|
}
|
|
162
165
|
|
|
@@ -6,15 +6,15 @@ import type { Stream } from 'node:stream';
|
|
|
6
6
|
import type { ReadableStream as TReadableStream } from 'node:stream/web';
|
|
7
7
|
import type { Interface as ReadlineInterface } from 'readline';
|
|
8
8
|
import type { ReadableStreamController } from 'stream/web';
|
|
9
|
-
import {
|
|
9
|
+
import { Errno, ErrnoError } from '../error.js';
|
|
10
10
|
import { ActionType, File, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
|
|
11
11
|
import type { FileContents } from '../filesystem.js';
|
|
12
|
-
import { BigIntStats, FileType, type
|
|
12
|
+
import { BigIntStats, FileType, type Stats } from '../stats.js';
|
|
13
13
|
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
14
14
|
import * as constants from './constants.js';
|
|
15
15
|
import { Dir, Dirent } from './dir.js';
|
|
16
16
|
import { dirname, join, parse } from './path.js';
|
|
17
|
-
import { cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
|
|
17
|
+
import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
|
|
18
18
|
import { ReadStream, WriteStream } from './streams.js';
|
|
19
19
|
export * as constants from './constants.js';
|
|
20
20
|
|
|
@@ -233,7 +233,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
233
233
|
if (typeof data === 'string') {
|
|
234
234
|
// Signature 1: (fd, string, [position?, [encoding?]])
|
|
235
235
|
position = typeof posOrOff === 'number' ? posOrOff : null;
|
|
236
|
-
const encoding =
|
|
236
|
+
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : ('utf8' as BufferEncoding);
|
|
237
237
|
offset = 0;
|
|
238
238
|
buffer = Buffer.from(data, encoding);
|
|
239
239
|
length = buffer.length;
|
|
@@ -491,28 +491,7 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
|
|
|
491
491
|
path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
|
|
492
492
|
const { fs, path: resolved } = resolveMount(path);
|
|
493
493
|
|
|
494
|
-
|
|
495
|
-
switch (pathExistsAction(flag)) {
|
|
496
|
-
case ActionType.THROW:
|
|
497
|
-
throw ErrnoError.With('EEXIST', path, '_open');
|
|
498
|
-
case ActionType.TRUNCATE:
|
|
499
|
-
/*
|
|
500
|
-
In a previous implementation, we deleted the file and
|
|
501
|
-
re-created it. However, this created a race condition if another
|
|
502
|
-
asynchronous request was trying to read the file, as the file
|
|
503
|
-
would not exist for a small period of time.
|
|
504
|
-
*/
|
|
505
|
-
const file: File = await fs.openFile(resolved, flag, cred);
|
|
506
|
-
await file.truncate(0);
|
|
507
|
-
await file.sync();
|
|
508
|
-
return new FileHandle(file);
|
|
509
|
-
case ActionType.NOP:
|
|
510
|
-
// Must await so thrown errors are caught by the catch below
|
|
511
|
-
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
512
|
-
default:
|
|
513
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
514
|
-
}
|
|
515
|
-
} catch (e) {
|
|
494
|
+
if (!(await fs.exists(path, cred))) {
|
|
516
495
|
switch (pathNotExistsAction(flag)) {
|
|
517
496
|
case ActionType.CREATE:
|
|
518
497
|
// Ensure parent exists.
|
|
@@ -527,6 +506,27 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
|
|
|
527
506
|
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
528
507
|
}
|
|
529
508
|
}
|
|
509
|
+
|
|
510
|
+
switch (pathExistsAction(flag)) {
|
|
511
|
+
case ActionType.THROW:
|
|
512
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
513
|
+
case ActionType.TRUNCATE:
|
|
514
|
+
/*
|
|
515
|
+
In a previous implementation, we deleted the file and
|
|
516
|
+
re-created it. However, this created a race condition if another
|
|
517
|
+
asynchronous request was trying to read the file, as the file
|
|
518
|
+
would not exist for a small period of time.
|
|
519
|
+
*/
|
|
520
|
+
const file: File = await fs.openFile(resolved, flag, cred);
|
|
521
|
+
await file.truncate(0);
|
|
522
|
+
await file.sync();
|
|
523
|
+
return new FileHandle(file);
|
|
524
|
+
case ActionType.NOP:
|
|
525
|
+
// Must await so thrown errors are caught by the catch below
|
|
526
|
+
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
527
|
+
default:
|
|
528
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
529
|
+
}
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
/**
|
|
@@ -1061,9 +1061,11 @@ cp satisfies typeof promises.cp;
|
|
|
1061
1061
|
* @since v18.15.0
|
|
1062
1062
|
* @return Fulfills with an {fs.StatFs} for the file system.
|
|
1063
1063
|
*/
|
|
1064
|
-
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigint?: false }): Promise<StatsFs>;
|
|
1065
|
-
export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<BigIntStatsFs>;
|
|
1066
|
-
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<StatsFs | BigIntStatsFs>;
|
|
1067
|
-
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<StatsFs | BigIntStatsFs> {
|
|
1068
|
-
|
|
1064
|
+
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigint?: false }): Promise<fs.StatsFs>;
|
|
1065
|
+
export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<fs.BigIntStatsFs>;
|
|
1066
|
+
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs>;
|
|
1067
|
+
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs> {
|
|
1068
|
+
path = normalizePath(path);
|
|
1069
|
+
const { fs } = resolveMount(path);
|
|
1070
|
+
return _statfs(fs, opts?.bigint);
|
|
1069
1071
|
}
|
package/src/emulation/shared.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// Utilities and shared data
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { BigIntStatsFs, StatsFs } from 'node:fs';
|
|
4
4
|
import { InMemory } from '../backends/memory.js';
|
|
5
5
|
import { Cred, rootCred } from '../cred.js';
|
|
6
|
+
import { Errno, ErrnoError } from '../error.js';
|
|
6
7
|
import type { File } from '../file.js';
|
|
7
8
|
import { FileSystem } from '../filesystem.js';
|
|
9
|
+
import { size_max } from '../inode.js';
|
|
10
|
+
import { ZenFsType } from '../stats.js';
|
|
8
11
|
import { normalizePath } from '../utils.js';
|
|
9
12
|
import { resolve, type AbsolutePath } from './path.js';
|
|
10
13
|
|
|
@@ -117,3 +120,21 @@ export function mountObject(mounts: MountObject): void {
|
|
|
117
120
|
mount(point, fs);
|
|
118
121
|
}
|
|
119
122
|
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @hidden
|
|
126
|
+
*/
|
|
127
|
+
export function _statfs<const T extends boolean>(fs: FileSystem, bigint?: T): T extends true ? BigIntStatsFs : StatsFs {
|
|
128
|
+
const md = fs.metadata();
|
|
129
|
+
const bs = md.blockSize || 4096;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
type: (bigint ? BigInt : Number)(md.type || ZenFsType),
|
|
133
|
+
bsize: (bigint ? BigInt : Number)(bs),
|
|
134
|
+
ffree: (bigint ? BigInt : Number)(md.freeNodes || size_max),
|
|
135
|
+
files: (bigint ? BigInt : Number)(md.totalNodes || size_max),
|
|
136
|
+
bavail: (bigint ? BigInt : Number)(md.freeSpace / bs),
|
|
137
|
+
bfree: (bigint ? BigInt : Number)(md.freeSpace / bs),
|
|
138
|
+
blocks: (bigint ? BigInt : Number)(md.totalSpace / bs),
|
|
139
|
+
} as T extends true ? BigIntStatsFs : StatsFs;
|
|
140
|
+
}
|