Skip to the content

Capsid menu

app/lib/webmention/decide.server.ts

45 lines and 1 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.

Functions

The functions of decide.server.ts
FunctionLinesComplexityCoveredCRAPMutation
decideMention16 to 528No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code

Imports

It imports app/db/index.ts, app/lib/cache-purge.server.ts, app/lib/editor/publish-policy.mjs. Imported by app/lib/operator/api.server.ts, app/routes/admin.mentions.tsx.

Source

import { decideWebmention, deleteWebmention } from "~/db";
import { purgePost } from "~/lib/cache-purge.server";
import { PolicyError, WRITE_CAPABILITIES, type Actor } from "~/lib/editor/publish-policy.mjs";

// Write and purge are one function: a forgotten purge is invisible, the page just stays stale.
// Delete also needs `destroy`: it removes the only copy of what a stranger sent. The admin page passes
// no actor because the layout middleware already refuses writes for the smoke credential.

type MentionDecision = "approve" | "reject" | "delete";

export const MENTION_POLICIES = {
  write: "mention-decide-requires-write",
  destroy: "mention-delete-requires-admin",
} as const;

export async function decideMention(
  env: Env,
  id: number,
  decision: MentionDecision,
  actor?: Actor,
): Promise<{ changed: boolean; slug: string | null; purged: boolean | null }> {
  if (actor) {
    const may = WRITE_CAPABILITIES[actor.kind];
    if (!may.write) {
      throw new PolicyError(
        `Refused (${MENTION_POLICIES.write}): this credential is read only and may ` +
          `not decide a mention.`,
        MENTION_POLICIES.write,
      );
    }
    if (decision === "delete" && !may.destroy) {
      throw new PolicyError(
        `Refused (${MENTION_POLICIES.destroy}): deleting a mention removes the only ` +
          `copy of what somebody sent and is reserved to the human admin. Reject it ` +
          `instead, which is reversible.`,
        MENTION_POLICIES.destroy,
      );
    }
  }

  // The slug comes from the statement that moved the row, so the purge names the row this call changed.
  const slug =
    decision === "delete"
      ? await deleteWebmention(env, id)
      : await decideWebmention(env, id, decision === "approve" ? "approved" : "rejected");

  // Null when nothing moved (the write only touches verified rows) or the runtime cannot purge; only
  // false is a failed purge, returned, not raised: the decision is stored and only the page is stale.
  const purged = slug ? await purgePost(slug, `mention ${decision}`) : null;

  return { changed: slug !== null, slug, purged };
}