@zenfs/core 1.2.1 → 1.2.3
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/store/simple.d.ts +3 -0
- package/dist/backends/store/simple.js +6 -0
- package/dist/backends/store/store.d.ts +16 -6
- package/dist/backends/store/store.js +7 -1
- package/dist/emulation/cache.d.ts +14 -2
- package/dist/emulation/cache.js +25 -0
- package/dist/emulation/promises.js +22 -12
- package/dist/emulation/sync.js +11 -11
- package/dist/mixins/async.js +17 -3
- package/package.json +1 -1
- package/src/backends/store/simple.ts +9 -0
- package/src/backends/store/store.ts +23 -6
- package/src/emulation/cache.ts +32 -3
- package/src/emulation/promises.ts +23 -12
- package/src/emulation/sync.ts +11 -11
- package/src/mixins/async.ts +22 -3
- package/tests/fs/appendFile.test.ts +3 -3
- package/tests/fs/dir.test.ts +7 -7
- package/tests/fs/directory.test.ts +7 -7
- package/tests/fs/links.test.ts +3 -3
- package/tests/fs/open.test.ts +2 -2
- package/tests/fs/permissions.test.ts +1 -1
- package/tests/fs/read.test.ts +5 -5
- package/tests/fs/readFile.test.ts +2 -2
- package/tests/fs/rename.test.ts +4 -4
- package/tests/fs/streams.test.ts +19 -19
- package/tests/fs/times.test.ts +4 -4
- package/tests/fs/watch.test.ts +10 -10
- package/tests/fs/write.test.ts +5 -5
- package/tests/fs/writeFile.test.ts +5 -5
- package/tests/handle.test.ts +2 -2
- package/tests/mutex.test.ts +1 -1
- package/tests/port/timeout.test.ts +2 -2
|
@@ -4,6 +4,7 @@ import { SyncTransaction, type Store } from './store.js';
|
|
|
4
4
|
* An interface for simple synchronous stores that don't have special support for transactions and such.
|
|
5
5
|
*/
|
|
6
6
|
export interface SimpleSyncStore extends Store {
|
|
7
|
+
keys(): Iterable<Ino>;
|
|
7
8
|
get(ino: Ino): Uint8Array | undefined;
|
|
8
9
|
set(ino: Ino, data: Uint8Array): void;
|
|
9
10
|
delete(ino: Ino): void;
|
|
@@ -17,6 +18,7 @@ export declare abstract class SimpleAsyncStore implements SimpleSyncStore {
|
|
|
17
18
|
protected cache: Map<Ino, Uint8Array>;
|
|
18
19
|
protected queue: Set<Promise<unknown>>;
|
|
19
20
|
protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
|
|
21
|
+
keys(): Iterable<Ino>;
|
|
20
22
|
get(ino: Ino): Uint8Array | undefined;
|
|
21
23
|
set(ino: Ino, data: Uint8Array): void;
|
|
22
24
|
protected abstract _set(ino: Ino, data: Uint8Array): Promise<void>;
|
|
@@ -43,6 +45,7 @@ export declare class SimpleTransaction extends SyncTransaction<SimpleSyncStore>
|
|
|
43
45
|
*/
|
|
44
46
|
protected modifiedKeys: Set<Ino>;
|
|
45
47
|
protected store: SimpleSyncStore;
|
|
48
|
+
keysSync(): Iterable<Ino>;
|
|
46
49
|
getSync(ino: Ino): Uint8Array;
|
|
47
50
|
setSync(ino: Ino, data: Uint8Array): void;
|
|
48
51
|
removeSync(ino: Ino): void;
|
|
@@ -8,6 +8,9 @@ export class SimpleAsyncStore {
|
|
|
8
8
|
this.cache = new Map();
|
|
9
9
|
this.queue = new Set();
|
|
10
10
|
}
|
|
11
|
+
keys() {
|
|
12
|
+
return this.cache.keys();
|
|
13
|
+
}
|
|
11
14
|
get(ino) {
|
|
12
15
|
return this.cache.get(ino);
|
|
13
16
|
}
|
|
@@ -55,6 +58,9 @@ export class SimpleTransaction extends SyncTransaction {
|
|
|
55
58
|
*/
|
|
56
59
|
this.modifiedKeys = new Set();
|
|
57
60
|
}
|
|
61
|
+
keysSync() {
|
|
62
|
+
return this.store.keys();
|
|
63
|
+
}
|
|
58
64
|
getSync(ino) {
|
|
59
65
|
const val = this.store.get(ino);
|
|
60
66
|
this.stashOldValue(ino, val);
|
|
@@ -32,9 +32,17 @@ export declare abstract class Transaction<T extends Store = Store> {
|
|
|
32
32
|
protected store: T;
|
|
33
33
|
constructor(store: T);
|
|
34
34
|
/**
|
|
35
|
-
* Whether the transaction was
|
|
35
|
+
* Whether the transaction was committed or aborted
|
|
36
36
|
*/
|
|
37
37
|
protected done: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Gets all of the keys
|
|
40
|
+
*/
|
|
41
|
+
abstract keys(): Promise<Iterable<Ino>>;
|
|
42
|
+
/**
|
|
43
|
+
* Gets all of the keys
|
|
44
|
+
*/
|
|
45
|
+
abstract keysSync(): Iterable<Ino>;
|
|
38
46
|
/**
|
|
39
47
|
* Retrieves the data at `ino`.
|
|
40
48
|
* @param ino The key to look under for data.
|
|
@@ -92,6 +100,7 @@ export declare abstract class Transaction<T extends Store = Store> {
|
|
|
92
100
|
* Transaction that implements asynchronous operations with synchronous ones
|
|
93
101
|
*/
|
|
94
102
|
export declare abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
103
|
+
keys(): Promise<Iterable<Ino>>;
|
|
95
104
|
get(ino: Ino): Promise<Uint8Array>;
|
|
96
105
|
set(ino: bigint, data: Uint8Array): Promise<void>;
|
|
97
106
|
remove(ino: Ino): Promise<void>;
|
|
@@ -102,9 +111,10 @@ export declare abstract class SyncTransaction<T extends Store = Store> extends T
|
|
|
102
111
|
* Transaction that only supports asynchronous operations
|
|
103
112
|
*/
|
|
104
113
|
export declare abstract class AsyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
keysSync(): never;
|
|
115
|
+
getSync(): never;
|
|
116
|
+
setSync(): never;
|
|
117
|
+
removeSync(): never;
|
|
118
|
+
commitSync(): never;
|
|
119
|
+
abortSync(): never;
|
|
110
120
|
}
|
|
@@ -7,7 +7,7 @@ export class Transaction {
|
|
|
7
7
|
constructor(store) {
|
|
8
8
|
this.store = store;
|
|
9
9
|
/**
|
|
10
|
-
* Whether the transaction was
|
|
10
|
+
* Whether the transaction was committed or aborted
|
|
11
11
|
*/
|
|
12
12
|
this.done = false;
|
|
13
13
|
}
|
|
@@ -29,6 +29,9 @@ export class Transaction {
|
|
|
29
29
|
*/
|
|
30
30
|
export class SyncTransaction extends Transaction {
|
|
31
31
|
/* eslint-disable @typescript-eslint/require-await */
|
|
32
|
+
async keys() {
|
|
33
|
+
return this.keysSync();
|
|
34
|
+
}
|
|
32
35
|
async get(ino) {
|
|
33
36
|
return this.getSync(ino);
|
|
34
37
|
}
|
|
@@ -49,6 +52,9 @@ export class SyncTransaction extends Transaction {
|
|
|
49
52
|
* Transaction that only supports asynchronous operations
|
|
50
53
|
*/
|
|
51
54
|
export class AsyncTransaction extends Transaction {
|
|
55
|
+
keysSync() {
|
|
56
|
+
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.keysSync');
|
|
57
|
+
}
|
|
52
58
|
getSync() {
|
|
53
59
|
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.getSync');
|
|
54
60
|
}
|
|
@@ -10,11 +10,23 @@ export declare function setEnabled(value: boolean): void;
|
|
|
10
10
|
/**
|
|
11
11
|
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
12
12
|
*/
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function getStatsSync(path: string): Stats | undefined;
|
|
14
14
|
/**
|
|
15
15
|
* Adds stats if the cache is enabled
|
|
16
16
|
*/
|
|
17
|
-
export declare function
|
|
17
|
+
export declare function setStatsSync(path: string, value: Stats): void;
|
|
18
|
+
/**
|
|
19
|
+
* Clears the cache if it is enabled
|
|
20
|
+
*/
|
|
21
|
+
export declare function clearStatsSync(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getStats(path: string): Promise<Stats | undefined> | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Adds stats if the cache is enabled
|
|
28
|
+
*/
|
|
29
|
+
export declare function setStats(path: string, value: Promise<Stats | undefined>): void;
|
|
18
30
|
/**
|
|
19
31
|
* Clears the cache if it is enabled
|
|
20
32
|
*/
|
package/dist/emulation/cache.js
CHANGED
|
@@ -9,6 +9,31 @@ export let isEnabled = false;
|
|
|
9
9
|
export function setEnabled(value) {
|
|
10
10
|
isEnabled = value;
|
|
11
11
|
}
|
|
12
|
+
const statsSync = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
15
|
+
*/
|
|
16
|
+
export function getStatsSync(path) {
|
|
17
|
+
if (!isEnabled)
|
|
18
|
+
return;
|
|
19
|
+
return statsSync.get(path);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Adds stats if the cache is enabled
|
|
23
|
+
*/
|
|
24
|
+
export function setStatsSync(path, value) {
|
|
25
|
+
if (!isEnabled)
|
|
26
|
+
return;
|
|
27
|
+
statsSync.set(path, value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Clears the cache if it is enabled
|
|
31
|
+
*/
|
|
32
|
+
export function clearStatsSync() {
|
|
33
|
+
if (!isEnabled)
|
|
34
|
+
return;
|
|
35
|
+
statsSync.clear();
|
|
36
|
+
}
|
|
12
37
|
const stats = new Map();
|
|
13
38
|
/**
|
|
14
39
|
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
@@ -435,7 +435,7 @@ export async function unlink(path) {
|
|
|
435
435
|
path = normalizePath(path);
|
|
436
436
|
const { fs, path: resolved } = resolveMount(path);
|
|
437
437
|
try {
|
|
438
|
-
if (config.checkAccess && !(cache.getStats(path) ||
|
|
438
|
+
if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
|
|
439
439
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
440
440
|
}
|
|
441
441
|
await fs.unlink(resolved);
|
|
@@ -584,7 +584,10 @@ export async function rmdir(path) {
|
|
|
584
584
|
path = await realpath(path);
|
|
585
585
|
const { fs, path: resolved } = resolveMount(path);
|
|
586
586
|
try {
|
|
587
|
-
const stats = cache.getStats(path) ||
|
|
587
|
+
const stats = await (cache.getStats(path) || fs.stat(resolved));
|
|
588
|
+
if (!stats) {
|
|
589
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
590
|
+
}
|
|
588
591
|
if (!stats.isDirectory()) {
|
|
589
592
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
590
593
|
}
|
|
@@ -640,8 +643,12 @@ export async function readdir(path, options) {
|
|
|
640
643
|
throw fixError(e, { [resolved]: path });
|
|
641
644
|
};
|
|
642
645
|
const { fs, path: resolved } = resolveMount(path);
|
|
643
|
-
const
|
|
644
|
-
cache.setStats(path,
|
|
646
|
+
const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
|
|
647
|
+
cache.setStats(path, _stats);
|
|
648
|
+
const stats = await _stats;
|
|
649
|
+
if (!stats) {
|
|
650
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
651
|
+
}
|
|
645
652
|
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
|
|
646
653
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
647
654
|
}
|
|
@@ -663,8 +670,9 @@ export async function readdir(path, options) {
|
|
|
663
670
|
const addEntry = async (entry) => {
|
|
664
671
|
let entryStats;
|
|
665
672
|
if (options?.recursive || options?.withFileTypes) {
|
|
666
|
-
|
|
667
|
-
cache.setStats(join(path, entry),
|
|
673
|
+
const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
|
|
674
|
+
cache.setStats(join(path, entry), _entryStats);
|
|
675
|
+
entryStats = await _entryStats;
|
|
668
676
|
}
|
|
669
677
|
if (options?.withFileTypes) {
|
|
670
678
|
values.push(new Dirent(entry, entryStats));
|
|
@@ -934,15 +942,17 @@ access;
|
|
|
934
942
|
*/
|
|
935
943
|
export async function rm(path, options) {
|
|
936
944
|
path = normalizePath(path);
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
if (error.code
|
|
940
|
-
|
|
941
|
-
|
|
945
|
+
const _stats = cache.getStats(path) ||
|
|
946
|
+
stat(path).catch((error) => {
|
|
947
|
+
if (error.code == 'ENOENT' && options?.force)
|
|
948
|
+
return undefined;
|
|
949
|
+
throw error;
|
|
950
|
+
});
|
|
951
|
+
cache.setStats(path, _stats);
|
|
952
|
+
const stats = await _stats;
|
|
942
953
|
if (!stats) {
|
|
943
954
|
return;
|
|
944
955
|
}
|
|
945
|
-
cache.setStats(path, stats);
|
|
946
956
|
switch (stats.mode & constants.S_IFMT) {
|
|
947
957
|
case constants.S_IFDIR:
|
|
948
958
|
if (options?.recursive) {
|
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.getStatsSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
|
|
151
151
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
152
152
|
}
|
|
153
153
|
fs.unlinkSync(resolved);
|
|
@@ -394,7 +394,7 @@ export function rmdirSync(path) {
|
|
|
394
394
|
path = normalizePath(path);
|
|
395
395
|
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
396
396
|
try {
|
|
397
|
-
const stats = cache.
|
|
397
|
+
const stats = cache.getStatsSync(path) || fs.statSync(resolved);
|
|
398
398
|
if (!stats.isDirectory()) {
|
|
399
399
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
400
400
|
}
|
|
@@ -447,8 +447,8 @@ export function readdirSync(path, options) {
|
|
|
447
447
|
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
448
448
|
let entries;
|
|
449
449
|
try {
|
|
450
|
-
const stats = cache.
|
|
451
|
-
cache.
|
|
450
|
+
const stats = cache.getStatsSync(path) || fs.statSync(resolved);
|
|
451
|
+
cache.setStatsSync(path, stats);
|
|
452
452
|
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
|
|
453
453
|
throw ErrnoError.With('EACCES', resolved, 'readdir');
|
|
454
454
|
}
|
|
@@ -474,8 +474,8 @@ export function readdirSync(path, options) {
|
|
|
474
474
|
// Iterate over entries and handle recursive case if needed
|
|
475
475
|
const values = [];
|
|
476
476
|
for (const entry of entries) {
|
|
477
|
-
const entryStat = cache.
|
|
478
|
-
cache.
|
|
477
|
+
const entryStat = cache.getStatsSync(join(path, entry)) || fs.statSync(join(resolved, entry));
|
|
478
|
+
cache.setStatsSync(join(path, entry), entryStat);
|
|
479
479
|
if (options?.withFileTypes) {
|
|
480
480
|
values.push(new Dirent(entry, entryStat));
|
|
481
481
|
}
|
|
@@ -501,7 +501,7 @@ export function readdirSync(path, options) {
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
if (!options?._isIndirect) {
|
|
504
|
-
cache.
|
|
504
|
+
cache.clearStatsSync();
|
|
505
505
|
}
|
|
506
506
|
return values;
|
|
507
507
|
}
|
|
@@ -638,7 +638,7 @@ export function rmSync(path, options) {
|
|
|
638
638
|
path = normalizePath(path);
|
|
639
639
|
let stats;
|
|
640
640
|
try {
|
|
641
|
-
stats = cache.
|
|
641
|
+
stats = cache.getStatsSync(path) || statSync(path);
|
|
642
642
|
}
|
|
643
643
|
catch (error) {
|
|
644
644
|
if (error.code != 'ENOENT' || !options?.force)
|
|
@@ -647,7 +647,7 @@ export function rmSync(path, options) {
|
|
|
647
647
|
if (!stats) {
|
|
648
648
|
return;
|
|
649
649
|
}
|
|
650
|
-
cache.
|
|
650
|
+
cache.setStatsSync(path, stats);
|
|
651
651
|
switch (stats.mode & constants.S_IFMT) {
|
|
652
652
|
case constants.S_IFDIR:
|
|
653
653
|
if (options?.recursive) {
|
|
@@ -666,11 +666,11 @@ export function rmSync(path, options) {
|
|
|
666
666
|
case constants.S_IFIFO:
|
|
667
667
|
case constants.S_IFSOCK:
|
|
668
668
|
default:
|
|
669
|
-
cache.
|
|
669
|
+
cache.clearStatsSync();
|
|
670
670
|
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
671
671
|
}
|
|
672
672
|
if (!options?._isIndirect) {
|
|
673
|
-
cache.
|
|
673
|
+
cache.clearStatsSync();
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
rmSync;
|
package/dist/mixins/async.js
CHANGED
|
@@ -45,6 +45,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
45
45
|
var e = new Error(message);
|
|
46
46
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
47
47
|
});
|
|
48
|
+
import { StoreFS } from '../backends/store/fs.js';
|
|
48
49
|
import { join } from '../emulation/path.js';
|
|
49
50
|
import { Errno, ErrnoError } from '../error.js';
|
|
50
51
|
import { parseFlag, PreloadFile } from '../file.js';
|
|
@@ -84,6 +85,18 @@ export function Async(FS) {
|
|
|
84
85
|
}
|
|
85
86
|
this.checkSync();
|
|
86
87
|
await this._sync.ready();
|
|
88
|
+
// optimization: for 2 storeFS', we copy at a lower abstraction level.
|
|
89
|
+
if (this._sync instanceof StoreFS && this instanceof StoreFS) {
|
|
90
|
+
const sync = this._sync['store'].transaction();
|
|
91
|
+
const async = this['store'].transaction();
|
|
92
|
+
const promises = [];
|
|
93
|
+
for (const key of sync.keysSync()) {
|
|
94
|
+
promises.push(async.set(key, sync.getSync(key)));
|
|
95
|
+
}
|
|
96
|
+
await Promise.all(promises);
|
|
97
|
+
this._isInitialized = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
87
100
|
try {
|
|
88
101
|
await this.crossCopy('/');
|
|
89
102
|
this._isInitialized = true;
|
|
@@ -187,10 +200,11 @@ export function Async(FS) {
|
|
|
187
200
|
const stats = await this.stat(path);
|
|
188
201
|
this._sync.mkdirSync(path, stats.mode);
|
|
189
202
|
}
|
|
190
|
-
const
|
|
191
|
-
for (const file of
|
|
192
|
-
|
|
203
|
+
const promises = [];
|
|
204
|
+
for (const file of await this.readdir(path)) {
|
|
205
|
+
promises.push(this.crossCopy(join(path, file)));
|
|
193
206
|
}
|
|
207
|
+
await Promise.all(promises);
|
|
194
208
|
}
|
|
195
209
|
/**
|
|
196
210
|
* @internal
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import { SyncTransaction, type Store } from './store.js';
|
|
|
5
5
|
* An interface for simple synchronous stores that don't have special support for transactions and such.
|
|
6
6
|
*/
|
|
7
7
|
export interface SimpleSyncStore extends Store {
|
|
8
|
+
keys(): Iterable<Ino>;
|
|
8
9
|
get(ino: Ino): Uint8Array | undefined;
|
|
9
10
|
set(ino: Ino, data: Uint8Array): void;
|
|
10
11
|
delete(ino: Ino): void;
|
|
@@ -23,6 +24,10 @@ export abstract class SimpleAsyncStore implements SimpleSyncStore {
|
|
|
23
24
|
|
|
24
25
|
protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
|
|
25
26
|
|
|
27
|
+
public keys(): Iterable<Ino> {
|
|
28
|
+
return this.cache.keys();
|
|
29
|
+
}
|
|
30
|
+
|
|
26
31
|
public get(ino: Ino): Uint8Array | undefined {
|
|
27
32
|
return this.cache.get(ino);
|
|
28
33
|
}
|
|
@@ -82,6 +87,10 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
|
|
|
82
87
|
|
|
83
88
|
protected declare store: SimpleSyncStore;
|
|
84
89
|
|
|
90
|
+
public keysSync(): Iterable<Ino> {
|
|
91
|
+
return this.store.keys();
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
public getSync(ino: Ino): Uint8Array {
|
|
86
95
|
const val = this.store.get(ino);
|
|
87
96
|
this.stashOldValue(ino, val);
|
|
@@ -39,10 +39,20 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
39
39
|
public constructor(protected store: T) {}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Whether the transaction was
|
|
42
|
+
* Whether the transaction was committed or aborted
|
|
43
43
|
*/
|
|
44
44
|
protected done: boolean = false;
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Gets all of the keys
|
|
48
|
+
*/
|
|
49
|
+
public abstract keys(): Promise<Iterable<Ino>>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Gets all of the keys
|
|
53
|
+
*/
|
|
54
|
+
public abstract keysSync(): Iterable<Ino>;
|
|
55
|
+
|
|
46
56
|
/**
|
|
47
57
|
* Retrieves the data at `ino`.
|
|
48
58
|
* @param ino The key to look under for data.
|
|
@@ -125,6 +135,9 @@ export abstract class Transaction<T extends Store = Store> {
|
|
|
125
135
|
*/
|
|
126
136
|
export abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
127
137
|
/* eslint-disable @typescript-eslint/require-await */
|
|
138
|
+
public async keys(): Promise<Iterable<Ino>> {
|
|
139
|
+
return this.keysSync();
|
|
140
|
+
}
|
|
128
141
|
public async get(ino: Ino): Promise<Uint8Array> {
|
|
129
142
|
return this.getSync(ino);
|
|
130
143
|
}
|
|
@@ -151,23 +164,27 @@ export abstract class SyncTransaction<T extends Store = Store> extends Transacti
|
|
|
151
164
|
* Transaction that only supports asynchronous operations
|
|
152
165
|
*/
|
|
153
166
|
export abstract class AsyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
154
|
-
public
|
|
167
|
+
public keysSync(): never {
|
|
168
|
+
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.keysSync');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public getSync(): never {
|
|
155
172
|
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.getSync');
|
|
156
173
|
}
|
|
157
174
|
|
|
158
|
-
public setSync():
|
|
175
|
+
public setSync(): never {
|
|
159
176
|
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.setSync');
|
|
160
177
|
}
|
|
161
178
|
|
|
162
|
-
public removeSync():
|
|
179
|
+
public removeSync(): never {
|
|
163
180
|
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.removeSync');
|
|
164
181
|
}
|
|
165
182
|
|
|
166
|
-
public commitSync():
|
|
183
|
+
public commitSync(): never {
|
|
167
184
|
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.commitSync');
|
|
168
185
|
}
|
|
169
186
|
|
|
170
|
-
public abortSync():
|
|
187
|
+
public abortSync(): never {
|
|
171
188
|
throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.abortSync');
|
|
172
189
|
}
|
|
173
190
|
}
|
package/src/emulation/cache.ts
CHANGED
|
@@ -14,12 +14,41 @@ export function setEnabled(value: boolean): void {
|
|
|
14
14
|
isEnabled = value;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const statsSync = new Map<string, Stats>();
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
21
21
|
*/
|
|
22
|
-
export function
|
|
22
|
+
export function getStatsSync(path: string): Stats | undefined {
|
|
23
|
+
if (!isEnabled) return;
|
|
24
|
+
|
|
25
|
+
return statsSync.get(path);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Adds stats if the cache is enabled
|
|
30
|
+
*/
|
|
31
|
+
export function setStatsSync(path: string, value: Stats): void {
|
|
32
|
+
if (!isEnabled) return;
|
|
33
|
+
|
|
34
|
+
statsSync.set(path, value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Clears the cache if it is enabled
|
|
39
|
+
*/
|
|
40
|
+
export function clearStatsSync(): void {
|
|
41
|
+
if (!isEnabled) return;
|
|
42
|
+
|
|
43
|
+
statsSync.clear();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const stats = new Map<string, Promise<Stats | undefined>>();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets stats from the cache, if they exist and the cache is enabled.
|
|
50
|
+
*/
|
|
51
|
+
export function getStats(path: string): Promise<Stats | undefined> | undefined {
|
|
23
52
|
if (!isEnabled) return;
|
|
24
53
|
|
|
25
54
|
return stats.get(path);
|
|
@@ -28,7 +57,7 @@ export function getStats(path: string): Stats | undefined {
|
|
|
28
57
|
/**
|
|
29
58
|
* Adds stats if the cache is enabled
|
|
30
59
|
*/
|
|
31
|
-
export function setStats(path: string, value: Stats): void {
|
|
60
|
+
export function setStats(path: string, value: Promise<Stats | undefined>): void {
|
|
32
61
|
if (!isEnabled) return;
|
|
33
62
|
|
|
34
63
|
stats.set(path, value);
|
|
@@ -471,7 +471,7 @@ export async function unlink(path: fs.PathLike): Promise<void> {
|
|
|
471
471
|
path = normalizePath(path);
|
|
472
472
|
const { fs, path: resolved } = resolveMount(path);
|
|
473
473
|
try {
|
|
474
|
-
if (config.checkAccess && !(cache.getStats(path) ||
|
|
474
|
+
if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
|
|
475
475
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
476
476
|
}
|
|
477
477
|
await fs.unlink(resolved);
|
|
@@ -624,7 +624,10 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
|
|
|
624
624
|
path = await realpath(path);
|
|
625
625
|
const { fs, path: resolved } = resolveMount(path);
|
|
626
626
|
try {
|
|
627
|
-
const stats = cache.getStats(path) ||
|
|
627
|
+
const stats = await (cache.getStats(path) || fs.stat(resolved));
|
|
628
|
+
if (!stats) {
|
|
629
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
630
|
+
}
|
|
628
631
|
if (!stats.isDirectory()) {
|
|
629
632
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
630
633
|
}
|
|
@@ -716,8 +719,13 @@ export async function readdir(
|
|
|
716
719
|
|
|
717
720
|
const { fs, path: resolved } = resolveMount(path);
|
|
718
721
|
|
|
719
|
-
const
|
|
720
|
-
cache.setStats(path,
|
|
722
|
+
const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
|
|
723
|
+
cache.setStats(path, _stats);
|
|
724
|
+
const stats = await _stats;
|
|
725
|
+
|
|
726
|
+
if (!stats) {
|
|
727
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
728
|
+
}
|
|
721
729
|
|
|
722
730
|
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
|
|
723
731
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
@@ -744,8 +752,9 @@ export async function readdir(
|
|
|
744
752
|
const addEntry = async (entry: string) => {
|
|
745
753
|
let entryStats: Stats | undefined;
|
|
746
754
|
if (options?.recursive || options?.withFileTypes) {
|
|
747
|
-
|
|
748
|
-
cache.setStats(join(path, entry),
|
|
755
|
+
const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
|
|
756
|
+
cache.setStats(join(path, entry), _entryStats);
|
|
757
|
+
entryStats = await _entryStats;
|
|
749
758
|
}
|
|
750
759
|
if (options?.withFileTypes) {
|
|
751
760
|
values.push(new Dirent(entry, entryStats!));
|
|
@@ -968,18 +977,20 @@ access satisfies typeof promises.access;
|
|
|
968
977
|
export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOptions) {
|
|
969
978
|
path = normalizePath(path);
|
|
970
979
|
|
|
971
|
-
const
|
|
980
|
+
const _stats =
|
|
972
981
|
cache.getStats(path) ||
|
|
973
|
-
|
|
974
|
-
if (error.code
|
|
975
|
-
|
|
982
|
+
stat(path).catch((error: ErrnoError) => {
|
|
983
|
+
if (error.code == 'ENOENT' && options?.force) return undefined;
|
|
984
|
+
throw error;
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
cache.setStats(path, _stats);
|
|
988
|
+
const stats = await _stats;
|
|
976
989
|
|
|
977
990
|
if (!stats) {
|
|
978
991
|
return;
|
|
979
992
|
}
|
|
980
993
|
|
|
981
|
-
cache.setStats(path, stats);
|
|
982
|
-
|
|
983
994
|
switch (stats.mode & constants.S_IFMT) {
|
|
984
995
|
case constants.S_IFDIR:
|
|
985
996
|
if (options?.recursive) {
|