app/lib/editor/converge.mjs
69 lines and 2 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| divergenceMessage | 13 to 24 | 3 | 100% | OkCRAP 3 | Critical30% |
| convergeWithRetry | 40 to 76 | 6 | 100% | WatchCRAP 6 | Watch77.3% |
Imports
It imports app/lib/error-message.mjs. Imported by app/kb/procedures/save.server.ts, app/kb/registry/save.server.ts, app/lib/cv/save.server.ts, app/lib/dictionary/save.server.ts, app/lib/editor/publish.server.ts, app/lib/llms/save.server.ts, app/lib/operator/api.server.ts, app/lib/pages/save.server.ts, app/lib/phages/save.server.ts, app/lib/publications/save.server.ts, app/lib/roster/save.server.ts.
Source
import { errorMessage } from "../error-message.mjs";
/**
* A name string, not a class: the thrower is .mjs and the catcher is .ts, and instanceof across
* two build graphs breaks when a bundler duplicates the module.
*/
export const DIVERGENCE_ERROR_NAME = "D1DivergenceError";
/**
* @param {{ slug: string, commitSha: string, attempts: number, cause?: unknown }} facts
* @returns {string}
*/
function divergenceMessage({ slug, commitSha, attempts, cause }) {
const reason = cause instanceof Error ? cause.message : String(cause ?? "unknown");
return (
`The post "${slug}" WAS committed as ${commitSha}, and the database index ` +
`could not be updated after ${attempts} attempt(s): ${reason}. ` +
`Your writing is safe in git and is not lost. The site will serve the ` +
`previous version of this post until the index catches up. ` +
`Do NOT save again; the commit already landed and a second save changes nothing. ` +
`Repair: run the content sync (npm run sync:content -- --remote), or press ` +
`Sync Ask corpus on /admin/posts, either of which rebuilds this row from the repo.`
);
}
/**
* Never reverts the commit on failure: the repo is the source and D1 is derived.
* One retry only: a second failure is not transient, and the repair is a rebuild from the repo.
* The recorder must not write to D1, the store that just failed; the caller passes KV.
*
* @param {{
* write: () => Promise<unknown>,
* recordDivergence: (facts: { slug: string, commitSha: string, error: string }) => Promise<unknown>,
* slug: string,
* commitSha: string,
* attempts?: number,
* }} options
* @returns {Promise<{ ok: true, attempts: number, retried: boolean }>}
*/
export async function convergeWithRetry({
write,
recordDivergence,
slug,
commitSha,
attempts = 2,
}) {
/** @type {unknown} */
let last;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
await write();
return { ok: true, attempts: attempt, retried: attempt > 1 };
} catch (error) {
last = error;
}
}
const message = divergenceMessage({ slug, commitSha, attempts, cause: last });
try {
await recordDivergence({
slug,
commitSha,
error: last instanceof Error ? last.message : String(last ?? "unknown"),
});
} catch (recordError) {
// Logged, never rethrown: the divergence error must still reach the operator.
console.error(JSON.stringify({ alert: "divergence-record-failed", detail: errorMessage(recordError) }));
}
const error = new Error(message);
error.name = DIVERGENCE_ERROR_NAME;
error.cause = last;
return Promise.reject(error);
}