mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
ec271121e4
Normalize path facts once at the owning filesystem boundary, preserve caller spelling and unavailable inventory members, and retain typed read failures. Record exclusion entry kinds so only proven directories can establish descendant symlink targets; omitted legacy kinds stay unknown. Reviewed fixes include #1969, #2642, #2759, and #2994. Validation: 26 reference graph source tests passed; prior artifact checks passed 275 tests with one Windows-only skip. Updated compiled import workflows are included in final validation after regeneration.
51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import { posix } from "node:path";
|
|
|
|
export interface ReferenceSourcePathLookup {
|
|
readonly resolve: (path: string) => string | undefined;
|
|
readonly resolveCandidates: (paths: readonly string[]) => string | undefined;
|
|
/** Resolve a path to the closest recorded parent identity. */
|
|
readonly resolveAncestor: (path: string) => string | undefined;
|
|
}
|
|
|
|
/**
|
|
* Reference inventories retain filesystem spelling. Prefer that exact identity;
|
|
* canonical Unicode equivalence is a fallback only when it identifies one entry.
|
|
*/
|
|
export const createReferenceSourcePathLookup = (
|
|
paths: Iterable<string>,
|
|
): ReferenceSourcePathLookup => {
|
|
const exact = new Set(paths);
|
|
const canonical = new Map<string, string | null>();
|
|
for (const path of exact) {
|
|
const key = path.normalize("NFC");
|
|
canonical.set(key, canonical.has(key) ? null : path);
|
|
}
|
|
const equivalent = (path: string): string | undefined =>
|
|
canonical.get(path.normalize("NFC")) ?? undefined;
|
|
const resolve = (path: string): string | undefined =>
|
|
exact.has(path) ? path : equivalent(path);
|
|
return {
|
|
resolve,
|
|
resolveCandidates: (candidates) =>
|
|
candidates.find((path) => exact.has(path)) ??
|
|
candidates.map(equivalent).find((path) => path !== undefined),
|
|
resolveAncestor: (path) => {
|
|
for (
|
|
let candidate = path;
|
|
candidate !== "." && candidate !== "/";
|
|
candidate = posix.dirname(candidate)
|
|
) {
|
|
const found = resolve(candidate);
|
|
if (found !== undefined) return found;
|
|
}
|
|
return undefined;
|
|
},
|
|
};
|
|
};
|
|
|
|
/** Symlink evidence can originate on a different OS from the graph reader. */
|
|
export const isPortableAbsoluteReferenceTarget = (target: string): boolean =>
|
|
target.startsWith("/") ||
|
|
/^[A-Za-z]:[\\/]/u.test(target) ||
|
|
/^\\\\[^\\/]+[\\/][^\\/]+/u.test(target);
|