Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 1x 1x 1x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 1x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 42x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 42x 1x 3x 3x 1x 178x 178x 1x 31x 31x 1x 19x 19x 19x 1x 40x 40x 1x 45x 45x 1x 3x 3x 1x 2x 2x 1x 44x 44x 1x | import { joinPath, normalizePath } from '../path.js';
import type {
ByteRange,
VFSAdapter,
VFSChangeFeed,
VFSListEntry,
VFSStat,
} from '../types.js';
/**
* A view of another adapter rooted at one of its subfolders, so any folder of
* any backend can host its own `.vfs` store. The handle-based backends can
* re-root natively; this wrapper gives every other backend the same ability.
*
* The optional methods (`fileId`, streaming) are only present when the base
* adapter has them, so capability checks like `canStream()` see the truth.
*/
export class ScopedAdapter implements VFSAdapter {
readonly name: string;
readonly base: VFSAdapter;
readonly root: string;
mkdir?: (path: string) => Promise<void>;
fileId?: (path: string) => Promise<string | null>;
readRange?: (path: string, range?: ByteRange) => Promise<Uint8Array>;
readStream?: (path: string, range?: ByteRange) => Promise<ReadableStream<Uint8Array>>;
writeStream?: (path: string) => Promise<WritableStream<Uint8Array>>;
append?: (path: string, data: Uint8Array) => Promise<void>;
writeIf?: (path: string, data: Uint8Array, tag: string | null) => Promise<string | null>;
tag?: (path: string) => Promise<string | null>;
changes?: (token: string | null) => Promise<VFSChangeFeed>;
constructor(base: VFSAdapter, root: string, name?: string) {
this.base = base;
this.root = normalizePath(root);
this.name = name ?? (this.root ? `${base.name}/${this.root}` : base.name);
const mkdir = base.mkdir?.bind(base);
if (mkdir) this.mkdir = (path) => mkdir(this.at(path));
const fileId = base.fileId?.bind(base);
if (fileId) this.fileId = (path) => fileId(this.at(path));
const readRange = base.readRange?.bind(base);
if (readRange) this.readRange = (path, range) => readRange(this.at(path), range);
const readStream = base.readStream?.bind(base);
if (readStream) this.readStream = (path, range) => readStream(this.at(path), range);
const writeStream = base.writeStream?.bind(base);
if (writeStream) this.writeStream = (path) => writeStream(this.at(path));
const append = base.append?.bind(base);
if (append) this.append = (path, data) => append(this.at(path), data);
const writeIf = base.writeIf?.bind(base);
if (writeIf) this.writeIf = (path, data, tag) => writeIf(this.at(path), data, tag);
const tag = base.tag?.bind(base);
if (tag) this.tag = (path) => tag(this.at(path));
// The change feed is account-wide on every backend that has one, so it is
// forwarded whole and filtered by the caller against the paths it knows.
const changes = base.changes?.bind(base);
if (changes) {
this.changes = async (token) => {
const feed = await changes(token);
return {
...feed,
changes: feed.changes
.filter((change) => !change.path || this.inside(change.path))
.map((change) => (change.path ? { ...change, path: this.un(change.path) } : change)),
};
};
}
}
private inside(path: string): boolean {
return !this.root || path === this.root || path.startsWith(`${this.root}/`);
}
private at(path: string): string {
return joinPath(this.root, path);
}
/** Base paths come back prefixed with the scope root; strip it. */
private un(path: string): string {
return this.root ? path.slice(this.root.length + 1) : path;
}
async list(path: string): Promise<VFSListEntry[]> {
const entries = await this.base.list(this.at(path));
return entries.map((entry) => ({ ...entry, path: this.un(entry.path) }));
}
read(path: string): Promise<Uint8Array> {
return this.base.read(this.at(path));
}
write(path: string, data: Uint8Array): Promise<void> {
return this.base.write(this.at(path), data);
}
delete(path: string): Promise<void> {
return this.base.delete(this.at(path));
}
rename(oldPath: string, newPath: string): Promise<void> {
return this.base.rename(this.at(oldPath), this.at(newPath));
}
stat(path: string): Promise<VFSStat | null> {
return this.base.stat(this.at(path));
}
}
|