@pranavraut033/ats-checker 1.3.2 → 1.4.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 +4 -0
- package/dist/{chunk-ZJ5E4H7Z.mjs → chunk-IY7OVF6Z.mjs} +36 -3
- package/dist/chunk-IY7OVF6Z.mjs.map +1 -0
- package/dist/index.cjs +137 -12
- 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 +106 -14
- package/dist/index.mjs.map +1 -1
- package/dist/lang/de/index.d.mts +1 -1
- package/dist/lang/de/index.d.ts +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/{scoring-BCShrnki.d.mts → scoring-pZZR3soa.d.mts} +25 -1
- package/dist/{scoring-BCShrnki.d.ts → scoring-pZZR3soa.d.ts} +25 -1
- package/package.json +1 -1
- package/dist/chunk-ZJ5E4H7Z.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ Zero-dependency TypeScript library that scores a resume against a job descriptio
|
|
|
23
23
|
- **Achievement strength** — classifies resume experience bullets as strong/weak (verb + quantified impact) and suggests rewrites
|
|
24
24
|
- **Multi-language keyword packs** — `/en` and `/de` subpaths ship categorized keyword registries; install more by passing your own `keywordRegistry`
|
|
25
25
|
- **Language proficiency matching** — detects spoken-language requirements in the JD (CEFR `A1`–`C2` or words like "fluent"/"native") and flags resume gaps below the required level
|
|
26
|
+
- **Skill-experience gaps** — parses "N+ years of X" requirements from the JD and flags resume skills that don't have enough overall experience behind them
|
|
27
|
+
- **Contact detection** — flags resumes with no parseable email address (warning-only by default, configurable to a scoring penalty)
|
|
26
28
|
- **Configurable** — adjust weights, add skill aliases or a custom keyword registry, define custom penalty rules
|
|
27
29
|
- **Zero dependencies** — core library has no runtime deps; ships ESM + CJS
|
|
28
30
|
- **PDF input** — optional `/pdf` subpath extracts resume text from a PDF buffer (requires `pdfjs-dist` peer dep)
|
|
@@ -93,6 +95,7 @@ console.log(result.suggestions); // ["Highlight these required skills: acce
|
|
|
93
95
|
| `achievementStrength` | `{ strong: number; weak: number }` | Count of resume bullets classified as strong vs weak achievement statements |
|
|
94
96
|
| `matchedLanguages` | `ParsedLanguage[]` | JD-required languages the resume meets or exceeds in proficiency |
|
|
95
97
|
| `missingLanguages` | `ParsedLanguage[]` | JD-required languages absent or below the required proficiency |
|
|
98
|
+
| `skillExperienceGaps` | `{ skill, requiredYears, resumeYears }[]` | JD skills the resume has but whose overall experience falls short of a JD "N+ years of X" requirement — informational, doesn't affect `score` |
|
|
96
99
|
| `suggestions` | `string[]` | Deterministic improvement recommendations |
|
|
97
100
|
| `warnings` | `string[]` | Parse warnings and section alerts |
|
|
98
101
|
| `experienceGap` | `number` | Years below JD minimum; `0` when met |
|
|
@@ -174,6 +177,7 @@ const result = analyzeResume({
|
|
|
174
177
|
| `keywordDensity.overusePenalty` | `5` |
|
|
175
178
|
| `allowPartialMatches` | `true` |
|
|
176
179
|
| `referenceDate` | Current date (use explicit ISO string for determinism) |
|
|
180
|
+
| `sectionPenalties.missingContact` | `0` (warning-only; no parseable email detected) |
|
|
177
181
|
|
|
178
182
|
See [Configuration docs](https://pranavraut033.github.io/ats-checker/docs/configuration/) for all options.
|
|
179
183
|
|
|
@@ -127,6 +127,39 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
127
127
|
"open",
|
|
128
128
|
"dynamic"
|
|
129
129
|
]);
|
|
130
|
+
var ROLE_NOUNS = [
|
|
131
|
+
"engineer",
|
|
132
|
+
"developer",
|
|
133
|
+
"manager",
|
|
134
|
+
"scientist",
|
|
135
|
+
"analyst",
|
|
136
|
+
"designer",
|
|
137
|
+
"architect",
|
|
138
|
+
"director",
|
|
139
|
+
"consultant",
|
|
140
|
+
"lead",
|
|
141
|
+
"vp",
|
|
142
|
+
"specialist",
|
|
143
|
+
"coordinator",
|
|
144
|
+
"administrator",
|
|
145
|
+
"accountant",
|
|
146
|
+
"recruiter",
|
|
147
|
+
"nurse",
|
|
148
|
+
"teacher",
|
|
149
|
+
"associate",
|
|
150
|
+
"representative",
|
|
151
|
+
"executive",
|
|
152
|
+
"officer",
|
|
153
|
+
"technician",
|
|
154
|
+
"strategist",
|
|
155
|
+
"marketer",
|
|
156
|
+
"writer",
|
|
157
|
+
"editor",
|
|
158
|
+
"product",
|
|
159
|
+
"principal",
|
|
160
|
+
"staff",
|
|
161
|
+
"head"
|
|
162
|
+
];
|
|
130
163
|
function normalizeWhitespace(text) {
|
|
131
164
|
return text.replace(/\r\n?/g, "\n").replace(/\s+/g, " ").trim();
|
|
132
165
|
}
|
|
@@ -441,6 +474,6 @@ var defaultProfiles = [
|
|
|
441
474
|
productManagerProfile
|
|
442
475
|
];
|
|
443
476
|
|
|
444
|
-
export { STOP_WORDS, buildCategoryIndex, clamp, containsTableLikeStructure, countFrequencies, defaultKeywordRegistry, defaultProfiles, defaultSkillAliases, deriveSkillAliases, escapeRegExp, mergeKeywordRegistries, normalizeForComparison, normalizeSkill, normalizeSkills, normalizeWhitespace, softwareEngineerProfile, splitLines, tokenize, unique };
|
|
445
|
-
//# sourceMappingURL=chunk-
|
|
446
|
-
//# sourceMappingURL=chunk-
|
|
477
|
+
export { ROLE_NOUNS, STOP_WORDS, buildCategoryIndex, clamp, containsTableLikeStructure, countFrequencies, defaultKeywordRegistry, defaultProfiles, defaultSkillAliases, deriveSkillAliases, escapeRegExp, mergeKeywordRegistries, normalizeForComparison, normalizeSkill, normalizeSkills, normalizeWhitespace, softwareEngineerProfile, splitLines, tokenize, unique };
|
|
478
|
+
//# sourceMappingURL=chunk-IY7OVF6Z.mjs.map
|
|
479
|
+
//# sourceMappingURL=chunk-IY7OVF6Z.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/text.ts","../src/utils/skills.ts","../src/profiles/index.ts"],"names":[],"mappings":";AAAO,IAAM,UAAA,uBAAiB,GAAA,CAAI;AAAA;AAAA,EAEhC,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,GAAA;AAAA,EAAK,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,IAAA;AAAA,EACtE,IAAA;AAAA,EAAM,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,MAAA;AAAA;AAAA,EAE1D,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,KAAA;AAAA,EAAO,QAAA;AAAA,EAAU,MAAA;AAAA,EAAQ,MAAA;AAAA,EACnE,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,MAAA;AAAA,EAAQ,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,KAAA;AAAA,EAC/D,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,KAAA;AAAA;AAAA,EAEzD,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,OAAA;AAAA,EAAS,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,MAAA;AAAA,EACnE,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,KAAA;AAAA;AAAA,EAE/C,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,QAAA;AAAA,EAC3D,QAAA;AAAA,EAAU,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,QAAA;AAAA,EAAU,OAAA;AAAA,EAAS,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,MAAA;AAAA,EAC3D,KAAA;AAAA,EAAO,IAAA;AAAA,EAAM,IAAA;AAAA,EAAM,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,MAAA;AAAA;AAAA,EAE1D,OAAA;AAAA,EAAS,MAAA;AAAA,EAAQ,YAAA;AAAA,EAAc,UAAA;AAAA,EAAY,aAAA;AAAA,EAAe,cAAA;AAAA,EAC1D,WAAA;AAAA,EAAa,MAAA;AAAA,EAAQ,QAAA;AAAA,EAAU,WAAA;AAAA,EAAa,MAAA;AAAA,EAAQ,QAAA;AAAA,EAAU,MAAA;AAAA,EAC9D,OAAA;AAAA,EAAS,SAAA;AAAA,EAAW,UAAA;AAAA,EAAY,SAAA;AAAA,EAAW,QAAA;AAAA,EAAU,OAAA;AAAA,EAAS,MAAA;AAAA,EAC9D,OAAA;AAAA,EAAS,OAAA;AAAA,EAAS,KAAA;AAAA,EAAO,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ;AACpE,CAAC;AAMM,IAAM,UAAA,GAAa;AAAA,EACxB,UAAA;AAAA,EAAY,WAAA;AAAA,EAAa,SAAA;AAAA,EAAW,WAAA;AAAA,EAAa,SAAA;AAAA,EAAW,UAAA;AAAA,EAC5D,WAAA;AAAA,EAAa,UAAA;AAAA,EAAY,YAAA;AAAA,EAAc,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,YAAA;AAAA,EACrD,aAAA;AAAA,EAAe,eAAA;AAAA,EAAiB,YAAA;AAAA,EAAc,WAAA;AAAA,EAAa,OAAA;AAAA,EAC3D,SAAA;AAAA,EAAW,WAAA;AAAA,EAAa,gBAAA;AAAA,EAAkB,WAAA;AAAA,EAAa,SAAA;AAAA,EACvD,YAAA;AAAA,EAAc,YAAA;AAAA,EAAc,UAAA;AAAA,EAAY,QAAA;AAAA,EAAU,QAAA;AAAA,EAAU,SAAA;AAAA,EAC5D,WAAA;AAAA,EAAa,OAAA;AAAA,EAAS;AACxB;AAEO,SAAS,oBAAoB,IAAA,EAAsB;AACxD,EAAA,OAAO,IAAA,CAAK,QAAQ,QAAA,EAAU,IAAI,EAAE,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA,CAAE,IAAA,EAAK;AAChE;AAEO,SAAS,uBAAuB,IAAA,EAAsB;AAE3D,EAAA,OAAO,oBAAoB,IAAI,CAAA,CAAE,SAAA,CAAU,MAAM,EAAE,WAAA,EAAY;AACjE;AAEO,SAAS,WAAW,IAAA,EAAwB;AACjD,EAAA,OAAO,KACJ,OAAA,CAAQ,QAAA,EAAU,IAAI,CAAA,CACtB,MAAM,IAAI,CAAA,CACV,GAAA,CAAI,CAAC,SAAS,IAAA,CAAK,IAAA,EAAM,CAAA,CACzB,OAAO,OAAO,CAAA;AACnB;AAKA,IAAM,aAAA,GAAgB,oCAAA;AAEf,SAAS,SAAS,IAAA,EAAwB;AAC/C,EAAA,MAAM,UAAA,GAAa,uBAAuB,IAAI,CAAA;AAE9C,EAAA,OAAA,CAAQ,UAAA,CAAW,KAAA,CAAM,aAAa,CAAA,IAAK,EAAC,EAAG,MAAA;AAAA,IAC7C,CAAC,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,IAAK,CAAC,UAAA,CAAW,GAAA,CAAI,CAAC;AAAA,GAC7C;AACF;AAEO,SAAS,aAAa,KAAA,EAAuB;AAClD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAA;AACpD;AAEO,SAAS,OAAO,MAAA,EAA4B;AACjD,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,KAAA,GAAQ,MAAM,WAAA,EAAY;AAChC,IAAA,IAAI,CAAC,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,EAAG;AACpB,MAAA,IAAA,CAAK,IAAI,KAAK,CAAA;AACd,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,IACnB;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,KAAA,CAAM,KAAA,EAAe,GAAA,EAAa,GAAA,EAAqB;AACrE,EAAA,OAAO,KAAK,GAAA,CAAI,IAAA,CAAK,IAAI,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AAC3C;AAEO,SAAS,iBAAiB,MAAA,EAA0C;AACzE,EAAA,MAAM,SAAiC,EAAC;AACxC,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAA,CAAO,KAAK,CAAA,GAAA,CAAK,MAAA,CAAO,KAAK,KAAK,CAAA,IAAK,CAAA;AAAA,EACzC;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,2BAA2B,IAAA,EAAuB;AAChE,EAAA,MAAM,KAAA,GAAQ,WAAW,IAAI,CAAA;AAC7B,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,cAAA,GAAiB,KAAK,QAAA,CAAS,GAAG,KAAK,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,IAAU,CAAA;AACvE,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA;AACxC,IAAA,MAAM,gBAAA,GAAmB,wBAAA,CAAyB,IAAA,CAAK,IAAI,CAAA;AAC3D,IAAA,IAAI,cAAA,IAAkB,iBAAiB,gBAAA,EAAkB;AACvD,MAAA,UAAA,IAAc,CAAA;AAAA,IAChB;AAAA,EACF;AACA,EAAA,OAAO,UAAA,IAAc,CAAA;AACvB;;;ACtGA,IAAM,eAAA,uBAAsB,OAAA,EAA2C;AAEvE,SAAS,cAAc,OAAA,EAA4C;AACjE,EAAA,IAAI,KAAA,GAAQ,eAAA,CAAgB,GAAA,CAAI,OAAO,CAAA;AACvC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,KAAA,uBAAY,GAAA,EAAI;AAChB,IAAA,KAAA,MAAW,CAAC,SAAA,EAAW,SAAS,KAAK,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC5D,MAAA,MAAM,KAAA,GAAQ,UAAU,WAAA,EAAY;AACpC,MAAA,KAAA,CAAM,GAAA,CAAI,OAAO,KAAK,CAAA;AACtB,MAAA,KAAA,MAAW,SAAS,SAAA,EAAW;AAC7B,QAAA,KAAA,CAAM,GAAA,CAAI,KAAA,CAAM,WAAA,EAAY,EAAG,KAAK,CAAA;AAAA,MACtC;AAAA,IACF;AACA,IAAA,eAAA,CAAgB,GAAA,CAAI,SAAS,KAAK,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,cAAA,CAAe,OAAe,OAAA,EAA+B;AAC3E,EAAA,MAAM,UAAA,GAAa,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AAC5C,EAAA,OAAO,aAAA,CAAc,OAAO,CAAA,CAAE,GAAA,CAAI,UAAU,CAAA,IAAK,UAAA;AACnD;AAEO,SAAS,eAAA,CAAgB,QAAkB,OAAA,EAAiC;AACjF,EAAA,OAAO,MAAA,CAAO,OAAO,GAAA,CAAI,CAAC,UAAU,cAAA,CAAe,KAAA,EAAO,OAAO,CAAC,CAAC,CAAA;AACrE;AAQO,SAAS,mBAAmB,QAAA,EAAyC;AAC1E,EAAA,MAAM,UAAwB,EAAC;AAC/B,EAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC5B,IAAA,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAA,GAAI,KAAA,CAAM,OAAA;AAAA,EACnC;AACA,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,mBAAmB,QAAA,EAAyD;AAC1F,EAAA,MAAM,KAAA,uBAAY,GAAA,EAA6B;AAC/C,EAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC5B,IAAA,KAAA,CAAM,IAAI,KAAA,CAAM,SAAA,CAAU,WAAA,EAAY,EAAG,MAAM,QAAQ,CAAA;AAAA,EACzD;AACA,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,sBAAA,CAAuB,MAAuB,SAAA,EAA6C;AACzG,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAqC;AAC7D,EAAA,KAAA,MAAW,KAAA,IAAS,MAAM,WAAA,CAAY,GAAA,CAAI,MAAM,SAAA,CAAU,WAAA,IAAe,KAAK,CAAA;AAC9E,EAAA,KAAA,MAAW,KAAA,IAAS,WAAW,WAAA,CAAY,GAAA,CAAI,MAAM,SAAA,CAAU,WAAA,IAAe,KAAK,CAAA;AACnF,EAAA,OAAO,CAAC,GAAG,WAAA,CAAY,MAAA,EAAQ,CAAA;AACjC;;;ACxDO,IAAM,sBAAA,GAA0C;AAAA;AAAA;AAAA,EAGrD,EAAE,WAAW,YAAA,EAAc,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAClE,EAAE,WAAW,MAAA,EAAQ,OAAA,EAAS,CAAC,SAAA,EAAW,QAAQ,CAAA,EAAG,QAAA,EAAU,WAAA,EAAY;AAAA,EAC3E,EAAE,WAAW,YAAA,EAAc,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAClE,EAAE,WAAW,OAAA,EAAS,OAAA,EAAS,CAAC,SAAA,EAAW,UAAU,CAAA,EAAG,QAAA,EAAU,WAAA,EAAY;AAAA,EAC9E,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,CAAC,WAAW,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACtE,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,QAAA,EAAU,OAAO,CAAA,EAAG,QAAA,EAAU,WAAA,EAAY;AAAA,EACxE,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EAC1D,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,CAAC,QAAQ,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACnE,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,KAAK,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAC5D,EAAE,WAAW,IAAA,EAAM,OAAA,EAAS,CAAC,QAAA,EAAU,MAAM,CAAA,EAAG,QAAA,EAAU,WAAA,EAAY;AAAA,EACtE,EAAE,SAAA,EAAW,MAAA,EAAQ,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACxD,EAAE,WAAW,QAAA,EAAU,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAC9D,EAAE,WAAW,IAAA,EAAM,OAAA,EAAS,CAAC,QAAQ,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAC9D,EAAE,SAAA,EAAW,MAAA,EAAQ,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACxD,EAAE,WAAW,MAAA,EAAQ,OAAA,EAAS,CAAC,eAAA,EAAiB,OAAO,CAAA,EAAG,QAAA,EAAU,WAAA,EAAY;AAAA,EAChF,EAAE,SAAA,EAAW,KAAA,EAAO,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACvD,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACzD,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EAC1D,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACzD,EAAE,WAAW,MAAA,EAAQ,OAAA,EAAS,CAAC,OAAO,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAC/D,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,MAAM,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAC7D,EAAE,WAAW,iBAAA,EAAmB,OAAA,EAAS,CAAC,KAAK,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACxE,EAAE,WAAW,qBAAA,EAAuB,OAAA,EAAS,CAAC,SAAS,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAChF,EAAE,SAAA,EAAW,cAAA,EAAgB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EAChE,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EAC3D,EAAE,WAAW,kBAAA,EAAoB,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACxE,EAAE,SAAA,EAAW,eAAA,EAAiB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACjE,EAAE,WAAW,6BAAA,EAA+B,OAAA,EAAS,CAAC,KAAK,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA;AAAA,EAEpF,EAAE,SAAA,EAAW,KAAA,EAAO,OAAA,EAAS,CAAC,YAAY,OAAA,EAAS,QAAQ,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO;AAAA,EAC/E,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,CAAC,KAAK,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC3D,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,qBAAqB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACvE,EAAE,WAAW,OAAA,EAAS,OAAA,EAAS,CAAC,iBAAiB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACrE,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,cAAA,EAAgB,uBAAuB,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO;AAAA,EACzF,EAAE,WAAW,QAAA,EAAU,OAAA,EAAS,CAAC,YAAY,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACjE,EAAE,WAAW,YAAA,EAAc,OAAA,EAAS,CAAC,KAAK,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC9D,EAAE,SAAA,EAAW,WAAA,EAAa,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACxD,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,QAAA,EAAU,QAAQ,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO;AAAA,EACpE,EAAE,SAAA,EAAW,MAAA,EAAQ,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACnD,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACzD,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,CAAC,OAAO,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC7D,EAAE,WAAW,YAAA,EAAc,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC7D,EAAE,WAAW,cAAA,EAAgB,OAAA,EAAS,CAAC,SAAS,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACpE,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACrD,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACpD,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACpD,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACrD,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACpD,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACpD,EAAE,WAAW,eAAA,EAAiB,OAAA,EAAS,CAAC,SAAS,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACrE,EAAE,WAAW,OAAA,EAAS,OAAA,EAAS,CAAC,cAAc,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAClE,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,SAAS,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAChE,EAAE,WAAW,OAAA,EAAS,OAAA,EAAS,CAAC,iBAAA,EAAmB,UAAU,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO;AAAA,EACjF,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACzD,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,SAAA,EAAW,KAAA,EAAO,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EAClD,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACzD,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACtD,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACzD,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACpD,EAAE,WAAW,WAAA,EAAa,OAAA,EAAS,CAAC,iBAAiB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACzE,EAAE,WAAW,aAAA,EAAe,OAAA,EAAS,CAAC,mBAAmB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC7E,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA;AAAA,EAEtD,EAAE,WAAW,eAAA,EAAiB,OAAA,EAAS,CAAC,MAAM,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EACrE,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,WAAW,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EACrE,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,CAAC,UAAU,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EACnE,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,eAAe,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EACzE,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,CAAC,UAAA,EAAY,QAAQ,CAAA,EAAG,QAAA,EAAU,SAAA,EAAU;AAAA,EAC7E,EAAE,SAAA,EAAW,eAAA,EAAiB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EAC/D,EAAE,WAAW,OAAA,EAAS,OAAA,EAAS,CAAC,OAAO,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EAC9D,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACxD,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EAC5D,EAAE,SAAA,EAAW,QAAA,EAAU,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACxD,EAAE,WAAW,OAAA,EAAS,OAAA,EAAS,CAAC,wBAAA,EAA0B,uBAAuB,CAAA,EAAG,QAAA,EAAU,SAAA,EAAU;AAAA,EACxG,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,aAAA,EAAe,WAAW,CAAA,EAAG,QAAA,EAAU,SAAA,EAAU;AAAA,EACpF,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACjE,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACjE,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EAC5D,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACjE,EAAE,SAAA,EAAW,eAAA,EAAiB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EAC/D,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,yBAAA,EAA2B,yBAAyB,CAAA,EAAG,QAAA,EAAU,SAAA,EAAU;AAAA,EACzG,EAAE,WAAW,WAAA,EAAa,OAAA,EAAS,CAAC,iBAAiB,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EAC5E,EAAE,WAAW,WAAA,EAAa,OAAA,EAAS,CAAC,uBAAuB,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA,EAClF,EAAE,SAAA,EAAW,oBAAA,EAAsB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACpE,EAAE,SAAA,EAAW,mBAAA,EAAqB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACnE,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,SAAA,EAAU;AAAA,EACjE,EAAE,WAAW,mBAAA,EAAqB,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,SAAA,EAAU;AAAA;AAAA,EAEvE,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACxD,EAAE,SAAA,EAAW,wBAAA,EAA0B,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACvE,EAAE,SAAA,EAAW,gBAAA,EAAkB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC/D,EAAE,WAAW,aAAA,EAAe,OAAA,EAAS,CAAC,YAAY,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACxE,EAAE,SAAA,EAAW,WAAA,EAAa,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC1D,EAAE,WAAW,YAAA,EAAc,OAAA,EAAS,CAAC,OAAO,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EAClE,EAAE,SAAA,EAAW,oBAAA,EAAsB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAEnE,EAAE,SAAA,EAAW,oBAAA,EAAsB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACnE,EAAE,SAAA,EAAW,WAAA,EAAa,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC1D,EAAE,SAAA,EAAW,aAAA,EAAe,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC5D,EAAE,SAAA,EAAW,aAAA,EAAe,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC5D,EAAE,WAAW,kBAAA,EAAoB,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACrE,EAAE,WAAW,qBAAA,EAAuB,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACxE,EAAE,SAAA,EAAW,SAAA,EAAW,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACxD,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,OAAO,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EAChE,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAChE,EAAE,SAAA,EAAW,MAAA,EAAQ,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAErD,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAChE,EAAE,SAAA,EAAW,oBAAA,EAAsB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACnE,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,kCAAkC,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACtF,EAAE,SAAA,EAAW,gBAAA,EAAkB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC/D,EAAE,SAAA,EAAW,cAAA,EAAgB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC7D,EAAE,WAAW,WAAA,EAAa,OAAA,EAAS,CAAC,eAAe,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACzE,EAAE,SAAA,EAAW,oBAAA,EAAsB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAEnE,EAAE,WAAW,YAAA,EAAc,OAAA,EAAS,CAAC,oBAAoB,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EAC/E,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC3D,EAAE,SAAA,EAAW,oBAAA,EAAsB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACnE,EAAE,SAAA,EAAW,yBAAA,EAA2B,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACxE,EAAE,SAAA,EAAW,wBAAA,EAA0B,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAEvE,EAAE,SAAA,EAAW,cAAA,EAAgB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC7D,EAAE,SAAA,EAAW,wBAAA,EAA0B,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACvE,EAAE,SAAA,EAAW,OAAA,EAAS,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACtD,EAAE,WAAW,2BAAA,EAA6B,OAAA,EAAS,CAAC,KAAA,EAAO,KAAK,CAAA,EAAG,QAAA,EAAU,QAAA,EAAS;AAAA,EACtF,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAEhE,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAChE,EAAE,SAAA,EAAW,gBAAA,EAAkB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC/D,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC3D,EAAE,WAAW,uBAAA,EAAyB,OAAA,EAAS,CAAC,YAAY,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EAClF,EAAE,SAAA,EAAW,eAAA,EAAiB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAE9D,EAAE,SAAA,EAAW,wBAAA,EAA0B,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACvE,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAChE,EAAE,SAAA,EAAW,sBAAA,EAAwB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACrE,EAAE,SAAA,EAAW,sBAAA,EAAwB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAErE,EAAE,WAAW,yBAAA,EAA2B,OAAA,EAAS,CAAC,cAAc,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACtF,EAAE,SAAA,EAAW,sBAAA,EAAwB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EACrE,EAAE,SAAA,EAAW,aAAA,EAAe,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAC5D,EAAE,SAAA,EAAW,mBAAA,EAAqB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAClE,EAAE,SAAA,EAAW,WAAA,EAAa,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAE1D,EAAE,WAAW,kBAAA,EAAoB,OAAA,EAAS,CAAC,kBAAkB,CAAA,EAAG,UAAU,QAAA,EAAS;AAAA,EACnF,EAAE,SAAA,EAAW,mBAAA,EAAqB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA,EAClE,EAAE,SAAA,EAAW,qBAAA,EAAuB,SAAS,EAAC,EAAG,UAAU,QAAA,EAAS;AAAA;AAAA,EAEpE,EAAE,SAAA,EAAW,eAAA,EAAiB,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EAC5D,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACzD,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,eAAe,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACtE,EAAE,WAAW,iBAAA,EAAmB,OAAA,EAAS,CAAC,iBAAiB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC/E,EAAE,WAAW,cAAA,EAAgB,OAAA,EAAS,CAAC,aAAa,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACxE,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EAC9D,EAAE,SAAA,EAAW,mBAAA,EAAqB,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EAChE,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACzD,EAAE,SAAA,EAAW,qBAAA,EAAuB,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EAClE,EAAE,WAAW,iBAAA,EAAmB,OAAA,EAAS,CAAC,iBAAiB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAC/E,EAAE,SAAA,EAAW,wBAAA,EAA0B,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACrE,EAAE,SAAA,EAAW,aAAA,EAAe,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EAC1D,EAAE,WAAW,cAAA,EAAgB,OAAA,EAAS,CAAC,uBAAuB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAClF,EAAE,WAAW,iBAAA,EAAmB,OAAA,EAAS,CAAC,qBAAqB,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EACnF,EAAE,WAAW,WAAA,EAAa,OAAA,EAAS,CAAC,UAAU,CAAA,EAAG,UAAU,MAAA,EAAO;AAAA,EAClE,EAAE,SAAA,EAAW,sBAAA,EAAwB,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA,EACnE,EAAE,SAAA,EAAW,YAAA,EAAc,SAAS,EAAC,EAAG,UAAU,MAAA,EAAO;AAAA;AAAA,EAEzD,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,4BAA4B,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACnF,EAAE,WAAW,UAAA,EAAY,OAAA,EAAS,CAAC,gBAAgB,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EAC5E,EAAE,SAAA,EAAW,qBAAA,EAAuB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACvE,EAAE,SAAA,EAAW,mBAAA,EAAqB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACrE,EAAE,WAAW,wBAAA,EAA0B,OAAA,EAAS,CAAC,cAAc,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACxF,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACnE,EAAE,SAAA,EAAW,mBAAA,EAAqB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACrE,EAAE,SAAA,EAAW,aAAA,EAAe,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EAC/D,EAAE,SAAA,EAAW,iBAAA,EAAmB,SAAS,EAAC,EAAG,UAAU,WAAA,EAAY;AAAA,EACnE,EAAE,WAAW,KAAA,EAAO,OAAA,EAAS,CAAC,eAAA,EAAiB,YAAY,CAAA,EAAG,QAAA,EAAU,WAAA,EAAY;AAAA,EACpF,EAAE,WAAW,8BAAA,EAAgC,OAAA,EAAS,CAAC,KAAK,CAAA,EAAG,UAAU,WAAA,EAAY;AAAA,EACrF,EAAE,WAAW,kBAAA,EAAoB,OAAA,EAAS,CAAC,IAAI,CAAA,EAAG,UAAU,WAAA;AAC9D;AAEO,IAAM,mBAAA,GAAsB,mBAAmB,sBAAsB;AAErE,IAAM,uBAAA,GAAsC;AAAA,EACjD,IAAA,EAAM,mBAAA;AAAA,EACN,eAAA,EAAiB,CAAC,YAAA,EAAc,YAAA,EAAc,SAAS,MAAM,CAAA;AAAA,EAC7D,cAAA,EAAgB,CAAC,SAAA,EAAW,KAAA,EAAO,QAAQ,CAAA;AAAA,EAC3C,aAAA,EAAe;AACjB;AAEO,IAAM,oBAAA,GAAmC;AAAA,EAC9C,IAAA,EAAM,gBAAA;AAAA,EACN,eAAA,EAAiB,CAAC,QAAA,EAAU,KAAA,EAAO,YAAY,CAAA;AAAA,EAC/C,cAAA,EAAgB,CAAC,QAAA,EAAU,OAAA,EAAS,WAAW,YAAY,CAAA;AAAA,EAC3D,aAAA,EAAe;AACjB,CAAA;AAEO,IAAM,qBAAA,GAAoC;AAAA,EAC/C,IAAA,EAAM,iBAAA;AAAA,EACN,eAAA,EAAiB,CAAC,SAAA,EAAW,wBAAA,EAA0B,gBAAgB,CAAA;AAAA,EACvE,cAAA,EAAgB,CAAC,aAAA,EAAe,WAAA,EAAa,KAAK,CAAA;AAAA,EAClD,aAAA,EAAe;AACjB,CAAA;AAEO,IAAM,eAAA,GAAgC;AAAA,EAC3C,uBAAA;AAAA,EACA,oBAAA;AAAA,EACA;AACF","file":"chunk-IY7OVF6Z.mjs","sourcesContent":["export const STOP_WORDS = new Set([\n // articles / prepositions / conjunctions\n \"the\", \"and\", \"or\", \"a\", \"an\", \"of\", \"for\", \"to\", \"with\", \"in\", \"on\", \"at\",\n \"by\", \"from\", \"as\", \"into\", \"onto\", \"upon\", \"via\", \"per\", \"plus\",\n // verbs / modals\n \"is\", \"are\", \"be\", \"was\", \"were\", \"will\", \"can\", \"should\", \"must\", \"have\",\n \"has\", \"had\", \"do\", \"does\", \"did\", \"get\", \"give\", \"go\", \"use\", \"see\",\n \"help\", \"work\", \"build\", \"show\", \"need\", \"want\", \"make\", \"let\",\n // pronouns / determiners\n \"it\", \"its\", \"this\", \"that\", \"these\", \"those\", \"we\", \"our\", \"you\", \"your\",\n \"they\", \"their\", \"us\", \"who\", \"what\", \"which\", \"how\",\n // common English fillers that leak into JDs\n \"no\", \"not\", \"all\", \"any\", \"also\", \"more\", \"well\", \"very\", \"highly\",\n \"across\", \"over\", \"under\", \"within\", \"about\", \"out\", \"up\", \"down\",\n \"new\", \"if\", \"so\", \"such\", \"both\", \"each\", \"one\", \"many\", \"only\",\n // JD/HR boilerplate — never skills\n \"years\", \"year\", \"experience\", \"required\", \"requirement\", \"requirements\",\n \"preferred\", \"role\", \"degree\", \"practices\", \"best\", \"skills\", \"team\",\n \"field\", \"related\", \"relevant\", \"desired\", \"strong\", \"solid\", \"good\",\n \"first\", \"based\", \"day\", \"week\", \"month\", \"time\", \"fast\", \"open\", \"dynamic\",\n]);\n\n// Shared role-noun vocabulary so the JD side (extractRoleKeywords) and the resume side\n// (title detection) agree on what counts as a \"role\" — this is what makes the two sides\n// comparable via token overlap instead of silently diverging whitelists. Broadened well\n// past engineering so non-eng resumes/JDs (design, sales, HR, finance, ...) get titles too.\nexport const ROLE_NOUNS = [\n \"engineer\", \"developer\", \"manager\", \"scientist\", \"analyst\", \"designer\",\n \"architect\", \"director\", \"consultant\", \"lead\", \"vp\", \"specialist\",\n \"coordinator\", \"administrator\", \"accountant\", \"recruiter\", \"nurse\",\n \"teacher\", \"associate\", \"representative\", \"executive\", \"officer\",\n \"technician\", \"strategist\", \"marketer\", \"writer\", \"editor\", \"product\",\n \"principal\", \"staff\", \"head\",\n];\n\nexport function normalizeWhitespace(text: string): string {\n return text.replace(/\\r\\n?/g, \"\\n\").replace(/\\s+/g, \" \").trim();\n}\n\nexport function normalizeForComparison(text: string): string {\n // NFKC folds fullwidth/accented chars before lowercasing (locale-independent)\n return normalizeWhitespace(text).normalize(\"NFKC\").toLowerCase();\n}\n\nexport function splitLines(text: string): string[] {\n return text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n}\n\n// Tech-aware token pattern: alnum start, optional internal . # + - / chars, alnum-or-symbol end.\n// Preserves c#, c++, node.js, ci/cd, full-stack, a/b as single tokens; drops lone single chars.\n// ponytail: custom regex beats any NLP lib here — domain-specific, no dep, fully deterministic\nconst TECH_TOKEN_RE = /[a-z0-9][a-z0-9.#+\\-/]*[a-z0-9#+]/g;\n\nexport function tokenize(text: string): string[] {\n const normalized = normalizeForComparison(text);\n // Require at least one letter: drops bare numbers (100, 3+, 50%) and keeps c#, 3d, node.js\n return (normalized.match(TECH_TOKEN_RE) ?? []).filter(\n (t) => /[a-z]/.test(t) && !STOP_WORDS.has(t)\n );\n}\n\nexport function escapeRegExp(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function unique(values: string[]): string[] {\n const seen = new Set<string>();\n const output: string[] = [];\n for (const value of values) {\n const lower = value.toLowerCase();\n if (!seen.has(lower)) {\n seen.add(lower);\n output.push(value);\n }\n }\n return output;\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nexport function countFrequencies(values: string[]): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const value of values) {\n counts[value] = (counts[value] ?? 0) + 1;\n }\n return counts;\n}\n\nexport function containsTableLikeStructure(text: string): boolean {\n const lines = splitLines(text);\n let tableLines = 0;\n for (const line of lines) {\n const hasPipeColumns = line.includes(\"|\") && line.split(\"|\").length >= 3;\n const hasTabColumns = /\\t.+\\t/.test(line);\n const hasAlignedSpaces = /( {3,})(\\S+)( {3,}\\S+)/.test(line);\n if (hasPipeColumns || hasTabColumns || hasAlignedSpaces) {\n tableLines += 1;\n }\n }\n return tableLines >= 2;\n}\n","import { KeywordCategory, KeywordRegistry, SkillAliases } from \"../types/config\";\nimport { unique } from \"./text\";\n\n// ponytail: Map lookup, not fuzzy match — add fuzzy only if real misses show up.\nconst aliasIndexCache = new WeakMap<SkillAliases, Map<string, string>>();\n\nfunction getAliasIndex(aliases: SkillAliases): Map<string, string> {\n let index = aliasIndexCache.get(aliases);\n if (!index) {\n index = new Map();\n for (const [canonical, aliasList] of Object.entries(aliases)) {\n const lower = canonical.toLowerCase();\n index.set(lower, lower);\n for (const alias of aliasList) {\n index.set(alias.toLowerCase(), lower);\n }\n }\n aliasIndexCache.set(aliases, index);\n }\n return index;\n}\n\nexport function normalizeSkill(skill: string, aliases: SkillAliases): string {\n const normalized = skill.trim().toLowerCase();\n return getAliasIndex(aliases).get(normalized) ?? normalized;\n}\n\nexport function normalizeSkills(skills: string[], aliases: SkillAliases): string[] {\n return unique(skills.map((skill) => normalizeSkill(skill, aliases)));\n}\n\nexport function skillMatched(candidate: string, targetSkills: Set<string>, aliases: SkillAliases): boolean {\n const normalizedCandidate = normalizeSkill(candidate, aliases);\n return targetSkills.has(normalizedCandidate);\n}\n\n/** Derive a flat canonical->aliases map from a keyword registry (back-compat with SkillAliases). */\nexport function deriveSkillAliases(registry: KeywordRegistry): SkillAliases {\n const aliases: SkillAliases = {};\n for (const entry of registry) {\n aliases[entry.canonical] = entry.aliases;\n }\n return aliases;\n}\n\n/** Derive a canonical->category lookup from a keyword registry. */\nexport function buildCategoryIndex(registry: KeywordRegistry): Map<string, KeywordCategory> {\n const index = new Map<string, KeywordCategory>();\n for (const entry of registry) {\n index.set(entry.canonical.toLowerCase(), entry.category);\n }\n return index;\n}\n\n/** Merge two registries by canonical term; entries in `overrides` win. */\nexport function mergeKeywordRegistries(base: KeywordRegistry, overrides: KeywordRegistry): KeywordRegistry {\n const byCanonical = new Map<string, KeywordRegistry[number]>();\n for (const entry of base) byCanonical.set(entry.canonical.toLowerCase(), entry);\n for (const entry of overrides) byCanonical.set(entry.canonical.toLowerCase(), entry);\n return [...byCanonical.values()];\n}\n","import { ATSProfile, KeywordRegistry } from \"../types/config\";\nimport { deriveSkillAliases } from \"../utils/skills\";\n\n// ponytail: broad seed set per category, still not an exhaustive taxonomy — grow on demand.\nexport const defaultKeywordRegistry: KeywordRegistry = [\n // languages / frameworks\n // ponytail: \"node\" split from javascript — Node.js runtime !== JS language\n { canonical: \"javascript\", aliases: [\"js\"], category: \"technical\" },\n { canonical: \"node\", aliases: [\"node.js\", \"nodejs\"], category: \"technical\" },\n { canonical: \"typescript\", aliases: [\"ts\"], category: \"technical\" },\n { canonical: \"react\", aliases: [\"reactjs\", \"react.js\"], category: \"technical\" },\n { canonical: \"angular\", aliases: [\"angularjs\"], category: \"technical\" },\n { canonical: \"vue\", aliases: [\"vue.js\", \"vuejs\"], category: \"technical\" },\n { canonical: \"svelte\", aliases: [], category: \"technical\" },\n { canonical: \"next.js\", aliases: [\"nextjs\"], category: \"technical\" },\n { canonical: \"c++\", aliases: [\"cpp\"], category: \"technical\" },\n { canonical: \"c#\", aliases: [\"csharp\", \".net\"], category: \"technical\" },\n { canonical: \"java\", aliases: [], category: \"technical\" },\n { canonical: \"python\", aliases: [\"py\"], category: \"technical\" },\n { canonical: \"go\", aliases: [\"golang\"], category: \"technical\" },\n { canonical: \"rust\", aliases: [], category: \"technical\" },\n { canonical: \"ruby\", aliases: [\"ruby on rails\", \"rails\"], category: \"technical\" },\n { canonical: \"php\", aliases: [], category: \"technical\" },\n { canonical: \"swift\", aliases: [], category: \"technical\" },\n { canonical: \"kotlin\", aliases: [], category: \"technical\" },\n { canonical: \"scala\", aliases: [], category: \"technical\" },\n { canonical: \"html\", aliases: [\"html5\"], category: \"technical\" },\n { canonical: \"css\", aliases: [\"css3\"], category: \"technical\" },\n { canonical: \"ios development\", aliases: [\"ios\"], category: \"technical\" },\n { canonical: \"android development\", aliases: [\"android\"], category: \"technical\" },\n { canonical: \"react native\", aliases: [], category: \"technical\" },\n { canonical: \"flutter\", aliases: [], category: \"technical\" },\n { canonical: \"machine learning\", aliases: [\"ml\"], category: \"technical\" },\n { canonical: \"deep learning\", aliases: [], category: \"technical\" },\n { canonical: \"natural language processing\", aliases: [\"nlp\"], category: \"technical\" },\n // tools / platforms / infra\n { canonical: \"sql\", aliases: [\"postgres\", \"mysql\", \"sqlite\"], category: \"tool\" },\n { canonical: \"graphql\", aliases: [\"gql\"], category: \"tool\" },\n { canonical: \"aws\", aliases: [\"amazon web services\"], category: \"tool\" },\n { canonical: \"azure\", aliases: [\"microsoft azure\"], category: \"tool\" },\n { canonical: \"gcp\", aliases: [\"google cloud\", \"google cloud platform\"], category: \"tool\" },\n { canonical: \"docker\", aliases: [\"containers\"], category: \"tool\" },\n { canonical: \"kubernetes\", aliases: [\"k8s\"], category: \"tool\" },\n { canonical: \"terraform\", aliases: [], category: \"tool\" },\n { canonical: \"ansible\", aliases: [], category: \"tool\" },\n { canonical: \"jenkins\", aliases: [], category: \"tool\" },\n { canonical: \"git\", aliases: [\"github\", \"gitlab\"], category: \"tool\" },\n { canonical: \"jira\", aliases: [], category: \"tool\" },\n { canonical: \"confluence\", aliases: [], category: \"tool\" },\n { canonical: \"pytorch\", aliases: [\"torch\"], category: \"tool\" },\n { canonical: \"tensorflow\", aliases: [\"tf\"], category: \"tool\" },\n { canonical: \"scikit-learn\", aliases: [\"sklearn\"], category: \"tool\" },\n { canonical: \"pandas\", aliases: [], category: \"tool\" },\n { canonical: \"numpy\", aliases: [], category: \"tool\" },\n { canonical: \"fastapi\", aliases: [], category: \"tool\" },\n { canonical: \"flask\", aliases: [], category: \"tool\" },\n { canonical: \"django\", aliases: [], category: \"tool\" },\n { canonical: \"kafka\", aliases: [], category: \"tool\" },\n { canonical: \"redis\", aliases: [], category: \"tool\" },\n { canonical: \"elasticsearch\", aliases: [\"elastic\"], category: \"tool\" },\n { canonical: \"spark\", aliases: [\"apache spark\"], category: \"tool\" },\n { canonical: \"tableau\", aliases: [], category: \"tool\" },\n { canonical: \"power bi\", aliases: [\"powerbi\"], category: \"tool\" },\n { canonical: \"excel\", aliases: [\"microsoft excel\", \"ms excel\"], category: \"tool\" },\n { canonical: \"salesforce\", aliases: [], category: \"tool\" },\n { canonical: \"hubspot\", aliases: [], category: \"tool\" },\n { canonical: \"sap\", aliases: [], category: \"tool\" },\n { canonical: \"quickbooks\", aliases: [], category: \"tool\" },\n { canonical: \"workday\", aliases: [], category: \"tool\" },\n { canonical: \"zendesk\", aliases: [], category: \"tool\" },\n { canonical: \"servicenow\", aliases: [], category: \"tool\" },\n { canonical: \"figma\", aliases: [], category: \"tool\" },\n { canonical: \"photoshop\", aliases: [\"adobe photoshop\"], category: \"tool\" },\n { canonical: \"illustrator\", aliases: [\"adobe illustrator\"], category: \"tool\" },\n { canonical: \"autocad\", aliases: [], category: \"tool\" },\n // engineering concepts\n { canonical: \"accessibility\", aliases: [\"a11y\"], category: \"concept\" },\n { canonical: \"frontend\", aliases: [\"front-end\"], category: \"concept\" },\n { canonical: \"backend\", aliases: [\"back-end\"], category: \"concept\" },\n { canonical: \"security\", aliases: [\"cybersecurity\"], category: \"concept\" },\n { canonical: \"testing\", aliases: [\"unittest\", \"pytest\"], category: \"concept\" },\n { canonical: \"microservices\", aliases: [], category: \"concept\" },\n { canonical: \"agile\", aliases: [\"scrum\"], category: \"concept\" },\n { canonical: \"kanban\", aliases: [], category: \"concept\" },\n { canonical: \"blockchain\", aliases: [], category: \"concept\" },\n { canonical: \"devops\", aliases: [], category: \"concept\" },\n { canonical: \"ci/cd\", aliases: [\"continuous integration\", \"continuous deployment\"], category: \"concept\" },\n { canonical: \"rest api\", aliases: [\"restful api\", \"rest apis\"], category: \"concept\" },\n { canonical: \"design patterns\", aliases: [], category: \"concept\" },\n { canonical: \"data structures\", aliases: [], category: \"concept\" },\n { canonical: \"algorithms\", aliases: [], category: \"concept\" },\n { canonical: \"cloud computing\", aliases: [], category: \"concept\" },\n { canonical: \"system design\", aliases: [], category: \"concept\" },\n { canonical: \"tdd\", aliases: [\"test driven development\", \"test-driven development\"], category: \"concept\" },\n { canonical: \"ux design\", aliases: [\"user experience\"], category: \"concept\" },\n { canonical: \"ui design\", aliases: [\"user interface design\"], category: \"concept\" },\n { canonical: \"project management\", aliases: [], category: \"concept\" },\n { canonical: \"change management\", aliases: [], category: \"concept\" },\n { canonical: \"risk management\", aliases: [], category: \"concept\" },\n { canonical: \"quality assurance\", aliases: [\"qa\"], category: \"concept\" },\n // product / data domain\n { canonical: \"roadmap\", aliases: [], category: \"domain\" },\n { canonical: \"stakeholder management\", aliases: [], category: \"domain\" },\n { canonical: \"prioritization\", aliases: [], category: \"domain\" },\n { canonical: \"a/b testing\", aliases: [\"ab testing\"], category: \"domain\" },\n { canonical: \"analytics\", aliases: [], category: \"domain\" },\n { canonical: \"statistics\", aliases: [\"stats\"], category: \"domain\" },\n { canonical: \"data visualization\", aliases: [], category: \"domain\" },\n // finance / accounting domain\n { canonical: \"financial analysis\", aliases: [], category: \"domain\" },\n { canonical: \"budgeting\", aliases: [], category: \"domain\" },\n { canonical: \"forecasting\", aliases: [], category: \"domain\" },\n { canonical: \"bookkeeping\", aliases: [], category: \"domain\" },\n { canonical: \"accounts payable\", aliases: [\"ap\"], category: \"domain\" },\n { canonical: \"accounts receivable\", aliases: [\"ar\"], category: \"domain\" },\n { canonical: \"payroll\", aliases: [], category: \"domain\" },\n { canonical: \"auditing\", aliases: [\"audit\"], category: \"domain\" },\n { canonical: \"tax preparation\", aliases: [], category: \"domain\" },\n { canonical: \"gaap\", aliases: [], category: \"domain\" },\n // sales / account management domain\n { canonical: \"lead generation\", aliases: [], category: \"domain\" },\n { canonical: \"account management\", aliases: [], category: \"domain\" },\n { canonical: \"crm\", aliases: [\"customer relationship management\"], category: \"domain\" },\n { canonical: \"sales pipeline\", aliases: [], category: \"domain\" },\n { canonical: \"cold calling\", aliases: [], category: \"domain\" },\n { canonical: \"upselling\", aliases: [\"cross-selling\"], category: \"domain\" },\n { canonical: \"customer retention\", aliases: [], category: \"domain\" },\n // human resources domain\n { canonical: \"recruiting\", aliases: [\"talent acquisition\"], category: \"domain\" },\n { canonical: \"onboarding\", aliases: [], category: \"domain\" },\n { canonical: \"employee relations\", aliases: [], category: \"domain\" },\n { canonical: \"benefits administration\", aliases: [], category: \"domain\" },\n { canonical: \"performance management\", aliases: [], category: \"domain\" },\n // healthcare domain\n { canonical: \"patient care\", aliases: [], category: \"domain\" },\n { canonical: \"clinical documentation\", aliases: [], category: \"domain\" },\n { canonical: \"hipaa\", aliases: [], category: \"domain\" },\n { canonical: \"electronic health records\", aliases: [\"ehr\", \"emr\"], category: \"domain\" },\n { canonical: \"medical billing\", aliases: [], category: \"domain\" },\n // legal domain\n { canonical: \"contract review\", aliases: [], category: \"domain\" },\n { canonical: \"legal research\", aliases: [], category: \"domain\" },\n { canonical: \"litigation\", aliases: [], category: \"domain\" },\n { canonical: \"regulatory compliance\", aliases: [\"compliance\"], category: \"domain\" },\n { canonical: \"due diligence\", aliases: [], category: \"domain\" },\n // education domain\n { canonical: \"curriculum development\", aliases: [], category: \"domain\" },\n { canonical: \"lesson planning\", aliases: [], category: \"domain\" },\n { canonical: \"classroom management\", aliases: [], category: \"domain\" },\n { canonical: \"instructional design\", aliases: [], category: \"domain\" },\n // operations / supply chain domain\n { canonical: \"supply chain management\", aliases: [\"supply chain\"], category: \"domain\" },\n { canonical: \"inventory management\", aliases: [], category: \"domain\" },\n { canonical: \"procurement\", aliases: [], category: \"domain\" },\n { canonical: \"vendor management\", aliases: [], category: \"domain\" },\n { canonical: \"logistics\", aliases: [], category: \"domain\" },\n // customer service domain\n { canonical: \"customer support\", aliases: [\"customer service\"], category: \"domain\" },\n { canonical: \"technical support\", aliases: [], category: \"domain\" },\n { canonical: \"conflict resolution\", aliases: [], category: \"domain\" },\n // soft skills\n { canonical: \"communication\", aliases: [], category: \"soft\" },\n { canonical: \"leadership\", aliases: [], category: \"soft\" },\n { canonical: \"teamwork\", aliases: [\"collaboration\"], category: \"soft\" },\n { canonical: \"problem solving\", aliases: [\"problem-solving\"], category: \"soft\" },\n { canonical: \"adaptability\", aliases: [\"flexibility\"], category: \"soft\" },\n { canonical: \"time management\", aliases: [], category: \"soft\" },\n { canonical: \"critical thinking\", aliases: [], category: \"soft\" },\n { canonical: \"creativity\", aliases: [], category: \"soft\" },\n { canonical: \"attention to detail\", aliases: [], category: \"soft\" },\n { canonical: \"decision making\", aliases: [\"decision-making\"], category: \"soft\" },\n { canonical: \"emotional intelligence\", aliases: [], category: \"soft\" },\n { canonical: \"negotiation\", aliases: [], category: \"soft\" },\n { canonical: \"organization\", aliases: [\"organizational skills\"], category: \"soft\" },\n { canonical: \"public speaking\", aliases: [\"presentation skills\"], category: \"soft\" },\n { canonical: \"mentoring\", aliases: [\"coaching\"], category: \"soft\" },\n { canonical: \"interpersonal skills\", aliases: [], category: \"soft\" },\n { canonical: \"work ethic\", aliases: [], category: \"soft\" },\n // marketing\n { canonical: \"seo\", aliases: [\"search engine optimization\"], category: \"marketing\" },\n { canonical: \"branding\", aliases: [\"brand strategy\"], category: \"marketing\" },\n { canonical: \"campaign management\", aliases: [], category: \"marketing\" },\n { canonical: \"content marketing\", aliases: [], category: \"marketing\" },\n { canonical: \"social media marketing\", aliases: [\"social media\"], category: \"marketing\" },\n { canonical: \"email marketing\", aliases: [], category: \"marketing\" },\n { canonical: \"digital marketing\", aliases: [], category: \"marketing\" },\n { canonical: \"copywriting\", aliases: [], category: \"marketing\" },\n { canonical: \"market research\", aliases: [], category: \"marketing\" },\n { canonical: \"ppc\", aliases: [\"pay-per-click\", \"google ads\"], category: \"marketing\" },\n { canonical: \"conversion rate optimization\", aliases: [\"cro\"], category: \"marketing\" },\n { canonical: \"public relations\", aliases: [\"pr\"], category: \"marketing\" },\n];\n\nexport const defaultSkillAliases = deriveSkillAliases(defaultKeywordRegistry);\n\nexport const softwareEngineerProfile: ATSProfile = {\n name: \"software-engineer\",\n mandatorySkills: [\"javascript\", \"typescript\", \"react\", \"node\"],\n optionalSkills: [\"graphql\", \"sql\", \"docker\"],\n minExperience: 3,\n};\n\nexport const dataScientistProfile: ATSProfile = {\n name: \"data-scientist\",\n mandatorySkills: [\"python\", \"sql\", \"statistics\"],\n optionalSkills: [\"pandas\", \"numpy\", \"pytorch\", \"tensorflow\"],\n minExperience: 2,\n};\n\nexport const productManagerProfile: ATSProfile = {\n name: \"product-manager\",\n mandatorySkills: [\"roadmap\", \"stakeholder management\", \"prioritization\"],\n optionalSkills: [\"a/b testing\", \"analytics\", \"sql\"],\n minExperience: 3,\n};\n\nexport const defaultProfiles: ATSProfile[] = [\n softwareEngineerProfile,\n dataScientistProfile,\n productManagerProfile,\n];\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -129,6 +129,39 @@ var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
129
129
|
"open",
|
|
130
130
|
"dynamic"
|
|
131
131
|
]);
|
|
132
|
+
var ROLE_NOUNS = [
|
|
133
|
+
"engineer",
|
|
134
|
+
"developer",
|
|
135
|
+
"manager",
|
|
136
|
+
"scientist",
|
|
137
|
+
"analyst",
|
|
138
|
+
"designer",
|
|
139
|
+
"architect",
|
|
140
|
+
"director",
|
|
141
|
+
"consultant",
|
|
142
|
+
"lead",
|
|
143
|
+
"vp",
|
|
144
|
+
"specialist",
|
|
145
|
+
"coordinator",
|
|
146
|
+
"administrator",
|
|
147
|
+
"accountant",
|
|
148
|
+
"recruiter",
|
|
149
|
+
"nurse",
|
|
150
|
+
"teacher",
|
|
151
|
+
"associate",
|
|
152
|
+
"representative",
|
|
153
|
+
"executive",
|
|
154
|
+
"officer",
|
|
155
|
+
"technician",
|
|
156
|
+
"strategist",
|
|
157
|
+
"marketer",
|
|
158
|
+
"writer",
|
|
159
|
+
"editor",
|
|
160
|
+
"product",
|
|
161
|
+
"principal",
|
|
162
|
+
"staff",
|
|
163
|
+
"head"
|
|
164
|
+
];
|
|
132
165
|
function normalizeWhitespace(text) {
|
|
133
166
|
return text.replace(/\r\n?/g, "\n").replace(/\s+/g, " ").trim();
|
|
134
167
|
}
|
|
@@ -387,8 +420,9 @@ function extractPreferredSkills(lines) {
|
|
|
387
420
|
}
|
|
388
421
|
return preferred;
|
|
389
422
|
}
|
|
423
|
+
var ROLE_NOUN_RE = new RegExp(`(${ROLE_NOUNS.join("|")})`, "gi");
|
|
390
424
|
function extractRoleKeywords(text) {
|
|
391
|
-
const roleMatches = text.match(
|
|
425
|
+
const roleMatches = text.match(ROLE_NOUN_RE) ?? [];
|
|
392
426
|
const fallback = roleMatches.length === 0 ? [text.split(/\n/)[0] ?? ""] : [];
|
|
393
427
|
return unique(tokenize([...roleMatches, ...fallback].join(" ")));
|
|
394
428
|
}
|
|
@@ -419,6 +453,32 @@ function isLanguageRequired(lang, jobDescription) {
|
|
|
419
453
|
return LANG_SECTION_RE.test(line) || LANG_REQUIREMENT_HINT_RE.test(line);
|
|
420
454
|
});
|
|
421
455
|
}
|
|
456
|
+
var YEARS_BEFORE_SKILL_RE = /(\d{1,2})\+?\s*years?\s*(?:of|in|with)?\s*((?:[a-z0-9][a-z0-9.#+\-/]*\s*){1,4})/gi;
|
|
457
|
+
var SKILL_BEFORE_YEARS_RE = /((?:[a-z0-9][a-z0-9.#+\-/]*[\s,]*){1,4})\(?(\d{1,2})\+?\s*years?\)?/gi;
|
|
458
|
+
function extractSkillExperienceRequirements(lines, skillVocab, aliases) {
|
|
459
|
+
const requirements = /* @__PURE__ */ new Map();
|
|
460
|
+
const record = (rawWord, years) => {
|
|
461
|
+
if (!Number.isFinite(years) || years <= 0 || years > 60) return;
|
|
462
|
+
const canonical = normalizeSkill(rawWord, aliases);
|
|
463
|
+
if (!skillVocab.has(rawWord.toLowerCase()) && !skillVocab.has(canonical)) return;
|
|
464
|
+
const existing = requirements.get(canonical);
|
|
465
|
+
if (existing === void 0 || years > existing) {
|
|
466
|
+
requirements.set(canonical, years);
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
for (const line of lines) {
|
|
470
|
+
for (const re of [YEARS_BEFORE_SKILL_RE, SKILL_BEFORE_YEARS_RE]) {
|
|
471
|
+
re.lastIndex = 0;
|
|
472
|
+
let match;
|
|
473
|
+
while (match = re.exec(line)) {
|
|
474
|
+
const years = Number.parseInt(re === YEARS_BEFORE_SKILL_RE ? match[1] : match[2], 10);
|
|
475
|
+
const candidateWords = tokenize(re === YEARS_BEFORE_SKILL_RE ? match[2] : match[1]);
|
|
476
|
+
for (const word of candidateWords) record(word, years);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return [...requirements.entries()].map(([skill, years]) => ({ skill, years })).sort((a, b) => a.skill.localeCompare(b.skill));
|
|
481
|
+
}
|
|
422
482
|
function extractDegreeLevels(text) {
|
|
423
483
|
const found = /* @__PURE__ */ new Set();
|
|
424
484
|
for (const [pattern, canonical] of DEGREE_VARIANTS) {
|
|
@@ -449,6 +509,11 @@ function parseJobDescription(jobDescription, config) {
|
|
|
449
509
|
const bodyTokens = tokenize(normalizedText).filter(isSkillLike);
|
|
450
510
|
const roleKeywords = extractRoleKeywords(jobDescription);
|
|
451
511
|
const keywords = unique([...requiredSkills, ...preferredSkills, ...roleKeywords, ...bodyTokens]);
|
|
512
|
+
const skillExperienceRequirements = extractSkillExperienceRequirements(
|
|
513
|
+
lines,
|
|
514
|
+
skillVocab,
|
|
515
|
+
config.skillAliases
|
|
516
|
+
);
|
|
452
517
|
return {
|
|
453
518
|
raw: jobDescription,
|
|
454
519
|
normalizedText,
|
|
@@ -457,6 +522,7 @@ function parseJobDescription(jobDescription, config) {
|
|
|
457
522
|
roleKeywords,
|
|
458
523
|
keywords,
|
|
459
524
|
minExperienceYears: extractMinExperience(jobDescription),
|
|
525
|
+
skillExperienceRequirements,
|
|
460
526
|
educationRequirements: extractDegreeLevels(jobDescription),
|
|
461
527
|
keywordSurfaceForms: collectKeywordSurfaceForms(jobDescription, config.skillAliases),
|
|
462
528
|
// A language only counts as required if its mention carries a requirement/level cue
|
|
@@ -653,6 +719,10 @@ var STRONG_VERBS = [
|
|
|
653
719
|
"increased"
|
|
654
720
|
];
|
|
655
721
|
var WEAK_VERBS = ["worked", "helped", "performed", "responsible", "assisted", "participated", "involved"];
|
|
722
|
+
var TITLE_SENIORITY_PREFIXES = ["senior", "lead", "principal", "staff", "vp", "director"];
|
|
723
|
+
var TITLE_MODIFIER_WORDS = ["software", "full\\s*stack", "frontend", "backend"];
|
|
724
|
+
var TITLE_PREFIX_WORDS = unique([...TITLE_SENIORITY_PREFIXES, ...TITLE_MODIFIER_WORDS, ...ROLE_NOUNS]);
|
|
725
|
+
var TITLE_RE = new RegExp(`^(${TITLE_PREFIX_WORDS.join("|")})[^,(-]*`, "i");
|
|
656
726
|
var METRIC_RE = /\d|%|\$|\bk\+|\bm\+/i;
|
|
657
727
|
function classifyAchievement(line) {
|
|
658
728
|
const lower = line.toLowerCase();
|
|
@@ -730,21 +800,26 @@ function parseExperience(sectionContent, referenceDate) {
|
|
|
730
800
|
const achievements = [];
|
|
731
801
|
for (const line of lines) {
|
|
732
802
|
const range = parseDateRange(line, referenceDate);
|
|
803
|
+
const titleMatch = line.match(TITLE_RE);
|
|
804
|
+
const title = titleMatch ? titleMatch[0].trim() : void 0;
|
|
733
805
|
if (range) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
806
|
+
if (title) {
|
|
807
|
+
jobTitles.push(title.toLowerCase());
|
|
808
|
+
entries.push({ title, dates: range, description: line });
|
|
737
809
|
} else {
|
|
738
|
-
entries.
|
|
810
|
+
const previous = entries[entries.length - 1];
|
|
811
|
+
if (previous && !previous.dates) {
|
|
812
|
+
previous.dates = range;
|
|
813
|
+
} else {
|
|
814
|
+
entries.push({ dates: range });
|
|
815
|
+
}
|
|
739
816
|
}
|
|
740
817
|
if (range.durationInMonths) {
|
|
741
818
|
rangesInMonths.push(range.durationInMonths);
|
|
742
819
|
}
|
|
743
820
|
continue;
|
|
744
821
|
}
|
|
745
|
-
|
|
746
|
-
if (titleMatch) {
|
|
747
|
-
const title = titleMatch[0].trim();
|
|
822
|
+
if (title) {
|
|
748
823
|
jobTitles.push(title.toLowerCase());
|
|
749
824
|
const entry = { title, description: line };
|
|
750
825
|
entries.push(entry);
|
|
@@ -766,6 +841,14 @@ function parseEducation(sectionContent) {
|
|
|
766
841
|
function collectKeywords(text) {
|
|
767
842
|
return unique(tokenize(text));
|
|
768
843
|
}
|
|
844
|
+
var EMAIL_RE = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/i;
|
|
845
|
+
var PHONE_RE = /\+?\(?\d{1,4}\)?(?:[\s.-]?\d{2,4}){2,4}/;
|
|
846
|
+
function extractContact(text) {
|
|
847
|
+
const email = text.match(EMAIL_RE)?.[0];
|
|
848
|
+
const phone = text.match(PHONE_RE)?.[0]?.trim();
|
|
849
|
+
if (!email && !phone) return void 0;
|
|
850
|
+
return { email, phone };
|
|
851
|
+
}
|
|
769
852
|
function parseResume(resumeText, config) {
|
|
770
853
|
const normalizedText = normalizeWhitespace(resumeText);
|
|
771
854
|
const { sections, detected } = extractSections(resumeText);
|
|
@@ -801,6 +884,12 @@ function parseResume(resumeText, config) {
|
|
|
801
884
|
warnings.push(`${section} section not detected`);
|
|
802
885
|
}
|
|
803
886
|
}
|
|
887
|
+
const contact = extractContact(resumeText);
|
|
888
|
+
if (!contact?.email) {
|
|
889
|
+
warnings.push(
|
|
890
|
+
"No email address detected \u2014 most ATS require a parseable contact email"
|
|
891
|
+
);
|
|
892
|
+
}
|
|
804
893
|
return {
|
|
805
894
|
raw: resumeText,
|
|
806
895
|
normalizedText,
|
|
@@ -816,6 +905,7 @@ function parseResume(resumeText, config) {
|
|
|
816
905
|
totalExperienceYears,
|
|
817
906
|
keywords: collectKeywords(normalizedText),
|
|
818
907
|
languages: parseLanguageMentions(resumeText),
|
|
908
|
+
contact,
|
|
819
909
|
warnings
|
|
820
910
|
};
|
|
821
911
|
}
|
|
@@ -865,6 +955,11 @@ var RuleEngine = class {
|
|
|
865
955
|
totalPenalty += 5;
|
|
866
956
|
warnings.push("Few recognizable sections found (penalty 5)");
|
|
867
957
|
}
|
|
958
|
+
if (!input.resume.contact?.email) {
|
|
959
|
+
const penalty = this.config.sectionPenalties.missingContact;
|
|
960
|
+
totalPenalty += penalty;
|
|
961
|
+
warnings.push(`Missing contact email (penalty ${penalty})`);
|
|
962
|
+
}
|
|
868
963
|
return { totalPenalty, warnings };
|
|
869
964
|
}
|
|
870
965
|
evaluate(input) {
|
|
@@ -936,8 +1031,9 @@ function scoreExperience(resume, job, config) {
|
|
|
936
1031
|
const yearCoverage = clamp(resume.totalExperienceYears / requiredYears, 0, 2);
|
|
937
1032
|
const yearsComponent = clamp(yearCoverage, 0, 1) * EXPERIENCE_YEARS_WEIGHT;
|
|
938
1033
|
const jobRoleSet = new Set(job.roleKeywords.map((value) => value.toLowerCase()));
|
|
939
|
-
const
|
|
940
|
-
const
|
|
1034
|
+
const resumeTitleTokens = new Set(resume.jobTitles.flatMap((t) => tokenize(t)));
|
|
1035
|
+
const matchedRoles = job.roleKeywords.filter((rk) => resumeTitleTokens.has(rk.toLowerCase()));
|
|
1036
|
+
const titleCoverage = jobRoleSet.size === 0 ? 1 : matchedRoles.length / jobRoleSet.size;
|
|
941
1037
|
const roleComponent = clamp(titleCoverage, 0, 1) * EXPERIENCE_ROLE_WEIGHT;
|
|
942
1038
|
const score = clamp((yearsComponent + roleComponent) * 100, 0, 100);
|
|
943
1039
|
const missingYears = Math.max(requiredYears - resume.totalExperienceYears, 0);
|
|
@@ -1013,6 +1109,17 @@ function scoreKeywords(resume, job, config) {
|
|
|
1013
1109
|
keywordWeights
|
|
1014
1110
|
};
|
|
1015
1111
|
}
|
|
1112
|
+
function computeSkillExperienceGaps(resume, job, config) {
|
|
1113
|
+
if (job.skillExperienceRequirements.length === 0) return [];
|
|
1114
|
+
const resumeSkills = new Set(normalizeSkills(resume.skills, config.skillAliases));
|
|
1115
|
+
const gaps = [];
|
|
1116
|
+
for (const { skill, years } of job.skillExperienceRequirements) {
|
|
1117
|
+
if (resumeSkills.has(skill) && resume.totalExperienceYears < years) {
|
|
1118
|
+
gaps.push({ skill, requiredYears: years, resumeYears: resume.totalExperienceYears });
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return gaps.sort((a, b) => a.skill.localeCompare(b.skill));
|
|
1122
|
+
}
|
|
1016
1123
|
function scoreEducation(resume, job) {
|
|
1017
1124
|
if (job.educationRequirements.length === 0) {
|
|
1018
1125
|
return 100;
|
|
@@ -1046,6 +1153,7 @@ function calculateScore(resume, job, config) {
|
|
|
1046
1153
|
resume.languages,
|
|
1047
1154
|
job.requiredLanguages
|
|
1048
1155
|
);
|
|
1156
|
+
const skillExperienceGaps = computeSkillExperienceGaps(resume, job, config);
|
|
1049
1157
|
return {
|
|
1050
1158
|
score: clamp(Number(weightedScore.toFixed(2)), 0, 100),
|
|
1051
1159
|
breakdown,
|
|
@@ -1059,6 +1167,7 @@ function calculateScore(resume, job, config) {
|
|
|
1059
1167
|
achievementStrength,
|
|
1060
1168
|
matchedLanguages,
|
|
1061
1169
|
missingLanguages,
|
|
1170
|
+
skillExperienceGaps,
|
|
1062
1171
|
suggestions: [],
|
|
1063
1172
|
warnings: [],
|
|
1064
1173
|
// detectedSections / parsedExperienceYears / experienceGap / experienceEntries: filled by index.ts
|
|
@@ -1301,7 +1410,10 @@ var DEFAULT_SECTION_PENALTIES = {
|
|
|
1301
1410
|
missingSummary: 4,
|
|
1302
1411
|
missingExperience: 10,
|
|
1303
1412
|
missingSkills: 8,
|
|
1304
|
-
missingEducation: 6
|
|
1413
|
+
missingEducation: 6,
|
|
1414
|
+
// Warning-only by default — see determinism note in the contact/parseability plan step;
|
|
1415
|
+
// callers who want it to actually move the score can override via config.sectionPenalties.
|
|
1416
|
+
missingContact: 0
|
|
1305
1417
|
};
|
|
1306
1418
|
function normalizeWeights(weights) {
|
|
1307
1419
|
const total = weights.skills + weights.experience + weights.keywords + weights.education;
|
|
@@ -1422,6 +1534,16 @@ var SuggestionEngine = class {
|
|
|
1422
1534
|
"Your resume may use a multi-column layout. Export as a single-column PDF or paste plain text \u2014 most ATS systems and this parser work best with a linear layout."
|
|
1423
1535
|
);
|
|
1424
1536
|
}
|
|
1537
|
+
if (!input.resume.contact?.email) {
|
|
1538
|
+
suggestions.push(
|
|
1539
|
+
"Add a clearly formatted email address near the top of your resume so ATS and recruiters can contact you."
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
for (const gap of input.score.skillExperienceGaps) {
|
|
1543
|
+
suggestions.push(
|
|
1544
|
+
`The role asks for ${gap.requiredYears}+ years of ${gap.skill}; make that duration explicit in your experience section.`
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1425
1547
|
return { suggestions, warnings };
|
|
1426
1548
|
}
|
|
1427
1549
|
};
|
|
@@ -1587,7 +1709,8 @@ No explanations. No markdown. No additional text.`;
|
|
|
1587
1709
|
});
|
|
1588
1710
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1589
1711
|
const id = globalThis.setTimeout(() => reject(new Error(`LLM call timeout after ${this.timeoutMs}ms`)), this.timeoutMs);
|
|
1590
|
-
void clientPromise.finally(() => globalThis.clearTimeout(id))
|
|
1712
|
+
void clientPromise.finally(() => globalThis.clearTimeout(id)).catch(() => {
|
|
1713
|
+
});
|
|
1591
1714
|
});
|
|
1592
1715
|
void timeoutPromise.catch(() => {
|
|
1593
1716
|
});
|
|
@@ -2085,6 +2208,7 @@ function analyzeResume(input) {
|
|
|
2085
2208
|
achievementStrength: scoring.achievementStrength,
|
|
2086
2209
|
matchedLanguages: scoring.matchedLanguages,
|
|
2087
2210
|
missingLanguages: scoring.missingLanguages,
|
|
2211
|
+
skillExperienceGaps: scoring.skillExperienceGaps,
|
|
2088
2212
|
experienceGap: scoring.experienceGap,
|
|
2089
2213
|
detectedSections: parsedResume.detectedSections,
|
|
2090
2214
|
parsedExperienceYears: parsedResume.totalExperienceYears,
|
|
@@ -2156,6 +2280,7 @@ async function analyzeResumeAsync(input) {
|
|
|
2156
2280
|
achievementStrength: scoring.achievementStrength,
|
|
2157
2281
|
matchedLanguages: scoring.matchedLanguages,
|
|
2158
2282
|
missingLanguages: scoring.missingLanguages,
|
|
2283
|
+
skillExperienceGaps: scoring.skillExperienceGaps,
|
|
2159
2284
|
experienceGap: scoring.experienceGap,
|
|
2160
2285
|
detectedSections: parsedResume.detectedSections,
|
|
2161
2286
|
parsedExperienceYears: parsedResume.totalExperienceYears,
|