← Files Biological Sequence & Alignment ViewerARCHIVED FILE

src/sequence/exports.ts

2.15 KB · Sep 30, 2026 · 23:01 UTC

↓ Download file

import type {
  SequenceDocument,
  SequenceRecord,
  SequenceSelection,
} from "./types";
import { extractSelectedSequence } from "./selection";

export function buildFastaExport(records: Array<SequenceRecord>): string {
  return records
    .map(
      (record) =>
        `>${record.sourceLabel}${record.description == null ? "" : ` ${record.description}`}\n${wrapSequence(record.sequence)}`,
    )
    .join("\n");
}

export function buildSelectedSequenceExport({
  document,
  selection,
}: {
  document: SequenceDocument;
  selection: SequenceSelection;
}): string {
  const record = document.records.find(({ id }) => id === selection.recordId);
  if (record == null) {
    throw new Error("Selected sequence record could not be found.");
  }
  const sequence = extractSelectedSequence(record, selection);
  const range =
    selection.segments == null
      ? `${selection.start}-${selection.end}`
      : selection.segments
          .map(({ end, start }) => `${start}-${end}`)
          .join(",");
  return `>${record.sourceLabel}:${range}\n${wrapSequence(sequence)}`;
}

export function buildFastqExport(record: SequenceRecord): string {
  if (record.quality == null) {
    throw new Error("Selected record does not include FASTQ quality.");
  }
  return `@${record.sourceLabel}\n${record.sequence}\n+\n${record.quality.ascii}`;
}

export function getSequenceExportFileName({
  record,
  selection,
  suffix,
}: {
  record: SequenceRecord;
  selection?: SequenceSelection;
  suffix: "fasta" | "fastq";
}): string {
  const recordFileStem = sanitizeFileName(record.sourceLabel) || "sequence";
  const rangeSuffix =
    selection == null
      ? ""
      : `-${selection.start}-${selection.end}${selection.segments == null ? "" : "-origin-spanning"}`;
  return `${recordFileStem}${rangeSuffix}.${suffix}`;
}

function wrapSequence(sequence: string, width = 80): string {
  const lines: Array<string> = [];
  for (let start = 0; start < sequence.length; start += width) {
    lines.push(sequence.slice(start, start + width));
  }
  return lines.join("\n");
}

function sanitizeFileName(value: string): string {
  return value.replaceAll(/[^A-Za-z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "");
}

SHA-256: 022c6c55324324ada3010c7587d48e573ba4d8b23d00d5daa6da1bc58e0c2250