@finbheara/names 0.11.0 → 0.13.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 +30 -0
- package/dist/chunks/{index-n4t288kj.js → index-wq6kn9jq.js} +326 -149
- package/dist/chunks/index-wq6kn9jq.js.map +21 -0
- package/dist/chunks/{index-jx97zxtz.js → index-yf904dam.js} +75 -7
- package/dist/chunks/{index-jx97zxtz.js.map → index-yf904dam.js.map} +3 -3
- package/dist/chunks/{index-61wk7v9c.js → index-ztpxgcs1.js} +2 -2
- package/dist/cli/census.js +2 -2
- package/dist/index.js +21 -9
- package/dist/index.js.map +1 -1
- package/dist/measure/index.js +2 -2
- package/dist/normalize/index.js +20 -8
- package/dist/normalize/index.js.map +1 -1
- package/dist/types/normalize/given-diminutives.d.ts +14 -1
- package/dist/types/normalize/index.d.ts +4 -3
- 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 +165 -0
- package/dist/types/normalize/name-middle.d.ts +11 -0
- package/dist/types/normalize/name-placeholder.d.ts +24 -9
- package/docs/NAME-NORMALIZATION.md +65 -0
- package/package.json +1 -1
- package/dist/chunks/index-n4t288kj.js.map +0 -19
- /package/dist/chunks/{index-61wk7v9c.js.map → index-ztpxgcs1.js.map} +0 -0
package/dist/measure/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
} from "../chunks/index-pncn9y2r.js";
|
|
5
5
|
import {
|
|
6
6
|
createNameCensus2
|
|
7
|
-
} from "../chunks/index-
|
|
8
|
-
import"../chunks/index-
|
|
7
|
+
} from "../chunks/index-ztpxgcs1.js";
|
|
8
|
+
import"../chunks/index-yf904dam.js";
|
|
9
9
|
import"../chunks/index-7vvt5hhy.js";
|
|
10
10
|
export {
|
|
11
11
|
composeEvalSet2 as composeEvalSet,
|
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-wq6kn9jq.js";
|
|
48
53
|
import"../chunks/index-kw6gbrnq.js";
|
|
49
54
|
import {
|
|
50
55
|
NAME_REFUSED2,
|
|
@@ -56,6 +61,7 @@ import {
|
|
|
56
61
|
nameFold2,
|
|
57
62
|
PLATFORM_PLACEHOLDERS2,
|
|
58
63
|
NAME_PLACEHOLDERS2,
|
|
64
|
+
placeholderOf2,
|
|
59
65
|
isPlaceholderPrint2,
|
|
60
66
|
isNoNamePrint2,
|
|
61
67
|
COMPOUND_SURNAMES2,
|
|
@@ -89,7 +95,7 @@ import {
|
|
|
89
95
|
defaultPersonNameFactory2,
|
|
90
96
|
personName2,
|
|
91
97
|
personNameFromIcao2
|
|
92
|
-
} from "../chunks/index-
|
|
98
|
+
} from "../chunks/index-yf904dam.js";
|
|
93
99
|
import {
|
|
94
100
|
TEAM_WORD2,
|
|
95
101
|
TEAM_DESIGNATION_WORDS2,
|
|
@@ -109,6 +115,7 @@ export {
|
|
|
109
115
|
DEFAULT_PERSON_NAME_CAPACITY2 as DEFAULT_PERSON_NAME_CAPACITY,
|
|
110
116
|
GIVEN_SEPARATOR2 as GIVEN_SEPARATOR,
|
|
111
117
|
INFEASIBLE_CLASSES2 as INFEASIBLE_CLASSES,
|
|
118
|
+
KINSHIP_VIAS2 as KINSHIP_VIAS,
|
|
112
119
|
MAX_READINGS2 as MAX_READINGS,
|
|
113
120
|
NAME_COLUMNS2 as NAME_COLUMNS,
|
|
114
121
|
NAME_FORMATS2 as NAME_FORMATS,
|
|
@@ -134,6 +141,8 @@ export {
|
|
|
134
141
|
compoundGiven2 as compoundGiven,
|
|
135
142
|
createCachingPersonNameFactory2 as createCachingPersonNameFactory,
|
|
136
143
|
createNameClusters2 as createNameClusters,
|
|
144
|
+
createNameIndex2 as createNameIndex,
|
|
145
|
+
createNameKinship2 as createNameKinship,
|
|
137
146
|
createNameMap2 as createNameMap,
|
|
138
147
|
createNameSet2 as createNameSet,
|
|
139
148
|
createPersonNameFactory2 as createPersonNameFactory,
|
|
@@ -143,6 +152,7 @@ export {
|
|
|
143
152
|
foldNameKey2 as foldNameKey,
|
|
144
153
|
formatPersonDisplay2 as formatPersonDisplay,
|
|
145
154
|
givenAbbreviates2 as givenAbbreviates,
|
|
155
|
+
givenFamilyTable2 as givenFamilyTable,
|
|
146
156
|
givenKeyAbbreviates2 as givenKeyAbbreviates,
|
|
147
157
|
infeasibleClass2 as infeasibleClass,
|
|
148
158
|
inferNameOrder2 as inferNameOrder,
|
|
@@ -166,6 +176,7 @@ export {
|
|
|
166
176
|
nameCompare2 as nameCompare,
|
|
167
177
|
nameFold2 as nameFold,
|
|
168
178
|
nameFormatOrder2 as nameFormatOrder,
|
|
179
|
+
nameKinship2 as nameKinship,
|
|
169
180
|
nameMeet2 as nameMeet,
|
|
170
181
|
nameReadings2 as nameReadings,
|
|
171
182
|
nameRelation2 as nameRelation,
|
|
@@ -177,6 +188,7 @@ export {
|
|
|
177
188
|
personList2 as personList,
|
|
178
189
|
personName2 as personName,
|
|
179
190
|
personNameFromIcao2 as personNameFromIcao,
|
|
191
|
+
placeholderOf2 as placeholderOf,
|
|
180
192
|
printAttestsOrder2 as printAttestsOrder,
|
|
181
193
|
probablySame2 as probablySame,
|
|
182
194
|
readNameCell2 as readNameCell,
|
|
@@ -202,5 +214,5 @@ export {
|
|
|
202
214
|
traditionLookupOf2 as traditionLookupOf
|
|
203
215
|
};
|
|
204
216
|
|
|
205
|
-
//# debugId=
|
|
217
|
+
//# debugId=6CAF39AD5BAB8B2064756E2164756E21
|
|
206
218
|
//# sourceMappingURL=index.js.map
|
|
@@ -11,7 +11,8 @@
|
|
|
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
|
* THE GATE READS LESS THAN THE LANE. `families` is the Python lane's table, first root
|
|
17
18
|
* wins, and the lane reads every form in it as evidence below an exact match. A GATE that
|
|
@@ -29,3 +30,15 @@
|
|
|
29
30
|
* tolerance past it, so a caller can tell which one admitted a pair.
|
|
30
31
|
*/
|
|
31
32
|
export declare function sameGivenFamily(a: string, b: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* What the table says about two DIFFERENT given names (NN3, letters only), as a kinship
|
|
35
|
+
* `via`: `given-family` where `sameGivenFamily` admits the pair, `given-family-not-gate`
|
|
36
|
+
* where the two share a root but either is a `notAtGate` form, and `null` where the table
|
|
37
|
+
* does not relate them. Two equal names answer `null`, for `sameGivenFamily`'s reason.
|
|
38
|
+
*
|
|
39
|
+
* THE DEFAULT GIVEN-NAME PROVIDER of `createNameKinship` (`./name-kinship.ts`). It reads the
|
|
40
|
+
* `ROOT` and `NOT_AT_GATE` that `sameGivenFamily` reads, and `sameGivenFamily` is defined as
|
|
41
|
+
* its `given-family` answer, so the gate and the tolerant relation cannot disagree about a
|
|
42
|
+
* pair.
|
|
43
|
+
*/
|
|
44
|
+
export declare function givenFamilyTable(a: string, b: string): "given-family" | "given-family-not-gate" | null;
|
|
@@ -11,16 +11,17 @@ export { type Level, NAME_TRADITIONS, type NamePart, type NameTradition, type Na
|
|
|
11
11
|
export { NAME_REFUSED, type ParentheticalHandler, type ScrubOptions, type ScrubResult, type ScrubRule, scrubName, scrubNameDetail, stripEveryAside, stripTrailingCredentials, } from "./name-scrub.ts";
|
|
12
12
|
export { repairMojibake } from "./name-mojibake.ts";
|
|
13
13
|
export { type CellVerdict, type NameRefusalRule, type Refusal, classifyCell, } from "./cell-verdict.ts";
|
|
14
|
-
export { NAME_PLACEHOLDERS, type NamePlaceholder, PLATFORM_PLACEHOLDERS, isNoNamePrint, isPlaceholderPrint, } from "./name-placeholder.ts";
|
|
14
|
+
export { NAME_PLACEHOLDERS, type NamePlaceholder, PLATFORM_PLACEHOLDERS, isNoNamePrint, isPlaceholderPrint, placeholderOf, } from "./name-placeholder.ts";
|
|
15
15
|
export { capitalise, nameCase, type SpellingLookup } from "./name-case.ts";
|
|
16
16
|
export { type DisplayLexicon, GIVEN_SEPARATOR, type PersonNameParts, SURNAME_SEPARATOR, formatPersonDisplay, lexiconKey, parsePersonName, personDisplayName, spellingLookup, titleCaseNamePart, } from "./person-display.ts";
|
|
17
17
|
export { type CommaReading, type NameOrder, type NameSplit, type ResolvedNameOrder, commaReading, inferNameOrder, resolveNameOrder, spaceAfterComma, splitPersonName, } from "./name-split.ts";
|
|
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,165 @@
|
|
|
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
|
+
* Mary Larson / Mary Conley-Larson candidate via [surname-part]
|
|
18
|
+
* Kate M Smith / Katherine Conley-Smith
|
|
19
|
+
* candidate via [surname-part, given-family, middle-absent]
|
|
20
|
+
*
|
|
21
|
+
* ## `via` IS A SET, NOT A KIND
|
|
22
|
+
*
|
|
23
|
+
* A pair can pass through several tolerances at once, one per field, and naming the
|
|
24
|
+
* combinations (`both`) grows as the product of the fields. So `via` lists every tolerance
|
|
25
|
+
* used, in the fixed order of `KINSHIP_VIAS`, and is empty for a pair NN3 already agreed on.
|
|
26
|
+
*
|
|
27
|
+
* ## `candidate` IS STILL NOT A MERGE
|
|
28
|
+
*
|
|
29
|
+
* The three values are `NameRelation`'s and mean what they mean there. Every tolerance here
|
|
30
|
+
* is "may be the same person, the rest of the evidence decides". `gateSafe` says something
|
|
31
|
+
* narrower: every tolerance used is one this package's own gate comparators already admit —
|
|
32
|
+
* a middle name `probablySame(…, "nn5")` admits, a given-name family `sameGivenFamily`
|
|
33
|
+
* admits, a compound the print itself carries. A table form that is a name in its own right
|
|
34
|
+
* (`given-family-not-gate`) or a probabilistic source (`given-maybe`) is a lead for the rest
|
|
35
|
+
* of the evidence and never a gate.
|
|
36
|
+
*
|
|
37
|
+
* ## THE GIVEN-NAME TOLERANCE IS INJECTED
|
|
38
|
+
*
|
|
39
|
+
* Which given names are one family is DATA, and the data will move: a probabilistic source
|
|
40
|
+
* of diminutive pairs is expected beside the curated table, and the table may shrink. So the
|
|
41
|
+
* relation asks an ordered list of `GivenProvider`s fixed by `createNameKinship`'s options,
|
|
42
|
+
* and the core knows no table. A new provider is an option; a new kind of answer is one
|
|
43
|
+
* line in `KINSHIP_VIAS`, which also carries its gate-safety, so no branch here changes.
|
|
44
|
+
*
|
|
45
|
+
* ## IT COMPOSES, IT DOES NOT FORK
|
|
46
|
+
*
|
|
47
|
+
* The middle-name and suffix rows are `tailRelation`, the half of `nameRelation` below the
|
|
48
|
+
* head, so `J` against `Jane` is decided in one place. With no tolerance used this answers
|
|
49
|
+
* exactly what `nameRelation` answers (`normalize-name-kinship.spec.ts` pins it), except that
|
|
50
|
+
* it splits `head-differs` into the half that differed.
|
|
51
|
+
*/
|
|
52
|
+
import { type NameRelation, type NameRelationReason } from "./name-middle.ts";
|
|
53
|
+
import type { PersonName } from "./person-name.ts";
|
|
54
|
+
/**
|
|
55
|
+
* EVERY TOLERANCE THE RELATION CAN REPORT, IN THE ORDER IT REPORTS THEM, with the field it
|
|
56
|
+
* tolerates and whether a gate may admit a pair through it. The one declaration: the
|
|
57
|
+
* `KinshipVia`, `GivenVia` and `MiddleVia` types are read off it, so adding a kind is a
|
|
58
|
+
* line here and nothing else in the core.
|
|
59
|
+
*/
|
|
60
|
+
export declare const KINSHIP_VIAS: Readonly<{
|
|
61
|
+
/** one surname is a whole hyphen- or space-joined part of the other's compound */
|
|
62
|
+
readonly "surname-part": Readonly<{
|
|
63
|
+
field: "surname";
|
|
64
|
+
gateSafe: true;
|
|
65
|
+
}>;
|
|
66
|
+
/** the given-name table relates the two first given names, and the gate admits them */
|
|
67
|
+
readonly "given-family": Readonly<{
|
|
68
|
+
field: "given";
|
|
69
|
+
gateSafe: true;
|
|
70
|
+
}>;
|
|
71
|
+
/** the table relates them through a form that is a name in its own right */
|
|
72
|
+
readonly "given-family-not-gate": Readonly<{
|
|
73
|
+
field: "given";
|
|
74
|
+
gateSafe: false;
|
|
75
|
+
}>;
|
|
76
|
+
/** a probabilistic source says the two MAY be one family; reserved for one */
|
|
77
|
+
readonly "given-maybe": Readonly<{
|
|
78
|
+
field: "given";
|
|
79
|
+
gateSafe: false;
|
|
80
|
+
}>;
|
|
81
|
+
/** NN5's own: one side carries no middle name */
|
|
82
|
+
readonly "middle-absent": Readonly<{
|
|
83
|
+
field: "middle";
|
|
84
|
+
gateSafe: true;
|
|
85
|
+
}>;
|
|
86
|
+
/** NN5's own: an initial against a full middle whose first letter matches */
|
|
87
|
+
readonly "initial-agrees": Readonly<{
|
|
88
|
+
field: "middle";
|
|
89
|
+
gateSafe: true;
|
|
90
|
+
}>;
|
|
91
|
+
}>;
|
|
92
|
+
/** A tolerance a pair passed through. */
|
|
93
|
+
export type KinshipVia = keyof typeof KINSHIP_VIAS;
|
|
94
|
+
type ViaOfField<F extends string> = {
|
|
95
|
+
[K in KinshipVia]: (typeof KINSHIP_VIAS)[K]["field"] extends F ? K : never;
|
|
96
|
+
}[KinshipVia];
|
|
97
|
+
/** The tolerances a `GivenProvider` may answer with. */
|
|
98
|
+
export type GivenVia = ViaOfField<"given">;
|
|
99
|
+
/** The middle-name tolerances: NN5's two `candidate` reasons, by the same names. */
|
|
100
|
+
export type MiddleVia = ViaOfField<"middle">;
|
|
101
|
+
/**
|
|
102
|
+
* Why the relation came out the way it did. NN5's reasons, except that `head-differs` is
|
|
103
|
+
* split into the half that differed, because this relation tolerates each half on its own.
|
|
104
|
+
*/
|
|
105
|
+
export type KinshipReason = Exclude<NameRelationReason, "head-differs">
|
|
106
|
+
/** the surnames are neither equal at NN3 nor one a whole part of the other */
|
|
107
|
+
| "surname-differs"
|
|
108
|
+
/** the first given names are neither equal at NN3 nor related by any provider (or one is missing) */
|
|
109
|
+
| "given-differs";
|
|
110
|
+
/** The tolerant relation's answer. */
|
|
111
|
+
export interface NameKinship {
|
|
112
|
+
/** `NameRelation`'s three values, meaning what they mean there: `candidate` is never a merge */
|
|
113
|
+
readonly relation: NameRelation;
|
|
114
|
+
/** every tolerance used, in `KINSHIP_VIAS` order; empty for `same` and for `different` */
|
|
115
|
+
readonly via: readonly KinshipVia[];
|
|
116
|
+
/** the veto for `different`; NN5's middle-name reason otherwise */
|
|
117
|
+
readonly reason: KinshipReason;
|
|
118
|
+
/** true only when the pair is related and every `via` is gate-safe */
|
|
119
|
+
readonly gateSafe: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Might two DIFFERENT first given names (NN3 fields, so upper case and letters) be one
|
|
123
|
+
* family, and by which tolerance? `null` is "this provider does not relate them".
|
|
124
|
+
*
|
|
125
|
+
* IT IS ASKED WITH `a < b` in code-unit order, once per pair, so a provider holding ordered
|
|
126
|
+
* pairs needs to hold each pair one way only and the relation stays symmetric whatever the
|
|
127
|
+
* provider does.
|
|
128
|
+
*/
|
|
129
|
+
export type GivenProvider = (a: string, b: string) => GivenVia | null;
|
|
130
|
+
export interface NameKinshipOptions {
|
|
131
|
+
/**
|
|
132
|
+
* The given-name tolerances, asked in order; THE FIRST NON-NULL ANSWER WINS, so a caller
|
|
133
|
+
* lists its most trusted source first. Default `[givenFamilyTable]`; `[]` tolerates no
|
|
134
|
+
* given name.
|
|
135
|
+
*/
|
|
136
|
+
readonly given?: readonly GivenProvider[];
|
|
137
|
+
}
|
|
138
|
+
/** A tolerant relation with its providers fixed. */
|
|
139
|
+
export type NameKinshipRelation = (a: PersonName, b: PersonName) => NameKinship;
|
|
140
|
+
/**
|
|
141
|
+
* Make the tolerant relation over a fixed list of given-name providers.
|
|
142
|
+
*
|
|
143
|
+
* The rule table, in the order it is applied:
|
|
144
|
+
*
|
|
145
|
+
* | the two sides | relation |
|
|
146
|
+
* |---|---|
|
|
147
|
+
* | either names no person | `different`, `not-a-person` |
|
|
148
|
+
* | surnames differ at NN3 and neither is a whole part of the other | `different`, `surname-differs` |
|
|
149
|
+
* | first given names differ at NN3 (or one is missing) and no provider relates them | `different`, `given-differs` |
|
|
150
|
+
* | NN5's suffix and middle rows (`tailRelation`) say `different` | `different`, NN5's reason |
|
|
151
|
+
* | no tolerance used and the middles are equal | `same` |
|
|
152
|
+
* | otherwise | `candidate`, via every tolerance used |
|
|
153
|
+
*/
|
|
154
|
+
export declare function createNameKinship(options?: NameKinshipOptions): NameKinshipRelation;
|
|
155
|
+
/** The tolerant relation over the curated given-name table, `givenFamilyTable`. */
|
|
156
|
+
export declare const nameKinship: NameKinshipRelation;
|
|
157
|
+
/**
|
|
158
|
+
* THE MIDDLE VIAS ARE NN5'S CANDIDATE REASONS, ASSERTED IN TYPECHECKED SOURCE. A middle
|
|
159
|
+
* `via` is `tailRelation`'s reason passed through, so the day either list moves without the
|
|
160
|
+
* other, `bun run typecheck` goes red here. Exported for `Nn5IsNotAMemberOfAParse`'s reason.
|
|
161
|
+
*/
|
|
162
|
+
type AssertTrue<T extends true> = T;
|
|
163
|
+
/** Every middle `via` is a reason NN5 can give. */
|
|
164
|
+
export type MiddleViasAreNn5Reasons = AssertTrue<MiddleVia extends NameRelationReason ? true : false>;
|
|
165
|
+
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
|
*
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* (`^[^\p{L}]*$`, any Unicode letter, so a name written in a non-Latin script is a
|
|
9
9
|
* name);
|
|
10
10
|
* 2. it is a platform sentinel a row's name cell and school cell share (`N/A`, `TBA`,
|
|
11
|
-
* `Unknown`, `None`): `PLATFORM_PLACEHOLDERS`, asked through
|
|
12
|
-
* list for both cells so they cannot drift apart;
|
|
11
|
+
* `Unknown`, `None`, `No School`, `Independent`): `PLATFORM_PLACEHOLDERS`, asked through
|
|
12
|
+
* `isPlaceholderPrint`, ONE list for both cells so they cannot drift apart;
|
|
13
13
|
* 3. it is one of `NAME_PLACEHOLDERS`: the sentinels only a NAME cell prints, each with
|
|
14
14
|
* where it was measured.
|
|
15
15
|
*
|
|
@@ -26,15 +26,24 @@ export interface NamePlaceholder {
|
|
|
26
26
|
readonly print: string;
|
|
27
27
|
/** where it was seen, so a reviewer can audit the row */
|
|
28
28
|
readonly authority: string;
|
|
29
|
+
/**
|
|
30
|
+
* `prefix`: the folded print STARTS with this row's words, for an entry form's instruction
|
|
31
|
+
* that runs on (`NO SCHOOL SELECTED - Please change your profile`). Whole words only.
|
|
32
|
+
* Absent: the folded print is exactly this row.
|
|
33
|
+
*/
|
|
34
|
+
readonly match?: "prefix";
|
|
29
35
|
}
|
|
30
36
|
/**
|
|
31
|
-
* THE SHARED SENTINELS: what an entry platform writes where nothing was entered, in a
|
|
32
|
-
* or a school cell alike. A school reader asks `isPlaceholderPrint` of its cell, so
|
|
33
|
-
* refused on both sides of a row by this one list. A
|
|
34
|
-
*
|
|
37
|
+
* THE SHARED SENTINELS: what an entry platform writes where nothing was entered or chosen, in a
|
|
38
|
+
* name cell or a school cell alike. A school reader asks `isPlaceholderPrint` of its cell, so
|
|
39
|
+
* `N/A`, `No School` and `Independent` are refused on both sides of a row by this one list. A
|
|
40
|
+
* sentinel worded about a school is still no name when a name cell prints it, so it is here
|
|
41
|
+
* too and not in a second list. Ordinary prints that merely CONTAIN one of these words are
|
|
42
|
+
* not placeholders: a row matches the whole folded print, or its leading words for `prefix`.
|
|
35
43
|
*
|
|
36
|
-
*
|
|
37
|
-
* cell, matched on
|
|
44
|
+
* The first five were measured 2026-10-01 over every stream of the results corpus (3,776,915
|
|
45
|
+
* rows), the rest 2026-10-05 over the same corpus (3,586,702 rows): rows per cell, matched on
|
|
46
|
+
* the fold. A row with 0 rows is an entry form's option seen only as a form label.
|
|
38
47
|
*/
|
|
39
48
|
export declare const PLATFORM_PLACEHOLDERS: readonly NamePlaceholder[];
|
|
40
49
|
/**
|
|
@@ -43,9 +52,15 @@ export declare const PLATFORM_PLACEHOLDERS: readonly NamePlaceholder[];
|
|
|
43
52
|
* every stream of the results corpus (3,776,915 rows).
|
|
44
53
|
*/
|
|
45
54
|
export declare const NAME_PLACEHOLDERS: readonly NamePlaceholder[];
|
|
55
|
+
/**
|
|
56
|
+
* The PLATFORM_PLACEHOLDERS row this print is, or `undefined`. For a reader that records
|
|
57
|
+
* which sentinel refused a cell; `isPlaceholderPrint` is the yes/no.
|
|
58
|
+
*/
|
|
59
|
+
export declare function placeholderOf(print: string): NamePlaceholder | undefined;
|
|
46
60
|
/**
|
|
47
61
|
* Is this print one of the PLATFORM PLACEHOLDERS a name cell and a school cell share
|
|
48
|
-
* (`N/A`, `TBA`, `Unknown`, `None`)? The question a school reader
|
|
62
|
+
* (`N/A`, `TBA`, `Unknown`, `None`, `No School`, `Independent`)? The question a school reader
|
|
63
|
+
* asks of its cell.
|
|
49
64
|
*/
|
|
50
65
|
export declare function isPlaceholderPrint(print: string): boolean;
|
|
51
66
|
/**
|
|
@@ -448,3 +448,68 @@ 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-family` | given | yes | a provider says the two first given names are one family, and a gate admits them |
|
|
472
|
+
| `given-family-not-gate` | given | no | the table relates them through a `notAtGate` form |
|
|
473
|
+
| `given-maybe` | given | no | a probabilistic source says they MAY be one family |
|
|
474
|
+
| `middle-absent` | middle | yes | NN5's: one side carries no middle |
|
|
475
|
+
| `initial-agrees` | middle | yes | NN5's: an initial against a full middle that starts with it |
|
|
476
|
+
|
|
477
|
+
- `candidate` is NEVER a merge, as in NN5. Every tolerance means "may be the same person;
|
|
478
|
+
the rest of the evidence decides".
|
|
479
|
+
- `via` is a SET, one entry per tolerance used, in the table's order. It is empty for
|
|
480
|
+
`same` and for `different`. `Kate M Smith` / `Katherine Conley-Smith` is a candidate via
|
|
481
|
+
`surname-part`, `given-family` and `middle-absent`.
|
|
482
|
+
- A surname part is compared WHOLE: `Larson` is a part of `Conley-Larson` and of `Conley
|
|
483
|
+
Larson, Mary`, and not of `Carlson` or `Conley-Fitzlarson`. Two compounds with the same
|
|
484
|
+
number of parts are two surnames (`Smith-Jones` / `Jones-Smith` is `different`).
|
|
485
|
+
- The given-name tolerance is INJECTED: `createNameKinship({ given: [p1, p2, …] })` asks
|
|
486
|
+
each `GivenProvider` in order and the first non-null answer wins. A provider is asked
|
|
487
|
+
once per pair, with the two NN3 first given names in code-unit order, and never with two
|
|
488
|
+
equal names. The default is `[givenFamilyTable]`, which answers from
|
|
489
|
+
`given-diminutives.json`; `sameGivenFamily(a, b)` is exactly
|
|
490
|
+
`givenFamilyTable(a, b) === "given-family"`. `[]` tolerates no given name.
|
|
491
|
+
- A new kind of `via` is one entry in `KINSHIP_VIAS`, which carries its field and its
|
|
492
|
+
gate-safety. The relation's rules do not change.
|
|
493
|
+
- With no tolerance used, the answer is `nameRelation`'s. `head-differs` is split into
|
|
494
|
+
`surname-differs` and `given-differs`, because each half is tolerated on its own. The
|
|
495
|
+
suffix and middle rows are NN5's (`tailRelation`) and still veto.
|
|
496
|
+
- A print that names no person is `different` from everything, itself included.
|
|
497
|
+
|
|
498
|
+
Over the 261 forms of the current table (33,930 distinct pairs), `givenFamilyTable` answers
|
|
499
|
+
`given-family` for 354 pairs, `given-family-not-gate` for 302, and `null` for the rest.
|
|
500
|
+
`sameGivenFamily` answers as it did before the provider existed on every one of them.
|
|
501
|
+
|
|
502
|
+
### The tolerant lookup
|
|
503
|
+
|
|
504
|
+
`createNameIndex(dop, { kinship?, entries? })` is a `NameMap` whose `get`, `has`, `set` and
|
|
505
|
+
`delete` are `createNameMap`'s, unchanged. It adds `related(name)`: every held entry the
|
|
506
|
+
relation does not call `different`, each with its `NameKinship`, `same` before `candidate`
|
|
507
|
+
and otherwise in insertion order. Candidates come from a secondary index on each entry's
|
|
508
|
+
NN3 surname key and each of its surname parts, so `Mary Larson` finds `Mary Conley-Larson`
|
|
509
|
+
and the other way round. The index does not key on the first initial, because a given-name
|
|
510
|
+
family need not keep one (`Peggy` / `Margaret`).
|
|
511
|
+
|
|
512
|
+
It is a sibling of `NameMap` and not a method on it. The tolerant answer is not
|
|
513
|
+
transitive, and a `NameMap` is an equivalence class. The lookup needs a relation chosen by
|
|
514
|
+
options. And the secondary index costs memory that a caller wanting only the exact map
|
|
515
|
+
should not pay.
|
package/package.json
CHANGED