-
Notifications
You must be signed in to change notification settings - Fork 6
/
overlay.ts
72 lines (66 loc) · 2.44 KB
/
overlay.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import type { FileSystemDriver, FileSystemNode } from './index';
interface OverlayNode {
driver: FileSystemDriver | null;
children: Record<string, OverlayNode>;
}
function prependToStream<T>(items: T[], stream: ReadableStream<T>) {
return stream.pipeThrough(new TransformStream({
start(c) {
items.forEach(i => c.enqueue(i));
}
}));
}
export class OverlayFS implements FileSystemDriver {
#mount: OverlayNode = { driver: null, children: Object.create(null) };
mount(path: string[], fs: FileSystemDriver) {
let node = this.#mount;
for (const segment of path)
node = node.children[segment] ??= { driver: null, children: Object.create(null) };
if (node.driver !== null)
throw new Error('A mount point is already exists at /' + path.join('/'));
node.driver = fs;
return this;
}
resolve(path: string[]): { driver: FileSystemDriver, node: OverlayNode | null } {
let node: OverlayNode | null = this.#mount, found = this.#mount.driver && this.#mount, founddepth = 0, depth = 0;
for (const segment of path) {
node = node!.children[segment] ?? null;
if (!node) {
node = null;
break;
}
depth++;
if (node.driver) {
founddepth = depth;
found = node;
}
}
if (found === null)
throw new Error('ENOTFOUND');
path.splice(0, founddepth);
return found && { driver: found.driver!, node };
}
resolveUri(path: string[]): Promise<string> {
return this.resolve(path).driver!.resolveUri(path);
}
access(path: string[]): Promise<boolean> {
return this.resolve(path).driver!.access(path);
}
async readDir(path: string[]): Promise<ReadableStream<FileSystemNode>> {
const fs = this.resolve(path);
const stream = await fs.driver!.readDir(path);
if (!fs.node)
return stream;
const mounts = Object.keys(fs.node.children).map(name => ({ type: 'directory', name }))
return prependToStream(mounts, stream);
}
readFile(path: string[], offset?: number, length?: number): Promise<ReadableStream<Uint8Array>> {
return this.resolve(path).driver!.readFile(path, offset, length);
}
writeFile(path: string[], offset: 'before' | 'after' | 'override', create: boolean): Promise<WritableStream<Uint8Array>> {
return this.resolve(path).driver!.writeFile(path, offset, create);
}
deleteNode(path: string[], recursive: boolean): Promise<void> {
return this.resolve(path).driver!.deleteNode(path, recursive);
}
}