@zenfs/core 0.6.0 → 0.7.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/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 -13
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +2 -2
- 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 +0 -1
- package/dist/index.js +0 -1
- package/dist/utils.d.ts +23 -3
- package/dist/utils.js +6 -3
- package/package.json +1 -1
- package/readme.md +18 -39
- package/dist/backends/AsyncMirror.d.ts +0 -126
- package/dist/backends/AsyncMirror.js +0 -253
package/dist/config.d.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { FileSystem } from './filesystem.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Configuration for a specific mount point
|
|
5
5
|
*/
|
|
6
|
-
export type MountConfiguration<FS extends FileSystem = FileSystem> = FS | BackendConfiguration<FS> | Backend<FS>;
|
|
6
|
+
export type MountConfiguration<FS extends FileSystem = FileSystem, TOptions extends object = object> = FS | BackendConfiguration<FS, TOptions> | Backend<FS, TOptions>;
|
|
7
7
|
/**
|
|
8
8
|
* Retrieve a file system with the given configuration.
|
|
9
9
|
* @param config A BackendConfig object.
|
|
10
10
|
*/
|
|
11
|
-
export declare function resolveMountConfig<FS extends FileSystem>(config: MountConfiguration<FS>, _depth?: number): Promise<FS>;
|
|
11
|
+
export declare function resolveMountConfig<FS extends FileSystem, TOptions extends object = object>(config: MountConfiguration<FS, TOptions>, _depth?: number): Promise<FS>;
|
|
12
12
|
/**
|
|
13
13
|
*A mapping of mount points to their configurations
|
|
14
14
|
*/
|
package/dist/file.d.ts
CHANGED
|
@@ -187,12 +187,9 @@ export declare abstract class File {
|
|
|
187
187
|
* An implementation of the File interface that operates on a file that is
|
|
188
188
|
* completely in-memory. PreloadFiles are backed by a Uint8Array.
|
|
189
189
|
*
|
|
190
|
-
* This is also an abstract class, as it lacks an implementation of 'sync' and
|
|
191
|
-
* 'close'. Each filesystem that wishes to use this file representation must
|
|
192
|
-
* extend this class and implement those two methods.
|
|
193
190
|
* @todo 'close' lever that disables functionality once closed.
|
|
194
191
|
*/
|
|
195
|
-
export declare
|
|
192
|
+
export declare class PreloadFile<FS extends FileSystem> extends File {
|
|
196
193
|
/**
|
|
197
194
|
* The file system that created the file.
|
|
198
195
|
*/
|
|
@@ -246,6 +243,10 @@ export declare abstract class PreloadFile<FS extends FileSystem> extends File {
|
|
|
246
243
|
* @param newPos new position
|
|
247
244
|
*/
|
|
248
245
|
set position(newPos: number);
|
|
246
|
+
sync(): Promise<void>;
|
|
247
|
+
syncSync(): void;
|
|
248
|
+
close(): Promise<void>;
|
|
249
|
+
closeSync(): void;
|
|
249
250
|
/**
|
|
250
251
|
* Asynchronous `stat`.
|
|
251
252
|
*/
|
|
@@ -350,16 +351,6 @@ export declare abstract class PreloadFile<FS extends FileSystem> extends File {
|
|
|
350
351
|
_setType(type: FileType): Promise<void>;
|
|
351
352
|
_setTypeSync(type: FileType): void;
|
|
352
353
|
}
|
|
353
|
-
/**
|
|
354
|
-
* For synchronous file systems
|
|
355
|
-
*/
|
|
356
|
-
export declare class SyncFile<FS extends FileSystem> extends PreloadFile<FS> {
|
|
357
|
-
constructor(_fs: FS, _path: string, _flag: string, _stat: Stats, contents?: Uint8Array);
|
|
358
|
-
sync(): Promise<void>;
|
|
359
|
-
syncSync(): void;
|
|
360
|
-
close(): Promise<void>;
|
|
361
|
-
closeSync(): void;
|
|
362
|
-
}
|
|
363
354
|
/**
|
|
364
355
|
* For the filesystems which do not sync to anything..
|
|
365
356
|
*/
|
package/dist/file.js
CHANGED
|
@@ -154,9 +154,6 @@ export class File {
|
|
|
154
154
|
* An implementation of the File interface that operates on a file that is
|
|
155
155
|
* completely in-memory. PreloadFiles are backed by a Uint8Array.
|
|
156
156
|
*
|
|
157
|
-
* This is also an abstract class, as it lacks an implementation of 'sync' and
|
|
158
|
-
* 'close'. Each filesystem that wishes to use this file representation must
|
|
159
|
-
* extend this class and implement those two methods.
|
|
160
157
|
* @todo 'close' lever that disables functionality once closed.
|
|
161
158
|
*/
|
|
162
159
|
export class PreloadFile extends File {
|
|
@@ -230,6 +227,26 @@ export class PreloadFile extends File {
|
|
|
230
227
|
set position(newPos) {
|
|
231
228
|
this._position = newPos;
|
|
232
229
|
}
|
|
230
|
+
async sync() {
|
|
231
|
+
if (!this.isDirty()) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
await this.fs.sync(this.path, this._buffer, this.stats);
|
|
235
|
+
this._dirty = false;
|
|
236
|
+
}
|
|
237
|
+
syncSync() {
|
|
238
|
+
if (!this.isDirty()) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
this.fs.syncSync(this.path, this._buffer, this.stats);
|
|
242
|
+
this._dirty = false;
|
|
243
|
+
}
|
|
244
|
+
async close() {
|
|
245
|
+
await this.sync();
|
|
246
|
+
}
|
|
247
|
+
closeSync() {
|
|
248
|
+
this.syncSync();
|
|
249
|
+
}
|
|
233
250
|
/**
|
|
234
251
|
* Asynchronous `stat`.
|
|
235
252
|
*/
|
|
@@ -248,7 +265,7 @@ export class PreloadFile extends File {
|
|
|
248
265
|
*/
|
|
249
266
|
truncate(len) {
|
|
250
267
|
this.truncateSync(len);
|
|
251
|
-
if (isSynchronous(this.flag)
|
|
268
|
+
if (isSynchronous(this.flag)) {
|
|
252
269
|
return this.sync();
|
|
253
270
|
}
|
|
254
271
|
}
|
|
@@ -266,7 +283,7 @@ export class PreloadFile extends File {
|
|
|
266
283
|
const buf = new Uint8Array(len - this._buffer.length);
|
|
267
284
|
// Write will set stats.size for us.
|
|
268
285
|
this.writeSync(buf, 0, buf.length, this._buffer.length);
|
|
269
|
-
if (isSynchronous(this.flag)
|
|
286
|
+
if (isSynchronous(this.flag)) {
|
|
270
287
|
this.syncSync();
|
|
271
288
|
}
|
|
272
289
|
return;
|
|
@@ -274,7 +291,7 @@ export class PreloadFile extends File {
|
|
|
274
291
|
this.stats.size = len;
|
|
275
292
|
// Truncate buffer to 'len'.
|
|
276
293
|
this._buffer = this._buffer.subarray(0, len);
|
|
277
|
-
if (isSynchronous(this.flag)
|
|
294
|
+
if (isSynchronous(this.flag)) {
|
|
278
295
|
this.syncSync();
|
|
279
296
|
}
|
|
280
297
|
}
|
|
@@ -445,29 +462,6 @@ export class PreloadFile extends File {
|
|
|
445
462
|
this.syncSync();
|
|
446
463
|
}
|
|
447
464
|
}
|
|
448
|
-
/**
|
|
449
|
-
* For synchronous file systems
|
|
450
|
-
*/
|
|
451
|
-
export class SyncFile extends PreloadFile {
|
|
452
|
-
constructor(_fs, _path, _flag, _stat, contents) {
|
|
453
|
-
super(_fs, _path, _flag, _stat, contents);
|
|
454
|
-
}
|
|
455
|
-
async sync() {
|
|
456
|
-
this.syncSync();
|
|
457
|
-
}
|
|
458
|
-
syncSync() {
|
|
459
|
-
if (this.isDirty()) {
|
|
460
|
-
this.fs.syncSync(this.path, this._buffer, this.stats);
|
|
461
|
-
this.resetDirty();
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
async close() {
|
|
465
|
-
this.closeSync();
|
|
466
|
-
}
|
|
467
|
-
closeSync() {
|
|
468
|
-
this.syncSync();
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
465
|
/**
|
|
472
466
|
* For the filesystems which do not sync to anything..
|
|
473
467
|
*/
|
package/dist/filesystem.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ApiError } from './ApiError.js';
|
|
2
2
|
import type { Stats } from './stats.js';
|
|
3
|
-
import type
|
|
4
|
-
import type
|
|
3
|
+
import { type File } from './file.js';
|
|
4
|
+
import { type Cred } from './cred.js';
|
|
5
5
|
export type NoArgCallback = (e?: ApiError) => unknown;
|
|
6
6
|
export type TwoArgCallback<T> = (e?: ApiError, rv?: T) => unknown;
|
|
7
7
|
export type ThreeArgCallback<T, U> = (e?: ApiError, arg1?: T, arg2?: U) => unknown;
|
|
@@ -18,10 +18,6 @@ export interface FileSystemMetadata {
|
|
|
18
18
|
* Wheter the FS is readonly or not
|
|
19
19
|
*/
|
|
20
20
|
readonly: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Does the FS support synchronous operations
|
|
23
|
-
*/
|
|
24
|
-
synchronous: boolean;
|
|
25
21
|
/**
|
|
26
22
|
* Does the FS support properties
|
|
27
23
|
*/
|
|
@@ -46,6 +42,9 @@ export interface FileSystemMetadata {
|
|
|
46
42
|
* - All arguments are present. Any optional arguments at the Node API level have been passed in with their default values.
|
|
47
43
|
*/
|
|
48
44
|
export declare abstract class FileSystem {
|
|
45
|
+
/**
|
|
46
|
+
* Get metadata about the current file syste,
|
|
47
|
+
*/
|
|
49
48
|
metadata(): FileSystemMetadata;
|
|
50
49
|
constructor(options?: object);
|
|
51
50
|
abstract ready(): Promise<this>;
|
|
@@ -178,8 +177,13 @@ export declare function Sync<T extends abstract new (...args: any[]) => FileSyst
|
|
|
178
177
|
/**
|
|
179
178
|
* @internal
|
|
180
179
|
*/
|
|
181
|
-
declare abstract class AsyncFileSystem {
|
|
180
|
+
declare abstract class AsyncFileSystem extends FileSystem {
|
|
181
|
+
/**
|
|
182
|
+
* @hidden
|
|
183
|
+
*/
|
|
184
|
+
abstract _sync: FileSystem;
|
|
182
185
|
metadata(): FileSystemMetadata;
|
|
186
|
+
ready(): Promise<this>;
|
|
183
187
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
184
188
|
statSync(path: string, cred: Cred): Stats;
|
|
185
189
|
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
|
|
@@ -191,11 +195,23 @@ declare abstract class AsyncFileSystem {
|
|
|
191
195
|
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
192
196
|
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
193
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Async() implements synchronous methods on an asynchronous file system
|
|
200
|
+
*
|
|
201
|
+
* Implementing classes must define a protected _sync property for the synchronous file system used as a cache.
|
|
202
|
+
* by:
|
|
203
|
+
*
|
|
204
|
+
* - Performing operations over the in-memory copy, while asynchronously pipelining them
|
|
205
|
+
* to the backing store.
|
|
206
|
+
* - During application loading, the contents of the async file system can be reloaded into
|
|
207
|
+
* the synchronous store, if desired.
|
|
208
|
+
*
|
|
209
|
+
*/
|
|
194
210
|
export declare function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFileSystem) & T;
|
|
195
211
|
/**
|
|
196
212
|
* @internal
|
|
197
213
|
*/
|
|
198
|
-
declare abstract class ReadonlyFileSystem {
|
|
214
|
+
declare abstract class ReadonlyFileSystem extends FileSystem {
|
|
199
215
|
metadata(): FileSystemMetadata;
|
|
200
216
|
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
201
217
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
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
package/dist/index.js
CHANGED
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
package/readme.md
CHANGED
|
@@ -8,14 +8,10 @@ ZenFS is a fork of [BrowserFS](https://github.com/jvilk/BrowserFS).
|
|
|
8
8
|
|
|
9
9
|
## Backends
|
|
10
10
|
|
|
11
|
-
ZenFS is modular and extensible. The core includes
|
|
11
|
+
ZenFS is modular and extensible. The core includes two built-in backends:
|
|
12
12
|
|
|
13
13
|
- `InMemory`: Stores files in-memory. This is cleared when the runtime ends (e.g. a user navigating away from a web page or a Node process exiting)
|
|
14
|
-
- `Overlay`: Use read-only file system as read-write by overlaying a writable file system on top of it.
|
|
15
|
-
- `AsyncMirror`: Use an asynchronous backend synchronously. This is very helpful for asynchronous backends
|
|
16
|
-
|
|
17
|
-
> [!NOTE]
|
|
18
|
-
> When constructed, `AsyncMirror` loads the entire contents of the async file system into a synchronous backend. It performs operations on the synchronous file system and then queues them to be mirrored onto the asynchronous backend.
|
|
14
|
+
- `Overlay`: Use read-only file system as read-write by overlaying a writable file system on top of it. ([copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write))
|
|
19
15
|
|
|
20
16
|
ZenFS supports a number of other backends. Many are provided as separate packages under `@zenfs`. More backends can be defined by separate libraries by extending the `FileSystem` class and/or providing a `Backend` object.
|
|
21
17
|
|
|
@@ -74,13 +70,13 @@ await configure({
|
|
|
74
70
|
> 2. An object that has the options accepted by the backend and a `backend` property which is a `Backend` object
|
|
75
71
|
> 3. A `FileSystem` instance (_not recommended_)
|
|
76
72
|
|
|
77
|
-
Here is an example that mounts the `
|
|
73
|
+
Here is an example that mounts the `WebStorage` backend from `@zenfs/dom` on `/`:
|
|
78
74
|
|
|
79
75
|
```js
|
|
80
76
|
import { configure, fs } from '@zenfs/core';
|
|
81
|
-
import {
|
|
77
|
+
import { WebStorage } from '@zenfs/dom';
|
|
82
78
|
|
|
83
|
-
await configure({ backend:
|
|
79
|
+
await configure({ backend: WebStorage });
|
|
84
80
|
|
|
85
81
|
if (!fs.existsSync('/test.txt')) {
|
|
86
82
|
fs.writeFileSync('/test.txt', 'This will persist across reloads!');
|
|
@@ -95,50 +91,33 @@ console.log(contents);
|
|
|
95
91
|
The FS promises API is exposed as `promises`.
|
|
96
92
|
|
|
97
93
|
```js
|
|
98
|
-
import { configure
|
|
94
|
+
import { configure } from '@zenfs/core';
|
|
95
|
+
import { exists, writeFile } from '@zenfs/core/promises';
|
|
99
96
|
import { IndexedDB } from '@zenfs/dom';
|
|
100
97
|
|
|
101
98
|
await configure({ '/': IndexedDB });
|
|
102
99
|
|
|
103
|
-
const exists = await
|
|
100
|
+
const exists = await exists('/myfile.txt');
|
|
104
101
|
if (!exists) {
|
|
105
|
-
await
|
|
102
|
+
await writeFile('/myfile.txt', 'Lots of persistant data');
|
|
106
103
|
}
|
|
107
104
|
```
|
|
108
105
|
|
|
109
106
|
> [!NOTE]
|
|
110
|
-
> You can import the promises API using
|
|
111
|
-
|
|
112
|
-
>
|
|
113
|
-
>
|
|
114
|
-
|
|
115
|
-
#### Using asynchronous backends synchronously
|
|
116
|
-
|
|
117
|
-
You may have noticed that attempting to use a synchronous function on an asynchronous backend (e.g. `IndexedDB`) results in a "not supplied" error (`ENOTSUP`). If you would like to use an asynchronous backend synchronously you need to wrap it in an `AsyncMirror`:
|
|
118
|
-
|
|
119
|
-
```js
|
|
120
|
-
import { configure, fs, AsyncMirror, InMemory } from '@zenfs/core';
|
|
121
|
-
import { IndexedDB } from '@zenfs/dom';
|
|
122
|
-
|
|
123
|
-
await configure({
|
|
124
|
-
'/': {
|
|
125
|
-
backend: AsyncMirror,
|
|
126
|
-
sync: InMemory,
|
|
127
|
-
async: IndexedDB,
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
fs.writeFileSync('/persistant.txt', 'My persistant data');
|
|
132
|
-
```
|
|
107
|
+
> You can import the promises API using:
|
|
108
|
+
>
|
|
109
|
+
> 1. Exports from `@zenfs/core/promises`
|
|
110
|
+
> 2. The `promises` export from `@zenfs/core`
|
|
111
|
+
> 3. `fs.promises` on the exported `fs` from `@zenfs/core`.
|
|
133
112
|
|
|
134
113
|
#### Mounting and unmounting, creating backends
|
|
135
114
|
|
|
136
|
-
If you would like to create backends without configure (e.g. to do something dynamic at runtime), you may do so by importing the backend and calling `
|
|
115
|
+
If you would like to create backends without configure (e.g. to do something dynamic at runtime), you may do so by importing the backend and calling `resolveMountConfig` with it.
|
|
137
116
|
|
|
138
117
|
You can then mount and unmount the backend instance by using `mount` and `umount`.
|
|
139
118
|
|
|
140
119
|
```js
|
|
141
|
-
import { configure,
|
|
120
|
+
import { configure, resolveMountConfig, InMemory } from '@zenfs/core';
|
|
142
121
|
import { IndexedDB } from '@zenfs/dom';
|
|
143
122
|
import { Zip } from '@zenfs/zip';
|
|
144
123
|
|
|
@@ -150,8 +129,8 @@ await configure({
|
|
|
150
129
|
fs.mkdirSync('/mnt');
|
|
151
130
|
|
|
152
131
|
const res = await fetch('mydata.zip');
|
|
153
|
-
const
|
|
154
|
-
fs.mount('/mnt/zip',
|
|
132
|
+
const zipfs = await resolveMountConfig({ backend: Zip, zipData: await res.arrayBuffer() });
|
|
133
|
+
fs.mount('/mnt/zip', zipfs);
|
|
155
134
|
|
|
156
135
|
// do stuff with the mounted zip
|
|
157
136
|
|