mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-24 17:03:55 +10:00
refactor: remove dead code, unused exports and redundant dependencies
- drop dotenv (Node 24 process.loadEnvFile) and dompurify (only used by dead code) - delete unused ui components/hooks (card, progress, checkbox, use-confirm, use-prompt) - delete dead sanitizeHtml/sanitizeCss, url-security helpers, patch-resume tool, schema/page, createResumePatches, patch-proposal preview builder, fonts fallback helpers - inline single-caller wrappers (flags service, auth getSession, pdf renderer passthrough) - deduplicate template color helpers into shared/color-helpers - unexport 50+ internal-only symbols, remove dead export-map entries - replace hand-rolled unique()/useIsMobile with Set spread and usehooks-ts
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
"./types": "./src/types.ts",
|
||||
"./prompts": "./src/prompts.ts",
|
||||
"./tools/resume-tool-contracts": "./src/tools/resume-tool-contracts.ts",
|
||||
"./tools/patch-resume": "./src/tools/patch-resume.ts",
|
||||
"./tools/patch-proposal": "./src/tools/patch-proposal.ts",
|
||||
"./resume/extraction-template": "./src/resume/extraction-template.ts",
|
||||
"./resume/sanitize": "./src/resume/sanitize.ts"
|
||||
@@ -29,7 +28,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@reactive-resume/config": "workspace:*",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260628.1",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260703.1",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ import { jsonrepair } from "jsonrepair";
|
||||
import { flattenError, ZodError } from "zod";
|
||||
import { resumeDataSchema } from "@reactive-resume/schema/resume/data";
|
||||
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
import { isObject } from "@reactive-resume/utils/sanitize";
|
||||
import { generateId } from "@reactive-resume/utils/string";
|
||||
import { buildAiExtractionTemplate } from "./extraction-template";
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
const aiExtractionTemplate = buildAiExtractionTemplate();
|
||||
|
||||
type SectionKey = keyof typeof sectionRequiredFieldMap;
|
||||
@@ -24,13 +27,13 @@ type DroppedSectionItemEntry = {
|
||||
reason: string;
|
||||
};
|
||||
|
||||
export type ResumeSanitizationDiagnostics = {
|
||||
type ResumeSanitizationDiagnostics = {
|
||||
coercions: CoercionEntry[];
|
||||
droppedSectionItems: DroppedSectionItemEntry[];
|
||||
salvageApplied: boolean;
|
||||
};
|
||||
|
||||
export type ResumeSanitizationResult = {
|
||||
type ResumeSanitizationResult = {
|
||||
data: ResumeData;
|
||||
diagnostics: ResumeSanitizationDiagnostics;
|
||||
};
|
||||
|
||||
@@ -1,40 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sampleResumeData } from "@reactive-resume/schema/resume/sample";
|
||||
import {
|
||||
buildResumePatchProposalPreview,
|
||||
normalizeResumePatchProposals,
|
||||
resumePatchProposalSchema,
|
||||
} from "./patch-proposal";
|
||||
|
||||
describe("buildResumePatchProposalPreview — additional cases", () => {
|
||||
it("shows the removed value as before with after undefined", () => {
|
||||
const proposal = resumePatchProposalSchema.parse({
|
||||
id: "proposal-1",
|
||||
title: "Remove first profile",
|
||||
operations: [{ op: "remove", path: "/sections/profiles/items/0" }],
|
||||
});
|
||||
|
||||
const preview = buildResumePatchProposalPreview(sampleResumeData, proposal);
|
||||
expect(preview.entries[0]).toMatchObject({
|
||||
operation: "remove",
|
||||
path: "/sections/profiles/items/0",
|
||||
after: undefined,
|
||||
});
|
||||
expect(preview.entries[0]?.before).toBeDefined();
|
||||
});
|
||||
|
||||
it("titleizes camelCase and hyphenated path segments", () => {
|
||||
const proposal = resumePatchProposalSchema.parse({
|
||||
id: "proposal-1",
|
||||
title: "Update page format",
|
||||
operations: [{ op: "replace", path: "/metadata/page/format", value: "letter" }],
|
||||
});
|
||||
|
||||
const preview = buildResumePatchProposalPreview(sampleResumeData, proposal);
|
||||
// Label includes the human-readable section name 'Metadata' and 'Page'.
|
||||
expect(preview.entries[0]?.label).toMatch(/Metadata/);
|
||||
});
|
||||
});
|
||||
import { normalizeResumePatchProposals } from "./patch-proposal";
|
||||
|
||||
describe("normalizeResumePatchProposals", () => {
|
||||
it("attaches a baseUpdatedAt to every proposal", () => {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sampleResumeData } from "@reactive-resume/schema/resume/sample";
|
||||
import {
|
||||
buildResumePatchProposalPreview,
|
||||
normalizeResumePatchProposals,
|
||||
resumePatchProposalSchema,
|
||||
resumePatchProposalToolInputSchema,
|
||||
resumePatchProposalToolOutputSchema,
|
||||
} from "./patch-proposal";
|
||||
|
||||
describe("resume patch proposals", () => {
|
||||
it("requires at least one operation per proposal", () => {
|
||||
const result = resumePatchProposalSchema.safeParse({
|
||||
id: "proposal-1",
|
||||
title: "Empty proposal",
|
||||
operations: [],
|
||||
const result = resumePatchProposalToolOutputSchema.safeParse({
|
||||
proposals: [
|
||||
{
|
||||
id: "proposal-1",
|
||||
title: "Empty proposal",
|
||||
operations: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
@@ -50,55 +51,4 @@ describe("resume patch proposals", () => {
|
||||
|
||||
expect(result.proposals[0]?.baseUpdatedAt).toBe("2026-05-10T06:38:27.093Z");
|
||||
});
|
||||
|
||||
it("builds readable before/after preview entries", () => {
|
||||
const proposal = resumePatchProposalSchema.parse({
|
||||
id: "proposal-1",
|
||||
title: "Rewrite summary",
|
||||
operations: [{ op: "replace", path: "/summary/content", value: "<p>Focused product engineer.</p>" }],
|
||||
});
|
||||
|
||||
const preview = buildResumePatchProposalPreview(sampleResumeData, proposal);
|
||||
|
||||
expect(preview.title).toBe("Rewrite summary");
|
||||
expect(preview.entries).toHaveLength(1);
|
||||
expect(preview.entries[0]).toMatchObject({
|
||||
operation: "replace",
|
||||
path: "/summary/content",
|
||||
label: "Summary content",
|
||||
after: "<p>Focused product engineer.</p>",
|
||||
});
|
||||
expect(preview.entries[0]?.before).toBe(sampleResumeData.summary.content);
|
||||
});
|
||||
|
||||
it("shows appended add values in the after preview", () => {
|
||||
const profile = {
|
||||
id: "b2c7f28a-3df7-4b8b-a8a1-8f95a8f522e8",
|
||||
hidden: false,
|
||||
network: "Instagram",
|
||||
username: "dkowalski.games",
|
||||
icon: "instagram-logo",
|
||||
iconColor: "",
|
||||
website: {
|
||||
url: "https://instagram.com/dkowalski.games",
|
||||
label: "instagram.com/dkowalski.games",
|
||||
inlineLink: false,
|
||||
},
|
||||
};
|
||||
const proposal = resumePatchProposalSchema.parse({
|
||||
id: "proposal-1",
|
||||
title: "Add social profile",
|
||||
operations: [{ op: "add", path: "/sections/profiles/items/-", value: profile }],
|
||||
});
|
||||
|
||||
const preview = buildResumePatchProposalPreview(sampleResumeData, proposal);
|
||||
|
||||
expect(preview.entries[0]).toMatchObject({
|
||||
operation: "add",
|
||||
path: "/sections/profiles/items/-",
|
||||
label: "Profiles new item",
|
||||
before: undefined,
|
||||
after: profile,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,45 +1,7 @@
|
||||
import type { JsonPatchOperation } from "@reactive-resume/resume/patch";
|
||||
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||||
import z from "zod";
|
||||
import { applyResumePatches, jsonPatchOperationSchema } from "@reactive-resume/resume/patch";
|
||||
import { jsonPatchOperationSchema } from "@reactive-resume/resume/patch";
|
||||
|
||||
const jsonPointerToken = /~1|~0/g;
|
||||
|
||||
const operationLabels: Record<JsonPatchOperation["op"], string> = {
|
||||
add: "Add",
|
||||
copy: "Copy",
|
||||
move: "Move",
|
||||
remove: "Remove",
|
||||
replace: "Replace",
|
||||
test: "Check",
|
||||
};
|
||||
|
||||
const sectionLabels: Record<string, string> = {
|
||||
basics: "Basics",
|
||||
customSections: "Custom sections",
|
||||
metadata: "Metadata",
|
||||
picture: "Picture",
|
||||
sections: "Sections",
|
||||
summary: "Summary",
|
||||
};
|
||||
|
||||
const fieldLabels: Record<string, string> = {
|
||||
background: "Background",
|
||||
basics: "Basics",
|
||||
company: "Company",
|
||||
content: "content",
|
||||
description: "description",
|
||||
headline: "Headline",
|
||||
items: "items",
|
||||
location: "Location",
|
||||
name: "Name",
|
||||
primary: "Primary color",
|
||||
summary: "Summary",
|
||||
text: "Text color",
|
||||
title: "title",
|
||||
};
|
||||
|
||||
export const resumePatchProposalSchema = z.object({
|
||||
const resumePatchProposalSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
title: z.string().trim().min(1),
|
||||
summary: z.string().trim().min(1).optional(),
|
||||
@@ -61,112 +23,8 @@ export const resumePatchProposalToolOutputSchema = z.object({
|
||||
proposals: z.array(resumePatchProposalSchema).min(1),
|
||||
});
|
||||
|
||||
export type ResumePatchProposal = z.infer<typeof resumePatchProposalSchema>;
|
||||
export type ResumePatchProposalToolInput = z.infer<typeof resumePatchProposalToolInputSchema>;
|
||||
export type ResumePatchProposalToolOutput = z.infer<typeof resumePatchProposalToolOutputSchema>;
|
||||
|
||||
export type ResumePatchPreviewEntry = {
|
||||
operation: JsonPatchOperation["op"];
|
||||
operationLabel: string;
|
||||
path: string;
|
||||
label: string;
|
||||
before: unknown;
|
||||
after: unknown;
|
||||
};
|
||||
|
||||
export type ResumePatchProposalPreview = {
|
||||
id: string;
|
||||
title: string;
|
||||
summary?: string;
|
||||
entries: ResumePatchPreviewEntry[];
|
||||
};
|
||||
|
||||
function decodeJsonPointerPath(path: string): string[] {
|
||||
if (path === "") return [];
|
||||
if (!path.startsWith("/")) return [];
|
||||
|
||||
return path
|
||||
.slice(1)
|
||||
.split("/")
|
||||
.map((segment) => segment.replace(jsonPointerToken, (token) => (token === "~1" ? "/" : "~")));
|
||||
}
|
||||
|
||||
function getValueAtPath(data: unknown, path: string): unknown {
|
||||
const segments = decodeJsonPointerPath(path);
|
||||
|
||||
return segments.reduce<unknown>((current, segment) => {
|
||||
if (current == null) return undefined;
|
||||
|
||||
if (Array.isArray(current)) {
|
||||
if (segment === "-") return undefined;
|
||||
const index = Number(segment);
|
||||
return Number.isInteger(index) ? current[index] : undefined;
|
||||
}
|
||||
|
||||
if (typeof current !== "object") return undefined;
|
||||
return (current as Record<string, unknown>)[segment];
|
||||
}, data);
|
||||
}
|
||||
|
||||
function getSectionLabel(segments: string[]): string {
|
||||
if (segments[0] === "sections" && segments[1]) return fieldLabels[segments[1]] ?? titleize(segments[1]);
|
||||
if (segments[0] === "metadata" && segments[1]) return `${sectionLabels.metadata} ${titleize(segments[1])}`;
|
||||
if (segments[0]) return sectionLabels[segments[0]] ?? titleize(segments[0]);
|
||||
|
||||
return "Resume";
|
||||
}
|
||||
|
||||
function titleize(value: string): string {
|
||||
return value
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.replace(/[-_]/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.replace(/^./, (char) => char.toUpperCase());
|
||||
}
|
||||
|
||||
function formatPathLabel(operation: JsonPatchOperation): string {
|
||||
const path = operation.path;
|
||||
const segments = decodeJsonPointerPath(path);
|
||||
const section = getSectionLabel(segments);
|
||||
const field = segments.at(-1);
|
||||
|
||||
if (!field) return section;
|
||||
if (field === "-") return operation.op === "add" ? `${section} new item` : section;
|
||||
if (/^\d+$/.test(field)) return `${section} item ${Number(field) + 1}`;
|
||||
if (field === "items") return `${section} items`;
|
||||
|
||||
const fieldLabel = fieldLabels[field] ?? titleize(field);
|
||||
return section === fieldLabel ? section : `${section} ${fieldLabel}`;
|
||||
}
|
||||
|
||||
function getPreviewAfterValue(patchedData: ResumeData, operation: JsonPatchOperation): unknown {
|
||||
if (operation.op === "remove") return undefined;
|
||||
if (operation.op === "add" && decodeJsonPointerPath(operation.path).at(-1) === "-") return operation.value;
|
||||
|
||||
return getValueAtPath(patchedData, operation.path);
|
||||
}
|
||||
|
||||
export function buildResumePatchProposalPreview(
|
||||
resumeData: ResumeData,
|
||||
proposal: ResumePatchProposal,
|
||||
): ResumePatchProposalPreview {
|
||||
const patchedData = applyResumePatches(resumeData, proposal.operations);
|
||||
|
||||
return {
|
||||
id: proposal.id,
|
||||
title: proposal.title,
|
||||
...(proposal.summary ? { summary: proposal.summary } : {}),
|
||||
entries: proposal.operations.map((operation) => ({
|
||||
operation: operation.op,
|
||||
operationLabel: operationLabels[operation.op],
|
||||
path: operation.path,
|
||||
label: formatPathLabel(operation),
|
||||
before: operation.op === "add" ? undefined : getValueAtPath(resumeData, operation.path),
|
||||
after: getPreviewAfterValue(patchedData, operation),
|
||||
})),
|
||||
};
|
||||
}
|
||||
type ResumePatchProposal = z.infer<typeof resumePatchProposalSchema>;
|
||||
type ResumePatchProposalToolInput = z.infer<typeof resumePatchProposalToolInputSchema>;
|
||||
|
||||
export function normalizeResumePatchProposals(
|
||||
input: ResumePatchProposalToolInput,
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
import { executePatchResume, patchResumeInputSchema } from "./patch-resume";
|
||||
|
||||
describe("patchResumeInputSchema", () => {
|
||||
it("requires at least one operation", () => {
|
||||
expect(patchResumeInputSchema.safeParse({ operations: [] }).success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts a valid replace operation", () => {
|
||||
const result = patchResumeInputSchema.safeParse({
|
||||
operations: [{ op: "replace", path: "/basics/name", value: "Jane" }],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts add and remove operations", () => {
|
||||
const result = patchResumeInputSchema.safeParse({
|
||||
operations: [
|
||||
{ op: "add", path: "/sections/skills/items/-", value: { id: "s1", name: "Go" } },
|
||||
{ op: "remove", path: "/sections/skills/items/0" },
|
||||
],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects unknown op values", () => {
|
||||
const result = patchResumeInputSchema.safeParse({
|
||||
operations: [{ op: "do-something", path: "/x", value: 1 }],
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("executePatchResume", () => {
|
||||
it("returns success and the applied operations on a valid patch", () => {
|
||||
const ops = [{ op: "replace" as const, path: "/basics/name", value: "Jane Doe" }];
|
||||
const result = executePatchResume(structuredClone(defaultResumeData), ops);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.appliedOperations).toEqual(ops);
|
||||
});
|
||||
|
||||
it("throws when an operation targets an invalid path", () => {
|
||||
const ops = [{ op: "replace" as const, path: "/non/existent/path", value: 1 }];
|
||||
expect(() => executePatchResume(structuredClone(defaultResumeData), ops)).toThrow();
|
||||
});
|
||||
|
||||
it("supports multi-op patches that touch top-level fields", () => {
|
||||
const ops = [
|
||||
{ op: "replace" as const, path: "/basics/name", value: "Jane Doe" },
|
||||
{ op: "replace" as const, path: "/basics/headline", value: "Senior Engineer" },
|
||||
];
|
||||
const result = executePatchResume(structuredClone(defaultResumeData), ops);
|
||||
expect(result.appliedOperations).toEqual(ops);
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { JsonPatchOperation } from "@reactive-resume/resume/patch";
|
||||
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||||
import { applyResumePatches } from "@reactive-resume/resume/patch";
|
||||
import { resumePatchOperationsInputSchema } from "./resume-tool-contracts";
|
||||
|
||||
export const patchResumeInputSchema = resumePatchOperationsInputSchema;
|
||||
|
||||
export const patchResumeDescription = `Apply JSON Patch (RFC 6902) operations to modify the user's resume data.
|
||||
Use this tool whenever the user asks to change, add, or remove content from their resume.
|
||||
Always generate the minimal set of operations needed. Prefer "replace" for updates, "add" for new content, "remove" for deletions.
|
||||
Use the special "-" index to append to arrays (e.g. "/sections/experience/items/-").`;
|
||||
|
||||
export function executePatchResume(resumeData: ResumeData, operations: JsonPatchOperation[]) {
|
||||
// Validates operations structurally and against the schema; throws on invalid
|
||||
applyResumePatches(resumeData, operations);
|
||||
return { success: true as const, appliedOperations: operations };
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { JsonPatchOperation } from "@reactive-resume/resume/patch";
|
||||
import z from "zod";
|
||||
import { jsonPatchOperationSchema } from "@reactive-resume/resume/patch";
|
||||
|
||||
@@ -6,10 +5,3 @@ export const resumePatchOperationsSchema = z
|
||||
.array(jsonPatchOperationSchema)
|
||||
.min(1)
|
||||
.describe("Array of JSON Patch (RFC 6902) operations to apply to the resume");
|
||||
|
||||
export const resumePatchOperationsInputSchema = z.object({
|
||||
operations: resumePatchOperationsSchema,
|
||||
});
|
||||
|
||||
export type ResumePatchOperationsInput = z.infer<typeof resumePatchOperationsInputSchema>;
|
||||
export type ResumePatchOperation = JsonPatchOperation;
|
||||
|
||||
Reference in New Issue
Block a user