Vectrum
Sign In
60 lines · 2.8 KB · TypeScript
import type { RepoEvent } from "./events";

// Events in a sentence, for lists of activity: "opened pull request #4: Fix login".

const obj = (v: unknown) => (typeof v === "object" && v !== null ? (v as Record<string, unknown>) : {});

export function describeEvent(e: RepoEvent) {
  const p = e.payload;
  const issue = obj(p.issue);
  const pr = obj(p.pull_request);
  const title = (o: Record<string, unknown>) => (o.title ? `: ${o.title}` : "");
  const action = e.action?.replace(/_/g, " ") ?? "";
  switch (e.kind) {
    case "push": {
      const ref = String(p.ref ?? "").replace(/^refs\/(heads|tags)\//, "");
      if (p.deleted) return `deleted ${ref}`;
      const n = Number(p.total_commits ?? 0);
      return `${p.forced ? "force-pushed" : "pushed"} ${n} commit${n === 1 ? "" : "s"} to ${ref}`;
    }
    case "create":
      return `created ${p.ref_type} ${p.ref}`;
    case "delete":
      return `deleted ${p.ref_type} ${p.ref}`;
    case "issues":
      if (e.action === "status_changed") return `moved issue ${e.subject}${title(issue)} to ${String(obj(obj(obj(p.changes).status)).to ?? "").replace(/_/g, " ")}`;
      return `${action} issue ${e.subject}${title(issue)}`;
    case "issue_comment":
      return `commented on ${p.pull_request ? "pull request" : "issue"} ${e.subject}${title(p.pull_request ? pr : issue)}`;
    case "pull_request":
      return `${action === "synchronize" ? "updated" : action} pull request ${e.subject}${title(pr)}`;
    case "pull_request_review":
      return `reviewed pull request ${e.subject} (${String(obj(p.review).state ?? "").replace(/_/g, " ")})`;
    case "pull_request_review_comment":
      return `commented on the changes of pull request ${e.subject}`;
    case "workflow_run":
      return `${e.subject} ${obj(p.workflow_run).status ?? "finished"}`;
    case "release":
      return `${action} release ${e.subject}`;
    case "fork":
      return `forked the repository to ${e.subject}`;
    default:
      return `${e.kind} ${action} ${e.subject}`.trim();
  }
}

/** Where an event's subject lives in the web UI, under the repository's base path. */
export function eventPath(e: RepoEvent) {
  const p = e.payload;
  const number = /^#(\d+)$/.exec(e.subject)?.[1];
  if (number) return p.pull_request || e.kind.startsWith("pull_request") ? `/pulls/${number}` : `/issues/${number}`;
  if (e.kind === "push" && typeof p.after === "string" && p.after) return `/commit/${p.after}`;
  if (e.kind === "workflow_run") return `/actions/runs/${obj(p.workflow_run).number}`;
  return "";
}

/** A push an agent made that can be undone: commits added to an existing branch. */
export function isRevertable(e: RepoEvent) {
  const p = e.payload;
  return e.kind === "push" && !!e.agent && String(p.ref ?? "").startsWith("refs/heads/") && !!p.before && !!p.after && !p.deleted && !p.reverted;
}