@zenfs/core 0.9.4 → 0.9.6
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/browser.min.js +4 -4
- package/dist/browser.min.js.map +3 -3
- package/dist/filesystem.d.ts +1 -0
- package/dist/filesystem.js +11 -8
- package/dist/stats.d.ts +5 -1
- package/dist/stats.js +2 -1
- package/package.json +1 -1
- package/src/backends/AsyncStore.ts +23 -9
- package/src/filesystem.ts +15 -10
- package/src/stats.ts +6 -1
- package/tsconfig.json +12 -0
package/dist/filesystem.d.ts
CHANGED
|
@@ -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
|
@@ -116,9 +116,17 @@ 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();
|
|
124
132
|
await super.ready();
|
|
@@ -200,7 +208,7 @@ export function Async(FS) {
|
|
|
200
208
|
try {
|
|
201
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
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
|
@@ -275,6 +275,7 @@ declare abstract class AsyncFileSystem extends FileSystem {
|
|
|
275
275
|
* @hidden
|
|
276
276
|
*/
|
|
277
277
|
abstract _sync: FileSystem;
|
|
278
|
+
queueDone(): Promise<void>;
|
|
278
279
|
metadata(): FileSystemMetadata;
|
|
279
280
|
ready(): Promise<this>;
|
|
280
281
|
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
@@ -316,7 +317,17 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
316
317
|
* Queue of pending asynchronous operations.
|
|
317
318
|
*/
|
|
318
319
|
private _queue: AsyncOperation[] = [];
|
|
319
|
-
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
|
+
|
|
320
331
|
private _isInitialized: boolean = false;
|
|
321
332
|
|
|
322
333
|
abstract _sync: FileSystem;
|
|
@@ -413,7 +424,7 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
413
424
|
try {
|
|
414
425
|
const buffer = new Uint8Array(stats.size);
|
|
415
426
|
await asyncFile.read(buffer);
|
|
416
|
-
syncFile.writeSync(buffer);
|
|
427
|
+
syncFile.writeSync(buffer, 0, stats.size);
|
|
417
428
|
} finally {
|
|
418
429
|
await asyncFile.close();
|
|
419
430
|
syncFile.closeSync();
|
|
@@ -425,12 +436,11 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
425
436
|
* @internal
|
|
426
437
|
*/
|
|
427
438
|
private async _next(): Promise<void> {
|
|
428
|
-
if (this.
|
|
429
|
-
this._queueRunning = false;
|
|
439
|
+
if (!this._queueRunning) {
|
|
430
440
|
return;
|
|
431
441
|
}
|
|
432
442
|
|
|
433
|
-
const [method, ...args] = this._queue.shift()
|
|
443
|
+
const [method, ...args] = this._queue.shift();
|
|
434
444
|
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
435
445
|
await this[method](...args);
|
|
436
446
|
await this._next();
|
|
@@ -441,11 +451,6 @@ export function Async<T extends abstract new (...args) => FileSystem>(FS: T): (a
|
|
|
441
451
|
*/
|
|
442
452
|
private queue(...op: AsyncOperation) {
|
|
443
453
|
this._queue.push(op);
|
|
444
|
-
if (this._queueRunning) {
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
this._queueRunning = true;
|
|
449
454
|
this._next();
|
|
450
455
|
}
|
|
451
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) {
|
package/tsconfig.json
ADDED