Files
Jobber/orchestrator/src/client/lib/job-dedup.ts
T
iliaandCursor 17c4d4490a
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
feat(jobs): suppress duplicate postings after skip or apply
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>
2026-05-16 18:50:11 -04:00

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 };