fix(artifacts): never treat a directory named *.map as a source map (#4131)

Artifact inventory decided source-map edges from the `.map` path suffix,
so an archive directory such as `main.js.map/` received a maps-source
edge whose parent was the sibling `main.js`, although the same
occurrence was classified as a directory container. Edge construction
now uses the occurrence's own artifact_kind, and the duplicate suffix
rule in relationFor is removed; such directories are contained by their
parent, and real source-map files keep maps-source.

Fixes #3909

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 authored and GitHub committed 2026-10-15 07:41:04 -07:00
1 parent a90c9eac6a
commit 9d6651e5b5
2 files changed
+47 -9

No files matched your search

@@ -121,6 +121,43 @@ describe("source-map artifact relationships", () => {
);
},
);
it("keeps a directory named like a source map contained by its parent", () => {
const archive = createOccurrence(entry("app", "directory"), "app", null);
archive.artifact_id = contentId("1");
const script = createOccurrence(
entry("app/main.js", "file"),
"app/main.js",
archive.occurrence_id,
);
script.artifact_id = contentId("2");
const directory = createOccurrence(
entry("app/main.js.map", "directory"),
"app/main.js.map",
archive.occurrence_id,
);
directory.artifact_id = contentId("3");
const { edges, limitations } = createArtifactEdges(contentId("4"), [
archive,
script,
directory,
]);
expect(directory).toMatchObject({
artifact_kind: "container",
artifact_format: "directory",
});
expect(
edges.find(
({ occurrence_id }) => occurrence_id === directory.occurrence_id,
),
).toMatchObject({
relation: "contains",
parent_artifact_id: contentId("1"),
});
expect(limitations).toEqual([]);
});
});
const fat = (wide = false): Buffer =>
@@ -295,7 +295,9 @@ export const createArtifactEdges = (
const edges: ArtifactEdge[] = [];
const limitations: string[] = [];
for (const occurrence of occurrences) {
const isSourceMap = occurrence.logical_path.toLowerCase().endsWith(".map");
// Use the occurrence's own classification: a directory named `x.map` is
// a container, not a source map of its sibling.
const isSourceMap = occurrence.artifact_kind === "source-map";
const sourcePath = isSourceMap
? occurrence.logical_path.slice(0, -".map".length)
: undefined;
@@ -337,8 +339,10 @@ export const createArtifactEdges = (
relation:
occurrence.entry_kind === "slice"
? ("slice-of" as const)
: isSourceMap && mappedSource === undefined
? ("contains" as const)
: isSourceMap
? mappedSource === undefined
? ("contains" as const)
: ("maps-source" as const)
: relationFor(occurrence.logical_path),
occurrence_id: occurrence.occurrence_id,
};
@@ -587,11 +591,8 @@ const classifyFatArtifact = (
return identity.state === "mach-o" ? "mach-o-universal" : "unknown";
};
const relationFor = (path: string): ArtifactEdge["relation"] => {
const lower = path.toLowerCase();
if (lower.endsWith(".map")) return "maps-source";
if (lower.includes(".framework/")) return "embeds";
return "contains";
};
/** Relation of a non-source-map occurrence to its parent. */
const relationFor = (path: string): ArtifactEdge["relation"] =>
path.toLowerCase().includes(".framework/") ? "embeds" : "contains";
const depth = (path: string): number => path.split("/").length;