1// Write-verdict taxonomy: classify mutation-error codes so consumers act on2// the code, never on the human-readable reason string.3//4// A Jazz write that reaches the sync server settles into a per-batch fate;5// when that fate is a rejection it carries a machine-readable `code` plus a6// free-text `reason`. The reason is diagnostics only — its wording changes7// between Jazz versions. The code is the contract, and this module is the one8// place that maps codes onto the two actions a consumer can take:9//10// - `permanent`: the store has adjudicated the write and will never accept it11// (permission denied, and any future validation/schema-enforcement codes).12// The write's verdict is `rejected`; compensation may run.13// - `transient`: the outcome is not adjudicated (store unreachable, timeout,14// ticket hiccup). The write stays pending; keep waiting and retrying.15//16// Codes not in the registry classify as `transient` by design: an17// unrecognized code must never trigger irreversible compensation. Waiting is18// recoverable; a wrong `rejected` verdict is not.1920/** How a consumer must treat a mutation-error code. */21export type MutationErrorClass = "permanent" | "transient";2223/**24 * The registry of known mutation-error codes.25 *26 * The pinned Jazz alpha emits exactly one rejection code over the sync27 * protocol: `permission_denied` (policy denies the operation on the table for28 * this session). Writes that violate the CURRENT locally-known policy or29 * schema never produce a code at all — the client runtime refuses them30 * synchronously at the call site, before any batch exists.31 */32export const MUTATION_ERROR_CLASSES: Readonly<Record<string, MutationErrorClass>> = {33 permission_denied: "permanent",34};3536/**37 * Classify a mutation-error code from a rejected batch fate — the `code` on38 * `MutationErrorEvent`, on `PersistedWriteRejectedError`, or on a39 * `BatchFate` of kind `rejected`.40 *41 * Unknown codes classify as `transient`: the consumer keeps the write42 * pending rather than compensating on a code it cannot interpret.43 */44export function classifyMutationError(code: string): MutationErrorClass {45 return MUTATION_ERROR_CLASSES[code] ?? "transient";46}4748/** True when {@link classifyMutationError} classifies `code` as permanent —49 * the write is `rejected` and compensation may run. */50export function isPermanentMutationError(code: string): boolean {51 return classifyMutationError(code) === "permanent";52}