@pranavraut033/ats-checker 1.1.1 → 1.3.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 +111 -5
- package/dist/chunk-ZJ5E4H7Z.mjs +446 -0
- package/dist/chunk-ZJ5E4H7Z.mjs.map +1 -0
- package/dist/{index.js → index.cjs} +512 -67
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +4 -259
- package/dist/index.d.ts +4 -259
- package/dist/index.mjs +278 -274
- package/dist/index.mjs.map +1 -1
- package/dist/lang/de/index.cjs +70 -0
- package/dist/lang/de/index.cjs.map +1 -0
- package/dist/lang/de/index.d.mts +16 -0
- package/dist/lang/de/index.d.ts +16 -0
- package/dist/lang/de/index.mjs +65 -0
- package/dist/lang/de/index.mjs.map +1 -0
- package/dist/lang/en/index.cjs +212 -0
- package/dist/lang/en/index.cjs.map +1 -0
- package/dist/lang/en/index.d.mts +5 -0
- package/dist/lang/en/index.d.ts +5 -0
- package/dist/lang/en/index.mjs +9 -0
- package/dist/lang/en/index.mjs.map +1 -0
- package/dist/pdf/index.cjs +81 -0
- package/dist/pdf/index.cjs.map +1 -0
- package/dist/pdf/index.d.mts +12 -0
- package/dist/pdf/index.d.ts +12 -0
- package/dist/pdf/index.mjs +79 -0
- package/dist/pdf/index.mjs.map +1 -0
- package/dist/scoring-BCShrnki.d.mts +319 -0
- package/dist/scoring-BCShrnki.d.ts +319 -0
- package/package.json +30 -3
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -17,8 +17,15 @@ Zero-dependency TypeScript library that scores a resume against a job descriptio
|
|
|
17
17
|
|
|
18
18
|
- **Deterministic** — same input always produces the same score; pin it with `referenceDate` to freeze "Present" date math
|
|
19
19
|
- **Explainable** — breakdown by category (skills / experience / keywords / education) plus matched and missing skill/keyword lists
|
|
20
|
-
- **
|
|
20
|
+
- **Categorized keywords** — every keyword/alias belongs to a category (technical, tool, concept, soft, marketing, domain); results are grouped by category
|
|
21
|
+
- **Weighted keyword scoring** — JD keywords are weighted by where they appear (required > preferred > body) and how often, so a missing "required" keyword costs more than a missing body-only one
|
|
22
|
+
- **Alias-aware suggestions** — flags resume terms that should be reworded to match the JD's own wording (e.g. "js" → "JavaScript")
|
|
23
|
+
- **Achievement strength** — classifies resume experience bullets as strong/weak (verb + quantified impact) and suggests rewrites
|
|
24
|
+
- **Multi-language keyword packs** — `/en` and `/de` subpaths ship categorized keyword registries; install more by passing your own `keywordRegistry`
|
|
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
|
+
- **Configurable** — adjust weights, add skill aliases or a custom keyword registry, define custom penalty rules
|
|
21
27
|
- **Zero dependencies** — core library has no runtime deps; ships ESM + CJS
|
|
28
|
+
- **PDF input** — optional `/pdf` subpath extracts resume text from a PDF buffer (requires `pdfjs-dist` peer dep)
|
|
22
29
|
- **Built-in profiles** — software engineer, data scientist, product manager out of the box
|
|
23
30
|
|
|
24
31
|
---
|
|
@@ -78,6 +85,11 @@ console.log(result.suggestions); // ["Add GraphQL to your skills section",
|
|
|
78
85
|
| `matchedKeywords` | `string[]` | JD keywords present in the resume (sorted) |
|
|
79
86
|
| `missingKeywords` | `string[]` | JD keywords absent from the resume (sorted) |
|
|
80
87
|
| `overusedKeywords` | `string[]` | Keywords exceeding density threshold (sorted) |
|
|
88
|
+
| `keywordsByCategory` | `Record<KeywordCategory, {matched, missing}>` | Matched/missing keywords grouped by category |
|
|
89
|
+
| `keywordWeights` | `KeywordWeight[]` | Per-keyword JD importance (`jdWeight`) and resume usage (`resumeWeight`) |
|
|
90
|
+
| `achievementStrength` | `{ strong: number; weak: number }` | Count of resume bullets classified as strong vs weak achievement statements |
|
|
91
|
+
| `matchedLanguages` | `ParsedLanguage[]` | JD-required languages the resume meets or exceeds in proficiency |
|
|
92
|
+
| `missingLanguages` | `ParsedLanguage[]` | JD-required languages absent or below the required proficiency |
|
|
81
93
|
| `suggestions` | `string[]` | Deterministic improvement recommendations |
|
|
82
94
|
| `warnings` | `string[]` | Parse warnings and section alerts |
|
|
83
95
|
| `experienceGap` | `number` | Years below JD minimum; `0` when met |
|
|
@@ -88,6 +100,8 @@ console.log(result.suggestions); // ["Add GraphQL to your skills section",
|
|
|
88
100
|
**Scoring formula:**
|
|
89
101
|
`score = skills×0.30 + experience×0.30 + keywords×0.25 + education×0.15` → clamped to 0–100 → rule penalties subtracted.
|
|
90
102
|
|
|
103
|
+
The `keywords` sub-score is a **weighted** coverage ratio, not a flat count: each JD keyword gets a weight from its location (required > preferred > body text) and frequency, so missing a required keyword drops the score more than missing one mentioned once in the body.
|
|
104
|
+
|
|
91
105
|
---
|
|
92
106
|
|
|
93
107
|
## Configuration
|
|
@@ -105,6 +119,9 @@ const result = analyzeResume({
|
|
|
105
119
|
// Additional skill synonyms merged over built-in defaults
|
|
106
120
|
skillAliases: { javascript: ["js", "ecmascript"] },
|
|
107
121
|
|
|
122
|
+
// Categorized keyword/alias entries; merges over the default registry by canonical term
|
|
123
|
+
keywordRegistry: [{ canonical: "rust", aliases: ["rustlang"], category: "technical" }],
|
|
124
|
+
|
|
108
125
|
// Industry profile: sets mandatory/optional skills and minExperience
|
|
109
126
|
profile: {
|
|
110
127
|
mandatorySkills: ["javascript", "react"],
|
|
@@ -155,15 +172,69 @@ See [Configuration docs](https://pranavraut033.github.io/ats-checker/docs/config
|
|
|
155
172
|
|
|
156
173
|
---
|
|
157
174
|
|
|
158
|
-
##
|
|
175
|
+
## Keyword Registry, Categories & Aliases
|
|
176
|
+
|
|
177
|
+
Every built-in keyword/skill belongs to a `KeywordRegistry` entry — a canonical term, its aliases, and a category (`technical` | `tool` | `concept` | `soft` | `marketing` | `domain`). Common tech synonyms are pre-loaded so `js` matches `javascript`, `k8s` matches `kubernetes`, etc.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { defaultKeywordRegistry, defaultSkillAliases } from "@pranavraut033/ats-checker";
|
|
181
|
+
// defaultKeywordRegistry: [{ canonical: "javascript", aliases: ["js"], category: "technical" }, ...]
|
|
182
|
+
// defaultSkillAliases: { javascript: ["js"], node: ["node.js", "nodejs"], ... } (derived, back-compat)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Extend or override the registry via `config.keywordRegistry` — entries merge over the defaults by canonical term:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const result = analyzeResume({
|
|
189
|
+
resumeText: "...",
|
|
190
|
+
jobDescription: "...",
|
|
191
|
+
config: {
|
|
192
|
+
keywordRegistry: [
|
|
193
|
+
{ canonical: "rust", aliases: ["rustlang"], category: "technical" },
|
|
194
|
+
{ canonical: "javascript", aliases: ["js", "ecmascript"], category: "technical" }, // overrides default
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
console.log(result.keywordsByCategory.technical); // { matched: [...], missing: [...] }
|
|
200
|
+
console.log(result.keywordWeights); // [{ term, category, jdWeight, resumeWeight, importance }, ...]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
You can still pass `config.skillAliases` for a flat override — it merges on top of the registry-derived aliases.
|
|
204
|
+
|
|
205
|
+
## Multi-language Keyword Packs
|
|
206
|
+
|
|
207
|
+
Categorized keyword registries ship as installable subpaths, one per language. Canonical terms stay in English (so scoring/profiles keep working); the pack supplies localized aliases.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import de from "@pranavraut033/ats-checker/de";
|
|
211
|
+
import { analyzeResume } from "@pranavraut033/ats-checker";
|
|
212
|
+
|
|
213
|
+
const result = analyzeResume({
|
|
214
|
+
resumeText: "...", // e.g. a German-language resume
|
|
215
|
+
jobDescription: "...",
|
|
216
|
+
config: { keywordRegistry: de },
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Available packs: `/en` (the default registry) and `/de` (seed set — grows on demand). Each default-exports a `KeywordRegistry`.
|
|
221
|
+
|
|
222
|
+
## Language Requirements
|
|
159
223
|
|
|
160
|
-
|
|
224
|
+
The JD parser scans for spoken-language mentions — CEFR codes (`A1`–`C2`) or descriptive words (`basic`, `conversational`, `professional`, `fluent`, `native`) — and the resume parser does the same. Any language found in the JD is treated as required; the resume must mention it at an equal or higher level to count as matched.
|
|
161
225
|
|
|
162
226
|
```typescript
|
|
163
|
-
|
|
164
|
-
|
|
227
|
+
const result = analyzeResume({
|
|
228
|
+
resumeText: "Languages: German (C1), English (native)",
|
|
229
|
+
jobDescription: "German (B2) required for this role.",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
console.log(result.matchedLanguages); // [{ name: "german", level: "b2", levelRank: 4 }]
|
|
233
|
+
console.log(result.missingLanguages); // []
|
|
165
234
|
```
|
|
166
235
|
|
|
236
|
+
A missing or under-leveled language surfaces both in `result.missingLanguages` and as a suggestion (`"Mention your proficiency in: german (b2)"`). This is informational/suggestion-only — it does not change `score` or `breakdown`.
|
|
237
|
+
|
|
167
238
|
---
|
|
168
239
|
|
|
169
240
|
## Built-in Profiles
|
|
@@ -184,6 +255,41 @@ const result = analyzeResume({
|
|
|
184
255
|
|
|
185
256
|
---
|
|
186
257
|
|
|
258
|
+
## PDF Input
|
|
259
|
+
|
|
260
|
+
Extract text from a PDF resume before passing it to `analyzeResume`. This uses `pdfjs-dist` as an optional peer dependency — the core library stays zero-dep.
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npm install pdfjs-dist
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import { extractTextFromPDF } from "@pranavraut033/ats-checker/pdf";
|
|
268
|
+
import { analyzeResume } from "@pranavraut033/ats-checker";
|
|
269
|
+
import { readFileSync } from "fs";
|
|
270
|
+
|
|
271
|
+
const bytes = readFileSync("resume.pdf");
|
|
272
|
+
const resumeText = await extractTextFromPDF(bytes);
|
|
273
|
+
|
|
274
|
+
const result = analyzeResume({ resumeText, jobDescription: "..." });
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`extractTextFromPDF` accepts a `Uint8Array` or `ArrayBuffer` and returns a plain `string`. Works in Node.js and the browser (text-layer PDFs only).
|
|
278
|
+
|
|
279
|
+
**Multi-column layouts are handled automatically.** The extractor uses glyph x/y coordinates to detect column boundaries and process each column independently, so a two-column resume parses cleanly without interleaved text.
|
|
280
|
+
|
|
281
|
+
For PDFs that can't be recovered — scanned/image resumes or exports with no text layer — `analyzeResume` surfaces an actionable message in `result.warnings`. Always check it after PDF input:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const result = analyzeResume({ resumeText, jobDescription: "..." });
|
|
285
|
+
if (result.warnings.length) {
|
|
286
|
+
console.warn("Parsing issues:", result.warnings);
|
|
287
|
+
// e.g. "Almost no text was extracted — the resume may be a scanned/image PDF."
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
187
293
|
## LLM Integration (deprecated)
|
|
188
294
|
|
|
189
295
|
`analyzeResumeAsync` accepts an optional `llm` config that rewrites suggestion text via a caller-supplied LLM client. **This path is deprecated** — scores and breakdowns are never touched by LLM. Prefer calling `analyzeResume` and running your own LLM pass on `result.suggestions` if you want AI-enhanced wording.
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// src/utils/text.ts
|
|
2
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
3
|
+
// articles / prepositions / conjunctions
|
|
4
|
+
"the",
|
|
5
|
+
"and",
|
|
6
|
+
"or",
|
|
7
|
+
"a",
|
|
8
|
+
"an",
|
|
9
|
+
"of",
|
|
10
|
+
"for",
|
|
11
|
+
"to",
|
|
12
|
+
"with",
|
|
13
|
+
"in",
|
|
14
|
+
"on",
|
|
15
|
+
"at",
|
|
16
|
+
"by",
|
|
17
|
+
"from",
|
|
18
|
+
"as",
|
|
19
|
+
"into",
|
|
20
|
+
"onto",
|
|
21
|
+
"upon",
|
|
22
|
+
"via",
|
|
23
|
+
"per",
|
|
24
|
+
"plus",
|
|
25
|
+
// verbs / modals
|
|
26
|
+
"is",
|
|
27
|
+
"are",
|
|
28
|
+
"be",
|
|
29
|
+
"was",
|
|
30
|
+
"were",
|
|
31
|
+
"will",
|
|
32
|
+
"can",
|
|
33
|
+
"should",
|
|
34
|
+
"must",
|
|
35
|
+
"have",
|
|
36
|
+
"has",
|
|
37
|
+
"had",
|
|
38
|
+
"do",
|
|
39
|
+
"does",
|
|
40
|
+
"did",
|
|
41
|
+
"get",
|
|
42
|
+
"give",
|
|
43
|
+
"go",
|
|
44
|
+
"use",
|
|
45
|
+
"see",
|
|
46
|
+
"help",
|
|
47
|
+
"work",
|
|
48
|
+
"build",
|
|
49
|
+
"show",
|
|
50
|
+
"need",
|
|
51
|
+
"want",
|
|
52
|
+
"make",
|
|
53
|
+
"let",
|
|
54
|
+
// pronouns / determiners
|
|
55
|
+
"it",
|
|
56
|
+
"its",
|
|
57
|
+
"this",
|
|
58
|
+
"that",
|
|
59
|
+
"these",
|
|
60
|
+
"those",
|
|
61
|
+
"we",
|
|
62
|
+
"our",
|
|
63
|
+
"you",
|
|
64
|
+
"your",
|
|
65
|
+
"they",
|
|
66
|
+
"their",
|
|
67
|
+
"us",
|
|
68
|
+
"who",
|
|
69
|
+
"what",
|
|
70
|
+
"which",
|
|
71
|
+
"how",
|
|
72
|
+
// common English fillers that leak into JDs
|
|
73
|
+
"no",
|
|
74
|
+
"not",
|
|
75
|
+
"all",
|
|
76
|
+
"any",
|
|
77
|
+
"also",
|
|
78
|
+
"more",
|
|
79
|
+
"well",
|
|
80
|
+
"very",
|
|
81
|
+
"highly",
|
|
82
|
+
"across",
|
|
83
|
+
"over",
|
|
84
|
+
"under",
|
|
85
|
+
"within",
|
|
86
|
+
"about",
|
|
87
|
+
"out",
|
|
88
|
+
"up",
|
|
89
|
+
"down",
|
|
90
|
+
"new",
|
|
91
|
+
"if",
|
|
92
|
+
"so",
|
|
93
|
+
"such",
|
|
94
|
+
"both",
|
|
95
|
+
"each",
|
|
96
|
+
"one",
|
|
97
|
+
"many",
|
|
98
|
+
"only",
|
|
99
|
+
// JD/HR boilerplate — never skills
|
|
100
|
+
"years",
|
|
101
|
+
"year",
|
|
102
|
+
"experience",
|
|
103
|
+
"required",
|
|
104
|
+
"requirement",
|
|
105
|
+
"requirements",
|
|
106
|
+
"preferred",
|
|
107
|
+
"role",
|
|
108
|
+
"degree",
|
|
109
|
+
"practices",
|
|
110
|
+
"best",
|
|
111
|
+
"skills",
|
|
112
|
+
"team",
|
|
113
|
+
"field",
|
|
114
|
+
"related",
|
|
115
|
+
"relevant",
|
|
116
|
+
"desired",
|
|
117
|
+
"strong",
|
|
118
|
+
"solid",
|
|
119
|
+
"good",
|
|
120
|
+
"first",
|
|
121
|
+
"based",
|
|
122
|
+
"day",
|
|
123
|
+
"week",
|
|
124
|
+
"month",
|
|
125
|
+
"time",
|
|
126
|
+
"fast",
|
|
127
|
+
"open",
|
|
128
|
+
"dynamic"
|
|
129
|
+
]);
|
|
130
|
+
function normalizeWhitespace(text) {
|
|
131
|
+
return text.replace(/\r\n?/g, "\n").replace(/\s+/g, " ").trim();
|
|
132
|
+
}
|
|
133
|
+
function normalizeForComparison(text) {
|
|
134
|
+
return normalizeWhitespace(text).normalize("NFKC").toLowerCase();
|
|
135
|
+
}
|
|
136
|
+
function splitLines(text) {
|
|
137
|
+
return text.replace(/\r\n?/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
138
|
+
}
|
|
139
|
+
var TECH_TOKEN_RE = /[a-z0-9][a-z0-9.#+\-/]*[a-z0-9#+]/g;
|
|
140
|
+
function tokenize(text) {
|
|
141
|
+
const normalized = normalizeForComparison(text);
|
|
142
|
+
return (normalized.match(TECH_TOKEN_RE) ?? []).filter(
|
|
143
|
+
(t) => /[a-z]/.test(t) && !STOP_WORDS.has(t)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function escapeRegExp(input) {
|
|
147
|
+
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
148
|
+
}
|
|
149
|
+
function unique(values) {
|
|
150
|
+
const seen = /* @__PURE__ */ new Set();
|
|
151
|
+
const output = [];
|
|
152
|
+
for (const value of values) {
|
|
153
|
+
const lower = value.toLowerCase();
|
|
154
|
+
if (!seen.has(lower)) {
|
|
155
|
+
seen.add(lower);
|
|
156
|
+
output.push(value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return output;
|
|
160
|
+
}
|
|
161
|
+
function clamp(value, min, max) {
|
|
162
|
+
return Math.min(Math.max(value, min), max);
|
|
163
|
+
}
|
|
164
|
+
function countFrequencies(values) {
|
|
165
|
+
const counts = {};
|
|
166
|
+
for (const value of values) {
|
|
167
|
+
counts[value] = (counts[value] ?? 0) + 1;
|
|
168
|
+
}
|
|
169
|
+
return counts;
|
|
170
|
+
}
|
|
171
|
+
function containsTableLikeStructure(text) {
|
|
172
|
+
const lines = splitLines(text);
|
|
173
|
+
let tableLines = 0;
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
const hasPipeColumns = line.includes("|") && line.split("|").length >= 3;
|
|
176
|
+
const hasTabColumns = /\t.+\t/.test(line);
|
|
177
|
+
const hasAlignedSpaces = /( {3,})(\S+)( {3,}\S+)/.test(line);
|
|
178
|
+
if (hasPipeColumns || hasTabColumns || hasAlignedSpaces) {
|
|
179
|
+
tableLines += 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return tableLines >= 2;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/utils/skills.ts
|
|
186
|
+
var aliasIndexCache = /* @__PURE__ */ new WeakMap();
|
|
187
|
+
function getAliasIndex(aliases) {
|
|
188
|
+
let index = aliasIndexCache.get(aliases);
|
|
189
|
+
if (!index) {
|
|
190
|
+
index = /* @__PURE__ */ new Map();
|
|
191
|
+
for (const [canonical, aliasList] of Object.entries(aliases)) {
|
|
192
|
+
const lower = canonical.toLowerCase();
|
|
193
|
+
index.set(lower, lower);
|
|
194
|
+
for (const alias of aliasList) {
|
|
195
|
+
index.set(alias.toLowerCase(), lower);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
aliasIndexCache.set(aliases, index);
|
|
199
|
+
}
|
|
200
|
+
return index;
|
|
201
|
+
}
|
|
202
|
+
function normalizeSkill(skill, aliases) {
|
|
203
|
+
const normalized = skill.trim().toLowerCase();
|
|
204
|
+
return getAliasIndex(aliases).get(normalized) ?? normalized;
|
|
205
|
+
}
|
|
206
|
+
function normalizeSkills(skills, aliases) {
|
|
207
|
+
return unique(skills.map((skill) => normalizeSkill(skill, aliases)));
|
|
208
|
+
}
|
|
209
|
+
function deriveSkillAliases(registry) {
|
|
210
|
+
const aliases = {};
|
|
211
|
+
for (const entry of registry) {
|
|
212
|
+
aliases[entry.canonical] = entry.aliases;
|
|
213
|
+
}
|
|
214
|
+
return aliases;
|
|
215
|
+
}
|
|
216
|
+
function buildCategoryIndex(registry) {
|
|
217
|
+
const index = /* @__PURE__ */ new Map();
|
|
218
|
+
for (const entry of registry) {
|
|
219
|
+
index.set(entry.canonical.toLowerCase(), entry.category);
|
|
220
|
+
}
|
|
221
|
+
return index;
|
|
222
|
+
}
|
|
223
|
+
function mergeKeywordRegistries(base, overrides) {
|
|
224
|
+
const byCanonical = /* @__PURE__ */ new Map();
|
|
225
|
+
for (const entry of base) byCanonical.set(entry.canonical.toLowerCase(), entry);
|
|
226
|
+
for (const entry of overrides) byCanonical.set(entry.canonical.toLowerCase(), entry);
|
|
227
|
+
return [...byCanonical.values()];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/profiles/index.ts
|
|
231
|
+
var defaultKeywordRegistry = [
|
|
232
|
+
// languages / frameworks
|
|
233
|
+
// ponytail: "node" split from javascript — Node.js runtime !== JS language
|
|
234
|
+
{ canonical: "javascript", aliases: ["js"], category: "technical" },
|
|
235
|
+
{ canonical: "node", aliases: ["node.js", "nodejs"], category: "technical" },
|
|
236
|
+
{ canonical: "typescript", aliases: ["ts"], category: "technical" },
|
|
237
|
+
{ canonical: "react", aliases: ["reactjs", "react.js"], category: "technical" },
|
|
238
|
+
{ canonical: "angular", aliases: ["angularjs"], category: "technical" },
|
|
239
|
+
{ canonical: "vue", aliases: ["vue.js", "vuejs"], category: "technical" },
|
|
240
|
+
{ canonical: "svelte", aliases: [], category: "technical" },
|
|
241
|
+
{ canonical: "next.js", aliases: ["nextjs"], category: "technical" },
|
|
242
|
+
{ canonical: "c++", aliases: ["cpp"], category: "technical" },
|
|
243
|
+
{ canonical: "c#", aliases: ["csharp", ".net"], category: "technical" },
|
|
244
|
+
{ canonical: "java", aliases: [], category: "technical" },
|
|
245
|
+
{ canonical: "python", aliases: ["py"], category: "technical" },
|
|
246
|
+
{ canonical: "go", aliases: ["golang"], category: "technical" },
|
|
247
|
+
{ canonical: "rust", aliases: [], category: "technical" },
|
|
248
|
+
{ canonical: "ruby", aliases: ["ruby on rails", "rails"], category: "technical" },
|
|
249
|
+
{ canonical: "php", aliases: [], category: "technical" },
|
|
250
|
+
{ canonical: "swift", aliases: [], category: "technical" },
|
|
251
|
+
{ canonical: "kotlin", aliases: [], category: "technical" },
|
|
252
|
+
{ canonical: "scala", aliases: [], category: "technical" },
|
|
253
|
+
{ canonical: "html", aliases: ["html5"], category: "technical" },
|
|
254
|
+
{ canonical: "css", aliases: ["css3"], category: "technical" },
|
|
255
|
+
{ canonical: "ios development", aliases: ["ios"], category: "technical" },
|
|
256
|
+
{ canonical: "android development", aliases: ["android"], category: "technical" },
|
|
257
|
+
{ canonical: "react native", aliases: [], category: "technical" },
|
|
258
|
+
{ canonical: "flutter", aliases: [], category: "technical" },
|
|
259
|
+
{ canonical: "machine learning", aliases: ["ml"], category: "technical" },
|
|
260
|
+
{ canonical: "deep learning", aliases: [], category: "technical" },
|
|
261
|
+
{ canonical: "natural language processing", aliases: ["nlp"], category: "technical" },
|
|
262
|
+
// tools / platforms / infra
|
|
263
|
+
{ canonical: "sql", aliases: ["postgres", "mysql", "sqlite"], category: "tool" },
|
|
264
|
+
{ canonical: "graphql", aliases: ["gql"], category: "tool" },
|
|
265
|
+
{ canonical: "aws", aliases: ["amazon web services"], category: "tool" },
|
|
266
|
+
{ canonical: "azure", aliases: ["microsoft azure"], category: "tool" },
|
|
267
|
+
{ canonical: "gcp", aliases: ["google cloud", "google cloud platform"], category: "tool" },
|
|
268
|
+
{ canonical: "docker", aliases: ["containers"], category: "tool" },
|
|
269
|
+
{ canonical: "kubernetes", aliases: ["k8s"], category: "tool" },
|
|
270
|
+
{ canonical: "terraform", aliases: [], category: "tool" },
|
|
271
|
+
{ canonical: "ansible", aliases: [], category: "tool" },
|
|
272
|
+
{ canonical: "jenkins", aliases: [], category: "tool" },
|
|
273
|
+
{ canonical: "git", aliases: ["github", "gitlab"], category: "tool" },
|
|
274
|
+
{ canonical: "jira", aliases: [], category: "tool" },
|
|
275
|
+
{ canonical: "confluence", aliases: [], category: "tool" },
|
|
276
|
+
{ canonical: "pytorch", aliases: ["torch"], category: "tool" },
|
|
277
|
+
{ canonical: "tensorflow", aliases: ["tf"], category: "tool" },
|
|
278
|
+
{ canonical: "scikit-learn", aliases: ["sklearn"], category: "tool" },
|
|
279
|
+
{ canonical: "pandas", aliases: [], category: "tool" },
|
|
280
|
+
{ canonical: "numpy", aliases: [], category: "tool" },
|
|
281
|
+
{ canonical: "fastapi", aliases: [], category: "tool" },
|
|
282
|
+
{ canonical: "flask", aliases: [], category: "tool" },
|
|
283
|
+
{ canonical: "django", aliases: [], category: "tool" },
|
|
284
|
+
{ canonical: "kafka", aliases: [], category: "tool" },
|
|
285
|
+
{ canonical: "redis", aliases: [], category: "tool" },
|
|
286
|
+
{ canonical: "elasticsearch", aliases: ["elastic"], category: "tool" },
|
|
287
|
+
{ canonical: "spark", aliases: ["apache spark"], category: "tool" },
|
|
288
|
+
{ canonical: "tableau", aliases: [], category: "tool" },
|
|
289
|
+
{ canonical: "power bi", aliases: ["powerbi"], category: "tool" },
|
|
290
|
+
{ canonical: "excel", aliases: ["microsoft excel", "ms excel"], category: "tool" },
|
|
291
|
+
{ canonical: "salesforce", aliases: [], category: "tool" },
|
|
292
|
+
{ canonical: "hubspot", aliases: [], category: "tool" },
|
|
293
|
+
{ canonical: "sap", aliases: [], category: "tool" },
|
|
294
|
+
{ canonical: "quickbooks", aliases: [], category: "tool" },
|
|
295
|
+
{ canonical: "workday", aliases: [], category: "tool" },
|
|
296
|
+
{ canonical: "zendesk", aliases: [], category: "tool" },
|
|
297
|
+
{ canonical: "servicenow", aliases: [], category: "tool" },
|
|
298
|
+
{ canonical: "figma", aliases: [], category: "tool" },
|
|
299
|
+
{ canonical: "photoshop", aliases: ["adobe photoshop"], category: "tool" },
|
|
300
|
+
{ canonical: "illustrator", aliases: ["adobe illustrator"], category: "tool" },
|
|
301
|
+
{ canonical: "autocad", aliases: [], category: "tool" },
|
|
302
|
+
// engineering concepts
|
|
303
|
+
{ canonical: "accessibility", aliases: ["a11y"], category: "concept" },
|
|
304
|
+
{ canonical: "frontend", aliases: ["front-end"], category: "concept" },
|
|
305
|
+
{ canonical: "backend", aliases: ["back-end"], category: "concept" },
|
|
306
|
+
{ canonical: "security", aliases: ["cybersecurity"], category: "concept" },
|
|
307
|
+
{ canonical: "testing", aliases: ["unittest", "pytest"], category: "concept" },
|
|
308
|
+
{ canonical: "microservices", aliases: [], category: "concept" },
|
|
309
|
+
{ canonical: "agile", aliases: ["scrum"], category: "concept" },
|
|
310
|
+
{ canonical: "kanban", aliases: [], category: "concept" },
|
|
311
|
+
{ canonical: "blockchain", aliases: [], category: "concept" },
|
|
312
|
+
{ canonical: "devops", aliases: [], category: "concept" },
|
|
313
|
+
{ canonical: "ci/cd", aliases: ["continuous integration", "continuous deployment"], category: "concept" },
|
|
314
|
+
{ canonical: "rest api", aliases: ["restful api", "rest apis"], category: "concept" },
|
|
315
|
+
{ canonical: "design patterns", aliases: [], category: "concept" },
|
|
316
|
+
{ canonical: "data structures", aliases: [], category: "concept" },
|
|
317
|
+
{ canonical: "algorithms", aliases: [], category: "concept" },
|
|
318
|
+
{ canonical: "cloud computing", aliases: [], category: "concept" },
|
|
319
|
+
{ canonical: "system design", aliases: [], category: "concept" },
|
|
320
|
+
{ canonical: "tdd", aliases: ["test driven development", "test-driven development"], category: "concept" },
|
|
321
|
+
{ canonical: "ux design", aliases: ["user experience"], category: "concept" },
|
|
322
|
+
{ canonical: "ui design", aliases: ["user interface design"], category: "concept" },
|
|
323
|
+
{ canonical: "project management", aliases: [], category: "concept" },
|
|
324
|
+
{ canonical: "change management", aliases: [], category: "concept" },
|
|
325
|
+
{ canonical: "risk management", aliases: [], category: "concept" },
|
|
326
|
+
{ canonical: "quality assurance", aliases: ["qa"], category: "concept" },
|
|
327
|
+
// product / data domain
|
|
328
|
+
{ canonical: "roadmap", aliases: [], category: "domain" },
|
|
329
|
+
{ canonical: "stakeholder management", aliases: [], category: "domain" },
|
|
330
|
+
{ canonical: "prioritization", aliases: [], category: "domain" },
|
|
331
|
+
{ canonical: "a/b testing", aliases: ["ab testing"], category: "domain" },
|
|
332
|
+
{ canonical: "analytics", aliases: [], category: "domain" },
|
|
333
|
+
{ canonical: "statistics", aliases: ["stats"], category: "domain" },
|
|
334
|
+
{ canonical: "data visualization", aliases: [], category: "domain" },
|
|
335
|
+
// finance / accounting domain
|
|
336
|
+
{ canonical: "financial analysis", aliases: [], category: "domain" },
|
|
337
|
+
{ canonical: "budgeting", aliases: [], category: "domain" },
|
|
338
|
+
{ canonical: "forecasting", aliases: [], category: "domain" },
|
|
339
|
+
{ canonical: "bookkeeping", aliases: [], category: "domain" },
|
|
340
|
+
{ canonical: "accounts payable", aliases: ["ap"], category: "domain" },
|
|
341
|
+
{ canonical: "accounts receivable", aliases: ["ar"], category: "domain" },
|
|
342
|
+
{ canonical: "payroll", aliases: [], category: "domain" },
|
|
343
|
+
{ canonical: "auditing", aliases: ["audit"], category: "domain" },
|
|
344
|
+
{ canonical: "tax preparation", aliases: [], category: "domain" },
|
|
345
|
+
{ canonical: "gaap", aliases: [], category: "domain" },
|
|
346
|
+
// sales / account management domain
|
|
347
|
+
{ canonical: "lead generation", aliases: [], category: "domain" },
|
|
348
|
+
{ canonical: "account management", aliases: [], category: "domain" },
|
|
349
|
+
{ canonical: "crm", aliases: ["customer relationship management"], category: "domain" },
|
|
350
|
+
{ canonical: "sales pipeline", aliases: [], category: "domain" },
|
|
351
|
+
{ canonical: "cold calling", aliases: [], category: "domain" },
|
|
352
|
+
{ canonical: "upselling", aliases: ["cross-selling"], category: "domain" },
|
|
353
|
+
{ canonical: "customer retention", aliases: [], category: "domain" },
|
|
354
|
+
// human resources domain
|
|
355
|
+
{ canonical: "recruiting", aliases: ["talent acquisition"], category: "domain" },
|
|
356
|
+
{ canonical: "onboarding", aliases: [], category: "domain" },
|
|
357
|
+
{ canonical: "employee relations", aliases: [], category: "domain" },
|
|
358
|
+
{ canonical: "benefits administration", aliases: [], category: "domain" },
|
|
359
|
+
{ canonical: "performance management", aliases: [], category: "domain" },
|
|
360
|
+
// healthcare domain
|
|
361
|
+
{ canonical: "patient care", aliases: [], category: "domain" },
|
|
362
|
+
{ canonical: "clinical documentation", aliases: [], category: "domain" },
|
|
363
|
+
{ canonical: "hipaa", aliases: [], category: "domain" },
|
|
364
|
+
{ canonical: "electronic health records", aliases: ["ehr", "emr"], category: "domain" },
|
|
365
|
+
{ canonical: "medical billing", aliases: [], category: "domain" },
|
|
366
|
+
// legal domain
|
|
367
|
+
{ canonical: "contract review", aliases: [], category: "domain" },
|
|
368
|
+
{ canonical: "legal research", aliases: [], category: "domain" },
|
|
369
|
+
{ canonical: "litigation", aliases: [], category: "domain" },
|
|
370
|
+
{ canonical: "regulatory compliance", aliases: ["compliance"], category: "domain" },
|
|
371
|
+
{ canonical: "due diligence", aliases: [], category: "domain" },
|
|
372
|
+
// education domain
|
|
373
|
+
{ canonical: "curriculum development", aliases: [], category: "domain" },
|
|
374
|
+
{ canonical: "lesson planning", aliases: [], category: "domain" },
|
|
375
|
+
{ canonical: "classroom management", aliases: [], category: "domain" },
|
|
376
|
+
{ canonical: "instructional design", aliases: [], category: "domain" },
|
|
377
|
+
// operations / supply chain domain
|
|
378
|
+
{ canonical: "supply chain management", aliases: ["supply chain"], category: "domain" },
|
|
379
|
+
{ canonical: "inventory management", aliases: [], category: "domain" },
|
|
380
|
+
{ canonical: "procurement", aliases: [], category: "domain" },
|
|
381
|
+
{ canonical: "vendor management", aliases: [], category: "domain" },
|
|
382
|
+
{ canonical: "logistics", aliases: [], category: "domain" },
|
|
383
|
+
// customer service domain
|
|
384
|
+
{ canonical: "customer support", aliases: ["customer service"], category: "domain" },
|
|
385
|
+
{ canonical: "technical support", aliases: [], category: "domain" },
|
|
386
|
+
{ canonical: "conflict resolution", aliases: [], category: "domain" },
|
|
387
|
+
// soft skills
|
|
388
|
+
{ canonical: "communication", aliases: [], category: "soft" },
|
|
389
|
+
{ canonical: "leadership", aliases: [], category: "soft" },
|
|
390
|
+
{ canonical: "teamwork", aliases: ["collaboration"], category: "soft" },
|
|
391
|
+
{ canonical: "problem solving", aliases: ["problem-solving"], category: "soft" },
|
|
392
|
+
{ canonical: "adaptability", aliases: ["flexibility"], category: "soft" },
|
|
393
|
+
{ canonical: "time management", aliases: [], category: "soft" },
|
|
394
|
+
{ canonical: "critical thinking", aliases: [], category: "soft" },
|
|
395
|
+
{ canonical: "creativity", aliases: [], category: "soft" },
|
|
396
|
+
{ canonical: "attention to detail", aliases: [], category: "soft" },
|
|
397
|
+
{ canonical: "decision making", aliases: ["decision-making"], category: "soft" },
|
|
398
|
+
{ canonical: "emotional intelligence", aliases: [], category: "soft" },
|
|
399
|
+
{ canonical: "negotiation", aliases: [], category: "soft" },
|
|
400
|
+
{ canonical: "organization", aliases: ["organizational skills"], category: "soft" },
|
|
401
|
+
{ canonical: "public speaking", aliases: ["presentation skills"], category: "soft" },
|
|
402
|
+
{ canonical: "mentoring", aliases: ["coaching"], category: "soft" },
|
|
403
|
+
{ canonical: "interpersonal skills", aliases: [], category: "soft" },
|
|
404
|
+
{ canonical: "work ethic", aliases: [], category: "soft" },
|
|
405
|
+
// marketing
|
|
406
|
+
{ canonical: "seo", aliases: ["search engine optimization"], category: "marketing" },
|
|
407
|
+
{ canonical: "branding", aliases: ["brand strategy"], category: "marketing" },
|
|
408
|
+
{ canonical: "campaign management", aliases: [], category: "marketing" },
|
|
409
|
+
{ canonical: "content marketing", aliases: [], category: "marketing" },
|
|
410
|
+
{ canonical: "social media marketing", aliases: ["social media"], category: "marketing" },
|
|
411
|
+
{ canonical: "email marketing", aliases: [], category: "marketing" },
|
|
412
|
+
{ canonical: "digital marketing", aliases: [], category: "marketing" },
|
|
413
|
+
{ canonical: "copywriting", aliases: [], category: "marketing" },
|
|
414
|
+
{ canonical: "market research", aliases: [], category: "marketing" },
|
|
415
|
+
{ canonical: "ppc", aliases: ["pay-per-click", "google ads"], category: "marketing" },
|
|
416
|
+
{ canonical: "conversion rate optimization", aliases: ["cro"], category: "marketing" },
|
|
417
|
+
{ canonical: "public relations", aliases: ["pr"], category: "marketing" }
|
|
418
|
+
];
|
|
419
|
+
var defaultSkillAliases = deriveSkillAliases(defaultKeywordRegistry);
|
|
420
|
+
var softwareEngineerProfile = {
|
|
421
|
+
name: "software-engineer",
|
|
422
|
+
mandatorySkills: ["javascript", "typescript", "react", "node"],
|
|
423
|
+
optionalSkills: ["graphql", "sql", "docker"],
|
|
424
|
+
minExperience: 3
|
|
425
|
+
};
|
|
426
|
+
var dataScientistProfile = {
|
|
427
|
+
name: "data-scientist",
|
|
428
|
+
mandatorySkills: ["python", "sql", "statistics"],
|
|
429
|
+
optionalSkills: ["pandas", "numpy", "pytorch", "tensorflow"],
|
|
430
|
+
minExperience: 2
|
|
431
|
+
};
|
|
432
|
+
var productManagerProfile = {
|
|
433
|
+
name: "product-manager",
|
|
434
|
+
mandatorySkills: ["roadmap", "stakeholder management", "prioritization"],
|
|
435
|
+
optionalSkills: ["a/b testing", "analytics", "sql"],
|
|
436
|
+
minExperience: 3
|
|
437
|
+
};
|
|
438
|
+
var defaultProfiles = [
|
|
439
|
+
softwareEngineerProfile,
|
|
440
|
+
dataScientistProfile,
|
|
441
|
+
productManagerProfile
|
|
442
|
+
];
|
|
443
|
+
|
|
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-ZJ5E4H7Z.mjs.map
|
|
446
|
+
//# sourceMappingURL=chunk-ZJ5E4H7Z.mjs.map
|