@zenfs/core 0.5.12 → 0.7.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/FileIndex.d.ts +24 -28
- package/dist/FileIndex.js +2 -2
- package/dist/backends/AsyncStore.d.ts +28 -20
- package/dist/backends/AsyncStore.js +14 -37
- package/dist/backends/InMemory.d.ts +14 -2
- package/dist/backends/Overlay.d.ts +18 -14
- package/dist/backends/Overlay.js +2 -31
- package/dist/backends/SyncStore.d.ts +2 -13
- package/dist/backends/SyncStore.js +1 -25
- package/dist/backends/backend.d.ts +17 -18
- package/dist/backends/backend.js +1 -34
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +16 -12
- package/dist/config.js +49 -18
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/promises.js +7 -7
- package/dist/emulation/shared.d.ts +2 -2
- package/dist/emulation/shared.js +2 -2
- package/dist/emulation/sync.js +6 -6
- package/dist/file.d.ts +5 -14
- package/dist/file.js +23 -29
- package/dist/filesystem.d.ts +24 -8
- package/dist/filesystem.js +129 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/utils.d.ts +23 -3
- package/dist/utils.js +6 -3
- package/package.json +3 -2
- package/dist/backends/AsyncMirror.d.ts +0 -126
- package/dist/backends/AsyncMirror.js +0 -253
package/dist/filesystem.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { ApiError, ErrorCode } from './ApiError.js';
|
|
2
|
+
import { PreloadFile, parseFlag } from './file.js';
|
|
3
|
+
import { rootCred } from './cred.js';
|
|
4
|
+
import { join } from './emulation/path.js';
|
|
2
5
|
/**
|
|
3
6
|
* Structure for a filesystem. All ZenFS FileSystems must implement this.
|
|
4
7
|
*
|
|
@@ -10,11 +13,13 @@ import { ApiError, ErrorCode } from './ApiError.js';
|
|
|
10
13
|
* - All arguments are present. Any optional arguments at the Node API level have been passed in with their default values.
|
|
11
14
|
*/
|
|
12
15
|
export class FileSystem {
|
|
16
|
+
/**
|
|
17
|
+
* Get metadata about the current file syste,
|
|
18
|
+
*/
|
|
13
19
|
metadata() {
|
|
14
20
|
return {
|
|
15
21
|
name: this.constructor.name,
|
|
16
22
|
readonly: false,
|
|
17
|
-
synchronous: false,
|
|
18
23
|
supportsProperties: false,
|
|
19
24
|
totalSpace: 0,
|
|
20
25
|
freeSpace: 0,
|
|
@@ -57,9 +62,6 @@ export function Sync(FS) {
|
|
|
57
62
|
* Implements the asynchronous API in terms of the synchronous API.
|
|
58
63
|
*/
|
|
59
64
|
class _SyncFileSystem extends FS {
|
|
60
|
-
metadata() {
|
|
61
|
-
return { ...super.metadata(), synchronous: true };
|
|
62
|
-
}
|
|
63
65
|
async ready() {
|
|
64
66
|
return this;
|
|
65
67
|
}
|
|
@@ -99,44 +101,148 @@ export function Sync(FS) {
|
|
|
99
101
|
}
|
|
100
102
|
return _SyncFileSystem;
|
|
101
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Async() implements synchronous methods on an asynchronous file system
|
|
106
|
+
*
|
|
107
|
+
* Implementing classes must define a protected _sync property for the synchronous file system used as a cache.
|
|
108
|
+
* by:
|
|
109
|
+
*
|
|
110
|
+
* - Performing operations over the in-memory copy, while asynchronously pipelining them
|
|
111
|
+
* to the backing store.
|
|
112
|
+
* - During application loading, the contents of the async file system can be reloaded into
|
|
113
|
+
* the synchronous store, if desired.
|
|
114
|
+
*
|
|
115
|
+
*/
|
|
102
116
|
export function Async(FS) {
|
|
103
117
|
class _AsyncFileSystem extends FS {
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
constructor() {
|
|
119
|
+
super(...arguments);
|
|
120
|
+
/**
|
|
121
|
+
* Queue of pending asynchronous operations.
|
|
122
|
+
*/
|
|
123
|
+
this._queue = [];
|
|
124
|
+
this._queueRunning = false;
|
|
125
|
+
this._isInitialized = false;
|
|
126
|
+
}
|
|
127
|
+
async ready() {
|
|
128
|
+
await this._initialize();
|
|
129
|
+
return this;
|
|
106
130
|
}
|
|
107
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
108
131
|
renameSync(oldPath, newPath, cred) {
|
|
109
|
-
|
|
132
|
+
this._sync.renameSync(oldPath, newPath, cred);
|
|
133
|
+
this.queue('rename', oldPath, newPath, cred);
|
|
110
134
|
}
|
|
111
|
-
statSync(
|
|
112
|
-
|
|
135
|
+
statSync(p, cred) {
|
|
136
|
+
return this._sync.statSync(p, cred);
|
|
113
137
|
}
|
|
114
138
|
createFileSync(path, flag, mode, cred) {
|
|
115
|
-
|
|
139
|
+
const file = this._sync.createFileSync(path, flag, mode, cred);
|
|
140
|
+
this.queue('createFile', path, flag, mode, cred);
|
|
141
|
+
const stats = file.statSync();
|
|
142
|
+
const buffer = new Uint8Array(stats.size);
|
|
143
|
+
file.readSync(buffer);
|
|
144
|
+
return new PreloadFile(this, path, flag, stats, buffer);
|
|
116
145
|
}
|
|
117
146
|
openFileSync(path, flag, cred) {
|
|
118
|
-
|
|
147
|
+
return this._sync.openFileSync(path, flag, cred);
|
|
119
148
|
}
|
|
120
|
-
unlinkSync(
|
|
121
|
-
|
|
149
|
+
unlinkSync(p, cred) {
|
|
150
|
+
this._sync.unlinkSync(p, cred);
|
|
151
|
+
this.queue('unlink', p, cred);
|
|
122
152
|
}
|
|
123
|
-
rmdirSync(
|
|
124
|
-
|
|
153
|
+
rmdirSync(p, cred) {
|
|
154
|
+
this._sync.rmdirSync(p, cred);
|
|
155
|
+
this.queue('rmdir', p, cred);
|
|
125
156
|
}
|
|
126
|
-
mkdirSync(
|
|
127
|
-
|
|
157
|
+
mkdirSync(p, mode, cred) {
|
|
158
|
+
this._sync.mkdirSync(p, mode, cred);
|
|
159
|
+
this.queue('mkdir', p, mode, cred);
|
|
128
160
|
}
|
|
129
|
-
readdirSync(
|
|
130
|
-
|
|
161
|
+
readdirSync(p, cred) {
|
|
162
|
+
return this._sync.readdirSync(p, cred);
|
|
131
163
|
}
|
|
132
164
|
linkSync(srcpath, dstpath, cred) {
|
|
133
|
-
|
|
165
|
+
this._sync.linkSync(srcpath, dstpath, cred);
|
|
166
|
+
this.queue('link', srcpath, dstpath, cred);
|
|
134
167
|
}
|
|
135
168
|
syncSync(path, data, stats) {
|
|
136
|
-
|
|
169
|
+
this._sync.syncSync(path, data, stats);
|
|
170
|
+
this.queue('sync', path, data, stats);
|
|
171
|
+
}
|
|
172
|
+
existsSync(p, cred) {
|
|
173
|
+
return this._sync.existsSync(p, cred);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
178
|
+
async crossCopy(p) {
|
|
179
|
+
const stats = await this.stat(p, rootCred);
|
|
180
|
+
if (stats.isDirectory()) {
|
|
181
|
+
if (p !== '/') {
|
|
182
|
+
const stats = await this.stat(p, rootCred);
|
|
183
|
+
this._sync.mkdirSync(p, stats.mode, stats.cred());
|
|
184
|
+
}
|
|
185
|
+
const files = await this.readdir(p, rootCred);
|
|
186
|
+
for (const file of files) {
|
|
187
|
+
await this.crossCopy(join(p, file));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const asyncFile = await this.openFile(p, parseFlag('r'), rootCred);
|
|
192
|
+
const syncFile = this._sync.createFileSync(p, parseFlag('w'), stats.mode, rootCred);
|
|
193
|
+
try {
|
|
194
|
+
const { size } = await asyncFile.stat();
|
|
195
|
+
const buffer = new Uint8Array(size);
|
|
196
|
+
await asyncFile.read(buffer);
|
|
197
|
+
syncFile.writeSync(buffer);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
await asyncFile.close();
|
|
201
|
+
syncFile.closeSync();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Called once to load up files from async storage into sync storage.
|
|
207
|
+
*/
|
|
208
|
+
async _initialize() {
|
|
209
|
+
if (this._isInitialized) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
await this.crossCopy('/');
|
|
214
|
+
this._isInitialized = true;
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
this._isInitialized = false;
|
|
218
|
+
throw e;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* @internal
|
|
223
|
+
*/
|
|
224
|
+
async _next() {
|
|
225
|
+
if (this._queue.length == 0) {
|
|
226
|
+
this._queueRunning = false;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const [method, ...args] = this._queue.shift();
|
|
230
|
+
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
231
|
+
await this[method](...args);
|
|
232
|
+
await this._next();
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* @internal
|
|
236
|
+
*/
|
|
237
|
+
queue(...op) {
|
|
238
|
+
this._queue.push(op);
|
|
239
|
+
if (this._queueRunning) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this._queueRunning = true;
|
|
243
|
+
this._next();
|
|
137
244
|
}
|
|
138
245
|
}
|
|
139
|
-
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
140
246
|
return _AsyncFileSystem;
|
|
141
247
|
}
|
|
142
248
|
export function Readonly(FS) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * from './backends/backend.js';
|
|
2
|
-
export * from './backends/AsyncMirror.js';
|
|
3
2
|
export * from './backends/AsyncStore.js';
|
|
4
3
|
export * from './backends/InMemory.js';
|
|
5
4
|
export * from './backends/Locked.js';
|
|
@@ -15,6 +14,7 @@ export * from './inode.js';
|
|
|
15
14
|
export * from './mutex.js';
|
|
16
15
|
export * from './stats.js';
|
|
17
16
|
export * from './utils.js';
|
|
17
|
+
export * from './emulation/index.js';
|
|
18
18
|
import * as fs from './emulation/index.js';
|
|
19
19
|
export { fs };
|
|
20
20
|
export default fs;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * from './backends/backend.js';
|
|
2
|
-
export * from './backends/AsyncMirror.js';
|
|
3
2
|
export * from './backends/AsyncStore.js';
|
|
4
3
|
export * from './backends/InMemory.js';
|
|
5
4
|
export * from './backends/Locked.js';
|
|
@@ -15,6 +14,7 @@ export * from './inode.js';
|
|
|
15
14
|
export * from './mutex.js';
|
|
16
15
|
export * from './stats.js';
|
|
17
16
|
export * from './utils.js';
|
|
17
|
+
export * from './emulation/index.js';
|
|
18
18
|
import * as fs from './emulation/index.js';
|
|
19
19
|
export { fs };
|
|
20
20
|
export default fs;
|
package/dist/utils.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ export declare function mkdirpSync(p: string, mode: number, cred: Cred, fs: File
|
|
|
14
14
|
* @hidden
|
|
15
15
|
*/
|
|
16
16
|
export declare function levenshtein(a: string, b: string): number;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Waits n ms.
|
|
19
|
+
* @hidden
|
|
20
|
+
*/
|
|
18
21
|
export declare function wait(ms: number): Promise<void>;
|
|
19
22
|
/**
|
|
20
23
|
* @hidden
|
|
@@ -32,11 +35,28 @@ export declare function encode(input: string): Uint8Array;
|
|
|
32
35
|
export declare function decode(input?: Uint8Array): string;
|
|
33
36
|
/**
|
|
34
37
|
* Decodes a directory listing
|
|
35
|
-
* @
|
|
38
|
+
* @hidden
|
|
36
39
|
*/
|
|
37
40
|
export declare function decodeDirListing(data: Uint8Array): Record<string, bigint>;
|
|
38
41
|
/**
|
|
39
42
|
* Encodes a directory listing
|
|
40
|
-
* @
|
|
43
|
+
* @hidden
|
|
41
44
|
*/
|
|
42
45
|
export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
|
|
46
|
+
/**
|
|
47
|
+
* Extracts an object of properties assignable to P from an object T
|
|
48
|
+
* @hidden
|
|
49
|
+
*/
|
|
50
|
+
export type ExtractProperties<T, P> = {
|
|
51
|
+
[K in keyof T as T[K] extends infer Prop ? (Prop extends P ? K : never) : never]: T[K];
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Extract a the keys in T which are required properties
|
|
55
|
+
* @hidden
|
|
56
|
+
* @see https://stackoverflow.com/a/55247867/17637456
|
|
57
|
+
*/
|
|
58
|
+
export type RequiredKeys<T> = {
|
|
59
|
+
[K in keyof T]-?: {} extends {
|
|
60
|
+
[P in K]: T[K];
|
|
61
|
+
} ? never : K;
|
|
62
|
+
}[keyof T];
|
package/dist/utils.js
CHANGED
|
@@ -83,7 +83,10 @@ export function levenshtein(a, b) {
|
|
|
83
83
|
}
|
|
84
84
|
return dd;
|
|
85
85
|
}
|
|
86
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Waits n ms.
|
|
88
|
+
* @hidden
|
|
89
|
+
*/
|
|
87
90
|
export function wait(ms) {
|
|
88
91
|
return new Promise(resolve => {
|
|
89
92
|
setTimeout(resolve, ms);
|
|
@@ -117,7 +120,7 @@ export function decode(input) {
|
|
|
117
120
|
}
|
|
118
121
|
/**
|
|
119
122
|
* Decodes a directory listing
|
|
120
|
-
* @
|
|
123
|
+
* @hidden
|
|
121
124
|
*/
|
|
122
125
|
export function decodeDirListing(data) {
|
|
123
126
|
return JSON.parse(decode(data), (k, v) => {
|
|
@@ -129,7 +132,7 @@ export function decodeDirListing(data) {
|
|
|
129
132
|
}
|
|
130
133
|
/**
|
|
131
134
|
* Encodes a directory listing
|
|
132
|
-
* @
|
|
135
|
+
* @hidden
|
|
133
136
|
*/
|
|
134
137
|
export function encodeDirListing(data) {
|
|
135
138
|
return encode(JSON.stringify(data, (k, v) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A filesystem in your browser",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"exports": {
|
|
34
34
|
".": "./dist/index.js",
|
|
35
|
-
"./*": "./dist/*"
|
|
35
|
+
"./*": "./dist/*",
|
|
36
|
+
"./promises": "./dist/emulations/promises.js"
|
|
36
37
|
},
|
|
37
38
|
"typesVersions": {
|
|
38
39
|
"*": {
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { FileSystem, FileSystemMetadata } from '../filesystem.js';
|
|
2
|
-
import { File, PreloadFile } from '../file.js';
|
|
3
|
-
import type { Stats } from '../stats.js';
|
|
4
|
-
import { Cred } from '../cred.js';
|
|
5
|
-
import type { Backend } from './backend.js';
|
|
6
|
-
/**
|
|
7
|
-
* We define our own file to interpose on syncSync() for mirroring purposes.
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
export declare class MirrorFile extends PreloadFile<AsyncMirrorFS> {
|
|
11
|
-
constructor(fs: AsyncMirrorFS, path: string, flag: string, stat: Stats, data: Uint8Array);
|
|
12
|
-
sync(): Promise<void>;
|
|
13
|
-
syncSync(): void;
|
|
14
|
-
close(): Promise<void>;
|
|
15
|
-
closeSync(): void;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Configuration options for the AsyncMirror file system.
|
|
19
|
-
*/
|
|
20
|
-
export interface AsyncMirrorOptions {
|
|
21
|
-
/**
|
|
22
|
-
* The synchronous file system to mirror the asynchronous file system to.
|
|
23
|
-
*/
|
|
24
|
-
sync: FileSystem;
|
|
25
|
-
/**
|
|
26
|
-
* The asynchronous file system to mirror.
|
|
27
|
-
*/
|
|
28
|
-
async: FileSystem;
|
|
29
|
-
}
|
|
30
|
-
declare const AsyncMirrorFS_base: (abstract new (...args: any[]) => {
|
|
31
|
-
metadata(): FileSystemMetadata;
|
|
32
|
-
ready(): Promise<any>;
|
|
33
|
-
exists(path: string, cred: Cred): Promise<boolean>;
|
|
34
|
-
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
35
|
-
stat(path: string, cred: Cred): Promise<Stats>;
|
|
36
|
-
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
|
|
37
|
-
openFile(path: string, flag: string, cred: Cred): Promise<File>;
|
|
38
|
-
unlink(path: string, cred: Cred): Promise<void>;
|
|
39
|
-
rmdir(path: string, cred: Cred): Promise<void>;
|
|
40
|
-
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
|
|
41
|
-
readdir(path: string, cred: Cred): Promise<string[]>;
|
|
42
|
-
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
|
|
43
|
-
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
|
|
44
|
-
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
45
|
-
statSync(path: string, cred: Cred): Stats;
|
|
46
|
-
openFileSync(path: string, flag: string, cred: Cred): File;
|
|
47
|
-
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
|
|
48
|
-
unlinkSync(path: string, cred: Cred): void;
|
|
49
|
-
rmdirSync(path: string, cred: Cred): void;
|
|
50
|
-
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
51
|
-
readdirSync(path: string, cred: Cred): string[];
|
|
52
|
-
existsSync(path: string, cred: Cred): boolean;
|
|
53
|
-
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
54
|
-
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
55
|
-
}) & typeof FileSystem;
|
|
56
|
-
/**
|
|
57
|
-
* AsyncMirrorFS mirrors a synchronous filesystem into an asynchronous filesystem
|
|
58
|
-
* by:
|
|
59
|
-
*
|
|
60
|
-
* * Performing operations over the in-memory copy, while asynchronously pipelining them
|
|
61
|
-
* to the backing store.
|
|
62
|
-
* * During application loading, the contents of the async file system can be reloaded into
|
|
63
|
-
* the synchronous store, if desired.
|
|
64
|
-
*
|
|
65
|
-
* The two stores will be kept in sync. The most common use-case is to pair a synchronous
|
|
66
|
-
* in-memory filesystem with an asynchronous backing store.
|
|
67
|
-
*
|
|
68
|
-
*/
|
|
69
|
-
export declare class AsyncMirrorFS extends AsyncMirrorFS_base {
|
|
70
|
-
/**
|
|
71
|
-
* Queue of pending asynchronous operations.
|
|
72
|
-
*/
|
|
73
|
-
private _queue;
|
|
74
|
-
private _queueRunning;
|
|
75
|
-
private _sync;
|
|
76
|
-
private _async;
|
|
77
|
-
private _isInitialized;
|
|
78
|
-
private _ready;
|
|
79
|
-
ready(): Promise<this>;
|
|
80
|
-
/**
|
|
81
|
-
*
|
|
82
|
-
* Mirrors the synchronous file system into the asynchronous file system.
|
|
83
|
-
*
|
|
84
|
-
* @param sync The synchronous file system to mirror the asynchronous file system to.
|
|
85
|
-
* @param async The asynchronous file system to mirror.
|
|
86
|
-
*/
|
|
87
|
-
constructor({ sync, async }: AsyncMirrorOptions);
|
|
88
|
-
metadata(): FileSystemMetadata;
|
|
89
|
-
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
90
|
-
openFileSync(path: string, flag: string, cred: Cred): File;
|
|
91
|
-
createFileSync(path: string, flag: string, mode: number, cred: Cred): MirrorFile;
|
|
92
|
-
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
93
|
-
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
94
|
-
statSync(p: string, cred: Cred): Stats;
|
|
95
|
-
unlinkSync(p: string, cred: Cred): void;
|
|
96
|
-
rmdirSync(p: string, cred: Cred): void;
|
|
97
|
-
mkdirSync(p: string, mode: number, cred: Cred): void;
|
|
98
|
-
readdirSync(p: string, cred: Cred): string[];
|
|
99
|
-
existsSync(p: string, cred: Cred): boolean;
|
|
100
|
-
/**
|
|
101
|
-
* @internal
|
|
102
|
-
*/
|
|
103
|
-
protected crossCopyDirectory(p: string, mode: number): Promise<void>;
|
|
104
|
-
/**
|
|
105
|
-
* @internal
|
|
106
|
-
*/
|
|
107
|
-
protected crossCopyFile(p: string, mode: number): Promise<void>;
|
|
108
|
-
/**
|
|
109
|
-
* @internal
|
|
110
|
-
*/
|
|
111
|
-
protected crossCopy(p: string): Promise<void>;
|
|
112
|
-
/**
|
|
113
|
-
* Called once to load up files from async storage into sync storage.
|
|
114
|
-
*/
|
|
115
|
-
protected _initialize(): Promise<void>;
|
|
116
|
-
/**
|
|
117
|
-
* @internal
|
|
118
|
-
*/
|
|
119
|
-
private _next;
|
|
120
|
-
/**
|
|
121
|
-
* @internal
|
|
122
|
-
*/
|
|
123
|
-
private enqueue;
|
|
124
|
-
}
|
|
125
|
-
export declare const AsyncMirror: Backend<AsyncMirrorFS>;
|
|
126
|
-
export {};
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { FileSystem, Sync } from '../filesystem.js';
|
|
2
|
-
import { ApiError, ErrorCode } from '../ApiError.js';
|
|
3
|
-
import { PreloadFile, parseFlag } from '../file.js';
|
|
4
|
-
import { join } from '../emulation/path.js';
|
|
5
|
-
import { rootCred } from '../cred.js';
|
|
6
|
-
/**
|
|
7
|
-
* We define our own file to interpose on syncSync() for mirroring purposes.
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
export class MirrorFile extends PreloadFile {
|
|
11
|
-
constructor(fs, path, flag, stat, data) {
|
|
12
|
-
super(fs, path, flag, stat, data);
|
|
13
|
-
}
|
|
14
|
-
async sync() {
|
|
15
|
-
this.syncSync();
|
|
16
|
-
}
|
|
17
|
-
syncSync() {
|
|
18
|
-
if (this.isDirty()) {
|
|
19
|
-
this.fs.syncSync(this.path, this._buffer, this.stats);
|
|
20
|
-
this.resetDirty();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
async close() {
|
|
24
|
-
this.closeSync();
|
|
25
|
-
}
|
|
26
|
-
closeSync() {
|
|
27
|
-
this.syncSync();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* AsyncMirrorFS mirrors a synchronous filesystem into an asynchronous filesystem
|
|
32
|
-
* by:
|
|
33
|
-
*
|
|
34
|
-
* * Performing operations over the in-memory copy, while asynchronously pipelining them
|
|
35
|
-
* to the backing store.
|
|
36
|
-
* * During application loading, the contents of the async file system can be reloaded into
|
|
37
|
-
* the synchronous store, if desired.
|
|
38
|
-
*
|
|
39
|
-
* The two stores will be kept in sync. The most common use-case is to pair a synchronous
|
|
40
|
-
* in-memory filesystem with an asynchronous backing store.
|
|
41
|
-
*
|
|
42
|
-
*/
|
|
43
|
-
export class AsyncMirrorFS extends Sync(FileSystem) {
|
|
44
|
-
async ready() {
|
|
45
|
-
await this._ready;
|
|
46
|
-
return this;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
50
|
-
* Mirrors the synchronous file system into the asynchronous file system.
|
|
51
|
-
*
|
|
52
|
-
* @param sync The synchronous file system to mirror the asynchronous file system to.
|
|
53
|
-
* @param async The asynchronous file system to mirror.
|
|
54
|
-
*/
|
|
55
|
-
constructor({ sync, async }) {
|
|
56
|
-
super();
|
|
57
|
-
/**
|
|
58
|
-
* Queue of pending asynchronous operations.
|
|
59
|
-
*/
|
|
60
|
-
this._queue = [];
|
|
61
|
-
this._queueRunning = false;
|
|
62
|
-
this._isInitialized = false;
|
|
63
|
-
this._sync = sync;
|
|
64
|
-
this._async = async;
|
|
65
|
-
this._ready = this._initialize();
|
|
66
|
-
}
|
|
67
|
-
metadata() {
|
|
68
|
-
return {
|
|
69
|
-
...super.metadata(),
|
|
70
|
-
name: AsyncMirrorFS.name,
|
|
71
|
-
synchronous: true,
|
|
72
|
-
supportsProperties: this._sync.metadata().supportsProperties && this._async.metadata().supportsProperties,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
syncSync(path, data, stats) {
|
|
76
|
-
this._sync.syncSync(path, data, stats);
|
|
77
|
-
this.enqueue({
|
|
78
|
-
apiMethod: 'sync',
|
|
79
|
-
arguments: [path, data, stats],
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
openFileSync(path, flag, cred) {
|
|
83
|
-
return this._sync.openFileSync(path, flag, cred);
|
|
84
|
-
}
|
|
85
|
-
createFileSync(path, flag, mode, cred) {
|
|
86
|
-
const file = this._sync.createFileSync(path, flag, mode, cred);
|
|
87
|
-
this.enqueue({
|
|
88
|
-
apiMethod: 'createFile',
|
|
89
|
-
arguments: [path, flag, mode, cred],
|
|
90
|
-
});
|
|
91
|
-
const stats = file.statSync();
|
|
92
|
-
const buffer = new Uint8Array(stats.size);
|
|
93
|
-
file.readSync(buffer);
|
|
94
|
-
return new MirrorFile(this, path, flag, stats, buffer);
|
|
95
|
-
}
|
|
96
|
-
linkSync(srcpath, dstpath, cred) {
|
|
97
|
-
this._sync.linkSync(srcpath, dstpath, cred);
|
|
98
|
-
this.enqueue({
|
|
99
|
-
apiMethod: 'link',
|
|
100
|
-
arguments: [srcpath, dstpath, cred],
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
renameSync(oldPath, newPath, cred) {
|
|
104
|
-
this._sync.renameSync(oldPath, newPath, cred);
|
|
105
|
-
this.enqueue({
|
|
106
|
-
apiMethod: 'rename',
|
|
107
|
-
arguments: [oldPath, newPath, cred],
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
statSync(p, cred) {
|
|
111
|
-
return this._sync.statSync(p, cred);
|
|
112
|
-
}
|
|
113
|
-
unlinkSync(p, cred) {
|
|
114
|
-
this._sync.unlinkSync(p, cred);
|
|
115
|
-
this.enqueue({
|
|
116
|
-
apiMethod: 'unlink',
|
|
117
|
-
arguments: [p, cred],
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
rmdirSync(p, cred) {
|
|
121
|
-
this._sync.rmdirSync(p, cred);
|
|
122
|
-
this.enqueue({
|
|
123
|
-
apiMethod: 'rmdir',
|
|
124
|
-
arguments: [p, cred],
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
mkdirSync(p, mode, cred) {
|
|
128
|
-
this._sync.mkdirSync(p, mode, cred);
|
|
129
|
-
this.enqueue({
|
|
130
|
-
apiMethod: 'mkdir',
|
|
131
|
-
arguments: [p, mode, cred],
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
readdirSync(p, cred) {
|
|
135
|
-
return this._sync.readdirSync(p, cred);
|
|
136
|
-
}
|
|
137
|
-
existsSync(p, cred) {
|
|
138
|
-
return this._sync.existsSync(p, cred);
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* @internal
|
|
142
|
-
*/
|
|
143
|
-
async crossCopyDirectory(p, mode) {
|
|
144
|
-
if (p !== '/') {
|
|
145
|
-
const stats = await this._async.stat(p, rootCred);
|
|
146
|
-
this._sync.mkdirSync(p, mode, stats.cred());
|
|
147
|
-
}
|
|
148
|
-
const files = await this._async.readdir(p, rootCred);
|
|
149
|
-
for (const file of files) {
|
|
150
|
-
await this.crossCopy(join(p, file));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* @internal
|
|
155
|
-
*/
|
|
156
|
-
async crossCopyFile(p, mode) {
|
|
157
|
-
const asyncFile = await this._async.openFile(p, parseFlag('r'), rootCred);
|
|
158
|
-
const syncFile = this._sync.createFileSync(p, parseFlag('w'), mode, rootCred);
|
|
159
|
-
try {
|
|
160
|
-
const { size } = await asyncFile.stat();
|
|
161
|
-
const buffer = new Uint8Array(size);
|
|
162
|
-
await asyncFile.read(buffer);
|
|
163
|
-
syncFile.writeSync(buffer);
|
|
164
|
-
}
|
|
165
|
-
finally {
|
|
166
|
-
await asyncFile.close();
|
|
167
|
-
syncFile.closeSync();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* @internal
|
|
172
|
-
*/
|
|
173
|
-
async crossCopy(p) {
|
|
174
|
-
const stats = await this._async.stat(p, rootCred);
|
|
175
|
-
if (stats.isDirectory()) {
|
|
176
|
-
await this.crossCopyDirectory(p, stats.mode);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
await this.crossCopyFile(p, stats.mode);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Called once to load up files from async storage into sync storage.
|
|
184
|
-
*/
|
|
185
|
-
async _initialize() {
|
|
186
|
-
if (this._isInitialized) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
try {
|
|
190
|
-
await this.crossCopy('/');
|
|
191
|
-
this._isInitialized = true;
|
|
192
|
-
}
|
|
193
|
-
catch (e) {
|
|
194
|
-
this._isInitialized = false;
|
|
195
|
-
throw e;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* @internal
|
|
200
|
-
*/
|
|
201
|
-
async _next() {
|
|
202
|
-
if (this._queue.length == 0) {
|
|
203
|
-
this._queueRunning = false;
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
const op = this._queue.shift();
|
|
207
|
-
try {
|
|
208
|
-
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
209
|
-
await this._async[op.apiMethod](...op.arguments);
|
|
210
|
-
}
|
|
211
|
-
catch (e) {
|
|
212
|
-
throw new ApiError(ErrorCode.EIO, 'AsyncMirror desync: ' + e);
|
|
213
|
-
}
|
|
214
|
-
await this._next();
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* @internal
|
|
218
|
-
*/
|
|
219
|
-
enqueue(op) {
|
|
220
|
-
this._queue.push(op);
|
|
221
|
-
if (this._queueRunning) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
this._queueRunning = true;
|
|
225
|
-
this._next();
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
export const AsyncMirror = {
|
|
229
|
-
name: 'AsyncMirror',
|
|
230
|
-
options: {
|
|
231
|
-
sync: {
|
|
232
|
-
type: 'object',
|
|
233
|
-
required: true,
|
|
234
|
-
description: 'The synchronous file system to mirror the asynchronous file system to.',
|
|
235
|
-
validator: async (backend) => {
|
|
236
|
-
if ('metadata' in backend && !backend.metadata().synchronous) {
|
|
237
|
-
throw new ApiError(ErrorCode.EINVAL, '"sync" option must be a file system that supports synchronous operations');
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
},
|
|
241
|
-
async: {
|
|
242
|
-
type: 'object',
|
|
243
|
-
required: true,
|
|
244
|
-
description: 'The asynchronous file system to mirror.',
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
isAvailable() {
|
|
248
|
-
return true;
|
|
249
|
-
},
|
|
250
|
-
create(options) {
|
|
251
|
-
return new AsyncMirrorFS(options);
|
|
252
|
-
},
|
|
253
|
-
};
|