@domorium/validator 2.0.1 → 2.1.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 +12 -0
- package/README.md +119 -2
- package/dist/document/gedcomDocument.d.ts +9 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +321 -234
- package/dist/types/date.d.ts +21 -0
- package/dist/validator/date-551.d.ts +10 -0
- package/dist/validator/date-v7.d.ts +2 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@domorium/validator` are documented here.
|
|
4
4
|
|
|
5
|
+
## 2.1.0
|
|
6
|
+
|
|
7
|
+
- **`readDate(payload)` answers what a date says, not only whether it is
|
|
8
|
+
valid.** A consumer wanting the year of a birth had to parse the payload
|
|
9
|
+
again, and a regular expression that finds `1401` in `1401 BCE` or a year in
|
|
10
|
+
`ABT HEBREW 5761` hands on a wrong year as a fact. The document now returns
|
|
11
|
+
the parse its diagnostics apply: the modifier, each date with its calendar,
|
|
12
|
+
day, month, year and epoch, a 5.5.1 phrase and dual year, and the text. It
|
|
13
|
+
reads in the document's dialect, resolves a `SCHMA` alias of a month or a
|
|
14
|
+
calendar, and answers `null` for whatever the diagnostics reject as a date
|
|
15
|
+
value, including a day its month does not have (#378).
|
|
16
|
+
|
|
5
17
|
## 2.0.1
|
|
6
18
|
|
|
7
19
|
- **A 5.5.1 date names its calendar where the specification puts it.** 5.5.1
|
package/README.md
CHANGED
|
@@ -59,8 +59,58 @@ interface GedcomError {
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
| Field | Type | What it holds |
|
|
63
|
+
| --------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| `code` | `string`, a `GedcomErrorCode` | What kind of problem this is. Match on this. |
|
|
65
|
+
| `message` | `string` | Written for a person to read, and rewritten when a clearer wording is found. Do not match on it. |
|
|
66
|
+
| `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. |
|
|
68
|
+
| `range` | `Range` | Zero-based line and character, `end` exclusive, over the smallest text that is wrong — the payload, not the line. |
|
|
69
|
+
| `level` | `"error" \| "warning" \| "info"` | How much it matters. |
|
|
70
|
+
|
|
71
|
+
`data` is what makes a fix offerable rather than merely reportable: an
|
|
72
|
+
unresolved pointer reports the xref that failed and the record tag it needed, so
|
|
73
|
+
a host can offer to create that record.
|
|
74
|
+
|
|
75
|
+
For this file, where `SEX` carries a value its schema does not permit and `FAMC`
|
|
76
|
+
points at a record that is not there:
|
|
77
|
+
|
|
78
|
+
```gedcom
|
|
79
|
+
0 HEAD
|
|
80
|
+
1 GEDC
|
|
81
|
+
2 VERS 7.0
|
|
82
|
+
0 @I1@ INDI
|
|
83
|
+
1 NAME Lisa /Simpson/
|
|
84
|
+
1 SEX Q
|
|
85
|
+
1 FAMC @F9@
|
|
86
|
+
0 TRLR
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`getErrors()` answers:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
"code": "VAL005",
|
|
95
|
+
"message": "Value for SEX should be in set [F, M, U, X]",
|
|
96
|
+
"range": {
|
|
97
|
+
"start": { "line": 5, "character": 6 },
|
|
98
|
+
"end": { "line": 5, "character": 7 }
|
|
99
|
+
},
|
|
100
|
+
"level": "error"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"code": "unresolved-xref",
|
|
104
|
+
"message": "No FAM record carries @F9@",
|
|
105
|
+
"data": { "xref": "@F9@", "requiredRecordTag": "FAM" },
|
|
106
|
+
"range": {
|
|
107
|
+
"start": { "line": 6, "character": 7 },
|
|
108
|
+
"end": { "line": 6, "character": 11 }
|
|
109
|
+
},
|
|
110
|
+
"level": "error"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
```
|
|
64
114
|
|
|
65
115
|
Match on `code` rather than on `message`; messages are written for people and
|
|
66
116
|
change. `GedcomErrorCode` is exported for this:
|
|
@@ -95,6 +145,73 @@ const unresolved = errors.filter(
|
|
|
95
145
|
| `LEXER` | `Lexer` | the text could not be tokenized |
|
|
96
146
|
| `PARSER` | `Parser` | the tokens could not be assembled into a tree |
|
|
97
147
|
|
|
148
|
+
## Reading the tree
|
|
149
|
+
|
|
150
|
+
`getNodes()` returns the records — the level-0 nodes — each holding its
|
|
151
|
+
substructures in `children`. A node carries its line's tokens in a `tokens` map
|
|
152
|
+
keyed by `TokenNames`, and which key holds the text is the thing to get right:
|
|
153
|
+
|
|
154
|
+
| Token | Written as | Holds |
|
|
155
|
+
| --------- | ------------- | -------------------------------------- |
|
|
156
|
+
| `LEVEL` | `1` | The level, as it was written. |
|
|
157
|
+
| `POINTER` | `0 @I1@ INDI` | The xref a record declares for itself. |
|
|
158
|
+
| `TAG` | `1 NAME` | The tag. |
|
|
159
|
+
| `XREF` | `1 FAMC @F1@` | An xref a line points at. |
|
|
160
|
+
| `VALUE` | `1 NAME Lisa` | A payload that is not a pointer. |
|
|
161
|
+
|
|
162
|
+
`POINTER` and `XREF` are the pair worth reading twice: a record's own xref is
|
|
163
|
+
never in `XREF`, and a pointer payload is never in `VALUE`.
|
|
164
|
+
|
|
165
|
+
Pulling every person's name out of a file:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { GedcomDocument, TokenNames } from "@domorium/validator";
|
|
169
|
+
import type { ASTNode } from "@domorium/validator";
|
|
170
|
+
|
|
171
|
+
const document = new GedcomDocument().createDocument(gedcomString);
|
|
172
|
+
|
|
173
|
+
const payload = (node: ASTNode, tag: string): string | undefined =>
|
|
174
|
+
node.children.find((child) => child.tokens[TokenNames.TAG]?.value === tag)
|
|
175
|
+
?.tokens[TokenNames.VALUE]?.value;
|
|
176
|
+
|
|
177
|
+
for (const record of document.getNodes()) {
|
|
178
|
+
if (record.tokens[TokenNames.TAG]?.value !== "INDI") continue;
|
|
179
|
+
console.log({
|
|
180
|
+
xref: record.tokens[TokenNames.POINTER]?.value,
|
|
181
|
+
name: payload(record, "NAME"),
|
|
182
|
+
line: record.range.start.line,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
{ xref: '@I1@', name: 'Lisa /Simpson/', line: 3 }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`range` on a node or a token is computed from the offsets on every access, not
|
|
192
|
+
stored: a large file has millions of tokens and a stored range costs three
|
|
193
|
+
objects apiece. Read it freely at the edges, and use `startOffset` and
|
|
194
|
+
`endOffset` inside a loop over the whole document.
|
|
195
|
+
|
|
196
|
+
## What a document answers
|
|
197
|
+
|
|
198
|
+
Every method reads the document last given to `createDocument`, which returns
|
|
199
|
+
the same instance so the two can be chained.
|
|
200
|
+
|
|
201
|
+
| Method | Answers |
|
|
202
|
+
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
203
|
+
| `createDocument(text, options?)` | Parses and validates, and returns the document itself. |
|
|
204
|
+
| `getErrors()` | Every diagnostic, in the order found. |
|
|
205
|
+
| `getNodes()` | The records, each with its substructures. |
|
|
206
|
+
| `getVersion()` | The version read from `HEAD`.`GEDC`.`VERS`, as written. |
|
|
207
|
+
| `getDialect()` | Which rules were applied: `"5.5.1"` or `"7.0"`. |
|
|
208
|
+
| `getVersionResolution()` | How that was decided — `supported`, `substituted`, `unsupported`, `undetermined`, or `paf` for a file whose header names a writing program before its version. |
|
|
209
|
+
| `getLabel(node)` | The specification's name for a structure, for a UI to show instead of the tag: `HEAD` answers `Header`. |
|
|
210
|
+
| `getPointerTargetTag(node)` | The record tag a pointer line must name: `FAMC` answers `FAM`. |
|
|
211
|
+
| `isRecordDeclaration(node)` | Whether the node declares a record other lines can point at. |
|
|
212
|
+
| `getCompletions(position, lineText)` | What may be written at a position — `{ label, kind: "tag" \| "enum" \| "pointer", detail? }`. |
|
|
213
|
+
| `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. See `GedcomDateValue`. |
|
|
214
|
+
|
|
98
215
|
## Scripts
|
|
99
216
|
|
|
100
217
|
| Command | Description |
|
|
@@ -3,6 +3,7 @@ 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";
|
|
6
|
+
import type { GedcomDateValue } from "../types/date";
|
|
6
7
|
export interface CreateDocumentOptions {
|
|
7
8
|
/** Text that is part of a document rather than one of its own. */
|
|
8
9
|
fragment?: boolean;
|
|
@@ -14,6 +15,7 @@ export declare class GedcomDocument {
|
|
|
14
15
|
private pointers;
|
|
15
16
|
private errors;
|
|
16
17
|
private scheme;
|
|
18
|
+
private validatedAs;
|
|
17
19
|
private resolution;
|
|
18
20
|
private requiresSchmaDeclaration;
|
|
19
21
|
private extensions;
|
|
@@ -28,6 +30,13 @@ export declare class GedcomDocument {
|
|
|
28
30
|
getVersionResolution(): VersionResolution | undefined;
|
|
29
31
|
getDialect(): GedcomDialect | undefined;
|
|
30
32
|
getCompletions(position: Position, lineText: string): GedcomCompletion[];
|
|
33
|
+
/**
|
|
34
|
+
* Null wherever the document's own diagnostics would not call the payload a
|
|
35
|
+
* valid date value, and for one with nothing to read: an empty payload, a
|
|
36
|
+
* 5.5.1 date in a calendar the schema gives no months, or a document not
|
|
37
|
+
* checked against any dialect.
|
|
38
|
+
*/
|
|
39
|
+
readDate(value: string): GedcomDateValue | null;
|
|
31
40
|
getErrors(): GedcomError[];
|
|
32
41
|
getNodes(): ASTNode[];
|
|
33
42
|
}
|