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.
@@ -1,6 +1,6 @@
1
1
  import { Name } from './name.js';
2
2
  import { Config } from './config.js';
3
- import { Namefully } from './namefully.js';
3
+ import { Namefully, NameOptions } from './namefully.js';
4
4
  type VoidCallback = () => void;
5
5
  type Callback<Type, Return> = (value: Type) => Return;
6
6
  /**
@@ -70,6 +70,6 @@ export declare class NameBuilder extends Builder<Name, Namefully> {
70
70
  * Regardless of how the names are added, both first and last names must exist
71
71
  * to complete a fine build. Otherwise, it throws a NameError.
72
72
  */
73
- build(config?: Partial<Config>): Namefully;
73
+ build(options?: NameOptions): Namefully;
74
74
  }
75
75
  export {};
@@ -1,5 +1,6 @@
1
- import { Namefully } from './namefully.js';
1
+ import { Config } from './config.js';
2
2
  import { ArrayNameValidator } from './validator.js';
3
+ import { Namefully } from './namefully.js';
3
4
  class Builder {
4
5
  prebuild;
5
6
  postbuild;
@@ -67,10 +68,12 @@ export class NameBuilder extends Builder {
67
68
  static use({ names, prebuild, postbuild, preclear, postclear, }) {
68
69
  return new NameBuilder(names ?? [], prebuild, postbuild, preclear, postclear);
69
70
  }
70
- build(config) {
71
+ build(options) {
71
72
  this.prebuild?.();
72
73
  const names = [...this.queue];
73
- ArrayNameValidator.create().validate(names);
74
+ const config = Config.merge(options);
75
+ if (!config.mono)
76
+ ArrayNameValidator.create().validate(names);
74
77
  this.instance = new Namefully(names, config);
75
78
  this.postbuild?.(this.instance);
76
79
  return this.instance;
@@ -1,4 +1,4 @@
1
- import { NameOrder, Separator, Title, Surname } from './types.js';
1
+ import { NameOrder, Namon, Separator, Title, Surname } from './types.js';
2
2
  /**
3
3
  * The Configuration to use across the other components.
4
4
  *
@@ -61,6 +61,8 @@ export declare class Config {
61
61
  * prioritized and viewed as the source of truth for future considerations.
62
62
  */
63
63
  get surname(): Surname;
64
+ /** Whether to parse a single word name as a mononym. */
65
+ get mono(): boolean | Namon;
64
66
  /** The name of the cached configuration. */
65
67
  get name(): string;
66
68
  private constructor();
@@ -89,14 +91,6 @@ export declare class Config {
89
91
  clone(): Config;
90
92
  /** Resets the configuration by setting it back to its default values. */
91
93
  reset(): void;
92
- /**
93
- * Alters the name order between the first and last name, and rearrange the
94
- * order of appearance of a name set.
95
- * @deprecated use `update()` method instead.
96
- */
97
- updateOrder(orderedBy: NameOrder): void;
98
- /**
99
- * Allows the possibility to alter some options after creating a name set.
100
- */
94
+ /** Allows the possibility to alter behavior-related options after creating a name set. */
101
95
  update({ orderedBy, title, ending }: Partial<Pick<Config, 'orderedBy' | 'title' | 'ending'>>): void;
102
96
  }
@@ -10,6 +10,7 @@ export class Config {
10
10
  #ending;
11
11
  #bypass;
12
12
  #surname;
13
+ #mono;
13
14
  static cache = new Map();
14
15
  get orderedBy() {
15
16
  return this.#orderedBy;
@@ -29,10 +30,13 @@ export class Config {
29
30
  get surname() {
30
31
  return this.#surname;
31
32
  }
33
+ get mono() {
34
+ return this.#mono;
35
+ }
32
36
  get name() {
33
37
  return this.#name;
34
38
  }
35
- constructor(name, orderedBy = NameOrder.FIRST_NAME, separator = Separator.SPACE, title = Title.UK, ending = false, bypass = true, surname = Surname.FATHER) {
39
+ constructor(name, orderedBy = NameOrder.FIRST_NAME, separator = Separator.SPACE, title = Title.UK, ending = false, bypass = true, surname = Surname.FATHER, mono = false) {
36
40
  this.#name = name;
37
41
  this.#orderedBy = orderedBy;
38
42
  this.#separator = separator;
@@ -40,6 +44,7 @@ export class Config {
40
44
  this.#ending = ending;
41
45
  this.#bypass = bypass;
42
46
  this.#surname = surname;
47
+ this.#mono = mono;
43
48
  }
44
49
  static create(name = defaultName) {
45
50
  if (!_a.cache.has(name))
@@ -58,11 +63,12 @@ export class Config {
58
63
  config.#ending = other.ending ?? config.ending;
59
64
  config.#bypass = other.bypass ?? config.bypass;
60
65
  config.#surname = other.surname ?? config.surname;
66
+ config.#mono = other.mono ?? config.mono;
61
67
  return config;
62
68
  }
63
69
  }
64
70
  copyWith(options = {}) {
65
- const { name, orderedBy, separator, title, ending, bypass, surname } = options;
71
+ const { name, orderedBy, separator, title, ending, bypass, surname, mono } = options;
66
72
  const config = _a.create(this.#genNewName(name ?? this.name + copyAlias));
67
73
  config.#orderedBy = orderedBy ?? this.orderedBy;
68
74
  config.#separator = separator ?? this.separator;
@@ -70,6 +76,7 @@ export class Config {
70
76
  config.#ending = ending ?? this.ending;
71
77
  config.#bypass = bypass ?? this.bypass;
72
78
  config.#surname = surname ?? this.surname;
79
+ config.#mono = mono ?? this.mono;
73
80
  return config;
74
81
  }
75
82
  clone() {
@@ -82,11 +89,9 @@ export class Config {
82
89
  this.#ending = false;
83
90
  this.#bypass = true;
84
91
  this.#surname = Surname.FATHER;
92
+ this.#mono = false;
85
93
  _a.cache.set(this.name, this);
86
94
  }
87
- updateOrder(orderedBy) {
88
- this.update({ orderedBy });
89
- }
90
95
  update({ orderedBy, title, ending }) {
91
96
  const config = _a.cache.get(this.name);
92
97
  if (!config)
@@ -1,4 +1,5 @@
1
- export declare const VERSION = "2.0.2";
1
+ export declare const VERSION = "2.2.0";
2
2
  export declare const MIN_NUMBER_OF_NAME_PARTS = 2;
3
3
  export declare const MAX_NUMBER_OF_NAME_PARTS = 5;
4
- export declare const ALLOWED_FORMAT_TOKENS: string[];
4
+ export declare const ALLOWED_FORMAT_TOKENS = " .,_-()[]<>'\"bBfFlLmMnNoOpPsS$";
5
+ export declare const ZERO_WIDTH_SPACE: string;
@@ -1,27 +1,5 @@
1
- export const VERSION = '2.0.2';
1
+ export const VERSION = '2.2.0';
2
2
  export const MIN_NUMBER_OF_NAME_PARTS = 2;
3
3
  export const MAX_NUMBER_OF_NAME_PARTS = 5;
4
- export const ALLOWED_FORMAT_TOKENS = [
5
- '.',
6
- ',',
7
- ' ',
8
- '-',
9
- '_',
10
- 'b',
11
- 'B',
12
- 'f',
13
- 'F',
14
- 'l',
15
- 'L',
16
- 'm',
17
- 'M',
18
- 'n',
19
- 'N',
20
- 'o',
21
- 'O',
22
- 'p',
23
- 'P',
24
- 's',
25
- 'S',
26
- '$',
27
- ];
4
+ export const ALLOWED_FORMAT_TOKENS = ` .,_-()[]<>'"bBfFlLmMnNoOpPsS$`;
5
+ export const ZERO_WIDTH_SPACE = String.fromCharCode(8203);
@@ -0,0 +1,31 @@
1
+ import { JsonName } from './name.js';
2
+ import { type Namefully } from './namefully.js';
3
+ /** Serialized representation of a Namefully instance. */
4
+ export interface SerializedName {
5
+ /** The name data (with its hierarchy intact). */
6
+ names: JsonName;
7
+ /** The configuration data. */
8
+ config: {
9
+ name: string;
10
+ orderedBy: string;
11
+ separator: string;
12
+ title: string;
13
+ ending: boolean;
14
+ bypass: boolean;
15
+ surname: string;
16
+ mono: boolean | string;
17
+ };
18
+ }
19
+ /**
20
+ * Deserializes a JSON object into a Namefully instance.
21
+ *
22
+ * This is the inverse operation of `serialize()`, reconstructing a Namefully
23
+ * instance from a previously serialized JSON object, preserving the name hierarchy.
24
+ *
25
+ * @param {SerializedName | string} data the serialized Namefully data (from `serialize()`
26
+ * or compatible format).
27
+ * @returns a new Namefully instance.
28
+ *
29
+ * @throws {NameError} if the data cannot be parsed or is invalid.
30
+ */
31
+ export declare function deserialize(data: SerializedName | string): Namefully;
@@ -0,0 +1,38 @@
1
+ import { NameBuilder } from './builder.js';
2
+ import { Namon, Separator } from './types.js';
3
+ import { Name, FirstName, LastName } from './name.js';
4
+ import { InputError, NameError, UnknownError } from './error.js';
5
+ export function deserialize(data) {
6
+ try {
7
+ const parsed = typeof data === 'string' ? JSON.parse(data) : data;
8
+ if (!parsed || typeof parsed !== 'object') {
9
+ throw new InputError({
10
+ source: String(data),
11
+ message: 'invalid serialized data; must be an object or a string',
12
+ });
13
+ }
14
+ const { names, config } = parsed;
15
+ const { firstName: fn, lastName: ln, middleName: mn, prefix: px, suffix: sx } = names;
16
+ const builder = NameBuilder.of();
17
+ if (px)
18
+ builder.add(Name.prefix(px));
19
+ if (sx)
20
+ builder.add(Name.suffix(sx));
21
+ if (mn)
22
+ builder.add(...(typeof mn === 'string' ? [Name.middle(mn)] : mn.map((n) => Name.middle(n))));
23
+ builder.add(typeof fn === 'string' ? Name.first(fn) : new FirstName(fn.value, ...(fn.more ?? [])));
24
+ builder.add(typeof ln === 'string' ? Name.last(ln) : new LastName(ln.father, ln.mother));
25
+ const separator = Separator.cast(config.separator);
26
+ const mono = typeof config.mono === 'string' ? (Namon.cast(config.mono) ?? false) : config.mono;
27
+ return builder.build({ ...config, separator, mono });
28
+ }
29
+ catch (error) {
30
+ if (error instanceof NameError)
31
+ throw error;
32
+ throw new UnknownError({
33
+ source: String(data),
34
+ message: 'could not deserialize data',
35
+ origin: error instanceof Error ? error : new Error(String(error)),
36
+ });
37
+ }
38
+ }
@@ -36,6 +36,8 @@ export declare class FullName {
36
36
  get middleName(): Name[];
37
37
  /** The suffix part of the full name. */
38
38
  get suffix(): Nullable<Name>;
39
+ /** Whether the full name is a single word name. */
40
+ get isMono(): boolean;
39
41
  /**
40
42
  * Parses a JSON name into a full name.
41
43
  * @param {JsonName} json parsable name element
@@ -48,5 +50,40 @@ export declare class FullName {
48
50
  setMiddleName(names: string[] | Name[]): FullName;
49
51
  setSuffix(name: Nullable<string | Name>): FullName;
50
52
  /** Returns true if a namon has been set. */
51
- has(namon: Namon): boolean;
53
+ has(key: Namon | string): boolean;
54
+ toString(): string;
55
+ /** Returns an `Iterable` of existing `Name`s. */
56
+ toIterable(flat?: boolean): Iterable<Name>;
57
+ /** Returns the default iterator for this name set (enabling for-of statements). */
58
+ [Symbol.iterator](): Iterator<Name>;
59
+ }
60
+ /**
61
+ * A single word name or mononym.
62
+ *
63
+ * This is a special case of `FullName` that is used to represent mononyms. This contradicts
64
+ * the original purpose of this library such as shaping and organizing name pieces accordingly.
65
+ *
66
+ * When enabled via `Config.mono`, this becomes the full name of a human. And as a single name,
67
+ * most of the `Namefully` methods become irrelevant.
68
+ */
69
+ export declare class Mononym extends FullName {
70
+ #private;
71
+ /**
72
+ * Constructs a mononym from a piece of string.
73
+ * @param {string | Name} name to be used to construct the mononym.
74
+ */
75
+ constructor(name: string | Name, options?: Partial<Config>);
76
+ /**
77
+ * Re-assigns which name type is being used to represent the mononym.
78
+ *
79
+ * Ideally, this doesn't really matter as the mononym is always a single piece of name.
80
+ * When used as `string`, it must be a valid `Namon` type or else it will default to
81
+ * `Namon.FIRST_NAME`.
82
+ * @param {string | Namon} type of name to use.
83
+ */
84
+ set type(type: string | Namon);
85
+ /** The type of name being used to represent the mononym. */
86
+ get type(): Namon;
87
+ /** The piece of string treated as a name. */
88
+ get value(): string;
52
89
  }
@@ -1,5 +1,6 @@
1
1
  import { Config } from './config.js';
2
2
  import { Validators } from './validator.js';
3
+ import { ZERO_WIDTH_SPACE } from './constants.js';
3
4
  import { Namon, Title } from './types.js';
4
5
  import { NameError, UnknownError } from './error.js';
5
6
  import { FirstName, LastName, Name } from './name.js';
@@ -31,14 +32,18 @@ export class FullName {
31
32
  get suffix() {
32
33
  return this.#suffix;
33
34
  }
35
+ get isMono() {
36
+ return this instanceof Mononym;
37
+ }
34
38
  static parse(json, config) {
35
39
  try {
40
+ const { prefix, firstName: fn, middleName: mn, lastName: ln, suffix } = json;
36
41
  return new FullName(config)
37
- .setPrefix(json.prefix)
38
- .setFirstName(json.firstName)
39
- .setMiddleName(json.middleName ?? [])
40
- .setLastName(json.lastName)
41
- .setSuffix(json.suffix);
42
+ .setPrefix(prefix)
43
+ .setFirstName(typeof fn === 'string' ? fn : new FirstName(fn.value, ...(fn.more ?? [])))
44
+ .setMiddleName(typeof mn === 'string' ? [mn] : (mn ?? []))
45
+ .setLastName(typeof ln === 'string' ? ln : new LastName(ln.father, ln.mother))
46
+ .setSuffix(suffix);
42
47
  }
43
48
  catch (error) {
44
49
  if (error instanceof NameError)
@@ -56,7 +61,7 @@ export class FullName {
56
61
  if (!this.#config.bypass)
57
62
  Validators.prefix.validate(name);
58
63
  const prefix = name instanceof Name ? name.value : name;
59
- this.#prefix = Name.prefix(this.#config.title === Title.US ? `${prefix}.` : prefix);
64
+ this.#prefix = Name.prefix(this.#config.title === Title.US && !prefix.endsWith('.') ? `${prefix}.` : prefix);
60
65
  return this;
61
66
  }
62
67
  setFirstName(name) {
@@ -87,11 +92,72 @@ export class FullName {
87
92
  this.#suffix = Name.suffix(name instanceof Name ? name.value : name);
88
93
  return this;
89
94
  }
90
- has(namon) {
95
+ has(key) {
96
+ const namon = typeof key === 'string' ? Namon.cast(key) : key;
97
+ if (!namon)
98
+ return false;
91
99
  if (namon.equal(Namon.PREFIX))
92
100
  return !!this.#prefix;
93
101
  if (namon.equal(Namon.SUFFIX))
94
102
  return !!this.#suffix;
95
103
  return namon.equal(Namon.MIDDLE_NAME) ? this.#middleName.length > 0 : true;
96
104
  }
105
+ toString() {
106
+ if (this.isMono)
107
+ return this.value;
108
+ return Array.from(this.toIterable(true)).join(' ');
109
+ }
110
+ *toIterable(flat = false) {
111
+ if (this.#prefix)
112
+ yield this.#prefix;
113
+ if (flat) {
114
+ yield* this.#firstName.asNames;
115
+ yield* this.#middleName;
116
+ yield* this.#lastName.asNames;
117
+ }
118
+ else {
119
+ yield this.#firstName;
120
+ yield* this.#middleName;
121
+ yield this.#lastName;
122
+ }
123
+ if (this.#suffix)
124
+ yield this.#suffix;
125
+ }
126
+ *[Symbol.iterator]() {
127
+ yield* this.toIterable(true);
128
+ }
129
+ }
130
+ export class Mononym extends FullName {
131
+ #namon;
132
+ #type;
133
+ constructor(name, options) {
134
+ super(options ?? { name: 'mononym', mono: true });
135
+ this.#namon = name.toString();
136
+ this.type = name instanceof Name ? name.type : Namon.FIRST_NAME;
137
+ }
138
+ set type(type) {
139
+ this.#type = typeof type === 'string' ? (Namon.cast(type) ?? Namon.FIRST_NAME) : type;
140
+ this.#build(this.#namon);
141
+ }
142
+ get type() {
143
+ return this.#type;
144
+ }
145
+ get value() {
146
+ return this.#namon;
147
+ }
148
+ #build(name) {
149
+ this.setFirstName(ZERO_WIDTH_SPACE).setLastName(ZERO_WIDTH_SPACE).setMiddleName([]).setPrefix(null).setSuffix(null);
150
+ if (this.#type.equal(Namon.FIRST_NAME))
151
+ this.setFirstName(name);
152
+ else if (this.#type.equal(Namon.LAST_NAME))
153
+ this.setLastName(name);
154
+ else if (this.#type.equal(Namon.MIDDLE_NAME))
155
+ this.setMiddleName([name]);
156
+ else if (this.#type.equal(Namon.PREFIX))
157
+ this.setPrefix(name);
158
+ else if (this.#type.equal(Namon.SUFFIX))
159
+ this.setSuffix(name);
160
+ else
161
+ throw new NameError(name, 'invalid mononym type');
162
+ }
97
163
  }
@@ -15,6 +15,7 @@ import namefully from './namefully.js';
15
15
  export * from './builder.js';
16
16
  export * from './config.js';
17
17
  export { VERSION as version } from './constants.js';
18
+ export * from './data.js';
18
19
  export * from './error.js';
19
20
  export * from './fullname.js';
20
21
  export * from './name.js';
package/dist/esm/index.js CHANGED
@@ -2,6 +2,7 @@ import namefully from './namefully.js';
2
2
  export * from './builder.js';
3
3
  export * from './config.js';
4
4
  export { VERSION as version } from './constants.js';
5
+ export * from './data.js';
5
6
  export * from './error.js';
6
7
  export * from './fullname.js';
7
8
  export * from './name.js';
@@ -80,14 +80,14 @@ export declare class FirstName extends Name {
80
80
  /** Representation of a last name with some extra functionality. */
81
81
  export declare class LastName extends Name {
82
82
  #private;
83
- readonly format: Surname;
83
+ readonly format: Surname | 'father' | 'mother' | 'hyphenated' | 'all';
84
84
  /**
85
85
  * Creates an extended version of `Name` and flags it as a last name `type`.
86
86
  *
87
87
  * Some people may keep their @param mother's surname and want to keep a clear cut
88
88
  * from their @param father's surname. However, there are no clear rules about it.
89
89
  */
90
- constructor(father: string, mother?: string, format?: Surname);
90
+ constructor(father: string, mother?: string, format?: Surname | 'father' | 'mother' | 'hyphenated' | 'all');
91
91
  /** The surname inherited from the father side. */
92
92
  get father(): string;
93
93
  /** The surname inherited from the mother side. */
@@ -97,8 +97,8 @@ export declare class LastName extends Name {
97
97
  get length(): number;
98
98
  /** Returns a combined version of the `father` and `mother` if any. */
99
99
  get asNames(): Name[];
100
- toString(format?: Surname): string;
101
- initials(format?: Surname): string[];
100
+ toString(format?: Surname | 'father' | 'mother' | 'hyphenated' | 'all'): string;
101
+ initials(format?: Surname | 'father' | 'mother' | 'hyphenated' | 'all'): string[];
102
102
  caps(range?: CapsRange): LastName;
103
103
  decaps(range?: CapsRange): LastName;
104
104
  /** Makes a copy of the current name. */
@@ -112,8 +112,14 @@ export declare function isNameArray(value?: unknown): value is Name[];
112
112
  /** JSON signature for `FullName` data. */
113
113
  export interface JsonName {
114
114
  prefix?: string;
115
- firstName: string;
116
- middleName?: string[];
117
- lastName: string;
115
+ firstName: string | {
116
+ value: string;
117
+ more?: string[];
118
+ };
119
+ middleName?: string | string[];
120
+ lastName: string | {
121
+ father: string;
122
+ mother?: string;
123
+ };
118
124
  suffix?: string;
119
125
  }
package/dist/esm/name.js CHANGED
@@ -162,7 +162,7 @@ export class LastName extends Name {
162
162
  return this.mother ?? '';
163
163
  case Surname.HYPHENATED:
164
164
  return this.hasMother ? `${this.value}-${this.#mother}` : this.value;
165
- case Surname.ALL:
165
+ default:
166
166
  return this.hasMother ? `${this.value} ${this.#mother}` : this.value;
167
167
  }
168
168
  }