mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
fix(linkedin-search): drop the never-delivered applyUrl detail field
The regex required class= before href= within one tag; LinkedIn's real markup puts href first, so applyUrl was null on every live posting while SKILL.md claimed the command returns an apply link. Fixing the regex would only yield the job-view URL - a duplicate of url - so the field is removed rather than repaired, and a test pins the removal. Review finding F19 (2026-08-19), decision approved by Mads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
56f679e5c0
commit
a306912133
@@ -64,7 +64,7 @@ bun run .agents/skills/linkedin-search/cli/src/cli.ts detail <id|url> [--format
|
||||
|
||||
`id` is the job ID from `search` results (e.g. `4426311357`). You may also pass a full
|
||||
LinkedIn `jobs/view/...` URL or a `urn:li:jobPosting:...` URN. Returns the full description,
|
||||
seniority, employment type, job function, industries, and apply link.
|
||||
seniority, employment type, job function, and industries.
|
||||
|
||||
## Usage examples
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ export async function runDetail(opts: DetailOpts): Promise<number> {
|
||||
job.description || "(no description)",
|
||||
"",
|
||||
`URL: ${job.url}`,
|
||||
job.applyUrl ? `Apply: ${job.applyUrl}` : "",
|
||||
].filter((l) => l !== "")
|
||||
process.stdout.write(lines.join("\n") + "\n")
|
||||
} else {
|
||||
|
||||
@@ -63,7 +63,6 @@ export interface JobDetail extends JobCard {
|
||||
employmentType: string | null
|
||||
jobFunction: string | null
|
||||
industries: string | null
|
||||
applyUrl: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,9 +227,6 @@ export function parseJobDetail(html: string, id: string): JobDetail {
|
||||
criteria[clean(cm[1]).toLowerCase()] = clean(cm[2])
|
||||
}
|
||||
|
||||
const applyMatch = html.match(/class="topcard__link[^"]*"[^>]*href="([^"]+)"/i)
|
||||
const applyUrl = applyMatch ? decodeHtmlEntities(applyMatch[1]).split("?")[0] : null
|
||||
|
||||
return {
|
||||
id,
|
||||
title: title ? clean(title) : "(untitled)",
|
||||
@@ -244,7 +240,6 @@ export function parseJobDetail(html: string, id: string): JobDetail {
|
||||
employmentType: criteria["employment type"] ?? null,
|
||||
jobFunction: criteria["job function"] ?? null,
|
||||
industries: criteria["industries"] ?? null,
|
||||
applyUrl,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,19 @@ describe("decodeHtmlEntities (via parseJobCards)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseJobDetail dropped fields", () => {
|
||||
test("emits no applyUrl field", () => {
|
||||
// The extraction regex assumed class-before-href and never matched
|
||||
// LinkedIn's real markup (null on every live posting), and a fixed
|
||||
// version would only capture the job-view URL - a duplicate of `url`.
|
||||
// The field is dropped rather than fixed (review finding F19,
|
||||
// 2026-08-19). This test pins the removal so it does not quietly
|
||||
// return as a broken or redundant field.
|
||||
const job = parseJobDetail("<html></html>", "1");
|
||||
expect("applyUrl" in job).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("decodeHtmlEntities (via parseJobDetail)", () => {
|
||||
test("decodes hex entities inside the job title", () => {
|
||||
const html = `<h1 class="topcard__title">Señor Engineer</h1>`;
|
||||
|
||||
@@ -65,6 +65,11 @@ per-file diff commands.
|
||||
|
||||
### Changed
|
||||
|
||||
- **`linkedin-search detail` drops the `applyUrl` field** - the extraction regex
|
||||
assumed `class=` before `href=` and never matched LinkedIn's real markup (`null` on
|
||||
every live posting since the markup ordering differs), and fixing the regex would only
|
||||
capture the job-view URL, a duplicate of the record's own `url`. The field and the
|
||||
SKILL.md "apply link" claim are removed; a test pins the removal.
|
||||
- **`jobdanmark-search` search output drops presentation-only keys** - `coverImage`,
|
||||
`companyLogo`, `companyLogoSvgMarkup`, `overlayColor`, and `silhouetteLogo` were ~40%
|
||||
of a live payload (a 30-result response shrinks from ~30k to ~20k chars), fed into
|
||||
|
||||
Reference in New Issue
Block a user