@domorium/validator 1.8.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +187 -0
- package/README.md +21 -16
- package/dist/completion/dateSlot.d.ts +2 -1
- package/dist/document/gedcomDocument.d.ts +4 -6
- package/dist/index.cjs.js +2 -2
- package/dist/index.esm.js +766 -506
- package/dist/parser/lineIndex.d.ts +1 -0
- package/dist/validator/calendarDays.d.ts +8 -4
- package/dist/validator/calendars.d.ts +38 -0
- package/dist/validator/epoch.d.ts +11 -0
- package/dist/validator/rule-node.d.ts +3 -1
- package/dist/validator/validate.d.ts +8 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,193 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@domorium/validator` are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.0.0 - 2026-08-20
|
|
6
|
+
|
|
7
|
+
Removes three members of `GedcomDocument` that no consumer read, which is what
|
|
8
|
+
makes this a major: `updateDocument`, which returned `this` and did nothing;
|
|
9
|
+
`xRefs`, a parse product nothing outside the package looked at; and the public
|
|
10
|
+
`pointers`, now private because writing to it poisoned the pointer-target cache
|
|
11
|
+
keyed on it. `getErrors` and `getNodes` lose parameters they never read — a
|
|
12
|
+
change to their types only, since JavaScript discards a surplus argument.
|
|
13
|
+
|
|
14
|
+
- **A 5.5.1 multimedia format is read without regard to case.** `2 FORM JPG` was
|
|
15
|
+
reported against a set the specification writes in lower case, so a file holding
|
|
16
|
+
the right value in the wrong spelling read as one holding a format 5.5.1 does not
|
|
17
|
+
have — and `FORM` is required under every `OBJE`.`FILE`, so an export writing
|
|
18
|
+
extensions in upper case earned an error per photograph. That set is now compared
|
|
19
|
+
without regard to case; `png` and `pdf`, which the specification has no spelling
|
|
20
|
+
for, are still reported. The LDS status sets, which 5.5.1 writes in upper case,
|
|
21
|
+
are unchanged: the case matters differently per set, so the set is named rather
|
|
22
|
+
than the comparison.
|
|
23
|
+
- **A tag typed in lower case is completed.** The lexer reads `1 sex` as the SEX
|
|
24
|
+
tag and the validator names the mistake — a tag is written in upper case, SEX —
|
|
25
|
+
but completion, which is the way out of it, matched upper case only: `1 se`
|
|
26
|
+
offered nothing where `1 SE` offered 62 tags, and a payload typed after `1 sex`
|
|
27
|
+
was offered nothing where after `1 SEX` it was offered the four values of the
|
|
28
|
+
enumeration. The typed tag is now read as the lexer reads it, and the schema is
|
|
29
|
+
asked about the tag it names rather than about the letters as they were typed.
|
|
30
|
+
- **A completion on a large document is answered in under a millisecond.** Every
|
|
31
|
+
request rebuilt the flattened node array and then filtered it, so a 300k-line
|
|
32
|
+
file paid 69 ms per keystroke — for a tree the last parse built and nothing has
|
|
33
|
+
touched since, where parsing and validating that whole file takes 492 ms once.
|
|
34
|
+
The walk is now cached against the array a parse replaces, and the line the
|
|
35
|
+
cursor sits on is found by halving the nodes rather than by reading the range of
|
|
36
|
+
every one before it: 6 ms for the first completion after a parse, 0.1 ms for
|
|
37
|
+
each one after that.
|
|
38
|
+
- **A second `createDocument` on one instance answers for the file it was given.**
|
|
39
|
+
A header naming a system before `GEDC`, a version no schema describes, and a
|
|
40
|
+
header naming no version at all each return before a schema is chosen, and the
|
|
41
|
+
schema, the extension tags and the GEDCOM 7 flag of the document read before
|
|
42
|
+
them stayed where they were: an instance that had read a 7.0 file offered
|
|
43
|
+
`SCHMA` while completing the header of a Personal Ancestral File, and labelled
|
|
44
|
+
a tag with a URI declared by a `HEAD`.`SCHMA` in another file. Parsing now
|
|
45
|
+
clears all three, so every call answers for its own text.
|
|
46
|
+
- **`GedcomDocument` no longer carries members nothing reads.** `updateDocument`
|
|
47
|
+
returned `this` and did nothing; `getErrors` took a language it never read and
|
|
48
|
+
`getNodes` a range it never read — and that `Range` was the DOM's, ambient
|
|
49
|
+
because the package pinned no `lib`, so `getNodes(new Range())` compiled in a
|
|
50
|
+
package that must also run in Node. `pointers` and `xRefs` were mutable parse
|
|
51
|
+
products no consumer outside the package read, and assigning to `pointers`
|
|
52
|
+
poisoned the pointer-target cache keyed on it. `getErrors()` and `getNodes()`
|
|
53
|
+
take no arguments, `pointers` is private and `updateDocument` and `xRefs` are
|
|
54
|
+
gone; the package compiles against `ES2023` alone, so a DOM global cannot be
|
|
55
|
+
named by accident again.
|
|
56
|
+
- **A 5.5.1 multimedia format and the four LDS ordinance statuses are read against
|
|
57
|
+
the value sets the specification spells out.** `3 FORM exe` beneath a FILE, and
|
|
58
|
+
`2 STAT nonsense` beneath BAPL, CONL, ENDL, SLGC or SLGS, were accepted: 5.5.1
|
|
59
|
+
states these closed sets in its primitive definitions rather than in an
|
|
60
|
+
enumeration vocabulary, so the scheme recorded only a type URI and the payload
|
|
61
|
+
was checked for one thing, that it was not empty. A multimedia FORM is now one
|
|
62
|
+
of `[bmp, gif, jpg, ole, pcx, tif, wav]` — 5.5 spelled two of those `jpeg` and
|
|
63
|
+
`tiff` — and each ordinance status one of the seven or eight the specification
|
|
64
|
+
lists for it. Their values are upper case where `PEDI` and `RESN` are lower, and
|
|
65
|
+
a file that mixes the two is now told so. Completion offers these values as well.
|
|
66
|
+
- **A payload type the validator does not describe is left alone, rather than
|
|
67
|
+
required to be a non-empty string.** Every payload type either schema declares is
|
|
68
|
+
now named, and a test over both says so. A type new to an upstream release used to
|
|
69
|
+
reach the same check as free text and report a missing value on every structure
|
|
70
|
+
that legitimately omits it; it now fails that test instead, where a person can see
|
|
71
|
+
it.
|
|
72
|
+
- **A 5.5.1 date is read against the calendar its escape names, and completion
|
|
73
|
+
offers one.** The twelve month tags lived in four places — the 5.5.1 date grammar,
|
|
74
|
+
the day-length table, the 7.0 `calendar` section and the completion reader's own
|
|
75
|
+
list — and 5.5.1, whose `calendar` section was empty, could use none of them. That
|
|
76
|
+
section now describes all six calendars 5.5.1 names, `validator/calendars.ts` is
|
|
77
|
+
the only reader of it, and the lengths stay in code with a test binding them to the
|
|
78
|
+
months both schemes name. `@#DHEBREW@ 45 XXX 5760` is reported rather than waved
|
|
79
|
+
through on a non-empty check, `@#DJULIAN@ 12 JAN 2000` is checked as a date rather
|
|
80
|
+
than accepted for being non-empty, and a date under `@#DROMAN@` or `@#DUNKNOWN@` —
|
|
81
|
+
calendars 5.5.1 names and gives no month — is still accepted, because an empty
|
|
82
|
+
month table says nothing rather than forbidding everything. Completion in a 5.5.1
|
|
83
|
+
file went from 8 candidates with the cursor after `2 DATE` to 26, the escapes and
|
|
84
|
+
the Gregorian months joining the modifiers; from none after `2 DATE 12` to 14; and
|
|
85
|
+
from none after `2 DATE 12 JAN 2000` to the two epoch spellings 5.5.1 writes.
|
|
86
|
+
Neither conformance corpus moved: 8 diagnostics over 23 official files, 14 795
|
|
87
|
+
over 14 vendor exports. See docs/adr/0012.
|
|
88
|
+
|
|
89
|
+
- **A document with more than about 125k diagnostics reports all of them instead of
|
|
90
|
+
none.** Diagnostics were gathered by spreading each level's array into its
|
|
91
|
+
parent's `push`, and a spread is one argument per element: V8 refuses somewhere
|
|
92
|
+
past 125k, so `createDocument` threw a `RangeError` for exceeding the call stack
|
|
93
|
+
and the caller got an exception where it had asked a question — not a truncated
|
|
94
|
+
list, none. A file of 130 000 records each carrying one tag the schema does not
|
|
95
|
+
define there now reports its 130 000 warnings. One array is carried down the walk
|
|
96
|
+
and pushed into, so nothing is capped and no array is allocated per node.
|
|
97
|
+
- **A line below one carrying a pointer but no tag answers nothing instead of
|
|
98
|
+
throwing.** `0 @I1@`, what a truncated export leaves behind, still becomes a node
|
|
99
|
+
and adopts the lines indented under it, so the walk from a node to the root met a
|
|
100
|
+
step with no tag and threw `TypeError: Cannot read properties of undefined`. Asking
|
|
101
|
+
for the type, the label or the pointer target of such a node — hovering
|
|
102
|
+
`1 NAME John /Doe/` is one way in — now gives the same empty answer already given
|
|
103
|
+
for a path the schema does not resolve.
|
|
104
|
+
- **A year followed by an extension epoch is a date.** `2 DATE 2000 _MYEPOCH` was
|
|
105
|
+
reported as an invalid date value. An extension tag is admissible as a month and
|
|
106
|
+
as an epoch alike, and the reader took the pair for a day and a month — leaving
|
|
107
|
+
the date with no year, which every date must have. A month is now read only when
|
|
108
|
+
the year it must precede follows it, so the epoch reading is the one left. The
|
|
109
|
+
same value under an extension calendar, `2 DATE _MYCAL 1900 _MYEPOCH`, was
|
|
110
|
+
rejected for the same reason and is now read too.
|
|
111
|
+
|
|
112
|
+
- **An unresolved pointer names the xref that failed, and lists nothing.** A `FAMC`
|
|
113
|
+
pointing at a family the file does not declare was reported as "Value for FAMC
|
|
114
|
+
should be in set [@F70@, @F75@, … 2853 more]" — the vocabulary of a closed set,
|
|
115
|
+
spent on ten families that were not the problem, ending in an admission that it
|
|
116
|
+
gave up. A pointer's set is the population of the document, so the diagnostic now
|
|
117
|
+
states the fact about the line: "No FAM record carries @F1450@". An enumeration
|
|
118
|
+
still lists its values, because for `SEX` the four of them are the whole answer.
|
|
119
|
+
Where the payload is not a pointer at all the message is unchanged — there the
|
|
120
|
+
shape is what is wrong, not the identifier.
|
|
121
|
+
|
|
122
|
+
## 1.8.2 - 2026-08-18
|
|
123
|
+
|
|
124
|
+
- **A tag written in mixed case is read as written, and the message says which tag
|
|
125
|
+
is meant.** `1 NoTe hello` was reported as "Unknown tag N" — the tag pattern held
|
|
126
|
+
upper case only, so it matched as far as `N` and `oTe hello` became the value, and
|
|
127
|
+
the reader was told about a tag that is nowhere in their file. The tag is now read
|
|
128
|
+
whole and the message says a tag is written in upper case, naming `NOTE`. Where
|
|
129
|
+
case is not the problem, `1 NOTEE` still reads as an unknown tag.
|
|
130
|
+
- **A line carrying no level is reported once.** The parser says a GEDCOM line must
|
|
131
|
+
begin with a level, and the validator no longer adds its own word about the tag of
|
|
132
|
+
a line that is not a line.
|
|
133
|
+
|
|
134
|
+
- **A tag declared twice in `HEAD.SCHMA` with different URIs is no longer reported.**
|
|
135
|
+
The specification permits it — "the schema structure may contain the same tag more
|
|
136
|
+
than once with different URIs" — and its own `extensions.ged` declares `_PARTY`
|
|
137
|
+
twice, once for a substructure and once for a record. The check now asks whether
|
|
138
|
+
the same tag was declared twice with the _same_ URI, which says nothing the first
|
|
139
|
+
declaration did not. Which of several URIs applies where a tag is used is a
|
|
140
|
+
separate question.
|
|
141
|
+
|
|
142
|
+
- **A line the lexer cannot read is reported once, in words about GEDCOM.** An xref
|
|
143
|
+
with a space in it — `0 @NoTe ref@ NOTE …` — produced "unexpected character:
|
|
144
|
+
->@<- at offset: 199, skipped 1 characters", which is true of the scanner and no
|
|
145
|
+
use to a reader. The lexer then resumed one character on, so the wreckage lexed
|
|
146
|
+
into a tag `N` and a value, and the line was reported a second time as an unknown
|
|
147
|
+
tag that is nowhere in the file. Such a line now yields one diagnostic, saying an
|
|
148
|
+
xref holds letters, digits and underscore between two @ marks, and its tokens are
|
|
149
|
+
dropped rather than read as something else. The lines around it are untouched.
|
|
150
|
+
- **An unknown tag at the top level says `root`** where it said `undefined`. The
|
|
151
|
+
message about a missing tag has said `root` all along.
|
|
152
|
+
- **A file whose lines end in CR is read as a file, not as one line.** 5.5.1 ends a
|
|
153
|
+
line with CR, LF, CR-LF or LF-CR; the lexer's terminator was `\r?\n` and the line
|
|
154
|
+
index counted only LF, so a CR-only document parsed to a single node, its version
|
|
155
|
+
was never found, and everything above the lexer worked from one line. All four
|
|
156
|
+
terminators now end a line, a two-character form counts as one, and positions come
|
|
157
|
+
out the same whichever is used.
|
|
158
|
+
|
|
159
|
+
- **A required tag missing from a childless parent is reported where it is
|
|
160
|
+
missing.** The position came from the parent of the first child, so a parent with
|
|
161
|
+
no children at all had no position to offer and the report landed on line 1. In a
|
|
162
|
+
105 707-line export that put ten `Missing required tag FORM in FILE` diagnostics
|
|
163
|
+
at the top of the document, a hundred thousand lines from the media references
|
|
164
|
+
they describe.
|
|
165
|
+
- **Six closed value sets in 5.5.1 are checked at last.** `QUAY 9`, `PEDI nonsense`,
|
|
166
|
+
`RESN whatever`, `ORDI maybe`, `STAT nonsense` under `FAMC` and `ADOP nonsense`
|
|
167
|
+
under `INDI.ADOP.FAMC` all passed, because 5.5.1 states these sets in its
|
|
168
|
+
primitive definitions rather than in an enumeration vocabulary, and a payload
|
|
169
|
+
type the field-type switch did not name was validated as free text. Each now
|
|
170
|
+
names its set in the scheme and is judged by the rule that already existed for
|
|
171
|
+
GEDCOM 7 enumerations. Every real export in the corpus already wrote these
|
|
172
|
+
correctly, so nothing new is reported there. Completion offers the same values
|
|
173
|
+
while one is typed, because it reads the same sets: a `PEDI` line in a 5.5.1
|
|
174
|
+
file now proposes `adopted`, `birth`, `foster` and `sealing`.
|
|
175
|
+
- **A BC year in a 5.5.1 file is read in the spellings exports write.** `1472 B.C.`
|
|
176
|
+
passed while `1472 BC`, `1472BC` and `1472B.C.` were reported, though `YEAR_GREG`
|
|
177
|
+
carries no epoch at all and the specification only says in prose that "(B.C.)"
|
|
178
|
+
appended to the year marks a date before the common era — pinning neither the
|
|
179
|
+
spelling nor the delimiter. Every spelling now reads as the same era, in the
|
|
180
|
+
validity check and in the leap-year rule alike, so `29 FEB 1000 BC` is no longer
|
|
181
|
+
judged by whether 1000 was a leap year. Free text stays reported: `609 BC Megiddo`
|
|
182
|
+
and `abt. 716 BC (or 725)` belong in `DATE_PHRASE` parentheses. GEDCOM 7 is
|
|
183
|
+
unchanged, where `epoch = %s"BCE"` says exactly what is allowed.
|
|
184
|
+
- **An age in a 5.5.1 file is no longer judged by GEDCOM 7's grammar.** `AGE <8y`
|
|
185
|
+
was reported while `AGE < 8y` passed, and `8Y` and `child` were reported while
|
|
186
|
+
`8y` and `CHILD` passed. One expression served both versions, and it was
|
|
187
|
+
written to v7's ABNF, where the delimiter after `<` or `>` is required and
|
|
188
|
+
`years = Integer %x79` pins the unit letter. 5.5.1 prints neither rule, and
|
|
189
|
+
real exports write both forms, so 5.5.1 ages are now read without a required
|
|
190
|
+
delimiter and without regard to case. v7 keeps its ABNF exactly.
|
|
191
|
+
|
|
5
192
|
## 1.8.1 - 2026-08-17
|
|
6
193
|
|
|
7
194
|
- **A note written as text in a 5.5.1 file is no longer reported.** `1 NOTE plain
|
package/README.md
CHANGED
|
@@ -73,22 +73,27 @@ const unresolved = errors.filter(
|
|
|
73
73
|
);
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
| Code | Member
|
|
77
|
-
| ----------------- |
|
|
78
|
-
| `VAL001` | `UnknownTag`
|
|
79
|
-
| `VAL002` | `MissingTag`
|
|
80
|
-
| `VAL003` | `MissingValue`
|
|
81
|
-
| `VAL004` | `IncorrectValue`
|
|
82
|
-
| `VAL005` | `ShouldBeSetValue`
|
|
83
|
-
| `VAL006` | `MissingRef`
|
|
84
|
-
| `VAL007` | `ManyOccurrences`
|
|
85
|
-
| `VAL008` | `UndocumentedTag`
|
|
86
|
-
| `VAL009` | `DuplicateDeclaration`
|
|
87
|
-
| `VAL010` | `EmptyEvent`
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
76
|
+
| Code | Member | Reported when |
|
|
77
|
+
| ----------------- | ----------------------- | ------------------------------------------------------------------------------ |
|
|
78
|
+
| `VAL001` | `UnknownTag` | a tag the schema does not define in this position |
|
|
79
|
+
| `VAL002` | `MissingTag` | a structure the schema requires is absent |
|
|
80
|
+
| `VAL003` | `MissingValue` | the payload is absent where one is required |
|
|
81
|
+
| `VAL004` | `IncorrectValue` | the payload does not match its data type |
|
|
82
|
+
| `VAL005` | `ShouldBeSetValue` | the payload is not one of the permitted values |
|
|
83
|
+
| `VAL006` | `MissingRef` | a pointer payload that is not written as a pointer |
|
|
84
|
+
| `VAL007` | `ManyOccurrences` | more occurrences than the cardinality allows |
|
|
85
|
+
| `VAL008` | `UndocumentedTag` | a GEDCOM 7 extension tag with no `HEAD`.`SCHMA` declaration |
|
|
86
|
+
| `VAL009` | `DuplicateDeclaration` | a tag declared more than once in `HEAD`.`SCHMA` |
|
|
87
|
+
| `VAL010` | `EmptyEvent` | an event with neither a payload nor substructures |
|
|
88
|
+
| `VAL011` | `UnsupportedVersion` | the version is one no schema in this package describes |
|
|
89
|
+
| `VAL012` | `UndeterminedVersion` | no version could be read from `HEAD`.`GEDC`.`VERS` |
|
|
90
|
+
| `VAL013` | `SubstitutedVersion` | a version checked against a different version's schema |
|
|
91
|
+
| `VAL014` | `ImpossibleDay` | a date naming a day its calendar does not have, such as `31 FEB` |
|
|
92
|
+
| `VAL015` | `PersonalAncestralFile` | a header naming `SYST` before `GEDC`, so the file is a Personal Ancestral File |
|
|
93
|
+
| `unresolved-xref` | `UnresolvedXref` | an xref naming no record of the required type |
|
|
94
|
+
| `invalid-level` | `InvalidLevel` | a level that cannot follow the line above it |
|
|
95
|
+
| `LEXER` | `Lexer` | the text could not be tokenized |
|
|
96
|
+
| `PARSER` | `Parser` | the tokens could not be assembled into a tree |
|
|
92
97
|
|
|
93
98
|
## Scripts
|
|
94
99
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* nothing here: the months worth offering depend on the calendar named a few
|
|
4
4
|
* tokens earlier, and the keywords depend on what the value opened with.
|
|
5
5
|
*/
|
|
6
|
+
import { GedcomScheme } from "../schemes/schema-types";
|
|
6
7
|
export type DateGrammar = "value" | "period" | "exact";
|
|
7
8
|
export interface DateSlot {
|
|
8
9
|
/** A calendar may be named here. */
|
|
@@ -14,4 +15,4 @@ export interface DateSlot {
|
|
|
14
15
|
/** Bare words the grammar allows here. */
|
|
15
16
|
keywords: string[];
|
|
16
17
|
}
|
|
17
|
-
export declare function dateSlot(typed: string, grammar: DateGrammar): DateSlot;
|
|
18
|
+
export declare function dateSlot(scheme: GedcomScheme, typed: string, grammar: DateGrammar): DateSlot;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GedcomError } from "../types/errors";
|
|
2
|
-
import { ASTNode
|
|
2
|
+
import { ASTNode } from "../parser";
|
|
3
3
|
import { type GedcomDialect, type VersionResolution } from "../validator/versionRegistry";
|
|
4
4
|
import { type GedcomCompletion } from "../completion/completion";
|
|
5
5
|
import type { Position } from "../types/position";
|
|
@@ -11,8 +11,7 @@ export interface CreateDocumentOptions {
|
|
|
11
11
|
}
|
|
12
12
|
export declare class GedcomDocument {
|
|
13
13
|
private nodes;
|
|
14
|
-
pointers
|
|
15
|
-
xRefs: Map<string, ASTToken[]>;
|
|
14
|
+
private pointers;
|
|
16
15
|
private errors;
|
|
17
16
|
private scheme;
|
|
18
17
|
private resolution;
|
|
@@ -29,7 +28,6 @@ export declare class GedcomDocument {
|
|
|
29
28
|
getVersionResolution(): VersionResolution | undefined;
|
|
30
29
|
getDialect(): GedcomDialect | undefined;
|
|
31
30
|
getCompletions(position: Position, lineText: string): GedcomCompletion[];
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
getNodes(_range?: Range): ASTNode[];
|
|
31
|
+
getErrors(): GedcomError[];
|
|
32
|
+
getNodes(): ASTNode[];
|
|
35
33
|
}
|