@zenfs/core 0.9.3 → 0.9.5
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/AsyncStore.d.ts +15 -1
- package/dist/backends/AsyncStore.js +22 -8
- package/dist/backends/Index.d.ts +3 -0
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +3 -3
- package/dist/filesystem.d.ts +2 -1
- package/dist/filesystem.js +17 -14
- package/dist/stats.d.ts +5 -1
- package/dist/stats.js +2 -1
- package/package.json +1 -1
- package/src/backends/AsyncStore.ts +24 -10
- package/src/filesystem.ts +21 -18
- package/src/stats.ts +6 -1
package/dist/filesystem.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export declare abstract class FileSystem {
|
|
|
39
39
|
*/
|
|
40
40
|
metadata(): FileSystemMetadata;
|
|
41
41
|
constructor(options?: object);
|
|
42
|
-
|
|
42
|
+
ready(): Promise<this>;
|
|
43
43
|
/**
|
|
44
44
|
* Asynchronous rename. No arguments other than a possible exception
|
|
45
45
|
* are given to the completion callback.
|
|
@@ -174,6 +174,7 @@ declare abstract class AsyncFileSystem extends FileSystem {
|
|
|
174
174
|
* @hidden
|
|
175
175
|
*/
|
|
176
176
|
abstract _sync: FileSystem;
|
|
177
|
+
queueDone(): Promise<void>;
|
|
177
178
|
metadata(): FileSystemMetadata;
|
|
178
179
|
ready(): Promise<this>;
|
|
179
180
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
package/dist/filesystem.js
CHANGED
|
@@ -27,6 +27,9 @@ export class FileSystem {
|
|
|
27
27
|
constructor(options) {
|
|
28
28
|
// unused
|
|
29
29
|
}
|
|
30
|
+
async ready() {
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
30
33
|
/**
|
|
31
34
|
* Test whether or not the given path exists by checking with the file system.
|
|
32
35
|
*/
|
|
@@ -57,9 +60,6 @@ export class FileSystem {
|
|
|
57
60
|
*/
|
|
58
61
|
export function Sync(FS) {
|
|
59
62
|
class _SyncFileSystem extends FS {
|
|
60
|
-
async ready() {
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
63
|
async exists(path, cred) {
|
|
64
64
|
return this.existsSync(path, cred);
|
|
65
65
|
}
|
|
@@ -116,11 +116,20 @@ export function Async(FS) {
|
|
|
116
116
|
* Queue of pending asynchronous operations.
|
|
117
117
|
*/
|
|
118
118
|
this._queue = [];
|
|
119
|
-
this._queueRunning = false;
|
|
120
119
|
this._isInitialized = false;
|
|
121
120
|
}
|
|
121
|
+
get _queueRunning() {
|
|
122
|
+
return !!this._queue.length;
|
|
123
|
+
}
|
|
124
|
+
queueDone() {
|
|
125
|
+
return new Promise(resolve => {
|
|
126
|
+
const check = () => (this._queueRunning ? setTimeout(check) : resolve());
|
|
127
|
+
check();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
122
130
|
async ready() {
|
|
123
131
|
await this._sync.ready();
|
|
132
|
+
await super.ready();
|
|
124
133
|
if (this._isInitialized) {
|
|
125
134
|
return this;
|
|
126
135
|
}
|
|
@@ -195,12 +204,11 @@ export function Async(FS) {
|
|
|
195
204
|
}
|
|
196
205
|
else {
|
|
197
206
|
const asyncFile = await this.openFile(p, parseFlag('r'), rootCred);
|
|
198
|
-
const syncFile = this._sync.createFileSync(p, parseFlag('w'), stats.mode,
|
|
207
|
+
const syncFile = this._sync.createFileSync(p, parseFlag('w'), stats.mode, stats.cred());
|
|
199
208
|
try {
|
|
200
|
-
const
|
|
201
|
-
const buffer = new Uint8Array(size);
|
|
209
|
+
const buffer = new Uint8Array(stats.size);
|
|
202
210
|
await asyncFile.read(buffer);
|
|
203
|
-
syncFile.writeSync(buffer);
|
|
211
|
+
syncFile.writeSync(buffer, 0, stats.size);
|
|
204
212
|
}
|
|
205
213
|
finally {
|
|
206
214
|
await asyncFile.close();
|
|
@@ -212,8 +220,7 @@ export function Async(FS) {
|
|
|
212
220
|
* @internal
|
|
213
221
|
*/
|
|
214
222
|
async _next() {
|
|
215
|
-
if (this.
|
|
216
|
-
this._queueRunning = false;
|
|
223
|
+
if (!this._queueRunning) {
|
|
217
224
|
return;
|
|
218
225
|
}
|
|
219
226
|
const [method, ...args] = this._queue.shift();
|
|
@@ -226,10 +233,6 @@ export function Async(FS) {
|
|
|
226
233
|
*/
|
|
227
234
|
queue(...op) {
|
|
228
235
|
this._queue.push(op);
|
|
229
|
-
if (this._queueRunning) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
this._queueRunning = true;
|
|
233
236
|
this._next();
|
|
234
237
|
}
|
|
235
238
|
}
|
package/dist/stats.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ export interface StatsLike {
|
|
|
47
47
|
* the id of the group that owns the file
|
|
48
48
|
*/
|
|
49
49
|
gid: number | bigint;
|
|
50
|
+
/**
|
|
51
|
+
* the ino
|
|
52
|
+
*/
|
|
53
|
+
ino: number | bigint;
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
56
|
* Provides information about a particular entry in the file system.
|
|
@@ -127,7 +131,7 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
|
|
|
127
131
|
/**
|
|
128
132
|
* Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
|
|
129
133
|
*/
|
|
130
|
-
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode }?: Partial<StatsLike>);
|
|
134
|
+
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino }?: Partial<StatsLike>);
|
|
131
135
|
/**
|
|
132
136
|
* @returns true if this item is a file.
|
|
133
137
|
*/
|
package/dist/stats.js
CHANGED
|
@@ -49,7 +49,7 @@ export class StatsCommon {
|
|
|
49
49
|
/**
|
|
50
50
|
* Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
|
|
51
51
|
*/
|
|
52
|
-
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode } = {}) {
|
|
52
|
+
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino } = {}) {
|
|
53
53
|
/**
|
|
54
54
|
* ID of device containing file
|
|
55
55
|
*/
|
|
@@ -91,6 +91,7 @@ export class StatsCommon {
|
|
|
91
91
|
this.uid = resolveT(uid, 0);
|
|
92
92
|
this.gid = resolveT(gid, 0);
|
|
93
93
|
this.size = this._convert(size);
|
|
94
|
+
this.ino = this._convert(ino);
|
|
94
95
|
const itemType = Number(mode) & S_IFMT || FileType.FILE;
|
|
95
96
|
if (mode) {
|
|
96
97
|
this.mode = this._convert(mode);
|
package/package.json
CHANGED
|
@@ -132,10 +132,15 @@ export interface AsyncStoreOptions {
|
|
|
132
132
|
*/
|
|
133
133
|
export class AsyncStoreFS extends Async(FileSystem) {
|
|
134
134
|
protected store: AsyncStore;
|
|
135
|
-
|
|
135
|
+
protected _cache?: LRUCache<string, Ino>;
|
|
136
|
+
private _initialized: boolean = false;
|
|
136
137
|
_sync: FileSystem;
|
|
137
138
|
|
|
138
|
-
public async ready() {
|
|
139
|
+
public async ready(): Promise<this> {
|
|
140
|
+
if (this._initialized) {
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
this._initialized = true;
|
|
139
144
|
if (this._options.lruCacheSize > 0) {
|
|
140
145
|
this._cache = new LRUCache(this._options.lruCacheSize);
|
|
141
146
|
}
|
|
@@ -165,7 +170,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
165
170
|
this._cache.reset();
|
|
166
171
|
}
|
|
167
172
|
await this.store.clear();
|
|
168
|
-
//
|
|
173
|
+
// Root always exists.
|
|
169
174
|
await this.makeRootDirectory();
|
|
170
175
|
}
|
|
171
176
|
|
|
@@ -173,6 +178,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
173
178
|
* @todo Make rename compatible with the cache.
|
|
174
179
|
*/
|
|
175
180
|
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
181
|
+
await this.queueDone();
|
|
176
182
|
const c = this._cache;
|
|
177
183
|
if (this._cache) {
|
|
178
184
|
// Clear and disable cache during renaming process.
|
|
@@ -255,6 +261,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
255
261
|
}
|
|
256
262
|
|
|
257
263
|
public async stat(p: string, cred: Cred): Promise<Stats> {
|
|
264
|
+
await this.queueDone();
|
|
258
265
|
const tx = this.store.beginTransaction();
|
|
259
266
|
const inode = await this.findINode(tx, p);
|
|
260
267
|
if (!inode) {
|
|
@@ -268,6 +275,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
268
275
|
}
|
|
269
276
|
|
|
270
277
|
public async createFile(p: string, flag: string, mode: number, cred: Cred): Promise<PreloadFile<this>> {
|
|
278
|
+
await this.queueDone();
|
|
271
279
|
const tx = this.store.beginTransaction(),
|
|
272
280
|
data = new Uint8Array(0),
|
|
273
281
|
newFile = await this.commitNewFile(tx, p, FileType.FILE, mode, cred, data);
|
|
@@ -276,6 +284,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
public async openFile(p: string, flag: string, cred: Cred): Promise<PreloadFile<this>> {
|
|
287
|
+
await this.queueDone();
|
|
279
288
|
const tx = this.store.beginTransaction(),
|
|
280
289
|
node = await this.findINode(tx, p),
|
|
281
290
|
data = await tx.get(node.ino);
|
|
@@ -289,10 +298,12 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
289
298
|
}
|
|
290
299
|
|
|
291
300
|
public async unlink(p: string, cred: Cred): Promise<void> {
|
|
301
|
+
await this.queueDone();
|
|
292
302
|
return this.removeEntry(p, false, cred);
|
|
293
303
|
}
|
|
294
304
|
|
|
295
305
|
public async rmdir(p: string, cred: Cred): Promise<void> {
|
|
306
|
+
await this.queueDone();
|
|
296
307
|
// Check first if directory is empty.
|
|
297
308
|
const list = await this.readdir(p, cred);
|
|
298
309
|
if (list.length > 0) {
|
|
@@ -302,12 +313,14 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
302
313
|
}
|
|
303
314
|
|
|
304
315
|
public async mkdir(p: string, mode: number, cred: Cred): Promise<void> {
|
|
316
|
+
await this.queueDone();
|
|
305
317
|
const tx = this.store.beginTransaction(),
|
|
306
318
|
data = encode('{}');
|
|
307
319
|
await this.commitNewFile(tx, p, FileType.DIRECTORY, mode, cred, data);
|
|
308
320
|
}
|
|
309
321
|
|
|
310
322
|
public async readdir(p: string, cred: Cred): Promise<string[]> {
|
|
323
|
+
await this.queueDone();
|
|
311
324
|
const tx = this.store.beginTransaction();
|
|
312
325
|
const node = await this.findINode(tx, p);
|
|
313
326
|
if (!node.toStats().hasAccess(R_OK, cred)) {
|
|
@@ -321,6 +334,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
321
334
|
* @todo Ensure mtime updates properly, and use that to determine if a data update is required.
|
|
322
335
|
*/
|
|
323
336
|
public async sync(p: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
337
|
+
await this.queueDone();
|
|
324
338
|
const tx = this.store.beginTransaction(),
|
|
325
339
|
// We use the _findInode helper because we actually need the INode id.
|
|
326
340
|
fileInodeId = await this._findINode(tx, dirname(p), basename(p)),
|
|
@@ -342,6 +356,7 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
342
356
|
}
|
|
343
357
|
|
|
344
358
|
public async link(existing: string, newpath: string, cred: Cred): Promise<void> {
|
|
359
|
+
await this.queueDone();
|
|
345
360
|
const tx = this.store.beginTransaction(),
|
|
346
361
|
existingDir: string = dirname(existing),
|
|
347
362
|
existingDirNode = await this.findINode(tx, existingDir);
|
|
@@ -382,14 +397,13 @@ export class AsyncStoreFS extends Async(FileSystem) {
|
|
|
382
397
|
*/
|
|
383
398
|
private async makeRootDirectory(): Promise<void> {
|
|
384
399
|
const tx = this.store.beginTransaction();
|
|
385
|
-
if ((await tx.get(rootIno))
|
|
400
|
+
if (!(await tx.get(rootIno))) {
|
|
386
401
|
// Create new inode. o777, owned by root:root
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
// If the root doesn't exist, the first random ID shouldn't exist
|
|
390
|
-
|
|
391
|
-
await tx.put(
|
|
392
|
-
await tx.put(rootIno, dirInode.data, false);
|
|
402
|
+
const inode = new Inode();
|
|
403
|
+
inode.mode = 0o777 | FileType.DIRECTORY;
|
|
404
|
+
// If the root doesn't exist, the first random ID shouldn't exist either.
|
|
405
|
+
await tx.put(inode.ino, encode('{}'), false);
|
|
406
|
+
await tx.put(rootIno, inode.data, false);
|
|
393
407
|
await tx.commit();
|
|
394
408
|
}
|
|
395
409
|
}
|
package/src/filesystem.ts
CHANGED
|
@@ -59,7 +59,9 @@ export abstract class FileSystem {
|
|
|
59
59
|
// unused
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
public
|
|
62
|
+
public async ready(): Promise<this> {
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
* Asynchronous rename. No arguments other than a possible exception
|
|
@@ -218,10 +220,6 @@ declare abstract class SyncFileSystem extends FileSystem {
|
|
|
218
220
|
*/
|
|
219
221
|
export function Sync<T extends abstract new (...args) => FileSystem>(FS: T): (abstract new (...args) => SyncFileSystem) & T {
|
|
220
222
|
abstract class _SyncFileSystem extends FS implements SyncFileSystem {
|
|
221
|
-
public async ready(): Promise<this> {
|
|
222
|
-
return this;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
223
|
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
226
224
|
return this.existsSync(path, cred);
|
|
227
225
|
}
|
|
@@ -277,6 +275,7 @@ declare abstract class AsyncFileSystem extends FileSystem {
|
|
|
277
275
|
* @hidden
|
|
278
276
|
*/
|
|
279
277
|
abstract _sync: FileSystem;
|
|
278
|
+
queueDone(): Promise<void>;
|
|
280
279
|
metadata(): FileSystemMetadata;
|
|
281
280
|
ready(): Promise<this>;
|
|
282
281
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
@@ -318,13 +317,24 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
318
317
|
* Queue of pending asynchronous operations.
|
|
319
318
|
*/
|
|
320
319
|
private _queue: AsyncOperation[] = [];
|
|
321
|
-
private _queueRunning: boolean
|
|
320
|
+
private get _queueRunning(): boolean {
|
|
321
|
+
return !!this._queue.length;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
public queueDone(): Promise<void> {
|
|
325
|
+
return new Promise(resolve => {
|
|
326
|
+
const check = () => (this._queueRunning ? setTimeout(check) : resolve());
|
|
327
|
+
check();
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
322
331
|
private _isInitialized: boolean = false;
|
|
323
332
|
|
|
324
333
|
abstract _sync: FileSystem;
|
|
325
334
|
|
|
326
335
|
public async ready(): Promise<this> {
|
|
327
336
|
await this._sync.ready();
|
|
337
|
+
await super.ready();
|
|
328
338
|
if (this._isInitialized) {
|
|
329
339
|
return this;
|
|
330
340
|
}
|
|
@@ -410,12 +420,11 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
410
420
|
}
|
|
411
421
|
} else {
|
|
412
422
|
const asyncFile = await this.openFile(p, parseFlag('r'), rootCred);
|
|
413
|
-
const syncFile = this._sync.createFileSync(p, parseFlag('w'), stats.mode,
|
|
423
|
+
const syncFile = this._sync.createFileSync(p, parseFlag('w'), stats.mode, stats.cred());
|
|
414
424
|
try {
|
|
415
|
-
const
|
|
416
|
-
const buffer = new Uint8Array(size);
|
|
425
|
+
const buffer = new Uint8Array(stats.size);
|
|
417
426
|
await asyncFile.read(buffer);
|
|
418
|
-
syncFile.writeSync(buffer);
|
|
427
|
+
syncFile.writeSync(buffer, 0, stats.size);
|
|
419
428
|
} finally {
|
|
420
429
|
await asyncFile.close();
|
|
421
430
|
syncFile.closeSync();
|
|
@@ -427,12 +436,11 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
427
436
|
* @internal
|
|
428
437
|
*/
|
|
429
438
|
private async _next(): Promise<void> {
|
|
430
|
-
if (this.
|
|
431
|
-
this._queueRunning = false;
|
|
439
|
+
if (!this._queueRunning) {
|
|
432
440
|
return;
|
|
433
441
|
}
|
|
434
442
|
|
|
435
|
-
const [method, ...args] = this._queue.shift()
|
|
443
|
+
const [method, ...args] = this._queue.shift();
|
|
436
444
|
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
437
445
|
await this[method](...args);
|
|
438
446
|
await this._next();
|
|
@@ -443,11 +451,6 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
443
451
|
*/
|
|
444
452
|
private queue(...op: AsyncOperation) {
|
|
445
453
|
this._queue.push(op);
|
|
446
|
-
if (this._queueRunning) {
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
this._queueRunning = true;
|
|
451
454
|
this._next();
|
|
452
455
|
}
|
|
453
456
|
}
|
package/src/stats.ts
CHANGED
|
@@ -49,6 +49,10 @@ export interface StatsLike {
|
|
|
49
49
|
* the id of the group that owns the file
|
|
50
50
|
*/
|
|
51
51
|
gid: number | bigint;
|
|
52
|
+
/**
|
|
53
|
+
* the ino
|
|
54
|
+
*/
|
|
55
|
+
ino: number | bigint;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/**
|
|
@@ -179,7 +183,7 @@ export abstract class StatsCommon<T extends number | bigint> implements Node.Sta
|
|
|
179
183
|
/**
|
|
180
184
|
* Creates a new stats instance from a stats-like object. Can be used to copy stats (note)
|
|
181
185
|
*/
|
|
182
|
-
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode }: Partial<StatsLike> = {}) {
|
|
186
|
+
constructor({ atimeMs, mtimeMs, ctimeMs, birthtimeMs, uid, gid, size, mode, ino }: Partial<StatsLike> = {}) {
|
|
183
187
|
const currentTime = Date.now();
|
|
184
188
|
const resolveT = (val: number | bigint, _default: number) => <T>(typeof val == this._typename ? val : this._convert(typeof val == this._typename_inverse ? val : _default));
|
|
185
189
|
this.atimeMs = resolveT(atimeMs, currentTime);
|
|
@@ -189,6 +193,7 @@ export abstract class StatsCommon<T extends number | bigint> implements Node.Sta
|
|
|
189
193
|
this.uid = resolveT(uid, 0);
|
|
190
194
|
this.gid = resolveT(gid, 0);
|
|
191
195
|
this.size = this._convert(size);
|
|
196
|
+
this.ino = this._convert(ino);
|
|
192
197
|
const itemType: FileType = Number(mode) & S_IFMT || FileType.FILE;
|
|
193
198
|
|
|
194
199
|
if (mode) {
|