namefully 1.3.0 → 2.0.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/dist/{lib → cjs}/builder.js +15 -13
- package/dist/cjs/config.js +100 -0
- package/dist/{lib → cjs}/constants.js +1 -1
- package/dist/{lib → cjs}/error.js +8 -5
- package/dist/cjs/fullname.js +102 -0
- package/dist/{lib → cjs}/index.js +18 -15
- package/dist/cjs/name.js +218 -0
- package/dist/cjs/namefully.js +391 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/parser.js +135 -0
- package/dist/{lib → cjs}/types.js +40 -36
- package/dist/{lib → cjs}/utils.js +17 -17
- package/dist/cjs/validator.js +266 -0
- package/dist/{types → esm}/builder.d.ts +8 -8
- package/dist/esm/builder.js +78 -0
- package/dist/{types → esm}/config.d.ts +1 -1
- package/dist/esm/config.js +96 -0
- package/dist/{types → esm}/constants.d.ts +1 -1
- package/dist/esm/constants.js +27 -0
- package/dist/{types → esm}/error.d.ts +2 -3
- package/dist/esm/error.js +87 -0
- package/dist/{types/full-name.d.ts → esm/fullname.d.ts} +3 -3
- package/dist/esm/fullname.js +98 -0
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.js +12 -0
- package/dist/{types → esm}/name.d.ts +2 -1
- package/dist/esm/name.js +211 -0
- package/dist/{types → esm}/namefully.d.ts +8 -8
- package/dist/esm/namefully.js +387 -0
- package/dist/esm/package.json +1 -0
- package/dist/{types → esm}/parser.d.ts +4 -4
- package/dist/esm/parser.js +127 -0
- package/dist/esm/types.js +106 -0
- package/dist/{types → esm}/utils.d.ts +1 -2
- package/dist/esm/utils.js +96 -0
- package/dist/{types → esm}/validator.d.ts +3 -3
- package/dist/esm/validator.js +259 -0
- package/dist/namefully.js +1580 -0
- package/dist/namefully.min.js +1 -0
- package/package.json +44 -27
- package/readme.md +1 -1
- package/dist/lib/config.js +0 -112
- package/dist/lib/full-name.js +0 -115
- package/dist/lib/name.js +0 -230
- package/dist/lib/namefully.js +0 -417
- package/dist/lib/parser.js +0 -144
- package/dist/lib/validator.js +0 -285
- package/dist/types/index.d.ts +0 -25
- package/dist/umd/namefully.js +0 -1931
- package/dist/umd/namefully.min.js +0 -1
- /package/dist/{types → esm}/types.d.ts +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { MIN_NUMBER_OF_NAME_PARTS, MAX_NUMBER_OF_NAME_PARTS } from './constants.js';
|
|
2
|
+
import { NameOrder, CapsRange } from './types.js';
|
|
3
|
+
export class NameIndex {
|
|
4
|
+
prefix;
|
|
5
|
+
firstName;
|
|
6
|
+
middleName;
|
|
7
|
+
lastName;
|
|
8
|
+
suffix;
|
|
9
|
+
static get min() {
|
|
10
|
+
return MIN_NUMBER_OF_NAME_PARTS;
|
|
11
|
+
}
|
|
12
|
+
static get max() {
|
|
13
|
+
return MAX_NUMBER_OF_NAME_PARTS;
|
|
14
|
+
}
|
|
15
|
+
constructor(prefix, firstName, middleName, lastName, suffix) {
|
|
16
|
+
this.prefix = prefix;
|
|
17
|
+
this.firstName = firstName;
|
|
18
|
+
this.middleName = middleName;
|
|
19
|
+
this.lastName = lastName;
|
|
20
|
+
this.suffix = suffix;
|
|
21
|
+
}
|
|
22
|
+
static base() {
|
|
23
|
+
return new this(-1, 0, -1, 1, -1);
|
|
24
|
+
}
|
|
25
|
+
static when(order, count = 2) {
|
|
26
|
+
if (order === NameOrder.FIRST_NAME) {
|
|
27
|
+
switch (count) {
|
|
28
|
+
case 2:
|
|
29
|
+
return new this(-1, 0, -1, 1, -1);
|
|
30
|
+
case 3:
|
|
31
|
+
return new this(-1, 0, 1, 2, -1);
|
|
32
|
+
case 4:
|
|
33
|
+
return new this(0, 1, 2, 3, -1);
|
|
34
|
+
case 5:
|
|
35
|
+
return new this(0, 1, 2, 3, 4);
|
|
36
|
+
default:
|
|
37
|
+
return NameIndex.base();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
switch (count) {
|
|
42
|
+
case 2:
|
|
43
|
+
return new this(-1, 1, -1, 0, -1);
|
|
44
|
+
case 3:
|
|
45
|
+
return new this(-1, 1, 2, 0, -1);
|
|
46
|
+
case 4:
|
|
47
|
+
return new this(0, 2, 3, 1, -1);
|
|
48
|
+
case 5:
|
|
49
|
+
return new this(0, 2, 3, 1, 4);
|
|
50
|
+
default:
|
|
51
|
+
return NameIndex.base();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
static only({ prefix = -1, firstName, middleName = -1, lastName, suffix = -1 }) {
|
|
56
|
+
return new this(prefix, firstName, middleName, lastName, suffix);
|
|
57
|
+
}
|
|
58
|
+
toJson() {
|
|
59
|
+
return {
|
|
60
|
+
prefix: this.prefix,
|
|
61
|
+
firstName: this.firstName,
|
|
62
|
+
middleName: this.middleName,
|
|
63
|
+
lastName: this.lastName,
|
|
64
|
+
suffix: this.suffix,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function capitalize(str, range = CapsRange.INITIAL) {
|
|
69
|
+
if (!str || range === CapsRange.NONE)
|
|
70
|
+
return str;
|
|
71
|
+
const initial = str[0].toUpperCase();
|
|
72
|
+
const rest = str.slice(1).toLowerCase();
|
|
73
|
+
return range === CapsRange.INITIAL ? initial.concat(rest) : str.toUpperCase();
|
|
74
|
+
}
|
|
75
|
+
export function decapitalize(str, range = CapsRange.INITIAL) {
|
|
76
|
+
if (!str || range === CapsRange.NONE)
|
|
77
|
+
return str;
|
|
78
|
+
const initial = str[0].toLowerCase();
|
|
79
|
+
const rest = str.slice(1);
|
|
80
|
+
return range === CapsRange.INITIAL ? initial.concat(rest) : str.toLowerCase();
|
|
81
|
+
}
|
|
82
|
+
export function toggleCase(str) {
|
|
83
|
+
const chars = [];
|
|
84
|
+
for (const c of str) {
|
|
85
|
+
if (c === c.toUpperCase()) {
|
|
86
|
+
chars.push(c.toLowerCase());
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
chars.push(c.toUpperCase());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return chars.join('');
|
|
93
|
+
}
|
|
94
|
+
export function isStringArray(value) {
|
|
95
|
+
return Array.isArray(value) && value.length > 0 && value.every((e) => typeof e === 'string');
|
|
96
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FirstName, LastName, Name } from './name';
|
|
2
|
-
import { Namon } from './types';
|
|
3
|
-
import { NameIndex } from './utils';
|
|
1
|
+
import { FirstName, LastName, Name } from './name.js';
|
|
2
|
+
import { Namon } from './types.js';
|
|
3
|
+
import { NameIndex } from './utils.js';
|
|
4
4
|
export interface Validator<T> {
|
|
5
5
|
validate(value: T): void;
|
|
6
6
|
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { MIN_NUMBER_OF_NAME_PARTS, MAX_NUMBER_OF_NAME_PARTS } from './constants.js';
|
|
2
|
+
import { InputError, ValidationError } from './error.js';
|
|
3
|
+
import { FirstName, LastName, Name, isNameArray } from './name.js';
|
|
4
|
+
import { Namon } from './types.js';
|
|
5
|
+
import { NameIndex } from './utils.js';
|
|
6
|
+
class ValidationRule {
|
|
7
|
+
static base = /[a-zA-Z\u00C0-\u00D6\u00D8-\u00f6\u00f8-\u00ff\u0400-\u04FFΆ-ωΑ-ώ]/;
|
|
8
|
+
static namon = new RegExp(`^${ValidationRule.base.source}+(([' -]${ValidationRule.base.source})?${ValidationRule.base.source}*)*$`);
|
|
9
|
+
static firstName = ValidationRule.namon;
|
|
10
|
+
static middleName = new RegExp(`^${ValidationRule.base.source}+(([' -]${ValidationRule.base.source})?${ValidationRule.base.source}*)*$`);
|
|
11
|
+
static lastName = ValidationRule.namon;
|
|
12
|
+
}
|
|
13
|
+
const toNameSource = (values) => {
|
|
14
|
+
return isNameArray(values) ? values.map((n) => n.toString()).join(' ') : '';
|
|
15
|
+
};
|
|
16
|
+
class ArrayValidator {
|
|
17
|
+
validate(values) {
|
|
18
|
+
if (values.length === 0 || values.length < MIN_NUMBER_OF_NAME_PARTS || values.length > MAX_NUMBER_OF_NAME_PARTS) {
|
|
19
|
+
throw new InputError({
|
|
20
|
+
source: values.map((n) => n.toString()),
|
|
21
|
+
message: `expecting a list of ${MIN_NUMBER_OF_NAME_PARTS}-${MIN_NUMBER_OF_NAME_PARTS} elements`,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
class NamonValidator {
|
|
27
|
+
static #validator;
|
|
28
|
+
static create() {
|
|
29
|
+
return this.#validator || (this.#validator = new this());
|
|
30
|
+
}
|
|
31
|
+
validate(value, type) {
|
|
32
|
+
if (value instanceof Name) {
|
|
33
|
+
NameValidator.create().validate(value, type);
|
|
34
|
+
}
|
|
35
|
+
else if (typeof value === 'string') {
|
|
36
|
+
if (!ValidationRule.namon.test(value)) {
|
|
37
|
+
throw new ValidationError({
|
|
38
|
+
source: value,
|
|
39
|
+
nameType: 'namon',
|
|
40
|
+
message: 'invalid content',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new InputError({
|
|
46
|
+
source: typeof value,
|
|
47
|
+
message: 'expecting types of string | Name',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
class FirstNameValidator {
|
|
53
|
+
static #validator;
|
|
54
|
+
static create() {
|
|
55
|
+
return this.#validator || (this.#validator = new this());
|
|
56
|
+
}
|
|
57
|
+
validate(value) {
|
|
58
|
+
if (value instanceof FirstName) {
|
|
59
|
+
value.asNames.forEach((name) => this.validate(name.value));
|
|
60
|
+
}
|
|
61
|
+
else if (typeof value === 'string') {
|
|
62
|
+
if (!ValidationRule.firstName.test(value)) {
|
|
63
|
+
throw new ValidationError({
|
|
64
|
+
source: value,
|
|
65
|
+
nameType: 'firstName',
|
|
66
|
+
message: 'invalid content',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new InputError({
|
|
72
|
+
source: typeof value,
|
|
73
|
+
message: 'expecting types string | FirstName',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
class MiddleNameValidator {
|
|
79
|
+
static #validator;
|
|
80
|
+
static create() {
|
|
81
|
+
return this.#validator || (this.#validator = new this());
|
|
82
|
+
}
|
|
83
|
+
validate(value) {
|
|
84
|
+
if (typeof value === 'string') {
|
|
85
|
+
if (!ValidationRule.middleName.test(value)) {
|
|
86
|
+
throw new ValidationError({
|
|
87
|
+
source: value,
|
|
88
|
+
nameType: 'middleName',
|
|
89
|
+
message: 'invalid content',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (Array.isArray(value)) {
|
|
94
|
+
try {
|
|
95
|
+
const validator = NamonValidator.create();
|
|
96
|
+
for (const name of value)
|
|
97
|
+
validator.validate(name, Namon.MIDDLE_NAME);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw new ValidationError({
|
|
101
|
+
source: toNameSource(value),
|
|
102
|
+
nameType: 'middleName',
|
|
103
|
+
message: error?.message,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
throw new InputError({
|
|
109
|
+
source: typeof value,
|
|
110
|
+
message: 'expecting types of string | string[] | Name[]',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
class LastNameValidator {
|
|
116
|
+
static #validator;
|
|
117
|
+
static create() {
|
|
118
|
+
return this.#validator || (this.#validator = new this());
|
|
119
|
+
}
|
|
120
|
+
validate(value) {
|
|
121
|
+
if (value instanceof LastName) {
|
|
122
|
+
value.asNames.forEach((name) => this.validate(name.value));
|
|
123
|
+
}
|
|
124
|
+
else if (typeof value === 'string') {
|
|
125
|
+
if (!ValidationRule.lastName.test(value)) {
|
|
126
|
+
throw new ValidationError({
|
|
127
|
+
source: value,
|
|
128
|
+
nameType: 'lastName',
|
|
129
|
+
message: 'invalid content',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new InputError({
|
|
135
|
+
source: typeof value,
|
|
136
|
+
message: 'expecting types string | LastName',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
class NameValidator {
|
|
142
|
+
static #validator;
|
|
143
|
+
static create() {
|
|
144
|
+
return this.#validator || (this.#validator = new this());
|
|
145
|
+
}
|
|
146
|
+
validate(name, type) {
|
|
147
|
+
if (type && name.type !== type) {
|
|
148
|
+
throw new ValidationError({
|
|
149
|
+
source: name.toString(),
|
|
150
|
+
nameType: name.type.toString(),
|
|
151
|
+
message: 'wrong type',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (!ValidationRule.namon.test(name.value)) {
|
|
155
|
+
throw new ValidationError({
|
|
156
|
+
source: name.toString(),
|
|
157
|
+
nameType: name.type.toString(),
|
|
158
|
+
message: 'invalid content',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export class NamaValidator {
|
|
164
|
+
static #validator;
|
|
165
|
+
static create() {
|
|
166
|
+
return this.#validator || (this.#validator = new this());
|
|
167
|
+
}
|
|
168
|
+
validate(value) {
|
|
169
|
+
this.validateKeys(value);
|
|
170
|
+
Validators.firstName.validate(value.get(Namon.FIRST_NAME));
|
|
171
|
+
Validators.lastName.validate(value.get(Namon.LAST_NAME));
|
|
172
|
+
if (value.has(Namon.PREFIX))
|
|
173
|
+
Validators.namon.validate(value.get(Namon.PREFIX));
|
|
174
|
+
if (value.has(Namon.SUFFIX))
|
|
175
|
+
Validators.namon.validate(value.get(Namon.SUFFIX));
|
|
176
|
+
}
|
|
177
|
+
validateKeys(nama) {
|
|
178
|
+
if (!nama.size) {
|
|
179
|
+
throw new InputError({ source: undefined, message: 'Map<k,v> must not be empty' });
|
|
180
|
+
}
|
|
181
|
+
else if (nama.size < MIN_NUMBER_OF_NAME_PARTS || nama.size > MAX_NUMBER_OF_NAME_PARTS) {
|
|
182
|
+
throw new InputError({
|
|
183
|
+
source: [...nama.values()],
|
|
184
|
+
message: `expecting ${MIN_NUMBER_OF_NAME_PARTS}-${MIN_NUMBER_OF_NAME_PARTS} fields`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (!nama.has(Namon.FIRST_NAME)) {
|
|
188
|
+
throw new InputError({
|
|
189
|
+
source: [...nama.values()],
|
|
190
|
+
message: '"firstName" is a required key',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (!nama.has(Namon.LAST_NAME)) {
|
|
194
|
+
throw new InputError({
|
|
195
|
+
source: [...nama.values()],
|
|
196
|
+
message: '"lastName" is a required key',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export class ArrayStringValidator extends ArrayValidator {
|
|
202
|
+
index;
|
|
203
|
+
constructor(index = NameIndex.base()) {
|
|
204
|
+
super();
|
|
205
|
+
this.index = index;
|
|
206
|
+
}
|
|
207
|
+
validate(values) {
|
|
208
|
+
this.validateIndex(values);
|
|
209
|
+
Validators.firstName.validate(values[this.index.firstName]);
|
|
210
|
+
Validators.lastName.validate(values[this.index.lastName]);
|
|
211
|
+
if (values.length >= 3)
|
|
212
|
+
Validators.middleName.validate(values[this.index.middleName]);
|
|
213
|
+
if (values.length >= 4)
|
|
214
|
+
Validators.namon.validate(values[this.index.prefix]);
|
|
215
|
+
if (values.length === 5)
|
|
216
|
+
Validators.namon.validate(values[this.index.suffix]);
|
|
217
|
+
}
|
|
218
|
+
validateIndex(values) {
|
|
219
|
+
super.validate(values);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export class ArrayNameValidator {
|
|
223
|
+
static #validator;
|
|
224
|
+
static create() {
|
|
225
|
+
return this.#validator || (this.#validator = new this());
|
|
226
|
+
}
|
|
227
|
+
validate(value) {
|
|
228
|
+
if (value.length < MIN_NUMBER_OF_NAME_PARTS) {
|
|
229
|
+
throw new InputError({
|
|
230
|
+
source: toNameSource(value),
|
|
231
|
+
message: `expecting at least ${MIN_NUMBER_OF_NAME_PARTS} elements`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (!this.#hasBasicNames(value)) {
|
|
235
|
+
throw new InputError({
|
|
236
|
+
source: toNameSource(value),
|
|
237
|
+
message: 'both first and last names are required',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
#hasBasicNames(names) {
|
|
242
|
+
const accumulator = {};
|
|
243
|
+
for (const name of names) {
|
|
244
|
+
if (name.isFirstName || name.isLastName) {
|
|
245
|
+
accumulator[name.type.key] = name.toString();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return Object.keys(accumulator).length === MIN_NUMBER_OF_NAME_PARTS;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
export class Validators {
|
|
252
|
+
static namon = NamonValidator.create();
|
|
253
|
+
static nama = NamaValidator.create();
|
|
254
|
+
static prefix = NamonValidator.create();
|
|
255
|
+
static firstName = FirstNameValidator.create();
|
|
256
|
+
static middleName = MiddleNameValidator.create();
|
|
257
|
+
static lastName = LastNameValidator.create();
|
|
258
|
+
static suffix = NamonValidator.create();
|
|
259
|
+
}
|