@finbheara/names 0.12.0 → 0.14.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/README.md +33 -0
- package/dist/chunks/{index-ztpxgcs1.js → index-a15g2gp6.js} +2 -2
- package/dist/chunks/{index-yf904dam.js → index-ka8nzreg.js} +6 -2
- package/dist/chunks/{index-yf904dam.js.map → index-ka8nzreg.js.map} +3 -3
- package/dist/chunks/{index-vqbvesky.js → index-kw69ry3j.js} +533 -185
- package/dist/chunks/index-kw69ry3j.js.map +21 -0
- package/dist/cli/census.js +2 -2
- package/dist/index.js +19 -9
- package/dist/index.js.map +1 -1
- package/dist/measure/index.js +2 -2
- package/dist/normalize/index.js +18 -8
- package/dist/normalize/index.js.map +1 -1
- package/dist/types/normalize/given-diminutives.d.ts +35 -11
- package/dist/types/normalize/index.d.ts +3 -2
- package/dist/types/normalize/multiset.d.ts +9 -0
- package/dist/types/normalize/name-compare.d.ts +43 -0
- package/dist/types/normalize/name-kinship.d.ts +171 -0
- package/dist/types/normalize/name-middle.d.ts +11 -0
- package/docs/NAME-NORMALIZATION.md +90 -0
- package/package.json +1 -1
- package/dist/chunks/index-vqbvesky.js.map +0 -19
- /package/dist/chunks/{index-ztpxgcs1.js.map → index-a15g2gp6.js.map} +0 -0
package/dist/normalize/index.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
repairMojibake2,
|
|
3
3
|
sameGivenFamily2,
|
|
4
|
+
givenFamilyTable2,
|
|
4
5
|
comparePosition2,
|
|
5
6
|
nameRelation2,
|
|
6
|
-
nameCompare2,
|
|
7
|
-
probablySame2,
|
|
8
|
-
createNameSet2,
|
|
9
|
-
createNameMap2,
|
|
10
|
-
createNameClusters2,
|
|
11
7
|
MAX_READINGS2,
|
|
12
8
|
surnameParts2,
|
|
13
9
|
printAttestsOrder2,
|
|
@@ -24,6 +20,15 @@ import {
|
|
|
24
20
|
blockKeysOfReadings2,
|
|
25
21
|
nameBlockKeys2,
|
|
26
22
|
namesFeasible2,
|
|
23
|
+
KINSHIP_VIAS2,
|
|
24
|
+
createNameKinship2,
|
|
25
|
+
nameKinship2,
|
|
26
|
+
nameCompare2,
|
|
27
|
+
probablySame2,
|
|
28
|
+
createNameSet2,
|
|
29
|
+
createNameMap2,
|
|
30
|
+
createNameIndex2,
|
|
31
|
+
createNameClusters2,
|
|
27
32
|
INFEASIBLE_CLASSES2,
|
|
28
33
|
editDistance2,
|
|
29
34
|
nearSpelling2,
|
|
@@ -44,7 +49,7 @@ import {
|
|
|
44
49
|
readNameCell2,
|
|
45
50
|
DEFAULT_NAME_FORMAT_THRESHOLD2,
|
|
46
51
|
deriveNameFormat2
|
|
47
|
-
} from "../chunks/index-
|
|
52
|
+
} from "../chunks/index-kw69ry3j.js";
|
|
48
53
|
import"../chunks/index-kw6gbrnq.js";
|
|
49
54
|
import {
|
|
50
55
|
NAME_REFUSED2,
|
|
@@ -90,7 +95,7 @@ import {
|
|
|
90
95
|
defaultPersonNameFactory2,
|
|
91
96
|
personName2,
|
|
92
97
|
personNameFromIcao2
|
|
93
|
-
} from "../chunks/index-
|
|
98
|
+
} from "../chunks/index-ka8nzreg.js";
|
|
94
99
|
import {
|
|
95
100
|
TEAM_WORD2,
|
|
96
101
|
TEAM_DESIGNATION_WORDS2,
|
|
@@ -110,6 +115,7 @@ export {
|
|
|
110
115
|
DEFAULT_PERSON_NAME_CAPACITY2 as DEFAULT_PERSON_NAME_CAPACITY,
|
|
111
116
|
GIVEN_SEPARATOR2 as GIVEN_SEPARATOR,
|
|
112
117
|
INFEASIBLE_CLASSES2 as INFEASIBLE_CLASSES,
|
|
118
|
+
KINSHIP_VIAS2 as KINSHIP_VIAS,
|
|
113
119
|
MAX_READINGS2 as MAX_READINGS,
|
|
114
120
|
NAME_COLUMNS2 as NAME_COLUMNS,
|
|
115
121
|
NAME_FORMATS2 as NAME_FORMATS,
|
|
@@ -135,6 +141,8 @@ export {
|
|
|
135
141
|
compoundGiven2 as compoundGiven,
|
|
136
142
|
createCachingPersonNameFactory2 as createCachingPersonNameFactory,
|
|
137
143
|
createNameClusters2 as createNameClusters,
|
|
144
|
+
createNameIndex2 as createNameIndex,
|
|
145
|
+
createNameKinship2 as createNameKinship,
|
|
138
146
|
createNameMap2 as createNameMap,
|
|
139
147
|
createNameSet2 as createNameSet,
|
|
140
148
|
createPersonNameFactory2 as createPersonNameFactory,
|
|
@@ -144,6 +152,7 @@ export {
|
|
|
144
152
|
foldNameKey2 as foldNameKey,
|
|
145
153
|
formatPersonDisplay2 as formatPersonDisplay,
|
|
146
154
|
givenAbbreviates2 as givenAbbreviates,
|
|
155
|
+
givenFamilyTable2 as givenFamilyTable,
|
|
147
156
|
givenKeyAbbreviates2 as givenKeyAbbreviates,
|
|
148
157
|
infeasibleClass2 as infeasibleClass,
|
|
149
158
|
inferNameOrder2 as inferNameOrder,
|
|
@@ -167,6 +176,7 @@ export {
|
|
|
167
176
|
nameCompare2 as nameCompare,
|
|
168
177
|
nameFold2 as nameFold,
|
|
169
178
|
nameFormatOrder2 as nameFormatOrder,
|
|
179
|
+
nameKinship2 as nameKinship,
|
|
170
180
|
nameMeet2 as nameMeet,
|
|
171
181
|
nameReadings2 as nameReadings,
|
|
172
182
|
nameRelation2 as nameRelation,
|
|
@@ -204,5 +214,5 @@ export {
|
|
|
204
214
|
traditionLookupOf2 as traditionLookupOf
|
|
205
215
|
};
|
|
206
216
|
|
|
207
|
-
//# debugId=
|
|
217
|
+
//# debugId=6CAF39AD5BAB8B2064756E2164756E21
|
|
208
218
|
//# sourceMappingURL=index.js.map
|
|
@@ -11,21 +11,45 @@
|
|
|
11
11
|
* A RELATION OF A PAIR AND NEVER A KEY. Nothing here rewrites a given name: a stored key,
|
|
12
12
|
* a filing key and every rendering keep the print's own spelling. A caller asks whether
|
|
13
13
|
* TWO given names are one family, at the moment it compares them, and nowhere else, so the
|
|
14
|
-
* root never leaves this module: `sameGivenFamily`
|
|
14
|
+
* root never leaves this module: `sameGivenFamily` and `givenFamilyTable` answer about a
|
|
15
|
+
* pair and neither returns a root, for that reason.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
17
|
+
* SPELLINGS, FAMILIES AND THE GATE. Two different given names are related in one of three
|
|
18
|
+
* ways, and the answer says which:
|
|
19
|
+
*
|
|
20
|
+
* - `given-variant`: two spellings of one name (`Connor` / `Conor`, `Sara` / `Sarah`). A
|
|
21
|
+
* spelling is not a familiar form, so `variants` is its own table, and a gate admits it.
|
|
22
|
+
* - `given-family`: after each spelling is read as its canonical, the two share a ROOT of
|
|
23
|
+
* `families` (`Tom` / `Thomas`, `Kate` / `Katherine`), and a gate admits it.
|
|
24
|
+
* - `given-family-not-gate`: they share a root, but only through a `notAtGate` form: a name in
|
|
25
|
+
* its own right (`Megan`, `Grace`), a translation (`Sean`, `Padraig`), another gender's
|
|
26
|
+
* name (`Michelle`, `Sam`) or a form of more than one root (`Ellie`, `Evie`). The lane
|
|
27
|
+
* reads it as evidence below an exact match; a gate refuses it.
|
|
28
|
+
*
|
|
29
|
+
* EVERY ROOT, NEVER THE FIRST. A form under several roots meets each of them (`Evie` is
|
|
30
|
+
* Evelyn's, Genevieve's, Eve's and Eva's), and every such form is `notAtGate`, because the
|
|
31
|
+
* form alone cannot say which root its bearer carries. Sharing a root is not transitive:
|
|
32
|
+
* `Eve` and `Eva` share none, though `Evie` meets both. A `notAtGate` form is refused AS A
|
|
33
|
+
* FORM: where it is the root itself (`Caitlin` / `Cait`), its own forms meet it at a gate.
|
|
23
34
|
*/
|
|
24
35
|
/**
|
|
25
36
|
* Do two DIFFERENT given names (NN3, letters only) stand for one another at a gate?
|
|
26
37
|
*
|
|
27
|
-
* True exactly when
|
|
28
|
-
* form. Two equal names answer `false`:
|
|
29
|
-
* tolerance past it, so a caller can tell
|
|
38
|
+
* True exactly when `givenFamilyTable` relates them by a gate-safe answer: two spellings of
|
|
39
|
+
* one name, or one family through no `notAtGate` form. Two equal names answer `false`:
|
|
40
|
+
* equality is the caller's own test and this is the tolerance past it, so a caller can tell
|
|
41
|
+
* which one admitted a pair.
|
|
30
42
|
*/
|
|
31
43
|
export declare function sameGivenFamily(a: string, b: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* What the table says about two DIFFERENT given names (NN3, letters only), as a kinship
|
|
46
|
+
* `via`: `given-variant` for two spellings of one name, `given-family` where the two share a
|
|
47
|
+
* root and a gate admits the pair, `given-family-not-gate` where every root they share is
|
|
48
|
+
* reached through a `notAtGate` form, and `null` where the table does not relate them. Two
|
|
49
|
+
* equal names answer `null`, for `sameGivenFamily`'s reason.
|
|
50
|
+
*
|
|
51
|
+
* THE DEFAULT GIVEN-NAME PROVIDER of `createNameKinship` (`./name-kinship.ts`).
|
|
52
|
+
* `sameGivenFamily` is defined as its gate-safe answers, so the gate and the tolerant relation
|
|
53
|
+
* cannot disagree about a pair.
|
|
54
|
+
*/
|
|
55
|
+
export declare function givenFamilyTable(a: string, b: string): "given-variant" | "given-family" | "given-family-not-gate" | null;
|
|
@@ -18,9 +18,10 @@ export { type CommaReading, type NameOrder, type NameSplit, type ResolvedNameOrd
|
|
|
18
18
|
export { TEAM_DESIGNATION_WORDS, TEAM_WORD, type TeamSuffixPartition, canonicalTeamName, isCanonicalTeamName, isTeamPrint, partitionTeamSuffix, teamLetter, teamRowDesignation, } from "./team-name.ts";
|
|
19
19
|
export { foldNameKey, nameFold } from "./name-fold.ts";
|
|
20
20
|
export { COMPOUND_SURNAMES, isCompoundSurname } from "./compound-surnames.ts";
|
|
21
|
-
export { sameGivenFamily } from "./given-diminutives.ts";
|
|
22
|
-
export { type ClusterLinkage, type Clusters, type NameMap, type NameSet, createNameClusters, createNameMap, createNameSet, nameCompare, probablySame, } from "./name-compare.ts";
|
|
21
|
+
export { givenFamilyTable, sameGivenFamily } from "./given-diminutives.ts";
|
|
22
|
+
export { type ClusterLinkage, type Clusters, type NameIndex, type NameIndexOptions, type NameMap, type NameMatch, type NameSet, createNameClusters, createNameIndex, createNameMap, createNameSet, nameCompare, probablySame, } from "./name-compare.ts";
|
|
23
23
|
export { type NameReading, type NameRelation, type NameRelationReason, comparePosition, nameRelation, } from "./name-middle.ts";
|
|
24
|
+
export { type GivenProvider, type GivenVia, KINSHIP_VIAS, type KinshipReason, type KinshipVia, type MiddleVia, type MiddleViasAreNn5Reasons, type NameKinship, type NameKinshipOptions, type NameKinshipRelation, createNameKinship, nameKinship, } from "./name-kinship.ts";
|
|
24
25
|
export * from "./name-compose.ts";
|
|
25
26
|
export { INFEASIBLE_CLASSES, type InfeasibleClass, compoundGiven, editDistance, infeasibleClass, middleForm, nearSpelling, } from "./name-infeasible.ts";
|
|
26
27
|
export { type ListedPerson, isListJoiner, personList } from "./person-list.ts";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* multiset.ts — the one sub-multiset test over surname parts.
|
|
3
|
+
*
|
|
4
|
+
* MOVED HERE FROM `./name-compose.ts` so that `./name-kinship.ts` reads the same test the
|
|
5
|
+
* meet of two surnames reads, without either file importing the other and without the test
|
|
6
|
+
* joining the package's surface (`index.ts` re-exports `name-compose.ts` whole).
|
|
7
|
+
*/
|
|
8
|
+
/** Is `outer` a sub-multiset of `inner`? `[Walter] ⊒ [Walter, Oreskarard]`. */
|
|
9
|
+
export declare function subMultiset(outer: readonly string[], inner: readonly string[]): boolean;
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
* argument at the call site IS the justification, inline and readable, which is
|
|
27
27
|
* why no catalog of imports is owed.
|
|
28
28
|
*/
|
|
29
|
+
import { type NameKinship, type NameKinshipRelation } from "./name-kinship.ts";
|
|
29
30
|
import { type Level, type PersonName, type TransitiveLevel } from "./person-name.ts";
|
|
30
31
|
/**
|
|
31
32
|
* Order two parsed names at one level.
|
|
@@ -124,6 +125,48 @@ export declare function createNameSet(dop: TransitiveLevel): NameSet;
|
|
|
124
125
|
* `createNameMap("nn5")` DOES NOT COMPILE — see `createNameSet`.
|
|
125
126
|
*/
|
|
126
127
|
export declare function createNameMap<V>(dop: TransitiveLevel, entries?: Iterable<readonly [PersonName, V]>): NameMap<V>;
|
|
128
|
+
/** One held entry a tolerant lookup found, with what the relation said about it. */
|
|
129
|
+
export interface NameMatch<V> {
|
|
130
|
+
/** the print the entry is held under — the first print filed into its slot */
|
|
131
|
+
readonly name: PersonName;
|
|
132
|
+
readonly value: V;
|
|
133
|
+
readonly kinship: NameKinship;
|
|
134
|
+
}
|
|
135
|
+
/** A `NameMap` that can also answer which held names are RELATED to a print. */
|
|
136
|
+
export interface NameIndex<V> extends NameMap<V> {
|
|
137
|
+
/**
|
|
138
|
+
* Every held entry the index's relation does not call `different`, `same` before
|
|
139
|
+
* `candidate` and otherwise in insertion order. `[]` for a print that names no person.
|
|
140
|
+
*/
|
|
141
|
+
related(name: PersonName): readonly NameMatch<V>[];
|
|
142
|
+
}
|
|
143
|
+
export interface NameIndexOptions<V> {
|
|
144
|
+
/** The tolerant relation `related` filters by. Default `nameKinship`. */
|
|
145
|
+
readonly kinship?: NameKinshipRelation;
|
|
146
|
+
readonly entries?: Iterable<readonly [PersonName, V]>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* A map keyed on parsed names at one declared level, with a TOLERANT LOOKUP beside it.
|
|
150
|
+
*
|
|
151
|
+
* `get`, `has`, `set` and `delete` ARE `createNameMap`'s, unchanged and delegated to: the
|
|
152
|
+
* exact answer at a transitive level is still the slot. `related(name)` is the other
|
|
153
|
+
* question — which held names MAY be this person — and it is answered by a relation, not by
|
|
154
|
+
* a slot: candidates come from a secondary index on the surname key and every surname part
|
|
155
|
+
* (so `Mary Larson` finds `Mary Conley-Larson` and the other way round), and each candidate
|
|
156
|
+
* is kept when the relation does not call it `different`.
|
|
157
|
+
*
|
|
158
|
+
* A SIBLING OF `NameMap` AND NOT A METHOD ON IT, for three reasons. The tolerant answer is
|
|
159
|
+
* NOT TRANSITIVE, and `NameMap`'s contract is an equivalence class; putting a non-transitive
|
|
160
|
+
* lookup on it would blur exactly the boundary the type split above draws. The lookup needs
|
|
161
|
+
* a RELATION, and a relation is chosen by options (its given-name providers), which a map
|
|
162
|
+
* keyed by `dop` alone has no place to hold. And the secondary index costs memory on every
|
|
163
|
+
* `set`, which a caller that only wants the exact map should not pay. A `NameIndex` IS a
|
|
164
|
+
* `NameMap`, so a caller that needs both holds one structure.
|
|
165
|
+
*
|
|
166
|
+
* A print that names no person is held, counted and iterated as `createNameMap` holds it,
|
|
167
|
+
* and is never related to anything, in either direction: `probablySame`'s rule.
|
|
168
|
+
*/
|
|
169
|
+
export declare function createNameIndex<V>(dop: TransitiveLevel, options?: NameIndexOptions<V>): NameIndex<V>;
|
|
127
170
|
/** How a clustering decides whether a name joins an existing cluster. */
|
|
128
171
|
export type ClusterLinkage = "single" | "complete" | "average";
|
|
129
172
|
/** A clustering over parsed names. Order-dependent by construction at NN5. */
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* name-kinship.ts — THE TYPED, TOLERANT NAME RELATION.
|
|
3
|
+
*
|
|
4
|
+
* `docs/NAME-NORMALIZATION.md` § *Name kinship — the tolerant relation* is the specification.
|
|
5
|
+
*
|
|
6
|
+
* ## WHAT IT ADDS TO NN5
|
|
7
|
+
*
|
|
8
|
+
* `nameRelation` (`./name-middle.ts`) is the strict identity relation: the surname and the
|
|
9
|
+
* first given name must agree at NN3, and only a middle name is tolerated. A different head
|
|
10
|
+
* is rightly `different` there. The other tolerances already exist, one helper each —
|
|
11
|
+
* `givenFamilyTable` / `sameGivenFamily` for a given name, `surnameParts` for a compound
|
|
12
|
+
* surname — and a caller that wanted them had to compose them by hand. This is that
|
|
13
|
+
* composition, done once, and it REPORTS WHICH TOLERANCES A PAIR PASSED THROUGH so that each
|
|
14
|
+
* caller decides how much to accept:
|
|
15
|
+
*
|
|
16
|
+
* Kate Smith / Katherine Smith candidate via [given-family]
|
|
17
|
+
* Connor Smith / Conor Smith candidate via [given-variant]
|
|
18
|
+
* Mary Larson / Mary Conley-Larson candidate via [surname-part]
|
|
19
|
+
* Kate M Smith / Katherine Conley-Smith
|
|
20
|
+
* candidate via [surname-part, given-family, middle-absent]
|
|
21
|
+
*
|
|
22
|
+
* ## `via` IS A SET, NOT A KIND
|
|
23
|
+
*
|
|
24
|
+
* A pair can pass through several tolerances at once, one per field, and naming the
|
|
25
|
+
* combinations (`both`) grows as the product of the fields. So `via` lists every tolerance
|
|
26
|
+
* used, in the fixed order of `KINSHIP_VIAS`, and is empty for a pair NN3 already agreed on.
|
|
27
|
+
*
|
|
28
|
+
* ## `candidate` IS STILL NOT A MERGE
|
|
29
|
+
*
|
|
30
|
+
* The three values are `NameRelation`'s and mean what they mean there. Every tolerance here
|
|
31
|
+
* is "may be the same person, the rest of the evidence decides". `gateSafe` says something
|
|
32
|
+
* narrower: every tolerance used is one this package's own gate comparators already admit —
|
|
33
|
+
* a middle name `probablySame(…, "nn5")` admits, a given-name family `sameGivenFamily`
|
|
34
|
+
* admits, a compound the print itself carries. A table form that is a name in its own right
|
|
35
|
+
* (`given-family-not-gate`) or a probabilistic source (`given-maybe`) is a lead for the rest
|
|
36
|
+
* of the evidence and never a gate.
|
|
37
|
+
*
|
|
38
|
+
* ## THE GIVEN-NAME TOLERANCE IS INJECTED
|
|
39
|
+
*
|
|
40
|
+
* Which given names are one family is DATA, and the data will move: a probabilistic source
|
|
41
|
+
* of diminutive pairs is expected beside the curated table, and the table may shrink. So the
|
|
42
|
+
* relation asks an ordered list of `GivenProvider`s fixed by `createNameKinship`'s options,
|
|
43
|
+
* and the core knows no table. A new provider is an option; a new kind of answer is one
|
|
44
|
+
* line in `KINSHIP_VIAS`, which also carries its gate-safety, so no branch here changes.
|
|
45
|
+
*
|
|
46
|
+
* ## IT COMPOSES, IT DOES NOT FORK
|
|
47
|
+
*
|
|
48
|
+
* The middle-name and suffix rows are `tailRelation`, the half of `nameRelation` below the
|
|
49
|
+
* head, so `J` against `Jane` is decided in one place. With no tolerance used this answers
|
|
50
|
+
* exactly what `nameRelation` answers (`normalize-name-kinship.spec.ts` pins it), except that
|
|
51
|
+
* it splits `head-differs` into the half that differed.
|
|
52
|
+
*/
|
|
53
|
+
import { type NameRelation, type NameRelationReason } from "./name-middle.ts";
|
|
54
|
+
import type { PersonName } from "./person-name.ts";
|
|
55
|
+
/**
|
|
56
|
+
* EVERY TOLERANCE THE RELATION CAN REPORT, IN THE ORDER IT REPORTS THEM, with the field it
|
|
57
|
+
* tolerates and whether a gate may admit a pair through it. The one declaration: the
|
|
58
|
+
* `KinshipVia`, `GivenVia` and `MiddleVia` types are read off it, so adding a kind is a
|
|
59
|
+
* line here and nothing else in the core.
|
|
60
|
+
*/
|
|
61
|
+
export declare const KINSHIP_VIAS: Readonly<{
|
|
62
|
+
/** one surname is a whole hyphen- or space-joined part of the other's compound */
|
|
63
|
+
readonly "surname-part": Readonly<{
|
|
64
|
+
field: "surname";
|
|
65
|
+
gateSafe: true;
|
|
66
|
+
}>;
|
|
67
|
+
/** the two first given names are two spellings of one name (`Connor` / `Conor`) */
|
|
68
|
+
readonly "given-variant": Readonly<{
|
|
69
|
+
field: "given";
|
|
70
|
+
gateSafe: true;
|
|
71
|
+
}>;
|
|
72
|
+
/** the given-name table relates the two first given names, and the gate admits them */
|
|
73
|
+
readonly "given-family": Readonly<{
|
|
74
|
+
field: "given";
|
|
75
|
+
gateSafe: true;
|
|
76
|
+
}>;
|
|
77
|
+
/** the table relates them through a form that is a name in its own right */
|
|
78
|
+
readonly "given-family-not-gate": Readonly<{
|
|
79
|
+
field: "given";
|
|
80
|
+
gateSafe: false;
|
|
81
|
+
}>;
|
|
82
|
+
/** a probabilistic source says the two MAY be one family; reserved for one */
|
|
83
|
+
readonly "given-maybe": Readonly<{
|
|
84
|
+
field: "given";
|
|
85
|
+
gateSafe: false;
|
|
86
|
+
}>;
|
|
87
|
+
/** NN5's own: one side carries no middle name */
|
|
88
|
+
readonly "middle-absent": Readonly<{
|
|
89
|
+
field: "middle";
|
|
90
|
+
gateSafe: true;
|
|
91
|
+
}>;
|
|
92
|
+
/** NN5's own: an initial against a full middle whose first letter matches */
|
|
93
|
+
readonly "initial-agrees": Readonly<{
|
|
94
|
+
field: "middle";
|
|
95
|
+
gateSafe: true;
|
|
96
|
+
}>;
|
|
97
|
+
}>;
|
|
98
|
+
/** A tolerance a pair passed through. */
|
|
99
|
+
export type KinshipVia = keyof typeof KINSHIP_VIAS;
|
|
100
|
+
type ViaOfField<F extends string> = {
|
|
101
|
+
[K in KinshipVia]: (typeof KINSHIP_VIAS)[K]["field"] extends F ? K : never;
|
|
102
|
+
}[KinshipVia];
|
|
103
|
+
/** The tolerances a `GivenProvider` may answer with. */
|
|
104
|
+
export type GivenVia = ViaOfField<"given">;
|
|
105
|
+
/** The middle-name tolerances: NN5's two `candidate` reasons, by the same names. */
|
|
106
|
+
export type MiddleVia = ViaOfField<"middle">;
|
|
107
|
+
/**
|
|
108
|
+
* Why the relation came out the way it did. NN5's reasons, except that `head-differs` is
|
|
109
|
+
* split into the half that differed, because this relation tolerates each half on its own.
|
|
110
|
+
*/
|
|
111
|
+
export type KinshipReason = Exclude<NameRelationReason, "head-differs">
|
|
112
|
+
/** the surnames are neither equal at NN3 nor one a whole part of the other */
|
|
113
|
+
| "surname-differs"
|
|
114
|
+
/** the first given names are neither equal at NN3 nor related by any provider (or one is missing) */
|
|
115
|
+
| "given-differs";
|
|
116
|
+
/** The tolerant relation's answer. */
|
|
117
|
+
export interface NameKinship {
|
|
118
|
+
/** `NameRelation`'s three values, meaning what they mean there: `candidate` is never a merge */
|
|
119
|
+
readonly relation: NameRelation;
|
|
120
|
+
/** every tolerance used, in `KINSHIP_VIAS` order; empty for `same` and for `different` */
|
|
121
|
+
readonly via: readonly KinshipVia[];
|
|
122
|
+
/** the veto for `different`; NN5's middle-name reason otherwise */
|
|
123
|
+
readonly reason: KinshipReason;
|
|
124
|
+
/** true only when the pair is related and every `via` is gate-safe */
|
|
125
|
+
readonly gateSafe: boolean;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Might two DIFFERENT first given names (NN3 fields, so upper case and letters) be one
|
|
129
|
+
* family, and by which tolerance? `null` is "this provider does not relate them".
|
|
130
|
+
*
|
|
131
|
+
* IT IS ASKED WITH `a < b` in code-unit order, once per pair, so a provider holding ordered
|
|
132
|
+
* pairs needs to hold each pair one way only and the relation stays symmetric whatever the
|
|
133
|
+
* provider does.
|
|
134
|
+
*/
|
|
135
|
+
export type GivenProvider = (a: string, b: string) => GivenVia | null;
|
|
136
|
+
export interface NameKinshipOptions {
|
|
137
|
+
/**
|
|
138
|
+
* The given-name tolerances, asked in order; THE FIRST NON-NULL ANSWER WINS, so a caller
|
|
139
|
+
* lists its most trusted source first. Default `[givenFamilyTable]`; `[]` tolerates no
|
|
140
|
+
* given name.
|
|
141
|
+
*/
|
|
142
|
+
readonly given?: readonly GivenProvider[];
|
|
143
|
+
}
|
|
144
|
+
/** A tolerant relation with its providers fixed. */
|
|
145
|
+
export type NameKinshipRelation = (a: PersonName, b: PersonName) => NameKinship;
|
|
146
|
+
/**
|
|
147
|
+
* Make the tolerant relation over a fixed list of given-name providers.
|
|
148
|
+
*
|
|
149
|
+
* The rule table, in the order it is applied:
|
|
150
|
+
*
|
|
151
|
+
* | the two sides | relation |
|
|
152
|
+
* |---|---|
|
|
153
|
+
* | either names no person | `different`, `not-a-person` |
|
|
154
|
+
* | surnames differ at NN3 and neither is a whole part of the other | `different`, `surname-differs` |
|
|
155
|
+
* | first given names differ at NN3 (or one is missing) and no provider relates them | `different`, `given-differs` |
|
|
156
|
+
* | NN5's suffix and middle rows (`tailRelation`) say `different` | `different`, NN5's reason |
|
|
157
|
+
* | no tolerance used and the middles are equal | `same` |
|
|
158
|
+
* | otherwise | `candidate`, via every tolerance used |
|
|
159
|
+
*/
|
|
160
|
+
export declare function createNameKinship(options?: NameKinshipOptions): NameKinshipRelation;
|
|
161
|
+
/** The tolerant relation over the curated given-name table, `givenFamilyTable`. */
|
|
162
|
+
export declare const nameKinship: NameKinshipRelation;
|
|
163
|
+
/**
|
|
164
|
+
* THE MIDDLE VIAS ARE NN5'S CANDIDATE REASONS, ASSERTED IN TYPECHECKED SOURCE. A middle
|
|
165
|
+
* `via` is `tailRelation`'s reason passed through, so the day either list moves without the
|
|
166
|
+
* other, `bun run typecheck` goes red here. Exported for `Nn5IsNotAMemberOfAParse`'s reason.
|
|
167
|
+
*/
|
|
168
|
+
type AssertTrue<T extends true> = T;
|
|
169
|
+
/** Every middle `via` is a reason NN5 can give. */
|
|
170
|
+
export type MiddleViasAreNn5Reasons = AssertTrue<MiddleVia extends NameRelationReason ? true : false>;
|
|
171
|
+
export {};
|
|
@@ -156,6 +156,17 @@ export declare function comparePosition(x: string, y: string): NameRelationReaso
|
|
|
156
156
|
* not this issue's to close.
|
|
157
157
|
*/
|
|
158
158
|
export declare function nameRelation(a: PersonName, b: PersonName): NameReading;
|
|
159
|
+
/**
|
|
160
|
+
* NN5's rule table BELOW THE HEAD: the suffix row and every middle-name row, for two
|
|
161
|
+
* persons whose surname and first given name the CALLER has already accepted.
|
|
162
|
+
*
|
|
163
|
+
* MOVED OUT OF `nameRelation`, NOT FORKED. `nameRelation` checks the head by equality and
|
|
164
|
+
* hands the rest here; `./name-kinship.ts` accepts the head through its own tolerances
|
|
165
|
+
* (a given-name family, a surname part) and hands the rest here too, so the two relations
|
|
166
|
+
* read one middle-name rule and cannot disagree about `J` and `Jane`. It is not on the
|
|
167
|
+
* package's surface: a caller that has not decided the head has no business asking it.
|
|
168
|
+
*/
|
|
169
|
+
export declare function tailRelation(a: PersonName, b: PersonName): NameReading;
|
|
159
170
|
/**
|
|
160
171
|
* THE OWNER'S RULE, ASSERTED IN TYPECHECKED SOURCE.
|
|
161
172
|
*
|
|
@@ -448,3 +448,93 @@ tolerating accent, punctuation and order.
|
|
|
448
448
|
classifier: its declared-order weights were fitted only on documents that declared that
|
|
449
449
|
order, and a guess is not a declaration. With no order, or `unknown`, the classifier
|
|
450
450
|
answers exactly as it does without the argument.
|
|
451
|
+
|
|
452
|
+
## Name kinship — the tolerant relation
|
|
453
|
+
|
|
454
|
+
`nameRelation` is the strict identity relation: the surname and the first given name MUST
|
|
455
|
+
agree at NN3, and only a middle name is tolerated. `nameKinship(a, b)` is the tolerant
|
|
456
|
+
relation built on it. It reports **which tolerances a pair passed through**, so each caller
|
|
457
|
+
decides how much to accept.
|
|
458
|
+
|
|
459
|
+
```ts
|
|
460
|
+
interface NameKinship {
|
|
461
|
+
relation: "same" | "candidate" | "different"; // NameRelation's values and meanings
|
|
462
|
+
via: readonly KinshipVia[]; // every tolerance used, KINSHIP_VIAS order
|
|
463
|
+
reason: KinshipReason; // the veto, or NN5's middle-name reason
|
|
464
|
+
gateSafe: boolean; // related, and every via is gate-safe
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
| via | field | gate-safe | when |
|
|
469
|
+
|---|---|---|---|
|
|
470
|
+
| `surname-part` | surname | yes | one surname is a whole hyphen- or space-joined part of the other's compound |
|
|
471
|
+
| `given-variant` | given | yes | the two first given names are two spellings of one name (`Connor` / `Conor`) |
|
|
472
|
+
| `given-family` | given | yes | a provider says the two first given names are one family, and a gate admits them |
|
|
473
|
+
| `given-family-not-gate` | given | no | the table relates them only through a `notAtGate` form |
|
|
474
|
+
| `given-maybe` | given | no | a probabilistic source says they MAY be one family |
|
|
475
|
+
| `middle-absent` | middle | yes | NN5's: one side carries no middle |
|
|
476
|
+
| `initial-agrees` | middle | yes | NN5's: an initial against a full middle that starts with it |
|
|
477
|
+
|
|
478
|
+
- `candidate` is NEVER a merge, as in NN5. Every tolerance means "may be the same person;
|
|
479
|
+
the rest of the evidence decides".
|
|
480
|
+
- `via` is a SET, one entry per tolerance used, in the table's order. It is empty for
|
|
481
|
+
`same` and for `different`. `Kate M Smith` / `Katherine Conley-Smith` is a candidate via
|
|
482
|
+
`surname-part`, `given-family` and `middle-absent`.
|
|
483
|
+
- A surname part is compared WHOLE: `Larson` is a part of `Conley-Larson` and of `Conley
|
|
484
|
+
Larson, Mary`, and not of `Carlson` or `Conley-Fitzlarson`. Two compounds with the same
|
|
485
|
+
number of parts are two surnames (`Smith-Jones` / `Jones-Smith` is `different`).
|
|
486
|
+
- The given-name tolerance is INJECTED: `createNameKinship({ given: [p1, p2, …] })` asks
|
|
487
|
+
each `GivenProvider` in order and the first non-null answer wins. A provider is asked
|
|
488
|
+
once per pair, with the two NN3 first given names in code-unit order, and never with two
|
|
489
|
+
equal names. The default is `[givenFamilyTable]`, which answers from
|
|
490
|
+
`given-diminutives.json`; `sameGivenFamily(a, b)` is exactly "`givenFamilyTable(a, b)` is
|
|
491
|
+
`given-variant` or `given-family`". `[]` tolerates no given name.
|
|
492
|
+
- A new kind of `via` is one entry in `KINSHIP_VIAS`, which carries its field and its
|
|
493
|
+
gate-safety. The relation's rules do not change.
|
|
494
|
+
- With no tolerance used, the answer is `nameRelation`'s. `head-differs` is split into
|
|
495
|
+
`surname-differs` and `given-differs`, because each half is tolerated on its own. The
|
|
496
|
+
suffix and middle rows are NN5's (`tailRelation`) and still veto.
|
|
497
|
+
- A print that names no person is `different` from everything, itself included.
|
|
498
|
+
|
|
499
|
+
### The given-name table
|
|
500
|
+
|
|
501
|
+
`given-diminutives.json` holds three things, each in NN3 upper case:
|
|
502
|
+
|
|
503
|
+
- `variants`: one name's SPELLINGS, canonical first (`CONNOR` / `CONOR`, `SARAH` / `SARA`,
|
|
504
|
+
`MEGAN` / `MEGHAN` / `MEAGAN` / `MEAGHAN`). Two spellings are `given-variant`, and a gate
|
|
505
|
+
admits them. A spelling is read as its canonical before `families` is consulted, so
|
|
506
|
+
`Cate` meets `Katherine` as `Kate` does. A transposition or a typo (`Oliva`, `Cailtin`,
|
|
507
|
+
`Naimh`) is NOT a variant: a misprint tolerance is a separate provider.
|
|
508
|
+
- `families`: root -> its familiar forms. Two names are one family when they SHARE A ROOT,
|
|
509
|
+
a root belonging to itself. A form may sit under several roots and meets EVERY one of
|
|
510
|
+
them: `Evie` is Evelyn's, Genevieve's, Eve's, Eva's and Evangeline's. Sharing a root is
|
|
511
|
+
not transitive, so `Eve` and `Eva` stay apart though `Evie` meets both.
|
|
512
|
+
- `notAtGate`: forms a gate refuses — a name in its own right (`Josie`, `Gracie`, `Lily`),
|
|
513
|
+
a translation (`Sean`), another gender's name (`Sam`, `Charlie`, `Frankie`), and every
|
|
514
|
+
form under more than one root (`Ellie`, `Maddie`, `Ally`, `Kit`), because the form alone
|
|
515
|
+
cannot say which root its bearer carries. A pair through such a form is
|
|
516
|
+
`given-family-not-gate`. The flag refuses a name AS A FORM: where it is the root itself,
|
|
517
|
+
its own forms still meet it at a gate (`Caitlin` is Catherine's Irish form, refused
|
|
518
|
+
against `Katherine`, and still meets `Cait`).
|
|
519
|
+
|
|
520
|
+
`Kate` and `Katie` are Catherine's, and `Katelyn` is a spelling of Caitlin. They meet below
|
|
521
|
+
the gate through Caitlin as a form of Catherine, so `Kate` / `Katherine` keeps its gate.
|
|
522
|
+
|
|
523
|
+
Over the 421 forms and spellings of the table (88,410 distinct pairs), `givenFamilyTable`
|
|
524
|
+
answers `given-variant` for 90 pairs, `given-family` for 371, `given-family-not-gate` for 553,
|
|
525
|
+
and `null` for the rest; `sameGivenFamily` admits 461.
|
|
526
|
+
|
|
527
|
+
### The tolerant lookup
|
|
528
|
+
|
|
529
|
+
`createNameIndex(dop, { kinship?, entries? })` is a `NameMap` whose `get`, `has`, `set` and
|
|
530
|
+
`delete` are `createNameMap`'s, unchanged. It adds `related(name)`: every held entry the
|
|
531
|
+
relation does not call `different`, each with its `NameKinship`, `same` before `candidate`
|
|
532
|
+
and otherwise in insertion order. Candidates come from a secondary index on each entry's
|
|
533
|
+
NN3 surname key and each of its surname parts, so `Mary Larson` finds `Mary Conley-Larson`
|
|
534
|
+
and the other way round. The index does not key on the first initial, because a given-name
|
|
535
|
+
family need not keep one (`Peggy` / `Margaret`).
|
|
536
|
+
|
|
537
|
+
It is a sibling of `NameMap` and not a method on it. The tolerant answer is not
|
|
538
|
+
transitive, and a `NameMap` is an equivalence class. The lookup needs a relation chosen by
|
|
539
|
+
options. And the secondary index costs memory that a caller wanting only the exact map
|
|
540
|
+
should not pay.
|
package/package.json
CHANGED