@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.
@@ -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;
@@ -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._queue.length == 0) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "A filesystem in your browser",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
@@ -132,10 +132,15 @@ export interface AsyncStoreOptions {
132
132
  */
133
133
  export class AsyncStoreFS extends Async(FileSystem) {
134
134
  protected store: AsyncStore;
135
- private _cache?: LRUCache<string, Ino>;
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
- // INVARIANT: Root always exists.
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)) === undefined) {
400
+ if (!(await tx.get(rootIno))) {
386
401
  // Create new inode. o777, owned by root:root
387
- const dirInode = new Inode();
388
- dirInode.mode = 0o777 | FileType.DIRECTORY;
389
- // If the root doesn't exist, the first random ID shouldn't exist,
390
- // either.
391
- await tx.put(dirInode.ino, encode('{}'), false);
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 = false;
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._queue.length == 0) {
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
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "NodeNext",
4
+ "target": "ES2020",
5
+ "outDir": "dist",
6
+ "lib": ["ESNext"],
7
+ "moduleResolution": "NodeNext",
8
+ "declaration": true
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["node_modules"]
12
+ }