@zenfs/core 2.6.2 → 2.6.4

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.
Files changed (64) hide show
  1. package/README.md +1 -1
  2. package/dist/backends/single_buffer.js +34 -11
  3. package/dist/context.js +1 -0
  4. package/dist/internal/contexts.d.ts +3 -1
  5. package/dist/internal/contexts.js +11 -1
  6. package/dist/internal/inode.d.ts +2 -0
  7. package/dist/internal/inode.js +2 -0
  8. package/dist/internal/rpc.d.ts +4 -4
  9. package/dist/node/async.d.ts +11 -1
  10. package/dist/node/async.js +14 -35
  11. package/dist/node/index.d.ts +1 -0
  12. package/dist/node/index.js +1 -0
  13. package/dist/node/promises.d.ts +17 -3
  14. package/dist/node/promises.js +32 -16
  15. package/dist/node/stats.d.ts +12 -0
  16. package/dist/node/stats.js +35 -6
  17. package/dist/node/sync.d.ts +39 -11
  18. package/dist/node/sync.js +41 -17
  19. package/dist/node/utf8stream.d.ts +99 -0
  20. package/dist/node/utf8stream.js +690 -0
  21. package/dist/utils.d.ts +10 -0
  22. package/dist/utils.js +17 -0
  23. package/dist/vfs/shared.js +9 -7
  24. package/package.json +14 -10
  25. package/scripts/test.js +15 -8
  26. package/tests/backend/single-buffer-rotate.worker.js +19 -0
  27. package/tests/backend/single-buffer.test.ts +106 -0
  28. package/tests/common/context.test.ts +38 -0
  29. package/tests/common/glob.test.ts +86 -1
  30. package/tests/common/utf8stream.test.ts +164 -0
  31. package/tests/common.ts +4 -2
  32. package/tests/fetch/fetch.ts +1 -1
  33. package/tests/fetch/run.sh +1 -1
  34. package/tests/fs/append.test.ts +1 -1
  35. package/tests/fs/concurrency.test.ts +1 -1
  36. package/tests/fs/dir.test.ts +1 -1
  37. package/tests/fs/directory.test.ts +3 -3
  38. package/tests/fs/errors.test.ts +1 -1
  39. package/tests/fs/exists.test.ts +3 -3
  40. package/tests/fs/links.test.ts +5 -5
  41. package/tests/fs/open.test.ts +5 -5
  42. package/tests/fs/permissions.test.ts +5 -5
  43. package/tests/fs/read.test.ts +3 -3
  44. package/tests/fs/readFile.test.ts +2 -2
  45. package/tests/fs/rename.test.ts +5 -5
  46. package/tests/fs/scaling.test.ts +1 -1
  47. package/tests/fs/stat.test.ts +32 -3
  48. package/tests/fs/streams.test.ts +1 -1
  49. package/tests/fs/tempdir.test.ts +1 -1
  50. package/tests/fs/times.test.ts +2 -2
  51. package/tests/fs/truncate.test.ts +1 -1
  52. package/tests/fs/watch.test.ts +1 -1
  53. package/tests/fs/write.test.ts +2 -2
  54. package/tests/fs/xattr.test.ts +1 -1
  55. package/tests/{native/setup.ts → native-setup.ts} +5 -5
  56. package/tests/setup/context.ts +1 -1
  57. package/tests/setup/cow.ts +1 -1
  58. package/tests/setup/index.ts +1 -1
  59. package/tests/setup/memory.ts +1 -1
  60. package/tests/setup/port.ts +1 -1
  61. package/tests/setup/single-buffer.ts +1 -1
  62. package/tests/setup.ts +1 -1
  63. package/tests/tsconfig.json +2 -1
  64. package/tests/native/run.sh +0 -3
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ZenFS is a cross-platform library that emulates the [Node.js filesystem API](http://nodejs.org/api/fs.html).
4
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.
5
+ ZenFS should cover the full API surface of the latest Node.js version, though complex changes may lag a little bit.
6
6
 
7
7
  ## Backends
8
8
 
@@ -320,7 +320,7 @@ const sb_magic = 0x62732e7a; // 'z.sb'
320
320
  * Shortcut for minor perf. bump
321
321
  * @internal
322
322
  */
323
- const usedBytes = 2;
323
+ const kUsedBytes = 2;
324
324
  /**
325
325
  * The super block structure for a single-buffer file system
326
326
  */
@@ -502,16 +502,34 @@ let SuperBlock = (() => {
502
502
  * @returns the new metadata block
503
503
  */
504
504
  rotateMetadata() {
505
- const padding = this.used_bytes % BigInt(4);
506
- Atomics.add(this, usedBytes, padding);
507
- const offset = Number(Atomics.add(this, usedBytes, BigInt(sizeof(MetadataBlock))));
508
- const metadata = new MetadataBlock(this.buffer, offset);
509
- metadata.previous_offset = this.metadata_offset;
505
+ const alignment = BigInt(Int32Array.BYTES_PER_ELEMENT), blockSize = BigInt(sizeof(MetadataBlock));
506
+ let used = Atomics.load(this, kUsedBytes), offset;
507
+ // Padding and block space must be reserved together because other writers can advance used_bytes at any time.
508
+ for (;;) {
509
+ const padding = (alignment - (used % alignment)) % alignment;
510
+ offset = used + padding;
511
+ const next = offset + blockSize;
512
+ if (next > this.total_bytes)
513
+ throw err(withErrno('ENOSPC', 'sbfs: not enough space for a new metadata block'));
514
+ const observed = Atomics.compareExchange(this, kUsedBytes, used, next);
515
+ if (observed === used)
516
+ break;
517
+ used = observed;
518
+ }
519
+ const metadata = new MetadataBlock(this.buffer, Number(offset));
520
+ const offsets = new Uint32Array(this.buffer, this.byteOffset, sizeof(SuperBlock) / Uint32Array.BYTES_PER_ELEMENT);
521
+ let previous = Atomics.load(offsets, kMetadataOffset);
522
+ for (;;) {
523
+ metadata.previous_offset = previous;
524
+ _update(metadata);
525
+ const observed = Atomics.compareExchange(offsets, kMetadataOffset, previous, metadata.byteOffset);
526
+ if (observed === previous)
527
+ break;
528
+ previous = observed;
529
+ }
510
530
  this.metadata = metadata;
511
- this.metadata_offset = metadata.byteOffset;
512
- _update(metadata);
513
531
  _update(this);
514
- debug(`sbfs: rotated metadata block at ${hex(metadata.previous_offset)} with new block at ${hex(offset)}`);
532
+ debug(`sbfs: rotated metadata block at ${hex(metadata.previous_offset)} with new block at ${hex(metadata.byteOffset)}`);
515
533
  return metadata;
516
534
  }
517
535
  /**
@@ -545,6 +563,11 @@ let SuperBlock = (() => {
545
563
  return SuperBlock = _classThis;
546
564
  })();
547
565
  export { SuperBlock };
566
+ /**
567
+ * Index of `SuperBlock.metadata_offset` in a `Uint32Array` view of the super block.
568
+ * @internal
569
+ */
570
+ const kMetadataOffset = offsetof(SuperBlock, 'metadata_offset') / Uint32Array.BYTES_PER_ELEMENT;
548
571
  /**
549
572
  * Compute the checksum for a super block or metadata block.
550
573
  * Note we don't include the checksum when computing a new one.
@@ -635,7 +658,7 @@ export class SingleBufferStore extends BufferView {
635
658
  _update(block);
636
659
  return;
637
660
  }
638
- entry.offset = Number(Atomics.add(this.superblock, usedBytes, BigInt(data.length)));
661
+ entry.offset = Number(Atomics.add(this.superblock, kUsedBytes, BigInt(data.length)));
639
662
  entry.size = data.length;
640
663
  this._u8.set(data, entry.offset);
641
664
  _update(block);
@@ -657,7 +680,7 @@ export class SingleBufferStore extends BufferView {
657
680
  entry = this.superblock.metadata.items[0];
658
681
  }
659
682
  const lock = __addDisposableResource(env_1, this.superblock.metadata.lock(), false);
660
- const offset = Number(Atomics.add(this.superblock, usedBytes, BigInt(data.length)));
683
+ const offset = Number(Atomics.add(this.superblock, kUsedBytes, BigInt(data.length)));
661
684
  entry.id = id;
662
685
  entry.offset = offset;
663
686
  entry.size = data.length;
package/dist/context.js CHANGED
@@ -24,6 +24,7 @@ export function bindContext(init = {}) {
24
24
  const bound = Object.assign(ctx, {
25
25
  fs: {
26
26
  ...bindFunctions(fs, ctx),
27
+ Utf8Stream: fs.Utf8Stream._withContext(ctx),
27
28
  promises: bindFunctions(fs.promises, ctx),
28
29
  xattr: bindFunctions(xattr, ctx),
29
30
  },
@@ -14,7 +14,7 @@ declare const kIsContext: unique symbol;
14
14
  * @category Contexts
15
15
  */
16
16
  export interface FSContext {
17
- /** The unique ID of the context */
17
+ /** Brand */
18
18
  readonly [kIsContext]: boolean;
19
19
  /** The unique ID of the context */
20
20
  readonly id: number;
@@ -35,6 +35,8 @@ export interface FSContext {
35
35
  /** The child contexts */
36
36
  readonly children: FSContext[];
37
37
  }
38
+ export declare function isContext(obj: unknown): obj is FSContext;
39
+ export declare function assertContext(ctx: unknown): asserts ctx is FSContext;
38
40
  /**
39
41
  * maybe an FS context
40
42
  */
@@ -1,9 +1,18 @@
1
1
  import { createCredentials } from './credentials.js';
2
+ import { warn } from 'kerium/log';
3
+ import { withErrno } from 'kerium';
2
4
  /**
3
5
  * Symbol used for context branding
4
6
  * @internal @hidden
5
7
  */
6
8
  const kIsContext = Symbol('ZenFSContext');
9
+ export function isContext(obj) {
10
+ return typeof obj === 'object' && obj !== null && kIsContext in obj;
11
+ }
12
+ export function assertContext(ctx) {
13
+ if (!isContext(ctx))
14
+ throw warn(withErrno('EINVAL', 'Invalid context provided'));
15
+ }
7
16
  /**
8
17
  * The default/global context.
9
18
  * @internal @hidden
@@ -20,7 +29,7 @@ export const defaultContext = {
20
29
  children: [],
21
30
  };
22
31
  export function contextOf($) {
23
- return typeof $ === 'object' && $ !== null && kIsContext in $ ? $ : defaultContext;
32
+ return isContext($) ? $ : defaultContext;
24
33
  }
25
34
  // 0 is reserved for the global/default context
26
35
  let _nextId = 1;
@@ -33,6 +42,7 @@ let _nextId = 1;
33
42
  * This exists so that `kIsContext` is not exported and to make sure the context is "secure".
34
43
  */
35
44
  export function createChildContext(parent, init = {}) {
45
+ assertContext(parent);
36
46
  const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials) } = init;
37
47
  const ctx = {
38
48
  [kIsContext]: true,
@@ -243,6 +243,8 @@ export declare function isFIFO(metadata: {
243
243
  export declare function hasAccess($: V_Context, inode: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, access: number): boolean;
244
244
  /**
245
245
  * @hidden @internal
246
+ *
247
+ * @todo https://github.com/nodejs/node/pull/58836 support -1 for `*chown`
246
248
  */
247
249
  export declare function _chown(stats: Partial<InodeLike>, uid: number, gid: number): boolean;
248
250
  export {};
@@ -662,6 +662,8 @@ export function hasAccess($, inode, access) {
662
662
  }
663
663
  /**
664
664
  * @hidden @internal
665
+ *
666
+ * @todo https://github.com/nodejs/node/pull/58836 support -1 for `*chown`
665
667
  */
666
668
  export function _chown(stats, uid, gid) {
667
669
  let valid = true;
@@ -1,12 +1,12 @@
1
1
  import type { ExceptionJSON } from 'kerium';
2
- import type { TransferListItem } from 'node:worker_threads';
2
+ import type { Transferable } from 'node:worker_threads';
3
3
  import type { WithOptional } from 'utilium';
4
4
  import type { Backend, FilesystemOf } from '../backends/backend.js';
5
5
  import type { PortFS } from '../backends/port.js';
6
6
  import type { CreationOptions, FileSystem, UsageInfo } from '../internal/filesystem.js';
7
7
  import '../polyfills.js';
8
8
  export interface WebMessagePort {
9
- postMessage(value: unknown, transfer?: TransferListItem[]): void;
9
+ postMessage(value: unknown, transfer?: Transferable[]): void;
10
10
  addEventListener(type: 'message', listener: (ev: {
11
11
  data: any;
12
12
  }) => void): void;
@@ -15,7 +15,7 @@ export interface WebMessagePort {
15
15
  }) => void): void;
16
16
  }
17
17
  export interface NodeMessagePort {
18
- postMessage(value: unknown, transfer?: TransferListItem[]): void;
18
+ postMessage(value: unknown, transfer?: Transferable[]): void;
19
19
  on(type: 'message', listener: (ev: any) => void): void;
20
20
  off(type: 'message', listener: (ev: any) => void): void;
21
21
  }
@@ -24,7 +24,7 @@ export type Channel = NodeMessagePort | WebMessagePort | WebSocket;
24
24
  export interface Port<T extends Channel = Channel> {
25
25
  readonly channel: T;
26
26
  /** Send a request */
27
- send<M extends Message>(message: M, transfer?: TransferListItem[]): void;
27
+ send<M extends Message>(message: M, transfer?: Transferable[]): void;
28
28
  /** Add a response handler */
29
29
  addHandler<M extends Message>(handler: (message: M) => void): void;
30
30
  /** Remove a response handler */
@@ -22,11 +22,21 @@ export declare function exists(this: V_Context, path: fs.PathLike, cb?: (exists:
22
22
  export declare function stat(this: V_Context, path: fs.PathLike, callback: Callback<[Stats]>): void;
23
23
  export declare function stat(this: V_Context, path: fs.PathLike, options: {
24
24
  bigint?: false;
25
+ throwIfNoEntry?: true;
25
26
  }, callback: Callback<[Stats]>): void;
26
27
  export declare function stat(this: V_Context, path: fs.PathLike, options: {
27
28
  bigint: true;
29
+ throwIfNoEntry?: true;
28
30
  }, callback: Callback<[BigIntStats]>): void;
29
- export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions, callback: Callback<[Stats] | [BigIntStats]>): void;
31
+ export declare function stat(this: V_Context, path: fs.PathLike, options: {
32
+ bigint?: false;
33
+ throwIfNoEntry: false;
34
+ }, callback: Callback<[Stats | undefined]>): void;
35
+ export declare function stat(this: V_Context, path: fs.PathLike, options: {
36
+ bigint: true;
37
+ throwIfNoEntry: false;
38
+ }, callback: Callback<[BigIntStats | undefined]>): void;
39
+ export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions, callback: Callback<[Stats | BigIntStats | undefined]>): void;
30
40
  /**
31
41
  * Asynchronous `lstat`.
32
42
  * `lstat()` is identical to `stat()`, except that if path is a symbolic link,
@@ -148,47 +148,26 @@ fdatasync;
148
148
  export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
149
149
  let buffer, offset, length, position, encoding;
150
150
  const handle = new promises.FileHandle(this, fd);
151
+ const _cb = ([cbPosOff, cbLenEnc, cbPosEnc].find(arg => typeof arg === 'function') ?? cb);
151
152
  if (typeof data === 'string') {
152
- // Signature 1: (fd, string, [position?, [encoding?]], cb?)
153
- encoding = 'utf8';
154
- switch (typeof cbPosOff) {
155
- case 'function':
156
- // (fd, string, cb)
157
- cb = cbPosOff;
158
- break;
159
- case 'number':
160
- // (fd, string, position, encoding?, cb?)
161
- position = cbPosOff;
162
- encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
163
- cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
164
- break;
165
- default:
166
- // ...try to find the callback and get out of here!
167
- cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb);
168
- cb(withErrno('EINVAL'));
169
- return;
170
- }
171
- buffer = Buffer.from(data);
153
+ // (fd, string, [position?, [encoding?]], cb?)
154
+ position = typeof cbPosOff === 'number' ? cbPosOff : null;
155
+ encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
156
+ buffer = Buffer.from(data, encoding);
172
157
  offset = 0;
173
- length = buffer.length;
174
- const _cb = cb;
175
- handle
176
- .write(buffer, offset, length, position)
177
- .then(({ bytesWritten }) => _cb(null, bytesWritten, buffer.toString(encoding)))
178
- .catch(_cb);
158
+ length = buffer.byteLength;
179
159
  }
180
160
  else {
181
- // Signature 2: (fd, buffer, offset, length, position?, cb?)
182
- buffer = Buffer.from(data.buffer);
183
- offset = cbPosOff;
184
- length = cbLenEnc;
161
+ // (fd, buffer, [offset?, [length?, [position?]]], cb?)
162
+ buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
163
+ offset = typeof cbPosOff === 'number' ? cbPosOff : 0;
164
+ length = typeof cbLenEnc === 'number' ? cbLenEnc : buffer.byteLength - offset;
185
165
  position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
186
- const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb);
187
- void handle
188
- .write(buffer, offset, length, position)
189
- .then(({ bytesWritten }) => _cb(null, bytesWritten, buffer))
190
- .catch(_cb);
191
166
  }
167
+ handle
168
+ .write(buffer, offset, length, position)
169
+ .then(({ bytesWritten }) => _cb(null, bytesWritten, typeof data === 'string' ? data : buffer))
170
+ .catch(_cb);
192
171
  }
193
172
  write;
194
173
  /**
@@ -6,3 +6,4 @@ export { BigIntStatsFs, Stats, StatsFs } from './stats.js';
6
6
  export type { StatsLike } from './stats.js';
7
7
  export * from './streams.js';
8
8
  export * from './sync.js';
9
+ export { Utf8Stream } from './utf8stream.js';
@@ -6,3 +6,4 @@ export * as promises from './promises.js';
6
6
  export { BigIntStatsFs, Stats, StatsFs } from './stats.js';
7
7
  export * from './streams.js';
8
8
  export * from './sync.js';
9
+ export { Utf8Stream } from './utf8stream.js';
@@ -180,11 +180,25 @@ export declare function rename(this: V_Context, oldPath: fs.PathLike, newPath: f
180
180
  * Test whether or not `path` exists by checking with the file system.
181
181
  */
182
182
  export declare function exists(this: V_Context, path: fs.PathLike): Promise<boolean>;
183
- export declare function stat(this: V_Context, path: fs.PathLike, options: fs.BigIntOptions): Promise<BigIntStats>;
184
- export declare function stat(this: V_Context, path: fs.PathLike, options?: {
183
+ export declare function stat(this: V_Context, path: fs.PathLike, options: fs.BigIntOptions & {
184
+ throwIfNoEntry?: true;
185
+ }): Promise<BigIntStats>;
186
+ export declare function stat(this: V_Context, path: fs.PathLike, options?: fs.StatOptions & {
185
187
  bigint?: false;
188
+ throwIfNoEntry?: true;
186
189
  }): Promise<Stats>;
187
- export declare function stat(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats>;
190
+ export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
191
+ bigint?: false;
192
+ throwIfNoEntry: false;
193
+ }): Promise<Stats | undefined>;
194
+ export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
195
+ bigint: true;
196
+ throwIfNoEntry: false;
197
+ }): Promise<BigIntStats | undefined>;
198
+ export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
199
+ throwIfNoEntry?: true;
200
+ }): Promise<Stats | BigIntStats>;
201
+ export declare function stat(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats | undefined>;
188
202
  /**
189
203
  * `lstat`.
190
204
  * `lstat()` is identical to `stat()`, except that if path is a symbolic link,
@@ -54,10 +54,11 @@ import { Buffer } from 'buffer';
54
54
  import { Exception, rethrow, UV } from 'kerium';
55
55
  import { encodeUTF8 } from 'utilium';
56
56
  import * as constants from '../constants.js';
57
+ import { contextOf } from '../internal/contexts.js';
57
58
  import { hasAccess, InodeFlags, isDirectory } from '../internal/inode.js';
58
- import { dirname, join, matchesGlob, resolve } from '../path.js';
59
+ import { dirname, join, matchesGlob, relative, resolve } from '../path.js';
59
60
  import '../polyfills.js';
60
- import { _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime, validateFD } from '../utils.js';
61
+ import { _isNoEntry, _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime, validateFD } from '../utils.js';
61
62
  import * as _async from '../vfs/async.js';
62
63
  import { checkAccess } from '../vfs/config.js';
63
64
  import { deleteFD, fromFD, toFD } from '../vfs/file.js';
@@ -541,7 +542,15 @@ export async function exists(path) {
541
542
  }
542
543
  }
543
544
  export async function stat(path, options) {
544
- const stats = await _async.stat.call(this, path, false);
545
+ let stats;
546
+ try {
547
+ stats = await _async.stat.call(this, path, false);
548
+ }
549
+ catch (e) {
550
+ if (options?.throwIfNoEntry === false && _isNoEntry(e))
551
+ return;
552
+ throw e;
553
+ }
545
554
  return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
546
555
  }
547
556
  stat;
@@ -1113,8 +1122,10 @@ export async function statfs(path, opts) {
1113
1122
  }
1114
1123
  export function glob(pattern, opt) {
1115
1124
  pattern = Array.isArray(pattern) ? pattern : [pattern];
1116
- const { cwd = '/', withFileTypes = false, exclude = () => false } = opt || {};
1117
- const normalizedPatterns = pattern.map(p => p.replace(/^\/+/g, ''));
1125
+ const $ = contextOf(this);
1126
+ const { cwd = $.pwd, withFileTypes = false, exclude = () => false, followSymlinks = false } = opt || {};
1127
+ const base = resolve.call($, cwd instanceof URL ? cwd.pathname : cwd);
1128
+ const normalizedPatterns = pattern.map(p => (p.startsWith('/') ? relative.call($, base, p) : p));
1118
1129
  const hasGlobStar = normalizedPatterns.some(p => p.includes('**'));
1119
1130
  const patternBases = normalizedPatterns.map(p => {
1120
1131
  const firstGlob = p.search(/[*?[\]{]/);
@@ -1124,23 +1135,28 @@ export function glob(pattern, opt) {
1124
1135
  return lastSlash === -1 ? '' : p.slice(0, lastSlash);
1125
1136
  });
1126
1137
  const regexPatterns = normalizedPatterns.map(globToRegex);
1138
+ async function shouldDescend(path, relativePath) {
1139
+ const isNamed = patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'));
1140
+ const inScope = hasGlobStar || isNamed;
1141
+ const stats = await lstat.call($, path);
1142
+ if (!stats.isSymbolicLink())
1143
+ return inScope && stats.isDirectory();
1144
+ return inScope && (followSymlinks || isNamed) && !!(await stat.call($, path, { throwIfNoEntry: false }))?.isDirectory();
1145
+ }
1127
1146
  async function* recursiveList(dir) {
1128
- const entries = await readdir(dir, { withFileTypes, encoding: 'utf8' });
1147
+ const entries = await readdir.call($, dir, { withFileTypes, encoding: 'utf8' });
1129
1148
  for (const entry of entries) {
1130
1149
  const fullPath = join(dir, withFileTypes ? entry.name : entry);
1131
- if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
1150
+ const relPath = relative.call($, base, fullPath);
1151
+ if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, relPath)) : exclude((withFileTypes ? entry : relPath)))
1132
1152
  continue;
1133
- const relativePath = fullPath.replace(/^\/+/g, '');
1134
- if ((await stat(fullPath)).isDirectory()) {
1135
- if (hasGlobStar || patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'))) {
1136
- yield* recursiveList(fullPath);
1137
- }
1138
- }
1139
- if (regexPatterns.some(rx => rx.test(relativePath))) {
1140
- yield withFileTypes ? entry : relativePath;
1153
+ if (await shouldDescend(fullPath, relPath))
1154
+ yield* recursiveList(fullPath);
1155
+ if (regexPatterns.some(rx => rx.test(relPath))) {
1156
+ yield withFileTypes ? entry : relPath;
1141
1157
  }
1142
1158
  }
1143
1159
  }
1144
- return recursiveList(cwd instanceof URL ? cwd.pathname : cwd);
1160
+ return recursiveList(base);
1145
1161
  }
1146
1162
  glob;
@@ -91,24 +91,32 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
91
91
  atimeMs: T;
92
92
  get atime(): Date;
93
93
  set atime(value: Date);
94
+ get atimeInstant(): Temporal.Instant;
95
+ set atimeInstant(value: Temporal.Instant);
94
96
  /**
95
97
  * Time of last modification, since epoch
96
98
  */
97
99
  mtimeMs: T;
98
100
  get mtime(): Date;
99
101
  set mtime(value: Date);
102
+ get mtimeInstant(): Temporal.Instant;
103
+ set mtimeInstant(value: Temporal.Instant);
100
104
  /**
101
105
  * Time of last time file status was changed, since epoch
102
106
  */
103
107
  ctimeMs: T;
104
108
  get ctime(): Date;
105
109
  set ctime(value: Date);
110
+ get ctimeInstant(): Temporal.Instant;
111
+ set ctimeInstant(value: Temporal.Instant);
106
112
  /**
107
113
  * Time of file creation, since epoch
108
114
  */
109
115
  birthtimeMs: T;
110
116
  get birthtime(): Date;
111
117
  set birthtime(value: Date);
118
+ get birthtimeInstant(): Temporal.Instant;
119
+ set birthtimeInstant(value: Temporal.Instant);
112
120
  /**
113
121
  * Size of the item in bytes.
114
122
  * For directories/symlinks, this is normally the size of the struct that represents the item.
@@ -193,6 +201,8 @@ export declare class StatsFs implements Node.StatsFsBase<number> {
193
201
  bavail: number;
194
202
  /** Total file nodes in file system. */
195
203
  files: number;
204
+ /** Fundamental file system block size. */
205
+ frsize: number;
196
206
  /** Free file nodes in file system. */
197
207
  ffree: number;
198
208
  }
@@ -212,6 +222,8 @@ export declare class BigIntStatsFs implements Node.StatsFsBase<bigint> {
212
222
  bavail: bigint;
213
223
  /** Total file nodes in file system. */
214
224
  files: bigint;
225
+ /** Fundamental file system block size. */
226
+ frsize: bigint;
215
227
  /** Free file nodes in file system. */
216
228
  ffree: bigint;
217
229
  }
@@ -1,7 +1,8 @@
1
1
  import { pick } from 'utilium';
2
- import { _inode_fields, hasAccess } from '../internal/inode.js';
3
2
  import * as c from '../constants.js';
4
- const n1000 = BigInt(1000);
3
+ import { _inode_fields, hasAccess } from '../internal/inode.js';
4
+ import { _temporal } from '../utils.js';
5
+ const _nsMultiplier = BigInt(1_000_000);
5
6
  /**
6
7
  * Provides information about a particular entry in the file system.
7
8
  * Common code used by both Stats and BigIntStats.
@@ -56,6 +57,12 @@ export class StatsCommon {
56
57
  set atime(value) {
57
58
  this.atimeMs = this._convert(value.getTime());
58
59
  }
60
+ get atimeInstant() {
61
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.atimeMs));
62
+ }
63
+ set atimeInstant(value) {
64
+ this.atimeMs = this._convert(value.epochMilliseconds);
65
+ }
59
66
  /**
60
67
  * Time of last modification, since epoch
61
68
  */
@@ -66,6 +73,12 @@ export class StatsCommon {
66
73
  set mtime(value) {
67
74
  this.mtimeMs = this._convert(value.getTime());
68
75
  }
76
+ get mtimeInstant() {
77
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.mtimeMs));
78
+ }
79
+ set mtimeInstant(value) {
80
+ this.mtimeMs = this._convert(value.epochMilliseconds);
81
+ }
69
82
  /**
70
83
  * Time of last time file status was changed, since epoch
71
84
  */
@@ -76,6 +89,12 @@ export class StatsCommon {
76
89
  set ctime(value) {
77
90
  this.ctimeMs = this._convert(value.getTime());
78
91
  }
92
+ get ctimeInstant() {
93
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.ctimeMs));
94
+ }
95
+ set ctimeInstant(value) {
96
+ this.ctimeMs = this._convert(value.epochMilliseconds);
97
+ }
79
98
  /**
80
99
  * Time of file creation, since epoch
81
100
  */
@@ -86,6 +105,12 @@ export class StatsCommon {
86
105
  set birthtime(value) {
87
106
  this.birthtimeMs = this._convert(value.getTime());
88
107
  }
108
+ get birthtimeInstant() {
109
+ return _temporal().Instant.fromEpochMilliseconds(Number(this.birthtimeMs));
110
+ }
111
+ set birthtimeInstant(value) {
112
+ this.birthtimeMs = this._convert(value.epochMilliseconds);
113
+ }
89
114
  /**
90
115
  * Size of the item in bytes.
91
116
  * For directories/symlinks, this is normally the size of the struct that represents the item.
@@ -156,16 +181,16 @@ export class StatsCommon {
156
181
  return hasAccess(context, this._isBigint ? new Stats(this) : this, mode);
157
182
  }
158
183
  get atimeNs() {
159
- return BigInt(this.atimeMs) * n1000;
184
+ return BigInt(this.atimeMs) * _nsMultiplier;
160
185
  }
161
186
  get mtimeNs() {
162
- return BigInt(this.mtimeMs) * n1000;
187
+ return BigInt(this.mtimeMs) * _nsMultiplier;
163
188
  }
164
189
  get ctimeNs() {
165
- return BigInt(this.ctimeMs) * n1000;
190
+ return BigInt(this.ctimeMs) * _nsMultiplier;
166
191
  }
167
192
  get birthtimeNs() {
168
- return BigInt(this.birthtimeMs) * n1000;
193
+ return BigInt(this.birthtimeMs) * _nsMultiplier;
169
194
  }
170
195
  }
171
196
  /**
@@ -218,6 +243,8 @@ export class StatsFs {
218
243
  bavail = 0;
219
244
  /** Total file nodes in file system. */
220
245
  files = c.size_max;
246
+ /** Fundamental file system block size. */
247
+ frsize = 4096;
221
248
  /** Free file nodes in file system. */
222
249
  ffree = c.size_max;
223
250
  }
@@ -237,6 +264,8 @@ export class BigIntStatsFs {
237
264
  bavail = BigInt(0);
238
265
  /** Total file nodes in file system. */
239
266
  files = BigInt(c.size_max);
267
+ /** Fundamental file system block size. */
268
+ frsize = BigInt(4096);
240
269
  /** Free file nodes in file system. */
241
270
  ffree = BigInt(c.size_max);
242
271
  }
@@ -10,23 +10,51 @@ export declare function renameSync(this: V_Context, oldPath: fs.PathLike, newPat
10
10
  * Test whether or not `path` exists by checking with the file system.
11
11
  */
12
12
  export declare function existsSync(this: V_Context, path: fs.PathLike): boolean;
13
- export declare function statSync(this: V_Context, path: fs.PathLike, options?: {
14
- bigint?: boolean;
13
+ export declare function statSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions & {
14
+ bigint?: false;
15
+ throwIfNoEntry?: true;
15
16
  }): Stats;
16
- export declare function statSync(this: V_Context, path: fs.PathLike, options: {
17
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
17
18
  bigint: true;
19
+ throwIfNoEntry?: true;
18
20
  }): BigIntStats;
21
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
22
+ bigint?: false;
23
+ throwIfNoEntry: false;
24
+ }): Stats | undefined;
25
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
26
+ bigint: true;
27
+ throwIfNoEntry: false;
28
+ }): BigIntStats | undefined;
29
+ export declare function statSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
30
+ throwIfNoEntry?: true;
31
+ }): Stats | BigIntStats;
32
+ export declare function statSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats | undefined;
19
33
  /**
20
34
  * Synchronous `lstat`.
21
35
  * `lstat()` is identical to `stat()`, except that if path is a symbolic link,
22
36
  * then the link itself is stat-ed, not the file that it refers to.
23
37
  */
24
- export declare function lstatSync(this: V_Context, path: fs.PathLike, options?: {
25
- bigint?: boolean;
38
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions & {
39
+ bigint?: false;
40
+ throwIfNoEntry?: true;
26
41
  }): Stats;
27
- export declare function lstatSync(this: V_Context, path: fs.PathLike, options: {
42
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
28
43
  bigint: true;
44
+ throwIfNoEntry?: true;
29
45
  }): BigIntStats;
46
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
47
+ bigint?: false;
48
+ throwIfNoEntry: false;
49
+ }): Stats | undefined;
50
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
51
+ bigint: true;
52
+ throwIfNoEntry: false;
53
+ }): BigIntStats | undefined;
54
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
55
+ throwIfNoEntry?: true;
56
+ }): Stats | BigIntStats;
57
+ export declare function lstatSync(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats | undefined;
30
58
  export declare function truncateSync(this: V_Context, path: fs.PathLike, len?: number | null): void;
31
59
  export declare function unlinkSync(this: V_Context, path: fs.PathLike): void;
32
60
  /**
@@ -94,7 +122,7 @@ export declare function fdatasyncSync(this: V_Context, fd: number): void;
94
122
  */
95
123
  export declare function writeSync(this: V_Context, fd: number, data: ArrayBufferView, offset?: number | null, length?: number | null, position?: number | null): number;
96
124
  export declare function writeSync(this: V_Context, fd: number, data: string, position?: number | null, encoding?: BufferEncoding | null): number;
97
- export declare function readSync(this: V_Context, fd: number, buffer: ArrayBufferView, options?: fs.ReadSyncOptions): number;
125
+ export declare function readSync(this: V_Context, fd: number, buffer: ArrayBufferView, options?: fs.ReadOptions): number;
98
126
  export declare function readSync(this: V_Context, fd: number, buffer: ArrayBufferView, offset: number, length: number, position?: fs.ReadPosition | null): number;
99
127
  export declare function fchownSync(this: V_Context, fd: number, uid: number, gid: number): void;
100
128
  export declare function fchmodSync(this: V_Context, fd: number, mode: number | string): void;
@@ -246,7 +274,7 @@ export declare function statfsSync(this: V_Context, path: fs.PathLike, options?:
246
274
  /**
247
275
  * Retrieves the files matching the specified pattern.
248
276
  */
249
- export declare function globSync(pattern: string | readonly string[]): string[];
250
- export declare function globSync(pattern: string | readonly string[], options: fs.GlobOptionsWithFileTypes): Dirent[];
251
- export declare function globSync(pattern: string | readonly string[], options: fs.GlobOptionsWithoutFileTypes): string[];
252
- export declare function globSync(pattern: string | readonly string[], options: fs.GlobOptions): Dirent[] | string[];
277
+ export declare function globSync(this: V_Context, pattern: string | readonly string[]): string[];
278
+ export declare function globSync(this: V_Context, pattern: string | readonly string[], options: fs.GlobOptionsWithFileTypes): Dirent[];
279
+ export declare function globSync(this: V_Context, pattern: string | readonly string[], options: fs.GlobOptionsWithoutFileTypes): string[];
280
+ export declare function globSync(this: V_Context, pattern: string | readonly string[], options: fs.GlobOptions): Dirent[] | string[];