MCPcopy
hub / github.com/browserless/browserless / FileSystem

Class FileSystem

src/file-system.ts:5–145  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

3import { EventEmitter } from 'events';
4
5export class FileSystem extends EventEmitter {
6 protected fsMap: Map<string, string[]> = new Map();
7 // mtime of the file when its cache entry was hydrated, so external
8 // writes to the same path invalidate the in-memory copy.
9 protected mtimes: Map<string, number> = new Map();
10 // Per-path promise chain so concurrent append() calls can't interleave
11 // their read-modify-write cycles and drop entries.
12 protected writeChains: Map<string, Promise<void>> = new Map();
13 protected currentAESKey: Buffer;
14 protected logger = new Logger('file-system');
15
16 constructor(protected config: Config) {
17 super();
18 this.currentAESKey = config.getAESKey();
19 }
20
21 /**
22 * Appends contents to a file-path for persistance. File contents are
23 * encrypted before being saved to disk. Reads happen via the in-memory
24 * lookup of the internal map. Appends to the same path are serialized.
25 *
26 * @param path The filepath to persist contents to
27 * @param newContent A string of new content to add to the file
28 * @param shouldEncode Whether contents are AES encoded on disk
29 * @param maxEntries When set (a positive integer), only the most recent N
30 * entries are kept, bounding both the file and its in-memory cache. Throws
31 * for a non-integer or non-positive value — a negative count would splice
32 * away the entry just appended (silent data loss) and 0 would disable the
33 * cap it was meant to enforce.
34 * @returns void
35 */
36 public async append(
37 path: string,
38 newContent: string,
39 shouldEncode: boolean,
40 maxEntries?: number,
41 ): Promise<void> {
42 if (
43 maxEntries !== undefined &&
44 (!Number.isInteger(maxEntries) || maxEntries < 1)
45 ) {
46 throw new Error(
47 `maxEntries must be a positive integer when set, got "${maxEntries}"`,
48 );
49 }
50
51 const prior = this.writeChains.get(path) ?? Promise.resolve();
52 const task = prior.then(async () => {
53 // Work on a copy — read() returns the live cached array, and the
54 // cache must only reflect the new entry once the disk write has
55 // succeeded, or a failed write leaves cache and file diverged.
56 const contents = [...(await this.read(path, shouldEncode))];
57
58 contents.push(newContent);
59 if (maxEntries && contents.length > maxEntries) {
60 contents.splice(0, contents.length - maxEntries);
61 }
62

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected