@dxos/random-access-storage 0.1.57-main.e87098f → 0.1.57

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.
@@ -2,14 +2,14 @@
2
2
  /// <reference types="node" />
3
3
  import { EventEmitter } from 'node:events';
4
4
  import { RandomAccessStorage } from 'random-access-storage';
5
- import { Directory, File, Storage, StorageType, DiskInfo } from '../common';
5
+ import { Directory, DiskInfo, File, Storage, StorageType } from '../common';
6
6
  /**
7
7
  * Web file systems.
8
8
  */
9
9
  export declare class WebFS implements Storage {
10
10
  readonly path: string;
11
11
  readonly type = StorageType.WEBFS;
12
- protected readonly _files: Map<string, File>;
12
+ protected readonly _files: Map<string, WebFile>;
13
13
  protected _root?: FileSystemDirectoryHandle;
14
14
  constructor(path: string);
15
15
  get size(): number;
@@ -25,42 +25,56 @@ export declare class WebFS implements Storage {
25
25
  getDiskInfo(): Promise<DiskInfo>;
26
26
  }
27
27
  export declare class WebFile extends EventEmitter implements File {
28
- readonly opened: boolean;
29
- readonly suspended: boolean;
30
- readonly closed: boolean;
31
- readonly unlinked: boolean;
32
- readonly writing: boolean;
33
- readonly readable: boolean;
34
- readonly writable: boolean;
35
- readonly deletable: boolean;
36
- readonly truncatable: boolean;
37
- readonly statable: boolean;
38
28
  private readonly _fileName;
39
29
  private readonly _fileHandle;
40
30
  private readonly _destroy;
41
- private _usage;
31
+ /**
32
+ * Current view of the file contents.
33
+ */
34
+ private _buffer;
35
+ private _loadBufferPromise;
36
+ private _flushScheduled;
37
+ private _flushPromise;
38
+ private _flushes;
42
39
  private _operations;
43
40
  private _reads;
44
41
  private _readBytes;
45
42
  private _writes;
46
43
  private _writeBytes;
44
+ get _bufferSize(): number | undefined;
47
45
  constructor({ fileName, file, destroy, }: {
48
46
  file: Promise<FileSystemFileHandle>;
49
47
  fileName: string;
50
48
  destroy: () => Promise<void>;
51
49
  });
50
+ type: StorageType;
51
+ readonly opened: boolean;
52
+ readonly suspended: boolean;
53
+ readonly closed: boolean;
54
+ readonly unlinked: boolean;
55
+ readonly writing: boolean;
56
+ readonly readable: boolean;
57
+ readonly writable: boolean;
58
+ readonly deletable: boolean;
59
+ readonly truncatable: boolean;
60
+ readonly statable: boolean;
52
61
  destroyed: boolean;
53
62
  directory: string;
54
63
  filename: string;
55
- type: StorageType;
56
64
  native: RandomAccessStorage;
57
- write(offset: number, data: Buffer): Promise<void>;
65
+ private _loadBuffer;
66
+ private _loadBufferGuarded;
67
+ private _flushCache;
68
+ private _flushLater;
69
+ private _flushNow;
58
70
  read(offset: number, size: number): Promise<Buffer>;
71
+ write(offset: number, data: Buffer): Promise<void>;
59
72
  del(offset: number, size: number): Promise<void>;
60
73
  stat(): Promise<{
61
- size: any;
74
+ size: number;
62
75
  }>;
63
76
  truncate(offset: number): Promise<void>;
77
+ flush(): Promise<void>;
64
78
  close(): Promise<void>;
65
79
  destroy(): Promise<void>;
66
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"web-fs.d.ts","sourceRoot":"","sources":["../../../../src/browser/web-fs.ts"],"names":[],"mappings":";;AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAO5D,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAe,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGzF;;GAEG;AACH,qBAAa,KAAM,YAAW,OAAO;aAMP,IAAI,EAAE,MAAM;IALxC,QAAQ,CAAC,IAAI,qBAAqB;IAElC,SAAS,CAAC,QAAQ,CAAC,MAAM,oBAA2B;IACpD,SAAS,CAAC,KAAK,CAAC,EAAE,yBAAyB,CAAC;gBAEhB,IAAI,EAAE,MAAM;IAExC,IAAW,IAAI,WAEd;IAED,OAAO,CAAC,SAAS;YAOH,KAAK;YAwBL,WAAW;IASzB,eAAe,CAAC,GAAG,SAAK;IAUxB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAWjE,OAAO,CAAC,WAAW;YAYL,OAAO;IASf,KAAK;IASX,OAAO,CAAC,gBAAgB;IASlB,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC;CA6BvC;AAGD,qBACa,OAAQ,SAAQ,YAAa,YAAW,IAAI;IACvD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAQ;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAS;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAS;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAS;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAS;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAQ;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAQ;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAQ;IACnC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAQ;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAQ;IAElC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgC;IAC5D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAG/C,OAAO,CAAC,MAAM,CAA0B;IAGxC,OAAO,CAAC,WAAW,CAA2B;IAG9C,OAAO,CAAC,MAAM,CAA2B;IAGzC,OAAO,CAAC,UAAU,CAA2B;IAG7C,OAAO,CAAC,OAAO,CAA2B;IAG1C,OAAO,CAAC,WAAW,CAA2B;gBAElC,EACV,QAAQ,EACR,IAAI,EACJ,OAAO,GACR,EAAE;QACD,IAAI,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACpC,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9B;IAOD,SAAS,UAAS;IAClB,SAAS,SAAM;IAEf,QAAQ,SAAM;IACd,IAAI,EAAE,WAAW,CAAqB;IACtC,MAAM,EAAE,mBAAmB,CAOF;IAGnB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAoBlC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAsBjC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IA6BhC,IAAI;;;IAkBJ,QAAQ,CAAC,MAAM,EAAE,MAAM;IAgBvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAGtB,OAAO;CAId"}
1
+ {"version":3,"file":"web-fs.d.ts","sourceRoot":"","sources":["../../../../src/browser/web-fs.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,8 @@ export declare class Directory {
9
9
  private readonly _list;
10
10
  private readonly _getOrCreateFile;
11
11
  private readonly _delete;
12
- constructor(type: StorageType, path: string, _list: (path: string) => Promise<string[]>, _getOrCreateFile: (path: string, filename: string, opts?: any) => File, _delete: () => Promise<void>);
12
+ private readonly _onFlush?;
13
+ constructor(type: StorageType, path: string, _list: (path: string) => Promise<string[]>, _getOrCreateFile: (path: string, filename: string, opts?: any) => File, _delete: () => Promise<void>, _onFlush?: (() => Promise<void>) | undefined);
13
14
  toString(): string;
14
15
  /**
15
16
  * Create a new sub-directory.
@@ -23,6 +24,7 @@ export declare class Directory {
23
24
  * Get or create a new file.
24
25
  */
25
26
  getOrCreateFile(filename: string, opts?: any): File;
27
+ flush(): Promise<void>;
26
28
  /**
27
29
  * Close and delete all files in the directory and all its sub-directories.
28
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../../../../src/common/directory.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC;;GAEG;AACH,qBAAa,SAAS;aAEF,IAAI,EAAE,WAAW;aACjB,IAAI,EAAE,MAAM;IAE5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBALR,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,MAAM,EAEX,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,EAC1C,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,EACtE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAG/C,QAAQ;IAIR;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAIxC;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzB;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAInD;;OAEG;IACG,MAAM;CAGb"}
1
+ {"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../../../../src/common/directory.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC;;GAEG;AACH,qBAAa,SAAS;aAEF,IAAI,EAAE,WAAW;aACjB,IAAI,EAAE,MAAM;IAE5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBANV,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,MAAM,EAEX,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,EAC1C,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,EACtE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC5B,QAAQ,CAAC,SAAQ,QAAQ,IAAI,CAAC,aAAA;IAGjD,QAAQ;IAIR;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAIxC;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzB;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAI7C,KAAK;IAIX;;OAEG;IACG,MAAM;CAGb"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/random-access-storage",
3
- "version": "0.1.57-main.e87098f",
3
+ "version": "0.1.57",
4
4
  "description": "Multiple random storage types.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -24,13 +24,14 @@
24
24
  "random-access-memory": "^4.1.0",
25
25
  "random-access-storage": "^3.0.0",
26
26
  "random-access-web": "^2.0.3",
27
- "@dxos/async": "0.1.57-main.e87098f",
28
- "@dxos/debug": "0.1.57-main.e87098f",
29
- "@dxos/invariant": "0.1.57-main.e87098f",
30
- "@dxos/log": "0.1.57-main.e87098f",
31
- "@dxos/node-std": "0.1.57-main.e87098f",
32
- "@dxos/tracing": "0.1.57-main.e87098f",
33
- "@dxos/util": "0.1.57-main.e87098f"
27
+ "@dxos/async": "0.1.57",
28
+ "@dxos/context": "0.1.57",
29
+ "@dxos/debug": "0.1.57",
30
+ "@dxos/invariant": "0.1.57",
31
+ "@dxos/log": "0.1.57",
32
+ "@dxos/node-std": "0.1.57",
33
+ "@dxos/tracing": "0.1.57",
34
+ "@dxos/util": "0.1.57"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/pify": "^3.0.2",
@@ -9,10 +9,9 @@ import { RandomAccessStorage } from 'random-access-storage';
9
9
  import { synchronized } from '@dxos/async';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { log } from '@dxos/log';
12
- import { TimeSeriesCounter, TimeUsageCounter, trace } from '@dxos/tracing';
12
+ import { TimeSeriesCounter, trace } from '@dxos/tracing';
13
13
 
14
- import { Directory, File, Storage, StorageType, getFullPath, DiskInfo } from '../common';
15
- import { STORAGE_MONITOR } from '../monitor';
14
+ import { Directory, DiskInfo, File, Storage, StorageType, getFullPath } from '../common';
16
15
 
17
16
  /**
18
17
  * Web file systems.
@@ -20,7 +19,7 @@ import { STORAGE_MONITOR } from '../monitor';
20
19
  export class WebFS implements Storage {
21
20
  readonly type = StorageType.WEBFS;
22
21
 
23
- protected readonly _files = new Map<string, File>();
22
+ protected readonly _files = new Map<string, WebFile>();
24
23
  protected _root?: FileSystemDirectoryHandle;
25
24
 
26
25
  constructor(public readonly path: string) {}
@@ -29,7 +28,7 @@ export class WebFS implements Storage {
29
28
  return this._files.size;
30
29
  }
31
30
 
32
- private _getFiles(path: string): Map<string, File> {
31
+ private _getFiles(path: string): Map<string, WebFile> {
33
32
  const fullName = this._getFullFilename(this.path, path);
34
33
  return new Map(
35
34
  [...this._files.entries()].filter(([path, file]) => path.includes(fullName) && file.destroyed !== true),
@@ -76,6 +75,9 @@ export class WebFS implements Storage {
76
75
  (path) => this._list(path),
77
76
  (...args) => this.getOrCreateFile(...args),
78
77
  () => this._delete(sub),
78
+ async () => {
79
+ await Promise.all(Array.from(this._getFiles(sub)).map(([path, file]) => file.flush()));
80
+ },
79
81
  );
80
82
  }
81
83
 
@@ -163,24 +165,29 @@ export class WebFS implements Storage {
163
165
  // TODO(mykola): Remove EventEmitter.
164
166
  @trace.resource()
165
167
  export class WebFile extends EventEmitter implements File {
166
- readonly opened: boolean = true;
167
- readonly suspended: boolean = false;
168
- readonly closed: boolean = false;
169
- readonly unlinked: boolean = false;
170
- readonly writing: boolean = false;
171
- readonly readable: boolean = true;
172
- readonly writable: boolean = true;
173
- readonly deletable: boolean = true;
174
- readonly truncatable: boolean = true;
175
- readonly statable: boolean = true;
176
168
  @trace.info()
177
169
  private readonly _fileName: string;
178
170
 
179
171
  private readonly _fileHandle: Promise<FileSystemFileHandle>;
180
172
  private readonly _destroy: () => Promise<void>;
181
173
 
174
+ /**
175
+ * Current view of the file contents.
176
+ */
177
+ private _buffer: Uint8Array | null = null;
178
+
179
+ private _loadBufferPromise: Promise<void> | null = null;
180
+
181
+ private _flushScheduled = false;
182
+
183
+ private _flushPromise: Promise<void> | null = null;
184
+
185
+ //
186
+ // Metrics
187
+ //
188
+
182
189
  @trace.metricsCounter()
183
- private _usage = new TimeUsageCounter();
190
+ private _flushes = new TimeSeriesCounter();
184
191
 
185
192
  @trace.metricsCounter()
186
193
  private _operations = new TimeSeriesCounter();
@@ -197,6 +204,11 @@ export class WebFile extends EventEmitter implements File {
197
204
  @trace.metricsCounter()
198
205
  private _writeBytes = new TimeSeriesCounter();
199
206
 
207
+ @trace.info()
208
+ get _bufferSize() {
209
+ return this._buffer?.length;
210
+ }
211
+
200
212
  constructor({
201
213
  fileName,
202
214
  file,
@@ -210,13 +222,32 @@ export class WebFile extends EventEmitter implements File {
210
222
  this._fileName = fileName;
211
223
  this._fileHandle = file;
212
224
  this._destroy = destroy;
225
+
226
+ void this._loadBufferGuarded();
213
227
  }
214
228
 
229
+ type: StorageType = StorageType.WEBFS;
230
+
231
+ //
232
+ // random-access-storage library compatibility
233
+ //
234
+
235
+ // TODO(dmaretskyi): Are those all needed?
236
+ readonly opened: boolean = true;
237
+ readonly suspended: boolean = false;
238
+ readonly closed: boolean = false;
239
+ readonly unlinked: boolean = false;
240
+ readonly writing: boolean = false;
241
+ readonly readable: boolean = true;
242
+ readonly writable: boolean = true;
243
+ readonly deletable: boolean = true;
244
+ readonly truncatable: boolean = true;
245
+ readonly statable: boolean = true;
246
+
215
247
  destroyed = false;
216
248
  directory = '';
217
249
  // TODO(dmaretskyi): is this used?
218
250
  filename = '';
219
- type: StorageType = StorageType.WEBFS;
220
251
  native: RandomAccessStorage = {
221
252
  write: callbackify(this.write.bind(this)),
222
253
  read: callbackify(this.read.bind(this)),
@@ -226,113 +257,161 @@ export class WebFile extends EventEmitter implements File {
226
257
  truncate: callbackify(this.truncate?.bind(this)),
227
258
  } as RandomAccessStorage;
228
259
 
229
- @synchronized
230
- async write(offset: number, data: Buffer) {
231
- const span = this._usage.beginRecording();
232
- this._operations.inc();
233
- this._writes.inc();
234
- this._writeBytes.inc(data.length);
260
+ private async _loadBuffer() {
261
+ const fileHandle = await this._fileHandle;
262
+ const file = await fileHandle.getFile();
263
+ this._buffer = new Uint8Array(await file.arrayBuffer());
264
+ }
265
+
266
+ private async _loadBufferGuarded() {
267
+ await (this._loadBufferPromise ??= this._loadBuffer());
268
+ }
235
269
 
236
- const metric = STORAGE_MONITOR.beginOp({ resource: this._fileName, type: 'write', size: data.length });
237
- try {
238
- // TODO(mykola): Fix types.
239
- const fileHandle: any = await this._fileHandle;
240
- const writable = await fileHandle.createWritable({ keepExistingData: true });
241
- await writable.write({ type: 'write', data, position: offset });
242
- await writable.close();
243
- } finally {
244
- span.end();
245
- metric.end();
270
+ // Do not call directly, use _flushLater or _flushNow.
271
+ private async _flushCache() {
272
+ if (this.destroyed) {
273
+ return;
246
274
  }
275
+
276
+ this._flushes.inc();
277
+
278
+ await this._loadBufferGuarded();
279
+ invariant(this._buffer);
280
+
281
+ const fileHandle = await this._fileHandle;
282
+ const writable = await fileHandle.createWritable({ keepExistingData: true });
283
+ await writable.write({ type: 'write', data: this._buffer, position: 0 });
284
+ await writable.close();
285
+ }
286
+
287
+ private _flushLater() {
288
+ if (this._flushScheduled) {
289
+ return;
290
+ }
291
+
292
+ setTimeout(async () => {
293
+ // Making sure only one flush can run at a time.
294
+ const promiseBefore = this._flushPromise;
295
+ await this._flushPromise;
296
+ this._flushScheduled = false;
297
+
298
+ // _flushNow might have been called. In that case we don't want to run the flush again.
299
+ if (promiseBefore !== this._flushPromise) {
300
+ return;
301
+ }
302
+
303
+ this._flushPromise = this._flushCache().catch((err) => log.warn(err));
304
+ });
305
+
306
+ this._flushScheduled = true;
307
+ }
308
+
309
+ private async _flushNow() {
310
+ this._flushPromise = (this._flushPromise ?? Promise.resolve())
311
+ .then(() => this._flushCache())
312
+ .catch((err) => log.warn(err));
313
+
314
+ await this._flushPromise;
247
315
  }
248
316
 
249
- @synchronized
250
317
  async read(offset: number, size: number) {
251
- const span = this._usage.beginRecording();
252
318
  this._operations.inc();
253
319
  this._reads.inc();
254
320
  this._readBytes.inc(size);
255
321
 
256
- const metric = STORAGE_MONITOR.beginOp({ resource: this._fileName, type: 'read', size });
257
- try {
258
- const fileHandle: any = await this._fileHandle;
259
- const file = await fileHandle.getFile();
260
- if (offset + size > file.size) {
261
- throw new Error('Read out of bounds');
262
- }
263
- // does not copy the buffer
264
- return Buffer.from(await file.slice(offset, offset + size).arrayBuffer());
265
- } finally {
266
- span.end();
267
- metric.end();
322
+ if (!this._buffer) {
323
+ await this._loadBufferGuarded();
324
+ invariant(this._buffer);
325
+ }
326
+
327
+ if (offset + size > this._buffer.length) {
328
+ throw new Error('Read out of bounds');
268
329
  }
330
+
331
+ // Copy data into a new buffer.
332
+ return Buffer.from(this._buffer.slice(offset, offset + size));
333
+ }
334
+
335
+ async write(offset: number, data: Buffer) {
336
+ this._operations.inc();
337
+ this._writes.inc();
338
+ this._writeBytes.inc(data.length);
339
+
340
+ if (!this._buffer) {
341
+ await this._loadBufferGuarded();
342
+ invariant(this._buffer);
343
+ }
344
+
345
+ if (offset + data.length <= this._buffer.length) {
346
+ this._buffer.set(data, offset);
347
+ } else {
348
+ // TODO(dmaretskyi): Optimize re-allocations.
349
+ const newCache = new Uint8Array(offset + data.length);
350
+ newCache.set(this._buffer);
351
+ newCache.set(data, offset);
352
+ this._buffer = newCache;
353
+ }
354
+
355
+ this._flushLater();
269
356
  }
270
357
 
271
- @synchronized
272
358
  async del(offset: number, size: number) {
273
- const span = this._usage.beginRecording();
274
359
  this._operations.inc();
275
360
 
276
- const metric = STORAGE_MONITOR.beginOp({ resource: this._fileName, type: 'delete', size });
277
- try {
278
- if (offset < 0 || size < 0) {
279
- return;
280
- }
281
- const fileHandle: any = await this._fileHandle;
282
- const writable = await fileHandle.createWritable({ keepExistingData: true });
283
- const file = await fileHandle.getFile();
284
- let leftoverSize = 0;
285
- if (offset + size < file.size) {
286
- // does not copy the buffer
287
- const leftover = Buffer.from(await file.slice(offset + size, file.size).arrayBuffer());
288
- leftoverSize = leftover.length;
289
- await writable.write({ type: 'write', data: leftover, position: offset });
290
- }
361
+ if (offset < 0 || size < 0) {
362
+ return;
363
+ }
291
364
 
292
- await writable.write({ type: 'truncate', size: offset + leftoverSize });
293
- await writable.close();
294
- } finally {
295
- span.end();
296
- metric.end();
365
+ if (!this._buffer) {
366
+ await this._loadBufferGuarded();
367
+ invariant(this._buffer);
297
368
  }
369
+
370
+ let leftoverSize = 0;
371
+ if (offset + size < this._buffer.length) {
372
+ leftoverSize = this._buffer.length - (offset + size);
373
+ this._buffer.set(this._buffer.slice(offset + size, offset + size + leftoverSize), offset);
374
+ }
375
+
376
+ this._buffer = this._buffer.slice(0, offset + leftoverSize);
377
+
378
+ this._flushLater();
298
379
  }
299
380
 
300
- @synchronized
301
381
  async stat() {
302
- const span = this._usage.beginRecording();
303
382
  this._operations.inc();
304
383
 
305
- const metric = STORAGE_MONITOR.beginOp({ resource: this._fileName, type: 'stat' });
306
- try {
307
- const fileHandle: any = await this._fileHandle;
308
- const file = await fileHandle.getFile();
309
- return {
310
- size: file.size,
311
- };
312
- } finally {
313
- span.end();
314
- metric.end();
384
+ // NOTE: This will load all data from the file just to get it's size. While this is a lot of overhead, this works ok for out use cases.
385
+ if (!this._buffer) {
386
+ await this._loadBufferGuarded();
387
+ invariant(this._buffer);
315
388
  }
389
+
390
+ return {
391
+ size: this._buffer.length,
392
+ };
316
393
  }
317
394
 
318
- @synchronized
319
395
  async truncate(offset: number) {
320
- const span = this._usage.beginRecording();
321
396
  this._operations.inc();
322
397
 
323
- const metric = STORAGE_MONITOR.beginOp({ resource: this._fileName, type: 'truncate' });
324
- try {
325
- const fileHandle: any = await this._fileHandle;
326
- const writable = await fileHandle.createWritable({ keepExistingData: true });
327
- await writable.write({ type: 'truncate', size: offset });
328
- await writable.close();
329
- } finally {
330
- span.end();
331
- metric.end();
398
+ if (!this._buffer) {
399
+ await this._loadBufferGuarded();
400
+ invariant(this._buffer);
332
401
  }
402
+
403
+ this._buffer = this._buffer.slice(0, offset);
404
+
405
+ this._flushLater();
333
406
  }
334
407
 
335
- async close(): Promise<void> {}
408
+ async flush() {
409
+ await this._flushNow();
410
+ }
411
+
412
+ async close(): Promise<void> {
413
+ await this._flushNow();
414
+ }
336
415
 
337
416
  @synchronized
338
417
  async destroy() {
@@ -17,6 +17,7 @@ export class Directory {
17
17
  private readonly _list: (path: string) => Promise<string[]>,
18
18
  private readonly _getOrCreateFile: (path: string, filename: string, opts?: any) => File,
19
19
  private readonly _delete: () => Promise<void>,
20
+ private readonly _onFlush?: () => Promise<void>,
20
21
  ) {}
21
22
 
22
23
  toString() {
@@ -44,6 +45,10 @@ export class Directory {
44
45
  return this._getOrCreateFile(this.path, filename, opts);
45
46
  }
46
47
 
48
+ async flush() {
49
+ await this._onFlush?.();
50
+ }
51
+
47
52
  /**
48
53
  * Close and delete all files in the directory and all its sub-directories.
49
54
  */
@@ -1,41 +0,0 @@
1
- export type StorageOperation = {
2
- resource: string;
3
- type: 'read' | 'write' | 'stat' | 'delete' | 'truncate';
4
- size?: number;
5
- };
6
- export type OpHandle = {
7
- end: () => void;
8
- };
9
- export type StatsBucket = {
10
- resource: string;
11
- beginTime: number;
12
- endTime: number;
13
- /**
14
- * Fraction of time spent in storage operations.
15
- */
16
- usage: number;
17
- readOps: number;
18
- readBytes: number;
19
- readTime: number;
20
- writeOps: number;
21
- writeBytes: number;
22
- writeTime: number;
23
- otherOps: number;
24
- otherTime: number;
25
- };
26
- /**
27
- * Storage performance metrics.
28
- */
29
- export declare class StorageMonitor {
30
- private _enabled;
31
- /**
32
- * By resource.
33
- */
34
- private readonly _buckets;
35
- enable(): void;
36
- beginOp(op: StorageOperation): OpHandle;
37
- private _getOrInitBucket;
38
- private _finalizeBuckets;
39
- }
40
- export declare const STORAGE_MONITOR: StorageMonitor;
41
- //# sourceMappingURL=monitor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../../src/monitor.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IAEjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAElB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAIF;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IAEzB;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D,MAAM;IASN,OAAO,CAAC,EAAE,EAAE,gBAAgB,GAAG,QAAQ;IAiCvC,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,gBAAgB;CAiBzB;AAED,eAAO,MAAM,eAAe,gBAAuB,CAAC"}