mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-05 03:03:58 +10:00
* fix(import): auto-detect JSON format and show readable errors (#1) Readable import errors, a fail-soft v4 parser, and auto-detect of the JSON format so uploads just work. The format dropdown becomes an optional override. PDF and DOCX (AI) paths are untouched. * fix(import): address review feedback on the v4 guard and error message - reactive-resume-v4-json.tsx: reject arrays in isRecord so array-valued basics, sections, or metadata no longer pass the v4 shape guard. - reactive-resume-v4-json.tsx: reuse the guard's error instance in the catch arm instead of allocating a duplicate NOT_V4_MESSAGE. - error.ts: use a singular "Problem" label for root-level Zod issues so the message stays grammatical. - add a regression test for array-valued v4 branches. * fix(import): preserve selected JSON format * test(import): cover selected JSON parser --------- Co-authored-by: MrTig-afk <MrTig-afk@users.noreply.github.com> Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
34 lines
1.4 KiB
TypeScript
34 lines
1.4 KiB
TypeScript
import { ZodError } from "zod";
|
|
|
|
const MAX_LISTED_ISSUES = 3;
|
|
|
|
const describeIssuePath = (path: ReadonlyArray<PropertyKey>): string =>
|
|
path.length > 0 ? path.map((segment) => String(segment)).join(".") : "the document";
|
|
|
|
/** Builds a short, human-readable summary from a ZodError instead of a raw JSON dump. */
|
|
const summarizeZodError = (error: ZodError): string => {
|
|
if (error.issues.length === 0) return "The file could not be read as a valid resume.";
|
|
|
|
const listed = error.issues
|
|
.slice(0, MAX_LISTED_ISSUES)
|
|
.map((issue) => `${describeIssuePath(issue.path)} (${issue.message})`)
|
|
.join(", ");
|
|
|
|
const remaining = error.issues.length - MAX_LISTED_ISSUES;
|
|
const suffix = remaining > 0 ? `, and ${remaining} more field${remaining === 1 ? "" : "s"}` : "";
|
|
|
|
// Root-level issues have an empty path (rendered as "the document"); use a
|
|
// singular label for them so the sentence stays grammatical.
|
|
const allAtRoot = error.issues.slice(0, MAX_LISTED_ISSUES).every((issue) => issue.path.length === 0);
|
|
const prefix = allAtRoot ? "Problem" : "Problem fields";
|
|
return `The file could not be read as a valid resume. ${prefix}: ${listed}${suffix}.`;
|
|
};
|
|
|
|
/** Rethrows a ZodError as a human-readable import error; re-throws other errors as-is. */
|
|
export function rethrowAsImportError(error: unknown): never {
|
|
if (error instanceof ZodError) {
|
|
throw new Error(summarizeZodError(error));
|
|
}
|
|
throw error;
|
|
}
|