@zenfs/core 1.3.6 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/backends/memory.d.ts +4 -4
  2. package/dist/backends/memory.js +4 -4
  3. package/dist/backends/overlay.d.ts +5 -2
  4. package/dist/backends/overlay.js +7 -10
  5. package/dist/backends/port/fs.js +1 -4
  6. package/dist/config.js +4 -8
  7. package/dist/context.d.ts +32 -0
  8. package/dist/context.js +23 -0
  9. package/dist/credentials.d.ts +5 -5
  10. package/dist/credentials.js +10 -6
  11. package/dist/emulation/async.d.ts +90 -89
  12. package/dist/emulation/async.js +76 -75
  13. package/dist/emulation/dir.d.ts +3 -1
  14. package/dist/emulation/dir.js +6 -7
  15. package/dist/emulation/index.d.ts +1 -1
  16. package/dist/emulation/index.js +1 -1
  17. package/dist/emulation/promises.d.ts +50 -48
  18. package/dist/emulation/promises.js +78 -77
  19. package/dist/emulation/shared.d.ts +35 -8
  20. package/dist/emulation/shared.js +37 -11
  21. package/dist/emulation/sync.d.ts +63 -62
  22. package/dist/emulation/sync.js +72 -73
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/stats.d.ts +2 -1
  26. package/dist/stats.js +5 -4
  27. package/package.json +3 -5
  28. package/scripts/test.js +78 -17
  29. package/tests/assignment.ts +1 -1
  30. package/tests/common/context.test.ts +19 -0
  31. package/tests/{devices.test.ts → common/devices.test.ts} +3 -3
  32. package/tests/{handle.test.ts → common/handle.test.ts} +1 -1
  33. package/tests/common/mounts.test.ts +36 -0
  34. package/tests/{mutex.test.ts → common/mutex.test.ts} +3 -3
  35. package/tests/common/path.test.ts +34 -0
  36. package/tests/common.ts +4 -3
  37. package/tests/fs/dir.test.ts +11 -11
  38. package/tests/fs/directory.test.ts +17 -17
  39. package/tests/fs/errors.test.ts +29 -39
  40. package/tests/fs/watch.test.ts +2 -2
  41. package/tests/setup/context.ts +9 -0
  42. package/tests/setup/cow+fetch.ts +1 -1
  43. package/tests/setup/memory.ts +1 -1
  44. package/tests/{setup/common.ts → setup.ts} +6 -5
  45. package/src/backends/backend.ts +0 -161
  46. package/src/backends/fetch.ts +0 -180
  47. package/src/backends/file_index.ts +0 -206
  48. package/src/backends/memory.ts +0 -45
  49. package/src/backends/overlay.ts +0 -560
  50. package/src/backends/port/fs.ts +0 -329
  51. package/src/backends/port/readme.md +0 -54
  52. package/src/backends/port/rpc.ts +0 -167
  53. package/src/backends/readme.md +0 -3
  54. package/src/backends/store/fs.ts +0 -667
  55. package/src/backends/store/readme.md +0 -9
  56. package/src/backends/store/simple.ts +0 -154
  57. package/src/backends/store/store.ts +0 -189
  58. package/src/config.ts +0 -227
  59. package/src/credentials.ts +0 -49
  60. package/src/devices.ts +0 -521
  61. package/src/emulation/async.ts +0 -834
  62. package/src/emulation/cache.ts +0 -86
  63. package/src/emulation/config.ts +0 -21
  64. package/src/emulation/constants.ts +0 -182
  65. package/src/emulation/dir.ts +0 -138
  66. package/src/emulation/index.ts +0 -8
  67. package/src/emulation/path.ts +0 -440
  68. package/src/emulation/promises.ts +0 -1140
  69. package/src/emulation/shared.ts +0 -172
  70. package/src/emulation/streams.ts +0 -34
  71. package/src/emulation/sync.ts +0 -863
  72. package/src/emulation/watchers.ts +0 -194
  73. package/src/error.ts +0 -307
  74. package/src/file.ts +0 -631
  75. package/src/filesystem.ts +0 -174
  76. package/src/index.ts +0 -35
  77. package/src/inode.ts +0 -128
  78. package/src/mixins/async.ts +0 -230
  79. package/src/mixins/index.ts +0 -5
  80. package/src/mixins/mutexed.ts +0 -257
  81. package/src/mixins/readonly.ts +0 -96
  82. package/src/mixins/shared.ts +0 -25
  83. package/src/mixins/sync.ts +0 -58
  84. package/src/polyfills.ts +0 -21
  85. package/src/stats.ts +0 -405
  86. package/src/utils.ts +0 -276
  87. package/tests/mounts.test.ts +0 -18
  88. package/tests/path.test.ts +0 -34
package/src/stats.ts DELETED
@@ -1,405 +0,0 @@
1
- import type * as Node from 'node:fs';
2
- import { credentials } from './credentials.js';
3
- import {
4
- R_OK,
5
- S_IFBLK,
6
- S_IFCHR,
7
- S_IFDIR,
8
- S_IFIFO,
9
- S_IFLNK,
10
- S_IFMT,
11
- S_IFREG,
12
- S_IFSOCK,
13
- S_IRGRP,
14
- S_IROTH,
15
- S_IRUSR,
16
- S_IWGRP,
17
- S_IWOTH,
18
- S_IWUSR,
19
- S_IXGRP,
20
- S_IXOTH,
21
- S_IXUSR,
22
- size_max,
23
- W_OK,
24
- X_OK,
25
- } from './emulation/constants.js';
26
-
27
- /**
28
- * Indicates the type of a file. Applied to 'mode'.
29
- */
30
- export type FileType = typeof S_IFREG | typeof S_IFDIR | typeof S_IFLNK;
31
-
32
- export interface StatsLike<T extends number | bigint = number | bigint> {
33
- /**
34
- * Size of the item in bytes.
35
- * For directories/symlinks, this is normally the size of the struct that represents the item.
36
- */
37
- size: T;
38
- /**
39
- * Unix-style file mode (e.g. 0o644) that includes the item type
40
- */
41
- mode: T;
42
- /**
43
- * Time of last access, since epoch
44
- */
45
- atimeMs: T;
46
- /**
47
- * Time of last modification, since epoch
48
- */
49
- mtimeMs: T;
50
- /**
51
- * Time of last time file status was changed, since epoch
52
- */
53
- ctimeMs: T;
54
- /**
55
- * Time of file creation, since epoch
56
- */
57
- birthtimeMs: T;
58
- /**
59
- * The id of the user that owns the file
60
- */
61
- uid: T;
62
- /**
63
- * The id of the group that owns the file
64
- */
65
- gid: T;
66
- /**
67
- * Inode number
68
- */
69
- ino: T;
70
- /**
71
- * Number of hard links
72
- */
73
- nlink: T;
74
- }
75
-
76
- /**
77
- * Provides information about a particular entry in the file system.
78
- * Common code used by both Stats and BigIntStats.
79
- */
80
- export abstract class StatsCommon<T extends number | bigint> implements Node.StatsBase<T>, StatsLike {
81
- protected abstract _isBigint: T extends bigint ? true : false;
82
-
83
- protected _convert(arg: number | bigint | string | boolean): T {
84
- return (this._isBigint ? BigInt(arg) : Number(arg)) as T;
85
- }
86
-
87
- public get blocks(): T {
88
- return this._convert(Math.ceil(Number(this.size) / 512));
89
- }
90
-
91
- /**
92
- * Unix-style file mode (e.g. 0o644) that includes the type of the item.
93
- * Type of the item can be FILE, DIRECTORY, SYMLINK, or SOCKET
94
- */
95
- public mode: T;
96
-
97
- /**
98
- * ID of device containing file
99
- */
100
- public dev: T = this._convert(0);
101
-
102
- /**
103
- * Inode number
104
- */
105
- public ino: T = this._convert(0);
106
-
107
- /**
108
- * Device ID (if special file)
109
- */
110
- public rdev: T = this._convert(0);
111
-
112
- /**
113
- * Number of hard links
114
- */
115
- public nlink: T = this._convert(1);
116
-
117
- /**
118
- * Block size for file system I/O
119
- */
120
- public blksize: T = this._convert(4096);
121
-
122
- /**
123
- * User ID of owner
124
- */
125
- public uid: T = this._convert(0);
126
-
127
- /**
128
- * Group ID of owner
129
- */
130
- public gid: T = this._convert(0);
131
-
132
- /**
133
- * Some file systems stash data on stats objects.
134
- */
135
- public fileData?: Uint8Array;
136
-
137
- /**
138
- * Time of last access, since epoch
139
- */
140
- public atimeMs: T;
141
-
142
- public get atime(): Date {
143
- return new Date(Number(this.atimeMs));
144
- }
145
-
146
- public set atime(value: Date) {
147
- this.atimeMs = this._convert(value.getTime());
148
- }
149
-
150
- /**
151
- * Time of last modification, since epoch
152
- */
153
- public mtimeMs: T;
154
-
155
- public get mtime(): Date {
156
- return new Date(Number(this.mtimeMs));
157
- }
158
-
159
- public set mtime(value: Date) {
160
- this.mtimeMs = this._convert(value.getTime());
161
- }
162
-
163
- /**
164
- * Time of last time file status was changed, since epoch
165
- */
166
- public ctimeMs: T;
167
-
168
- public get ctime(): Date {
169
- return new Date(Number(this.ctimeMs));
170
- }
171
-
172
- public set ctime(value: Date) {
173
- this.ctimeMs = this._convert(value.getTime());
174
- }
175
-
176
- /**
177
- * Time of file creation, since epoch
178
- */
179
- public birthtimeMs: T;
180
-
181
- public get birthtime(): Date {
182
- return new Date(Number(this.birthtimeMs));
183
- }
184
-
185
- public set birthtime(value: Date) {
186
- this.birthtimeMs = this._convert(value.getTime());
187
- }
188
-
189
- /**
190
- * Size of the item in bytes.
191
- * For directories/symlinks, this is normally the size of the struct that represents the item.
192
- */
193
- public size: T;
194
-
195
- /**
196
- * Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
197
- */
198
- public constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino }: Partial<StatsLike> = {}) {
199
- const now = Date.now();
200
- this.atimeMs = this._convert(atimeMs ?? now);
201
- this.mtimeMs = this._convert(mtimeMs ?? now);
202
- this.ctimeMs = this._convert(ctimeMs ?? now);
203
- this.birthtimeMs = this._convert(birthtimeMs ?? now);
204
- this.uid = this._convert(uid ?? 0);
205
- this.gid = this._convert(gid ?? 0);
206
- this.size = this._convert(size ?? 0);
207
- this.ino = this._convert(ino ?? 0);
208
- this.mode = this._convert(mode ?? 0o644 & S_IFREG);
209
-
210
- if ((this.mode & S_IFMT) == 0) {
211
- this.mode = (this.mode | this._convert(S_IFREG)) as T;
212
- }
213
- }
214
-
215
- public isFile(): boolean {
216
- return (this.mode & S_IFMT) === S_IFREG;
217
- }
218
-
219
- public isDirectory(): boolean {
220
- return (this.mode & S_IFMT) === S_IFDIR;
221
- }
222
-
223
- public isSymbolicLink(): boolean {
224
- return (this.mode & S_IFMT) === S_IFLNK;
225
- }
226
-
227
- public isSocket(): boolean {
228
- return (this.mode & S_IFMT) === S_IFSOCK;
229
- }
230
-
231
- public isBlockDevice(): boolean {
232
- return (this.mode & S_IFMT) === S_IFBLK;
233
- }
234
-
235
- public isCharacterDevice(): boolean {
236
- return (this.mode & S_IFMT) === S_IFCHR;
237
- }
238
-
239
- public isFIFO(): boolean {
240
- return (this.mode & S_IFMT) === S_IFIFO;
241
- }
242
-
243
- /**
244
- * Checks if a given user/group has access to this item
245
- * @param mode The requested access, combination of W_OK, R_OK, and X_OK
246
- * @returns True if the request has access, false if the request does not
247
- * @internal
248
- */
249
- public hasAccess(mode: number): boolean {
250
- if (this.isSymbolicLink() || credentials.euid === 0 || credentials.egid === 0) return true;
251
-
252
- let perm = 0;
253
-
254
- // Owner permissions
255
- if (credentials.uid === this.uid) {
256
- if (this.mode & S_IRUSR) perm |= R_OK;
257
- if (this.mode & S_IWUSR) perm |= W_OK;
258
- if (this.mode & S_IXUSR) perm |= X_OK;
259
- }
260
-
261
- // Group permissions
262
- if (credentials.gid === this.gid || credentials.groups.includes(Number(this.gid))) {
263
- if (this.mode & S_IRGRP) perm |= R_OK;
264
- if (this.mode & S_IWGRP) perm |= W_OK;
265
- if (this.mode & S_IXGRP) perm |= X_OK;
266
- }
267
-
268
- // Others permissions
269
- if (this.mode & S_IROTH) perm |= R_OK;
270
- if (this.mode & S_IWOTH) perm |= W_OK;
271
- if (this.mode & S_IXOTH) perm |= X_OK;
272
-
273
- // Perform the access check
274
- return (perm & mode) === mode;
275
- }
276
-
277
- /**
278
- * Change the mode of the file.
279
- * We use this helper function to prevent messing up the type of the file.
280
- * @internal
281
- * @deprecated This will be removed in the next minor release since it is internal
282
- */
283
- public chmod(mode: number): void {
284
- this.mode = this._convert((this.mode & S_IFMT) | mode);
285
- }
286
-
287
- /**
288
- * Change the owner user/group of the file.
289
- * This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
290
- * @internal
291
- * @deprecated This will be removed in the next minor release since it is internal
292
- */
293
- public chown(uid: number, gid: number): void {
294
- uid = Number(uid);
295
- gid = Number(gid);
296
- if (!isNaN(uid) && 0 <= uid && uid < 2 ** 32) {
297
- this.uid = this._convert(uid);
298
- }
299
- if (!isNaN(gid) && 0 <= gid && gid < 2 ** 32) {
300
- this.gid = this._convert(gid);
301
- }
302
- }
303
-
304
- public get atimeNs(): bigint {
305
- return BigInt(this.atimeMs) * 1000n;
306
- }
307
-
308
- public get mtimeNs(): bigint {
309
- return BigInt(this.mtimeMs) * 1000n;
310
- }
311
-
312
- public get ctimeNs(): bigint {
313
- return BigInt(this.ctimeMs) * 1000n;
314
- }
315
-
316
- public get birthtimeNs(): bigint {
317
- return BigInt(this.birthtimeMs) * 1000n;
318
- }
319
- }
320
-
321
- /**
322
- * @hidden @internal
323
- */
324
- export function _chown(stats: Partial<StatsLike<number>>, uid: number, gid: number) {
325
- if (!isNaN(uid) && 0 <= uid && uid < 2 ** 32) {
326
- stats.uid = uid;
327
- }
328
- if (!isNaN(gid) && 0 <= gid && gid < 2 ** 32) {
329
- stats.gid = gid;
330
- }
331
- }
332
-
333
- /**
334
- * Implementation of Node's `Stats`.
335
- *
336
- * Attribute descriptions are from `man 2 stat'
337
- * @see http://nodejs.org/api/fs.html#fs_class_fs_stats
338
- * @see http://man7.org/linux/man-pages/man2/stat.2.html
339
- */
340
- export class Stats extends StatsCommon<number> implements Node.Stats, StatsLike {
341
- protected _isBigint = false as const;
342
- }
343
- Stats satisfies typeof Node.Stats;
344
-
345
- /**
346
- * Stats with bigint
347
- */
348
- export class BigIntStats extends StatsCommon<bigint> implements Node.BigIntStats, StatsLike {
349
- protected _isBigint = true as const;
350
- }
351
-
352
- /**
353
- * Determines if the file stats have changed by comparing relevant properties.
354
- *
355
- * @param left The previous stats.
356
- * @param right The current stats.
357
- * @returns `true` if stats have changed; otherwise, `false`.
358
- * @internal
359
- */
360
- export function isStatsEqual<T extends number | bigint>(left: StatsCommon<T>, right: StatsCommon<T>): boolean {
361
- return left.size == right.size && +left.atime == +right.atime && +left.mtime == +right.mtime && +left.ctime == +right.ctime && left.mode == right.mode;
362
- }
363
-
364
- /** @internal */
365
- export const ZenFsType = 0x7a656e6673; // 'z' 'e' 'n' 'f' 's'
366
-
367
- /**
368
- * @hidden
369
- */
370
- export class StatsFs implements Node.StatsFsBase<number> {
371
- /** Type of file system. */
372
- public type: number = 0x7a656e6673;
373
- /** Optimal transfer block size. */
374
- public bsize: number = 4096;
375
- /** Total data blocks in file system. */
376
- public blocks: number = 0;
377
- /** Free blocks in file system. */
378
- public bfree: number = 0;
379
- /** Available blocks for unprivileged users */
380
- public bavail: number = 0;
381
- /** Total file nodes in file system. */
382
- public files: number = size_max;
383
- /** Free file nodes in file system. */
384
- public ffree: number = size_max;
385
- }
386
-
387
- /**
388
- * @hidden
389
- */
390
- export class BigIntStatsFs implements Node.StatsFsBase<bigint> {
391
- /** Type of file system. */
392
- public type: bigint = 0x7a656e6673n;
393
- /** Optimal transfer block size. */
394
- public bsize: bigint = 4096n;
395
- /** Total data blocks in file system. */
396
- public blocks: bigint = 0n;
397
- /** Free blocks in file system. */
398
- public bfree: bigint = 0n;
399
- /** Available blocks for unprivileged users */
400
- public bavail: bigint = 0n;
401
- /** Total file nodes in file system. */
402
- public files: bigint = BigInt(size_max);
403
- /** Free file nodes in file system. */
404
- public ffree: bigint = BigInt(size_max);
405
- }
package/src/utils.ts DELETED
@@ -1,276 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return */
2
- import type * as fs from 'node:fs';
3
- import type { ClassLike, OptionalTuple } from 'utilium';
4
- import { dirname, resolve, type AbsolutePath } from './emulation/path.js';
5
- import { Errno, ErrnoError } from './error.js';
6
- import type { FileSystem } from './filesystem.js';
7
-
8
- declare global {
9
- function atob(data: string): string;
10
- function btoa(data: string): string;
11
- }
12
-
13
- /**
14
- * Synchronous recursive makedir.
15
- * @hidden
16
- */
17
- export function mkdirpSync(path: string, mode: number, fs: FileSystem): void {
18
- if (!fs.existsSync(path)) {
19
- mkdirpSync(dirname(path), mode, fs);
20
- fs.mkdirSync(path, mode);
21
- }
22
- }
23
-
24
- function _min(d0: number, d1: number, d2: number, bx: number, ay: number): number {
25
- return Math.min(d0 + 1, d1 + 1, d2 + 1, bx === ay ? d1 : d1 + 1);
26
- }
27
-
28
- /**
29
- * Calculates levenshtein distance.
30
- * @hidden
31
- */
32
- export function levenshtein(a: string, b: string): number {
33
- if (a === b) {
34
- return 0;
35
- }
36
-
37
- if (a.length > b.length) {
38
- [a, b] = [b, a]; // Swap a and b
39
- }
40
-
41
- let la = a.length;
42
- let lb = b.length;
43
-
44
- // Trim common suffix
45
- while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
46
- la--;
47
- lb--;
48
- }
49
-
50
- let offset = 0;
51
-
52
- // Trim common prefix
53
- while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
54
- offset++;
55
- }
56
-
57
- la -= offset;
58
- lb -= offset;
59
-
60
- if (la === 0 || lb === 1) {
61
- return lb;
62
- }
63
-
64
- const vector = new Array<number>(la << 1);
65
-
66
- for (let y = 0; y < la; ) {
67
- vector[la + y] = a.charCodeAt(offset + y);
68
- vector[y] = ++y;
69
- }
70
-
71
- let x: number;
72
- let d0: number;
73
- let d1: number;
74
- let d2: number;
75
- let d3: number;
76
- for (x = 0; x + 3 < lb; ) {
77
- const bx0 = b.charCodeAt(offset + (d0 = x));
78
- const bx1 = b.charCodeAt(offset + (d1 = x + 1));
79
- const bx2 = b.charCodeAt(offset + (d2 = x + 2));
80
- const bx3 = b.charCodeAt(offset + (d3 = x + 3));
81
- let dd = (x += 4);
82
- for (let y = 0; y < la; ) {
83
- const ay = vector[la + y];
84
- const dy = vector[y];
85
- d0 = _min(dy, d0, d1, bx0, ay);
86
- d1 = _min(d0, d1, d2, bx1, ay);
87
- d2 = _min(d1, d2, d3, bx2, ay);
88
- dd = _min(d2, d3, dd, bx3, ay);
89
- vector[y++] = dd;
90
- d3 = d2;
91
- d2 = d1;
92
- d1 = d0;
93
- d0 = dy;
94
- }
95
- }
96
-
97
- let dd: number = 0;
98
- for (; x < lb; ) {
99
- const bx0 = b.charCodeAt(offset + (d0 = x));
100
- dd = ++x;
101
- for (let y = 0; y < la; y++) {
102
- const dy = vector[y];
103
- vector[y] = dd = dy < d0 || dd < d0 ? (dy > dd ? dd + 1 : dy + 1) : bx0 === vector[la + y] ? d0 : d0 + 1;
104
- d0 = dy;
105
- }
106
- }
107
-
108
- return dd;
109
- }
110
-
111
- /**
112
- * Encodes a string into a buffer
113
- * @internal
114
- */
115
- export function encodeRaw(input: string): Uint8Array {
116
- if (typeof input != 'string') {
117
- throw new ErrnoError(Errno.EINVAL, 'Can not encode a non-string');
118
- }
119
- return new Uint8Array(Array.from(input).map(char => char.charCodeAt(0)));
120
- }
121
-
122
- /**
123
- * Decodes a string from a buffer
124
- * @internal
125
- */
126
- export function decodeRaw(input?: Uint8Array): string {
127
- if (!(input instanceof Uint8Array)) {
128
- throw new ErrnoError(Errno.EINVAL, 'Can not decode a non-Uint8Array');
129
- }
130
-
131
- return Array.from(input)
132
- .map(char => String.fromCharCode(char))
133
- .join('');
134
- }
135
-
136
- const encoder = new TextEncoder();
137
-
138
- /**
139
- * Encodes a string into a buffer
140
- * @internal
141
- */
142
- export function encodeUTF8(input: string): Uint8Array {
143
- if (typeof input != 'string') {
144
- throw new ErrnoError(Errno.EINVAL, 'Can not encode a non-string');
145
- }
146
- return encoder.encode(input);
147
- }
148
-
149
- export { /** @deprecated @hidden */ encodeUTF8 as encode };
150
-
151
- const decoder = new TextDecoder();
152
-
153
- /**
154
- * Decodes a string from a buffer
155
- * @internal
156
- */
157
- export function decodeUTF8(input?: Uint8Array): string {
158
- if (!(input instanceof Uint8Array)) {
159
- throw new ErrnoError(Errno.EINVAL, 'Can not decode a non-Uint8Array');
160
- }
161
-
162
- return decoder.decode(input);
163
- }
164
-
165
- export { /** @deprecated @hidden */ decodeUTF8 as decode };
166
-
167
- /**
168
- * Decodes a directory listing
169
- * @hidden
170
- */
171
- export function decodeDirListing(data: Uint8Array): Record<string, bigint> {
172
- return JSON.parse(decodeUTF8(data), (k, v) => (k == '' ? v : BigInt(v as string)));
173
- }
174
-
175
- /**
176
- * Encodes a directory listing
177
- * @hidden
178
- */
179
- export function encodeDirListing(data: Record<string, bigint>): Uint8Array {
180
- return encodeUTF8(JSON.stringify(data, (k, v) => (k == '' ? v : v.toString())));
181
- }
182
-
183
- export type Callback<Args extends unknown[] = []> = (e?: ErrnoError, ...args: OptionalTuple<Args>) => unknown;
184
-
185
- /**
186
- * Normalizes a mode
187
- * @internal
188
- */
189
- export function normalizeMode(mode: unknown, def?: number): number {
190
- if (typeof mode == 'number') {
191
- return mode;
192
- }
193
-
194
- if (typeof mode == 'string') {
195
- const parsed = parseInt(mode, 8);
196
- if (!isNaN(parsed)) {
197
- return parsed;
198
- }
199
- }
200
-
201
- if (typeof def == 'number') {
202
- return def;
203
- }
204
-
205
- throw new ErrnoError(Errno.EINVAL, 'Invalid mode: ' + mode?.toString());
206
- }
207
-
208
- /**
209
- * Normalizes a time
210
- * @internal
211
- */
212
- export function normalizeTime(time: string | number | Date): Date {
213
- if (time instanceof Date) {
214
- return time;
215
- }
216
-
217
- try {
218
- return new Date(time);
219
- } catch {
220
- throw new ErrnoError(Errno.EINVAL, 'Invalid time.');
221
- }
222
- }
223
-
224
- /**
225
- * Normalizes a path
226
- * @internal
227
- */
228
- export function normalizePath(p: fs.PathLike): AbsolutePath {
229
- p = p.toString();
230
- if (p.includes('\x00')) {
231
- throw new ErrnoError(Errno.EINVAL, 'Path can not contain null character');
232
- }
233
- if (p.length == 0) {
234
- throw new ErrnoError(Errno.EINVAL, 'Path can not be empty');
235
- }
236
- return resolve(p.replaceAll(/[/\\]+/g, '/'));
237
- }
238
-
239
- /**
240
- * Normalizes options
241
- * @param options options to normalize
242
- * @param encoding default encoding
243
- * @param flag default flag
244
- * @param mode default mode
245
- * @internal
246
- */
247
- export function normalizeOptions(
248
- options: fs.WriteFileOptions | (fs.EncodingOption & { flag?: fs.OpenMode }) | undefined,
249
- encoding: BufferEncoding | null = 'utf8',
250
- flag: string,
251
- mode: number = 0
252
- ): { encoding?: BufferEncoding | null; flag: string; mode: number } {
253
- if (typeof options != 'object' || options === null) {
254
- return {
255
- encoding: typeof options == 'string' ? options : encoding ?? null,
256
- flag,
257
- mode,
258
- };
259
- }
260
-
261
- return {
262
- encoding: typeof options?.encoding == 'string' ? options.encoding : encoding ?? null,
263
- flag: typeof options?.flag == 'string' ? options.flag : flag,
264
- mode: normalizeMode('mode' in options ? options?.mode : null, mode),
265
- };
266
- }
267
-
268
- export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: any[]) => InstanceType<T>);
269
-
270
- /**
271
- * Generate a random ino
272
- * @internal
273
- */
274
- export function randomBigInt(): bigint {
275
- return crypto.getRandomValues(new BigUint64Array(1))[0];
276
- }
@@ -1,18 +0,0 @@
1
- import assert from 'node:assert';
2
- import { suite, test } from 'node:test';
3
- import { configure } from '../src/config.js';
4
- import * as fs from '../src/emulation/index.js';
5
- import { InMemory } from '../src/index.js';
6
-
7
- suite('Mounts', () => {
8
- test('Mount in nested directory', async () => {
9
- await configure({
10
- mounts: {
11
- '/nested/dir': InMemory,
12
- },
13
- });
14
-
15
- assert.deepStrictEqual(fs.readdirSync('/'), ['nested']);
16
- assert.deepStrictEqual(fs.readdirSync('/nested'), ['dir']);
17
- });
18
- });