chore: reorganize repo structure and update docs
- move temp scripts to scripts/, cv files to cv/, jobs.csv to results/ - update index.ts and recheck.ts CSV path to results/jobs.csv - fix imports in scripts/ to use ../config.ts and ../fetch.ts - update CLAUDE.md and README.md to reflect new structure - add plans/ with LinkedIn post drafts - update .gitignore: ignore results/, jobs/, cv binaries, .DS_Store
This commit is contained in:
@@ -8,6 +8,7 @@ export interface Job {
|
||||
postedAt: string;
|
||||
link: string;
|
||||
search: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
const WORK_TYPE_MAP: Record<WorkType, string> = {
|
||||
@@ -54,7 +55,7 @@ const SENIORITY_MAP: Record<Seniority, string> = {
|
||||
// - gender markers: (H/F), (F/H), (h/f)
|
||||
// - French-only accented chars: é è ê ë à â ç î ï ô ù û ÿ œ æ
|
||||
// - French-only vocabulary
|
||||
const NON_ENGLISH_RE = new RegExp(
|
||||
export const NON_ENGLISH_RE = new RegExp(
|
||||
[
|
||||
// German gender markers (w = weiblich; must not match m/f/d)
|
||||
String.raw`\(m\/w[^)]*\)`,
|
||||
@@ -87,7 +88,9 @@ const HEADERS = {
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||
};
|
||||
|
||||
const JUNIOR_TITLE_RE = /\b(junior|jr\.?|entry.?level|graduate|intern(?:ship)?|trainee|apprentice|werkstudent)\b/i;
|
||||
export const JUNIOR_TITLE_RE = /\b(junior|jr\.?|entry.?level|graduate|intern(?:ship)?|trainee|apprentice|werkstudent)\b/i;
|
||||
|
||||
export const JAVA_TITLE_RE = /\bjava\b/i;
|
||||
|
||||
export const SOFTWARE_TITLE_RE = /\b(software|developer|architect|devops|platform|data|backend|frontend|fullstack|full.stack|tech\s*lead|technical|typescript|python|rails|node\.?js|ai\b|machine\s*learning|cloud|security|analytics|sre|reliability|solutions?|consultant|applications?\s*engineer|system[\s-]*engineer|informatics|director|product\s*manager|engineering\s*manager|it\s*manager|it\s*project|staff\s*engineer|principal\s*engineer|cto|vp\s*eng)\b/i;
|
||||
|
||||
@@ -166,11 +169,17 @@ function parseJobs(html: string, label: string): Job[] {
|
||||
const companyM = card.match(/<h4 class="base-search-card__subtitle">[\s\S]{0,300}?<a[^>]*>\s*([\s\S]*?)\s*<\/a>/);
|
||||
const timeM = card.match(/<time[^>]+datetime="([^"]+)"/);
|
||||
|
||||
const locM = card.match(/job-search-card__location[^>]*>\s*([^<]+)/);
|
||||
const jobLocation = locM ? locM[1].trim() : "";
|
||||
|
||||
// Drop jobs not physically in Switzerland — LinkedIn ignores geoId without auth
|
||||
if (jobLocation && !jobLocation.includes("Switzerland")) continue;
|
||||
|
||||
const title = clean(titleM[1]);
|
||||
const company = companyM ? clean(companyM[1]) : "";
|
||||
const postedAt = timeM ? timeM[1] : "";
|
||||
|
||||
jobs.push({ id, title, company, location: label, postedAt, link: `https://www.linkedin.com/jobs/view/${id}/`, search: label });
|
||||
jobs.push({ id, title, company, location: label, postedAt, link: `https://www.linkedin.com/jobs/view/${id}/`, search: label, source: "linkedin.com" });
|
||||
}
|
||||
|
||||
return jobs;
|
||||
@@ -196,6 +205,7 @@ export async function fetchJobs(config: SearchConfig, { raw = false } = {}): Pro
|
||||
const jobs = parseJobs(html, config.label)
|
||||
.filter((j) => !NON_ENGLISH_RE.test(j.title))
|
||||
.filter((j) => !JUNIOR_TITLE_RE.test(j.title))
|
||||
.filter((j) => !JAVA_TITLE_RE.test(j.title))
|
||||
.filter((j) => raw || SOFTWARE_TITLE_RE.test(j.title));
|
||||
|
||||
let newOnPage = 0;
|
||||
|
||||
Reference in New Issue
Block a user