@zenfs/core 2.5.0 → 2.5.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.
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents */
2
+ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */
3
3
  import { withErrno } from 'kerium';
4
4
  import { debug, err } from 'kerium/log';
5
5
  /**
@@ -1,3 +1,4 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-or-later
1
2
  import { Errno, Exception, withErrno } from 'kerium';
2
3
  import { err, info, warn } from 'kerium/log';
3
4
  import { isJSON, pick } from 'utilium';
@@ -30,7 +30,6 @@ export function Async(FS) {
30
30
  }
31
31
  _promise = Promise.resolve();
32
32
  _async(thunk) {
33
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
34
33
  this._promise = this._promise.finally(() => thunk());
35
34
  }
36
35
  _isInitialized = false;
@@ -1,8 +1,4 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
- /*
4
- Code shared by various mixins
5
- */
6
2
  export const _asyncFSKeys = [
7
3
  'rename',
8
4
  'stat',
@@ -72,11 +72,11 @@ export declare class FileHandle implements promises.FileHandle {
72
72
  read<T extends NodeJS.ArrayBufferView = Buffer>(options?: promises.FileReadOptions<T>): Promise<promises.FileReadResult<T>>;
73
73
  readFile(options?: ({
74
74
  encoding?: null;
75
- } & Abortable) | null): Promise<Buffer>;
75
+ } & Abortable) | null): Promise<NonSharedBuffer>;
76
76
  readFile(options: ({
77
77
  encoding: BufferEncoding;
78
78
  } & Abortable) | BufferEncoding): Promise<string>;
79
- readFile(_options?: (fs.ObjectEncodingOptions & Abortable) | BufferEncoding | null): Promise<string | Buffer>;
79
+ readFile(_options?: (fs.ObjectEncodingOptions & Abortable) | BufferEncoding | null): Promise<string | NonSharedBuffer>;
80
80
  /**
81
81
  * Read file data using a `ReadableStream`.
82
82
  * The handle will not be closed automatically.
@@ -139,14 +139,14 @@ export declare class FileHandle implements promises.FileHandle {
139
139
  * @param position The position in the file where to begin writing.
140
140
  * @returns The number of bytes written.
141
141
  */
142
- writev(buffers: Uint8Array[], position?: number): Promise<fs.WriteVResult>;
142
+ writev<TBuffers extends readonly NodeJS.ArrayBufferView[]>(buffers: TBuffers, position?: number): Promise<fs.WriteVResult<TBuffers>>;
143
143
  /**
144
144
  * Asynchronous `readv`. Reads into multiple buffers.
145
145
  * @param buffers An array of Uint8Array buffers.
146
146
  * @param position The position in the file where to begin reading.
147
147
  * @returns The number of bytes read.
148
148
  */
149
- readv(buffers: NodeJS.ArrayBufferView[], position?: number): Promise<fs.ReadVResult>;
149
+ readv<TBuffers extends readonly NodeJS.ArrayBufferView[]>(buffers: TBuffers, position?: number): Promise<fs.ReadVResult<TBuffers>>;
150
150
  /**
151
151
  * Creates a stream for reading from the file.
152
152
  * @param options Options for the readable stream
@@ -197,14 +197,14 @@ export declare function open(this: V_Context, path: fs.PathLike, flag?: fs.OpenM
197
197
  export declare function readFile(this: V_Context, path: fs.PathLike | promises.FileHandle, options?: ({
198
198
  encoding?: null;
199
199
  flag?: fs.OpenMode;
200
- } & Abortable) | null): Promise<Buffer>;
200
+ } & Abortable) | null): Promise<NonSharedBuffer>;
201
201
  export declare function readFile(this: V_Context, path: fs.PathLike | promises.FileHandle, options: ({
202
202
  encoding: BufferEncoding;
203
203
  flag?: fs.OpenMode;
204
204
  } & Abortable) | BufferEncoding): Promise<string>;
205
205
  export declare function readFile(this: V_Context, path: fs.PathLike | promises.FileHandle, _options?: (fs.ObjectEncodingOptions & Abortable & {
206
206
  flag?: fs.OpenMode;
207
- }) | BufferEncoding | null): Promise<string | Buffer>;
207
+ }) | BufferEncoding | null): Promise<string | NonSharedBuffer>;
208
208
  /**
209
209
  * Asynchronously writes data to a file, replacing the file if it already exists.
210
210
  *
@@ -257,11 +257,11 @@ export declare function readdir(this: V_Context, path: fs.PathLike, options: {
257
257
  encoding: 'buffer';
258
258
  withFileTypes?: false;
259
259
  recursive?: boolean;
260
- } | 'buffer'): Promise<Buffer[]>;
260
+ } | 'buffer'): Promise<NonSharedBuffer[]>;
261
261
  export declare function readdir(this: V_Context, path: fs.PathLike, options?: (fs.ObjectEncodingOptions & {
262
262
  withFileTypes?: false;
263
263
  recursive?: boolean;
264
- }) | BufferEncoding | null): Promise<string[] | Buffer[]>;
264
+ }) | BufferEncoding | null): Promise<string[] | NonSharedBuffer[]>;
265
265
  export declare function readdir(this: V_Context, path: fs.PathLike, options: fs.ObjectEncodingOptions & {
266
266
  withFileTypes: true;
267
267
  recursive?: boolean;
@@ -270,7 +270,7 @@ export declare function readdir(this: V_Context, path: fs.PathLike, options: {
270
270
  encoding: 'buffer';
271
271
  withFileTypes: true;
272
272
  recursive?: boolean;
273
- }): Promise<Dirent<Buffer>[]>;
273
+ }): Promise<Dirent<NonSharedBuffer>[]>;
274
274
  export declare function readdir(this: V_Context, path: fs.PathLike, options?: NodeReaddirOptions): Promise<string[] | Dirent<any>[] | Buffer[]>;
275
275
  export declare function link(this: V_Context, path: fs.PathLike, dest: fs.PathLike): Promise<void>;
276
276
  /**
@@ -280,9 +280,9 @@ export declare function link(this: V_Context, path: fs.PathLike, dest: fs.PathLi
280
280
  * @param type can be either `'dir'` or `'file'` (default is `'file'`)
281
281
  */
282
282
  export declare function symlink(this: V_Context, dest: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type | string | null): Promise<void>;
283
- export declare function readlink(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
283
+ export declare function readlink(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Promise<NonSharedBuffer>;
284
284
  export declare function readlink(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | null): Promise<string>;
285
- export declare function readlink(this: V_Context, path: fs.PathLike, options?: fs.BufferEncodingOption | fs.EncodingOption | string | null): Promise<string | Buffer>;
285
+ export declare function readlink(this: V_Context, path: fs.PathLike, options?: fs.BufferEncodingOption | fs.EncodingOption | string | null): Promise<string | NonSharedBuffer>;
286
286
  export declare function chown(this: V_Context, path: fs.PathLike, uid: number, gid: number): Promise<void>;
287
287
  export declare function lchown(this: V_Context, path: fs.PathLike, uid: number, gid: number): Promise<void>;
288
288
  export declare function chmod(this: V_Context, path: fs.PathLike, mode: fs.Mode): Promise<void>;
@@ -301,11 +301,11 @@ export declare function lutimes(this: V_Context, path: fs.PathLike, atime: fs.Ti
301
301
  * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. Defaults to `'utf8'`.
302
302
  * @todo handle options
303
303
  */
304
- export declare function realpath(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
304
+ export declare function realpath(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Promise<NonSharedBuffer>;
305
305
  export declare function realpath(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
306
306
  export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIteratorObject<promises.FileChangeInfo<string>, undefined>;
307
- export declare function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIteratorObject<promises.FileChangeInfo<Buffer>, undefined>;
308
- export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIteratorObject<promises.FileChangeInfo<string>, undefined> | AsyncIteratorObject<promises.FileChangeInfo<Buffer>, undefined>;
307
+ export declare function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIteratorObject<promises.FileChangeInfo<NonSharedBuffer>, undefined>;
308
+ export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIteratorObject<promises.FileChangeInfo<string>, undefined> | AsyncIteratorObject<promises.FileChangeInfo<NonSharedBuffer>, undefined>;
309
309
  export declare function access(this: V_Context, path: fs.PathLike, mode?: number): Promise<void>;
310
310
  /**
311
311
  * Asynchronous `rm`. Removes files or directories (recursively).
@@ -319,7 +319,7 @@ export declare function rm(this: V_Context, path: fs.PathLike, options?: fs.RmOp
319
319
  * @returns The path to the created temporary directory, encoded as a string or buffer.
320
320
  */
321
321
  export declare function mkdtemp(this: V_Context, prefix: string, options?: fs.EncodingOption): Promise<string>;
322
- export declare function mkdtemp(this: V_Context, prefix: string, options?: fs.BufferEncodingOption): Promise<Buffer>;
322
+ export declare function mkdtemp(this: V_Context, prefix: string, options?: fs.BufferEncodingOption): Promise<NonSharedBuffer>;
323
323
  /**
324
324
  * The resulting Promise holds an async-disposable object whose `path` property holds the created directory path.
325
325
  * When the object is disposed, the directory and its contents will be removed asynchronously if it still exists.
@@ -898,25 +898,33 @@ export async function statfs(path, opts) {
898
898
  export function glob(pattern, opt) {
899
899
  pattern = Array.isArray(pattern) ? pattern : [pattern];
900
900
  const { cwd = '/', withFileTypes = false, exclude = () => false } = opt || {};
901
- // Escape special characters in pattern
902
- const regexPatterns = pattern.map(globToRegex);
901
+ const normalizedPatterns = pattern.map(p => p.replace(/^\/+/g, ''));
902
+ const hasGlobStar = normalizedPatterns.some(p => p.includes('**'));
903
+ const patternBases = normalizedPatterns.map(p => {
904
+ const firstGlob = p.search(/[*?[\]{]/);
905
+ if (firstGlob === -1)
906
+ return p;
907
+ const lastSlash = p.lastIndexOf('/', firstGlob);
908
+ return lastSlash === -1 ? '' : p.slice(0, lastSlash);
909
+ });
910
+ const regexPatterns = normalizedPatterns.map(globToRegex);
903
911
  async function* recursiveList(dir) {
904
912
  const entries = await readdir(dir, { withFileTypes, encoding: 'utf8' });
905
913
  for (const entry of entries) {
906
- const fullPath = withFileTypes ? join(entry.parentPath, entry.name) : dir + '/' + entry;
914
+ const fullPath = join(dir, withFileTypes ? entry.name : entry);
907
915
  if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
908
916
  continue;
909
- /**
910
- * @todo is the pattern.source check correct?
911
- */
912
- if ((await stat(fullPath)).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
913
- yield* recursiveList(fullPath);
917
+ const relativePath = fullPath.replace(/^\/+/g, '');
918
+ if ((await stat(fullPath)).isDirectory()) {
919
+ if (hasGlobStar || patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'))) {
920
+ yield* recursiveList(fullPath);
921
+ }
914
922
  }
915
- if (regexPatterns.some(pattern => pattern.test(fullPath.replace(/^\/+/g, '')))) {
916
- yield withFileTypes ? entry : fullPath.replace(/^\/+/g, '');
923
+ if (regexPatterns.some(rx => rx.test(relativePath))) {
924
+ yield withFileTypes ? entry : relativePath;
917
925
  }
918
926
  }
919
927
  }
920
- return recursiveList(cwd);
928
+ return recursiveList(cwd instanceof URL ? cwd.pathname : cwd);
921
929
  }
922
930
  glob;
@@ -1,5 +1,4 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  // A cross-platform node:readline implementation
4
3
  import { EventEmitter } from 'eventemitter3';
5
4
  import { warn } from 'kerium/log';
@@ -122,11 +122,11 @@ export declare function readdirSync(this: V_Context, path: fs.PathLike, options:
122
122
  encoding: 'buffer';
123
123
  withFileTypes?: false;
124
124
  recursive?: boolean;
125
- } | 'buffer'): Buffer[];
125
+ } | 'buffer'): NonSharedBuffer[];
126
126
  export declare function readdirSync(this: V_Context, path: fs.PathLike, options?: (fs.ObjectEncodingOptions & {
127
127
  withFileTypes?: false;
128
128
  recursive?: boolean;
129
- }) | BufferEncoding | null): string[] | Buffer[];
129
+ }) | BufferEncoding | null): string[] | NonSharedBuffer[];
130
130
  export declare function readdirSync(this: V_Context, path: fs.PathLike, options: fs.ObjectEncodingOptions & {
131
131
  withFileTypes: true;
132
132
  recursive?: boolean;
@@ -135,7 +135,7 @@ export declare function readdirSync(this: V_Context, path: fs.PathLike, options:
135
135
  encoding: 'buffer';
136
136
  withFileTypes: true;
137
137
  recursive?: boolean;
138
- }): Dirent<Buffer>[];
138
+ }): Dirent<NonSharedBuffer>[];
139
139
  export declare function readdirSync(this: V_Context, path: fs.PathLike, options?: NodeReaddirOptions): string[] | Dirent<any>[] | Buffer[];
140
140
  export declare function linkSync(this: V_Context, targetPath: fs.PathLike, linkPath: fs.PathLike): void;
141
141
  /**
@@ -145,9 +145,9 @@ export declare function linkSync(this: V_Context, targetPath: fs.PathLike, linkP
145
145
  * @param type can be either `'dir'` or `'file'` (default is `'file'`)
146
146
  */
147
147
  export declare function symlinkSync(this: V_Context, target: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type | null): void;
148
- export declare function readlinkSync(this: V_Context, path: fs.PathLike, options?: fs.BufferEncodingOption): Buffer;
148
+ export declare function readlinkSync(this: V_Context, path: fs.PathLike, options?: fs.BufferEncodingOption): NonSharedBuffer;
149
149
  export declare function readlinkSync(this: V_Context, path: fs.PathLike, options: fs.EncodingOption | BufferEncoding): string;
150
- export declare function readlinkSync(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Buffer | string;
150
+ export declare function readlinkSync(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): NonSharedBuffer | string;
151
151
  export declare function chownSync(this: V_Context, path: fs.PathLike, uid: number, gid: number): void;
152
152
  export declare function lchownSync(this: V_Context, path: fs.PathLike, uid: number, gid: number): void;
153
153
  export declare function chmodSync(this: V_Context, path: fs.PathLike, mode: fs.Mode): void;
@@ -174,7 +174,7 @@ export declare function rmSync(this: V_Context, path: fs.PathLike, options?: fs.
174
174
  * @param options The encoding (or an object including `encoding`).
175
175
  * @returns The path to the created temporary directory, encoded as a string or buffer.
176
176
  */
177
- export declare function mkdtempSync(this: V_Context, prefix: fs.PathLike, options: fs.BufferEncodingOption): Buffer;
177
+ export declare function mkdtempSync(this: V_Context, prefix: fs.PathLike, options: fs.BufferEncodingOption): NonSharedBuffer;
178
178
  export declare function mkdtempSync(this: V_Context, prefix: fs.PathLike, options?: fs.EncodingOption): string;
179
179
  /**
180
180
  * Returns a disposable object whose `path` property holds the created directory path.
package/dist/node/sync.js CHANGED
@@ -656,27 +656,35 @@ export function statfsSync(path, options) {
656
656
  export function globSync(pattern, options = {}) {
657
657
  pattern = Array.isArray(pattern) ? pattern : [pattern];
658
658
  const { cwd = '/', withFileTypes = false, exclude = () => false } = options;
659
- // Escape special characters in pattern
660
- const regexPatterns = pattern.map(globToRegex);
659
+ const normalizedPatterns = pattern.map(p => p.replace(/^\/+/g, ''));
660
+ const hasGlobStar = normalizedPatterns.some(p => p.includes('**'));
661
+ const patternBases = normalizedPatterns.map(p => {
662
+ const firstGlob = p.search(/[*?[\]{]/);
663
+ if (firstGlob === -1)
664
+ return p;
665
+ const lastSlash = p.lastIndexOf('/', firstGlob);
666
+ return lastSlash === -1 ? '' : p.slice(0, lastSlash);
667
+ });
668
+ const regexPatterns = normalizedPatterns.map(globToRegex);
661
669
  const results = [];
662
670
  function recursiveList(dir) {
663
671
  const entries = readdirSync(dir, { withFileTypes, encoding: 'utf8' });
664
672
  for (const entry of entries) {
665
- const fullPath = withFileTypes ? join(entry.parentPath, entry.name) : dir + '/' + entry;
673
+ const fullPath = join(dir, withFileTypes ? entry.name : entry);
666
674
  if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
667
675
  continue;
668
- /**
669
- * @todo is the pattern.source check correct?
670
- */
671
- if (statSync(fullPath).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
672
- recursiveList(fullPath);
676
+ const relativePath = fullPath.replace(/^\/+/g, '');
677
+ if (statSync(fullPath).isDirectory()) {
678
+ if (hasGlobStar || patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'))) {
679
+ recursiveList(fullPath);
680
+ }
673
681
  }
674
- if (regexPatterns.some(pattern => pattern.test(fullPath.replace(/^\/+/g, '')))) {
675
- results.push(withFileTypes ? entry : fullPath.replace(/^\/+/g, ''));
682
+ if (regexPatterns.some(rx => rx.test(relativePath))) {
683
+ results.push(withFileTypes ? entry : relativePath);
676
684
  }
677
685
  }
678
686
  }
679
- recursiveList(cwd);
687
+ recursiveList(cwd instanceof URL ? cwd.pathname : cwd);
680
688
  return results;
681
689
  }
682
690
  globSync;
package/dist/polyfills.js CHANGED
@@ -5,9 +5,7 @@ import { warn } from 'kerium/log';
5
5
  Promise.withResolvers ??=
6
6
  (warn('Using a polyfill of Promise.withResolvers'),
7
7
  function () {
8
- let _resolve,
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- _reject;
8
+ let _resolve, _reject;
11
9
  const promise = new Promise((resolve, reject) => {
12
10
  _resolve = resolve;
13
11
  _reject = reject;
package/dist/utils.js CHANGED
@@ -97,11 +97,16 @@ export function normalizeOptions(options, encoding = 'utf8', flag, mode = 0) {
97
97
  * @internal
98
98
  */
99
99
  export function globToRegex(pattern) {
100
+ const GLOBSTAR = '\0GS\0';
101
+ const STAR = '\0S\0';
100
102
  pattern = pattern
101
- .replace(/([.?+^$(){}|[\]/])/g, '$1')
102
- .replace(/\*\*/g, '.*')
103
- .replace(/\*/g, '[^/]*')
104
- .replace(/\?/g, '.');
103
+ .replace(/\*\*/g, GLOBSTAR)
104
+ .replace(/\*/g, STAR)
105
+ .replace(/[.+^$(){}|[\]\\]/g, '\\$&')
106
+ .replace(/\?/g, '.')
107
+ .replaceAll('/' + GLOBSTAR + '/', '(?:/.*)?/')
108
+ .replaceAll(GLOBSTAR, '.*')
109
+ .replaceAll(STAR, '[^/]*');
105
110
  return new RegExp(`^${pattern}$`);
106
111
  }
107
112
  export async function waitOnline(worker) {
@@ -14,14 +14,12 @@ import { normalizePath } from '../utils.js';
14
14
  class Watcher extends EventEmitter {
15
15
  _context;
16
16
  path;
17
- /* eslint-disable @typescript-eslint/no-explicit-any */
18
17
  off(event, fn, context, once) {
19
18
  return super.off(event, fn, context, once);
20
19
  }
21
20
  removeListener(event, fn, context, once) {
22
21
  return super.removeListener(event, fn, context, once);
23
22
  }
24
- /* eslint-enable @typescript-eslint/no-explicit-any */
25
23
  constructor(
26
24
  /**
27
25
  * @internal
package/eslint.shared.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /* Shared eslint rules */
2
2
  import eslint from '@eslint/js';
3
3
  import globals from 'globals';
4
- import tseslint from 'typescript-eslint';
4
+ import { configs } from 'typescript-eslint';
5
5
 
6
6
  export default [
7
7
  {
8
8
  name: 'ZenFS',
9
- extends: [eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked],
9
+ extends: [eslint.configs.recommended, ...configs.recommendedTypeChecked],
10
10
  files: ['src/**/*.ts', 'tests/**/*.ts'],
11
11
  languageOptions: {
12
12
  globals: { ...globals.browser, ...globals.node },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -71,7 +71,7 @@
71
71
  "prepublishOnly": "npm run build"
72
72
  },
73
73
  "dependencies": {
74
- "@types/node": "^24.1.0",
74
+ "@types/node": "^25.2.0",
75
75
  "buffer": "^6.0.3",
76
76
  "eventemitter3": "^5.0.1",
77
77
  "kerium": "^1.3.4",
@@ -80,12 +80,10 @@
80
80
  "utilium": "^2.5.0"
81
81
  },
82
82
  "devDependencies": {
83
- "@eslint/js": "^9.8.0",
84
- "@octokit/action": "^7.0.0",
85
- "@types/eslint__js": "^8.42.3",
86
- "c8": "^10.1.2",
83
+ "@octokit/action": "^8.0.4",
84
+ "c8": "^10.1.3",
87
85
  "eslint": "^9.15.0",
88
- "globals": "^16.0.0",
86
+ "globals": "^17.3.0",
89
87
  "prettier": "^3.2.5",
90
88
  "tsx": "^4.19.1",
91
89
  "typedoc": "^0.28.0",
package/scripts/test.js CHANGED
@@ -87,9 +87,19 @@ if (options.clean) {
87
87
  process.exit();
88
88
  }
89
89
 
90
+ function report() {
91
+ try {
92
+ execSync('npx c8 report --reporter=text', { stdio: 'inherit' });
93
+ } catch (e) {
94
+ console.error('Failed to generate coverage report!');
95
+ console.error(e);
96
+ } finally {
97
+ rmSync(options.coverage, { recursive: true });
98
+ }
99
+ }
100
+
90
101
  if (options.report) {
91
- execSync('npx c8 report --reporter=text', { stdio: 'inherit' });
92
- rmSync(options.coverage, { recursive: true, force: true });
102
+ report();
93
103
  process.exit();
94
104
  }
95
105
 
@@ -237,7 +247,4 @@ for (const setupFile of positionals) {
237
247
  }
238
248
  }
239
249
 
240
- if (!options.preserve) {
241
- execSync('npx c8 report --reporter=text', { stdio: 'inherit' });
242
- rmSync(options.coverage, { recursive: true });
243
- }
250
+ if (!options.preserve) report();
@@ -64,7 +64,7 @@ await suite('SingleBuffer', () => {
64
64
  const growthSizes = [0, 1, 17, 512, 8192, 65535, 262144, 524288];
65
65
  const shrinkSizes = [262144, 4096, 128, 0];
66
66
 
67
- const verifySnapshot = async (expected: Buffer, size: number) => {
67
+ const verifySnapshot = (expected: Buffer, size: number) => {
68
68
  mount(verifyMountPoint, writable);
69
69
  try {
70
70
  const reopened = fs.readFileSync(`${verifyMountPoint}/payload.bin`);
@@ -82,7 +82,7 @@ await suite('SingleBuffer', () => {
82
82
  const direct = fs.readFileSync(filePath);
83
83
  assert.strictEqual(direct.byteLength, size, `direct size mismatch for ${size} bytes`);
84
84
  assert.deepStrictEqual(direct, payload, `direct content mismatch for ${size} bytes`);
85
- await verifySnapshot(direct, size);
85
+ verifySnapshot(direct, size);
86
86
  }
87
87
 
88
88
  for (const size of shrinkSizes) {
@@ -91,7 +91,7 @@ await suite('SingleBuffer', () => {
91
91
  const direct = fs.readFileSync(filePath);
92
92
  assert.strictEqual(direct.byteLength, size, `direct size mismatch after shrink to ${size} bytes`);
93
93
  assert.deepStrictEqual(direct, payload, `direct content mismatch after shrink to ${size} bytes`);
94
- await verifySnapshot(direct, size);
94
+ verifySnapshot(direct, size);
95
95
  }
96
96
  } finally {
97
97
  if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
@@ -0,0 +1,135 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-or-later
2
+ import assert from 'node:assert/strict';
3
+ import { suite, test } from 'node:test';
4
+ import { fs } from '../common.js';
5
+
6
+ // Set up a directory structure for glob tests
7
+ fs.mkdirSync('/glob');
8
+ fs.mkdirSync('/glob/sub');
9
+ fs.mkdirSync('/glob/sub/deep');
10
+ fs.writeFileSync('/glob/a.txt', 'a');
11
+ fs.writeFileSync('/glob/b.txt', 'b');
12
+ fs.writeFileSync('/glob/c.js', 'c');
13
+ fs.writeFileSync('/glob/sub/d.txt', 'd');
14
+ fs.writeFileSync('/glob/sub/e.js', 'e');
15
+ fs.writeFileSync('/glob/sub/deep/f.txt', 'f');
16
+
17
+ suite('globSync', () => {
18
+ test('wildcard in root', () => {
19
+ const results = fs.globSync('glob/*');
20
+ assert(results.includes('glob/a.txt'), 'should include glob/a.txt');
21
+ assert(results.includes('glob/b.txt'), 'should include glob/b.txt');
22
+ assert(results.includes('glob/c.js'), 'should include glob/c.js');
23
+ assert(results.includes('glob/sub'), 'should include glob/sub');
24
+ });
25
+
26
+ test('wildcard with absolute path pattern', () => {
27
+ const results = fs.globSync('/glob/*');
28
+ assert(results.includes('glob/a.txt'), 'should include glob/a.txt');
29
+ assert(results.includes('glob/b.txt'), 'should include glob/b.txt');
30
+ assert(results.includes('glob/c.js'), 'should include glob/c.js');
31
+ });
32
+
33
+ test('wildcard with extension filter', () => {
34
+ const results = fs.globSync('/glob/*.txt');
35
+ assert(results.includes('glob/a.txt'));
36
+ assert(results.includes('glob/b.txt'));
37
+ assert(!results.includes('glob/c.js'), 'should not include .js files');
38
+ });
39
+
40
+ test('nested path wildcard', () => {
41
+ const results = fs.globSync('/glob/sub/*');
42
+ assert(results.includes('glob/sub/d.txt'));
43
+ assert(results.includes('glob/sub/e.js'));
44
+ assert(!results.includes('glob/a.txt'), 'should not include files from parent');
45
+ });
46
+
47
+ test('globstar (**)', () => {
48
+ const results = fs.globSync('/glob/**/*.txt');
49
+ assert(results.includes('glob/a.txt'));
50
+ assert(results.includes('glob/b.txt'));
51
+ assert(results.includes('glob/sub/d.txt'));
52
+ assert(results.includes('glob/sub/deep/f.txt'));
53
+ assert(!results.includes('glob/c.js'), 'should not include .js files');
54
+ });
55
+
56
+ test('question mark wildcard', () => {
57
+ const results = fs.globSync('/glob/?.txt');
58
+ assert(results.includes('glob/a.txt'));
59
+ assert(results.includes('glob/b.txt'));
60
+ assert(!results.includes('glob/c.js'));
61
+ });
62
+
63
+ test('multiple patterns', () => {
64
+ const results = fs.globSync(['/glob/*.txt', '/glob/*.js']);
65
+ assert(results.includes('glob/a.txt'));
66
+ assert(results.includes('glob/c.js'));
67
+ });
68
+
69
+ test('no matches returns empty', () => {
70
+ const results = fs.globSync('/glob/*.xyz');
71
+ assert.equal(results.length, 0);
72
+ });
73
+
74
+ test('withFileTypes option', () => {
75
+ const results = fs.globSync('/glob/*.txt', { withFileTypes: true });
76
+ assert(results.length > 0, 'should have results');
77
+ assert(typeof results[0] === 'object' && 'name' in results[0], 'results should be Dirent objects');
78
+ });
79
+
80
+ test('exclude option with function', () => {
81
+ const results = fs.globSync('/glob/*', { exclude: path => typeof path === 'string' && path.endsWith('.js') });
82
+ assert(!results.includes('glob/c.js'), 'should exclude .js files');
83
+ assert(results.includes('glob/a.txt'), 'should still include .txt files');
84
+ });
85
+ });
86
+
87
+ await suite('promises.glob', () => {
88
+ test('wildcard in root', async () => {
89
+ const results = await Array.fromAsync(fs.promises.glob('/glob/*'));
90
+ assert(results.includes('glob/a.txt'));
91
+ assert(results.includes('glob/b.txt'));
92
+ assert(results.includes('glob/c.js'));
93
+ assert(results.includes('glob/sub'));
94
+ });
95
+
96
+ test('wildcard with absolute path pattern', async () => {
97
+ const results = await Array.fromAsync(fs.promises.glob('/glob/*'));
98
+ assert(results.includes('glob/a.txt'));
99
+ assert(results.includes('glob/b.txt'));
100
+ });
101
+
102
+ test('wildcard with extension filter', async () => {
103
+ const results = await Array.fromAsync(fs.promises.glob('/glob/*.txt'));
104
+ assert(results.includes('glob/a.txt'));
105
+ assert(results.includes('glob/b.txt'));
106
+ assert(!results.includes('glob/c.js'));
107
+ });
108
+
109
+ test('nested path wildcard', async () => {
110
+ const results = await Array.fromAsync(fs.promises.glob('/glob/sub/*'));
111
+ assert(results.includes('glob/sub/d.txt'));
112
+ assert(results.includes('glob/sub/e.js'));
113
+ assert(!results.includes('glob/a.txt'));
114
+ });
115
+
116
+ test('globstar (**)', async () => {
117
+ const results = await Array.fromAsync(fs.promises.glob('/glob/**/*.txt'));
118
+ assert(results.includes('glob/a.txt'));
119
+ assert(results.includes('glob/b.txt'));
120
+ assert(results.includes('glob/sub/d.txt'));
121
+ assert(results.includes('glob/sub/deep/f.txt'));
122
+ assert(!results.includes('glob/c.js'));
123
+ });
124
+
125
+ test('multiple patterns', async () => {
126
+ const results = await Array.fromAsync(fs.promises.glob(['/glob/*.txt', '/glob/*.js']));
127
+ assert(results.includes('glob/a.txt'));
128
+ assert(results.includes('glob/c.js'));
129
+ });
130
+
131
+ test('no matches returns empty', async () => {
132
+ const results = await Array.fromAsync(fs.promises.glob('/glob/*.xyz'));
133
+ assert.equal(results.length, 0);
134
+ });
135
+ });