Skip to the content

Capsid menu

app/kb/procedures/primer-dependents.server.ts

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

Functions

The functions of primer-dependents.server.ts
FunctionLinesComplexityCoveredCRAPMutation
procedureSlugsNaming32 to 488No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code
refreshProceduresNaming51 to 665No data: no coverage report in this mapNo data: no coverage report in this mapNo data: no mutants in this code

Imports

It imports app/kb/procedures/parse.mjs, app/kb/procedures/save.server.ts, app/lib/cache-purge.server.ts, app/lib/editor/github.server.ts. Imported by app/kb/registry/save.server.ts, app/lib/operator/sync-tools.server.ts.

Source

// The protocols that print a registry primer, or name a registry strain, carry its facts in their record (compile.mjs), so a
// change to one must reach them. A registry save and sync_registry call this after the primer's own row is written: each protocol that names
// a changed primer is recompiled from its file, which reads the primer again, and its row and search records are
// rewritten through the one write door a procedure save uses. Nothing here reads D1's primer row: the repository is the
// source, so a primer edited through git and a primer saved through Carrel converge the same way.
//
// A protocol that no longer compiles with the new primer (the primer was deleted, or made a draft) stops the refresh and
// the error names it, so the registry never reports success over a protocol it left reading the old sequence.

import { purgeProcedures } from "~/lib/cache-purge.server";
import { readFile } from "~/lib/editor/github.server";

import { procedurePath } from "./parse.mjs";
import { compile, writeRow } from "./save.server";

type ProcedureEnv = Env & { GITHUB_TOKEN?: string };

/** The record field each kind's items are named in. */
const NAMED_IN = {
  primer: [{ field: "$.primers", id: "$.id" }],
  // The organism list is the ids themselves.
  strain: [
    { field: "$.hostStrains", id: "$.id" },
    { field: "$.organisms", id: null },
  ],
  reagent: [{ field: "$.materials", id: "$.reagent.id" }],
  equipment: [{ field: "$.equipment", id: "$.item.id" }],
} as const;
export type NamedKind = keyof typeof NAMED_IN;

/** The slugs of the procedures whose stored record names one of these items (of these kinds), or any item of the kinds when `ids` is null. */
async function procedureSlugsNaming(env: ProcedureEnv, ids: string[] | null, kinds: NamedKind[] = ["primer"]): Promise<string[]> {
  const wanted = ids === null ? null : new Set(ids);
  const slugs = new Set<string>();
  for (const kind of kinds) {
    for (const { field, id } of NAMED_IN[kind]) {
      // A list of items has the id inside each (at the path given); a list of ids is the ids themselves.
      const rows = await env.DB.prepare(
        `SELECT DISTINCT p.slug AS slug, CASE WHEN ?2 IS NULL THEN j.value ELSE json_extract(j.value, ?2) END AS item
         FROM procedures p, json_each(json_extract(p.record, ?1)) j`,
      )
        .bind(field, id)
        .all<{ slug: string; item: string }>();
      for (const row of rows.results ?? []) if (wanted === null || wanted.has(row.item)) slugs.add(row.slug);
    }
  }
  return [...slugs];
}

/** Recompiles and rewrites every protocol that names one of these items (null: every protocol that names any item of the kinds). */
export async function refreshProceduresNaming(env: ProcedureEnv, ids: string[] | null, kinds: NamedKind[] = ["primer"]): Promise<string[]> {
  const slugs = await procedureSlugsNaming(env, ids, kinds);
  for (const slug of slugs) {
    const file = await readFile(env, procedurePath(slug));
    if (!file) throw new Error(`the protocol ${slug} names a registry item but has no file (${procedurePath(slug)}), so it was not refreshed`);
    const compiled = await compile(env, slug, file.content);
    if (!compiled.ok) {
      throw new Error(
        `the protocol ${slug} no longer compiles with the registry as it now stands, so it was not refreshed: ${compiled.errors.join("; ")}`,
      );
    }
    await writeRow(env, compiled);
  }
  if (slugs.length > 0) await purgeProcedures(`registry refresh ${slugs.join(", ")}`);
  return slugs;
}