@whiteslove/parsing-lexicon 0.2.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/LICENSE +21 -0
- package/README.md +137 -0
- package/index.d.ts +375 -0
- package/package.json +75 -0
- package/src/central-asia-locations.js +211 -0
- package/src/central-asia.js +233 -0
- package/src/contact.d.ts +44 -0
- package/src/contact.js +159 -0
- package/src/countries.js +175 -0
- package/src/country-context.d.ts +10 -0
- package/src/country-context.js +22 -0
- package/src/currency.d.ts +14 -0
- package/src/currency.js +66 -0
- package/src/geo.js +169 -0
- package/src/geography-central-asia.js +42 -0
- package/src/geography-detection.d.ts +14 -0
- package/src/geography-detection.js +64 -0
- package/src/geography-display.d.ts +13 -0
- package/src/geography-display.js +113 -0
- package/src/geography.js +210 -0
- package/src/hiring-advanced.js +147 -0
- package/src/hiring-candidate-fields.d.ts +9 -0
- package/src/hiring-candidate-fields.js +92 -0
- package/src/hiring-context.d.ts +64 -0
- package/src/hiring-context.js +412 -0
- package/src/hiring-language-extensions.d.ts +19 -0
- package/src/hiring-language-extensions.js +139 -0
- package/src/hiring-languages.js +135 -0
- package/src/hiring-location-fields.d.ts +3 -0
- package/src/hiring-location-fields.js +53 -0
- package/src/hiring-professions-ro.js +191 -0
- package/src/hiring-professions.js +437 -0
- package/src/hiring-requirements.d.ts +16 -0
- package/src/hiring-requirements.js +170 -0
- package/src/hiring-salary-context.d.ts +27 -0
- package/src/hiring-salary-context.js +76 -0
- package/src/hiring-semantics.d.ts +29 -0
- package/src/hiring-semantics.js +218 -0
- package/src/hiring-skills.d.ts +21 -0
- package/src/hiring-skills.js +315 -0
- package/src/hiring-source-aliases.d.ts +32 -0
- package/src/hiring-source-aliases.js +186 -0
- package/src/hiring-source-semantics.d.ts +31 -0
- package/src/hiring-source-semantics.js +293 -0
- package/src/hiring-temporal.d.ts +15 -0
- package/src/hiring-temporal.js +67 -0
- package/src/hiring-vacancy-fields.d.ts +5 -0
- package/src/hiring-vacancy-fields.js +23 -0
- package/src/hiring-work-semantics.d.ts +34 -0
- package/src/hiring-work-semantics.js +72 -0
- package/src/hiring.js +180 -0
- package/src/housing-address.d.ts +18 -0
- package/src/housing-address.js +210 -0
- package/src/housing-context.d.ts +36 -0
- package/src/housing-context.js +183 -0
- package/src/housing-features.js +30 -0
- package/src/housing-intent.d.ts +9 -0
- package/src/housing-intent.js +132 -0
- package/src/housing-listing-fields.js +131 -0
- package/src/housing-money.d.ts +7 -0
- package/src/housing-money.js +102 -0
- package/src/housing-source-aliases.d.ts +8 -0
- package/src/housing-source-aliases.js +51 -0
- package/src/housing-structured.d.ts +55 -0
- package/src/housing-structured.js +219 -0
- package/src/housing.js +200 -0
- package/src/index.js +44 -0
- package/src/kz-location-extensions.js +307 -0
- package/src/landmarks.js +21 -0
- package/src/lexicon-core.js +58 -0
- package/src/location-merge.js +106 -0
- package/src/locations.js +456 -0
- package/src/money-core.d.ts +10 -0
- package/src/money-core.js +81 -0
- package/src/money-lexicon.d.ts +5 -0
- package/src/money-lexicon.js +79 -0
- package/src/money.js +154 -0
- package/src/normalization.js +384 -0
- package/src/odesa-metropolitan.js +145 -0
- package/src/romania-geography.js +47 -0
- package/src/tashkent-colloquial.js +72 -0
- package/src/tashkent-housing-geography.d.ts +38 -0
- package/src/tashkent-housing-geography.js +211 -0
- package/src/tashkent-pois.js +215 -0
- package/src/tashkent-residential-complexes.js +272 -0
- package/src/ua-location-extensions-major.js +118 -0
- package/src/ua-location-extensions-metro.js +9 -0
- package/src/ua-location-extensions-regional.js +246 -0
- package/src/ua-secondary-cities.d.ts +2 -0
- package/src/ua-secondary-cities.js +57 -0
- package/src/ukraine.js +75 -0
- package/src/uz-location-extensions.js +259 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// These canonical labels are ordinary words or one-letter tokens. Matching the
|
|
2
|
+
// label itself would create noisy results; only their explicit aliases are safe.
|
|
3
|
+
const AMBIGUOUS_CANONICALS = new Set(['C', 'Go', 'Make', 'REST', 'Spring'])
|
|
4
|
+
|
|
5
|
+
const group = (category, subcategory, entries) =>
|
|
6
|
+
entries.map(([name, aliases = []]) => ({
|
|
7
|
+
name,
|
|
8
|
+
category,
|
|
9
|
+
subcategory,
|
|
10
|
+
aliases: [...(AMBIGUOUS_CANONICALS.has(name) ? [] : [name]), ...aliases],
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
// Shared by Nitro enrichment and the browser-only ATS scorer. Keep canonical
|
|
14
|
+
// labels readable; aliases hold abbreviations, spelling variants and local terms.
|
|
15
|
+
export const SKILL_CATALOG = [
|
|
16
|
+
...group('IT', 'Frontend', [
|
|
17
|
+
['HTML', ['html5']], ['CSS', ['css3']], ['Sass', ['scss']], ['Less', ['less css']],
|
|
18
|
+
['JavaScript', ['ecmascript', 'es6', 'js developer', 'js framework']], ['TypeScript', ['type script']],
|
|
19
|
+
['React', ['react.js', 'reactjs']], ['React Native', ['react-native']],
|
|
20
|
+
['Vue', ['vue.js', 'vuejs']], ['Nuxt', ['nuxt.js', 'nuxtjs']],
|
|
21
|
+
['Next.js', ['nextjs', 'next js']], ['Angular', ['angular.js', 'angularjs']],
|
|
22
|
+
['Svelte'], ['SvelteKit', ['svelte kit']], ['Astro', ['astro.js']], ['jQuery'],
|
|
23
|
+
['Knockout', ['knockout.js', 'knockout js']], ['AlpineJS', ['alpine.js', 'alpine js']],
|
|
24
|
+
['HTMX'], ['Web Components', ['custom elements']], ['Tailwind', ['tailwindcss', 'tailwind css']],
|
|
25
|
+
['Bootstrap'], ['Material UI', ['mui', '@mui/material']], ['Ant Design', ['antd']],
|
|
26
|
+
['Vuetify'], ['Nuxt UI'], ['Quasar'], ['PrimeVue', ['prime vue']], ['Storybook'],
|
|
27
|
+
['Redux'], ['Redux Toolkit', ['rtk', '@reduxjs/toolkit']], ['Zustand'], ['MobX'],
|
|
28
|
+
['Pinia'], ['Vuex'], ['RxJS'], ['React Query', ['tanstack query', '@tanstack/react-query']],
|
|
29
|
+
['Axios'], ['Fetch API', ['window.fetch']], ['Webpack'], ['Vite'], ['Rollup'], ['Babel'],
|
|
30
|
+
['ESLint'], ['Prettier'], ['Stylelint'], ['npm'], ['Yarn'], ['pnpm'],
|
|
31
|
+
['Freemarker'], ['Velocity'], ['Liquid'], ['Nunjucks'],
|
|
32
|
+
['Responsive Design', ['responsive web design', 'responsive layout', 'адаптивная верстка', 'адаптивний дизайн']],
|
|
33
|
+
['Cross-browser Development', ['cross-browser', 'cross browser', 'кроссбраузерность']],
|
|
34
|
+
['Accessibility', ['web accessibility', 'wcag', 'a11y']], ['BEM', ['бэм']],
|
|
35
|
+
['SEO', ['search engine optimization', 'поисковая оптимизация']],
|
|
36
|
+
['Technical SEO', ['техническое seo']], ['i18n', ['internationalization', 'localization', 'локализация']],
|
|
37
|
+
]),
|
|
38
|
+
...group('IT', 'Backend', [
|
|
39
|
+
['Node.js', ['nodejs', 'node js']], ['Express', ['express.js', 'expressjs']],
|
|
40
|
+
['NestJS', ['nest.js', 'nest js']], ['Fastify'], ['Python', ['python3']], ['Django'],
|
|
41
|
+
['Flask'], ['FastAPI', ['fast api']], ['PHP'], ['Laravel'], ['Symfony'], ['WordPress'],
|
|
42
|
+
['Java', ['java ee', 'jakarta ee']], ['Spring', ['spring framework']], ['Spring Boot', ['springboot']],
|
|
43
|
+
['Hibernate'], ['Maven'], ['Gradle'], ['Kotlin'], ['Ktor'], ['C#', ['c sharp', 'csharp']],
|
|
44
|
+
['.NET', ['dotnet', 'dot net']], ['ASP.NET', ['asp net', 'asp.net core']],
|
|
45
|
+
['Entity Framework', ['entityframework', 'ef core']], ['Tomcat'], ['Go', ['golang', 'go language']],
|
|
46
|
+
['Rust', ['rustlang']], ['C', ['c programming', 'c language']], ['C++', ['cpp', 'c plus plus']],
|
|
47
|
+
['Ruby'], ['Ruby on Rails', ['rails framework']], ['Scala'], ['Elixir'], ['Erlang'],
|
|
48
|
+
['OOP', ['object-oriented programming', 'object oriented programming']], ['SOLID', ['solid principles']],
|
|
49
|
+
]),
|
|
50
|
+
...group('IT', 'API & Architecture', [
|
|
51
|
+
['REST', ['rest api', 'restful api', 'rest services']], ['GraphQL'],
|
|
52
|
+
['Apollo GraphQL', ['apollo client', 'apollo server']], ['gRPC'], ['WebSocket', ['websockets', 'web socket']],
|
|
53
|
+
['Socket.IO', ['socketio']], ['API Integration', ['api integrations', 'third-party integration', 'интеграция api']],
|
|
54
|
+
['Webhooks', ['webhook']], ['SOAP', ['soap api', 'soap web service']], ['Microservices', ['micro services']],
|
|
55
|
+
['PWA', ['progressive web app']], ['SPA', ['single-page application']],
|
|
56
|
+
['SSR', ['server-side rendering']], ['SSG', ['static site generation']], ['JSON'], ['XML'], ['YAML', ['yml']],
|
|
57
|
+
]),
|
|
58
|
+
...group('IT', 'Mobile & Desktop', [
|
|
59
|
+
['Android'], ['Android Studio'], ['Jetpack Compose'], ['Android SDK'], ['iOS'], ['Swift'], ['Objective-C', ['objective c']],
|
|
60
|
+
['SwiftUI'], ['UIKit'], ['Xcode'], ['Flutter'], ['Dart'], ['Electron', ['electron.js', 'electronjs']],
|
|
61
|
+
['Tauri'], ['Qt'], ['WPF'], ['WinForms'], ['Unity', ['unity3d']], ['Unreal Engine', ['ue4', 'ue5']], ['Godot'],
|
|
62
|
+
]),
|
|
63
|
+
...group('IT', 'Databases', [
|
|
64
|
+
['SQL'], ['NoSQL', ['no sql', 'no-sql']], ['PostgreSQL', ['postgres', 'psql']], ['MySQL'], ['MariaDB', ['maria db']], ['SQLite'],
|
|
65
|
+
['Microsoft SQL Server', ['mssql', 'sql server']], ['Oracle Database', ['oracle db']],
|
|
66
|
+
['MongoDB', ['mongo db', 'mongo']], ['Redis'], ['Elasticsearch', ['elastic search']], ['OpenSearch'],
|
|
67
|
+
['ClickHouse', ['click house']], ['DynamoDB'], ['Firestore'], ['Firebase'], ['Neo4j'],
|
|
68
|
+
['Supabase'], ['Snowflake'], ['BigQuery'], ['Prisma', ['prisma orm']], ['TypeORM'], ['Sequelize'], ['SQLAlchemy'],
|
|
69
|
+
]),
|
|
70
|
+
...group('IT', 'DevOps & Cloud', [
|
|
71
|
+
['Git'], ['GitHub'], ['GitLab'], ['Bitbucket'], ['DevOps'], ['Docker'], ['Docker Compose', ['docker-compose']],
|
|
72
|
+
['Kubernetes', ['k8s']], ['Helm'], ['Terraform'], ['Ansible'], ['Jenkins'],
|
|
73
|
+
['GitHub Actions'], ['GitLab CI', ['gitlab ci/cd']], ['CircleCI'], ['TeamCity'], ['ArgoCD', ['argo cd']],
|
|
74
|
+
['CI/CD', ['ci cd', 'cicd', 'continuous integration', 'continuous delivery']],
|
|
75
|
+
['AWS', ['amazon web services']], ['Azure', ['microsoft azure']], ['Google Cloud', ['gcp', 'google cloud platform']],
|
|
76
|
+
['Cloudflare'], ['Vercel'], ['Netlify'], ['Nginx'], ['Apache'], ['Linux'], ['Ubuntu'], ['Windows Server'],
|
|
77
|
+
['Bash', ['shell scripting']], ['PowerShell'], ['SSH'], ['Active Directory'], ['VMware'], ['Grafana'],
|
|
78
|
+
['Prometheus'], ['Zabbix'], ['Sentry'], ['ELK', ['elastic stack']], ['Kibana'],
|
|
79
|
+
]),
|
|
80
|
+
...group('IT', 'QA & Security', [
|
|
81
|
+
['Jest'], ['Vitest'], ['Cypress'], ['Playwright'], ['Selenium'], ['Appium'], ['Postman'],
|
|
82
|
+
['Swagger', ['openapi']], ['TestRail'], ['TDD', ['test-driven development']], ['BDD', ['behavior-driven development']],
|
|
83
|
+
['Manual Testing', ['ручное тестирование']],
|
|
84
|
+
['Test Automation', ['automation testing', 'автоматизация тестирования']],
|
|
85
|
+
['Regression Testing', ['регрессионное тестирование']], ['Unit Testing', ['unit tests']],
|
|
86
|
+
['Integration Testing'], ['E2E Testing', ['end-to-end testing']], ['Cybersecurity', ['information security']],
|
|
87
|
+
['SIEM'], ['Splunk'], ['OAuth', ['oauth2']], ['JWT'], ['OWASP'], ['Penetration Testing', ['pentest', 'пентест']],
|
|
88
|
+
]),
|
|
89
|
+
...group('Data', 'Analytics & AI', [
|
|
90
|
+
['Data Analysis', ['analytics', 'data analytics', 'анализ данных']], ['Business Analytics'], ['Commercial Analytics'],
|
|
91
|
+
['Pandas'], ['NumPy'], ['Jupyter'], ['Power BI', ['powerbi']], ['Tableau'], ['Looker'], ['Qlik'],
|
|
92
|
+
['Apache Spark', ['pyspark']], ['Hadoop'], ['Airflow'], ['Kafka'], ['RabbitMQ'], ['ETL'],
|
|
93
|
+
['Data Warehouse'], ['Data Science'], ['Machine Learning', ['машинное обучение']], ['Deep Learning'], ['TensorFlow'],
|
|
94
|
+
['PyTorch'], ['Scikit-learn', ['sklearn']], ['NLP'], ['Computer Vision'], ['LLM', ['large language models']],
|
|
95
|
+
['Generative AI', ['genai', 'генеративный ии']], ['OpenAI'], ['LangChain'], ['RAG'], ['AI Tools', ['ai tools']],
|
|
96
|
+
]),
|
|
97
|
+
...group('Office', 'Productivity', [
|
|
98
|
+
['Microsoft Office', ['ms office', 'мс офис']], ['Microsoft 365', ['office 365', 'm365']],
|
|
99
|
+
['Microsoft Word', ['ms word', 'ворд']],
|
|
100
|
+
['Microsoft PowerPoint', ['ms powerpoint', 'powerpoint']], ['Microsoft Outlook', ['ms outlook']],
|
|
101
|
+
['Microsoft Access', ['ms access']], ['Microsoft Visio', ['ms visio']], ['Microsoft Project', ['ms project']],
|
|
102
|
+
['Microsoft Teams', ['ms teams']], ['SharePoint'], ['OneDrive'], ['Google Workspace', ['g suite']],
|
|
103
|
+
['Google Sheets'], ['Google Docs'], ['Google Slides'], ['LibreOffice'],
|
|
104
|
+
]),
|
|
105
|
+
...group('Office', 'Spreadsheets', [
|
|
106
|
+
['Microsoft Excel', ['ms excel', 'excel', 'эксель']], ['Excel Pivot Tables', ['pivot tables', 'сводные таблицы']],
|
|
107
|
+
['VLOOKUP', ['впр excel']], ['XLOOKUP'],
|
|
108
|
+
['Power Query', ['powerquery']], ['Power Pivot', ['powerpivot']], ['Excel Macros', ['макросы excel']], ['VBA'],
|
|
109
|
+
]),
|
|
110
|
+
...group('Design', 'UI & UX', [
|
|
111
|
+
['Figma'], ['Adobe Photoshop', ['photoshop']], ['Adobe Illustrator', ['illustrator']],
|
|
112
|
+
['Adobe XD'], ['Sketch', ['sketch app']], ['UI Design'], ['UX Design'], ['Design Systems'], ['Prototyping'],
|
|
113
|
+
]),
|
|
114
|
+
...group('Business', 'Management', [
|
|
115
|
+
['Business Strategy'], ['Strategic Planning'], ['Corporate Planning'], ['Business Planning'],
|
|
116
|
+
['Corporate Strategy'], ['Commercial Strategy'], ['Management Consulting'], ['M&A', ['mergers and acquisitions']],
|
|
117
|
+
['Project Management'], ['Portfolio Management'],
|
|
118
|
+
['Corporate Governance', ['group governance']], ['Business Process'], ['Process Improvement', ['process improvements']],
|
|
119
|
+
['Operational Efficiency'], ['Operations'], ['Stakeholder Management'], ['Negotiation'],
|
|
120
|
+
['Cross-functional Collaboration', ['cross-functional', 'cross functionally']], ['KPI', ['key performance indicators']],
|
|
121
|
+
['Dashboards'], ['Forecasting'], ['Risk Management'], ['Resource Planning'], ['Gantt Charts'],
|
|
122
|
+
['Agile'], ['Scrum'], ['Kanban'], ['Jira'], ['Confluence'], ['SDLC'],
|
|
123
|
+
]),
|
|
124
|
+
...group('Finance', 'Accounting Software', [
|
|
125
|
+
['1C', ['1с', '1c accounting', '1с бухгалтерия']], ['SAP', ['sap erp', 'sap fico']],
|
|
126
|
+
['SONO', ['соно']], ['M.E.Doc', ['medoc', 'медок']], ['Didox'], ['MySoliq', ['my soliq']],
|
|
127
|
+
['ProZorro', ['прозорро']], ['SAGA', ['saga accounting', 'saga software']],
|
|
128
|
+
]),
|
|
129
|
+
...group('Finance', 'Accounting & Banking', [
|
|
130
|
+
['Accounting', ['бухгалтерский учет', 'бухгалтерський облік']],
|
|
131
|
+
['Financial Analysis'], ['P&L', ['profit and loss']], ['Budgeting'], ['Management Accounting'],
|
|
132
|
+
['Tax Accounting'], ['Payroll'], ['IFRS', ['мсфо']], ['GAAP'], ['Audit'], ['Treasury'],
|
|
133
|
+
['Investment Banking'], ['Credit Analysis'], ['AML', ['anti-money laundering']], ['KYC', ['know your customer']],
|
|
134
|
+
]),
|
|
135
|
+
...group('Business Systems', 'CRM & ERP', [
|
|
136
|
+
['Microsoft Dynamics', ['dynamics 365']], ['HubSpot', ['hubspot crm']],
|
|
137
|
+
['Bitrix24', ['битрикс24']], ['amoCRM', ['амо срм']], ['Smartup', ['smart up']], ['Odoo'], ['Oracle ERP'],
|
|
138
|
+
]),
|
|
139
|
+
...group('Sales', 'Sales & Customer Service', [
|
|
140
|
+
['Sales', ['продажи', 'сотув', 'savdo']], ['Sales Pipeline'], ['Lead Generation', ['лидогенерация']],
|
|
141
|
+
['Cold Calling', ['холодные звонки']], ['Account Management'], ['Key Account Management'],
|
|
142
|
+
['CRM'], ['Customer Service', ['обслуживание клиентов']], ['Customer Support', ['поддержка клиентов']],
|
|
143
|
+
['Call Center', ['колл-центр']], ['Client Communication', ['переписка с клиентами', 'мижозлар билан ёзишмалар']],
|
|
144
|
+
['Complaint Handling'], ['Onboarding', ['employee onboarding', 'courier onboarding']], ['Conversion Funnel'],
|
|
145
|
+
]),
|
|
146
|
+
...group('Sales', 'CRM', [
|
|
147
|
+
['Salesforce', ['salesforce crm']],
|
|
148
|
+
]),
|
|
149
|
+
...group('Marketing', 'Digital Marketing', [
|
|
150
|
+
['Digital Marketing'], ['Content Marketing'], ['Social Media Marketing', ['smm']], ['Email Marketing'],
|
|
151
|
+
['Google Ads', ['google adwords']], ['Meta Ads', ['facebook ads']], ['Google Analytics', ['ga4']],
|
|
152
|
+
['Google Tag Manager', ['gtm']], ['Marketing Automation'], ['Copywriting', ['копирайтинг']],
|
|
153
|
+
['Market Research'], ['A/B Testing', ['ab testing']], ['Conversion Rate Optimization', ['cro']],
|
|
154
|
+
]),
|
|
155
|
+
...group('HR', 'Recruiting & People', [
|
|
156
|
+
['Recruitment', ['recruiting', 'подбор персонала']], ['Candidate Sourcing'], ['Boolean Search'],
|
|
157
|
+
['LinkedIn Recruiter'], ['Interviewing', ['проведение собеседований']], ['HR Administration'],
|
|
158
|
+
['HRIS'], ['Workday HCM'], ['Greenhouse ATS'], ['BambooHR'], ['SAP SuccessFactors'],
|
|
159
|
+
]),
|
|
160
|
+
...group('Legal', 'Legal & Compliance', [
|
|
161
|
+
['Legal Research', ['правовой анализ']], ['Contract Drafting', ['составление договоров']],
|
|
162
|
+
['Contract Management', ['договорная работа']], ['Due Diligence'], ['Corporate Law'], ['Commercial Law'],
|
|
163
|
+
['Labor Law', ['трудовое право']], ['Litigation'], ['Compliance', ['комплаенс']], ['GDPR'],
|
|
164
|
+
]),
|
|
165
|
+
...group('Administration', 'Office Work', [
|
|
166
|
+
['Document Management', ['документооборот']], ['Electronic Document Management', ['эдо']],
|
|
167
|
+
['Records Management', ['делопроизводство']], ['Business Correspondence', ['деловая переписка']],
|
|
168
|
+
['Data Entry', ['ввод данных']], ['Office Administration'], ['Calendar Management'], ['Meeting Coordination'],
|
|
169
|
+
]),
|
|
170
|
+
...group('Logistics', 'Supply Chain & Transport', [
|
|
171
|
+
['Logistics', ['логистика']], ['Supply Chain', ['управление цепями поставок']], ['Procurement', ['закупки']],
|
|
172
|
+
['Strategic Sourcing'], ['Tendering', ['тендеры']], ['Vendor Management'], ['Inventory Management'],
|
|
173
|
+
['Warehouse Management'], ['WMS'], ['TMS'], ['Incoterms'], ['Customs Clearance'], ['Import/Export'],
|
|
174
|
+
['Freight Forwarding'], ['Route Planning', ['маршрутизация']], ['Last-mile Logistics'],
|
|
175
|
+
['Driving License B', ['права категории b']], ['Driving License C', ['права категории c']],
|
|
176
|
+
['Forklift', ['водитель погрузчика']], ['Barcode Scanner', ['тсд', 'терминал сбора данных']],
|
|
177
|
+
]),
|
|
178
|
+
...group('Retail & Hospitality', 'Retail, POS & HoReCa', [
|
|
179
|
+
['POS', ['point of sale']], ['R-Keeper', ['rkeeper']], ['iiko', ['айко ресторан']],
|
|
180
|
+
['Poster POS', ['poster restaurant']], ['MICROS', ['oracle micros']], ['Cash Register', ['работа с кассой']],
|
|
181
|
+
['Merchandising', ['мерчандайзинг']], ['Stocktaking', ['инвентаризация']], ['HACCP', ['хассп']],
|
|
182
|
+
['Hotel Management'], ['Opera PMS', ['oracle hospitality opera']], ['Restaurant Management'],
|
|
183
|
+
]),
|
|
184
|
+
...group('Engineering', 'CAD', [
|
|
185
|
+
['AutoCAD', ['autocad civil 3d']], ['SolidWorks'], ['Revit'], ['ArchiCAD'], ['SketchUp'],
|
|
186
|
+
]),
|
|
187
|
+
...group('Engineering', 'Manufacturing', [
|
|
188
|
+
['CNC', ['чпу']], ['PLC', ['плк']], ['SCADA', ['асу тп']], ['TIA Portal'], ['Siemens S7'],
|
|
189
|
+
['Electrical Engineering'], ['Mechanical Engineering'], ['Mechatronics'], ['Welding', ['сварка']],
|
|
190
|
+
['Lean Manufacturing', ['бережливое производство']], ['Six Sigma'], ['Kaizen'], ['5S'],
|
|
191
|
+
['ISO 9001', ['iso9001']], ['ISO 14001'], ['ISO 45001'], ['Quality Management'], ['HSE', ['охрана труда']],
|
|
192
|
+
]),
|
|
193
|
+
...group('Construction', 'Construction & Design', [
|
|
194
|
+
['Construction Management'], ['Quantity Surveying'], ['Cost Estimation', ['сметное дело', 'составление смет']],
|
|
195
|
+
['Project Documentation'], ['Technical Drawings', ['технические чертежи']], ['BIM'],
|
|
196
|
+
]),
|
|
197
|
+
...group('Healthcare', 'Medical & Pharma', [
|
|
198
|
+
['EMR', ['electronic medical record']], ['EHR', ['electronic health record']],
|
|
199
|
+
['Medical Records', ['медицинская документация']], ['GMP'], ['GLP'],
|
|
200
|
+
['Good Clinical Practice', ['gcp clinical']], ['PCR', ['пцр']], ['ELISA', ['ифа анализ']],
|
|
201
|
+
['Laboratory Equipment'], ['Pharmacovigilance'], ['Clinical Research'], ['Patient Care'],
|
|
202
|
+
]),
|
|
203
|
+
...group('Education', 'Teaching & Learning', [
|
|
204
|
+
['Teaching', ['преподавание', 'викладання']], ['Curriculum Development'], ['Lesson Planning'],
|
|
205
|
+
['Classroom Management'], ['Moodle'], ['Google Classroom'], ['LMS', ['learning management system']],
|
|
206
|
+
]),
|
|
207
|
+
...group('Aviation', 'Aviation Operations', [
|
|
208
|
+
['Airport Operations'], ['Flight Operations'], ['Ground Handling'], ['Aviation Safety'],
|
|
209
|
+
['IATA'], ['ICAO'], ['Amadeus'], ['Sabre'],
|
|
210
|
+
]),
|
|
211
|
+
...group('E-commerce', 'Marketplaces', [
|
|
212
|
+
['E-commerce', ['ecommerce', 'электронная коммерция', 'онлайн савдо']], ['Shopify'], ['Magento'],
|
|
213
|
+
['OpenCart'], ['Amazon Seller Central'], ['Wildberries', ['вайлдберриз']], ['Ozon'],
|
|
214
|
+
['Kaspi', ['kaspi marketplace', 'kaspi магазин']], ['Uzum', ['uzum market']],
|
|
215
|
+
['Marketplace Management', ['ведение маркетплейсов']], ['Product Cards', ['карточки товаров']],
|
|
216
|
+
]),
|
|
217
|
+
...group('Automation', 'Low Code', [
|
|
218
|
+
['Automation'], ['Zapier'], ['Make', ['make.com', 'integromat']], ['n8n'], ['Power Automate'], ['Power Apps'],
|
|
219
|
+
['UiPath'], ['Automation Anywhere'], ['RPA', ['robotic process automation']],
|
|
220
|
+
]),
|
|
221
|
+
...group('Professional', 'Soft Skills', [
|
|
222
|
+
['Communication', ['communication skills', 'коммуникация', 'коммуникабельность', 'коммуникация кўникмалари']],
|
|
223
|
+
['Teamwork', ['team player', 'работа в команде', 'командная работа', 'жамоа билан']],
|
|
224
|
+
['Leadership'], ['Time Management'], ['Problem Solving', ['решение проблем']], ['Critical Thinking'],
|
|
225
|
+
['Analytical Thinking'], ['Multitasking', ['многозадачность']], ['Attention to Detail'],
|
|
226
|
+
['Presentation Skills'], ['Responsibility', ['ответственность', 'масъулиятлилик']],
|
|
227
|
+
['Adaptability', ['адаптивность', 'мослашувчан']], ['Fast Learner', ['быстрая обучаемость', 'тез ўрганувчи']],
|
|
228
|
+
['Goal Orientation', ['стремление к цели', 'мақсадларга эришиш']],
|
|
229
|
+
]),
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
export const SKILL_KEYWORDS = Object.freeze(
|
|
233
|
+
Object.fromEntries(SKILL_CATALOG.map(({ name, aliases }) => [name, Object.freeze([...new Set(aliases)])])),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
export const SKILL_META = Object.freeze(
|
|
237
|
+
Object.fromEntries(SKILL_CATALOG.map(({ name, category, subcategory }) => [name, { category, subcategory }])),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
export function normalizeSkillText(value) {
|
|
241
|
+
return value
|
|
242
|
+
.normalize('NFKC')
|
|
243
|
+
.toLocaleLowerCase('en')
|
|
244
|
+
.replace(/[‘’`´]/g, "'")
|
|
245
|
+
.replace(/[‐‑‒–—]/g, '-')
|
|
246
|
+
.replace(/\s+/g, ' ')
|
|
247
|
+
.trim()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function escapeRegex(value) {
|
|
251
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function buildSkillRegex(alias) {
|
|
255
|
+
const normalized = normalizeSkillText(alias)
|
|
256
|
+
const pattern = escapeRegex(normalized).replace(/\s+/g, '\\s+')
|
|
257
|
+
// Unicode-aware boundaries prevent `react` matching `reactive`, `reaction`
|
|
258
|
+
// or `interaction`, while still handling C++, C#, .NET, 1C and Vue.js.
|
|
259
|
+
return new RegExp(`(?:^|[^\\p{L}\\p{N}_])${pattern}(?=$|[^\\p{L}\\p{N}_])`, 'iu')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const CONTEXTUAL_SKILL_PATTERNS = Object.freeze([
|
|
263
|
+
['Corporate Governance', /\b(?:group|company|corporate) governance\b/i],
|
|
264
|
+
['Data Analysis', /\banalys(?:e|is|ing)\b[^.;\n]{0,60}\b(?:data|metrics?|funnels?)\b|\b(?:data|metrics?|funnels?)\b[^.;\n]{0,60}\banalys(?:e|is|ing)\b/i],
|
|
265
|
+
['Conversion Funnel', /\b(?:conversion|onboarding|registration)[- ](?:to[- ]\w+\s+)?funnel\b|\bonboarding funnel\b/i],
|
|
266
|
+
['Cross-functional Collaboration', /\bcross[- ]function(?:al|ally)\b/i],
|
|
267
|
+
])
|
|
268
|
+
|
|
269
|
+
const COMPILED_SKILLS = SKILL_CATALOG.map((definition) => ({
|
|
270
|
+
definition,
|
|
271
|
+
patterns: [...new Set(definition.aliases.map(normalizeSkillText))].map(buildSkillRegex),
|
|
272
|
+
}))
|
|
273
|
+
|
|
274
|
+
const CANONICAL_BY_ALIAS = new Map()
|
|
275
|
+
for (const { name, aliases } of SKILL_CATALOG) {
|
|
276
|
+
CANONICAL_BY_ALIAS.set(normalizeSkillText(name), name)
|
|
277
|
+
for (const alias of aliases) CANONICAL_BY_ALIAS.set(normalizeSkillText(alias), name)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function canonicalSkillName(value) {
|
|
281
|
+
const normalized = normalizeSkillText(value)
|
|
282
|
+
return CANONICAL_BY_ALIAS.get(normalized) ?? extractSkillNames(normalized)[0]
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function extractSkillDetails(text) {
|
|
286
|
+
const normalized = normalizeSkillText(text)
|
|
287
|
+
const found = []
|
|
288
|
+
const names = new Set()
|
|
289
|
+
for (const { definition, patterns } of COMPILED_SKILLS) {
|
|
290
|
+
if (!patterns.some((pattern) => pattern.test(normalized))) continue
|
|
291
|
+
found.push({
|
|
292
|
+
name: definition.name,
|
|
293
|
+
category: definition.category,
|
|
294
|
+
subcategory: definition.subcategory,
|
|
295
|
+
})
|
|
296
|
+
names.add(definition.name)
|
|
297
|
+
}
|
|
298
|
+
for (const [name, pattern] of CONTEXTUAL_SKILL_PATTERNS) {
|
|
299
|
+
if (names.has(name) || !pattern.test(normalized)) continue
|
|
300
|
+
const meta = SKILL_META[name]
|
|
301
|
+
if (!meta) continue
|
|
302
|
+
found.push({ name, ...meta })
|
|
303
|
+
names.add(name)
|
|
304
|
+
}
|
|
305
|
+
return found
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function extractSkillNames(text) {
|
|
309
|
+
return extractSkillDetails(text).map(({ name }) => name)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function getSkillMeta(name) {
|
|
313
|
+
const canonical = canonicalSkillName(name)
|
|
314
|
+
return canonical ? SKILL_META[canonical] : undefined
|
|
315
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ExtendedProfessionMatch {
|
|
2
|
+
canonical: string;
|
|
3
|
+
group: string;
|
|
4
|
+
family: string;
|
|
5
|
+
score: number;
|
|
6
|
+
strength: 'strong' | 'weak';
|
|
7
|
+
matched: string;
|
|
8
|
+
index: number;
|
|
9
|
+
label: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SourceProfessionAlias {
|
|
13
|
+
canonical: string;
|
|
14
|
+
label: string;
|
|
15
|
+
group: string;
|
|
16
|
+
aliases: readonly string[];
|
|
17
|
+
re: RegExp;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SourceRoleNormalization {
|
|
21
|
+
canonical: string;
|
|
22
|
+
label: string;
|
|
23
|
+
re: RegExp;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const SOURCE_PROFESSION_ALIASES: readonly SourceProfessionAlias[];
|
|
27
|
+
export const SOURCE_ROLE_NORMALIZATION_RULES: readonly SourceRoleNormalization[];
|
|
28
|
+
export const SOURCE_CANDIDATE_INTENT_ALIASES: readonly string[];
|
|
29
|
+
export function normalizeSourceRole(value: unknown): Readonly<{ canonical: string; label: string }> | null;
|
|
30
|
+
export function matchesSourceCandidateIntent(value: unknown): boolean;
|
|
31
|
+
export function professionDisplayLabel(canonical: string): string;
|
|
32
|
+
export function matchExtendedProfessions(value: unknown, options?: { limit?: number; allowWeak?: boolean }): readonly ExtendedProfessionMatch[];
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { aliasesToRegex } from './normalization.js';
|
|
2
|
+
import { matchProfessions } from './hiring-professions.js';
|
|
3
|
+
|
|
4
|
+
const sourceRole = (canonical, label, aliases, group = 'other') => Object.freeze({
|
|
5
|
+
canonical,
|
|
6
|
+
label,
|
|
7
|
+
group,
|
|
8
|
+
aliases: Object.freeze([...new Set(aliases)]),
|
|
9
|
+
re: aliasesToRegex([...new Set(aliases)]),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Source spellings seen in real CV feeds which are too source-specific or
|
|
14
|
+
* colloquial for the core profession rows. They still live in the shared
|
|
15
|
+
* package so consumers do not grow their own profession regex catalogs.
|
|
16
|
+
*/
|
|
17
|
+
export const SOURCE_PROFESSION_ALIASES = Object.freeze([
|
|
18
|
+
sourceRole('chief_executive_officer', 'Chief Executive Officer', ['ceo', 'chief executive officer', 'генеральный директор', 'гендиректор', 'виконавчий директор'], 'management'),
|
|
19
|
+
sourceRole('chief_technology_officer', 'Chief Technology Officer', ['cto', 'chief technology officer', 'технический директор', 'технічний директор'], 'management'),
|
|
20
|
+
sourceRole('sales_manager', 'Sales Manager', ['sales executive', 'head of sales', 'менеджер экспортных продаж', 'менеджер по экспортным продажам', 'роп', 'sotuv menejer'], 'sales'),
|
|
21
|
+
sourceRole('driver', 'Driver', ['се категория', 'се категория буйича', 'ce категория', 'ce category', 'shafyor'], 'transport'),
|
|
22
|
+
sourceRole('accountant', 'Accountant', ['bugalteriya', 'buxgalteriya', 'buhgalteriya'], 'finance'),
|
|
23
|
+
sourceRole('cashier', 'Cashier', ['kassa xodimi', 'kassa mudiri'], 'retail'),
|
|
24
|
+
sourceRole('notary', 'Notary', ['notary', 'notarius', 'нотариус'], 'legal'),
|
|
25
|
+
sourceRole('metrology_specialist', 'Metrology Specialist', ['metrologiya', 'metrology specialist', 'метролог', 'standartlashtirish'], 'manufacturing'),
|
|
26
|
+
sourceRole('security_guard', 'Security Guard', ['xavfsizlik', 'qoriqlash', 'qo‘riqlash', "qo'riqlash", 'охорона'], 'security'),
|
|
27
|
+
sourceRole('finance_banking_specialist', 'Finance / Banking Specialist', ['finance specialist', 'banking specialist', 'moliya', 'soliq', 'bank'], 'finance'),
|
|
28
|
+
sourceRole('teacher', 'Teacher', ['tyutorlik', 'тьютор', 'titur', "o'qituvchilik", 'o‘qituvchilik', 'oʻqituvchilik', 'ustoz'], 'education'),
|
|
29
|
+
sourceRole('english_teacher', 'English Teacher', ['ingliz tili ustoziman', 'ingliz tili ustoz', 'ingliz tili oqituvchi', "ingliz tili o'qituvchi"], 'education'),
|
|
30
|
+
sourceRole('it_specialist', 'IT Specialist', ['it specialist', 'itishnik', 'it ishnik', 'kompyuter boyicha ish', "kompyuter bo'yicha ish", 'kompyuter bo‘yicha ish'], 'infrastructure'),
|
|
31
|
+
sourceRole('biotechnologist', 'Biotechnologist', ['biotechnologist', 'биотехнолог', 'biotexnolog'], 'medicine'),
|
|
32
|
+
sourceRole('laboratory_technician', 'Laboratory Technician', ['laborant'], 'medicine'),
|
|
33
|
+
sourceRole('media_specialist', 'Media Specialist', ['media specialist', 'специалист по сми', 'matbuot'], 'media_content'),
|
|
34
|
+
sourceRole('engineer', 'Engineer', ['engineer', 'инженер', 'інженер', 'muhandis', 'injiner'], 'engineering'),
|
|
35
|
+
sourceRole('chat_operator', 'Chat Operator', ['chat operatori'], 'customer_support'),
|
|
36
|
+
sourceRole('call_center_operator', 'Call Center Operator', ['koll-markaz operatori', 'koll markaz operatori', 'call-markaz operatori'], 'customer_support'),
|
|
37
|
+
sourceRole('consultant', 'Consultant', ['consultant', 'консультант', 'консультантка'], 'commercial'),
|
|
38
|
+
sourceRole('supervisor', 'Supervisor', ['supervisor', 'супервайзер', 'начальник отряд', 'начальник отряда'], 'management'),
|
|
39
|
+
sourceRole('quality_inspector', 'Quality Inspector', ['quality inspector', 'инспектор по качеству', 'інспектор з якості'], 'manufacturing'),
|
|
40
|
+
sourceRole('oil_gas_worker', 'Oil & Gas Worker', ['oil and gas', 'oil & gas', 'нефть и газ', 'нефтегаз', 'neft va gaz', 'neft vagaz', 'neft vagaz sohasida', 'neft va gaz sohasida'], 'manufacturing'),
|
|
41
|
+
sourceRole('cybersecurity_specialist', 'Cybersecurity Specialist', ['cybersecurity specialist', 'ciso', 'руководитель по информационной безопасности', 'руководитель информационной безопасности'], 'information_security'),
|
|
42
|
+
sourceRole('architect', 'Architect', ['arxitektor loyihachi', 'arxitektor'], 'construction'),
|
|
43
|
+
sourceRole('economist', 'Economist', ['iqtisodchi', 'iqtsodchi', 'iqtisodiy'], 'finance'),
|
|
44
|
+
sourceRole('logistics_manager', 'Logistics Specialist', ['logist', 'logistics specialist', 'логист'], 'logistics'),
|
|
45
|
+
sourceRole('frontend_developer', 'Frontend Developer', ['frontet', 'frontet developer', 'frontet dasturchi'], 'software_development'),
|
|
46
|
+
sourceRole('nanny', 'Nanny', ['bolalarga qarash', 'bolaga qarash'], 'education'),
|
|
47
|
+
sourceRole('loader', 'Loader', ['грузчиком', 'грузчика', 'грузчик'], 'logistics'),
|
|
48
|
+
sourceRole('welder', 'Welder', ['сварщиком', 'сварщица', 'зварювальником'], 'construction'),
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const sourceRoleNormalization = (canonical, label, re) => Object.freeze({ canonical, label, re });
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Anchored or strongly contextualized raw-role repairs. These rules are kept
|
|
55
|
+
* separate from SOURCE_PROFESSION_ALIASES so generic values such as `model`,
|
|
56
|
+
* `online`, or `bank` never become profession matches in arbitrary CV prose.
|
|
57
|
+
*/
|
|
58
|
+
export const SOURCE_ROLE_NORMALIZATION_RULES = Object.freeze([
|
|
59
|
+
sourceRoleNormalization('economist', 'Economist', /^iqt(?:i)?sodchi$/iu),
|
|
60
|
+
sourceRoleNormalization('economist', 'Economist', /^iqtisodiy$/iu),
|
|
61
|
+
sourceRoleNormalization('logistics_manager', 'Logistics Specialist', /^logist(?:ika)?(?:\s+updater)?$/iu),
|
|
62
|
+
sourceRoleNormalization('english_teacher', 'English Teacher', /^ingliz\s+tili\s+ustoz(?:iman)?$/iu),
|
|
63
|
+
sourceRoleNormalization('frontend_developer', 'Frontend Developer', /mobilagraf[\s\S]*itishnik[\s\S]*front(?:et|ent|end)/iu),
|
|
64
|
+
sourceRoleNormalization('nanny', 'Nanny', /farqi\s+yo[\s\S]*bolalarga\s+qarash/iu),
|
|
65
|
+
sourceRoleNormalization('sales_manager', 'Sales Manager', /^(?:sales\s+executive(?:\s+ind)?|роп(?:,?\s*sales\s+executive)?)$/iu),
|
|
66
|
+
sourceRoleNormalization('operative_officer', 'Operative Officer', /^(?:оперативник|оперуполномоченн\p{L}*|оперативный\s+уполномоченн\p{L}*)$/iu),
|
|
67
|
+
sourceRoleNormalization('water_supply_specialist', 'Water Supply Specialist', /^(?:suv\s+ta['’ʻʼ‘`]?minoti|водоснабжение)$/iu),
|
|
68
|
+
sourceRoleNormalization('any_role', 'Any Role', /^(?:onlayn(?:\s+ish(?:chi)?)?|online(?:\s+ish(?:chi)?)?|онлайн|удал[её]нно|remote(?:\s+work)?|boshqa\s+ishlar?|farqi\s+(?:yo['’ʻʼ‘`]?q|yuq)|tungi|bilmaym\p{L}*)$/iu),
|
|
69
|
+
sourceRoleNormalization('restaurant_cafe_worker', 'Restaurant / Cafe Worker', /^ищу\s+работу\s+(?:в\s+)?(?:кафе|ресторанах?|кафе\s+или\s+ресторанах)$/iu),
|
|
70
|
+
sourceRoleNormalization('driver', 'Driver', /^(?:xaydovchilik|haydovchilik|shafyorlik|shofyorlik)/iu),
|
|
71
|
+
sourceRoleNormalization('retail_worker', 'Retail Worker', /^do['’ʻʼ‘`]?kon$/iu),
|
|
72
|
+
sourceRoleNormalization('salesperson', 'Salesperson', /^(?:savdo|sotuvchi)$/iu),
|
|
73
|
+
sourceRoleNormalization('pharmacist', 'Pharmacist', /^(?:dorishunos|farmatsevt)$/iu),
|
|
74
|
+
sourceRoleNormalization('notary_assistant', 'Notary Assistant', /^(?:natarus|notarius)\s+yordamchisi/iu),
|
|
75
|
+
sourceRoleNormalization('librarian', 'Librarian', /^kutubxonachi$/iu),
|
|
76
|
+
sourceRoleNormalization('singer_vocalist', 'Singer / Vocalist', /^(?:vokal\s*:\s*)?xonanda$/iu),
|
|
77
|
+
sourceRoleNormalization('model', 'Model', /^model$/iu),
|
|
78
|
+
sourceRoleNormalization('flight_attendant', 'Flight Attendant', /^bortprovodnik$|^бортпроводник$/iu),
|
|
79
|
+
sourceRoleNormalization('hvac_technician', 'HVAC Technician', /^(?:konditsaner|kanditsaner|konditsioner)/iu),
|
|
80
|
+
sourceRoleNormalization('mobile_content_creator', 'Mobile Content Creator', /^mobilografiya(?:\s+bo['’ʻʼ‘`]?yicha)?$/iu),
|
|
81
|
+
sourceRoleNormalization('cctv_intercom_technician', 'CCTV / Intercom Technician', /kamera\s+(?:dama?fon|domofon)|domofon\s+xizmat/iu),
|
|
82
|
+
sourceRoleNormalization('internal_control_specialist', 'Internal Control Specialist', /^ichki\s+nazoratchi$/iu),
|
|
83
|
+
sourceRoleNormalization('brand_ambassador', 'Brand Ambassador', /^(?:бренд\s+фейс|brand\s+face)$/iu),
|
|
84
|
+
sourceRoleNormalization('insurance_specialist', 'Insurance Specialist', /^sug['’ʻʼ‘`]?urta$/iu),
|
|
85
|
+
sourceRoleNormalization('bank_operations_specialist', 'Bank Operations Specialist', /^стаж[её]р\s+операционист|^операционист$/iu),
|
|
86
|
+
sourceRoleNormalization('commercial_director', 'Commercial Director', /^коммерческ\p{L}*\s+директор|\bchief\s+commercial\s+officer\b|\bCCO\b/iu),
|
|
87
|
+
sourceRoleNormalization('security_specialist', 'Security Specialist', /^по\s+безопасност\p{L}*\s+объекта$/iu),
|
|
88
|
+
sourceRoleNormalization('healthcare_specialist', 'Healthcare Specialist', /^mededsina$|^meditsina$|^медицина$/iu),
|
|
89
|
+
sourceRoleNormalization('tourism_hospitality_specialist', 'Tourism / Hospitality Specialist', /^mehmonxona[^\n]*turfirma|^turfirma[^\n]*mehmonxona/iu),
|
|
90
|
+
sourceRoleNormalization('confectioner', 'Confectioner', /qandolat|qandolatchi/iu),
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
export function normalizeSourceRole(value) {
|
|
94
|
+
const text = String(value || '').trim();
|
|
95
|
+
if (!text) return null;
|
|
96
|
+
const match = SOURCE_ROLE_NORMALIZATION_RULES.find((entry) => entry.re.test(text));
|
|
97
|
+
return match ? Object.freeze({ canonical: match.canonical, label: match.label }) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const SOURCE_CANDIDATE_INTENT_ALIASES = Object.freeze([
|
|
101
|
+
'работу ищу',
|
|
102
|
+
'роботу шукаю',
|
|
103
|
+
'срочно работу ищу',
|
|
104
|
+
'терміново роботу шукаю',
|
|
105
|
+
'могу работать',
|
|
106
|
+
'можу працювати',
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
const SOURCE_CANDIDATE_INTENT_RE = aliasesToRegex(SOURCE_CANDIDATE_INTENT_ALIASES);
|
|
110
|
+
|
|
111
|
+
export function matchesSourceCandidateIntent(value) {
|
|
112
|
+
return SOURCE_CANDIDATE_INTENT_RE.test(String(value || ''));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const LABELS = new Map(SOURCE_PROFESSION_ALIASES.map((entry) => [entry.canonical, entry.label]));
|
|
116
|
+
const ACRONYMS = new Map([
|
|
117
|
+
['qa', 'QA'], ['hr', 'HR'], ['ui', 'UI'], ['ux', 'UX'], ['ai', 'AI'], ['ml', 'ML'],
|
|
118
|
+
['seo', 'SEO'], ['sre', 'SRE'], ['dba', 'DBA'], ['crm', 'CRM'], ['erp', 'ERP'], ['pmo', 'PMO'], ['it', 'IT'],
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
export function professionDisplayLabel(canonical) {
|
|
122
|
+
if (!canonical) return '';
|
|
123
|
+
const explicit = LABELS.get(canonical);
|
|
124
|
+
if (explicit) return explicit;
|
|
125
|
+
return String(canonical).split('_').map((part) => ACRONYMS.get(part) || `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join(' ');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function sourceMatches(text) {
|
|
129
|
+
const matches = [];
|
|
130
|
+
for (const entry of SOURCE_PROFESSION_ALIASES) {
|
|
131
|
+
const match = String(text || '').match(entry.re);
|
|
132
|
+
if (!match) continue;
|
|
133
|
+
matches.push({
|
|
134
|
+
canonical: entry.canonical,
|
|
135
|
+
group: entry.group,
|
|
136
|
+
family: entry.group,
|
|
137
|
+
score: 1,
|
|
138
|
+
strength: 'strong',
|
|
139
|
+
matched: match[0].trim(),
|
|
140
|
+
index: match.index ?? 0,
|
|
141
|
+
label: entry.label,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return matches;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Combine the core taxonomy with real-source aliases. More specific matches
|
|
149
|
+
* suppress contained generic matches ("Chief Accountant" must not also become
|
|
150
|
+
* "Accountant"), while separate spans remain separate professions.
|
|
151
|
+
*/
|
|
152
|
+
export function matchExtendedProfessions(value, { limit = 5, allowWeak = true } = {}) {
|
|
153
|
+
const text = String(value || '');
|
|
154
|
+
if (!text) return [];
|
|
155
|
+
const base = matchProfessions(text, { limit: Math.max(limit * 4, 24), allowWeak }).map((match) => {
|
|
156
|
+
const index = text.toLocaleLowerCase().indexOf(String(match.matched || '').toLocaleLowerCase());
|
|
157
|
+
return { ...match, index: index < 0 ? 0 : index, label: professionDisplayLabel(match.canonical) };
|
|
158
|
+
});
|
|
159
|
+
const matches = [...base, ...sourceMatches(text)]
|
|
160
|
+
.sort((a, b) => b.score - a.score || b.matched.length - a.matched.length || a.index - b.index);
|
|
161
|
+
|
|
162
|
+
const selected = [];
|
|
163
|
+
const canonicals = new Set();
|
|
164
|
+
for (const match of matches) {
|
|
165
|
+
if (canonicals.has(match.canonical)) continue;
|
|
166
|
+
const start = match.index;
|
|
167
|
+
const end = start + match.matched.length;
|
|
168
|
+
const contained = selected.some((chosen) => {
|
|
169
|
+
const chosenStart = chosen.index;
|
|
170
|
+
const chosenEnd = chosenStart + chosen.matched.length;
|
|
171
|
+
return chosen.score >= match.score && chosenStart <= start && chosenEnd >= end && chosen.matched.length > match.matched.length;
|
|
172
|
+
});
|
|
173
|
+
if (contained) continue;
|
|
174
|
+
canonicals.add(match.canonical);
|
|
175
|
+
selected.push({ ...match });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// A concrete software role outranks a generic "IT specialist" mention in
|
|
179
|
+
// the same role/title text, even when the two aliases occupy separate spans.
|
|
180
|
+
const hasSoftwareRole = selected.some((match) => match.group === 'software_development');
|
|
181
|
+
const filtered = hasSoftwareRole
|
|
182
|
+
? selected.filter((match) => match.canonical !== 'it_specialist')
|
|
183
|
+
: selected;
|
|
184
|
+
|
|
185
|
+
return Object.freeze(filtered.slice(0, limit).map((match) => Object.freeze(match)));
|
|
186
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SalaryParseResult } from '../index.d.ts';
|
|
2
|
+
|
|
3
|
+
export type CandidateExperienceMention = Readonly<{ years: number; context: string; approximate?: true }>;
|
|
4
|
+
export type VisaSponsorshipWording = 'offered' | 'notOffered' | null;
|
|
5
|
+
|
|
6
|
+
export function extractCandidateStructuredField(value: unknown, key: string, maxLength?: number): string | null;
|
|
7
|
+
export function extractJobStructuredField(value: unknown, key: string, maxLength?: number): string | null;
|
|
8
|
+
export function extractCandidateDisplayName(value: unknown): string;
|
|
9
|
+
export function extractCandidateExperienceMentions(value: unknown): readonly CandidateExperienceMention[];
|
|
10
|
+
export function parseHiringSourceSalary(value: unknown): SalaryParseResult | null;
|
|
11
|
+
export function parseCandidateSalary(value: unknown, country?: string): SalaryParseResult | null;
|
|
12
|
+
export function detectUsLocation(value: unknown): boolean;
|
|
13
|
+
export function detectVisaSponsorshipWording(value: unknown): VisaSponsorshipWording;
|
|
14
|
+
export const TEMPORARY_WORK_AUTH_RE: RegExp;
|
|
15
|
+
export function detectRecruitmentAgency(value: unknown): boolean;
|
|
16
|
+
export function extractNiceToHaveContext(value: unknown, maxLength?: number): string;
|
|
17
|
+
export type CandidatePostSignals = Readonly<{
|
|
18
|
+
candidateForm: boolean;
|
|
19
|
+
cvMarker: boolean;
|
|
20
|
+
firstPerson: boolean;
|
|
21
|
+
personalProfile: boolean;
|
|
22
|
+
contact: boolean;
|
|
23
|
+
emptyRecommendation: boolean;
|
|
24
|
+
sectionCount: number;
|
|
25
|
+
}>;
|
|
26
|
+
export function detectCandidatePostSignals(value: unknown): CandidatePostSignals;
|
|
27
|
+
export function extractCandidateStructuredBlock(value: unknown, key: string, maxLength?: number): string | null;
|
|
28
|
+
export function defaultHiringCurrency(country: unknown): string | null;
|
|
29
|
+
export function isHiringCharityAppeal(value: unknown): boolean;
|
|
30
|
+
export function isHiringRecruitingOpportunity(value: unknown): boolean;
|
|
31
|
+
export function sameHiringProfessionFamily(a: unknown, b: unknown): boolean;
|