@zenfs/core 1.2.9 → 1.3.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/fetch.js +1 -1
- package/dist/backends/memory.d.ts +1 -2
- package/dist/backends/overlay.js +2 -3
- package/dist/backends/port/fs.d.ts +2 -15
- package/dist/backends/store/fs.d.ts +17 -28
- package/dist/backends/store/fs.js +169 -191
- package/dist/backends/store/simple.d.ts +20 -21
- package/dist/backends/store/simple.js +24 -24
- package/dist/backends/store/store.d.ts +22 -23
- package/dist/backends/store/store.js +6 -6
- package/dist/config.d.ts +8 -0
- package/dist/config.js +2 -1
- package/dist/devices.d.ts +2 -3
- package/dist/emulation/cache.d.ts +40 -31
- package/dist/emulation/cache.js +62 -53
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/promises.js +18 -17
- package/dist/emulation/shared.d.ts +6 -0
- package/dist/emulation/shared.js +13 -1
- package/dist/emulation/sync.d.ts +1 -1
- package/dist/emulation/sync.js +16 -15
- package/dist/file.d.ts +3 -15
- package/dist/file.js +7 -19
- package/dist/inode.d.ts +4 -13
- package/dist/inode.js +22 -29
- package/dist/mixins/async.d.ts +15 -10
- package/dist/mixins/async.js +3 -1
- package/dist/stats.js +30 -5
- package/dist/utils.d.ts +5 -7
- package/dist/utils.js +11 -20
- package/package.json +1 -1
- package/src/backends/fetch.ts +1 -1
- package/src/backends/memory.ts +1 -2
- package/src/backends/overlay.ts +2 -2
- package/src/backends/store/fs.ts +187 -220
- package/src/backends/store/simple.ts +36 -37
- package/src/backends/store/store.ts +25 -26
- package/src/config.ts +11 -1
- package/src/devices.ts +2 -3
- package/src/emulation/cache.ts +68 -60
- package/src/emulation/index.ts +1 -1
- package/src/emulation/promises.ts +20 -19
- package/src/emulation/shared.ts +13 -1
- package/src/emulation/sync.ts +16 -15
- package/src/file.ts +9 -21
- package/src/inode.ts +10 -31
- package/src/mixins/async.ts +27 -24
- package/src/stats.ts +47 -5
- package/src/utils.ts +11 -23
- package/tests/fs/dir.test.ts +21 -31
- package/tests/fs/directory.test.ts +6 -4
- package/tests/fs/links.test.ts +9 -2
- package/tests/fs/permissions.test.ts +2 -2
- package/tests/fs/stat.test.ts +42 -0
- package/tests/fs/times.test.ts +28 -28
- package/tests/setup/cow+fetch.ts +4 -2
package/dist/emulation/shared.js
CHANGED
|
@@ -69,6 +69,15 @@ export function resolveMount(path) {
|
|
|
69
69
|
}
|
|
70
70
|
throw new ErrnoError(Errno.EIO, 'ZenFS not initialized with a file system');
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Wait for all file systems to be ready and synced.
|
|
74
|
+
* May be removed at some point.
|
|
75
|
+
* @experimental @internal
|
|
76
|
+
*/
|
|
77
|
+
export async function _synced() {
|
|
78
|
+
await Promise.all([...mounts.values()].map(m => m.ready()));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
72
81
|
/**
|
|
73
82
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
74
83
|
* @hidden
|
|
@@ -87,7 +96,10 @@ export function fixError(e, paths) {
|
|
|
87
96
|
if (typeof e.stack == 'string') {
|
|
88
97
|
e.stack = fixPaths(e.stack, paths);
|
|
89
98
|
}
|
|
90
|
-
|
|
99
|
+
try {
|
|
100
|
+
e.message = fixPaths(e.message, paths);
|
|
101
|
+
}
|
|
102
|
+
catch { }
|
|
91
103
|
return e;
|
|
92
104
|
}
|
|
93
105
|
/**
|
package/dist/emulation/sync.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export declare function writeFileSync(path: fs.PathOrFileDescriptor, data: FileC
|
|
|
64
64
|
* Asynchronously append data to a file, creating the file if it not yet exists.
|
|
65
65
|
* @option encoding Defaults to `'utf8'`.
|
|
66
66
|
* @option mode Defaults to `0644`.
|
|
67
|
-
* @option flag Defaults to `'a'`.
|
|
67
|
+
* @option flag Defaults to `'a+'`.
|
|
68
68
|
*/
|
|
69
69
|
export declare function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileContents, _options?: fs.WriteFileOptions): void;
|
|
70
70
|
/**
|
package/dist/emulation/sync.js
CHANGED
|
@@ -147,7 +147,7 @@ export function unlinkSync(path) {
|
|
|
147
147
|
path = normalizePath(path);
|
|
148
148
|
const { fs, path: resolved } = resolveMount(path);
|
|
149
149
|
try {
|
|
150
|
-
if (config.checkAccess && !(cache.
|
|
150
|
+
if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
|
|
151
151
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
152
152
|
}
|
|
153
153
|
fs.unlinkSync(resolved);
|
|
@@ -272,12 +272,12 @@ writeFileSync;
|
|
|
272
272
|
* Asynchronously append data to a file, creating the file if it not yet exists.
|
|
273
273
|
* @option encoding Defaults to `'utf8'`.
|
|
274
274
|
* @option mode Defaults to `0644`.
|
|
275
|
-
* @option flag Defaults to `'a'`.
|
|
275
|
+
* @option flag Defaults to `'a+'`.
|
|
276
276
|
*/
|
|
277
277
|
export function appendFileSync(filename, data, _options = {}) {
|
|
278
278
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
279
279
|
try {
|
|
280
|
-
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
280
|
+
const options = normalizeOptions(_options, 'utf8', 'a+', 0o644);
|
|
281
281
|
const flag = parseFlag(options.flag);
|
|
282
282
|
if (!isAppendable(flag)) {
|
|
283
283
|
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
@@ -393,7 +393,7 @@ export function rmdirSync(path) {
|
|
|
393
393
|
path = normalizePath(path);
|
|
394
394
|
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
395
395
|
try {
|
|
396
|
-
const stats = cache.
|
|
396
|
+
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
|
|
397
397
|
if (!stats.isDirectory()) {
|
|
398
398
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
399
399
|
}
|
|
@@ -446,8 +446,8 @@ export function readdirSync(path, options) {
|
|
|
446
446
|
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
447
447
|
let entries;
|
|
448
448
|
try {
|
|
449
|
-
const stats = cache.
|
|
450
|
-
cache.
|
|
449
|
+
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
|
|
450
|
+
cache.stats.setSync(path, stats);
|
|
451
451
|
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
|
|
452
452
|
throw ErrnoError.With('EACCES', resolved, 'readdir');
|
|
453
453
|
}
|
|
@@ -462,8 +462,8 @@ export function readdirSync(path, options) {
|
|
|
462
462
|
// Iterate over entries and handle recursive case if needed
|
|
463
463
|
const values = [];
|
|
464
464
|
for (const entry of entries) {
|
|
465
|
-
const entryStat = cache.
|
|
466
|
-
cache.
|
|
465
|
+
const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry));
|
|
466
|
+
cache.stats.setSync(join(path, entry), entryStat);
|
|
467
467
|
if (options?.withFileTypes) {
|
|
468
468
|
values.push(new Dirent(entry, entryStat));
|
|
469
469
|
}
|
|
@@ -489,7 +489,7 @@ export function readdirSync(path, options) {
|
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
if (!options?._isIndirect) {
|
|
492
|
-
cache.
|
|
492
|
+
cache.stats.clearSync();
|
|
493
493
|
}
|
|
494
494
|
return values;
|
|
495
495
|
}
|
|
@@ -593,14 +593,15 @@ lutimesSync;
|
|
|
593
593
|
export function realpathSync(path, options) {
|
|
594
594
|
path = normalizePath(path);
|
|
595
595
|
const { base, dir } = parse(path);
|
|
596
|
-
const lpath = join(dir == '/' ? '/' : realpathSync(dir), base);
|
|
596
|
+
const lpath = join(dir == '/' ? '/' : cache.paths.getSync(dir) || realpathSync(dir), base);
|
|
597
597
|
const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
|
|
598
598
|
try {
|
|
599
599
|
const stats = fs.statSync(resolvedPath);
|
|
600
600
|
if (!stats.isSymbolicLink()) {
|
|
601
601
|
return lpath;
|
|
602
602
|
}
|
|
603
|
-
|
|
603
|
+
const target = mountPoint + readlinkSync(lpath, options).toString();
|
|
604
|
+
return cache.paths.getSync(target) || realpathSync(target);
|
|
604
605
|
}
|
|
605
606
|
catch (e) {
|
|
606
607
|
if (e.code == 'ENOENT') {
|
|
@@ -626,7 +627,7 @@ export function rmSync(path, options) {
|
|
|
626
627
|
path = normalizePath(path);
|
|
627
628
|
let stats;
|
|
628
629
|
try {
|
|
629
|
-
stats = cache.
|
|
630
|
+
stats = cache.stats.getSync(path) || statSync(path);
|
|
630
631
|
}
|
|
631
632
|
catch (error) {
|
|
632
633
|
if (error.code != 'ENOENT' || !options?.force)
|
|
@@ -635,7 +636,7 @@ export function rmSync(path, options) {
|
|
|
635
636
|
if (!stats) {
|
|
636
637
|
return;
|
|
637
638
|
}
|
|
638
|
-
cache.
|
|
639
|
+
cache.stats.setSync(path, stats);
|
|
639
640
|
switch (stats.mode & constants.S_IFMT) {
|
|
640
641
|
case constants.S_IFDIR:
|
|
641
642
|
if (options?.recursive) {
|
|
@@ -654,11 +655,11 @@ export function rmSync(path, options) {
|
|
|
654
655
|
case constants.S_IFIFO:
|
|
655
656
|
case constants.S_IFSOCK:
|
|
656
657
|
default:
|
|
657
|
-
cache.
|
|
658
|
+
cache.stats.clearSync();
|
|
658
659
|
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
659
660
|
}
|
|
660
661
|
if (!options?._isIndirect) {
|
|
661
|
-
cache.
|
|
662
|
+
cache.stats.clearSync();
|
|
662
663
|
}
|
|
663
664
|
}
|
|
664
665
|
rmSync;
|
package/dist/file.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare function isTruncating(flag: string): boolean;
|
|
|
38
38
|
export declare function isAppendable(flag: string): boolean;
|
|
39
39
|
export declare function isSynchronous(flag: string): boolean;
|
|
40
40
|
export declare function isExclusive(flag: string): boolean;
|
|
41
|
-
export declare abstract class File {
|
|
41
|
+
export declare abstract class File<FS extends FileSystem = FileSystem> {
|
|
42
42
|
/**
|
|
43
43
|
* @internal
|
|
44
44
|
* The file system that created the file
|
|
@@ -138,12 +138,7 @@ export declare abstract class File {
|
|
|
138
138
|
* An implementation of `File` that operates completely in-memory.
|
|
139
139
|
* `PreloadFile`s are backed by a `Uint8Array`.
|
|
140
140
|
*/
|
|
141
|
-
export declare class PreloadFile<FS extends FileSystem> extends File {
|
|
142
|
-
/**
|
|
143
|
-
* The file system that created the file.
|
|
144
|
-
* @internal
|
|
145
|
-
*/
|
|
146
|
-
fs: FS;
|
|
141
|
+
export declare class PreloadFile<FS extends FileSystem> extends File<FS> {
|
|
147
142
|
readonly flag: string;
|
|
148
143
|
readonly stats: Stats;
|
|
149
144
|
/**
|
|
@@ -166,12 +161,7 @@ export declare class PreloadFile<FS extends FileSystem> extends File {
|
|
|
166
161
|
* Creates a file with `path` and, optionally, the given contents.
|
|
167
162
|
* Note that, if contents is specified, it will be mutated by the file.
|
|
168
163
|
*/
|
|
169
|
-
constructor(
|
|
170
|
-
/**
|
|
171
|
-
* The file system that created the file.
|
|
172
|
-
* @internal
|
|
173
|
-
*/
|
|
174
|
-
fs: FS, path: string, flag: string, stats: Stats,
|
|
164
|
+
constructor(fs: FS, path: string, flag: string, stats: Stats,
|
|
175
165
|
/**
|
|
176
166
|
* A buffer containing the entire contents of the file.
|
|
177
167
|
*/
|
|
@@ -255,8 +245,6 @@ export declare class PreloadFile<FS extends FileSystem> extends File {
|
|
|
255
245
|
utimesSync(atime: Date, mtime: Date): void;
|
|
256
246
|
_setType(type: FileType): Promise<void>;
|
|
257
247
|
_setTypeSync(type: FileType): void;
|
|
258
|
-
[Symbol.asyncDispose](): Promise<void>;
|
|
259
|
-
[Symbol.dispose](): void;
|
|
260
248
|
}
|
|
261
249
|
/**
|
|
262
250
|
* For the file systems which do not sync to anything.
|
package/dist/file.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT, size_max } from './emulation/constants.js';
|
|
2
1
|
import { config } from './emulation/config.js';
|
|
2
|
+
import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT, size_max } from './emulation/constants.js';
|
|
3
3
|
import { Errno, ErrnoError } from './error.js';
|
|
4
4
|
import './polyfills.js';
|
|
5
5
|
import { Stats } from './stats.js';
|
|
@@ -114,11 +114,11 @@ export class File {
|
|
|
114
114
|
this.fs = fs;
|
|
115
115
|
this.path = path;
|
|
116
116
|
}
|
|
117
|
-
[Symbol.asyncDispose]() {
|
|
118
|
-
|
|
117
|
+
async [Symbol.asyncDispose]() {
|
|
118
|
+
await this.close();
|
|
119
119
|
}
|
|
120
120
|
[Symbol.dispose]() {
|
|
121
|
-
|
|
121
|
+
this.closeSync();
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
124
|
* Default implementation maps to `sync`.
|
|
@@ -142,18 +142,12 @@ export class PreloadFile extends File {
|
|
|
142
142
|
* Creates a file with `path` and, optionally, the given contents.
|
|
143
143
|
* Note that, if contents is specified, it will be mutated by the file.
|
|
144
144
|
*/
|
|
145
|
-
constructor(
|
|
146
|
-
/**
|
|
147
|
-
* The file system that created the file.
|
|
148
|
-
* @internal
|
|
149
|
-
*/
|
|
150
|
-
fs, path, flag, stats,
|
|
145
|
+
constructor(fs, path, flag, stats,
|
|
151
146
|
/**
|
|
152
147
|
* A buffer containing the entire contents of the file.
|
|
153
148
|
*/
|
|
154
149
|
_buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
|
|
155
150
|
super(fs, path);
|
|
156
|
-
this.fs = fs;
|
|
157
151
|
this.flag = flag;
|
|
158
152
|
this.stats = stats;
|
|
159
153
|
this._buffer = _buffer;
|
|
@@ -280,12 +274,12 @@ export class PreloadFile extends File {
|
|
|
280
274
|
if (length > this._buffer.length) {
|
|
281
275
|
const data = new Uint8Array(length - this._buffer.length);
|
|
282
276
|
// Write will set stats.size and handle syncing.
|
|
283
|
-
this.
|
|
277
|
+
this._write(data, 0, data.length, this._buffer.length);
|
|
284
278
|
return;
|
|
285
279
|
}
|
|
286
280
|
this.stats.size = length;
|
|
287
281
|
// Truncate.
|
|
288
|
-
this._buffer = this._buffer.slice(0, length);
|
|
282
|
+
this._buffer = length ? this._buffer.slice(0, length) : new Uint8Array();
|
|
289
283
|
}
|
|
290
284
|
async truncate(length) {
|
|
291
285
|
this._truncate(length);
|
|
@@ -484,12 +478,6 @@ export class PreloadFile extends File {
|
|
|
484
478
|
this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
|
|
485
479
|
this.syncSync();
|
|
486
480
|
}
|
|
487
|
-
async [Symbol.asyncDispose]() {
|
|
488
|
-
await this.close();
|
|
489
|
-
}
|
|
490
|
-
[Symbol.dispose]() {
|
|
491
|
-
this.closeSync();
|
|
492
|
-
}
|
|
493
481
|
}
|
|
494
482
|
/**
|
|
495
483
|
* For the file systems which do not sync to anything.
|
package/dist/inode.d.ts
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
import { Stats, type StatsLike } from './stats.js';
|
|
2
|
-
/**
|
|
3
|
-
* Alias for an ino.
|
|
4
|
-
* This will be helpful if in the future inode numbers/IDs are changed to strings or numbers.
|
|
5
|
-
*/
|
|
6
|
-
export type Ino = bigint;
|
|
7
2
|
/**
|
|
8
3
|
* Room inode
|
|
9
4
|
* @hidden
|
|
10
5
|
*/
|
|
11
6
|
export declare const rootIno = 0n;
|
|
12
|
-
/**
|
|
13
|
-
* Generate a random ino
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
export declare function randomIno(): Ino;
|
|
17
7
|
/**
|
|
18
8
|
* Generic inode definition that can easily be serialized.
|
|
9
|
+
* @internal
|
|
19
10
|
*/
|
|
20
11
|
export declare class Inode implements StatsLike {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ino: Ino;
|
|
12
|
+
constructor(buffer?: ArrayBufferLike | ArrayBufferView);
|
|
13
|
+
data: bigint;
|
|
24
14
|
size: number;
|
|
25
15
|
mode: number;
|
|
26
16
|
nlink: number;
|
|
@@ -30,6 +20,7 @@ export declare class Inode implements StatsLike {
|
|
|
30
20
|
birthtimeMs: number;
|
|
31
21
|
mtimeMs: number;
|
|
32
22
|
ctimeMs: number;
|
|
23
|
+
ino: bigint;
|
|
33
24
|
/**
|
|
34
25
|
* Handy function that converts the Inode to a Node Stats object.
|
|
35
26
|
*/
|
package/dist/inode.js
CHANGED
|
@@ -36,38 +36,27 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
|
|
|
36
36
|
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
37
37
|
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
38
38
|
};
|
|
39
|
+
import { deserialize, sizeof, struct, types as t } from 'utilium';
|
|
39
40
|
import { Stats } from './stats.js';
|
|
40
|
-
import {
|
|
41
|
+
import { randomBigInt } from './utils.js';
|
|
41
42
|
/**
|
|
42
43
|
* Room inode
|
|
43
44
|
* @hidden
|
|
44
45
|
*/
|
|
45
46
|
export const rootIno = 0n;
|
|
46
|
-
/**
|
|
47
|
-
* Generates a random 32 bit integer, then converts to a hex string
|
|
48
|
-
*/
|
|
49
|
-
function _random() {
|
|
50
|
-
return Math.round(Math.random() * 2 ** 32).toString(16);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Generate a random ino
|
|
54
|
-
* @internal
|
|
55
|
-
*/
|
|
56
|
-
export function randomIno() {
|
|
57
|
-
return BigInt('0x' + _random() + _random());
|
|
58
|
-
}
|
|
59
47
|
/**
|
|
60
48
|
* Generic inode definition that can easily be serialized.
|
|
49
|
+
* @internal
|
|
61
50
|
*/
|
|
62
51
|
let Inode = (() => {
|
|
63
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
52
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
64
53
|
let _classDecorators = [struct()];
|
|
65
54
|
let _classDescriptor;
|
|
66
55
|
let _classExtraInitializers = [];
|
|
67
56
|
let _classThis;
|
|
68
|
-
let
|
|
69
|
-
let
|
|
70
|
-
let
|
|
57
|
+
let _data_decorators;
|
|
58
|
+
let _data_initializers = [];
|
|
59
|
+
let _data_extraInitializers = [];
|
|
71
60
|
let _size_decorators;
|
|
72
61
|
let _size_initializers = [];
|
|
73
62
|
let _size_extraInitializers = [];
|
|
@@ -95,13 +84,13 @@ let Inode = (() => {
|
|
|
95
84
|
let _ctimeMs_decorators;
|
|
96
85
|
let _ctimeMs_initializers = [];
|
|
97
86
|
let _ctimeMs_extraInitializers = [];
|
|
87
|
+
let _ino_decorators;
|
|
88
|
+
let _ino_initializers = [];
|
|
89
|
+
let _ino_extraInitializers = [];
|
|
98
90
|
var Inode = _classThis = class {
|
|
99
|
-
get data() {
|
|
100
|
-
return serialize(this);
|
|
101
|
-
}
|
|
102
91
|
constructor(buffer) {
|
|
103
|
-
this.
|
|
104
|
-
this.size = (__runInitializers(this,
|
|
92
|
+
this.data = __runInitializers(this, _data_initializers, void 0);
|
|
93
|
+
this.size = (__runInitializers(this, _data_extraInitializers), __runInitializers(this, _size_initializers, void 0));
|
|
105
94
|
this.mode = (__runInitializers(this, _size_extraInitializers), __runInitializers(this, _mode_initializers, void 0));
|
|
106
95
|
this.nlink = (__runInitializers(this, _mode_extraInitializers), __runInitializers(this, _nlink_initializers, void 0));
|
|
107
96
|
this.uid = (__runInitializers(this, _nlink_extraInitializers), __runInitializers(this, _uid_initializers, void 0));
|
|
@@ -110,7 +99,8 @@ let Inode = (() => {
|
|
|
110
99
|
this.birthtimeMs = (__runInitializers(this, _atimeMs_extraInitializers), __runInitializers(this, _birthtimeMs_initializers, void 0));
|
|
111
100
|
this.mtimeMs = (__runInitializers(this, _birthtimeMs_extraInitializers), __runInitializers(this, _mtimeMs_initializers, void 0));
|
|
112
101
|
this.ctimeMs = (__runInitializers(this, _mtimeMs_extraInitializers), __runInitializers(this, _ctimeMs_initializers, void 0));
|
|
113
|
-
__runInitializers(this, _ctimeMs_extraInitializers);
|
|
102
|
+
this.ino = (__runInitializers(this, _ctimeMs_extraInitializers), __runInitializers(this, _ino_initializers, void 0));
|
|
103
|
+
__runInitializers(this, _ino_extraInitializers);
|
|
114
104
|
if (buffer) {
|
|
115
105
|
if (buffer.byteLength < sizeof(Inode)) {
|
|
116
106
|
throw new RangeError(`Can not create an inode from a buffer less than ${sizeof(Inode)} bytes`);
|
|
@@ -119,7 +109,8 @@ let Inode = (() => {
|
|
|
119
109
|
return;
|
|
120
110
|
}
|
|
121
111
|
// set defaults on a fresh inode
|
|
122
|
-
this.ino =
|
|
112
|
+
this.ino = randomBigInt();
|
|
113
|
+
this.data = randomBigInt();
|
|
123
114
|
this.nlink = 1;
|
|
124
115
|
this.size = 4096;
|
|
125
116
|
const now = Date.now();
|
|
@@ -162,8 +153,8 @@ let Inode = (() => {
|
|
|
162
153
|
this.uid = stats.uid;
|
|
163
154
|
hasChanged = true;
|
|
164
155
|
}
|
|
165
|
-
if (this.
|
|
166
|
-
this.
|
|
156
|
+
if (this.gid !== stats.gid) {
|
|
157
|
+
this.gid = stats.gid;
|
|
167
158
|
hasChanged = true;
|
|
168
159
|
}
|
|
169
160
|
if (this.atimeMs !== stats.atimeMs) {
|
|
@@ -184,7 +175,7 @@ let Inode = (() => {
|
|
|
184
175
|
__setFunctionName(_classThis, "Inode");
|
|
185
176
|
(() => {
|
|
186
177
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
187
|
-
|
|
178
|
+
_data_decorators = [(_a = t).uint64.bind(_a)];
|
|
188
179
|
_size_decorators = [(_b = t).uint32.bind(_b)];
|
|
189
180
|
_mode_decorators = [(_c = t).uint16.bind(_c)];
|
|
190
181
|
_nlink_decorators = [(_d = t).uint32.bind(_d)];
|
|
@@ -194,7 +185,8 @@ let Inode = (() => {
|
|
|
194
185
|
_birthtimeMs_decorators = [(_h = t).float64.bind(_h)];
|
|
195
186
|
_mtimeMs_decorators = [(_j = t).float64.bind(_j)];
|
|
196
187
|
_ctimeMs_decorators = [(_k = t).float64.bind(_k)];
|
|
197
|
-
|
|
188
|
+
_ino_decorators = [(_l = t).uint64.bind(_l)];
|
|
189
|
+
__esDecorate(null, null, _data_decorators, { kind: "field", name: "data", static: false, private: false, access: { has: obj => "data" in obj, get: obj => obj.data, set: (obj, value) => { obj.data = value; } }, metadata: _metadata }, _data_initializers, _data_extraInitializers);
|
|
198
190
|
__esDecorate(null, null, _size_decorators, { kind: "field", name: "size", static: false, private: false, access: { has: obj => "size" in obj, get: obj => obj.size, set: (obj, value) => { obj.size = value; } }, metadata: _metadata }, _size_initializers, _size_extraInitializers);
|
|
199
191
|
__esDecorate(null, null, _mode_decorators, { kind: "field", name: "mode", static: false, private: false, access: { has: obj => "mode" in obj, get: obj => obj.mode, set: (obj, value) => { obj.mode = value; } }, metadata: _metadata }, _mode_initializers, _mode_extraInitializers);
|
|
200
192
|
__esDecorate(null, null, _nlink_decorators, { kind: "field", name: "nlink", static: false, private: false, access: { has: obj => "nlink" in obj, get: obj => obj.nlink, set: (obj, value) => { obj.nlink = value; } }, metadata: _metadata }, _nlink_initializers, _nlink_extraInitializers);
|
|
@@ -204,6 +196,7 @@ let Inode = (() => {
|
|
|
204
196
|
__esDecorate(null, null, _birthtimeMs_decorators, { kind: "field", name: "birthtimeMs", static: false, private: false, access: { has: obj => "birthtimeMs" in obj, get: obj => obj.birthtimeMs, set: (obj, value) => { obj.birthtimeMs = value; } }, metadata: _metadata }, _birthtimeMs_initializers, _birthtimeMs_extraInitializers);
|
|
205
197
|
__esDecorate(null, null, _mtimeMs_decorators, { kind: "field", name: "mtimeMs", static: false, private: false, access: { has: obj => "mtimeMs" in obj, get: obj => obj.mtimeMs, set: (obj, value) => { obj.mtimeMs = value; } }, metadata: _metadata }, _mtimeMs_initializers, _mtimeMs_extraInitializers);
|
|
206
198
|
__esDecorate(null, null, _ctimeMs_decorators, { kind: "field", name: "ctimeMs", static: false, private: false, access: { has: obj => "ctimeMs" in obj, get: obj => obj.ctimeMs, set: (obj, value) => { obj.ctimeMs = value; } }, metadata: _metadata }, _ctimeMs_initializers, _ctimeMs_extraInitializers);
|
|
199
|
+
__esDecorate(null, null, _ino_decorators, { kind: "field", name: "ino", static: false, private: false, access: { has: obj => "ino" in obj, get: obj => obj.ino, set: (obj, value) => { obj.ino = value; } }, metadata: _metadata }, _ino_initializers, _ino_extraInitializers);
|
|
207
200
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
208
201
|
Inode = _classThis = _classDescriptor.value;
|
|
209
202
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
package/dist/mixins/async.d.ts
CHANGED
|
@@ -7,16 +7,9 @@ export type AsyncOperation = {
|
|
|
7
7
|
[K in keyof AsyncFSMethods]: [K, ...Parameters<FileSystem[K]>];
|
|
8
8
|
}[keyof AsyncFSMethods];
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Implementing classes must define `_sync` for the synchronous file system used as a cache.
|
|
13
|
-
*
|
|
14
|
-
* Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
|
|
15
|
-
* while asynchronously pipelining them to the backing store.
|
|
16
|
-
* During loading, the contents of the async file system are preloaded into the synchronous store.
|
|
17
|
-
*
|
|
10
|
+
* @internal
|
|
18
11
|
*/
|
|
19
|
-
export
|
|
12
|
+
export interface Async {
|
|
20
13
|
/**
|
|
21
14
|
* @internal @protected
|
|
22
15
|
*/
|
|
@@ -33,4 +26,16 @@ export declare function Async<T extends typeof FileSystem>(FS: T): Mixin<T, {
|
|
|
33
26
|
readdirSync(path: string): string[];
|
|
34
27
|
linkSync(srcpath: string, dstpath: string): void;
|
|
35
28
|
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
36
|
-
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Async() implements synchronous methods on an asynchronous file system
|
|
32
|
+
*
|
|
33
|
+
* Implementing classes must define `_sync` for the synchronous file system used as a cache.
|
|
34
|
+
*
|
|
35
|
+
* Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
|
|
36
|
+
* while asynchronously pipelining them to the backing store.
|
|
37
|
+
* During loading, the contents of the async file system are preloaded into the synchronous store.
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
export declare function Async<const T extends typeof FileSystem>(FS: T): Mixin<T, Async>;
|
|
41
|
+
export declare function asyncPatch<T extends typeof FileSystem>(fs: Mixin<T, Async>): void;
|
package/dist/mixins/async.js
CHANGED
|
@@ -80,6 +80,7 @@ export function Async(FS) {
|
|
|
80
80
|
}
|
|
81
81
|
async ready() {
|
|
82
82
|
await super.ready();
|
|
83
|
+
await this.queueDone();
|
|
83
84
|
if (this._isInitialized || this._disableSync) {
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
@@ -131,7 +132,7 @@ export function Async(FS) {
|
|
|
131
132
|
}
|
|
132
133
|
openFileSync(path, flag) {
|
|
133
134
|
this.checkSync(path, 'openFile');
|
|
134
|
-
const file = this._sync.openFileSync(path, flag);
|
|
135
|
+
const file = this._sync.openFileSync(path, flag + '+');
|
|
135
136
|
const stats = file.statSync();
|
|
136
137
|
const buffer = new Uint8Array(stats.size);
|
|
137
138
|
file.readSync(buffer);
|
|
@@ -228,3 +229,4 @@ export function Async(FS) {
|
|
|
228
229
|
}
|
|
229
230
|
return AsyncFS;
|
|
230
231
|
}
|
|
232
|
+
export function asyncPatch(fs) { }
|
package/dist/stats.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { credentials } from './credentials.js';
|
|
2
|
-
import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK,
|
|
2
|
+
import { R_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, size_max, W_OK, X_OK, } from './emulation/constants.js';
|
|
3
3
|
/**
|
|
4
4
|
* Provides information about a particular entry in the file system.
|
|
5
5
|
* Common code used by both Stats and BigIntStats.
|
|
@@ -110,12 +110,37 @@ export class StatsCommon {
|
|
|
110
110
|
*/
|
|
111
111
|
hasAccess(mode) {
|
|
112
112
|
if (credentials.euid === 0 || credentials.egid === 0) {
|
|
113
|
-
//Running as root
|
|
113
|
+
// Running as root
|
|
114
114
|
return true;
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
let perm = 0;
|
|
117
|
+
// Owner permissions
|
|
118
|
+
if (credentials.uid === this.uid) {
|
|
119
|
+
if (this.mode & S_IRUSR)
|
|
120
|
+
perm |= R_OK;
|
|
121
|
+
if (this.mode & S_IWUSR)
|
|
122
|
+
perm |= W_OK;
|
|
123
|
+
if (this.mode & S_IXUSR)
|
|
124
|
+
perm |= X_OK;
|
|
125
|
+
}
|
|
126
|
+
// Group permissions
|
|
127
|
+
if (credentials.gid === this.gid) {
|
|
128
|
+
if (this.mode & S_IRGRP)
|
|
129
|
+
perm |= R_OK;
|
|
130
|
+
if (this.mode & S_IWGRP)
|
|
131
|
+
perm |= W_OK;
|
|
132
|
+
if (this.mode & S_IXGRP)
|
|
133
|
+
perm |= X_OK;
|
|
134
|
+
}
|
|
135
|
+
// Others permissions
|
|
136
|
+
if (this.mode & S_IROTH)
|
|
137
|
+
perm |= R_OK;
|
|
138
|
+
if (this.mode & S_IWOTH)
|
|
139
|
+
perm |= W_OK;
|
|
140
|
+
if (this.mode & S_IXOTH)
|
|
141
|
+
perm |= X_OK;
|
|
142
|
+
// Perform the access check
|
|
143
|
+
return (perm & mode) === mode;
|
|
119
144
|
}
|
|
120
145
|
/**
|
|
121
146
|
* Convert the current stats object into a credentials object
|
package/dist/utils.d.ts
CHANGED
|
@@ -50,13 +50,6 @@ export declare function decodeDirListing(data: Uint8Array): Record<string, bigin
|
|
|
50
50
|
*/
|
|
51
51
|
export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
|
|
52
52
|
export type Callback<Args extends unknown[] = []> = (e?: ErrnoError, ...args: OptionalTuple<Args>) => unknown;
|
|
53
|
-
/**
|
|
54
|
-
* converts Date or number to a integer UNIX timestamp
|
|
55
|
-
* Grabbed from NodeJS sources (lib/fs.js)
|
|
56
|
-
*
|
|
57
|
-
* @internal
|
|
58
|
-
*/
|
|
59
|
-
export declare function _toUnixTimestamp(time: Date | number): number;
|
|
60
53
|
/**
|
|
61
54
|
* Normalizes a mode
|
|
62
55
|
* @internal
|
|
@@ -88,3 +81,8 @@ export declare function normalizeOptions(options: fs.WriteFileOptions | (fs.Enco
|
|
|
88
81
|
mode: number;
|
|
89
82
|
};
|
|
90
83
|
export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: any[]) => InstanceType<T>);
|
|
84
|
+
/**
|
|
85
|
+
* Generate a random ino
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
export declare function randomBigInt(): bigint;
|
package/dist/utils.js
CHANGED
|
@@ -143,21 +143,6 @@ export function decodeDirListing(data) {
|
|
|
143
143
|
export function encodeDirListing(data) {
|
|
144
144
|
return encodeUTF8(JSON.stringify(data, (k, v) => (k == '' ? v : v.toString())));
|
|
145
145
|
}
|
|
146
|
-
/**
|
|
147
|
-
* converts Date or number to a integer UNIX timestamp
|
|
148
|
-
* Grabbed from NodeJS sources (lib/fs.js)
|
|
149
|
-
*
|
|
150
|
-
* @internal
|
|
151
|
-
*/
|
|
152
|
-
export function _toUnixTimestamp(time) {
|
|
153
|
-
if (typeof time === 'number') {
|
|
154
|
-
return Math.floor(time);
|
|
155
|
-
}
|
|
156
|
-
if (time instanceof Date) {
|
|
157
|
-
return Math.floor(time.getTime() / 1000);
|
|
158
|
-
}
|
|
159
|
-
throw new Error('Cannot parse time');
|
|
160
|
-
}
|
|
161
146
|
/**
|
|
162
147
|
* Normalizes a mode
|
|
163
148
|
* @internal
|
|
@@ -185,13 +170,12 @@ export function normalizeTime(time) {
|
|
|
185
170
|
if (time instanceof Date) {
|
|
186
171
|
return time;
|
|
187
172
|
}
|
|
188
|
-
|
|
189
|
-
return new Date(time * 1000);
|
|
190
|
-
}
|
|
191
|
-
if (typeof time == 'string') {
|
|
173
|
+
try {
|
|
192
174
|
return new Date(time);
|
|
193
175
|
}
|
|
194
|
-
|
|
176
|
+
catch {
|
|
177
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid time.');
|
|
178
|
+
}
|
|
195
179
|
}
|
|
196
180
|
/**
|
|
197
181
|
* Normalizes a path
|
|
@@ -229,3 +213,10 @@ export function normalizeOptions(options, encoding = 'utf8', flag, mode = 0) {
|
|
|
229
213
|
mode: normalizeMode('mode' in options ? options?.mode : null, mode),
|
|
230
214
|
};
|
|
231
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Generate a random ino
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
export function randomBigInt() {
|
|
221
|
+
return crypto.getRandomValues(new BigUint64Array(1))[0];
|
|
222
|
+
}
|
package/package.json
CHANGED
package/src/backends/fetch.ts
CHANGED
|
@@ -106,7 +106,7 @@ export class FetchFS extends IndexFS {
|
|
|
106
106
|
baseUrl += '/';
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
super(typeof index != 'string' ? index : fetchFile<IndexData>(
|
|
109
|
+
super(typeof index != 'string' ? index : fetchFile<IndexData>(index, 'json', requestInit));
|
|
110
110
|
|
|
111
111
|
this.baseUrl = baseUrl;
|
|
112
112
|
this.requestInit = requestInit;
|
package/src/backends/memory.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Ino } from '../inode.js';
|
|
2
1
|
import type { Backend } from './backend.js';
|
|
3
2
|
import { StoreFS } from './store/fs.js';
|
|
4
3
|
import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
|
|
@@ -6,7 +5,7 @@ import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
|
|
|
6
5
|
/**
|
|
7
6
|
* A simple in-memory store
|
|
8
7
|
*/
|
|
9
|
-
export class InMemoryStore extends Map<
|
|
8
|
+
export class InMemoryStore extends Map<bigint, Uint8Array> implements SimpleSyncStore {
|
|
10
9
|
public constructor(public name: string = 'tmp') {
|
|
11
10
|
super();
|
|
12
11
|
}
|
package/src/backends/overlay.ts
CHANGED
|
@@ -159,7 +159,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
159
159
|
if (this._deletedFiles.has(path)) {
|
|
160
160
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
161
161
|
}
|
|
162
|
-
const oldStat =
|
|
162
|
+
const oldStat = await this.readable.stat(path);
|
|
163
163
|
// Make the oldStat's mode writable.
|
|
164
164
|
oldStat.mode |= 0o222;
|
|
165
165
|
return oldStat;
|
|
@@ -174,7 +174,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
174
174
|
if (this._deletedFiles.has(path)) {
|
|
175
175
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
176
176
|
}
|
|
177
|
-
const oldStat =
|
|
177
|
+
const oldStat = this.readable.statSync(path);
|
|
178
178
|
// Make the oldStat's mode writable.
|
|
179
179
|
oldStat.mode |= 0o222;
|
|
180
180
|
return oldStat;
|