mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
fix(jobbank): parse JobPosting entries nested in JSON-LD @graph (#190)
Extracts the JSON-LD JobPosting lookup into a recursive parseJobPostingJsonLd helper that handles top-level objects, arrays, and @graph wrappers (including nested combinations), keeps skipping malformed scripts, and covers all four cases with network-free Bun tests. By @luochen211. Closes #189.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { parse as parseHtml } from "node-html-parser"
|
||||
|
||||
export const BASE_URL = "https://jobbank.dk"
|
||||
|
||||
export const USER_AGENT =
|
||||
@@ -158,3 +160,36 @@ export function extractJobIdFromUrl(url: string): string {
|
||||
const match = url.match(/\/job\/(\d+)\//)
|
||||
return match ? match[1] : ""
|
||||
}
|
||||
|
||||
function findJobPosting(value: unknown): Record<string, unknown> | null {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
const jobPosting = findJobPosting(item)
|
||||
if (jobPosting) return jobPosting
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
if (!value || typeof value !== "object") return null
|
||||
|
||||
const record = value as Record<string, unknown>
|
||||
if (record["@type"] === "JobPosting") return record
|
||||
|
||||
return findJobPosting(record["@graph"])
|
||||
}
|
||||
|
||||
export function parseJobPostingJsonLd(html: string): Record<string, unknown> | null {
|
||||
const root = parseHtml(html)
|
||||
const scripts = root.querySelectorAll('script[type="application/ld+json"]')
|
||||
|
||||
for (const script of scripts) {
|
||||
try {
|
||||
const jobPosting = findJobPosting(JSON.parse(script.text) as unknown)
|
||||
if (jobPosting) return jobPosting
|
||||
} catch {
|
||||
// Invalid JSON-LD should not prevent later scripts from being checked.
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user