73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
import type { FileManifestEntries } from "../types/file-manifest-entries";
|
|
import {
|
|
allocatePortablePath,
|
|
validatePortablePaths
|
|
} from "../utils/portable-path";
|
|
|
|
export function validateFileManifest(entries: FileManifestEntries): void {
|
|
validatePortablePaths(Object.values(entries));
|
|
}
|
|
|
|
export function sameFileManifest(
|
|
a: FileManifestEntries,
|
|
b: FileManifestEntries
|
|
): boolean {
|
|
return (
|
|
Object.keys(a).length === Object.keys(b).length &&
|
|
Object.keys(a).every((id) => a[id] === b[id])
|
|
);
|
|
}
|
|
|
|
export function resolvePaths(
|
|
proposed: FileManifestEntries,
|
|
remote: FileManifestEntries,
|
|
protectedIds: readonly string[] = [],
|
|
reservedPaths: readonly string[] = []
|
|
): FileManifestEntries {
|
|
const result: FileManifestEntries = {};
|
|
for (const id of protectedIds)
|
|
if (proposed[id] !== undefined) result[id] = proposed[id];
|
|
const occupied = { ...result };
|
|
for (const [index, path] of reservedPaths.entries())
|
|
if (!Object.values(result).includes(path))
|
|
occupied[`reserved-${index}`] = path;
|
|
const ids = Object.keys(proposed).sort(
|
|
(a, b) =>
|
|
Number(remote[b] === proposed[b]) -
|
|
Number(remote[a] === proposed[a]) ||
|
|
(a < b ? -1 : a > b ? 1 : 0)
|
|
);
|
|
for (const id of ids.filter(
|
|
(candidate) => result[candidate] === undefined
|
|
)) {
|
|
result[id] = allocatePortablePath(proposed[id], id, occupied);
|
|
occupied[id] = result[id];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function mergeFileManifests(
|
|
base: FileManifestEntries,
|
|
local: FileManifestEntries,
|
|
remote: FileManifestEntries
|
|
): FileManifestEntries {
|
|
const proposed: FileManifestEntries = {};
|
|
for (const id of new Set([
|
|
...Object.keys(base),
|
|
...Object.keys(local),
|
|
...Object.keys(remote)
|
|
])) {
|
|
let path: string | undefined;
|
|
|
|
if (local[id] === base[id]) {
|
|
path = remote[id];
|
|
} else if (remote[id] === base[id]) {
|
|
path = local[id];
|
|
} else {
|
|
path = remote[id];
|
|
}
|
|
|
|
if (path !== undefined) proposed[id] = path;
|
|
}
|
|
return resolvePaths(proposed, remote);
|
|
}
|