CI / Linting (Biome) (push) Failing after 41s
CI / Tests (push) Successful in 5m25s
CI / Type Check (adzuna-extractor) (push) Successful in 1m8s
CI / Type Check (gradcracker-extractor) (push) Successful in 1m12s
CI / Type Check (hiringcafe-extractor) (push) Successful in 1m9s
CI / Type Check (orchestrator) (push) Successful in 1m25s
CI / Type Check (startupjobs-extractor) (push) Successful in 1m9s
CI / Type Check (ukvisajobs-extractor) (push) Successful in 1m9s
CI / Documentation (push) Failing after 1m56s
Dedup by employer+title and description at import; cascade skip on dismiss; hide repeats in the job list. Document product scope and duplicate detection in docs. Co-authored-by: Cursor <cursoragent@cursor.com>
51 lines
1.3 KiB
TypeScript
51 lines
1.3 KiB
TypeScript
import { collectJobDedupKeys } from "@shared/job-fingerprint";
|
|
import type { JobListItem, JobStatus } from "@shared/types";
|
|
|
|
export type DuplicateDismissReason = "skipped" | "applied";
|
|
|
|
/**
|
|
* Map open jobs to a prior skip/apply when employer+title or description matches.
|
|
*/
|
|
export function buildDuplicateDismissHints(
|
|
jobs: readonly JobListItem[],
|
|
): Map<string, DuplicateDismissReason> {
|
|
const dismissedKeys = new Map<string, DuplicateDismissReason>();
|
|
|
|
for (const job of jobs) {
|
|
if (job.status !== "skipped" && job.status !== "applied") continue;
|
|
const reason: DuplicateDismissReason =
|
|
job.status === "applied" ? "applied" : "skipped";
|
|
for (const key of collectJobDedupKeys({
|
|
employer: job.employer,
|
|
title: job.title,
|
|
})) {
|
|
if (!dismissedKeys.has(key)) dismissedKeys.set(key, reason);
|
|
}
|
|
}
|
|
|
|
const hints = new Map<string, DuplicateDismissReason>();
|
|
const openStatuses = new Set<JobStatus>([
|
|
"discovered",
|
|
"ready",
|
|
"processing",
|
|
]);
|
|
|
|
for (const job of jobs) {
|
|
if (!openStatuses.has(job.status)) continue;
|
|
for (const key of collectJobDedupKeys({
|
|
employer: job.employer,
|
|
title: job.title,
|
|
})) {
|
|
const reason = dismissedKeys.get(key);
|
|
if (reason) {
|
|
hints.set(job.id, reason);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hints;
|
|
}
|
|
|
|
export { collectJobDedupKeys };
|