@pranavraut033/ats-checker 1.3.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -50
- package/dist/chunk-JAFFS7YZ.mjs +1219 -0
- package/dist/chunk-JAFFS7YZ.mjs.map +1 -0
- package/dist/{scoring-BCShrnki.d.mts → config-CVmXtmvk.d.mts} +208 -88
- package/dist/{scoring-BCShrnki.d.ts → config-CVmXtmvk.d.ts} +208 -88
- package/dist/index.cjs +1777 -298
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +840 -133
- package/dist/index.mjs.map +1 -1
- package/dist/lang/de/index.cjs +424 -29
- package/dist/lang/de/index.cjs.map +1 -1
- package/dist/lang/de/index.d.mts +1 -1
- package/dist/lang/de/index.d.ts +1 -1
- package/dist/lang/de/index.mjs +424 -29
- package/dist/lang/de/index.mjs.map +1 -1
- package/dist/lang/en/index.cjs +613 -41
- package/dist/lang/en/index.cjs.map +1 -1
- package/dist/lang/en/index.d.mts +1 -1
- package/dist/lang/en/index.d.ts +1 -1
- package/dist/lang/en/index.mjs +1 -1
- package/dist/lang/en/index.mjs.map +1 -1
- package/dist/pdf/index.cjs.map +1 -1
- package/dist/pdf/index.mjs.map +1 -1
- package/package.json +11 -2
- package/dist/chunk-ZJ5E4H7Z.mjs +0 -446
- package/dist/chunk-ZJ5E4H7Z.mjs.map +0 -1
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
interface EmploymentGap {
|
|
2
|
+
/** Title (or company/fallback label) of the role worked immediately before the gap. */
|
|
3
|
+
afterRole: string;
|
|
4
|
+
months: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** ATS-relevant parseability signals extracted from a resume's raw text. */
|
|
8
|
+
interface FormattingSignals {
|
|
9
|
+
/** Pipe/tab/aligned-space table structure (reuses containsTableLikeStructure). */
|
|
10
|
+
hasTables: boolean;
|
|
11
|
+
/** Very wide, inconsistent whitespace gaps suggesting a PDF-extracted multi-column layout. */
|
|
12
|
+
hasMultiColumn: boolean;
|
|
13
|
+
/** Control chars / private-use-area glyphs (icon fonts) / replacement chars from a bad extraction. */
|
|
14
|
+
hasSpecialChars: boolean;
|
|
15
|
+
/** Bullets other than the common -, *, •, o, numbered/lettered list markers. */
|
|
16
|
+
nonStandardBullets: boolean;
|
|
17
|
+
/** Text density/shape consistent with a scanned image (no embedded text layer) rather than real text. */
|
|
18
|
+
likelyScanned: boolean;
|
|
19
|
+
/** A plausible email address is present in the raw text (contact info is extractable). */
|
|
20
|
+
contactParseable: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type Seniority = "junior" | "mid" | "senior" | "lead" | "principal";
|
|
24
|
+
|
|
1
25
|
type ResumeSection = "summary" | "experience" | "skills" | "education" | "projects" | "certifications";
|
|
2
26
|
interface ParsedDateRange {
|
|
3
27
|
raw?: string;
|
|
@@ -16,6 +40,8 @@ interface ParsedExperienceEntry {
|
|
|
16
40
|
location?: string;
|
|
17
41
|
dates?: ParsedDateRange;
|
|
18
42
|
description?: string;
|
|
43
|
+
/** Skills mentioned within this role's bullets, resolved via the alias/fuzzy registry lookup. */
|
|
44
|
+
skills: string[];
|
|
19
45
|
}
|
|
20
46
|
interface ParsedAchievement {
|
|
21
47
|
text: string;
|
|
@@ -47,8 +73,26 @@ interface ParsedResume {
|
|
|
47
73
|
totalExperienceYears: number;
|
|
48
74
|
keywords: string[];
|
|
49
75
|
languages: ParsedLanguage[];
|
|
76
|
+
/** Contact details extracted via regex; absent fields mean detection failed, not that the resume lacks them. */
|
|
77
|
+
contact?: {
|
|
78
|
+
email?: string;
|
|
79
|
+
phone?: string;
|
|
80
|
+
linkedin?: string;
|
|
81
|
+
location?: string;
|
|
82
|
+
};
|
|
83
|
+
/** Inferred overall seniority (highest-ranked signal across job titles), or undefined if unknown. */
|
|
84
|
+
seniority?: Seniority;
|
|
85
|
+
/** Gaps of minGapMonths+ between consecutive (chronologically merged) roles. */
|
|
86
|
+
employmentGaps: EmploymentGap[];
|
|
87
|
+
/** ATS-relevant parseability/formatting signals extracted from the raw text. */
|
|
88
|
+
formatting: FormattingSignals;
|
|
50
89
|
warnings: string[];
|
|
51
90
|
}
|
|
91
|
+
/** A JD's "N+ years of <skill>" requirement, e.g. { skill: "figma", years: 5 }. */
|
|
92
|
+
interface SkillExperienceRequirement {
|
|
93
|
+
skill: string;
|
|
94
|
+
years: number;
|
|
95
|
+
}
|
|
52
96
|
interface ParsedJobDescription {
|
|
53
97
|
raw: string;
|
|
54
98
|
normalizedText: string;
|
|
@@ -57,97 +101,14 @@ interface ParsedJobDescription {
|
|
|
57
101
|
roleKeywords: string[];
|
|
58
102
|
keywords: string[];
|
|
59
103
|
minExperienceYears?: number;
|
|
104
|
+
/** Per-skill year requirements parsed from the JD, e.g. "5+ years of Figma". */
|
|
105
|
+
skillExperienceRequirements: SkillExperienceRequirement[];
|
|
60
106
|
educationRequirements: string[];
|
|
61
107
|
/** canonical keyword -> the surface form (original casing/spelling) the JD used. */
|
|
62
108
|
keywordSurfaceForms: Record<string, string>;
|
|
63
109
|
requiredLanguages: ParsedLanguage[];
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
interface ATSWeights {
|
|
67
|
-
skills: number;
|
|
68
|
-
experience: number;
|
|
69
|
-
keywords: number;
|
|
70
|
-
education: number;
|
|
71
|
-
}
|
|
72
|
-
type SkillAliases = Record<string, string[]>;
|
|
73
|
-
type KeywordCategory = "technical" | "tool" | "concept" | "soft" | "marketing" | "domain";
|
|
74
|
-
interface KeywordEntry {
|
|
75
|
-
canonical: string;
|
|
76
|
-
aliases: string[];
|
|
77
|
-
category: KeywordCategory;
|
|
78
|
-
}
|
|
79
|
-
type KeywordRegistry = KeywordEntry[];
|
|
80
|
-
interface ATSProfile {
|
|
81
|
-
name: string;
|
|
82
|
-
mandatorySkills: string[];
|
|
83
|
-
optionalSkills: string[];
|
|
84
|
-
minExperience?: number;
|
|
85
|
-
}
|
|
86
|
-
interface KeywordDensityConfig {
|
|
87
|
-
/** Minimum density before a keyword is considered underused (informational only). */
|
|
88
|
-
min: number;
|
|
89
|
-
/** Maximum density before a keyword is considered stuffed. */
|
|
90
|
-
max: number;
|
|
91
|
-
/** Penalty applied when density exceeds max. */
|
|
92
|
-
overusePenalty: number;
|
|
93
|
-
}
|
|
94
|
-
interface SectionPenaltyConfig {
|
|
95
|
-
missingSummary?: number;
|
|
96
|
-
missingExperience?: number;
|
|
97
|
-
missingSkills?: number;
|
|
98
|
-
missingEducation?: number;
|
|
99
|
-
}
|
|
100
|
-
interface ATSRule {
|
|
101
|
-
id: string;
|
|
102
|
-
description?: string;
|
|
103
|
-
penalty: number;
|
|
104
|
-
warning?: string;
|
|
105
|
-
condition: (context: RuleContext) => boolean;
|
|
106
|
-
}
|
|
107
|
-
interface ATSConfig {
|
|
108
|
-
weights?: Partial<ATSWeights>;
|
|
109
|
-
skillAliases?: SkillAliases;
|
|
110
|
-
/** Categorized keyword/alias entries (technical, tool, concept, soft, marketing, domain). Merges over the default registry by canonical term. */
|
|
111
|
-
keywordRegistry?: KeywordRegistry;
|
|
112
|
-
profile?: ATSProfile;
|
|
113
|
-
rules?: ATSRule[];
|
|
114
|
-
keywordDensity?: KeywordDensityConfig;
|
|
115
|
-
sectionPenalties?: SectionPenaltyConfig;
|
|
116
|
-
allowPartialMatches?: boolean;
|
|
117
|
-
/**
|
|
118
|
-
* ISO date string (e.g. "2024-06-01") used as the "today" reference when
|
|
119
|
-
* computing duration for open-ended date ranges ("Present"/"Current"/"Now").
|
|
120
|
-
* Omit to use the actual current date (live/production behaviour).
|
|
121
|
-
* Set to a fixed value in tests or batch processing to guarantee determinism.
|
|
122
|
-
*/
|
|
123
|
-
referenceDate?: string;
|
|
124
|
-
}
|
|
125
|
-
interface NormalizedWeights extends ATSWeights {
|
|
126
|
-
/** Weights normalized so they sum to 1. */
|
|
127
|
-
normalizedTotal: number;
|
|
128
|
-
}
|
|
129
|
-
interface ResolvedATSConfig {
|
|
130
|
-
weights: NormalizedWeights;
|
|
131
|
-
skillAliases: SkillAliases;
|
|
132
|
-
keywordRegistry: KeywordRegistry;
|
|
133
|
-
/** canonical term -> category, derived once from keywordRegistry. */
|
|
134
|
-
categoryIndex: Map<string, KeywordCategory>;
|
|
135
|
-
profile?: ATSProfile;
|
|
136
|
-
rules: ATSRule[];
|
|
137
|
-
keywordDensity: KeywordDensityConfig;
|
|
138
|
-
sectionPenalties: Required<SectionPenaltyConfig>;
|
|
139
|
-
allowPartialMatches: boolean;
|
|
140
|
-
/** Resolved reference date for "Present" duration calculations. */
|
|
141
|
-
referenceDate?: Date;
|
|
142
|
-
}
|
|
143
|
-
interface RuleContext {
|
|
144
|
-
resume: ParsedResume;
|
|
145
|
-
job: ParsedJobDescription;
|
|
146
|
-
weights: NormalizedWeights;
|
|
147
|
-
keywordDensity: KeywordDensityConfig;
|
|
148
|
-
breakdown?: ATSBreakdown;
|
|
149
|
-
matchedKeywords?: string[];
|
|
150
|
-
overusedKeywords?: string[];
|
|
110
|
+
/** Inferred seniority cue from the JD's role/title text, or undefined if unknown. */
|
|
111
|
+
seniority?: Seniority;
|
|
151
112
|
}
|
|
152
113
|
|
|
153
114
|
/**
|
|
@@ -260,8 +221,32 @@ interface ATSBreakdown {
|
|
|
260
221
|
skills: number;
|
|
261
222
|
experience: number;
|
|
262
223
|
keywords: number;
|
|
224
|
+
/** Real-ATS parseability score (0-100) — formatting, contact extractability, section coverage. */
|
|
225
|
+
parseability: number;
|
|
263
226
|
education: number;
|
|
264
227
|
}
|
|
228
|
+
/** One point deduction applied while computing the parseability score. */
|
|
229
|
+
interface ParseabilityDeduction {
|
|
230
|
+
reason: string;
|
|
231
|
+
points: number;
|
|
232
|
+
}
|
|
233
|
+
/** Detail behind `breakdown.parseability` — the formatting signals plus what was deducted and why. */
|
|
234
|
+
interface ParseabilityReport extends FormattingSignals {
|
|
235
|
+
/** Number of standard resume sections the parser detected. */
|
|
236
|
+
detectedSectionCount: number;
|
|
237
|
+
/** Ordered list of deductions applied to get from 100 down to `breakdown.parseability`. */
|
|
238
|
+
deductions: ParseabilityDeduction[];
|
|
239
|
+
}
|
|
240
|
+
/** Comparison between the resume's inferred seniority and the JD's required seniority. */
|
|
241
|
+
interface SeniorityMatch {
|
|
242
|
+
resume?: Seniority;
|
|
243
|
+
required?: Seniority;
|
|
244
|
+
/**
|
|
245
|
+
* True when the resume's seniority rank is >= the JD's required rank, or when either side
|
|
246
|
+
* is unknown (we never penalize on missing signal — only on a confirmed mismatch).
|
|
247
|
+
*/
|
|
248
|
+
met: boolean;
|
|
249
|
+
}
|
|
265
250
|
interface AnalyzeResumeInput {
|
|
266
251
|
resumeText: string;
|
|
267
252
|
jobDescription: string;
|
|
@@ -314,6 +299,141 @@ interface ATSAnalysisResult {
|
|
|
314
299
|
parsedExperienceYears: number;
|
|
315
300
|
/** Parsed experience entries from the resume, with titles and date ranges. */
|
|
316
301
|
experienceEntries: ParsedExperienceEntry[];
|
|
302
|
+
/**
|
|
303
|
+
* JD skills the resume has but whose overall experience falls short of the JD's per-skill
|
|
304
|
+
* year requirement (e.g. JD wants "5+ years Figma", resume has Figma but only 3 years total).
|
|
305
|
+
* Informational only — does not feed `score`/`breakdown`, same as language proficiency.
|
|
306
|
+
*/
|
|
307
|
+
skillExperienceGaps: {
|
|
308
|
+
skill: string;
|
|
309
|
+
requiredYears: number;
|
|
310
|
+
resumeYears: number;
|
|
311
|
+
}[];
|
|
312
|
+
/** Detail behind `breakdown.parseability` — which formatting signals were detected and penalized. */
|
|
313
|
+
parseabilityReport: ParseabilityReport;
|
|
314
|
+
/** Gaps of 3+ months between consecutive (chronologically merged) roles. Informational only. */
|
|
315
|
+
employmentGaps: EmploymentGap[];
|
|
316
|
+
/** Resume vs JD-required seniority comparison. Informational only outside its modest, capped
|
|
317
|
+
* contribution to `breakdown.experience` (see scorer.ts). */
|
|
318
|
+
seniorityMatch: SeniorityMatch;
|
|
319
|
+
/**
|
|
320
|
+
* Per-skill years of experience derived from per-role dating (sum of durations of roles whose
|
|
321
|
+
* bullets mention that skill), replacing the old "assume the skill spans the whole resume
|
|
322
|
+
* tenure" approximation. Informational only — does not feed `score`/`breakdown`.
|
|
323
|
+
*/
|
|
324
|
+
perSkillExperience: {
|
|
325
|
+
skill: string;
|
|
326
|
+
years: number;
|
|
327
|
+
}[];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
interface ATSWeights {
|
|
331
|
+
skills: number;
|
|
332
|
+
experience: number;
|
|
333
|
+
keywords: number;
|
|
334
|
+
/** How much the resume's real-ATS parseability (formatting/contact/sections) counts toward score. */
|
|
335
|
+
parseability: number;
|
|
336
|
+
education: number;
|
|
337
|
+
}
|
|
338
|
+
type SkillAliases = Record<string, string[]>;
|
|
339
|
+
type KeywordCategory = "technical" | "tool" | "concept" | "soft" | "marketing" | "domain";
|
|
340
|
+
interface KeywordEntry {
|
|
341
|
+
canonical: string;
|
|
342
|
+
aliases: string[];
|
|
343
|
+
category: KeywordCategory;
|
|
344
|
+
}
|
|
345
|
+
type KeywordRegistry = KeywordEntry[];
|
|
346
|
+
interface ATSProfile {
|
|
347
|
+
name: string;
|
|
348
|
+
mandatorySkills: string[];
|
|
349
|
+
optionalSkills: string[];
|
|
350
|
+
minExperience?: number;
|
|
351
|
+
}
|
|
352
|
+
interface KeywordDensityConfig {
|
|
353
|
+
/** Minimum density before a keyword is considered underused (informational only). */
|
|
354
|
+
min: number;
|
|
355
|
+
/** Maximum density before a keyword is considered stuffed. */
|
|
356
|
+
max: number;
|
|
357
|
+
/** Penalty applied when density exceeds max. */
|
|
358
|
+
overusePenalty: number;
|
|
359
|
+
}
|
|
360
|
+
interface SectionPenaltyConfig {
|
|
361
|
+
missingSummary?: number;
|
|
362
|
+
missingExperience?: number;
|
|
363
|
+
missingSkills?: number;
|
|
364
|
+
missingEducation?: number;
|
|
365
|
+
/**
|
|
366
|
+
* Penalty when no parseable contact email is detected. Defaults to 12 — a real ATS treats
|
|
367
|
+
* unparseable contact info as a near-knockout (recruiters/automated outreach can't reach the
|
|
368
|
+
* candidate at all), so this is deliberately more than a token warning-only value.
|
|
369
|
+
*/
|
|
370
|
+
missingContact?: number;
|
|
371
|
+
}
|
|
372
|
+
interface MatchingConfig {
|
|
373
|
+
/**
|
|
374
|
+
* Fall back to stemmed/fuzzy matching (typos, word-form variants like "developing" vs
|
|
375
|
+
* "develop", "ReactJS" vs "react") when an exact alias lookup misses. Default: true.
|
|
376
|
+
* Set to false to reproduce pre-v2 exact-match-only behavior.
|
|
377
|
+
*/
|
|
378
|
+
fuzzy?: boolean;
|
|
379
|
+
/** Passed through to fuzzyEqual's bounded Levenshtein distance when fuzzy matching is enabled. */
|
|
380
|
+
threshold?: number;
|
|
381
|
+
}
|
|
382
|
+
interface ATSRule {
|
|
383
|
+
id: string;
|
|
384
|
+
description?: string;
|
|
385
|
+
penalty: number;
|
|
386
|
+
warning?: string;
|
|
387
|
+
condition: (context: RuleContext) => boolean;
|
|
388
|
+
}
|
|
389
|
+
interface ATSConfig {
|
|
390
|
+
weights?: Partial<ATSWeights>;
|
|
391
|
+
skillAliases?: SkillAliases;
|
|
392
|
+
/** Categorized keyword/alias entries (technical, tool, concept, soft, marketing, domain). Merges over the default registry by canonical term. */
|
|
393
|
+
keywordRegistry?: KeywordRegistry;
|
|
394
|
+
profile?: ATSProfile;
|
|
395
|
+
rules?: ATSRule[];
|
|
396
|
+
keywordDensity?: KeywordDensityConfig;
|
|
397
|
+
sectionPenalties?: SectionPenaltyConfig;
|
|
398
|
+
allowPartialMatches?: boolean;
|
|
399
|
+
/** Stemmed/fuzzy matching behavior for skills/keywords. Defaults to `{ fuzzy: true }`. */
|
|
400
|
+
matching?: MatchingConfig;
|
|
401
|
+
/**
|
|
402
|
+
* ISO date string (e.g. "2024-06-01") used as the "today" reference when
|
|
403
|
+
* computing duration for open-ended date ranges ("Present"/"Current"/"Now").
|
|
404
|
+
* Omit to use the actual current date (live/production behaviour).
|
|
405
|
+
* Set to a fixed value in tests or batch processing to guarantee determinism.
|
|
406
|
+
*/
|
|
407
|
+
referenceDate?: string;
|
|
408
|
+
}
|
|
409
|
+
interface NormalizedWeights extends ATSWeights {
|
|
410
|
+
/** Weights normalized so they sum to 1. */
|
|
411
|
+
normalizedTotal: number;
|
|
412
|
+
}
|
|
413
|
+
interface ResolvedATSConfig {
|
|
414
|
+
weights: NormalizedWeights;
|
|
415
|
+
skillAliases: SkillAliases;
|
|
416
|
+
keywordRegistry: KeywordRegistry;
|
|
417
|
+
/** canonical term -> category, derived once from keywordRegistry. */
|
|
418
|
+
categoryIndex: Map<string, KeywordCategory>;
|
|
419
|
+
profile?: ATSProfile;
|
|
420
|
+
rules: ATSRule[];
|
|
421
|
+
keywordDensity: KeywordDensityConfig;
|
|
422
|
+
sectionPenalties: Required<SectionPenaltyConfig>;
|
|
423
|
+
allowPartialMatches: boolean;
|
|
424
|
+
/** Resolved matching behavior; `fuzzy` is always defined (defaults to true). */
|
|
425
|
+
matching: Required<Pick<MatchingConfig, "fuzzy">> & Pick<MatchingConfig, "threshold">;
|
|
426
|
+
/** Resolved reference date for "Present" duration calculations. */
|
|
427
|
+
referenceDate?: Date;
|
|
428
|
+
}
|
|
429
|
+
interface RuleContext {
|
|
430
|
+
resume: ParsedResume;
|
|
431
|
+
job: ParsedJobDescription;
|
|
432
|
+
weights: NormalizedWeights;
|
|
433
|
+
keywordDensity: KeywordDensityConfig;
|
|
434
|
+
breakdown?: ATSBreakdown;
|
|
435
|
+
matchedKeywords?: string[];
|
|
436
|
+
overusedKeywords?: string[];
|
|
317
437
|
}
|
|
318
438
|
|
|
319
|
-
export type { ATSProfile as A, JSONSchema as J, KeywordRegistry as K, LLMConfig as L, NormalizedWeights as N, ParsedDateRange as P, ResolvedATSConfig as R, SkillAliases as S, LLMResult as a, LLMBudget as b, AnalyzeResumeInput as c, ATSAnalysisResult as d, ATSWeights as e, KeywordCategory as f, KeywordEntry as g, KeywordDensityConfig as h, SectionPenaltyConfig as i, ATSRule as j, ATSConfig as k, RuleContext as l, ResumeSection as m, ParsedExperienceEntry as n, ParsedAchievement as o, ParsedLanguage as p, ParsedResume as q,
|
|
439
|
+
export type { ATSProfile as A, AnalyzeResumeInputV2 as B, LLMUsageStats as C, JSONSchema as J, KeywordRegistry as K, LLMConfig as L, MatchingConfig as M, NormalizedWeights as N, ParsedDateRange as P, ResolvedATSConfig as R, SkillAliases as S, LLMResult as a, LLMBudget as b, AnalyzeResumeInput as c, ATSAnalysisResult as d, ATSWeights as e, KeywordCategory as f, KeywordEntry as g, KeywordDensityConfig as h, SectionPenaltyConfig as i, ATSRule as j, ATSConfig as k, RuleContext as l, ResumeSection as m, ParsedExperienceEntry as n, ParsedAchievement as o, ParsedLanguage as p, ParsedResume as q, SkillExperienceRequirement as r, ParsedJobDescription as s, ATSBreakdown as t, ParseabilityDeduction as u, ParseabilityReport as v, SeniorityMatch as w, KeywordWeight as x, LLMClient as y, LLMFeatures as z };
|