@pranavraut033/ats-checker 1.2.0 → 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 CHANGED
@@ -17,7 +17,13 @@ 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
- - **Configurable** — adjust weights, add skill aliases, define custom penalty rules
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
22
28
  - **PDF input** — optional `/pdf` subpath extracts resume text from a PDF buffer (requires `pdfjs-dist` peer dep)
23
29
  - **Built-in profiles** — software engineer, data scientist, product manager out of the box
@@ -79,6 +85,11 @@ console.log(result.suggestions); // ["Add GraphQL to your skills section",
79
85
  | `matchedKeywords` | `string[]` | JD keywords present in the resume (sorted) |
80
86
  | `missingKeywords` | `string[]` | JD keywords absent from the resume (sorted) |
81
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 |
82
93
  | `suggestions` | `string[]` | Deterministic improvement recommendations |
83
94
  | `warnings` | `string[]` | Parse warnings and section alerts |
84
95
  | `experienceGap` | `number` | Years below JD minimum; `0` when met |
@@ -89,6 +100,8 @@ console.log(result.suggestions); // ["Add GraphQL to your skills section",
89
100
  **Scoring formula:**
90
101
  `score = skills×0.30 + experience×0.30 + keywords×0.25 + education×0.15` → clamped to 0–100 → rule penalties subtracted.
91
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
+
92
105
  ---
93
106
 
94
107
  ## Configuration
@@ -106,6 +119,9 @@ const result = analyzeResume({
106
119
  // Additional skill synonyms merged over built-in defaults
107
120
  skillAliases: { javascript: ["js", "ecmascript"] },
108
121
 
122
+ // Categorized keyword/alias entries; merges over the default registry by canonical term
123
+ keywordRegistry: [{ canonical: "rust", aliases: ["rustlang"], category: "technical" }],
124
+
109
125
  // Industry profile: sets mandatory/optional skills and minExperience
110
126
  profile: {
111
127
  mandatorySkills: ["javascript", "react"],
@@ -156,15 +172,69 @@ See [Configuration docs](https://pranavraut033.github.io/ats-checker/docs/config
156
172
 
157
173
  ---
158
174
 
159
- ## Built-in Skill Aliases
175
+ ## Keyword Registry, Categories & Aliases
160
176
 
161
- Common tech synonyms are pre-loaded so `js` matches `javascript`, `k8s` matches `kubernetes`, etc. Extend or override via `config.skillAliases`.
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.
162
178
 
163
179
  ```typescript
164
- import { defaultSkillAliases } from "@pranavraut033/ats-checker";
165
- // { javascript: ["js"], node: ["node.js", "nodejs"], typescript: ["ts"], ... }
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 }, ...]
166
201
  ```
167
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
223
+
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.
225
+
226
+ ```typescript
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); // []
234
+ ```
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
+
168
238
  ---
169
239
 
170
240
  ## Built-in Profiles
@@ -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