mirror of
https://github.com/morluto/rea.git
synced 2026-11-06 23:06:18 +00:00
fix(javascript)!: report ambiguous artifact paths (#7864)
* fix(javascript): report ambiguous artifact paths * fix(javascript): retain unavailable colliding sources
This commit is contained in:
1 parent
29945d435d
commit
83cd1e04c9
3 files changed
+334
-21
No files matched your search
@@ -115,17 +115,6 @@ export const readJavaScriptArtifactFiles = async (
|
||||
undefined,
|
||||
issues,
|
||||
);
|
||||
const unavailablePath = sourcePaths?.find((path) =>
|
||||
inventory.unavailableFiles.has(path),
|
||||
);
|
||||
if (unavailablePath !== undefined)
|
||||
throw new ArtifactReaderFailure(
|
||||
"unavailable",
|
||||
`Selected inventoried source bytes are unavailable: ${unavailablePath}`,
|
||||
{
|
||||
partialObservation: { kind: "artifact-inventory", inventory: snapshot },
|
||||
},
|
||||
);
|
||||
if (reader instanceof AsarArtifactReader) {
|
||||
const root = snapshot.nodes.find(
|
||||
({ artifact_id }) => artifact_id === snapshot.manifest.root_artifact_id,
|
||||
@@ -141,6 +130,25 @@ export const readJavaScriptArtifactFiles = async (
|
||||
signal,
|
||||
);
|
||||
}
|
||||
if (inventory.ambiguousPath !== undefined)
|
||||
throw new ArtifactReaderFailure(
|
||||
"path",
|
||||
`JavaScript artifact path collision: ${JSON.stringify(inventory.ambiguousPath)}. Multiple occurrences share this normalized logical path; inventory retains each occurrence.`,
|
||||
{
|
||||
partialObservation: { kind: "artifact-inventory", inventory: snapshot },
|
||||
},
|
||||
);
|
||||
const unavailablePath = sourcePaths?.find((path) =>
|
||||
inventory.unavailableFiles.has(path),
|
||||
);
|
||||
if (unavailablePath !== undefined)
|
||||
throw new ArtifactReaderFailure(
|
||||
"unavailable",
|
||||
`Selected inventoried source bytes are unavailable: ${unavailablePath}`,
|
||||
{
|
||||
partialObservation: { kind: "artifact-inventory", inventory: snapshot },
|
||||
},
|
||||
);
|
||||
const context: ReadContext = {
|
||||
resources,
|
||||
expected,
|
||||
@@ -415,21 +423,38 @@ const expectedInventory = (
|
||||
readonly files: ReadonlyMap<string, ExpectedFile>;
|
||||
readonly containers: ReadonlyMap<string, ExpectedContainer>;
|
||||
readonly unavailableFiles: ReadonlySet<string>;
|
||||
readonly ambiguousPath: string | undefined;
|
||||
} => {
|
||||
const nodes = new Map(snapshot.nodes.map((node) => [node.artifact_id, node]));
|
||||
const files = new Map<string, ExpectedFile>();
|
||||
const containers = new Map<string, ExpectedContainer>();
|
||||
const unavailableFiles = new Set<string>();
|
||||
const seenPaths = new Map<string, boolean>();
|
||||
let ambiguousPath: string | undefined;
|
||||
for (const occurrence of snapshot.occurrences) {
|
||||
if (occurrence.logical_path === ".") continue;
|
||||
const isNestedAsar =
|
||||
occurrence.entry_kind === "file" &&
|
||||
occurrence.logical_path.toLowerCase().endsWith(".asar");
|
||||
const kind = relevantKind(occurrence.logical_path);
|
||||
if (
|
||||
occurrence.entry_kind === "file" &&
|
||||
(isNestedAsar || kind !== undefined)
|
||||
) {
|
||||
const available = occurrence.artifact_id !== null;
|
||||
const priorAvailable = seenPaths.get(occurrence.logical_path);
|
||||
if (priorAvailable !== undefined) {
|
||||
// Unavailable siblings cannot bind content to the wrong occurrence.
|
||||
// Keep them as unknown; any available sibling makes the shared path
|
||||
// ambiguous for acquisition, regardless of encounter order.
|
||||
if (priorAvailable || available)
|
||||
ambiguousPath ??= occurrence.logical_path;
|
||||
continue;
|
||||
}
|
||||
seenPaths.set(occurrence.logical_path, available);
|
||||
}
|
||||
if (occurrence.artifact_id === null) {
|
||||
if (
|
||||
occurrence.entry_kind === "file" &&
|
||||
relevantKind(occurrence.logical_path) !== undefined
|
||||
)
|
||||
if (occurrence.entry_kind === "file" && kind !== undefined)
|
||||
unavailableFiles.add(occurrence.logical_path);
|
||||
if (isNestedAsar)
|
||||
containers.set(occurrence.logical_path, {
|
||||
@@ -460,7 +485,6 @@ const expectedInventory = (
|
||||
inventory_artifact_id: node.artifact_id,
|
||||
},
|
||||
});
|
||||
const kind = relevantKind(occurrence.logical_path);
|
||||
if (kind === undefined) continue;
|
||||
files.set(occurrence.logical_path, {
|
||||
path: occurrence.logical_path,
|
||||
@@ -470,7 +494,7 @@ const expectedInventory = (
|
||||
kind,
|
||||
});
|
||||
}
|
||||
return { files, containers, unavailableFiles };
|
||||
return { files, containers, unavailableFiles, ambiguousPath };
|
||||
};
|
||||
|
||||
/** Canonical snapshot arrays are already ordered by code-point identity. */
|
||||
@@ -524,11 +548,33 @@ const createUnanalyzedArtifactLookup = (
|
||||
coverage: snapshot.coverage,
|
||||
get(path) {
|
||||
if (requested.has(path)) return requested.get(path);
|
||||
const occurrence =
|
||||
snapshot.occurrences[
|
||||
lowerBound(snapshot.occurrences, path, (entry) => entry.logical_path)
|
||||
];
|
||||
const index = lowerBound(
|
||||
snapshot.occurrences,
|
||||
path,
|
||||
(entry) => entry.logical_path,
|
||||
);
|
||||
const occurrence = snapshot.occurrences[index];
|
||||
const next = snapshot.occurrences[index + 1];
|
||||
const kind = relevantKind(path);
|
||||
if (
|
||||
occurrence?.logical_path === path &&
|
||||
next?.logical_path === path &&
|
||||
(isOpaque(occurrence) || isUnavailableRelevant(occurrence))
|
||||
) {
|
||||
const ambiguous: JavaScriptUnanalyzedArtifactFile = {
|
||||
path,
|
||||
container_sha256: containerFor(path),
|
||||
sha256: null,
|
||||
bytes: null,
|
||||
inventory_artifact_id: null,
|
||||
...(kind === undefined ? {} : { kind }),
|
||||
limitations: [
|
||||
"Multiple inventory occurrences share this normalized path; content identity is unknown.",
|
||||
],
|
||||
};
|
||||
requested.set(path, ambiguous);
|
||||
return ambiguous;
|
||||
}
|
||||
const id =
|
||||
occurrence?.logical_path === path &&
|
||||
(isOpaque(occurrence) || isUnavailableRelevant(occurrence))
|
||||
|
||||
Reference in new issue
Block a user