@zenfs/core 2.1.1 → 2.2.0
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/config.d.ts +6 -0
- package/dist/config.js +21 -7
- 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 +2 -2
- 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
|
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();
|
|
@@ -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.0",
|
|
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",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { mounts, configure, fs } from '../../dist/index.js';
|
|
4
|
+
|
|
5
|
+
suite('Case folding', () => {
|
|
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));
|