Feat/company blacklist tokenized input (#219)

* initial commit

* docs mention!

* Update orchestrator/src/server/pipeline/steps/discover-jobs.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* normalizeStringArray

* poppier orange

* comments

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Shaheer Sarfaraz
2026-02-21 04:07:06 +00:00
committed by GitHub
co-authored by Copilot
parent 60ca350da8
commit cc7cacd7f5
14 changed files with 271 additions and 10 deletions
+22
View File
@@ -0,0 +1,22 @@
import { describe, expect, it } from "vitest";
import { normalizeStringArray } from "./normalize-string-array";
describe("normalizeStringArray", () => {
it("returns empty array for nullish/empty input", () => {
expect(normalizeStringArray(undefined)).toEqual([]);
expect(normalizeStringArray(null)).toEqual([]);
expect(normalizeStringArray([])).toEqual([]);
});
it("trims values and removes empty entries", () => {
expect(normalizeStringArray([" staffing ", " ", "\n"])).toEqual([
"staffing",
]);
});
it("deduplicates values case-insensitively while preserving first casing", () => {
expect(
normalizeStringArray(["Recruit", "staffing", "recruit", "STAFFING"]),
).toEqual(["Recruit", "staffing"]);
});
});
+21
View File
@@ -0,0 +1,21 @@
export function normalizeStringArray(
values: readonly string[] | null | undefined,
): string[] {
if (!values || values.length === 0) return [];
const seen = new Set<string>();
const normalized: string[] = [];
for (const value of values) {
const trimmed = value.trim();
if (!trimmed) continue;
const key = trimmed.toLowerCase();
if (seen.has(key)) continue;
seen.add(key);
normalized.push(trimmed);
}
return normalized;
}
+7
View File
@@ -177,6 +177,13 @@ export const settingsRegistry = {
parse: parseJsonArrayOrNull,
serialize: serializeNullableJsonArray,
},
blockedCompanyKeywords: {
kind: "typed" as const,
schema: z.array(z.string().trim().min(1).max(200)).max(200),
default: (): string[] => [],
parse: parseJsonArrayOrNull,
serialize: serializeNullableJsonArray,
},
searchCities: {
kind: "typed" as const,
schema: z.string().trim().max(100),
+5
View File
@@ -156,6 +156,11 @@ export const createAppSettings = (
default: ["Software Engineer"],
override: null,
},
blockedCompanyKeywords: {
value: [],
default: [],
override: null,
},
searchCities: {
value: "United Kingdom",
default: "United Kingdom",
+1
View File
@@ -123,6 +123,7 @@ export interface AppSettings {
adzunaMaxJobsPerTerm: Resolved<number>;
gradcrackerMaxJobsPerTerm: Resolved<number>;
searchTerms: Resolved<string[]>;
blockedCompanyKeywords: Resolved<string[]>;
searchCities: Resolved<string>;
jobspyResultsWanted: Resolved<number>;
jobspyCountryIndeed: Resolved<string>;