refactor(reference): centralize source inventory rules

This commit is contained in:
morluto committed 2026-10-22 09:11:44 +08:00
1 parent 5a28a147f1
commit 8dbe92a4a3
3 files changed
+50 -62

No files matched your search

+11 -40
View File
@@ -1,9 +1,11 @@
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
import {
createHistoricalSourceGraph,
historicalSourceInventoryHasPartialFacts,
historicalSourceLanguages,
historicalSourceManifests,
historicalSourceParseFailureKey,
historicalSourceRelationshipKey,
type HistoricalSourceGraph,
type HistoricalSourceGraphInput,
} from "../domain/referenceSourceGraph.js";
@@ -45,18 +47,13 @@ const cancelled = (
const isAborted = (signal?: AbortSignal): boolean => signal?.aborted === true;
const relationshipKey = (
relationship: HistoricalSourceGraphInput["relationships"][number],
): string =>
`${relationship.from_path}\u0000${relationship.to}\u0000${relationship.kind}\u0000${relationship.resolution}\u0000${relationship.parse_state}`;
const deduplicateRelationships = (
relationships: HistoricalSourceGraphInput["relationships"],
): HistoricalSourceGraphInput["relationships"] => {
const seen = new Set<string>();
const result: HistoricalSourceGraphInput["relationships"] = [];
for (const relationship of relationships) {
const key = relationshipKey(relationship);
const key = historicalSourceRelationshipKey(relationship);
if (seen.has(key)) continue;
seen.add(key);
result.push(relationship);
@@ -93,37 +90,6 @@ const buildProvenance = (
caller: options.caller,
});
const deriveInventoryState = (
input: Pick<
HistoricalSourceGraphInput,
| "entries"
| "relationships"
| "parse_failures"
| "exclusions"
| "limitations"
>,
): "complete" | "partial" | "unknown" => {
if (input.limitations.length > 0) return "partial";
if (input.exclusions.length > 0) return "partial";
if (input.parse_failures.length > 0) return "partial";
const partialEntry = input.entries.some(
(entry) =>
entry.limitations.length > 0 ||
(entry.kind === "file" && entry.content_state !== "hashed") ||
(entry.kind === "directory" && entry.tree_state !== "enumerated") ||
(entry.kind === "symlink" && entry.target_state !== "internal") ||
entry.kind === "unknown",
);
if (partialEntry) return "partial";
const partialRelationship = input.relationships.some(
({ parse_state, resolution }) =>
parse_state !== "parsed" ||
["unresolved", "unknown"].includes(resolution),
);
if (partialRelationship) return "partial";
return "complete";
};
const sortExclusions = (
exclusions: HistoricalSourceGraphInput["exclusions"],
): HistoricalSourceGraphInput["exclusions"] =>
@@ -261,16 +227,21 @@ export const importReferenceSource = async (
schema: "HistoricalSourceGraph",
authority: "historical-reference",
root_alias: "$REFERENCE_ROOT",
inventory_state: deriveInventoryState({
inventory_state: historicalSourceInventoryHasPartialFacts({
entries: sortedEntries,
relationships: uniqueRelationships,
parse_failures: uniqueFailures,
exclusions: sortedExclusions,
limitations: sortedLimitations,
}),
})
? "partial"
: "complete",
entries: sortedEntries,
relationships: uniqueRelationships.sort((left, right) =>
compareUnicodeCodePoints(relationshipKey(left), relationshipKey(right)),
compareUnicodeCodePoints(
historicalSourceRelationshipKey(left),
historicalSourceRelationshipKey(right),
),
),
parse_failures: uniqueFailures,
exclusions: sortedExclusions,
+4
View File
@@ -337,6 +337,10 @@ describe("partial historical source graphs", () => {
expect(createHistoricalSourceGraph(partial).inventory_state).toBe(
"partial",
);
expect(
createHistoricalSourceGraph({ ...partial, inventory_state: "unknown" })
.inventory_state,
).toBe("unknown");
for (const change of [
{ entries: partial.entries },
+35 -22
View File
@@ -237,13 +237,43 @@ export type HistoricalSourceManifest = z.infer<
typeof historicalSourceManifestBaseSchema
>;
/** Whether source facts prevent an inventory from being claimed complete. */
export const historicalSourceInventoryHasPartialFacts = (
input: Pick<
HistoricalSourceGraphInput,
| "entries"
| "relationships"
| "parse_failures"
| "exclusions"
| "limitations"
>,
): boolean =>
input.limitations.length > 0 ||
input.exclusions.length > 0 ||
input.parse_failures.length > 0 ||
input.entries.some(
(entry) =>
entry.limitations.length > 0 ||
(entry.kind === "file" && entry.content_state !== "hashed") ||
(entry.kind === "directory" && entry.tree_state !== "enumerated") ||
(entry.kind === "symlink" && entry.target_state !== "internal") ||
entry.kind === "unknown",
) ||
input.relationships.some(
({ parse_state, resolution }) =>
parse_state !== "parsed" ||
resolution === "unresolved" ||
resolution === "unknown",
);
/** Stable identity used to sort and deduplicate source parse failures. */
export const historicalSourceParseFailureKey = (
failure: HistoricalSourceGraphInput["parse_failures"][number],
): string => `${failure.path}\u0000${failure.parser}\u0000${failure.reason}`;
const relationshipKey = (
relationship: z.infer<typeof sourceRelationshipSchema>,
/** Stable identity used to sort and deduplicate source relationships. */
export const historicalSourceRelationshipKey = (
relationship: HistoricalSourceGraphInput["relationships"][number],
): string =>
`${relationship.from_path}\u0000${relationship.to}\u0000${relationship.kind}\u0000${relationship.resolution}\u0000${relationship.parse_state}`;
@@ -397,26 +427,9 @@ const checkCompleteInventory = (
graph: GraphStructure,
context: z.RefinementCtx,
): void => {
if (graph.inventory_state !== "complete") return;
const partialEntry = graph.entries.some(
(entry) =>
entry.limitations.length > 0 ||
(entry.kind === "file" && entry.content_state !== "hashed") ||
(entry.kind === "directory" && entry.tree_state !== "enumerated") ||
(entry.kind === "symlink" && entry.target_state !== "internal") ||
entry.kind === "unknown",
);
const partialRelationship = graph.relationships.some(
({ parse_state, resolution }) =>
parse_state !== "parsed" ||
["unresolved", "unknown"].includes(resolution),
);
if (
!partialEntry &&
!partialRelationship &&
graph.parse_failures.length === 0 &&
graph.exclusions.length === 0 &&
graph.limitations.length === 0
graph.inventory_state !== "complete" ||
!historicalSourceInventoryHasPartialFacts(graph)
)
return;
context.addIssue({
@@ -445,7 +458,7 @@ function checkGraphInvariants(
checkSortedUnique(graph.languages, "languages", context);
checkSortedUnique(graph.manifests, "manifests", context);
checkSortedUnique(
graph.relationships.map(relationshipKey),
graph.relationships.map(historicalSourceRelationshipKey),
"relationships",
context,
);