@zenfs/core 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/backends/backend.js +3 -4
  2. package/dist/backends/fetch.d.ts +17 -18
  3. package/dist/backends/fetch.js +95 -58
  4. package/dist/backends/index.d.ts +2 -1
  5. package/dist/backends/index.js +2 -1
  6. package/dist/backends/memory.d.ts +1 -1
  7. package/dist/backends/overlay.d.ts +8 -14
  8. package/dist/backends/overlay.js +38 -31
  9. package/dist/backends/passthrough.d.ts +8 -3
  10. package/dist/backends/passthrough.js +148 -4
  11. package/dist/backends/port/fs.d.ts +15 -49
  12. package/dist/backends/port/fs.js +28 -116
  13. package/dist/backends/port/rpc.d.ts +13 -6
  14. package/dist/backends/port/rpc.js +9 -7
  15. package/dist/backends/store/file_index.d.ts +38 -0
  16. package/dist/backends/store/file_index.js +76 -0
  17. package/dist/backends/store/fs.d.ts +39 -34
  18. package/dist/backends/store/fs.js +407 -238
  19. package/dist/backends/store/index_fs.d.ts +34 -0
  20. package/dist/backends/store/index_fs.js +67 -0
  21. package/dist/backends/store/inode.d.ts +26 -8
  22. package/dist/backends/store/inode.js +92 -91
  23. package/dist/backends/store/simple.d.ts +20 -20
  24. package/dist/backends/store/simple.js +3 -4
  25. package/dist/backends/store/store.d.ts +12 -12
  26. package/dist/backends/store/store.js +4 -6
  27. package/dist/devices.d.ts +44 -21
  28. package/dist/devices.js +110 -55
  29. package/dist/file.d.ts +111 -7
  30. package/dist/file.js +324 -92
  31. package/dist/filesystem.d.ts +44 -4
  32. package/dist/mixins/async.js +12 -6
  33. package/dist/mixins/mutexed.d.ts +8 -3
  34. package/dist/mixins/mutexed.js +57 -1
  35. package/dist/mixins/readonly.d.ts +17 -16
  36. package/dist/mixins/readonly.js +6 -0
  37. package/dist/mixins/sync.d.ts +1 -1
  38. package/dist/stats.d.ts +12 -6
  39. package/dist/stats.js +14 -6
  40. package/dist/utils.d.ts +23 -3
  41. package/dist/utils.js +58 -10
  42. package/dist/vfs/async.js +1 -1
  43. package/dist/vfs/constants.d.ts +2 -2
  44. package/dist/vfs/constants.js +2 -2
  45. package/dist/vfs/dir.js +3 -1
  46. package/dist/vfs/index.js +4 -1
  47. package/dist/vfs/promises.js +33 -13
  48. package/dist/vfs/shared.js +2 -0
  49. package/dist/vfs/sync.js +25 -13
  50. package/dist/vfs/types.d.ts +15 -0
  51. package/eslint.shared.js +1 -0
  52. package/package.json +2 -3
  53. package/readme.md +2 -2
  54. package/scripts/test.js +73 -11
  55. package/tests/common/mutex.test.ts +1 -1
  56. package/tests/fetch/run.sh +16 -0
  57. package/tests/fetch/server.ts +49 -0
  58. package/tests/fetch/setup.ts +13 -0
  59. package/tests/fs/read.test.ts +10 -10
  60. package/tests/fs/times.test.ts +2 -2
  61. package/tests/fs/write.test.ts +6 -11
  62. package/tests/setup/index.ts +38 -0
  63. package/tests/setup/port.ts +15 -0
  64. package/dist/backends/file_index.d.ts +0 -63
  65. package/dist/backends/file_index.js +0 -163
  66. package/tests/common/async.test.ts +0 -31
  67. package/tests/setup/cow+fetch.ts +0 -45
  68. /package/tests/fs/{appendFile.test.ts → append.test.ts} +0 -0
@@ -3,6 +3,7 @@ import type { CreationOptions, FileSystem, FileSystemMetadata } from '../filesys
3
3
  import type { Stats } from '../stats.js';
4
4
  import type { Concrete } from '../utils.js';
5
5
  import '../polyfills.js';
6
+ import type { InodeLike } from '../backends/index.js';
6
7
  export declare class MutexLock {
7
8
  protected readonly previous?: MutexLock | undefined;
8
9
  protected current: PromiseWithResolvers<void>;
@@ -68,15 +69,19 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
68
69
  existsSync(path: string): boolean;
69
70
  link(srcpath: string, dstpath: string): Promise<void>;
70
71
  linkSync(srcpath: string, dstpath: string): void;
71
- sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
72
- syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
72
+ sync(path: string, data?: Uint8Array, stats?: Readonly<InodeLike>): Promise<void>;
73
+ syncSync(path: string, data?: Uint8Array, stats?: Readonly<InodeLike>): void;
74
+ read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
75
+ readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
76
+ write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
77
+ writeSync(path: string, buffer: Uint8Array, offset: number): void;
73
78
  }
74
79
  /**
75
80
  * This serializes access to an underlying async filesystem.
76
81
  * For example, on an OverlayFS instance with an async lower
77
82
  * directory operations like rename and rmdir may involve multiple
78
83
  * requests involving both the upper and lower file systems -- they
79
- * are not executed in a single atomic step. OverlayFS uses this
84
+ * are not executed in a single atomic step. OverlayFS used to use this
80
85
  * to avoid having to reason about the correctness of
81
86
  * multiple requests interleaving.
82
87
  *
@@ -448,13 +448,69 @@ export class _MutexedFS {
448
448
  __disposeResources(env_22);
449
449
  }
450
450
  }
451
+ async read(path, buffer, offset, end) {
452
+ const env_23 = { stack: [], error: void 0, hasError: false };
453
+ try {
454
+ const _ = __addDisposableResource(env_23, await this.lock(path, 'read'), false);
455
+ return await this._fs.read(path, buffer, offset, end);
456
+ }
457
+ catch (e_23) {
458
+ env_23.error = e_23;
459
+ env_23.hasError = true;
460
+ }
461
+ finally {
462
+ __disposeResources(env_23);
463
+ }
464
+ }
465
+ readSync(path, buffer, offset, end) {
466
+ const env_24 = { stack: [], error: void 0, hasError: false };
467
+ try {
468
+ const _ = __addDisposableResource(env_24, this.lockSync(path, 'read'), false);
469
+ return this._fs.readSync(path, buffer, offset, end);
470
+ }
471
+ catch (e_24) {
472
+ env_24.error = e_24;
473
+ env_24.hasError = true;
474
+ }
475
+ finally {
476
+ __disposeResources(env_24);
477
+ }
478
+ }
479
+ async write(path, buffer, offset) {
480
+ const env_25 = { stack: [], error: void 0, hasError: false };
481
+ try {
482
+ const _ = __addDisposableResource(env_25, await this.lock(path, 'write'), false);
483
+ return await this._fs.write(path, buffer, offset);
484
+ }
485
+ catch (e_25) {
486
+ env_25.error = e_25;
487
+ env_25.hasError = true;
488
+ }
489
+ finally {
490
+ __disposeResources(env_25);
491
+ }
492
+ }
493
+ writeSync(path, buffer, offset) {
494
+ const env_26 = { stack: [], error: void 0, hasError: false };
495
+ try {
496
+ const _ = __addDisposableResource(env_26, this.lockSync(path, 'write'), false);
497
+ return this._fs.writeSync(path, buffer, offset);
498
+ }
499
+ catch (e_26) {
500
+ env_26.error = e_26;
501
+ env_26.hasError = true;
502
+ }
503
+ finally {
504
+ __disposeResources(env_26);
505
+ }
506
+ }
451
507
  }
452
508
  /**
453
509
  * This serializes access to an underlying async filesystem.
454
510
  * For example, on an OverlayFS instance with an async lower
455
511
  * directory operations like rename and rmdir may involve multiple
456
512
  * requests involving both the upper and lower file systems -- they
457
- * are not executed in a single atomic step. OverlayFS uses this
513
+ * are not executed in a single atomic step. OverlayFS used to use this
458
514
  * to avoid having to reason about the correctness of
459
515
  * multiple requests interleaving.
460
516
  *
@@ -1,26 +1,27 @@
1
- import type { File } from '../file.js';
2
1
  import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
3
- import type { Stats } from '../stats.js';
2
+ import type { StatsLike } from '../stats.js';
4
3
  import type { Mixin } from './shared.js';
5
4
  /**
6
5
  * @internal
7
6
  */
8
7
  export interface ReadonlyMixin {
9
8
  metadata(): FileSystemMetadata;
10
- rename(oldPath: string, newPath: string): Promise<void>;
11
- renameSync(oldPath: string, newPath: string): void;
12
- createFile(path: string, flag: string, mode: number): Promise<File>;
13
- createFileSync(path: string, flag: string, mode: number): File;
14
- unlink(path: string): Promise<void>;
15
- unlinkSync(path: string): void;
16
- rmdir(path: string): Promise<void>;
17
- rmdirSync(path: string): void;
18
- mkdir(path: string, mode: number): Promise<void>;
19
- mkdirSync(path: string, mode: number): void;
20
- link(srcpath: string, dstpath: string): Promise<void>;
21
- linkSync(srcpath: string, dstpath: string): void;
22
- sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
23
- syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
9
+ rename(oldPath: string, newPath: string): Promise<never>;
10
+ renameSync(oldPath: string, newPath: string): never;
11
+ createFile(path: string, flag: string, mode: number): Promise<never>;
12
+ createFileSync(path: string, flag: string, mode: number): never;
13
+ unlink(path: string): Promise<never>;
14
+ unlinkSync(path: string): never;
15
+ rmdir(path: string): Promise<never>;
16
+ rmdirSync(path: string): never;
17
+ mkdir(path: string, mode: number): Promise<never>;
18
+ mkdirSync(path: string, mode: number): never;
19
+ link(srcpath: string, dstpath: string): Promise<never>;
20
+ linkSync(srcpath: string, dstpath: string): never;
21
+ sync(path: string, data: Uint8Array, stats: Readonly<StatsLike<number>>): Promise<never>;
22
+ syncSync(path: string, data: Uint8Array, stats: Readonly<StatsLike<number>>): never;
23
+ write(path: string, buffer: Uint8Array, offset: number): Promise<never>;
24
+ writeSync(path: string, buffer: Uint8Array, offset: number): Promise<never>;
24
25
  }
25
26
  /**
26
27
  * Implements the non-readonly methods to throw `EROFS`
@@ -50,6 +50,12 @@ export function Readonly(FS) {
50
50
  syncSync() {
51
51
  throw new ErrnoError(Errno.EROFS);
52
52
  }
53
+ async write() {
54
+ throw new ErrnoError(Errno.EROFS);
55
+ }
56
+ writeSync() {
57
+ throw new ErrnoError(Errno.EROFS);
58
+ }
53
59
  }
54
60
  return ReadonlyFS;
55
61
  }
@@ -1,5 +1,5 @@
1
1
  import type { FileSystem } from '../filesystem.js';
2
- import type { Mixin, AsyncFSMethods } from './shared.js';
2
+ import type { AsyncFSMethods, Mixin } from './shared.js';
3
3
  /**
4
4
  * Implements the asynchronous API in terms of the synchronous API.
5
5
  */
package/dist/stats.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type * as Node from 'node:fs';
2
2
  import type { V_Context } from './context.js';
3
3
  import * as c from './vfs/constants.js';
4
+ import type { InodeFields, InodeLike } from './backends/store/inode.js';
4
5
  /**
5
6
  * Indicates the type of a file. Applied to 'mode'.
6
7
  */
@@ -91,8 +92,10 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
91
92
  gid: T;
92
93
  /**
93
94
  * Some file systems stash data on stats objects.
95
+ * @todo [BREAKING] Remove this
96
+ * @deprecated @hidden
94
97
  */
95
- fileData?: Uint8Array;
98
+ fileData?: unknown;
96
99
  /**
97
100
  * Time of last access, since epoch
98
101
  */
@@ -122,10 +125,12 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
122
125
  * For directories/symlinks, this is normally the size of the struct that represents the item.
123
126
  */
124
127
  size: T;
128
+ data?: number;
129
+ flags?: number;
125
130
  /**
126
131
  * Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
127
132
  */
128
- constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino }?: Partial<StatsLike>);
133
+ constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino, ...rest }?: Partial<InodeLike>);
129
134
  isFile(): boolean;
130
135
  isDirectory(): boolean;
131
136
  isSymbolicLink(): boolean;
@@ -133,6 +138,7 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
133
138
  isBlockDevice(): boolean;
134
139
  isCharacterDevice(): boolean;
135
140
  isFIFO(): boolean;
141
+ toJSON(): StatsLike<T> & InodeFields;
136
142
  /**
137
143
  * Checks if a given user/group has access to this item
138
144
  * @param mode The requested access, combination of W_OK, R_OK, and X_OK
@@ -143,15 +149,15 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
143
149
  /**
144
150
  * Change the mode of the file.
145
151
  * We use this helper function to prevent messing up the type of the file.
146
- * @internal
147
- * @deprecated This will be removed in the next minor release since it is internal
152
+ * @internal @deprecated
153
+ * @todo [BREAKING] Remove
148
154
  */
149
155
  chmod(mode: number): void;
150
156
  /**
151
157
  * Change the owner user/group of the file.
152
158
  * This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
153
- * @internal
154
- * @deprecated This will be removed in the next minor release since it is internal
159
+ * @internal @deprecated
160
+ * @todo [BREAKING] Remove
155
161
  */
156
162
  chown(uid: number, gid: number): void;
157
163
  get atimeNs(): bigint;
package/dist/stats.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { credentials } from './credentials.js';
2
2
  import * as c from './vfs/constants.js';
3
+ import { pick } from 'utilium';
4
+ import { _inode_fields } from './backends/store/inode.js';
3
5
  const n1000 = BigInt(1000);
4
6
  /**
5
7
  * Provides information about a particular entry in the file system.
@@ -39,7 +41,7 @@ export class StatsCommon {
39
41
  /**
40
42
  * Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
41
43
  */
42
- constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino } = {}) {
44
+ constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino, ...rest } = {}) {
43
45
  /**
44
46
  * ID of device containing file
45
47
  */
@@ -81,6 +83,7 @@ export class StatsCommon {
81
83
  if ((this.mode & c.S_IFMT) == 0) {
82
84
  this.mode = (this.mode | this._convert(c.S_IFREG));
83
85
  }
86
+ Object.assign(this, rest);
84
87
  }
85
88
  isFile() {
86
89
  return (this.mode & c.S_IFMT) === c.S_IFREG;
@@ -103,6 +106,9 @@ export class StatsCommon {
103
106
  isFIFO() {
104
107
  return (this.mode & c.S_IFMT) === c.S_IFIFO;
105
108
  }
109
+ toJSON() {
110
+ return pick(this, _inode_fields);
111
+ }
106
112
  /**
107
113
  * Checks if a given user/group has access to this item
108
114
  * @param mode The requested access, combination of W_OK, R_OK, and X_OK
@@ -142,11 +148,12 @@ export class StatsCommon {
142
148
  // Perform the access check
143
149
  return (perm & mode) === mode;
144
150
  }
151
+ /* node:coverage disable */
145
152
  /**
146
153
  * Change the mode of the file.
147
154
  * We use this helper function to prevent messing up the type of the file.
148
- * @internal
149
- * @deprecated This will be removed in the next minor release since it is internal
155
+ * @internal @deprecated
156
+ * @todo [BREAKING] Remove
150
157
  */
151
158
  chmod(mode) {
152
159
  this.mode = this._convert((this.mode & c.S_IFMT) | mode);
@@ -154,8 +161,8 @@ export class StatsCommon {
154
161
  /**
155
162
  * Change the owner user/group of the file.
156
163
  * This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
157
- * @internal
158
- * @deprecated This will be removed in the next minor release since it is internal
164
+ * @internal @deprecated
165
+ * @todo [BREAKING] Remove
159
166
  */
160
167
  chown(uid, gid) {
161
168
  uid = Number(uid);
@@ -167,6 +174,7 @@ export class StatsCommon {
167
174
  this.gid = this._convert(gid);
168
175
  }
169
176
  }
177
+ /* node:coverage enable */
170
178
  get atimeNs() {
171
179
  return BigInt(this.atimeMs) * n1000;
172
180
  }
@@ -184,7 +192,7 @@ export class StatsCommon {
184
192
  * @hidden @internal
185
193
  */
186
194
  export function _chown(stats, uid, gid) {
187
- if (!isNaN(uid) && 0 <= uid && uid < 2 ** 32) {
195
+ if (!isNaN(uid) && 0 <= uid && uid < c.size_max) {
188
196
  stats.uid = uid;
189
197
  }
190
198
  if (!isNaN(gid) && 0 <= gid && gid < 2 ** 32) {
package/dist/utils.d.ts CHANGED
@@ -32,15 +32,16 @@ export { /** @deprecated @hidden */ decodeUTF8 as decode };
32
32
  * Decodes a directory listing
33
33
  * @hidden
34
34
  */
35
- export declare function decodeDirListing(data: Uint8Array): Record<string, bigint>;
35
+ export declare function decodeDirListing(data: Uint8Array): Record<string, number>;
36
36
  /**
37
37
  * Encodes a directory listing
38
38
  * @hidden
39
39
  */
40
- export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
40
+ export declare function encodeDirListing(data: Record<string, number>): Uint8Array;
41
41
  export type Callback<Args extends unknown[] = [], NoError = undefined | void> = (e: ErrnoError | NoError, ...args: OptionalTuple<Args>) => unknown;
42
42
  /**
43
43
  * Normalizes a mode
44
+ * @param def default
44
45
  * @internal
45
46
  */
46
47
  export declare function normalizeMode(mode: unknown, def?: number): number;
@@ -48,7 +49,7 @@ export declare function normalizeMode(mode: unknown, def?: number): number;
48
49
  * Normalizes a time
49
50
  * @internal
50
51
  */
51
- export declare function normalizeTime(time: string | number | Date): Date;
52
+ export declare function normalizeTime(time: string | number | Date): number;
52
53
  /**
53
54
  * Normalizes a path
54
55
  * @internal
@@ -75,3 +76,22 @@ export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: an
75
76
  * @internal
76
77
  */
77
78
  export declare function randomBigInt(): bigint;
79
+ /**
80
+ * Prevents infinite loops
81
+ * @internal
82
+ */
83
+ export declare function canary(path?: string, syscall?: string): () => void;
84
+ /**
85
+ * A wrapper for throwing things.
86
+ * Used in expressions.
87
+ * @todo Remove once `throw` is allowed in expressions
88
+ * @see https://github.com/tc39/proposal-throw-expressions
89
+ * @internal @hidden
90
+ */
91
+ export declare function _throw(e: unknown): never;
92
+ /**
93
+ * Grows a buffer if it isn't large enough
94
+ * @returns The original buffer if resized successfully, or a newly created buffer
95
+ * @internal Not for external use!
96
+ */
97
+ export declare function growBuffer<T extends ArrayBufferLike | ArrayBufferView>(buffer: T, newByteLength: number): T;
package/dist/utils.js CHANGED
@@ -34,7 +34,9 @@ export function encodeUTF8(input) {
34
34
  }
35
35
  return encoder.encode(input);
36
36
  }
37
+ /* node:coverage disable */
37
38
  export { /** @deprecated @hidden */ encodeUTF8 as encode };
39
+ /* node:coverage enable */
38
40
  const decoder = new TextDecoder();
39
41
  /**
40
42
  * Decodes a string from a buffer
@@ -46,38 +48,39 @@ export function decodeUTF8(input) {
46
48
  }
47
49
  return decoder.decode(input);
48
50
  }
51
+ /* node:coverage disable */
49
52
  export { /** @deprecated @hidden */ decodeUTF8 as decode };
53
+ /* node:coverage enable */
50
54
  /**
51
55
  * Decodes a directory listing
52
56
  * @hidden
53
57
  */
54
58
  export function decodeDirListing(data) {
55
- return JSON.parse(decodeUTF8(data), (k, v) => (k == '' ? v : BigInt(v)));
59
+ return JSON.parse(decodeUTF8(data), (k, v) => (k == '' ? v : typeof v == 'string' ? BigInt(v).toString(16).slice(0, Math.min(v.length, 8)) : v));
56
60
  }
57
61
  /**
58
62
  * Encodes a directory listing
59
63
  * @hidden
60
64
  */
61
65
  export function encodeDirListing(data) {
62
- return encodeUTF8(JSON.stringify(data, (k, v) => (k == '' ? v : v.toString())));
66
+ return encodeUTF8(JSON.stringify(data));
63
67
  }
64
68
  /**
65
69
  * Normalizes a mode
70
+ * @param def default
66
71
  * @internal
67
72
  */
68
73
  export function normalizeMode(mode, def) {
69
- if (typeof mode == 'number') {
74
+ if (typeof mode == 'number')
70
75
  return mode;
71
- }
72
76
  if (typeof mode == 'string') {
73
77
  const parsed = parseInt(mode, 8);
74
78
  if (!isNaN(parsed)) {
75
79
  return parsed;
76
80
  }
77
81
  }
78
- if (typeof def == 'number') {
82
+ if (typeof def == 'number')
79
83
  return def;
80
- }
81
84
  throw new ErrnoError(Errno.EINVAL, 'Invalid mode: ' + (mode === null || mode === void 0 ? void 0 : mode.toString()));
82
85
  }
83
86
  /**
@@ -85,11 +88,10 @@ export function normalizeMode(mode, def) {
85
88
  * @internal
86
89
  */
87
90
  export function normalizeTime(time) {
88
- if (time instanceof Date) {
89
- return time;
90
- }
91
+ if (time instanceof Date)
92
+ return time.getTime();
91
93
  try {
92
- return new Date(time);
94
+ return Number(time);
93
95
  }
94
96
  catch {
95
97
  throw new ErrnoError(Errno.EINVAL, 'Invalid time.');
@@ -138,3 +140,49 @@ export function normalizeOptions(options, encoding = 'utf8', flag, mode = 0) {
138
140
  export function randomBigInt() {
139
141
  return BigInt('0x' + randomHex(8));
140
142
  }
143
+ /**
144
+ * Prevents infinite loops
145
+ * @internal
146
+ */
147
+ export function canary(path, syscall) {
148
+ const timeout = setTimeout(() => {
149
+ throw ErrnoError.With('EDEADLK', path, syscall);
150
+ }, 5000);
151
+ return () => clearTimeout(timeout);
152
+ }
153
+ /**
154
+ * A wrapper for throwing things.
155
+ * Used in expressions.
156
+ * @todo Remove once `throw` is allowed in expressions
157
+ * @see https://github.com/tc39/proposal-throw-expressions
158
+ * @internal @hidden
159
+ */
160
+ export function _throw(e) {
161
+ throw e;
162
+ }
163
+ /**
164
+ * Grows a buffer if it isn't large enough
165
+ * @returns The original buffer if resized successfully, or a newly created buffer
166
+ * @internal Not for external use!
167
+ */
168
+ export function growBuffer(buffer, newByteLength) {
169
+ if (buffer.byteLength >= newByteLength)
170
+ return buffer;
171
+ if (ArrayBuffer.isView(buffer)) {
172
+ const newBuffer = growBuffer(buffer.buffer, newByteLength);
173
+ return new buffer.constructor(newBuffer, buffer.byteOffset, newByteLength);
174
+ }
175
+ const isShared = buffer instanceof SharedArrayBuffer;
176
+ // Note: If true, the buffer must be resizable/growable because of the first check.
177
+ if (buffer.maxByteLength > newByteLength) {
178
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
179
+ isShared ? buffer.grow(newByteLength) : buffer.resize(newByteLength);
180
+ return buffer;
181
+ }
182
+ if (!isShared) {
183
+ return buffer.transfer(newByteLength);
184
+ }
185
+ const newBuffer = new SharedArrayBuffer(newByteLength);
186
+ new Uint8Array(newBuffer).set(new Uint8Array(buffer));
187
+ return newBuffer;
188
+ }
package/dist/vfs/async.js CHANGED
@@ -431,7 +431,7 @@ export function createReadStream(path, options) {
431
431
  try {
432
432
  handle || (handle = await promises.open.call(context, path, 'r', options === null || options === void 0 ? void 0 : options.mode));
433
433
  const result = await handle.read(new Uint8Array(size), 0, size, handle.file.position);
434
- stream.push(!result.bytesRead ? null : result.buffer.slice(0, result.bytesRead));
434
+ stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead));
435
435
  handle.file.position += result.bytesRead;
436
436
  if (!result.bytesRead) {
437
437
  await handle.close();
@@ -111,7 +111,7 @@ export declare const S_IXOTH = 1;
111
111
  */
112
112
  export declare const UV_FS_O_FILEMAP = 0;
113
113
  /**
114
- * Max 32-bit integer
114
+ * Max 32-bit unsigned integer
115
115
  * @hidden
116
116
  */
117
- export declare const size_max: number;
117
+ export declare const size_max = 4294967295;
@@ -123,7 +123,7 @@ export const S_IXOTH = 1;
123
123
  */
124
124
  export const UV_FS_O_FILEMAP = 0;
125
125
  /**
126
- * Max 32-bit integer
126
+ * Max 32-bit unsigned integer
127
127
  * @hidden
128
128
  */
129
- export const size_max = 2 ** 32 - 1;
129
+ export const size_max = 0xffffffff; // 2^32 - 1
package/dist/vfs/dir.js CHANGED
@@ -66,7 +66,9 @@ export class Dir {
66
66
  async _read() {
67
67
  var _a, _b;
68
68
  this.checkClosed();
69
- (_a = this._entries) !== null && _a !== void 0 ? _a : (this._entries = await readdir.call(this.context, this.path, { withFileTypes: true }));
69
+ (_a = this._entries) !== null && _a !== void 0 ? _a : (this._entries = await readdir.call(this.context, this.path, {
70
+ withFileTypes: true,
71
+ }));
70
72
  if (!this._entries.length)
71
73
  return null;
72
74
  return (_b = this._entries.shift()) !== null && _b !== void 0 ? _b : null;
package/dist/vfs/index.js CHANGED
@@ -4,6 +4,7 @@ export * as constants from './constants.js';
4
4
  export * from './dir.js';
5
5
  export * as promises from './promises.js';
6
6
  export { chroot, mount, mountObject,
7
+ /* node:coverage disable */
7
8
  /**
8
9
  * The map of mount points.
9
10
  * Using `fs.mounts` instead of the `mounts` export is a security issue and not recommended.
@@ -11,6 +12,8 @@ export { chroot, mount, mountObject,
11
12
  * @todo [BREAKING] remove `fs.mounts` for security.
12
13
  * @deprecated Use the `mounts` export that isn't an `fs` property!
13
14
  */
14
- mounts, umount, } from './shared.js';
15
+ mounts,
16
+ /* node:coverage enable */
17
+ umount, } from './shared.js';
15
18
  export * from './streams.js';
16
19
  export * from './sync.js';
@@ -197,7 +197,7 @@ export class FileHandle {
197
197
  controller.close();
198
198
  return;
199
199
  }
200
- controller.enqueue(result.buffer.slice(0, result.bytesRead));
200
+ controller.enqueue(result.buffer.subarray(0, result.bytesRead));
201
201
  position += result.bytesRead;
202
202
  if (++i >= maxChunks) {
203
203
  throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
@@ -213,7 +213,10 @@ export class FileHandle {
213
213
  if (!('ReadableStream' in _gt)) {
214
214
  throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
215
215
  }
216
- return new _gt.ReadableStream({ start, type: options.type });
216
+ return new _gt.ReadableStream({
217
+ start,
218
+ type: options.type,
219
+ });
217
220
  }
218
221
  /**
219
222
  * @todo Implement
@@ -330,7 +333,7 @@ export class FileHandle {
330
333
  read: async (size) => {
331
334
  try {
332
335
  const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
333
- stream.push(!result.bytesRead ? null : result.buffer.slice(0, result.bytesRead)); // Push data or null for EOF
336
+ stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead)); // Push data or null for EOF
334
337
  this.file.position += result.bytesRead;
335
338
  }
336
339
  catch (error) {
@@ -478,11 +481,11 @@ async function applySetId(file, uid, gid) {
478
481
  * Opens a file. This helper handles the complexity of file flags.
479
482
  * @internal
480
483
  */
481
- async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
484
+ async function _open(path, opt) {
482
485
  var _a;
483
486
  path = normalizePath(path);
484
- const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
485
- path = resolveSymlinks ? await realpath.call(this, path) : path;
487
+ const mode = normalizeMode(opt.mode, 0o644), flag = parseFlag(opt.flag);
488
+ path = opt.preserveSymlinks ? path : await realpath.call(this, path);
486
489
  const { fs, path: resolved } = resolveMount(path, this);
487
490
  const stats = await fs.stat(resolved).catch(() => null);
488
491
  if (!stats) {
@@ -527,7 +530,7 @@ async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
527
530
  * @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
528
531
  */
529
532
  export async function open(path, flag = 'r', mode = 0o644) {
530
- return await _open.call(this, path, flag, mode, true);
533
+ return await _open.call(this, path, { flag, mode });
531
534
  }
532
535
  open;
533
536
  export async function readFile(path, _options) {
@@ -771,7 +774,7 @@ export async function symlink(target, path, type = 'file') {
771
774
  if (await exists.call(this, path)) {
772
775
  throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
773
776
  }
774
- const handle = __addDisposableResource(env_5, await _open.call(this, path, 'w+', 0o644, false), true);
777
+ const handle = __addDisposableResource(env_5, await _open.call(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
775
778
  await handle.writeFile(target.toString());
776
779
  await handle.file.chmod(constants.S_IFLNK);
777
780
  }
@@ -789,7 +792,7 @@ symlink;
789
792
  export async function readlink(path, options) {
790
793
  const env_6 = { stack: [], error: void 0, hasError: false };
791
794
  try {
792
- const handle = __addDisposableResource(env_6, await _open.call(this, normalizePath(path), 'r', 0o644, false), true);
795
+ const handle = __addDisposableResource(env_6, await _open.call(this, normalizePath(path), { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
793
796
  const value = await handle.readFile();
794
797
  const encoding = typeof options == 'object' ? options === null || options === void 0 ? void 0 : options.encoding : options;
795
798
  // always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
@@ -826,7 +829,12 @@ chown;
826
829
  export async function lchown(path, uid, gid) {
827
830
  const env_8 = { stack: [], error: void 0, hasError: false };
828
831
  try {
829
- const handle = __addDisposableResource(env_8, await _open.call(this, path, 'r+', 0o644, false), true);
832
+ const handle = __addDisposableResource(env_8, await _open.call(this, path, {
833
+ flag: 'r+',
834
+ mode: 0o644,
835
+ preserveSymlinks: true,
836
+ allowDirectory: true,
837
+ }), true);
830
838
  await handle.chown(uid, gid);
831
839
  }
832
840
  catch (e_8) {
@@ -860,7 +868,12 @@ chmod;
860
868
  export async function lchmod(path, mode) {
861
869
  const env_10 = { stack: [], error: void 0, hasError: false };
862
870
  try {
863
- const handle = __addDisposableResource(env_10, await _open.call(this, path, 'r+', 0o644, false), true);
871
+ const handle = __addDisposableResource(env_10, await _open.call(this, path, {
872
+ flag: 'r+',
873
+ mode: 0o644,
874
+ preserveSymlinks: true,
875
+ allowDirectory: true,
876
+ }), true);
864
877
  await handle.chmod(mode);
865
878
  }
866
879
  catch (e_10) {
@@ -900,7 +913,12 @@ utimes;
900
913
  export async function lutimes(path, atime, mtime) {
901
914
  const env_12 = { stack: [], error: void 0, hasError: false };
902
915
  try {
903
- const handle = __addDisposableResource(env_12, await _open.call(this, path, 'r+', 0o644, false), true);
916
+ const handle = __addDisposableResource(env_12, await _open.call(this, path, {
917
+ flag: 'r+',
918
+ mode: 0o644,
919
+ preserveSymlinks: true,
920
+ allowDirectory: true,
921
+ }), true);
904
922
  await handle.utimes(new Date(atime), new Date(mtime));
905
923
  }
906
924
  catch (e_12) {
@@ -1006,7 +1024,9 @@ export async function rm(path, options) {
1006
1024
  switch (stats.mode & constants.S_IFMT) {
1007
1025
  case constants.S_IFDIR:
1008
1026
  if (options === null || options === void 0 ? void 0 : options.recursive) {
1009
- for (const entry of await readdir.call(this, path, { _isIndirect: true })) {
1027
+ for (const entry of await readdir.call(this, path, {
1028
+ _isIndirect: true,
1029
+ })) {
1010
1030
  await rm.call(this, join(path, entry), { ...options, _isIndirect: true });
1011
1031
  }
1012
1032
  }