@zenfs/core 2.1.1 → 2.2.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/backends/backend.d.ts +5 -1
- package/dist/backends/single_buffer.js +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.js +21 -7
- package/dist/index.js +2 -1
- package/dist/internal/filesystem.d.ts +7 -2
- package/dist/internal/filesystem.js +1 -2
- package/dist/internal/index_fs.js +1 -0
- package/dist/mixins/async.d.ts +7 -1
- package/dist/mixins/async.js +31 -19
- package/dist/mixins/mutexed.d.ts +1 -1
- package/dist/mixins/shared.d.ts +5 -2
- package/dist/mixins/shared.js +16 -2
- package/dist/path.d.ts +1 -0
- package/dist/path.js +4 -0
- package/dist/readline.d.ts +1 -0
- package/dist/readline.js +3 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +12 -0
- package/dist/vfs/promises.d.ts +2 -2
- package/dist/vfs/promises.js +4 -11
- package/dist/vfs/shared.js +11 -6
- package/dist/vfs/sync.js +4 -11
- package/package.json +4 -4
- package/readme.md +16 -9
- package/tests/common/casefold.test.ts +19 -0
- package/tests/fs/directory.test.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RequiredKeys } from 'utilium';
|
|
2
|
-
import type { FileSystem } from '../internal/filesystem.js';
|
|
2
|
+
import type { CaseFold, FileSystem } from '../internal/filesystem.js';
|
|
3
3
|
type OptionType = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | string | ((arg: any) => boolean) | {
|
|
4
4
|
[Symbol.hasInstance](instance: any): boolean;
|
|
5
5
|
toString(): string;
|
|
@@ -33,6 +33,10 @@ export interface SharedConfig {
|
|
|
33
33
|
* If set, disables the sync cache and sync operations on async file systems.
|
|
34
34
|
*/
|
|
35
35
|
disableAsyncCache?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* If set, sets case folding for the file system(s).
|
|
38
|
+
*/
|
|
39
|
+
caseFold?: CaseFold;
|
|
36
40
|
}
|
|
37
41
|
/**
|
|
38
42
|
* A backend
|
|
@@ -710,7 +710,7 @@ export class SingleBufferStore extends BufferView {
|
|
|
710
710
|
return this._fs;
|
|
711
711
|
}
|
|
712
712
|
set fs(fs) {
|
|
713
|
-
if (this.buffer
|
|
713
|
+
if (this.buffer.constructor.name === 'SharedArrayBuffer')
|
|
714
714
|
fs === null || fs === void 0 ? void 0 : fs.attributes.set('no_id_tables', true);
|
|
715
715
|
this._fs = fs;
|
|
716
716
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
|
|
2
2
|
import type { Device, DeviceDriver } from './internal/devices.js';
|
|
3
3
|
import { log } from 'kerium';
|
|
4
|
+
import { FileSystem } from './internal/filesystem.js';
|
|
5
|
+
/**
|
|
6
|
+
* Update the configuration of a file system.
|
|
7
|
+
* @category Backends and Configuration
|
|
8
|
+
*/
|
|
9
|
+
export declare function configureFileSystem(fs: FileSystem, config: SharedConfig): void;
|
|
4
10
|
/**
|
|
5
11
|
* Configuration for a specific mount point
|
|
6
12
|
* @category Backends and Configuration
|
package/dist/config.js
CHANGED
|
@@ -7,6 +7,16 @@ import { FileSystem } from './internal/filesystem.js';
|
|
|
7
7
|
import { _setAccessChecks } from './vfs/config.js';
|
|
8
8
|
import * as fs from './vfs/index.js';
|
|
9
9
|
import { mounts } from './vfs/shared.js';
|
|
10
|
+
/**
|
|
11
|
+
* Update the configuration of a file system.
|
|
12
|
+
* @category Backends and Configuration
|
|
13
|
+
*/
|
|
14
|
+
export function configureFileSystem(fs, config) {
|
|
15
|
+
if (config.disableAsyncCache)
|
|
16
|
+
fs.attributes.set('no_async_preload');
|
|
17
|
+
if (config.caseFold)
|
|
18
|
+
fs.attributes.set('case_fold', config.caseFold);
|
|
19
|
+
}
|
|
10
20
|
function isMountConfig(arg) {
|
|
11
21
|
return isBackendConfig(arg) || isBackend(arg) || arg instanceof FileSystem;
|
|
12
22
|
}
|
|
@@ -46,8 +56,7 @@ export async function resolveMountConfig(configuration, _depth = 0) {
|
|
|
46
56
|
}
|
|
47
57
|
checkOptions(backend, configuration);
|
|
48
58
|
const mount = (await backend.create(configuration));
|
|
49
|
-
|
|
50
|
-
mount.attributes.set('no_async_preload');
|
|
59
|
+
configureFileSystem(mount, configuration);
|
|
51
60
|
await mount.ready();
|
|
52
61
|
return mount;
|
|
53
62
|
}
|
|
@@ -98,10 +107,11 @@ export function addDevice(driver, options) {
|
|
|
98
107
|
* @see Configuration
|
|
99
108
|
*/
|
|
100
109
|
export async function configure(configuration) {
|
|
101
|
-
var _a;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
var _a, _b;
|
|
111
|
+
Object.assign(defaultContext.credentials, createCredentials({
|
|
112
|
+
uid: configuration.uid || 0,
|
|
113
|
+
gid: configuration.gid || 0,
|
|
114
|
+
}));
|
|
105
115
|
_setAccessChecks(!configuration.disableAccessChecks);
|
|
106
116
|
if (configuration.log)
|
|
107
117
|
log.configure(configuration.log);
|
|
@@ -111,13 +121,17 @@ export async function configure(configuration) {
|
|
|
111
121
|
const point = _point.startsWith('/') ? _point : '/' + _point;
|
|
112
122
|
if (isBackendConfig(mountConfig)) {
|
|
113
123
|
(_a = mountConfig.disableAsyncCache) !== null && _a !== void 0 ? _a : (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
|
|
124
|
+
(_b = mountConfig.caseFold) !== null && _b !== void 0 ? _b : (mountConfig.caseFold = configuration.caseFold);
|
|
114
125
|
}
|
|
115
126
|
if (point == '/')
|
|
116
127
|
fs.umount('/');
|
|
117
128
|
await mount(point, await resolveMountConfig(mountConfig));
|
|
118
129
|
}
|
|
119
130
|
}
|
|
120
|
-
|
|
131
|
+
for (const fs of mounts.values()) {
|
|
132
|
+
configureFileSystem(fs, configuration);
|
|
133
|
+
}
|
|
134
|
+
if (configuration.addDevices && !mounts.has('/dev')) {
|
|
121
135
|
const devfs = new DeviceFS();
|
|
122
136
|
devfs.addDefaults();
|
|
123
137
|
await devfs.ready();
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,5 @@ export * from './vfs/index.js';
|
|
|
10
10
|
export { fs };
|
|
11
11
|
import * as fs from './vfs/index.js';
|
|
12
12
|
export default fs;
|
|
13
|
-
|
|
13
|
+
import $pkg from '../package.json' with { type: 'json' };
|
|
14
|
+
globalThis.__zenfs__ = Object.assign(Object.create(fs), { _version: $pkg.version });
|
|
@@ -29,13 +29,14 @@ export interface UsageInfo {
|
|
|
29
29
|
*/
|
|
30
30
|
freeNodes?: number;
|
|
31
31
|
}
|
|
32
|
+
export type CaseFold = 'upper' | 'lower';
|
|
32
33
|
/**
|
|
33
34
|
* Attributes that control how the file system interacts with the VFS.
|
|
34
35
|
* No options are set by default.
|
|
35
36
|
* @category Internals
|
|
36
37
|
* @internal
|
|
37
38
|
*/
|
|
38
|
-
export
|
|
39
|
+
export interface FileSystemAttributes {
|
|
39
40
|
/**
|
|
40
41
|
* If set disables async file systems from preloading their contents.
|
|
41
42
|
* This means *sync operations will not work* (unless the contents are cached)
|
|
@@ -79,7 +80,11 @@ export type FileSystemAttributes = {
|
|
|
79
80
|
* @experimental
|
|
80
81
|
*/
|
|
81
82
|
sync: void;
|
|
82
|
-
|
|
83
|
+
/**
|
|
84
|
+
* If set, the VFS layer will convert paths to lower/upper case.
|
|
85
|
+
*/
|
|
86
|
+
case_fold?: CaseFold;
|
|
87
|
+
}
|
|
83
88
|
/**
|
|
84
89
|
* Options used when creating files and directories.
|
|
85
90
|
* This weird naming and such is to preserve backward compatibility.
|
|
@@ -39,8 +39,7 @@ export class FileSystem {
|
|
|
39
39
|
this.attributes.set('default_stream_write');
|
|
40
40
|
}
|
|
41
41
|
toString() {
|
|
42
|
-
|
|
43
|
-
return `${this.name} ${(_a = this.label) !== null && _a !== void 0 ? _a : ''} (${this._mountPoint ? 'mounted on ' + this._mountPoint : 'unmounted'})`;
|
|
42
|
+
return `${this.name} ${this.label ? JSON.stringify(this.label) : ''} (${this._mountPoint ? 'mounted on ' + this._mountPoint : 'unmounted'})`;
|
|
44
43
|
}
|
|
45
44
|
/**
|
|
46
45
|
* Default implementation.
|
package/dist/mixins/async.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FileSystem } from '../internal/filesystem.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type _SyncFSKeys, type AsyncFSMethods, type Mixin } from './shared.js';
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
* @category Internals
|
|
@@ -16,7 +16,13 @@ export interface AsyncMixin extends Pick<FileSystem, Exclude<_SyncFSKeys, 'exist
|
|
|
16
16
|
* @internal @protected
|
|
17
17
|
*/
|
|
18
18
|
_sync?: FileSystem;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use {@link sync | `sync`} instead
|
|
21
|
+
*/
|
|
19
22
|
queueDone(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* @deprecated Use {@link sync | `sync`} instead
|
|
25
|
+
*/
|
|
20
26
|
ready(): Promise<void>;
|
|
21
27
|
}
|
|
22
28
|
/**
|
package/dist/mixins/async.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { _asyncFSKeys } from './shared.js';
|
|
1
2
|
import { withErrno } from 'kerium';
|
|
2
3
|
import { crit, debug, err } from 'kerium/log';
|
|
3
|
-
import { getAllPrototypes } from 'utilium';
|
|
4
4
|
import { StoreFS } from '../backends/store/fs.js';
|
|
5
5
|
import { isDirectory } from '../internal/inode.js';
|
|
6
6
|
import { join } from '../path.js';
|
|
@@ -16,11 +16,17 @@ import { join } from '../path.js';
|
|
|
16
16
|
*/
|
|
17
17
|
export function Async(FS) {
|
|
18
18
|
class AsyncFS extends FS {
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use {@link sync | `sync`} instead
|
|
21
|
+
*/
|
|
19
22
|
async done() {
|
|
20
|
-
|
|
23
|
+
return this.sync();
|
|
21
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated Use {@link sync | `sync`} instead
|
|
27
|
+
*/
|
|
22
28
|
queueDone() {
|
|
23
|
-
return this.
|
|
29
|
+
return this.sync();
|
|
24
30
|
}
|
|
25
31
|
_async(promise) {
|
|
26
32
|
this._promise = this._promise.then(() => promise);
|
|
@@ -91,8 +97,8 @@ export function Async(FS) {
|
|
|
91
97
|
}
|
|
92
98
|
unlinkSync(path) {
|
|
93
99
|
this.checkSync();
|
|
94
|
-
this._sync.unlinkSync(path);
|
|
95
100
|
this._async(this.unlink(path));
|
|
101
|
+
this._sync.unlinkSync(path);
|
|
96
102
|
}
|
|
97
103
|
rmdirSync(path) {
|
|
98
104
|
this.checkSync();
|
|
@@ -114,8 +120,8 @@ export function Async(FS) {
|
|
|
114
120
|
this._async(this.link(srcpath, dstpath));
|
|
115
121
|
}
|
|
116
122
|
async sync() {
|
|
117
|
-
this.
|
|
118
|
-
|
|
123
|
+
if (!this.attributes.has('no_async_preload') && this._sync)
|
|
124
|
+
this._sync.syncSync();
|
|
119
125
|
await this._promise;
|
|
120
126
|
}
|
|
121
127
|
syncSync() {
|
|
@@ -180,19 +186,19 @@ export function Async(FS) {
|
|
|
180
186
|
* Patch all async methods to also call their synchronous counterparts unless called from themselves (either sync or async)
|
|
181
187
|
*/
|
|
182
188
|
_patchAsync() {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
.filter(key => typeof this[key] == 'function' && `${key}Sync` in this);
|
|
186
|
-
debug('Async: patching methods: ' + methods.join(', '));
|
|
187
|
-
for (const key of methods) {
|
|
189
|
+
debug(`Async: patched ${_asyncFSKeys.length} methods`);
|
|
190
|
+
for (const key of _asyncFSKeys) {
|
|
188
191
|
// TS does not narrow the union based on the key
|
|
189
|
-
const originalMethod = this[key];
|
|
192
|
+
const originalMethod = this[key].bind(this);
|
|
190
193
|
this[key] = async (...args) => {
|
|
191
|
-
var _a, _b
|
|
192
|
-
const result = await originalMethod
|
|
193
|
-
const stack =
|
|
194
|
-
//
|
|
195
|
-
if (
|
|
194
|
+
var _a, _b;
|
|
195
|
+
const result = await originalMethod(...args);
|
|
196
|
+
const stack = new Error().stack.split('\n').slice(2).join('\n');
|
|
197
|
+
// From the async queue
|
|
198
|
+
if (!stack
|
|
199
|
+
|| stack.includes(`at <computed> [as ${key}]`)
|
|
200
|
+
|| stack.includes(`at async <computed> [as ${key}]`)
|
|
201
|
+
|| stack.includes(`${key}Sync `))
|
|
196
202
|
return result;
|
|
197
203
|
if (!this._isInitialized) {
|
|
198
204
|
this._skippedCacheUpdates++;
|
|
@@ -200,10 +206,16 @@ export function Async(FS) {
|
|
|
200
206
|
}
|
|
201
207
|
try {
|
|
202
208
|
// @ts-expect-error 2556 - The type of `args` is not narrowed
|
|
203
|
-
(
|
|
209
|
+
(_b = (_a = this._sync) === null || _a === void 0 ? void 0 : _a[`${key}Sync`]) === null || _b === void 0 ? void 0 : _b.call(_a, ...args);
|
|
204
210
|
}
|
|
205
211
|
catch (e) {
|
|
206
|
-
|
|
212
|
+
const stack = e.stack.split('\n').slice(3).join('\n');
|
|
213
|
+
if (stack.includes(`at <computed> [as ${key}]`)
|
|
214
|
+
|| stack.includes(`at async <computed> [as ${key}]`)
|
|
215
|
+
|| stack.includes(`${key}Sync `))
|
|
216
|
+
return result;
|
|
217
|
+
e.message += ' (Out of sync!)';
|
|
218
|
+
throw err(e);
|
|
207
219
|
}
|
|
208
220
|
return result;
|
|
209
221
|
};
|
package/dist/mixins/mutexed.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
30
30
|
get name(): string;
|
|
31
31
|
get label(): string | undefined;
|
|
32
32
|
set label(value: string | undefined);
|
|
33
|
-
get attributes(): import("utilium").ConstMap<import("../internal/filesystem.js").FileSystemAttributes, keyof import("../internal/filesystem.js").FileSystemAttributes, void> & Map<string, any>;
|
|
33
|
+
get attributes(): import("utilium").ConstMap<import("../internal/filesystem.js").FileSystemAttributes, keyof import("../internal/filesystem.js").FileSystemAttributes, void | import("../internal/filesystem.js").CaseFold | undefined> & Map<string, any>;
|
|
34
34
|
get _uuid(): UUID;
|
|
35
35
|
set _uuid(value: UUID);
|
|
36
36
|
get uuid(): UUID;
|
package/dist/mixins/shared.d.ts
CHANGED
|
@@ -18,15 +18,18 @@ export type _SyncFSKeys = Exclude<Extract<keyof FileSystem, `${string}Sync`>, '_
|
|
|
18
18
|
export type _AsyncFSKeys = {
|
|
19
19
|
[K in _SyncFSKeys]: K extends `${infer T}Sync` ? T : never;
|
|
20
20
|
}[_SyncFSKeys];
|
|
21
|
+
export declare const _asyncFSKeys: ["rename", "stat", "touch", "createFile", "unlink", "rmdir", "mkdir", "readdir", "exists", "link", "sync", "read", "write"];
|
|
21
22
|
/**
|
|
22
23
|
* Asynchronous `FileSystem` methods. This is a convenience type for all of the async operations.
|
|
23
24
|
* @category Internals
|
|
24
25
|
* @internal
|
|
25
26
|
*/
|
|
26
|
-
export
|
|
27
|
+
export interface AsyncFSMethods extends Pick<FileSystem, _AsyncFSKeys> {
|
|
28
|
+
}
|
|
27
29
|
/**
|
|
28
30
|
* Concrete `FileSystem`. This is a convenience type.
|
|
29
31
|
* @category Internals
|
|
30
32
|
* @internal
|
|
31
33
|
*/
|
|
32
|
-
export
|
|
34
|
+
export interface ConcreteFS extends ExtractProperties<FileSystem, any> {
|
|
35
|
+
}
|
package/dist/mixins/shared.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-empty-object-type,@typescript-eslint/no-explicit-any */
|
|
2
2
|
/*
|
|
3
3
|
Code shared by various mixins
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
5
|
+
export const _asyncFSKeys = [
|
|
6
|
+
'rename',
|
|
7
|
+
'stat',
|
|
8
|
+
'touch',
|
|
9
|
+
'createFile',
|
|
10
|
+
'unlink',
|
|
11
|
+
'rmdir',
|
|
12
|
+
'mkdir',
|
|
13
|
+
'readdir',
|
|
14
|
+
'exists',
|
|
15
|
+
'link',
|
|
16
|
+
'sync',
|
|
17
|
+
'read',
|
|
18
|
+
'write',
|
|
19
|
+
];
|
package/dist/path.d.ts
CHANGED
|
@@ -14,3 +14,4 @@ export declare function basename(path: string, suffix?: string): string;
|
|
|
14
14
|
export declare function extname(path: string): string;
|
|
15
15
|
export declare function format(pathObject: ParsedPath): string;
|
|
16
16
|
export declare function parse(path: string): ParsedPath;
|
|
17
|
+
export declare function matchesGlob(pattern: string, str: string): boolean;
|
package/dist/path.js
CHANGED
|
@@ -21,6 +21,7 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
21
21
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
import { defaultContext } from './internal/contexts.js';
|
|
24
|
+
import { globToRegex } from './utils.js';
|
|
24
25
|
export const sep = '/';
|
|
25
26
|
function validateObject(str, name) {
|
|
26
27
|
if (typeof str != 'object') {
|
|
@@ -419,3 +420,6 @@ export function parse(path) {
|
|
|
419
420
|
ret.dir = '/';
|
|
420
421
|
return ret;
|
|
421
422
|
}
|
|
423
|
+
export function matchesGlob(pattern, str) {
|
|
424
|
+
return globToRegex(pattern).test(str);
|
|
425
|
+
}
|
package/dist/readline.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ export declare class Interface extends EventEmitter<InterfaceEvents> implements
|
|
|
85
85
|
*/
|
|
86
86
|
getMaxListeners(): number;
|
|
87
87
|
[Symbol.asyncIterator](): AsyncIteratorObject<string>;
|
|
88
|
+
[Symbol.dispose](): void;
|
|
88
89
|
[Symbol.asyncDispose](): Promise<void>;
|
|
89
90
|
rawListeners(event: keyof InterfaceEvents): ((...args: any[]) => void)[];
|
|
90
91
|
}
|
package/dist/readline.js
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -53,3 +53,8 @@ export declare function normalizeOptions(options: fs.WriteFileOptions | (fs.Enco
|
|
|
53
53
|
flag: string;
|
|
54
54
|
mode: number;
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Converts a glob pattern to a regular expression
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export declare function globToRegex(pattern: string): RegExp;
|
package/dist/utils.js
CHANGED
|
@@ -96,3 +96,15 @@ export function normalizeOptions(options, encoding = 'utf8', flag, mode = 0) {
|
|
|
96
96
|
mode: normalizeMode('mode' in options ? options === null || options === void 0 ? void 0 : options.mode : null, mode),
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Converts a glob pattern to a regular expression
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export function globToRegex(pattern) {
|
|
104
|
+
pattern = pattern
|
|
105
|
+
.replace(/([.?+^$(){}|[\]/])/g, '$1')
|
|
106
|
+
.replace(/\*\*/g, '.*')
|
|
107
|
+
.replace(/\*/g, '[^/]*')
|
|
108
|
+
.replace(/\?/g, '.');
|
|
109
|
+
return new RegExp(`^${pattern}$`);
|
|
110
|
+
}
|
package/dist/vfs/promises.d.ts
CHANGED
|
@@ -128,7 +128,7 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
128
128
|
* Read file data using a `ReadableStream`.
|
|
129
129
|
* The handle will not be closed automatically.
|
|
130
130
|
*/
|
|
131
|
-
readableWebStream(options?:
|
|
131
|
+
readableWebStream(options?: StreamOptions): NodeReadableStream<Uint8Array>;
|
|
132
132
|
/**
|
|
133
133
|
* Not part of the Node.js API!
|
|
134
134
|
*
|
|
@@ -136,7 +136,7 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
136
136
|
* The handle will not be closed automatically.
|
|
137
137
|
* @internal
|
|
138
138
|
*/
|
|
139
|
-
writableWebStream(options?:
|
|
139
|
+
writableWebStream(options?: StreamOptions): WritableStream;
|
|
140
140
|
/**
|
|
141
141
|
* Creates a readline Interface object that allows reading the file line by line
|
|
142
142
|
* @param options Options for creating a read stream
|
package/dist/vfs/promises.js
CHANGED
|
@@ -55,10 +55,10 @@ import { Exception, rethrow, setUVMessage, UV } from 'kerium';
|
|
|
55
55
|
import { decodeUTF8, pick } from 'utilium';
|
|
56
56
|
import { defaultContext } from '../internal/contexts.js';
|
|
57
57
|
import { hasAccess, InodeFlags, isBlockDevice, isCharacterDevice, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
58
|
-
import { dirname, join, parse, resolve } from '../path.js';
|
|
58
|
+
import { dirname, join, matchesGlob, parse, resolve } from '../path.js';
|
|
59
59
|
import '../polyfills.js';
|
|
60
60
|
import { createInterface } from '../readline.js';
|
|
61
|
-
import { __assertType, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
61
|
+
import { __assertType, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
62
62
|
import { checkAccess } from './config.js';
|
|
63
63
|
import * as constants from './constants.js';
|
|
64
64
|
import { Dir, Dirent } from './dir.js';
|
|
@@ -1219,19 +1219,12 @@ export function glob(pattern, opt) {
|
|
|
1219
1219
|
pattern = Array.isArray(pattern) ? pattern : [pattern];
|
|
1220
1220
|
const { cwd = '/', withFileTypes = false, exclude = () => false } = opt || {};
|
|
1221
1221
|
// Escape special characters in pattern
|
|
1222
|
-
const regexPatterns = pattern.map(
|
|
1223
|
-
p = p
|
|
1224
|
-
.replace(/([.?+^$(){}|[\]/])/g, '$1')
|
|
1225
|
-
.replace(/\*\*/g, '.*')
|
|
1226
|
-
.replace(/\*/g, '[^/]*')
|
|
1227
|
-
.replace(/\?/g, '.');
|
|
1228
|
-
return new RegExp(`^${p}$`);
|
|
1229
|
-
});
|
|
1222
|
+
const regexPatterns = pattern.map(globToRegex);
|
|
1230
1223
|
async function* recursiveList(dir) {
|
|
1231
1224
|
const entries = await readdir(dir, { withFileTypes, encoding: 'utf8' });
|
|
1232
1225
|
for (const entry of entries) {
|
|
1233
1226
|
const fullPath = withFileTypes ? entry.path : dir + '/' + entry;
|
|
1234
|
-
if (exclude((withFileTypes ? entry : fullPath)))
|
|
1227
|
+
if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
|
|
1235
1228
|
continue;
|
|
1236
1229
|
/**
|
|
1237
1230
|
* @todo is the pattern.source check correct?
|
package/dist/vfs/shared.js
CHANGED
|
@@ -55,12 +55,17 @@ export function resolveMount(path, ctx) {
|
|
|
55
55
|
const sortedMounts = [...mounts].sort((a, b) => (a[0].length > b[0].length ? -1 : 1)); // descending order of the string length
|
|
56
56
|
for (const [mountPoint, fs] of sortedMounts) {
|
|
57
57
|
// We know path is normalized, so it would be a substring of the mount point.
|
|
58
|
-
if (_isParentOf(mountPoint, path))
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
if (!_isParentOf(mountPoint, path))
|
|
59
|
+
continue;
|
|
60
|
+
path = path.slice(mountPoint.length > 1 ? mountPoint.length : 0); // Resolve the path relative to the mount point
|
|
61
|
+
if (path === '')
|
|
62
|
+
path = root;
|
|
63
|
+
const case_fold = fs.attributes.get('case_fold');
|
|
64
|
+
if (case_fold === 'lower')
|
|
65
|
+
path = path.toLowerCase();
|
|
66
|
+
if (case_fold === 'upper')
|
|
67
|
+
path = path.toUpperCase();
|
|
68
|
+
return { fs, path, mountPoint, root };
|
|
64
69
|
}
|
|
65
70
|
throw alert(new Exception(Errno.EIO, 'No file system for ' + path));
|
|
66
71
|
}
|
package/dist/vfs/sync.js
CHANGED
|
@@ -56,8 +56,8 @@ import { decodeUTF8, encodeUTF8 } from 'utilium';
|
|
|
56
56
|
import { defaultContext } from '../internal/contexts.js';
|
|
57
57
|
import { wrap } from '../internal/error.js';
|
|
58
58
|
import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
|
|
59
|
-
import { dirname, join, parse, resolve } from '../path.js';
|
|
60
|
-
import { __assertType, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
59
|
+
import { dirname, join, matchesGlob, parse, resolve } from '../path.js';
|
|
60
|
+
import { __assertType, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
61
61
|
import { checkAccess } from './config.js';
|
|
62
62
|
import * as constants from './constants.js';
|
|
63
63
|
import { Dir, Dirent } from './dir.js';
|
|
@@ -860,20 +860,13 @@ export function globSync(pattern, options = {}) {
|
|
|
860
860
|
pattern = Array.isArray(pattern) ? pattern : [pattern];
|
|
861
861
|
const { cwd = '/', withFileTypes = false, exclude = () => false } = options;
|
|
862
862
|
// Escape special characters in pattern
|
|
863
|
-
const regexPatterns = pattern.map(
|
|
864
|
-
p = p
|
|
865
|
-
.replace(/([.?+^$(){}|[\]/])/g, '\\$1')
|
|
866
|
-
.replace(/\*\*/g, '.*')
|
|
867
|
-
.replace(/\*/g, '[^/]*')
|
|
868
|
-
.replace(/\?/g, '.');
|
|
869
|
-
return new RegExp(`^${p}$`);
|
|
870
|
-
});
|
|
863
|
+
const regexPatterns = pattern.map(globToRegex);
|
|
871
864
|
const results = [];
|
|
872
865
|
function recursiveList(dir) {
|
|
873
866
|
const entries = readdirSync(dir, { withFileTypes, encoding: 'utf8' });
|
|
874
867
|
for (const entry of entries) {
|
|
875
868
|
const fullPath = withFileTypes ? entry.path : dir + '/' + entry;
|
|
876
|
-
if (exclude((withFileTypes ? entry : fullPath)))
|
|
869
|
+
if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
|
|
877
870
|
continue;
|
|
878
871
|
/**
|
|
879
872
|
* @todo is the pattern.source check correct?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"prepublishOnly": "npm run build"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@types/node": "^22.
|
|
70
|
+
"@types/node": "^22.15.2",
|
|
71
71
|
"buffer": "^6.0.3",
|
|
72
72
|
"eventemitter3": "^5.0.1",
|
|
73
73
|
"kerium": "^1.3.4",
|
|
@@ -81,10 +81,10 @@
|
|
|
81
81
|
"@types/eslint__js": "^8.42.3",
|
|
82
82
|
"c8": "^10.1.2",
|
|
83
83
|
"eslint": "^9.15.0",
|
|
84
|
-
"globals": "^
|
|
84
|
+
"globals": "^16.0.0",
|
|
85
85
|
"prettier": "^3.2.5",
|
|
86
86
|
"tsx": "^4.19.1",
|
|
87
|
-
"typedoc": "^0.
|
|
87
|
+
"typedoc": "^0.28.0",
|
|
88
88
|
"typescript": "^5.7.2",
|
|
89
89
|
"typescript-eslint": "^8.16.0"
|
|
90
90
|
}
|
package/readme.md
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
# ZenFS
|
|
2
2
|
|
|
3
|
-
ZenFS is a cross-platform library that emulates the [
|
|
3
|
+
ZenFS is a cross-platform library that emulates the [Node.js filesystem API](http://nodejs.org/api/fs.html).
|
|
4
|
+
It works using a system of backends, which are used by ZenFS to store and retrieve data.
|
|
5
|
+
ZenFS can also integrate with other tools.
|
|
4
6
|
|
|
5
7
|
## Backends
|
|
6
8
|
|
|
7
|
-
ZenFS is modular and
|
|
9
|
+
ZenFS is modular and easily extended. The core includes some built-in backends:
|
|
8
10
|
|
|
9
11
|
- `InMemory`: Stores files in-memory. This is cleared when the runtime ends (e.g. a user navigating away from a web page or a Node process exiting)
|
|
10
|
-
- `CopyOnWrite`: Use readable and writable file systems with
|
|
12
|
+
- `CopyOnWrite`: Use readable and writable file systems with [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write).
|
|
11
13
|
- `Fetch`: Downloads files over HTTP with the `fetch` API
|
|
12
14
|
- `Port`: Interacts with a remote over a `MessagePort`-like interface (e.g. a worker)
|
|
13
15
|
- `Passthrough`: Use an existing `node:fs` interface with ZenFS
|
|
14
16
|
- `SingleBuffer`: A backend contained within a single buffer. Can be used for synchronous multi-threaded operations using `SharedArrayBuffer`
|
|
15
17
|
|
|
16
|
-
ZenFS supports a number of other backends.
|
|
18
|
+
ZenFS supports a number of other backends.
|
|
19
|
+
Many are provided as separate packages under `@zenfs`.
|
|
20
|
+
More backends can be defined by separate libraries by extending the `FileSystem` class and providing a `Backend` object.
|
|
17
21
|
|
|
18
22
|
You can find all of the packages available over on [NPM](https://www.npmjs.com/org/zenfs).
|
|
19
23
|
|
|
20
|
-
As an added bonus, all ZenFS backends support synchronous operations.
|
|
24
|
+
As an added bonus, all ZenFS backends support synchronous operations.
|
|
25
|
+
Additionally, all of the backends included with the core are cross-platform.
|
|
21
26
|
|
|
22
27
|
For more information, see the [docs](https://zenfs.dev/core).
|
|
23
28
|
|
|
@@ -27,7 +32,9 @@ For more information, see the [docs](https://zenfs.dev/core).
|
|
|
27
32
|
npm install @zenfs/core
|
|
28
33
|
```
|
|
29
34
|
|
|
30
|
-
If you're using ZenFS, especially for big projects, please consider supporting the project.
|
|
35
|
+
If you're using ZenFS, especially for big projects, please consider supporting the project.
|
|
36
|
+
Thousands of hours have been dedicated to its development.
|
|
37
|
+
Your acknowledgment or financial support would go a long way toward improving ZenFS and its community.
|
|
31
38
|
|
|
32
39
|
## Usage
|
|
33
40
|
|
|
@@ -149,7 +156,7 @@ fs.umount('/mnt/zip'); // finished using the zip
|
|
|
149
156
|
|
|
150
157
|
### Devices and device files
|
|
151
158
|
|
|
152
|
-
ZenFS includes
|
|
159
|
+
ZenFS includes support for device files. These are designed to follow Linux's device file behavior, for consistency and ease of use. You can automatically add some normal devices with the `addDevices` configuration option:
|
|
153
160
|
|
|
154
161
|
```ts
|
|
155
162
|
await configure({
|
|
@@ -208,9 +215,9 @@ A huge thank you to [ => {
|
|
6
|
+
test('Configuration', async () => {
|
|
7
|
+
await configure({ caseFold: 'lower' });
|
|
8
|
+
assert.equal(mounts.get('/')?.attributes.get('case_fold'), 'lower');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('Write', () => {
|
|
12
|
+
fs.writeFileSync('/Test.txt', 'test');
|
|
13
|
+
assert(fs.existsSync('/test.txt'));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('Read', () => {
|
|
17
|
+
assert.equal(fs.readFileSync('/TEST.txt', 'utf8'), 'test');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -21,7 +21,7 @@ suite('Directories', () => {
|
|
|
21
21
|
test('mkdir', async () => {
|
|
22
22
|
await fs.promises.mkdir('/one', 0o755);
|
|
23
23
|
assert(await fs.promises.exists('/one'));
|
|
24
|
-
await assert.rejects(fs.promises.mkdir('/one', 0o755),
|
|
24
|
+
await assert.rejects(fs.promises.mkdir('/one', 0o755), { code: 'EEXIST' });
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
test('mkdirSync', async () => await fs.promises.mkdir('/two', 0o000));
|