@zenfs/core 2.7.7 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/backends/index.d.ts +3 -1
- package/dist/backends/index.js +3 -1
- package/dist/backends/single_buffer/backend.d.ts +42 -0
- package/dist/backends/single_buffer/backend.js +204 -0
- package/dist/backends/{single_buffer.d.ts → single_buffer/v1.d.ts} +24 -34
- package/dist/backends/{single_buffer.js → single_buffer/v1.js} +10 -26
- package/dist/backends/single_buffer/v2.d.ts +257 -0
- package/dist/backends/single_buffer/v2.js +1283 -0
- package/dist/backends/store/fs.d.ts +1 -1
- package/dist/backends/store/fs.js +34 -15
- package/dist/backends/store/map.d.ts +6 -6
- package/dist/backends/store/map.js +6 -6
- package/dist/backends/store/store.d.ts +18 -14
- package/dist/backends/store/store.js +11 -11
- package/dist/internal/contexts.d.ts +5 -0
- package/dist/internal/contexts.js +7 -1
- package/dist/mixins/async.js +26 -7
- package/dist/node/promises.js +6 -103
- package/dist/node/sync.js +6 -33
- package/dist/node/types.d.ts +6 -1
- package/dist/vfs/async.d.ts +4 -1
- package/dist/vfs/async.js +72 -5
- package/dist/vfs/sync.d.ts +4 -1
- package/dist/vfs/sync.js +65 -4
- package/package.json +3 -3
- package/tests/backend/single-buffer-rotate.worker.js +1 -1
- package/tests/backend/single-buffer-v2.test.ts +342 -0
- package/tests/backend/single-buffer-v2.worker.js +51 -0
- package/tests/backend/single-buffer.test.ts +6 -6
- package/tests/backend/single-buffer.worker.js +1 -0
- package/tests/common/async.test.ts +11 -0
- package/tests/common/context.test.ts +30 -0
- package/tests/fs/permissions.test.ts +27 -6
- package/tests/fs/watch.test.ts +14 -0
- package/tests/perf/config.json +31 -1
- package/tests/perf/single-buffer-random.ts +38 -0
- package/tests/perf/single-buffer-read.ts +28 -0
- package/tests/perf/single-buffer-write.ts +41 -0
- package/tests/setup/single-buffer-v2.ts +10 -0
- package/tests/setup/single-buffer.ts +1 -0
|
@@ -123,7 +123,7 @@ export declare class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
123
123
|
protected findInodeSync(tx: WrappedTransaction, path: string): Inode;
|
|
124
124
|
private _lastID?;
|
|
125
125
|
/** Allocates a new ID and adds the ID/path */
|
|
126
|
-
protected allocNew(path: string): number;
|
|
126
|
+
protected allocNew(path: string): [number, number];
|
|
127
127
|
/**
|
|
128
128
|
* Commits a new file (well, a FILE or a DIRECTORY) to the file system with `mode`.
|
|
129
129
|
* Note: This will commit the transaction.
|
|
@@ -616,13 +616,20 @@ export class StoreFS extends FileSystem {
|
|
|
616
616
|
const tx = __addDisposableResource(env_17, this.transaction(), true);
|
|
617
617
|
const inode = await this.findInode(tx, path);
|
|
618
618
|
const end = offset + data.byteLength;
|
|
619
|
-
let buffer = data;
|
|
620
619
|
if (!tx.flag('partial')) {
|
|
621
|
-
buffer = extendBuffer((await tx.get(inode.data)) ?? new Uint8Array(), end);
|
|
620
|
+
const buffer = extendBuffer((await tx.get(inode.data)) ?? new Uint8Array(), end);
|
|
622
621
|
buffer.set(data, offset);
|
|
623
|
-
|
|
622
|
+
await tx.set(inode.data, buffer);
|
|
623
|
+
}
|
|
624
|
+
else if (offset <= inode.size) {
|
|
625
|
+
await tx.set(inode.data, data, offset);
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
// Writes past the end of the file also zero the bytes before them
|
|
629
|
+
const extended = new Uint8Array(end - inode.size);
|
|
630
|
+
extended.set(data, offset - inode.size);
|
|
631
|
+
await tx.set(inode.data, extended, inode.size);
|
|
624
632
|
}
|
|
625
|
-
await tx.set(inode.data, buffer, offset);
|
|
626
633
|
// Update the inode in the same transaction, so data and metadata can not get out of sync (#286)
|
|
627
634
|
inode.update({ size: Math.max(inode.size, end), mtimeMs: Date.now() });
|
|
628
635
|
await tx.set(inode.ino, inode);
|
|
@@ -645,13 +652,20 @@ export class StoreFS extends FileSystem {
|
|
|
645
652
|
const tx = __addDisposableResource(env_18, this.transaction(), false);
|
|
646
653
|
const inode = this.findInodeSync(tx, path);
|
|
647
654
|
const end = offset + data.byteLength;
|
|
648
|
-
let buffer = data;
|
|
649
655
|
if (!tx.flag('partial')) {
|
|
650
|
-
buffer = extendBuffer(tx.getSync(inode.data) ?? new Uint8Array(), end);
|
|
656
|
+
const buffer = extendBuffer(tx.getSync(inode.data) ?? new Uint8Array(), end);
|
|
651
657
|
buffer.set(data, offset);
|
|
652
|
-
|
|
658
|
+
tx.setSync(inode.data, buffer);
|
|
659
|
+
}
|
|
660
|
+
else if (offset <= inode.size) {
|
|
661
|
+
tx.setSync(inode.data, data, offset);
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
// Writes past the end of the file also zero the bytes before them
|
|
665
|
+
const extended = new Uint8Array(end - inode.size);
|
|
666
|
+
extended.set(data, offset - inode.size);
|
|
667
|
+
tx.setSync(inode.data, extended, inode.size);
|
|
653
668
|
}
|
|
654
|
-
tx.setSync(inode.data, buffer, offset);
|
|
655
669
|
// Update the inode in the same transaction, so data and metadata can not get out of sync (#286)
|
|
656
670
|
inode.update({ size: Math.max(inode.size, end), mtimeMs: Date.now() });
|
|
657
671
|
tx.setSync(inode.ino, inode);
|
|
@@ -915,6 +929,11 @@ export class StoreFS extends FileSystem {
|
|
|
915
929
|
_lastID;
|
|
916
930
|
/** Allocates a new ID and adds the ID/path */
|
|
917
931
|
allocNew(path) {
|
|
932
|
+
if (this.store.allocId) {
|
|
933
|
+
const ids = this.store.allocId();
|
|
934
|
+
this._add(ids[0], path);
|
|
935
|
+
return ids;
|
|
936
|
+
}
|
|
918
937
|
// @todo use `Iterator` helper #296
|
|
919
938
|
this._lastID ||= Array.from(this._paths.keys()).reduce((max, ino) => Math.max(max, ino), 0);
|
|
920
939
|
this._lastID += 2;
|
|
@@ -922,7 +941,7 @@ export class StoreFS extends FileSystem {
|
|
|
922
941
|
if (id > size_max)
|
|
923
942
|
throw err(withErrno('ENOSPC', 'No IDs available'));
|
|
924
943
|
this._add(id, path);
|
|
925
|
-
return id;
|
|
944
|
+
return [id, id + 1];
|
|
926
945
|
}
|
|
927
946
|
/**
|
|
928
947
|
* Commits a new file (well, a FILE or a DIRECTORY) to the file system with `mode`.
|
|
@@ -947,11 +966,11 @@ export class StoreFS extends FileSystem {
|
|
|
947
966
|
const listing = decodeDirListing((await tx.get(parent.data)) ?? _throw(withErrno('ENOENT')));
|
|
948
967
|
if (listing[fname])
|
|
949
968
|
throw withErrno('EEXIST');
|
|
950
|
-
const
|
|
969
|
+
const [ino, dataId] = this.allocNew(path);
|
|
951
970
|
const inode = new Inode({
|
|
952
971
|
...options,
|
|
953
|
-
ino
|
|
954
|
-
data:
|
|
972
|
+
ino,
|
|
973
|
+
data: dataId,
|
|
955
974
|
size: data.byteLength,
|
|
956
975
|
nlink: 1,
|
|
957
976
|
});
|
|
@@ -996,11 +1015,11 @@ export class StoreFS extends FileSystem {
|
|
|
996
1015
|
const listing = decodeDirListing(tx.getSync(parent.data) ?? _throw(withErrno('ENOENT')));
|
|
997
1016
|
if (listing[fname])
|
|
998
1017
|
throw withErrno('EEXIST');
|
|
999
|
-
const
|
|
1018
|
+
const [ino, dataId] = this.allocNew(path);
|
|
1000
1019
|
const inode = new Inode({
|
|
1001
1020
|
...options,
|
|
1002
|
-
ino
|
|
1003
|
-
data:
|
|
1021
|
+
ino,
|
|
1022
|
+
data: dataId,
|
|
1004
1023
|
size: data.byteLength,
|
|
1005
1024
|
nlink: 1,
|
|
1006
1025
|
});
|
|
@@ -6,9 +6,9 @@ import { AsyncTransaction, SyncTransaction } from './store.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export interface SyncMapStore extends Store {
|
|
8
8
|
keys(): Iterable<number>;
|
|
9
|
-
get(id: number): Uint8Array | undefined;
|
|
10
|
-
getAsync?(id: number): Promise<Uint8Array | undefined>;
|
|
11
|
-
set(id: number, data: Uint8Array): void;
|
|
9
|
+
get(id: number, offset?: number, end?: number): Uint8Array | undefined;
|
|
10
|
+
getAsync?(id: number, offset?: number, end?: number): Promise<Uint8Array | undefined>;
|
|
11
|
+
set(id: number, data: Uint8Array, offset?: number): void;
|
|
12
12
|
delete(id: number): void;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
@@ -19,9 +19,9 @@ export interface SyncMapStore extends Store {
|
|
|
19
19
|
export declare class SyncMapTransaction extends SyncTransaction<SyncMapStore> {
|
|
20
20
|
readonly store: SyncMapStore;
|
|
21
21
|
keys(): Promise<Iterable<number>>;
|
|
22
|
-
get(id: number): Promise<Uint8Array | undefined>;
|
|
23
|
-
getSync(id: number): Uint8Array | undefined;
|
|
24
|
-
setSync(id: number, data: Uint8Array): void;
|
|
22
|
+
get(id: number, offset?: number, end?: number): Promise<Uint8Array | undefined>;
|
|
23
|
+
getSync(id: number, offset?: number, end?: number): Uint8Array | undefined;
|
|
24
|
+
setSync(id: number, data: Uint8Array, offset?: number): void;
|
|
25
25
|
removeSync(id: number): void;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
@@ -9,14 +9,14 @@ export class SyncMapTransaction extends SyncTransaction {
|
|
|
9
9
|
async keys() {
|
|
10
10
|
return this.store.keys();
|
|
11
11
|
}
|
|
12
|
-
async get(id) {
|
|
13
|
-
return await (this.store.getAsync?.(id) ?? this.store.get(id));
|
|
12
|
+
async get(id, offset, end) {
|
|
13
|
+
return await (this.store.getAsync?.(id, offset, end) ?? this.store.get(id, offset, end));
|
|
14
14
|
}
|
|
15
|
-
getSync(id) {
|
|
16
|
-
return this.store.get(id);
|
|
15
|
+
getSync(id, offset, end) {
|
|
16
|
+
return this.store.get(id, offset, end);
|
|
17
17
|
}
|
|
18
|
-
setSync(id, data) {
|
|
19
|
-
this.store.set(id, data);
|
|
18
|
+
setSync(id, data, offset) {
|
|
19
|
+
this.store.set(id, data, offset);
|
|
20
20
|
}
|
|
21
21
|
removeSync(id) {
|
|
22
22
|
this.store.delete(id);
|
|
@@ -7,7 +7,7 @@ import type { StoreFS } from './fs.js';
|
|
|
7
7
|
* @category Stores and Transactions
|
|
8
8
|
*/
|
|
9
9
|
export type StoreFlag =
|
|
10
|
-
/** The store supports
|
|
10
|
+
/** The store supports reading ranges and writing at offsets */
|
|
11
11
|
'partial';
|
|
12
12
|
/**
|
|
13
13
|
* Represents a key-value store.
|
|
@@ -48,6 +48,10 @@ export interface Store {
|
|
|
48
48
|
* Usage information for the store
|
|
49
49
|
*/
|
|
50
50
|
usage?(): UsageInfo;
|
|
51
|
+
/**
|
|
52
|
+
* Allocate IDs for a new inode + data
|
|
53
|
+
*/
|
|
54
|
+
allocId?(): [ino: number, data: number];
|
|
51
55
|
/**
|
|
52
56
|
* @internal @hidden
|
|
53
57
|
*/
|
|
@@ -77,17 +81,17 @@ export declare abstract class Transaction<T extends Store = Store> {
|
|
|
77
81
|
*/
|
|
78
82
|
abstract getSync(id: number, offset: number, end?: number): Uint8Array | undefined;
|
|
79
83
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
84
|
+
* Stores data under an id.
|
|
85
|
+
* Without `offset`, replaces any existing data.
|
|
86
|
+
* With `offset`, writes the data at `offset` and keeps the rest, which partial stores must support.
|
|
83
87
|
*/
|
|
84
|
-
abstract set(id: number, data: Uint8Array, offset
|
|
88
|
+
abstract set(id: number, data: Uint8Array, offset?: number): Promise<void>;
|
|
85
89
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
90
|
+
* Stores data under an id.
|
|
91
|
+
* Without `offset`, replaces any existing data.
|
|
92
|
+
* With `offset`, writes the data at `offset` and keeps the rest, which partial stores must support.
|
|
89
93
|
*/
|
|
90
|
-
abstract setSync(id: number, data: Uint8Array, offset
|
|
94
|
+
abstract setSync(id: number, data: Uint8Array, offset?: number): void;
|
|
91
95
|
/**
|
|
92
96
|
* Deletes the data at `ino`.
|
|
93
97
|
* @param id The key to delete from the store.
|
|
@@ -105,7 +109,7 @@ export declare abstract class Transaction<T extends Store = Store> {
|
|
|
105
109
|
*/
|
|
106
110
|
export declare abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
|
|
107
111
|
get(id: number, offset: number, end?: number): Promise<Uint8Array | undefined>;
|
|
108
|
-
set(id: number, data: Uint8Array, offset
|
|
112
|
+
set(id: number, data: Uint8Array, offset?: number): Promise<void>;
|
|
109
113
|
remove(id: number): Promise<void>;
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
@@ -136,7 +140,7 @@ export declare abstract class AsyncTransaction<T extends AsyncStore = AsyncStore
|
|
|
136
140
|
size: number;
|
|
137
141
|
}): Resource<number> | undefined;
|
|
138
142
|
getSync(id: number, offset: number, end?: number): Uint8Array | undefined;
|
|
139
|
-
setSync(id: number, data: Uint8Array, offset
|
|
143
|
+
setSync(id: number, data: Uint8Array, offset?: number): void;
|
|
140
144
|
removeSync(id: number): void;
|
|
141
145
|
}
|
|
142
146
|
/**
|
|
@@ -160,7 +164,7 @@ export declare class WrappedTransaction<T extends Store = Store> {
|
|
|
160
164
|
*/
|
|
161
165
|
protected originalData: Map<number, {
|
|
162
166
|
data?: Uint8Array;
|
|
163
|
-
offset
|
|
167
|
+
offset?: number;
|
|
164
168
|
}[]>;
|
|
165
169
|
/**
|
|
166
170
|
* List of keys modified in this transaction, if any.
|
|
@@ -189,9 +193,9 @@ export declare class WrappedTransaction<T extends Store = Store> {
|
|
|
189
193
|
/**
|
|
190
194
|
* Marks an id as modified, and stashes its value if it has not been stashed already.
|
|
191
195
|
*/
|
|
192
|
-
protected markModified(id: number, offset
|
|
196
|
+
protected markModified(id: number, offset?: number, length?: number): Promise<void>;
|
|
193
197
|
/**
|
|
194
198
|
* Marks an id as modified, and stashes its value if it has not been stashed already.
|
|
195
199
|
*/
|
|
196
|
-
protected markModifiedSync(id: number, offset
|
|
200
|
+
protected markModifiedSync(id: number, offset?: number, length?: number): void;
|
|
197
201
|
}
|
|
@@ -126,22 +126,22 @@ export class WrappedTransaction {
|
|
|
126
126
|
this.stash(id);
|
|
127
127
|
return data;
|
|
128
128
|
}
|
|
129
|
-
async set(id, view, offset
|
|
129
|
+
async set(id, view, offset) {
|
|
130
130
|
await this.markModified(id, offset, view.byteLength);
|
|
131
131
|
const buffer = view instanceof Uint8Array ? view : new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
132
132
|
await this.raw.set(id, buffer, offset);
|
|
133
133
|
}
|
|
134
|
-
setSync(id, view, offset
|
|
134
|
+
setSync(id, view, offset) {
|
|
135
135
|
this.markModifiedSync(id, offset, view.byteLength);
|
|
136
136
|
const buffer = view instanceof Uint8Array ? view : new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
137
137
|
this.raw.setSync(id, buffer, offset);
|
|
138
138
|
}
|
|
139
139
|
async remove(id) {
|
|
140
|
-
await this.markModified(id
|
|
140
|
+
await this.markModified(id);
|
|
141
141
|
await this.raw.remove(id);
|
|
142
142
|
}
|
|
143
143
|
removeSync(id) {
|
|
144
|
-
this.markModifiedSync(id
|
|
144
|
+
this.markModifiedSync(id);
|
|
145
145
|
this.raw.removeSync(id);
|
|
146
146
|
}
|
|
147
147
|
commit() {
|
|
@@ -201,7 +201,7 @@ export class WrappedTransaction {
|
|
|
201
201
|
* prevent needless `get` requests if the program modifies the data later
|
|
202
202
|
* on during the transaction.
|
|
203
203
|
*/
|
|
204
|
-
stash(id, data, offset
|
|
204
|
+
stash(id, data, offset) {
|
|
205
205
|
this.originalData.getOrInsertComputed(id, () => []).push({ data, offset });
|
|
206
206
|
}
|
|
207
207
|
/**
|
|
@@ -209,9 +209,9 @@ export class WrappedTransaction {
|
|
|
209
209
|
*/
|
|
210
210
|
async markModified(id, offset, length) {
|
|
211
211
|
this.modifiedKeys.add(id);
|
|
212
|
-
const end = length ? offset + length : undefined;
|
|
212
|
+
const end = offset !== undefined && length ? offset + length : undefined;
|
|
213
213
|
try {
|
|
214
|
-
this.stash(id, await this.raw.get(id, offset, end), offset);
|
|
214
|
+
this.stash(id, await this.raw.get(id, offset ?? 0, end), offset);
|
|
215
215
|
}
|
|
216
216
|
catch (e) {
|
|
217
217
|
if (!(this.raw instanceof AsyncTransaction))
|
|
@@ -225,7 +225,7 @@ export class WrappedTransaction {
|
|
|
225
225
|
const resource = tx._cached(id);
|
|
226
226
|
if (!resource)
|
|
227
227
|
throw e;
|
|
228
|
-
for (const range of resource.cached(offset, end ?? offset)) {
|
|
228
|
+
for (const range of resource.cached(offset ?? 0, end ?? offset ?? 0)) {
|
|
229
229
|
this.stash(id, await this.raw.get(id, range.start, range.end), range.start);
|
|
230
230
|
}
|
|
231
231
|
}
|
|
@@ -235,9 +235,9 @@ export class WrappedTransaction {
|
|
|
235
235
|
*/
|
|
236
236
|
markModifiedSync(id, offset, length) {
|
|
237
237
|
this.modifiedKeys.add(id);
|
|
238
|
-
const end = length ? offset + length : undefined;
|
|
238
|
+
const end = offset !== undefined && length ? offset + length : undefined;
|
|
239
239
|
try {
|
|
240
|
-
this.stash(id, this.raw.getSync(id, offset, end), offset);
|
|
240
|
+
this.stash(id, this.raw.getSync(id, offset ?? 0, end), offset);
|
|
241
241
|
}
|
|
242
242
|
catch (e) {
|
|
243
243
|
if (!(this.raw instanceof AsyncTransaction))
|
|
@@ -251,7 +251,7 @@ export class WrappedTransaction {
|
|
|
251
251
|
const resource = tx._cached(id);
|
|
252
252
|
if (!resource)
|
|
253
253
|
throw e;
|
|
254
|
-
for (const range of resource.cached(offset, end ?? offset)) {
|
|
254
|
+
for (const range of resource.cached(offset ?? 0, end ?? offset ?? 0)) {
|
|
255
255
|
this.stash(id, this.raw.getSync(id, range.start, range.end), range.start);
|
|
256
256
|
}
|
|
257
257
|
}
|
|
@@ -28,6 +28,8 @@ export interface FSContext {
|
|
|
28
28
|
pwd: string;
|
|
29
29
|
/** The credentials of the context, used for access checks */
|
|
30
30
|
readonly credentials: Credentials;
|
|
31
|
+
/** Permission bits cleared from the mode of newly created files and directories */
|
|
32
|
+
umask: number;
|
|
31
33
|
/** A map of open file descriptors to their handles */
|
|
32
34
|
readonly descriptors: Map<number, Handle>;
|
|
33
35
|
/** The parent context, if any. */
|
|
@@ -66,6 +68,7 @@ export interface ContextInit {
|
|
|
66
68
|
root?: string;
|
|
67
69
|
pwd?: string;
|
|
68
70
|
credentials?: CredentialsInit;
|
|
71
|
+
umask?: number;
|
|
69
72
|
mounts?: Record<string, FileSystem>;
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
@@ -75,6 +78,8 @@ export interface ContextInit {
|
|
|
75
78
|
*/
|
|
76
79
|
export declare const defaultContext: FSContext;
|
|
77
80
|
export declare function contextOf($: unknown): FSContext;
|
|
81
|
+
/** Clears the permission bits of `mode` that are set in the context's umask. */
|
|
82
|
+
export declare function applyUmask($: unknown, mode: number): number;
|
|
78
83
|
/**
|
|
79
84
|
* Create a blank FS Context
|
|
80
85
|
* @internal
|
|
@@ -26,6 +26,7 @@ export const defaultContext = {
|
|
|
26
26
|
root: '/',
|
|
27
27
|
pwd: '/',
|
|
28
28
|
credentials: createCredentials({ uid: 0, gid: 0 }),
|
|
29
|
+
umask: 0,
|
|
29
30
|
descriptors: new Map(),
|
|
30
31
|
parent: null,
|
|
31
32
|
children: [],
|
|
@@ -34,6 +35,10 @@ export const defaultContext = {
|
|
|
34
35
|
export function contextOf($) {
|
|
35
36
|
return isContext($) ? $ : defaultContext;
|
|
36
37
|
}
|
|
38
|
+
/** Clears the permission bits of `mode` that are set in the context's umask. */
|
|
39
|
+
export function applyUmask($, mode) {
|
|
40
|
+
return mode & ~(contextOf($).umask & 0o777);
|
|
41
|
+
}
|
|
37
42
|
// 1 is reserved for the global/default context
|
|
38
43
|
let _nextId = 2;
|
|
39
44
|
/**
|
|
@@ -46,13 +51,14 @@ let _nextId = 2;
|
|
|
46
51
|
*/
|
|
47
52
|
export function createChildContext(parent, init = {}) {
|
|
48
53
|
assertContext(parent);
|
|
49
|
-
const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials), mounts } = init;
|
|
54
|
+
const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials), umask = parent.umask, mounts } = init;
|
|
50
55
|
const ctx = {
|
|
51
56
|
[kIsContext]: true,
|
|
52
57
|
id: _nextId++,
|
|
53
58
|
root,
|
|
54
59
|
pwd,
|
|
55
60
|
credentials: createCredentials(credentials),
|
|
61
|
+
umask,
|
|
56
62
|
descriptors: new Map(),
|
|
57
63
|
parent: parent,
|
|
58
64
|
children: [],
|
package/dist/mixins/async.js
CHANGED
|
@@ -63,6 +63,12 @@ import { _asyncFSKeys } from './shared.js';
|
|
|
63
63
|
function isInLoop(key, error = new Error()) {
|
|
64
64
|
return new RegExp(`__patched__[^]*(__patched__|\\b${key}Sync\\b)`).test(error.stack ?? '');
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Whether the stack has a queued replay below the innermost patched method.
|
|
68
|
+
*/
|
|
69
|
+
function isReplay() {
|
|
70
|
+
return /__patched__[^]*__replay__/.test(new Error().stack ?? '');
|
|
71
|
+
}
|
|
66
72
|
/**
|
|
67
73
|
* Async() implements synchronous methods on an asynchronous file system
|
|
68
74
|
*
|
|
@@ -88,10 +94,21 @@ export function Async(FS) {
|
|
|
88
94
|
return this.sync();
|
|
89
95
|
}
|
|
90
96
|
_promise = Promise.resolve();
|
|
97
|
+
/** How many queued operations have not finished */
|
|
98
|
+
_pending = 0;
|
|
91
99
|
/** The async methods before patching, which do not update the sync cache */
|
|
92
100
|
_unpatched = {};
|
|
93
101
|
_async(thunk) {
|
|
94
|
-
this.
|
|
102
|
+
this._pending++;
|
|
103
|
+
const replay = async function __replay__() {
|
|
104
|
+
try {
|
|
105
|
+
await thunk();
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
this._pending--;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
this._promise = this._promise.finally(replay.bind(this));
|
|
95
112
|
}
|
|
96
113
|
_isInitialized = false;
|
|
97
114
|
/** Tracks how many updates to the sync. cache we skipped during initialization */
|
|
@@ -261,17 +278,19 @@ export function Async(FS) {
|
|
|
261
278
|
}
|
|
262
279
|
/**
|
|
263
280
|
* @internal
|
|
264
|
-
* Patch all async methods to
|
|
281
|
+
* Patch all async methods to wait for queued operations,
|
|
282
|
+
* and methods that change the file system to also call their synchronous counterparts unless called from themselves (either sync or async)
|
|
265
283
|
*/
|
|
266
284
|
_patchAsync() {
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
for (const key of toPatch) {
|
|
285
|
+
const reads = ['read', 'readdir', 'stat', 'exists'];
|
|
286
|
+
for (const key of _asyncFSKeys) {
|
|
270
287
|
// TS does not narrow the union based on the key
|
|
271
288
|
const originalMethod = this[key].bind(this);
|
|
272
289
|
const fn = async function __patched__(...args) {
|
|
290
|
+
if (this._pending && !isReplay())
|
|
291
|
+
await this._promise.catch(() => { });
|
|
273
292
|
const result = await originalMethod(...args);
|
|
274
|
-
if (isInLoop(key))
|
|
293
|
+
if (reads.includes(key) || isInLoop(key))
|
|
275
294
|
return result;
|
|
276
295
|
if (!this._isInitialized) {
|
|
277
296
|
this._skippedCacheUpdates++;
|
|
@@ -293,7 +312,7 @@ export function Async(FS) {
|
|
|
293
312
|
this._unpatched[key] = originalMethod;
|
|
294
313
|
this[key] = fn;
|
|
295
314
|
}
|
|
296
|
-
debug(`Async: patched ${
|
|
315
|
+
debug(`Async: patched ${_asyncFSKeys.length} methods`);
|
|
297
316
|
}
|
|
298
317
|
}
|
|
299
318
|
return AsyncFS;
|
package/dist/node/promises.js
CHANGED
|
@@ -780,130 +780,33 @@ export async function readlink(path, options) {
|
|
|
780
780
|
}
|
|
781
781
|
readlink;
|
|
782
782
|
export async function chown(path, uid, gid) {
|
|
783
|
-
|
|
784
|
-
try {
|
|
785
|
-
const handle = __addDisposableResource(env_8, await open.call(this, path, 'r+'), true);
|
|
786
|
-
await handle.chown(uid, gid);
|
|
787
|
-
}
|
|
788
|
-
catch (e_8) {
|
|
789
|
-
env_8.error = e_8;
|
|
790
|
-
env_8.hasError = true;
|
|
791
|
-
}
|
|
792
|
-
finally {
|
|
793
|
-
const result_6 = __disposeResources(env_8);
|
|
794
|
-
if (result_6)
|
|
795
|
-
await result_6;
|
|
796
|
-
}
|
|
783
|
+
await _async.chown.call(this, path, uid, gid, false);
|
|
797
784
|
}
|
|
798
785
|
chown;
|
|
799
786
|
export async function lchown(path, uid, gid) {
|
|
800
|
-
|
|
801
|
-
try {
|
|
802
|
-
const handle = __addDisposableResource(env_9, await _async.open(this, path, {
|
|
803
|
-
flag: 'r+',
|
|
804
|
-
mode: 0o644,
|
|
805
|
-
preserveSymlinks: true,
|
|
806
|
-
allowDirectory: true,
|
|
807
|
-
}), true);
|
|
808
|
-
await handle.chown(uid, gid);
|
|
809
|
-
}
|
|
810
|
-
catch (e_9) {
|
|
811
|
-
env_9.error = e_9;
|
|
812
|
-
env_9.hasError = true;
|
|
813
|
-
}
|
|
814
|
-
finally {
|
|
815
|
-
const result_7 = __disposeResources(env_9);
|
|
816
|
-
if (result_7)
|
|
817
|
-
await result_7;
|
|
818
|
-
}
|
|
787
|
+
await _async.chown.call(this, path, uid, gid, true);
|
|
819
788
|
}
|
|
820
789
|
lchown;
|
|
821
790
|
export async function chmod(path, mode) {
|
|
822
|
-
|
|
823
|
-
try {
|
|
824
|
-
const handle = __addDisposableResource(env_10, await open.call(this, path, 'r+').catch(rethrow({ syscall: 'chmod', path: normalizePath.call(this, path) })), true);
|
|
825
|
-
await handle.chmod(mode);
|
|
826
|
-
}
|
|
827
|
-
catch (e_10) {
|
|
828
|
-
env_10.error = e_10;
|
|
829
|
-
env_10.hasError = true;
|
|
830
|
-
}
|
|
831
|
-
finally {
|
|
832
|
-
const result_8 = __disposeResources(env_10);
|
|
833
|
-
if (result_8)
|
|
834
|
-
await result_8;
|
|
835
|
-
}
|
|
791
|
+
await _async.chmod.call(this, path, mode, false);
|
|
836
792
|
}
|
|
837
793
|
chmod;
|
|
838
794
|
export async function lchmod(path, mode) {
|
|
839
|
-
|
|
840
|
-
try {
|
|
841
|
-
mode = normalizeMode(mode);
|
|
842
|
-
const handle = __addDisposableResource(env_11, await _async.open(this, path, {
|
|
843
|
-
flag: 'r+',
|
|
844
|
-
mode: 0o644,
|
|
845
|
-
preserveSymlinks: true,
|
|
846
|
-
allowDirectory: true,
|
|
847
|
-
}), true);
|
|
848
|
-
await handle.chmod(mode);
|
|
849
|
-
}
|
|
850
|
-
catch (e_11) {
|
|
851
|
-
env_11.error = e_11;
|
|
852
|
-
env_11.hasError = true;
|
|
853
|
-
}
|
|
854
|
-
finally {
|
|
855
|
-
const result_9 = __disposeResources(env_11);
|
|
856
|
-
if (result_9)
|
|
857
|
-
await result_9;
|
|
858
|
-
}
|
|
795
|
+
await _async.chmod.call(this, path, mode, true);
|
|
859
796
|
}
|
|
860
797
|
lchmod;
|
|
861
798
|
/**
|
|
862
799
|
* Change file timestamps of the file referenced by the supplied path.
|
|
863
800
|
*/
|
|
864
801
|
export async function utimes(path, atime, mtime) {
|
|
865
|
-
|
|
866
|
-
try {
|
|
867
|
-
const handle = __addDisposableResource(env_12, await _async.open(this, path, {
|
|
868
|
-
flag: 'r+',
|
|
869
|
-
allowDirectory: true,
|
|
870
|
-
}), true);
|
|
871
|
-
await handle.utimes(normalizeTime(atime), normalizeTime(mtime));
|
|
872
|
-
}
|
|
873
|
-
catch (e_12) {
|
|
874
|
-
env_12.error = e_12;
|
|
875
|
-
env_12.hasError = true;
|
|
876
|
-
}
|
|
877
|
-
finally {
|
|
878
|
-
const result_10 = __disposeResources(env_12);
|
|
879
|
-
if (result_10)
|
|
880
|
-
await result_10;
|
|
881
|
-
}
|
|
802
|
+
await _async.utimes.call(this, path, atime, mtime, false);
|
|
882
803
|
}
|
|
883
804
|
utimes;
|
|
884
805
|
/**
|
|
885
806
|
* Change file timestamps of the file referenced by the supplied path.
|
|
886
807
|
*/
|
|
887
808
|
export async function lutimes(path, atime, mtime) {
|
|
888
|
-
|
|
889
|
-
try {
|
|
890
|
-
const handle = __addDisposableResource(env_13, await _async.open(this, path, {
|
|
891
|
-
flag: 'r+',
|
|
892
|
-
mode: 0o644,
|
|
893
|
-
preserveSymlinks: true,
|
|
894
|
-
allowDirectory: true,
|
|
895
|
-
}), true);
|
|
896
|
-
await handle.utimes(normalizeTime(atime), normalizeTime(mtime));
|
|
897
|
-
}
|
|
898
|
-
catch (e_13) {
|
|
899
|
-
env_13.error = e_13;
|
|
900
|
-
env_13.hasError = true;
|
|
901
|
-
}
|
|
902
|
-
finally {
|
|
903
|
-
const result_11 = __disposeResources(env_13);
|
|
904
|
-
if (result_11)
|
|
905
|
-
await result_11;
|
|
906
|
-
}
|
|
809
|
+
await _async.utimes.call(this, path, atime, mtime, true);
|
|
907
810
|
}
|
|
908
811
|
lutimes;
|
|
909
812
|
export async function realpath(path, options) {
|
package/dist/node/sync.js
CHANGED
|
@@ -453,60 +453,33 @@ export function readlinkSync(path, options) {
|
|
|
453
453
|
}
|
|
454
454
|
readlinkSync;
|
|
455
455
|
export function chownSync(path, uid, gid) {
|
|
456
|
-
|
|
457
|
-
try {
|
|
458
|
-
const handle = __addDisposableResource(env_7, _sync.open(this, path, { flag: 'r+', mode: constants.F_OK }), false);
|
|
459
|
-
handle.chownSync(uid, gid);
|
|
460
|
-
}
|
|
461
|
-
catch (e_7) {
|
|
462
|
-
env_7.error = e_7;
|
|
463
|
-
env_7.hasError = true;
|
|
464
|
-
}
|
|
465
|
-
finally {
|
|
466
|
-
__disposeResources(env_7);
|
|
467
|
-
}
|
|
456
|
+
_sync.chown.call(this, path, uid, gid);
|
|
468
457
|
}
|
|
469
458
|
chownSync;
|
|
470
459
|
export function lchownSync(path, uid, gid) {
|
|
471
|
-
|
|
472
|
-
fchownSync.call(this, fd, uid, gid);
|
|
473
|
-
closeSync.call(this, fd);
|
|
460
|
+
_sync.chown.call(this, path, uid, gid, true);
|
|
474
461
|
}
|
|
475
462
|
lchownSync;
|
|
476
463
|
export function chmodSync(path, mode) {
|
|
477
|
-
|
|
478
|
-
try {
|
|
479
|
-
fd = openSync.call(this, path, 'r+');
|
|
480
|
-
}
|
|
481
|
-
catch (e) {
|
|
482
|
-
throw setUVMessage(Object.assign(e, { syscall: 'chmod', path: normalizePath.call(this, path) }));
|
|
483
|
-
}
|
|
484
|
-
fchmodSync.call(this, fd, mode);
|
|
485
|
-
closeSync.call(this, fd);
|
|
464
|
+
_sync.chmod.call(this, path, mode);
|
|
486
465
|
}
|
|
487
466
|
chmodSync;
|
|
488
467
|
export function lchmodSync(path, mode) {
|
|
489
|
-
|
|
490
|
-
fchmodSync.call(this, fd, mode);
|
|
491
|
-
closeSync.call(this, fd);
|
|
468
|
+
_sync.chmod.call(this, path, mode, true);
|
|
492
469
|
}
|
|
493
470
|
lchmodSync;
|
|
494
471
|
/**
|
|
495
472
|
* Change file timestamps of the file referenced by the supplied path.
|
|
496
473
|
*/
|
|
497
474
|
export function utimesSync(path, atime, mtime) {
|
|
498
|
-
|
|
499
|
-
futimesSync.call(this, fd, atime, mtime);
|
|
500
|
-
closeSync.call(this, fd);
|
|
475
|
+
_sync.utimes.call(this, path, atime, mtime);
|
|
501
476
|
}
|
|
502
477
|
utimesSync;
|
|
503
478
|
/**
|
|
504
479
|
* Change file timestamps of the file referenced by the supplied path.
|
|
505
480
|
*/
|
|
506
481
|
export function lutimesSync(path, atime, mtime) {
|
|
507
|
-
|
|
508
|
-
futimesSync.call(this, fd, atime, mtime);
|
|
509
|
-
closeSync.call(this, fd);
|
|
482
|
+
_sync.utimes.call(this, path, atime, mtime, true);
|
|
510
483
|
}
|
|
511
484
|
lutimesSync;
|
|
512
485
|
export function realpathSync(path, options) {
|