@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.
@@ -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
- if (configuration.disableAsyncCache)
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
- const uid = 'uid' in configuration ? configuration.uid || 0 : 0;
103
- const gid = 'gid' in configuration ? configuration.gid || 0 : 0;
104
- Object.assign(defaultContext.credentials, createCredentials({ uid, gid }));
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
- if (configuration.addDevices) {
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 type FileSystemAttributes = {
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
- var _a;
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.
@@ -128,6 +128,7 @@ export class IndexFS extends FileSystem {
128
128
  size: 0,
129
129
  uid: parent.mode & S_ISUID ? parent.uid : options.uid,
130
130
  gid: parent.mode & S_ISGID ? parent.gid : options.gid,
131
+ nlink: 1,
131
132
  });
132
133
  this.index.set(path, inode);
133
134
  return inode;
@@ -1,5 +1,5 @@
1
1
  import type { FileSystem } from '../internal/filesystem.js';
2
- import type { _SyncFSKeys, AsyncFSMethods, Mixin } from './shared.js';
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
  /**
@@ -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
- await this._promise;
23
+ return this.sync();
21
24
  }
25
+ /**
26
+ * @deprecated Use {@link sync | `sync`} instead
27
+ */
22
28
  queueDone() {
23
- return this.done();
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.checkSync();
118
- this._sync.syncSync();
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
- const methods = Array.from(getAllPrototypes(this))
184
- .flatMap(Object.getOwnPropertyNames)
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, _c;
192
- const result = await originalMethod.apply(this, args);
193
- const stack = (_a = new Error().stack) === null || _a === void 0 ? void 0 : _a.split('\n').slice(2).join('\n');
194
- // !stack == From the async queue
195
- if ((stack === null || stack === void 0 ? void 0 : stack.includes(`at <computed> [as ${key}]`)) || (stack === null || stack === void 0 ? void 0 : stack.includes(`${key}Sync `)) || !stack)
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
- (_c = (_b = this._sync) === null || _b === void 0 ? void 0 : _b[`${key}Sync`]) === null || _c === void 0 ? void 0 : _c.call(_b, ...args);
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
- throw err(withErrno(e.errno, e.message + ' (Out of sync!)'));
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
  };
@@ -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;
@@ -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 type AsyncFSMethods = Pick<FileSystem, _AsyncFSKeys>;
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 type ConcreteFS = ExtractProperties<FileSystem, any>;
34
+ export interface ConcreteFS extends ExtractProperties<FileSystem, any> {
35
+ }
@@ -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
+ }
@@ -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
@@ -365,6 +365,9 @@ export class Interface extends EventEmitter {
365
365
  },
366
366
  };
367
367
  }
368
+ [Symbol.dispose]() {
369
+ this.close();
370
+ }
368
371
  async [Symbol.asyncDispose]() {
369
372
  if (this._closed)
370
373
  return;
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
+ }
@@ -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?: promises.ReadableWebStreamOptions & StreamOptions): NodeReadableStream<Uint8Array>;
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?: promises.ReadableWebStreamOptions & StreamOptions): WritableStream;
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
@@ -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(p => {
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?
@@ -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
- path = path.slice(mountPoint.length > 1 ? mountPoint.length : 0); // Resolve the path relative to the mount point
60
- if (path === '')
61
- path = root;
62
- return { fs, path, mountPoint, root };
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(p => {
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.1.1",
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.10.1 <22.13.7",
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), /EEXIST/);
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));