@empyria/name 0.1.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/.oxfmtrc.json +9 -0
- package/.oxlintrc.json +3 -0
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/index.js +3 -0
- package/lib/Chunker.js +4 -0
- package/lib/Name.js +102 -0
- package/package.json +35 -0
- package/test/Name.test.js +75 -0
package/.oxfmtrc.json
ADDED
package/.oxlintrc.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Imre Fazekas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @empyria/name
|
|
2
|
+
|
|
3
|
+
Name and title manipulation utilities for **Empyria**, a nanoservice framework built
|
|
4
|
+
primarily on Bun.
|
|
5
|
+
|
|
6
|
+
Provides a curated list of honorifics/titles (`TITLE_LIST`/`TITLES`) spanning common,
|
|
7
|
+
formal, academic, and religious usage, plus `findTitle` to resolve free-text input
|
|
8
|
+
(any case, punctuation, or transliterable script) to its canonical form. The generic
|
|
9
|
+
string helpers `TRANS`, `capitalise`, and `shortify`, and `transliterate`/`slugify`
|
|
10
|
+
from the [`transliteration`](https://www.npmjs.com/package/transliteration) package,
|
|
11
|
+
are re-exported here for convenience since name/title handling relies on them heavily.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Bun `>=1.4.0` or Node.js `>=26`
|
|
16
|
+
- Plain ESM, no build step, no TypeScript
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bun add @empyria/name
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import { findTitle, TITLES, capitalise, slugify } from '@empyria/name'
|
|
28
|
+
|
|
29
|
+
findTitle('dr.') // 'Dr'
|
|
30
|
+
findTitle('Vice-Chancellor') // 'Vice-Chancellor'
|
|
31
|
+
findTitle('Wizard') // undefined — not a recognised title
|
|
32
|
+
|
|
33
|
+
TITLES.DR // 'Dr'
|
|
34
|
+
|
|
35
|
+
capitalise('jean') // 'Jean'
|
|
36
|
+
slugify('Jean Pierre') // 'jean-pierre'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Modules
|
|
40
|
+
|
|
41
|
+
| Module | Purpose |
|
|
42
|
+
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| [index.js](./index.js) | Package entry point; re-exports [lib/Name.js](./lib/Name.js). |
|
|
44
|
+
| [lib/Name.js](./lib/Name.js) | `TITLE_LIST`/`TITLES` (the full title catalog), `findTitle`, plus `TRANS`/`capitalise`/`shortify`/`transliterate`/`slugify` re-exported from `@empyria/classification`/`transliteration` for convenience. |
|
|
45
|
+
|
|
46
|
+
Tests live under [test/](./test/), one file per module, separate from the `lib/` sources.
|
|
47
|
+
|
|
48
|
+
## Scripts
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
bun run format # check formatting (oxfmt)
|
|
52
|
+
bun run format:fix # apply formatting
|
|
53
|
+
bun run lint # lint (oxlint)
|
|
54
|
+
bun run lint:fix # lint and fix
|
|
55
|
+
bun run test # run tests with coverage
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT © Imre Fazekas
|
package/index.js
ADDED
package/lib/Chunker.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Re-exported from @empyria/classification, which owns the canonical implementation (this
|
|
2
|
+
// package already depends on it) — kept here as its own module so `chunkDocument` stays
|
|
3
|
+
// importable from this package too, without maintaining a second copy that can drift.
|
|
4
|
+
export { chunkDocument } from '@empyria/classification'
|
package/lib/Name.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { transliterate, slugify } from 'transliteration'
|
|
2
|
+
|
|
3
|
+
import { enumise, TRANS, capitalise, shortify } from '@empyria/classification'
|
|
4
|
+
|
|
5
|
+
export const TITLE_LIST = [
|
|
6
|
+
// Common titles
|
|
7
|
+
'Master',
|
|
8
|
+
'Mr',
|
|
9
|
+
'Messrs',
|
|
10
|
+
'Miss',
|
|
11
|
+
'Mrs',
|
|
12
|
+
'Ms',
|
|
13
|
+
'Mx',
|
|
14
|
+
// Formal titles
|
|
15
|
+
'Sir',
|
|
16
|
+
'Gentleman',
|
|
17
|
+
'Sire',
|
|
18
|
+
'Mistress',
|
|
19
|
+
'Madame',
|
|
20
|
+
'Dame',
|
|
21
|
+
'Lord',
|
|
22
|
+
'Esq',
|
|
23
|
+
'Esquire',
|
|
24
|
+
'Excellency',
|
|
25
|
+
'Excellence',
|
|
26
|
+
'Her Honour',
|
|
27
|
+
'His Honour',
|
|
28
|
+
'The Right Honourable',
|
|
29
|
+
'The Most Honourable',
|
|
30
|
+
'The Much Honoured',
|
|
31
|
+
'King',
|
|
32
|
+
'Queen',
|
|
33
|
+
'Prince',
|
|
34
|
+
'Princess',
|
|
35
|
+
// Academic and professional titles
|
|
36
|
+
'Dr',
|
|
37
|
+
'Professor',
|
|
38
|
+
'KC', // King's Counsel
|
|
39
|
+
'Cl', // Counsel
|
|
40
|
+
'Counsel',
|
|
41
|
+
'SCl', // Senior Counsel
|
|
42
|
+
'Senior Counsel',
|
|
43
|
+
'Chancellor',
|
|
44
|
+
'Vice-Chancellor',
|
|
45
|
+
'Principal',
|
|
46
|
+
'President',
|
|
47
|
+
'Warden',
|
|
48
|
+
'Dean',
|
|
49
|
+
'Regent',
|
|
50
|
+
'Rector',
|
|
51
|
+
'Provost',
|
|
52
|
+
// Religious titles
|
|
53
|
+
'His Holiness',
|
|
54
|
+
'His All Holiness',
|
|
55
|
+
'His Beatitude',
|
|
56
|
+
'His Excellency',
|
|
57
|
+
'His Most Eminent Highness',
|
|
58
|
+
'His Eminence',
|
|
59
|
+
'Most Reverend Eminence',
|
|
60
|
+
'The Most Reverend',
|
|
61
|
+
'His Grace',
|
|
62
|
+
'His Lordship',
|
|
63
|
+
'The Reverend',
|
|
64
|
+
'Father',
|
|
65
|
+
'Pastor',
|
|
66
|
+
'Brother',
|
|
67
|
+
'Sister',
|
|
68
|
+
'Elder',
|
|
69
|
+
'Rabbi',
|
|
70
|
+
'Cantor',
|
|
71
|
+
'Chief Rabbi',
|
|
72
|
+
'Grand Rabbi',
|
|
73
|
+
'Rebbetzin',
|
|
74
|
+
transliterate('Imām'),
|
|
75
|
+
transliterate('Shaykh'),
|
|
76
|
+
transliterate('Muftī'),
|
|
77
|
+
transliterate('Hāfiz'),
|
|
78
|
+
transliterate('Hāfizah'),
|
|
79
|
+
transliterate('Qārī'),
|
|
80
|
+
transliterate('Mawlānā'),
|
|
81
|
+
transliterate('Hājī'),
|
|
82
|
+
'Sayyid',
|
|
83
|
+
'Sayyidah',
|
|
84
|
+
'Sharif',
|
|
85
|
+
'Ayatollah',
|
|
86
|
+
'Seghatoleslam',
|
|
87
|
+
'Mohyeddin',
|
|
88
|
+
'Eminent',
|
|
89
|
+
transliterate('Rōshi'),
|
|
90
|
+
'Sensei',
|
|
91
|
+
'Venerable',
|
|
92
|
+
]
|
|
93
|
+
export const TITLES = enumise(TITLE_LIST)
|
|
94
|
+
|
|
95
|
+
export function findTitle(_title) {
|
|
96
|
+
if (!_title) return undefined
|
|
97
|
+
|
|
98
|
+
const title = transliterate(_title.replace(/[^a-z0-9\s-]/gi, '')).toUpperCase()
|
|
99
|
+
return TITLE_LIST.find((t) => transliterate(t).toUpperCase() === title)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export { transliterate, slugify, TRANS, capitalise, shortify }
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@empyria/name",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Name and title manipulation library for Empyria",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Imre Fazekas <imre.fazekas@icloud.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/empyria-nano/name.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./index.js",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"format": "bun oxfmt --check",
|
|
15
|
+
"format:fix": "bun oxfmt",
|
|
16
|
+
"lint": "bun oxlint",
|
|
17
|
+
"lint:fix": "bun oxlint --fix",
|
|
18
|
+
"test": "bun test --coverage",
|
|
19
|
+
"test:watch": "bun test --watch"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@empyria/classification": "0.1.5",
|
|
23
|
+
"@empyria/common": "0.1.8",
|
|
24
|
+
"transliteration": "^2.6.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"oxfmt": "0.66.0",
|
|
28
|
+
"oxlint": "1.81.0"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"bun": ">=1.4.0",
|
|
32
|
+
"node": ">=26"
|
|
33
|
+
},
|
|
34
|
+
"packageManager": "bun@1.4.2"
|
|
35
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
TITLE_LIST,
|
|
4
|
+
TITLES,
|
|
5
|
+
findTitle,
|
|
6
|
+
TRANS,
|
|
7
|
+
capitalise,
|
|
8
|
+
shortify,
|
|
9
|
+
transliterate,
|
|
10
|
+
slugify,
|
|
11
|
+
} from '../lib/Name.js'
|
|
12
|
+
|
|
13
|
+
describe('TITLE_LIST', () => {
|
|
14
|
+
test('has no duplicate entries', () => {
|
|
15
|
+
expect(new Set(TITLE_LIST).size).toBe(TITLE_LIST.length)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('TITLES', () => {
|
|
20
|
+
test('enumises every title in TITLE_LIST', () => {
|
|
21
|
+
expect(TITLES.DR).toBe('Dr')
|
|
22
|
+
expect(TITLES.MR).toBe('Mr')
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('findTitle', () => {
|
|
27
|
+
test('finds a plain title regardless of case', () => {
|
|
28
|
+
expect(findTitle('dr')).toBe('Dr')
|
|
29
|
+
expect(findTitle('DR')).toBe('Dr')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('ignores surrounding punctuation', () => {
|
|
33
|
+
expect(findTitle('Dr.')).toBe('Dr')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('matches a multi-word title', () => {
|
|
37
|
+
expect(findTitle('the right honourable')).toBe('The Right Honourable')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('matches a hyphenated title', () => {
|
|
41
|
+
expect(findTitle('Vice-Chancellor')).toBe('Vice-Chancellor')
|
|
42
|
+
expect(findTitle('vice chancellor')).toBeUndefined()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('returns undefined for an unknown title', () => {
|
|
46
|
+
expect(findTitle('Wizard')).toBeUndefined()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('returns undefined for falsy input', () => {
|
|
50
|
+
expect(findTitle('')).toBeUndefined()
|
|
51
|
+
expect(findTitle(null)).toBeUndefined()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('re-exported string helpers (from @empyria/classification)', () => {
|
|
56
|
+
test('TRANS transliterates, strips whitespace, and upper-cases', () => {
|
|
57
|
+
expect(TRANS('hello world')).toBe('HELLOWORLD')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('capitalise upper-cases only the first character', () => {
|
|
61
|
+
expect(capitalise('hello')).toBe('Hello')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('shortify truncates and appends an ellipsis when over the limit', () => {
|
|
65
|
+
expect(shortify('hello world', 5)).toBe('hello...')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('transliterate is re-exported from the transliteration package', () => {
|
|
69
|
+
expect(typeof transliterate).toBe('function')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('slugify is re-exported from the transliteration package', () => {
|
|
73
|
+
expect(slugify('Jean Pierre')).toBe('jean-pierre')
|
|
74
|
+
})
|
|
75
|
+
})
|