@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.
- package/dist/backends/memory.d.ts +4 -4
- package/dist/backends/memory.js +4 -4
- package/dist/backends/overlay.d.ts +5 -2
- package/dist/backends/overlay.js +7 -10
- package/dist/backends/port/fs.js +1 -4
- package/dist/config.js +4 -8
- package/dist/context.d.ts +32 -0
- package/dist/context.js +23 -0
- package/dist/credentials.d.ts +5 -5
- package/dist/credentials.js +10 -6
- package/dist/emulation/async.d.ts +90 -89
- package/dist/emulation/async.js +76 -75
- package/dist/emulation/dir.d.ts +3 -1
- package/dist/emulation/dir.js +6 -7
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/promises.d.ts +50 -48
- package/dist/emulation/promises.js +78 -77
- package/dist/emulation/shared.d.ts +35 -8
- package/dist/emulation/shared.js +37 -11
- package/dist/emulation/sync.d.ts +63 -62
- package/dist/emulation/sync.js +72 -73
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/stats.d.ts +2 -1
- package/dist/stats.js +5 -4
- package/package.json +3 -5
- package/scripts/test.js +78 -17
- package/tests/assignment.ts +1 -1
- package/tests/common/context.test.ts +19 -0
- package/tests/{devices.test.ts → common/devices.test.ts} +3 -3
- package/tests/{handle.test.ts → common/handle.test.ts} +1 -1
- package/tests/common/mounts.test.ts +36 -0
- package/tests/{mutex.test.ts → common/mutex.test.ts} +3 -3
- package/tests/common/path.test.ts +34 -0
- package/tests/common.ts +4 -3
- package/tests/fs/dir.test.ts +11 -11
- package/tests/fs/directory.test.ts +17 -17
- package/tests/fs/errors.test.ts +29 -39
- package/tests/fs/watch.test.ts +2 -2
- package/tests/setup/context.ts +9 -0
- package/tests/setup/cow+fetch.ts +1 -1
- package/tests/setup/memory.ts +1 -1
- package/tests/{setup/common.ts → setup.ts} +6 -5
- package/src/backends/backend.ts +0 -161
- package/src/backends/fetch.ts +0 -180
- package/src/backends/file_index.ts +0 -206
- package/src/backends/memory.ts +0 -45
- package/src/backends/overlay.ts +0 -560
- package/src/backends/port/fs.ts +0 -329
- package/src/backends/port/readme.md +0 -54
- package/src/backends/port/rpc.ts +0 -167
- package/src/backends/readme.md +0 -3
- package/src/backends/store/fs.ts +0 -667
- package/src/backends/store/readme.md +0 -9
- package/src/backends/store/simple.ts +0 -154
- package/src/backends/store/store.ts +0 -189
- package/src/config.ts +0 -227
- package/src/credentials.ts +0 -49
- package/src/devices.ts +0 -521
- package/src/emulation/async.ts +0 -834
- package/src/emulation/cache.ts +0 -86
- package/src/emulation/config.ts +0 -21
- package/src/emulation/constants.ts +0 -182
- package/src/emulation/dir.ts +0 -138
- package/src/emulation/index.ts +0 -8
- package/src/emulation/path.ts +0 -440
- package/src/emulation/promises.ts +0 -1140
- package/src/emulation/shared.ts +0 -172
- package/src/emulation/streams.ts +0 -34
- package/src/emulation/sync.ts +0 -863
- package/src/emulation/watchers.ts +0 -194
- package/src/error.ts +0 -307
- package/src/file.ts +0 -631
- package/src/filesystem.ts +0 -174
- package/src/index.ts +0 -35
- package/src/inode.ts +0 -128
- package/src/mixins/async.ts +0 -230
- package/src/mixins/index.ts +0 -5
- package/src/mixins/mutexed.ts +0 -257
- package/src/mixins/readonly.ts +0 -96
- package/src/mixins/shared.ts +0 -25
- package/src/mixins/sync.ts +0 -58
- package/src/polyfills.ts +0 -21
- package/src/stats.ts +0 -405
- package/src/utils.ts +0 -276
- package/tests/mounts.test.ts +0 -18
- 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
|
-
}
|
package/tests/mounts.test.ts
DELETED
|
@@ -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
|
-
});
|