seqcolyte / studio /src /lib /resolveSpecRef.ts
seqmachines's picture
Deploy Seqcolyte Studio
ff34739 verified
Raw
History Blame Contribute Delete
2.9 kB
import type { SpecDoc } from "./types";
/**
* Resolve a finding's `evidence[].spec_ref` (a heterogeneous dotted path with an
* optional [selector]) to a DOM anchor id in the spec viewer, and check whether
* it actually resolves. The spec-viewer components render matching `id`s.
* Keyed on the run's snapshot spec — NEVER on spec_id (which drifts).
*/
// Anchor-id helpers — shared by the resolver and the spec viewer so they agree.
export const anchor = {
read: (read: string) => `spec-read-${read}`,
chain: (read: string, name: string) => `spec-chain-${read}-${name}`,
oligo: (id: string) => `spec-oligo-${id}`,
whitelist: (key: string) => `spec-whitelist-${key}`,
platformParam: (field: string) => `spec-pp-${field}`,
libStep: (step: number) => `spec-libstep-${step}`,
};
export interface SpecRefTarget {
anchorId: string | null;
label: string; // human-friendly label for the chip
found: boolean;
}
function parseSelector(seg: string): { key: string; selector?: string } {
const m = seg.match(/^([^[]+)(?:\[([^\]]+)\])?$/);
if (!m) return { key: seg };
return { key: m[1], selector: m[2] };
}
export function resolveSpecRef(spec: SpecDoc | null, ref: string): SpecRefTarget {
const fail = (label: string): SpecRefTarget => ({ anchorId: null, label, found: false });
if (!ref) return fail(ref);
if (!spec) return fail(ref);
const parts = ref.split(".");
const root = parts[0];
if (root === "read_structure") {
// read_structure.R2 OR read_structure.R2.readthrough_chain[tso_5prime]
const readName = parts[1];
const reads = spec.read_structure?.reads ?? [];
const read = reads.find((r) => r.read === readName);
if (parts[2]) {
const { key, selector } = parseSelector(parts[2]);
if (key === "readthrough_chain" && selector) {
const el = read?.readthrough_chain?.find((c) => c.name === selector);
return el
? { anchorId: anchor.chain(readName, selector), label: `${readName} · ${selector}`, found: true }
: fail(ref);
}
}
return read
? { anchorId: anchor.read(readName), label: `read ${readName}`, found: true }
: fail(ref);
}
if (root === "oligos") {
const id = parts.slice(1).join(".");
const o = spec.oligos?.find((x) => x.oligo_id === id);
return o ? { anchorId: anchor.oligo(id), label: id, found: true } : fail(ref);
}
if (root === "whitelists") {
const key = parts.slice(1).join(".");
return spec.whitelists && key in spec.whitelists
? { anchorId: anchor.whitelist(key), label: key, found: true }
: fail(ref);
}
if (root === "platform_params") {
const field = parts.slice(1).join(".");
return spec.platform_params && field in spec.platform_params
? { anchorId: anchor.platformParam(field), label: `platform · ${field}`, found: true }
: fail(ref);
}
return fail(ref);
}