@domorium/validator 2.2.0 → 2.3.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 +56 -0
- package/README.md +88 -38
- package/dist/document/gedcomDocument.d.ts +7 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.esm.js +655 -380
- package/dist/payload/escape.d.ts +4 -0
- package/dist/payload/fileExtensions.d.ts +2 -0
- package/dist/payload/filePath.d.ts +6 -0
- package/dist/types/errors.d.ts +38 -0
- package/dist/types/structure.d.ts +37 -0
- package/dist/validator/calendarDays.d.ts +2 -1
- package/dist/validator/checkPayload.d.ts +5 -0
- package/dist/validator/lineBreaks.d.ts +5 -0
- package/dist/validator/rule-node.d.ts +5 -0
- package/dist/validator/structureShape.d.ts +6 -0
- package/dist/validator/validate.d.ts +11 -1
- package/dist/validator/versionRegistry.d.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@domorium/validator` are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.3.0
|
|
6
|
+
|
|
7
|
+
- **What a structure may hold is answered without a document edit.**
|
|
8
|
+
`getStructureShape(node, tag?)` and `getStructureShapeAtPath(path)` give the
|
|
9
|
+
fields the dialect allows at a node or a schema path, with their cardinality
|
|
10
|
+
and payload kind; `checkPayload` judges one value without parsing the
|
|
11
|
+
document; `escapePayload` and `unescapePayload` write and read back a text
|
|
12
|
+
payload by dialect; `resolveValue` joins a value with its `CONT` and `CONC`
|
|
13
|
+
lines; `getValidatedDialect()` names the dialect the document was checked
|
|
14
|
+
against (#521).
|
|
15
|
+
- **A diagnostic carries the facts its message names in `data`.** `VAL014`
|
|
16
|
+
`{ day, month, monthLength }`, `VAL016` `{ year, calendar }`, `VAL002`
|
|
17
|
+
`{ missingTag, parentTag }`, `VAL001` `{ meantTag }` for a tag in the wrong
|
|
18
|
+
case, `VAL008` `{ tag }`, and `VAL012` `{ missingHeader }` when there is no
|
|
19
|
+
`HEAD` record, so a host can word the message in its reader's language. No
|
|
20
|
+
code, message or range changed (#502, #525).
|
|
21
|
+
- **A missing `TRLR` is reported on the last line**, where it is missing, not
|
|
22
|
+
on the first (#502).
|
|
23
|
+
- **A value is checked before it is written.** `checkPayload` refuses a line
|
|
24
|
+
break where the value may not span lines (`VAL017`), a value on a structure
|
|
25
|
+
that takes none (`VAL018`), an emptied GEDCOM 7 record that holds nothing
|
|
26
|
+
else (`VAL019`), and a GEDCOM 7 `FILE` path naming no file beside the
|
|
27
|
+
document (`VAL020`). These are answered for a value about to be written; a
|
|
28
|
+
file that already holds one gets no new diagnostic (#524, #527, #531).
|
|
29
|
+
- **A 5.5.1 `FORM` that is a known file extension is a warning.** `png`, `pdf`,
|
|
30
|
+
`mp4` and the like were the error `VAL005`, as any value outside
|
|
31
|
+
`[bmp gif jpg ole pcx tif wav]`; they are now the warning `VAL021`, and a
|
|
32
|
+
misspelling stays `VAL005`. `filePathProblem` and `KNOWN_FILE_EXTENSIONS` are
|
|
33
|
+
exported (#531).
|
|
34
|
+
- **A 5.5.1 `EVEN` is described as taking an optional description.** The
|
|
35
|
+
scheme gave the individual and the family `EVEN` no payload, so
|
|
36
|
+
`getStructureShape` answered the kind `none` for a line on which
|
|
37
|
+
`checkPayload` accepted `1 EVEN Appointed Zoning Committee Chairperson`.
|
|
38
|
+
Both now carry the payload type `type-EVENT_DESCRIPTOR`, read as text that
|
|
39
|
+
may be left out (#528).
|
|
40
|
+
- **GEDCOM 7 labels follow the specification.** The schema generator kept
|
|
41
|
+
whatever labels the schema already had, so the specification's later renames
|
|
42
|
+
never reached it. It now reads them from the specification's tag files: the
|
|
43
|
+
deprecated `ADR1`, `ADR2`, `ADR3` and `HEAD.SOUR.DATA` are labelled
|
|
44
|
+
`(deprecated) …`, the four Latter-day Saint ordinances are spelled as the
|
|
45
|
+
specification spells them, and the payload types gain labels. Labels the
|
|
46
|
+
specification does not give, such as `AFN` and the registered `EXID` types,
|
|
47
|
+
are kept.
|
|
48
|
+
|
|
49
|
+
## 2.2.1
|
|
50
|
+
|
|
51
|
+
- **A month or a calendar written as a `SCHMA` alias is read as the one it
|
|
52
|
+
stands for when a day is judged.** The date grammar resolved the alias and the
|
|
53
|
+
day-length check did not, so with `_APRX` declared as `month-APR`,
|
|
54
|
+
`31 _APRX 2000` passed and `readDate` answered it as 31 April; with `_JUL`
|
|
55
|
+
declared as `cal-JULIAN`, `_JUL 29 FEB 1900` carried `VAL014` although 1900 is
|
|
56
|
+
a Julian leap year, and read as `null`. The first now carries `VAL014`, naming
|
|
57
|
+
`31 APR`, and reads as `null`; the second carries nothing and reads as a
|
|
58
|
+
Julian date. GEDCOM 7 only: 5.5.1 has no `SCHMA`, and a document checked
|
|
59
|
+
against no dialect still resolves no alias (#448).
|
|
60
|
+
|
|
5
61
|
## 2.2.0
|
|
6
62
|
|
|
7
63
|
- **The lexer's mode stack no longer grows with the document.** Each line left
|
package/README.md
CHANGED
|
@@ -53,7 +53,21 @@ interface GedcomError {
|
|
|
53
53
|
code: string;
|
|
54
54
|
message: string;
|
|
55
55
|
hint?: string;
|
|
56
|
-
data?: {
|
|
56
|
+
data?: {
|
|
57
|
+
xref?: string;
|
|
58
|
+
requiredRecordTag?: string;
|
|
59
|
+
expectedLevel?: number;
|
|
60
|
+
missingTag?: string;
|
|
61
|
+
parentTag?: string;
|
|
62
|
+
missingHeader?: boolean;
|
|
63
|
+
day?: number;
|
|
64
|
+
month?: string;
|
|
65
|
+
monthLength?: number;
|
|
66
|
+
year?: number;
|
|
67
|
+
calendar?: string;
|
|
68
|
+
meantTag?: string;
|
|
69
|
+
tag?: string;
|
|
70
|
+
};
|
|
57
71
|
range: Range;
|
|
58
72
|
level: "error" | "warning" | "info";
|
|
59
73
|
}
|
|
@@ -64,13 +78,32 @@ interface GedcomError {
|
|
|
64
78
|
| `code` | `string`, a `GedcomErrorCode` | What kind of problem this is. Match on this. |
|
|
65
79
|
| `message` | `string` | Written for a person to read, and rewritten when a clearer wording is found. Do not match on it. |
|
|
66
80
|
| `hint` | `string?` | Declared for advice alongside the message. Nothing in this package sets it yet, so treat it as absent. |
|
|
67
|
-
| `data` | object? | The structured facts a quick fix is built from, where the diagnostic has any.
|
|
81
|
+
| `data` | object? | The structured facts a quick fix or a message of your own is built from, where the diagnostic has any. |
|
|
68
82
|
| `range` | `Range` | Zero-based line and character, `end` exclusive, over the smallest text that is wrong — the payload, not the line. |
|
|
69
83
|
| `level` | `"error" \| "warning" \| "info"` | How much it matters. |
|
|
70
84
|
|
|
71
85
|
`data` is what makes a fix offerable rather than merely reportable: an
|
|
72
86
|
unresolved pointer reports the xref that failed and the record tag it needed, so
|
|
73
|
-
a host can offer to create that record.
|
|
87
|
+
a host can offer to create that record. A missing `TRLR` names the tag, and a
|
|
88
|
+
document with no `HEAD` at all says so, which a header with no version does not.
|
|
89
|
+
|
|
90
|
+
It also holds the facts the message names, so a host can say them in its own
|
|
91
|
+
words and its reader's language without parsing the English:
|
|
92
|
+
|
|
93
|
+
| Code | `data` |
|
|
94
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `VAL014` | `day`, `month`, `monthLength`: `31`, `"FEB"`, `28` |
|
|
96
|
+
| `VAL016` | `year`, `calendar`: `0`, `"GREGORIAN"` |
|
|
97
|
+
| `VAL002` | `missingTag`, and `parentTag` except at the root: `"LANG"`, `"TRAN"` |
|
|
98
|
+
| `VAL001` | `meantTag`, only for a standard tag in the wrong case: `"NICK"` |
|
|
99
|
+
| `VAL008` | `tag`, the undeclared extension tag: `"_X"` |
|
|
100
|
+
| `VAL018` | `tag`, the structure that takes no value: `"MAP"` |
|
|
101
|
+
| `VAL019` | `tag`, the record a 7.0 empty value would leave with nothing: `"SNOTE"` |
|
|
102
|
+
| `VAL020` | `tag`, `pathProblem` — `"absolute"`, `"parent-folder"` or `"unescaped-character"`: `"FILE"`, `"parent-folder"` |
|
|
103
|
+
| `VAL021` | `tag`, the structure holding the form: `"FORM"` |
|
|
104
|
+
| `unresolved-xref` | `xref`, `requiredRecordTag`: `"@S9@"`, `"SOUR"` |
|
|
105
|
+
|
|
106
|
+
`checkPayload` keeps the same `data` on the problems it answers.
|
|
74
107
|
|
|
75
108
|
For this file, where `SEX` carries a value its schema does not permit and `FAMC`
|
|
76
109
|
points at a record that is not there:
|
|
@@ -123,28 +156,33 @@ const unresolved = errors.filter(
|
|
|
123
156
|
);
|
|
124
157
|
```
|
|
125
158
|
|
|
126
|
-
| Code | Member | Reported when
|
|
127
|
-
| ----------------- | ----------------------- |
|
|
128
|
-
| `VAL001` | `UnknownTag` | a tag the schema does not define in this position
|
|
129
|
-
| `VAL002` | `MissingTag` | a structure the schema requires is absent
|
|
130
|
-
| `VAL003` | `MissingValue` | the payload is absent where one is required
|
|
131
|
-
| `VAL004` | `IncorrectValue` | the payload does not match its data type
|
|
132
|
-
| `VAL005` | `ShouldBeSetValue` | the payload is not one of the permitted values
|
|
133
|
-
| `VAL006` | `MissingRef` | a pointer payload that is not written as a pointer
|
|
134
|
-
| `VAL007` | `ManyOccurrences` | more occurrences than the cardinality allows
|
|
135
|
-
| `VAL008` | `UndocumentedTag` | a GEDCOM 7 extension tag with no `HEAD`.`SCHMA` declaration
|
|
136
|
-
| `VAL009` | `DuplicateDeclaration` | a tag declared more than once in `HEAD`.`SCHMA`
|
|
137
|
-
| `VAL010` | `EmptyEvent` | an event with neither a payload nor substructures
|
|
138
|
-
| `VAL011` | `UnsupportedVersion` | the version is one no schema in this package describes
|
|
139
|
-
| `VAL012` | `UndeterminedVersion` | no version could be read from `HEAD`.`GEDC`.`VERS`
|
|
140
|
-
| `VAL013` | `SubstitutedVersion` | a version checked against a different version's schema
|
|
141
|
-
| `VAL014` | `ImpossibleDay` | a date naming a day its calendar does not have, such as `31 FEB`
|
|
142
|
-
| `VAL015` | `PersonalAncestralFile` | a header naming `SYST` before `GEDC`, so the file is a Personal Ancestral File
|
|
143
|
-
| `VAL016` | `ImpossibleYear` | a Gregorian or Julian date naming year 0, which neither calendar numbers
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
159
|
+
| Code | Member | Reported when |
|
|
160
|
+
| ----------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
161
|
+
| `VAL001` | `UnknownTag` | a tag the schema does not define in this position |
|
|
162
|
+
| `VAL002` | `MissingTag` | a structure the schema requires is absent |
|
|
163
|
+
| `VAL003` | `MissingValue` | the payload is absent where one is required |
|
|
164
|
+
| `VAL004` | `IncorrectValue` | the payload does not match its data type |
|
|
165
|
+
| `VAL005` | `ShouldBeSetValue` | the payload is not one of the permitted values |
|
|
166
|
+
| `VAL006` | `MissingRef` | a pointer payload that is not written as a pointer |
|
|
167
|
+
| `VAL007` | `ManyOccurrences` | more occurrences than the cardinality allows |
|
|
168
|
+
| `VAL008` | `UndocumentedTag` | a GEDCOM 7 extension tag with no `HEAD`.`SCHMA` declaration |
|
|
169
|
+
| `VAL009` | `DuplicateDeclaration` | a tag declared more than once in `HEAD`.`SCHMA` |
|
|
170
|
+
| `VAL010` | `EmptyEvent` | an event with neither a payload nor substructures |
|
|
171
|
+
| `VAL011` | `UnsupportedVersion` | the version is one no schema in this package describes |
|
|
172
|
+
| `VAL012` | `UndeterminedVersion` | no version could be read from `HEAD`.`GEDC`.`VERS` |
|
|
173
|
+
| `VAL013` | `SubstitutedVersion` | a version checked against a different version's schema |
|
|
174
|
+
| `VAL014` | `ImpossibleDay` | a date naming a day its calendar does not have, such as `31 FEB` |
|
|
175
|
+
| `VAL015` | `PersonalAncestralFile` | a header naming `SYST` before `GEDC`, so the file is a Personal Ancestral File |
|
|
176
|
+
| `VAL016` | `ImpossibleYear` | a Gregorian or Julian date naming year 0, which neither calendar numbers |
|
|
177
|
+
| `VAL017` | `SpansLines` | a line break in a value about to be written whose datatype keeps to one line |
|
|
178
|
+
| `VAL018` | `ValueNotAllowed` | a value about to be written for a structure the standard gives no value |
|
|
179
|
+
| `VAL019` | `EmptyStructure` | an empty value about to be written that would leave a 7.0 record with nothing |
|
|
180
|
+
| `VAL020` | `FileTargetUnsupported` | a 7.0 local file path about to be written that names no file beside the document |
|
|
181
|
+
| `VAL021` | `ToleratedForm` | a warning: a 5.5.1 multimedia `FORM` outside the standard's list that is a known file extension, such as `png` |
|
|
182
|
+
| `unresolved-xref` | `UnresolvedXref` | an xref naming no record of the required type |
|
|
183
|
+
| `invalid-level` | `InvalidLevel` | a level that cannot follow the line above it |
|
|
184
|
+
| `LEXER` | `Lexer` | the text could not be tokenized |
|
|
185
|
+
| `PARSER` | `Parser` | the tokens could not be assembled into a tree |
|
|
148
186
|
|
|
149
187
|
## Reading the tree
|
|
150
188
|
|
|
@@ -199,19 +237,31 @@ objects apiece. Read it freely at the edges, and use `startOffset` and
|
|
|
199
237
|
Every method reads the document last given to `createDocument`, which returns
|
|
200
238
|
the same instance so the two can be chained.
|
|
201
239
|
|
|
202
|
-
| Method
|
|
203
|
-
|
|
|
204
|
-
| `createDocument(text, options?)`
|
|
205
|
-
| `getErrors()`
|
|
206
|
-
| `getNodes()`
|
|
207
|
-
| `getVersion()`
|
|
208
|
-
| `getDialect()`
|
|
209
|
-
| `getVersionResolution()`
|
|
210
|
-
| `getLabel(node)`
|
|
211
|
-
| `getPointerTargetTag(node)`
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
240
|
+
| Method | Answers |
|
|
241
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
242
|
+
| `createDocument(text, options?)` | Parses and validates, and returns the document itself. |
|
|
243
|
+
| `getErrors()` | Every diagnostic, in the order found. |
|
|
244
|
+
| `getNodes()` | The records, each with its substructures. |
|
|
245
|
+
| `getVersion()` | The version read from `HEAD`.`GEDC`.`VERS`, as written. |
|
|
246
|
+
| `getDialect()` | Which rules were applied: `"5.5.1"` or `"7.0"`. |
|
|
247
|
+
| `getVersionResolution()` | How that was decided — `supported`, `substituted`, `unsupported`, `undetermined` (with `missingHeader` set when there is no `HEAD` at all), or `paf` for a file whose header names a writing program before its version. |
|
|
248
|
+
| `getLabel(node)` | The specification's name for a structure, for a UI to show instead of the tag: `HEAD` answers `Header`. |
|
|
249
|
+
| `getPointerTargetTag(node)` | The record tag a pointer line must name: `FAMC` answers `FAM`. |
|
|
250
|
+
| `getValidatedDialect()` | The dialect the text was checked against, a fragment given one by its options included — `getDialect()` answers nothing for that fragment. |
|
|
251
|
+
| `getStructureShape(node, tag?)` | What the dialect allows at a node, or beneath it under `tag` before it is in the text: its fields with their minimum, maximum, payload kind, whether a value may span lines and whether that is its ordinary case, and whether its own payload is required. An enumeration that takes a known file extension with a warning says `tolerates: "file-extension"`. Nothing for a document with no dialect. |
|
|
252
|
+
| `getStructureShapeAtPath(path)` | The same by a path of tags from a record, `["INDI", "NAME", "TRAN"]`, with no node at all. |
|
|
253
|
+
| `checkPayload(parent, tag, value, replacing?)` | The rules a single value breaks — an unknown tag, one more occurrence than allowed, a value outside an enumeration, a datatype failure, a 7.0 file path that names no file beside the document — answered without parsing anything. |
|
|
254
|
+
| `isRecordDeclaration(node)` | Whether the node declares a record other lines can point at. |
|
|
255
|
+
| `getCompletions(position, lineText)` | What may be written at a position — `{ label, kind: "tag" \| "enum" \| "pointer", detail? }`. |
|
|
256
|
+
| `readDate(payload)` | What a date payload says, read in the document's dialect — or `null` where the diagnostics would reject it as a date value. A document checked against no dialect reads by 5.5.1, then 7.0. See `GedcomDateValue`. |
|
|
257
|
+
|
|
258
|
+
`escapePayload(value, dialect, kind)` and `unescapePayload(...)` write and read the at sign of a text payload as
|
|
259
|
+
the dialect does — every one in 5.5.1, the first of each line in 7.0 — and leave a pointer, a date and the
|
|
260
|
+
other kinds as given.
|
|
261
|
+
|
|
262
|
+
`filePathProblem(path)` answers why a GEDCOM 7 local file reference — a value with no URI scheme; a caller sets URLs aside first — names no file beside the document —
|
|
263
|
+
`"absolute"`, `"parent-folder"` or `"unescaped-character"` — or nothing, and `KNOWN_FILE_EXTENSIONS` holds the
|
|
264
|
+
extensions a 5.5.1 `FORM` may name with a warning rather than an error.
|
|
215
265
|
|
|
216
266
|
## Scripts
|
|
217
267
|
|
|
@@ -4,6 +4,7 @@ import { type GedcomDialect, type VersionResolution } from "../validator/version
|
|
|
4
4
|
import { type GedcomCompletion } from "../completion/completion";
|
|
5
5
|
import type { Position } from "../types/position";
|
|
6
6
|
import type { GedcomDateValue } from "../types/date";
|
|
7
|
+
import type { PayloadProblem, StructureShape } from "../types/structure";
|
|
7
8
|
export interface CreateDocumentOptions {
|
|
8
9
|
/** Text that is part of a document rather than one of its own. */
|
|
9
10
|
fragment?: boolean;
|
|
@@ -24,10 +25,16 @@ export declare class GedcomDocument {
|
|
|
24
25
|
private applyScheme;
|
|
25
26
|
private validateLevels;
|
|
26
27
|
getLabel(node: ASTNode): string | undefined;
|
|
28
|
+
getStructureShape(node: ASTNode, tag?: string): StructureShape | undefined;
|
|
29
|
+
getStructureShapeAtPath(path: readonly string[]): StructureShape | undefined;
|
|
30
|
+
/** `replacing` is the occurrence a set overwrites; it does not count against the maximum. */
|
|
31
|
+
checkPayload(parent: ASTNode | readonly string[], tag: string, value: string, replacing?: ASTNode): PayloadProblem[] | undefined;
|
|
27
32
|
getPointerTargetTag(node: ASTNode): string | undefined;
|
|
28
33
|
isRecordDeclaration(node: ASTNode): boolean;
|
|
29
34
|
getVersion(): string | undefined;
|
|
30
35
|
getVersionResolution(): VersionResolution | undefined;
|
|
36
|
+
/** The dialect the text was checked against, a fragment's own included. */
|
|
37
|
+
getValidatedDialect(): GedcomDialect | undefined;
|
|
31
38
|
getDialect(): GedcomDialect | undefined;
|
|
32
39
|
getCompletions(position: Position, lineText: string): GedcomCompletion[];
|
|
33
40
|
/**
|