import { z } from "zod";
import { defineContract } from "@withboundary/contract";
const lineItemSchema = z.object({
code: z.string(),
amount: z.number().positive(),
});
export const claimSchema = z.object({
npi: z.string(),
providerName: z.string(),
patientDob: z.string(),
procedureCode: z.string(),
diagnosisCode: z.string(),
amount: z.number().positive(),
lineItems: z.array(lineItemSchema).min(1),
});
function isValidIsoDate(value: string) {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
if (!match) return false;
const [, yearText, monthText, dayText] = match;
const year = Number(yearText);
const month = Number(monthText);
const day = Number(dayText);
const date = new Date(Date.UTC(year, month - 1, day));
return (
date.getUTCFullYear() === year &&
date.getUTCMonth() === month - 1 &&
date.getUTCDate() === day
);
}
export const claimContract = defineContract({
name: "healthcare-claim",
schema: claimSchema,
rules: [
{
name: "npi_is_ten_digits",
description: "NPI is a 10-digit National Provider Identifier",
fields: ["npi"],
check: (claim) =>
/^\d{10}$/.test(claim.npi)
|| `npi "${claim.npi}" must be exactly 10 digits`,
},
{
name: "diagnosis_is_icd10",
description: "Diagnosis code matches ICD-10 format",
fields: ["diagnosisCode"],
check: (claim) =>
/^[A-Z]\d{2}(\.\d{1,4})?$/.test(claim.diagnosisCode)
|| `diagnosisCode "${claim.diagnosisCode}" does not match ICD-10 format`,
},
{
name: "procedure_is_cpt",
description: "Procedure code is a 5-digit CPT code",
fields: ["procedureCode"],
check: (claim) =>
/^\d{5}$/.test(claim.procedureCode)
|| `procedureCode "${claim.procedureCode}" must be a 5-digit CPT code`,
},
{
name: "patient_dob_is_valid",
description: "Patient date of birth is a real ISO date",
fields: ["patientDob"],
check: (claim) =>
isValidIsoDate(claim.patientDob)
|| `patientDob "${claim.patientDob}" must be a real date in YYYY-MM-DD format`,
},
{
name: "line_items_sum_to_amount",
description: "Line item amounts sum to the claim total",
fields: ["lineItems", "amount"],
check: (claim) => {
const lineTotal = claim.lineItems.reduce((sum, item) => sum + item.amount, 0);
return Math.abs(lineTotal - claim.amount) < 0.01
|| `line items total ${lineTotal.toFixed(2)}, but claim amount is ${claim.amount.toFixed(2)}`;
},
},
{
name: "procedure_in_line_items",
description: "Primary procedure code appears in the line items",
fields: ["procedureCode", "lineItems"],
check: (claim) =>
claim.lineItems.some((item) => item.code === claim.procedureCode)
|| `procedureCode "${claim.procedureCode}" must appear in lineItems`,
},
],
});