namefully 2.0.2 → 2.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/dist/namefully.js CHANGED
@@ -4,33 +4,11 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.namefully = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
- const VERSION = '2.0.2';
7
+ const VERSION = '2.2.0';
8
8
  const MIN_NUMBER_OF_NAME_PARTS = 2;
9
9
  const MAX_NUMBER_OF_NAME_PARTS = 5;
10
- const ALLOWED_FORMAT_TOKENS = [
11
- '.',
12
- ',',
13
- ' ',
14
- '-',
15
- '_',
16
- 'b',
17
- 'B',
18
- 'f',
19
- 'F',
20
- 'l',
21
- 'L',
22
- 'm',
23
- 'M',
24
- 'n',
25
- 'N',
26
- 'o',
27
- 'O',
28
- 'p',
29
- 'P',
30
- 's',
31
- 'S',
32
- '$',
33
- ];
10
+ const ALLOWED_FORMAT_TOKENS = ` .,_-()[]<>'"bBfFlLmMnNoOpPsS$`;
11
+ const ZERO_WIDTH_SPACE = String.fromCharCode(8203);
34
12
 
35
13
  exports.Title = void 0;
36
14
  (function (Title) {
@@ -87,6 +65,13 @@
87
65
  [Namon.LAST_NAME.key, Namon.LAST_NAME],
88
66
  [Namon.SUFFIX.key, Namon.SUFFIX],
89
67
  ]);
68
+ static aliases = {
69
+ [Namon.PREFIX.key]: ['prefix', 'px', 'p'],
70
+ [Namon.FIRST_NAME.key]: ['firstname', 'first', 'fn', 'f'],
71
+ [Namon.MIDDLE_NAME.key]: ['middlename', 'middle', 'mid', 'mn', 'm'],
72
+ [Namon.LAST_NAME.key]: ['lastname', 'last', 'ln', 'l'],
73
+ [Namon.SUFFIX.key]: ['suffix', 'sx', 's'],
74
+ };
90
75
  constructor(index, key) {
91
76
  this.index = index;
92
77
  this.key = key;
@@ -95,7 +80,9 @@
95
80
  return Namon.all.has(key);
96
81
  }
97
82
  static cast(key) {
98
- return Namon.has(key) ? Namon.all.get(key) : undefined;
83
+ const searchValue = String(key).toLowerCase();
84
+ const namon = Object.entries(Namon.aliases).find(([, list]) => list.includes(searchValue))?.[0];
85
+ return Namon.has(namon ?? '') ? Namon.all.get(key) : undefined;
99
86
  }
100
87
  toString() {
101
88
  return `Namon.${this.key}`;
@@ -134,6 +121,14 @@
134
121
  this.name = name;
135
122
  this.token = token;
136
123
  }
124
+ static cast(key) {
125
+ for (const [name, separator] of Separator.all) {
126
+ if (separator.token === key || name.toLowerCase() === key.toLowerCase()) {
127
+ return separator;
128
+ }
129
+ }
130
+ return undefined;
131
+ }
137
132
  toString() {
138
133
  return `Separator.${this.name}`;
139
134
  }
@@ -472,7 +467,7 @@
472
467
  return this.mother ?? '';
473
468
  case exports.Surname.HYPHENATED:
474
469
  return this.hasMother ? `${this.value}-${this.#mother}` : this.value;
475
- case exports.Surname.ALL:
470
+ default:
476
471
  return this.hasMother ? `${this.value} ${this.#mother}` : this.value;
477
472
  }
478
473
  }
@@ -527,6 +522,7 @@
527
522
  #ending;
528
523
  #bypass;
529
524
  #surname;
525
+ #mono;
530
526
  static cache = new Map();
531
527
  get orderedBy() {
532
528
  return this.#orderedBy;
@@ -546,10 +542,13 @@
546
542
  get surname() {
547
543
  return this.#surname;
548
544
  }
545
+ get mono() {
546
+ return this.#mono;
547
+ }
549
548
  get name() {
550
549
  return this.#name;
551
550
  }
552
- constructor(name, orderedBy = exports.NameOrder.FIRST_NAME, separator = Separator.SPACE, title = exports.Title.UK, ending = false, bypass = true, surname = exports.Surname.FATHER) {
551
+ constructor(name, orderedBy = exports.NameOrder.FIRST_NAME, separator = Separator.SPACE, title = exports.Title.UK, ending = false, bypass = true, surname = exports.Surname.FATHER, mono = false) {
553
552
  this.#name = name;
554
553
  this.#orderedBy = orderedBy;
555
554
  this.#separator = separator;
@@ -557,6 +556,7 @@
557
556
  this.#ending = ending;
558
557
  this.#bypass = bypass;
559
558
  this.#surname = surname;
559
+ this.#mono = mono;
560
560
  }
561
561
  static create(name = defaultName) {
562
562
  if (!_a.cache.has(name))
@@ -575,11 +575,12 @@
575
575
  config.#ending = other.ending ?? config.ending;
576
576
  config.#bypass = other.bypass ?? config.bypass;
577
577
  config.#surname = other.surname ?? config.surname;
578
+ config.#mono = other.mono ?? config.mono;
578
579
  return config;
579
580
  }
580
581
  }
581
582
  copyWith(options = {}) {
582
- const { name, orderedBy, separator, title, ending, bypass, surname } = options;
583
+ const { name, orderedBy, separator, title, ending, bypass, surname, mono } = options;
583
584
  const config = _a.create(this.#genNewName(name ?? this.name + copyAlias));
584
585
  config.#orderedBy = orderedBy ?? this.orderedBy;
585
586
  config.#separator = separator ?? this.separator;
@@ -587,6 +588,7 @@
587
588
  config.#ending = ending ?? this.ending;
588
589
  config.#bypass = bypass ?? this.bypass;
589
590
  config.#surname = surname ?? this.surname;
591
+ config.#mono = mono ?? this.mono;
590
592
  return config;
591
593
  }
592
594
  clone() {
@@ -599,11 +601,9 @@
599
601
  this.#ending = false;
600
602
  this.#bypass = true;
601
603
  this.#surname = exports.Surname.FATHER;
604
+ this.#mono = false;
602
605
  _a.cache.set(this.name, this);
603
606
  }
604
- updateOrder(orderedBy) {
605
- this.update({ orderedBy });
606
- }
607
607
  update({ orderedBy, title, ending }) {
608
608
  const config = _a.cache.get(this.name);
609
609
  if (!config)
@@ -889,14 +889,18 @@
889
889
  get suffix() {
890
890
  return this.#suffix;
891
891
  }
892
+ get isMono() {
893
+ return this instanceof Mononym;
894
+ }
892
895
  static parse(json, config) {
893
896
  try {
897
+ const { prefix, firstName: fn, middleName: mn, lastName: ln, suffix } = json;
894
898
  return new FullName(config)
895
- .setPrefix(json.prefix)
896
- .setFirstName(json.firstName)
897
- .setMiddleName(json.middleName ?? [])
898
- .setLastName(json.lastName)
899
- .setSuffix(json.suffix);
899
+ .setPrefix(prefix)
900
+ .setFirstName(typeof fn === 'string' ? fn : new FirstName(fn.value, ...(fn.more ?? [])))
901
+ .setMiddleName(typeof mn === 'string' ? [mn] : (mn ?? []))
902
+ .setLastName(typeof ln === 'string' ? ln : new LastName(ln.father, ln.mother))
903
+ .setSuffix(suffix);
900
904
  }
901
905
  catch (error) {
902
906
  if (error instanceof NameError)
@@ -914,7 +918,7 @@
914
918
  if (!this.#config.bypass)
915
919
  Validators.prefix.validate(name);
916
920
  const prefix = name instanceof Name ? name.value : name;
917
- this.#prefix = Name.prefix(this.#config.title === exports.Title.US ? `${prefix}.` : prefix);
921
+ this.#prefix = Name.prefix(this.#config.title === exports.Title.US && !prefix.endsWith('.') ? `${prefix}.` : prefix);
918
922
  return this;
919
923
  }
920
924
  setFirstName(name) {
@@ -945,13 +949,74 @@
945
949
  this.#suffix = Name.suffix(name instanceof Name ? name.value : name);
946
950
  return this;
947
951
  }
948
- has(namon) {
952
+ has(key) {
953
+ const namon = typeof key === 'string' ? Namon.cast(key) : key;
954
+ if (!namon)
955
+ return false;
949
956
  if (namon.equal(Namon.PREFIX))
950
957
  return !!this.#prefix;
951
958
  if (namon.equal(Namon.SUFFIX))
952
959
  return !!this.#suffix;
953
960
  return namon.equal(Namon.MIDDLE_NAME) ? this.#middleName.length > 0 : true;
954
961
  }
962
+ toString() {
963
+ if (this.isMono)
964
+ return this.value;
965
+ return Array.from(this.toIterable(true)).join(' ');
966
+ }
967
+ *toIterable(flat = false) {
968
+ if (this.#prefix)
969
+ yield this.#prefix;
970
+ if (flat) {
971
+ yield* this.#firstName.asNames;
972
+ yield* this.#middleName;
973
+ yield* this.#lastName.asNames;
974
+ }
975
+ else {
976
+ yield this.#firstName;
977
+ yield* this.#middleName;
978
+ yield this.#lastName;
979
+ }
980
+ if (this.#suffix)
981
+ yield this.#suffix;
982
+ }
983
+ *[Symbol.iterator]() {
984
+ yield* this.toIterable(true);
985
+ }
986
+ }
987
+ class Mononym extends FullName {
988
+ #namon;
989
+ #type;
990
+ constructor(name, options) {
991
+ super(options ?? { name: 'mononym', mono: true });
992
+ this.#namon = name.toString();
993
+ this.type = name instanceof Name ? name.type : Namon.FIRST_NAME;
994
+ }
995
+ set type(type) {
996
+ this.#type = typeof type === 'string' ? (Namon.cast(type) ?? Namon.FIRST_NAME) : type;
997
+ this.#build(this.#namon);
998
+ }
999
+ get type() {
1000
+ return this.#type;
1001
+ }
1002
+ get value() {
1003
+ return this.#namon;
1004
+ }
1005
+ #build(name) {
1006
+ this.setFirstName(ZERO_WIDTH_SPACE).setLastName(ZERO_WIDTH_SPACE).setMiddleName([]).setPrefix(null).setSuffix(null);
1007
+ if (this.#type.equal(Namon.FIRST_NAME))
1008
+ this.setFirstName(name);
1009
+ else if (this.#type.equal(Namon.LAST_NAME))
1010
+ this.setLastName(name);
1011
+ else if (this.#type.equal(Namon.MIDDLE_NAME))
1012
+ this.setMiddleName([name]);
1013
+ else if (this.#type.equal(Namon.PREFIX))
1014
+ this.setPrefix(name);
1015
+ else if (this.#type.equal(Namon.SUFFIX))
1016
+ this.setSuffix(name);
1017
+ else
1018
+ throw new NameError(name, 'invalid mononym type');
1019
+ }
955
1020
  }
956
1021
 
957
1022
  class Parser {
@@ -969,10 +1034,7 @@
969
1034
  return new ArrayNameParser(names);
970
1035
  }
971
1036
  if (length < 2) {
972
- throw new InputError({
973
- source: text,
974
- message: 'cannot build from invalid input',
975
- });
1037
+ throw new InputError({ source: text, message: 'expecting at least 2 name parts' });
976
1038
  }
977
1039
  else if (length === 2 || length === 3) {
978
1040
  return new StringParser(text);
@@ -996,13 +1058,15 @@
996
1058
  parse(options) {
997
1059
  const config = Config.merge(options);
998
1060
  const names = this.raw.split(config.separator.token);
999
- return new ArrayStringParser(names).parse(options);
1061
+ return new ArrayStringParser(names).parse(config);
1000
1062
  }
1001
1063
  }
1002
1064
  class ArrayStringParser extends Parser {
1003
1065
  parse(options) {
1004
1066
  const config = Config.merge(options);
1005
- const fullName = new FullName(config);
1067
+ if (this.raw.length === 1 && config.mono) {
1068
+ return new MonoParser(this.raw[0]).parse(config);
1069
+ }
1006
1070
  const raw = this.raw.map((n) => n.trim());
1007
1071
  const index = NameIndex.when(config.orderedBy, raw.length);
1008
1072
  const validator = new ArrayStringValidator(index);
@@ -1013,8 +1077,9 @@
1013
1077
  validator.validate(raw);
1014
1078
  }
1015
1079
  const { firstName, lastName, middleName, prefix, suffix } = index;
1016
- fullName.setFirstName(new FirstName(raw[firstName]));
1017
- fullName.setLastName(new LastName(raw[lastName]));
1080
+ const fullName = new FullName(config)
1081
+ .setFirstName(new FirstName(raw[firstName]))
1082
+ .setLastName(new LastName(raw[lastName]));
1018
1083
  if (raw.length >= 3)
1019
1084
  fullName.setMiddleName(raw[middleName].split(config.separator.token));
1020
1085
  if (raw.length >= 4)
@@ -1038,10 +1103,10 @@
1038
1103
  return [namon, value];
1039
1104
  }));
1040
1105
  if (config.bypass) {
1041
- NamaValidator.create().validateKeys(names);
1106
+ Validators.nama.validateKeys(names);
1042
1107
  }
1043
1108
  else {
1044
- NamaValidator.create().validate(names);
1109
+ Validators.nama.validate(names);
1045
1110
  }
1046
1111
  return FullName.parse(this.raw, config);
1047
1112
  }
@@ -1049,8 +1114,13 @@
1049
1114
  class ArrayNameParser extends Parser {
1050
1115
  parse(options) {
1051
1116
  const config = Config.merge(options);
1117
+ if (this.raw.length === 1 && config.mono) {
1118
+ return new MonoParser(this.raw[0]).parse(options);
1119
+ }
1120
+ else {
1121
+ ArrayNameValidator.create().validate(this.raw);
1122
+ }
1052
1123
  const fullName = new FullName(config);
1053
- ArrayNameValidator.create().validate(this.raw);
1054
1124
  for (const name of this.raw) {
1055
1125
  if (name.isPrefix) {
1056
1126
  fullName.setPrefix(name);
@@ -1065,13 +1135,23 @@
1065
1135
  fullName.middleName.push(name);
1066
1136
  }
1067
1137
  else if (name.isLastName) {
1068
- const lastName = new LastName(name.value, name instanceof LastName ? name.mother : undefined, config.surname);
1069
- fullName.setLastName(lastName);
1138
+ const mother = name instanceof LastName ? name.mother : undefined;
1139
+ fullName.setLastName(new LastName(name.value, mother, config.surname));
1070
1140
  }
1071
1141
  }
1072
1142
  return fullName;
1073
1143
  }
1074
1144
  }
1145
+ class MonoParser extends Parser {
1146
+ parse(options) {
1147
+ const config = Config.merge(options);
1148
+ if (config.bypass)
1149
+ Validators.namon.validate(this.raw);
1150
+ const type = config.mono instanceof Namon ? config.mono : Namon.FIRST_NAME;
1151
+ const name = this.raw instanceof Name ? this.raw : new Name(this.raw.trim(), type);
1152
+ return new Mononym(name, config);
1153
+ }
1154
+ }
1075
1155
 
1076
1156
  class Namefully {
1077
1157
  #fullName;
@@ -1092,7 +1172,12 @@
1092
1172
  get config() {
1093
1173
  return this.#fullName.config;
1094
1174
  }
1175
+ get isMono() {
1176
+ return this.#fullName.isMono;
1177
+ }
1095
1178
  get length() {
1179
+ if (this.isMono)
1180
+ return this.#fullName.toString().length;
1096
1181
  return this.birth.length;
1097
1182
  }
1098
1183
  get prefix() {
@@ -1131,25 +1216,43 @@
1131
1216
  get salutation() {
1132
1217
  return this.format('p l');
1133
1218
  }
1219
+ get parts() {
1220
+ return this.#fullName.toIterable();
1221
+ }
1222
+ get size() {
1223
+ return Array.from(this.parts).length;
1224
+ }
1225
+ *[Symbol.iterator]() {
1226
+ yield* this.#fullName.toIterable(true);
1227
+ }
1134
1228
  toString() {
1135
1229
  return this.full;
1136
1230
  }
1137
- get(namon) {
1138
- if (namon.equal(Namon.PREFIX))
1231
+ get(key) {
1232
+ const namon = typeof key === 'string' ? Namon.cast(key) : key;
1233
+ if (namon?.equal(Namon.PREFIX))
1139
1234
  return this.#fullName.prefix;
1140
- if (namon.equal(Namon.FIRST_NAME))
1235
+ if (namon?.equal(Namon.FIRST_NAME))
1141
1236
  return this.#fullName.firstName;
1142
- if (namon.equal(Namon.MIDDLE_NAME))
1237
+ if (namon?.equal(Namon.MIDDLE_NAME))
1143
1238
  return this.#fullName.middleName;
1144
- if (namon.equal(Namon.LAST_NAME))
1239
+ if (namon?.equal(Namon.LAST_NAME))
1145
1240
  return this.#fullName.lastName;
1146
- if (namon.equal(Namon.SUFFIX))
1241
+ if (namon?.equal(Namon.SUFFIX))
1147
1242
  return this.#fullName.suffix;
1148
1243
  return undefined;
1149
1244
  }
1150
1245
  equal(other) {
1151
1246
  return this.toString() === other.toString();
1152
1247
  }
1248
+ deepEqual(other) {
1249
+ const others = Array.from(other.parts);
1250
+ for (const part of this.parts) {
1251
+ if (!others.some((name) => name.equal(part)))
1252
+ return false;
1253
+ }
1254
+ return true;
1255
+ }
1153
1256
  toJson() {
1154
1257
  return {
1155
1258
  prefix: this.prefix,
@@ -1164,6 +1267,8 @@
1164
1267
  return this.#fullName.has(namon);
1165
1268
  }
1166
1269
  fullName(orderedBy) {
1270
+ if (this.isMono)
1271
+ return this.#fullName.toString();
1167
1272
  const sep = this.config.ending ? ',' : '';
1168
1273
  const names = [];
1169
1274
  if (this.prefix)
@@ -1172,13 +1277,21 @@
1172
1277
  names.push(this.first, ...this.middleName(), this.last + sep);
1173
1278
  }
1174
1279
  else {
1175
- names.push(this.last, this.first, this.middleName().join(' ') + sep);
1280
+ names.push(this.last);
1281
+ if (this.hasMiddle) {
1282
+ names.push(this.first, this.middleName().join(' ') + sep);
1283
+ }
1284
+ else {
1285
+ names.push(this.first + sep);
1286
+ }
1176
1287
  }
1177
1288
  if (this.suffix)
1178
1289
  names.push(this.suffix);
1179
1290
  return names.join(' ').trim();
1180
1291
  }
1181
1292
  birthName(orderedBy) {
1293
+ if (this.isMono)
1294
+ return this.#fullName.toString();
1182
1295
  orderedBy ??= this.config.orderedBy;
1183
1296
  return orderedBy === exports.NameOrder.FIRST_NAME
1184
1297
  ? [this.first, ...this.middleName(), this.last].join(' ')
@@ -1194,6 +1307,8 @@
1194
1307
  return this.#fullName.lastName.toString(format);
1195
1308
  }
1196
1309
  initials(options) {
1310
+ if (this.isMono)
1311
+ return [this.#fullName.toString()[0]];
1197
1312
  const { orderedBy = this.config.orderedBy, only = exports.NameType.BIRTH_NAME, asJson } = options ?? {};
1198
1313
  const firstInits = this.#fullName.firstName.initials();
1199
1314
  const midInits = this.#fullName.middleName.map((n) => n.value[0]);
@@ -1211,6 +1326,8 @@
1211
1326
  }
1212
1327
  }
1213
1328
  shorten(orderedBy) {
1329
+ if (this.isMono)
1330
+ return this.#fullName.toString();
1214
1331
  orderedBy ??= this.config.orderedBy;
1215
1332
  const { firstName, lastName } = this.#fullName;
1216
1333
  return orderedBy === exports.NameOrder.FIRST_NAME
@@ -1221,6 +1338,8 @@
1221
1338
  const { by = exports.Flat.MIDDLE_NAME, limit = 20, recursive = false, withMore = false, withPeriod = true, surname, } = options;
1222
1339
  if (this.length <= limit)
1223
1340
  return this.full;
1341
+ if (this.isMono)
1342
+ return `${this.initials()}${withPeriod ? '.' : ''}`;
1224
1343
  const { firstName, lastName, middleName } = this.#fullName;
1225
1344
  const sep = withPeriod ? '.' : '';
1226
1345
  const hasMid = this.hasMiddle;
@@ -1248,7 +1367,7 @@
1248
1367
  case exports.Flat.MID_LAST:
1249
1368
  name = hasMid ? [fn, m, l] : [fn, l];
1250
1369
  break;
1251
- case exports.Flat.ALL:
1370
+ default:
1252
1371
  name = hasMid ? [f, m, l] : [f, l];
1253
1372
  break;
1254
1373
  }
@@ -1270,7 +1389,7 @@
1270
1389
  case exports.Flat.MID_LAST:
1271
1390
  name = hasMid ? [l, fn, m] : [l, fn];
1272
1391
  break;
1273
- case exports.Flat.ALL:
1392
+ default:
1274
1393
  name = hasMid ? [l, f, m] : [l, f];
1275
1394
  break;
1276
1395
  }
@@ -1311,7 +1430,7 @@
1311
1430
  let group = '';
1312
1431
  const formatted = [];
1313
1432
  for (const char of pattern) {
1314
- if (ALLOWED_FORMAT_TOKENS.indexOf(char) === -1) {
1433
+ if (!ALLOWED_FORMAT_TOKENS.includes(char)) {
1315
1434
  throw new NotAllowedError({
1316
1435
  source: this.full,
1317
1436
  operation: 'format',
@@ -1368,6 +1487,28 @@
1368
1487
  toToggleCase() {
1369
1488
  return toggleCase(this.birth);
1370
1489
  }
1490
+ serialize() {
1491
+ const { config, firstName: fn, lastName: ln } = this.#fullName;
1492
+ return {
1493
+ names: {
1494
+ prefix: this.prefix,
1495
+ firstName: fn.hasMore ? { value: fn.value, more: fn.more } : fn.value,
1496
+ middleName: this.hasMiddle ? this.middleName() : undefined,
1497
+ lastName: ln.hasMother ? { father: ln.father, mother: ln.mother } : ln.value,
1498
+ suffix: this.suffix,
1499
+ },
1500
+ config: {
1501
+ name: config.name,
1502
+ orderedBy: config.orderedBy,
1503
+ separator: config.separator.token,
1504
+ title: config.title,
1505
+ ending: config.ending,
1506
+ bypass: config.bypass,
1507
+ surname: config.surname,
1508
+ mono: config.mono instanceof Namon ? config.mono.key : config.mono,
1509
+ },
1510
+ };
1511
+ }
1371
1512
  #toParser(raw) {
1372
1513
  if (raw instanceof Parser)
1373
1514
  return raw;
@@ -1383,12 +1524,6 @@
1383
1524
  }
1384
1525
  #map(char) {
1385
1526
  switch (char) {
1386
- case '.':
1387
- case ',':
1388
- case ' ':
1389
- case '-':
1390
- case '_':
1391
- return char;
1392
1527
  case 'b':
1393
1528
  return this.birth;
1394
1529
  case 'B':
@@ -1430,16 +1565,19 @@
1430
1565
  case 'S':
1431
1566
  return this.suffix?.toUpperCase();
1432
1567
  case '$f':
1433
- case '$F':
1434
1568
  return this.#fullName.firstName.value[0];
1569
+ case '$F':
1570
+ return this.#fullName.firstName.initials(true).join('');
1435
1571
  case '$l':
1436
- case '$L':
1437
1572
  return this.#fullName.lastName.value[0];
1573
+ case '$L':
1574
+ return this.#fullName.lastName.initials().join('');
1438
1575
  case '$m':
1439
- case '$M':
1440
1576
  return this.hasMiddle ? this.middle[0] : undefined;
1577
+ case '$M':
1578
+ return this.hasMiddle ? this.#fullName.middleName.map((n) => n.value[0]).join('') : undefined;
1441
1579
  default:
1442
- return undefined;
1580
+ return ALLOWED_FORMAT_TOKENS.includes(char) ? char : undefined;
1443
1581
  }
1444
1582
  }
1445
1583
  }
@@ -1514,21 +1652,59 @@
1514
1652
  static use({ names, prebuild, postbuild, preclear, postclear, }) {
1515
1653
  return new NameBuilder(names ?? [], prebuild, postbuild, preclear, postclear);
1516
1654
  }
1517
- build(config) {
1655
+ build(options) {
1518
1656
  this.prebuild?.();
1519
1657
  const names = [...this.queue];
1520
- ArrayNameValidator.create().validate(names);
1658
+ const config = Config.merge(options);
1659
+ if (!config.mono)
1660
+ ArrayNameValidator.create().validate(names);
1521
1661
  this.instance = new Namefully(names, config);
1522
1662
  this.postbuild?.(this.instance);
1523
1663
  return this.instance;
1524
1664
  }
1525
1665
  }
1526
1666
 
1667
+ function deserialize(data) {
1668
+ try {
1669
+ const parsed = typeof data === 'string' ? JSON.parse(data) : data;
1670
+ if (!parsed || typeof parsed !== 'object') {
1671
+ throw new InputError({
1672
+ source: String(data),
1673
+ message: 'invalid serialized data; must be an object or a string',
1674
+ });
1675
+ }
1676
+ const { names, config } = parsed;
1677
+ const { firstName: fn, lastName: ln, middleName: mn, prefix: px, suffix: sx } = names;
1678
+ const builder = NameBuilder.of();
1679
+ if (px)
1680
+ builder.add(Name.prefix(px));
1681
+ if (sx)
1682
+ builder.add(Name.suffix(sx));
1683
+ if (mn)
1684
+ builder.add(...(typeof mn === 'string' ? [Name.middle(mn)] : mn.map((n) => Name.middle(n))));
1685
+ builder.add(typeof fn === 'string' ? Name.first(fn) : new FirstName(fn.value, ...(fn.more ?? [])));
1686
+ builder.add(typeof ln === 'string' ? Name.last(ln) : new LastName(ln.father, ln.mother));
1687
+ const separator = Separator.cast(config.separator);
1688
+ const mono = typeof config.mono === 'string' ? (Namon.cast(config.mono) ?? false) : config.mono;
1689
+ return builder.build({ ...config, separator, mono });
1690
+ }
1691
+ catch (error) {
1692
+ if (error instanceof NameError)
1693
+ throw error;
1694
+ throw new UnknownError({
1695
+ source: String(data),
1696
+ message: 'could not deserialize data',
1697
+ origin: error instanceof Error ? error : new Error(String(error)),
1698
+ });
1699
+ }
1700
+ }
1701
+
1527
1702
  exports.Config = Config;
1528
1703
  exports.FirstName = FirstName;
1529
1704
  exports.FullName = FullName;
1530
1705
  exports.InputError = InputError;
1531
1706
  exports.LastName = LastName;
1707
+ exports.Mononym = Mononym;
1532
1708
  exports.Name = Name;
1533
1709
  exports.NameBuilder = NameBuilder;
1534
1710
  exports.NameError = NameError;
@@ -1541,6 +1717,7 @@
1541
1717
  exports.UnknownError = UnknownError;
1542
1718
  exports.ValidationError = ValidationError;
1543
1719
  exports.default = namefully;
1720
+ exports.deserialize = deserialize;
1544
1721
  exports.isNameArray = isNameArray;
1545
1722
  exports.version = VERSION;
1546
1723