gedcom-ts 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/README.md +373 -92
  3. package/dist/commons/Act.d.ts +43 -6
  4. package/dist/commons/DateAct.d.ts +75 -0
  5. package/dist/commons/Identificator.enum.d.ts +5 -131
  6. package/dist/commons/Identifier.enum.d.ts +134 -0
  7. package/dist/commons/IndiAttribute.d.ts +11 -0
  8. package/dist/commons/IndiGedcomSubLine.d.ts +6 -0
  9. package/dist/commons/MultimediaFile.d.ts +13 -2
  10. package/dist/commons/Note.d.ts +2 -2
  11. package/dist/commons/Person.d.ts +21 -3
  12. package/dist/commons/PersonNameVariant.d.ts +21 -0
  13. package/dist/commons/Place.d.ts +19 -0
  14. package/dist/commons/gedcomEventTags.d.ts +10 -0
  15. package/dist/edit/ActEdit.d.ts +29 -0
  16. package/dist/edit/ActsEdit.d.ts +17 -0
  17. package/dist/edit/DateActEdit.d.ts +37 -0
  18. package/dist/edit/NotesEdit.d.ts +14 -0
  19. package/dist/edit/PersonEdit.d.ts +27 -0
  20. package/dist/edit/PlaceEdit.d.ts +23 -0
  21. package/dist/edit/index.d.ts +19 -0
  22. package/dist/export/GEDCOM.d.ts +33 -7
  23. package/dist/import/LoadFile.d.ts +14 -0
  24. package/dist/import/ReadGed.d.ts +48 -0
  25. package/dist/import/SplitedInformations.d.ts +29 -3
  26. package/dist/index.cjs +1 -1
  27. package/dist/index.d.ts +25 -9
  28. package/dist/index.mjs +1 -1
  29. package/dist/utils/gedcom/actExtraction.d.ts +8 -0
  30. package/dist/utils/gedcom/datasetVersion.d.ts +7 -0
  31. package/dist/utils/gedcom/extractIndiNamesAndAttributes.d.ts +4 -0
  32. package/dist/utils/gedcom/labelKeyedRecords.d.ts +13 -0
  33. package/dist/utils/gedcom/mediaFormFromUri.d.ts +2 -0
  34. package/dist/utils/gedcom/parseStandaloneObje.d.ts +9 -0
  35. package/dist/utils/gedcom/personName.d.ts +13 -0
  36. package/dist/utils/gedcom/pointers.d.ts +19 -0
  37. package/dist/utils/multimedia/collectRelativeMediaPaths.d.ts +2 -0
  38. package/dist/version.d.ts +6 -0
  39. package/package.json +4 -3
package/README.md CHANGED
@@ -2,13 +2,19 @@
2
2
 
3
3
  `gedcom-ts` is a browser-oriented TypeScript library to:
4
4
 
5
- - import genealogy data from GEDCOM (`.ged`) or ZIP (`.zip`)
6
- - work with a typed JSON model (persons, acts, notes, media, places)
7
- - export data back to GEDCOM (`.ged`) or ZIP (`.zip`)
5
+ - import genealogy data from GEDCOM (`.ged`) or ZIP (`.zip` / `.gdz`)
6
+ - work with a typed JSON model (persons, acts, dates, places, notes, media, name variants, attributes)
7
+ - edit the model in-place through a fluent, chainable API (`editPerson`, `editAct`, …)
8
+ - export data back to GEDCOM (`.ged`) or GEDZIP (`.zip`)
9
+
10
+ ## Live demo
11
+
12
+ A graphical demo showcasing the public API (import a `.ged` / `.zip`, browse the typed model, export back) is available at **[https://gedcomts.jaunet.me](https://gedcomts.jaunet.me)**
8
13
 
9
14
  ## Project
10
15
 
11
16
  - NPM package: [gedcom-ts](https://www.npmjs.com/package/gedcom-ts)
17
+ - **GEDCOM 7 roadmap** (spec gedcom.io, coverage matrix, prioritized gaps): [docs/GEDCOM7-roadmap.md](docs/GEDCOM7-roadmap.md)
12
18
 
13
19
  ## Installation
14
20
 
@@ -19,58 +25,143 @@ npm install gedcom-ts
19
25
  ## Runtime Requirements
20
26
 
21
27
  - modern browser runtime (`File`, `Blob`, `XMLHttpRequest`, `URL.createObjectURL`)
22
- - for pure Node.js usage, DOM polyfills are required
28
+ - for pure Node.js usage, DOM polyfills are required (the library targets browsers)
23
29
 
24
- ## Import
30
+ ## Quick start
25
31
 
26
32
  ```ts
27
- import { importGedFile } from "gedcom-ts";
33
+ import { importGedFile, ExportGedzipFile } from "gedcom-ts";
28
34
 
29
- async function loadGenealogy(file: File) {
35
+ async function roundTrip(file: File) {
30
36
  const readGed = await importGedFile(file);
31
- return readGed.persons;
37
+ const persons = readGed.persons;
38
+
39
+ if (persons.length > 0) {
40
+ persons[0].lastname = persons[0].lastname.toUpperCase();
41
+ }
42
+
43
+ await new ExportGedzipFile("updated-tree", persons).download();
32
44
  }
33
45
  ```
34
46
 
35
- `importGedFile(file)` accepts:
47
+ Typical workflow:
48
+
49
+ 1. import a file with `importGedFile` (or start from `createEmptyReadGed()`)
50
+ 2. read / mutate the typed `Person`, `Act`, `Place`, `Note`, `MultimediaFile` objects (directly or through `editPerson` / `editAct` / `editPlace` / …)
51
+ 3. export as `.ged` (`ExportGedcomFile`) or `.zip` (`ExportGedzipFile`)
52
+
53
+ ## Public API reference
54
+
55
+ Everything exported from `gedcom-ts` is documented below with a short description and a minimal usage snippet. The full list mirrors the public exports of `src/index.ts`.
36
56
 
37
- - a GED file (`.ged`)
38
- - a ZIP containing one GED file + optional media files
57
+ ### Importing a file
39
58
 
40
- It returns a `ReadGed` instance with parsed persons, families, notes, acts, media and maps.
59
+ #### `importGedFile(file: File): Promise<ReadGed>`
41
60
 
42
- ## Utilisation
61
+ Detects the format from the file MIME / extension and dispatches to the right reader.
62
+
63
+ - accepts a single `.ged` file
64
+ - accepts a `.zip` / `.gdz` archive containing one `.ged` + optional media files
65
+ - throws `Error(IMPORT_ERR_ZIP_GED_UNREADABLE)` when the embedded `.ged` cannot be decoded (typical cause: password-protected ZIP)
43
66
 
44
67
  ```ts
45
- import { ReadGed, Person, createSosaMap } from "gedcom-ts";
68
+ import { importGedFile } from "gedcom-ts";
46
69
 
47
- function buildViewModel(readGed: ReadGed, root: Person) {
48
- const persons = readGed.persons;
49
- const personByIndi = readGed.mapPersons;
50
- const sosaMap = createSosaMap(root, readGed.partnersMap);
51
- return { persons, personByIndi, sosaMap };
70
+ const readGed = await importGedFile(fileInput.files![0]);
71
+ console.log(readGed.persons.length, readGed.datasetVersion);
72
+ ```
73
+
74
+ #### `createEmptyReadGed(options?): ReadGed`
75
+
76
+ Creates an empty graph with the same runtime shape as a successful import (empty `persons`, initialized `mapPersons` / `partnersMap` / `childsMap` / `placesMap` / `mapFiles`). Use it to start a brand-new tree without parsing a file. `datasetVersion` is set to `"7.0"`.
77
+
78
+ ```ts
79
+ import { createEmptyReadGed } from "gedcom-ts";
80
+
81
+ const readGed = createEmptyReadGed();
82
+ ```
83
+
84
+ #### `IMPORT_ERR_ZIP_GED_UNREADABLE: string`
85
+
86
+ Sentinel error message thrown by `importGedFile` when a `.ged` inside a ZIP cannot be decoded (typically because the ZIP is password-protected). Compare with `error.message === IMPORT_ERR_ZIP_GED_UNREADABLE` to display a tailored message.
87
+
88
+ ```ts
89
+ import { importGedFile, IMPORT_ERR_ZIP_GED_UNREADABLE } from "gedcom-ts";
90
+
91
+ try {
92
+ await importGedFile(file);
93
+ } catch (error) {
94
+ if (error instanceof Error && error.message === IMPORT_ERR_ZIP_GED_UNREADABLE) {
95
+ alert("Please unzip the archive manually and import the .ged file.");
96
+ }
52
97
  }
53
98
  ```
54
99
 
55
- Typical workflow:
100
+ #### `ReadGed`
101
+
102
+ Result of an import. Top-level GEDCOM records not modeled into the typed graph (`OBJE`, `REPO`, `SOUR`, `SUBM`, …) are kept in order on `readGed.preservedTopLevelRecords` for round-trip export.
103
+
104
+ Notable members:
105
+
106
+ | Member | Description |
107
+ | --- | --- |
108
+ | `persons: Person[]` | Imported individuals. |
109
+ | `mapPersons: Map<number, Person>` | `INDI` (integer) → `Person`. |
110
+ | `partnersMap: Map<number, Person[]>` | Family id → spouses. |
111
+ | `childsMap: Map<number, Person[]>` | Family id → children. |
112
+ | `placesMap: Map<string, Place>` | City name → `Place` (first occurrence wins). |
113
+ | `mapFiles: Map<string, File>` | Relative path → media `File` (for ZIP imports). |
114
+ | `datasetVersion: GedcomDatasetVersion` | `"7.0"`, `"5.5"` or `"unknown"`. |
115
+ | `preservedTopLevelRecords: string[]` | Raw blocks for partial round-trip. |
116
+ | `resolveIndividualPointer(raw)` | Resolves `@I12@`, `I12`, `@Homer_Simpson@`, `Homer_Simpson` to a `Person`. |
117
+ | `getChildrenForParent(parent)` | All children attached to a parent (via `FAMS`). |
118
+ | `getChildrenOfFamily(familyId)` | Children of a single family. |
119
+ | `groupPartners()` | Rebuilds `partnersMap` / `childsMap` after editing links. |
120
+ | `generateUniqueIndi()` | Next free `INDI` number for new persons. |
56
121
 
57
- 1. import a file with `importGedFile`
58
- 2. read and update `Person` objects
59
- 3. export as `.ged` or `.zip`
122
+ ```ts
123
+ import { ReadGed } from "gedcom-ts";
124
+
125
+ function describe(readGed: ReadGed) {
126
+ return {
127
+ version: readGed.datasetVersion,
128
+ persons: readGed.persons.length,
129
+ places: readGed.placesMap.size,
130
+ };
131
+ }
132
+ ```
133
+
134
+ ### Exporting
60
135
 
61
- ## Export
136
+ #### `ExportGedcomFile`
62
137
 
63
- ### GED (`.ged`)
138
+ Writes a GEDCOM 7 (`.ged`) file. Three call signatures are supported:
64
139
 
65
140
  ```ts
66
- import { ExportGedcomFile, Person } from "gedcom-ts";
141
+ new ExportGedcomFile(persons);
142
+ new ExportGedcomFile(title, persons);
143
+ new ExportGedcomFile(title, persons, options);
144
+ ```
145
+
146
+ `.download()` triggers a browser download. `.toString()` returns the GEDCOM text.
67
147
 
68
- function exportGed(persons: Person[]) {
69
- new ExportGedcomFile("my-tree", persons).download();
148
+ ```ts
149
+ import { ExportGedcomFile, type ReadGed } from "gedcom-ts";
150
+
151
+ function exportGed(readGed: ReadGed) {
152
+ new ExportGedcomFile("my-tree", readGed.persons, {
153
+ extraTopLevelRecords: readGed.preservedTopLevelRecords,
154
+ headLanguageTag: "fr-FR",
155
+ headCopyright: "© 2026 Family Archive",
156
+ headDestination: "https://gedcom.io/",
157
+ headSchemaTagDefs: [{ tag: "_FOO", uri: "https://example.com/foo" }],
158
+ }).download();
70
159
  }
71
160
  ```
72
161
 
73
- ### ZIP (`.zip`) GED + media
162
+ #### `ExportGedzipFile`
163
+
164
+ Writes a `.zip` (GEDZIP) bundling the GEDCOM and all attached `MultimediaFile` payloads.
74
165
 
75
166
  ```ts
76
167
  import { ExportGedzipFile, Person } from "gedcom-ts";
@@ -80,37 +171,132 @@ async function exportZip(persons: Person[]) {
80
171
  }
81
172
  ```
82
173
 
83
- ## Public API with Examples
174
+ #### `GedcomExportOptions`
175
+
176
+ Options shared by both exporters:
84
177
 
85
- Everything exported from `src/index.ts` is shown below with a minimal usage snippet.
178
+ | Option | Effect |
179
+ | --- | --- |
180
+ | `extraTopLevelRecords` | Raw `0 …` blocks re-emitted before `SUBM` / `TRLR` (round-trip with `readGed.preservedTopLevelRecords`). |
181
+ | `headLanguageTag` | BCP 47 tag for `HEAD`.`LANG` (defaults to `en-US`). |
182
+ | `headCopyright` | One-line `1 COPR` notice. |
183
+ | `headDestination` | Value of `HEAD`.`DEST` (target app / URI). |
184
+ | `headSchemaTagDefs` | Extension tag definitions emitted as `HEAD`.`SCHMA` / `2 TAG …`. |
86
185
 
87
- ### Person, Sex
186
+ ### Domain model
187
+
188
+ #### `Person`, `Sex`
88
189
 
89
190
  ```ts
90
191
  import { Person, Sex } from "gedcom-ts";
91
192
 
92
193
  const person = new Person();
93
194
  person.INDI = 1;
94
- person.SEX = Sex.M;
195
+ person.SEX = Sex.M; // M | F | U | X
95
196
  person.firstnames = ["Jean"];
96
197
  person.lastname = "DUPONT";
198
+
199
+ person.addMultimedia(/* MultimediaFile */);
200
+ person.deleteMultimedia("1/photo.jpg");
201
+ ```
202
+
203
+ Key fields: `INDI`, `sosa`, `SEX`, `firstnames`, `lastname`, `FAMC`, `FAMS`, `acts`, `notes`, `multimediaFiles`, `nameVariants`, `attributes`.
204
+
205
+ #### `PersonGedcomImportOptions` (type)
206
+
207
+ Optional hints consumed by `Person.createPersonJson` when re-parsing a single individual block (label-keyed pointers for `FAMS` / `FAMC` / `NOTE`, and standalone `OBJE` payloads). Useful when assembling a graph manually outside `ReadGed`.
208
+
209
+ #### `PersonNameVariant`, `PersonNameTranslation`
210
+
211
+ Lossless representation of every `1 NAME` block of an individual (type, `NPFX`/`GIVN`/`SURN`/… parts, `TRAN` translations). `Person.nameVariants` keeps them in order so alias / translation data survive an import → export round-trip.
212
+
213
+ ```ts
214
+ import { PersonNameVariant, PersonNameTranslation } from "gedcom-ts";
215
+
216
+ const variant = new PersonNameVariant("Jean /Dupont/", "BIRTH");
217
+ variant.parts.push({ level: "2", tag: "GIVN", value: "Jean" });
218
+ variant.translations.push(new PersonNameTranslation("ジャン /デュポン/", "jp"));
97
219
  ```
98
220
 
99
- ### Act, Acts, TypeAct
221
+ #### `IndiAttribute`, `IndiGedcomSubLine`
222
+
223
+ Generic level-1 individual attributes (`FACT`, `DSCR`, `CAST`, `EDUC`, `OCCU`, `RELI`, `TITL`, `RESN`, …) with their sub-lines preserved (`IndiGedcomSubLine = { level; tag; value }`).
100
224
 
101
225
  ```ts
102
- import { Act, Acts, TypeAct, Identificator } from "gedcom-ts";
226
+ import { IndiAttribute } from "gedcom-ts";
227
+
228
+ const occ = new IndiAttribute("OCCU", "Blacksmith", [
229
+ { level: "2", tag: "DATE", value: "1820" },
230
+ ]);
231
+ ```
232
+
233
+ #### `Act`, `Acts`, `TypeAct`, `ActConstructionOptions`
234
+
235
+ `Act` models an individual or family event (BIRT, MARR, etc.). `Acts` is the ordered collection on a `Person`. `TypeAct` is the union of every supported GEDCOM 7 event tag (= `Gedcom7EventTag`).
236
+
237
+ ```ts
238
+ import { Act, Acts, Identifier, type TypeAct } from "gedcom-ts";
103
239
 
104
- const actType: TypeAct = Identificator.BIRT;
105
- const act = new Act(actType);
106
240
  const acts = new Acts();
107
- acts.add(act);
241
+ const type: TypeAct = Identifier.BIRT;
242
+ acts.add(new Act(type));
243
+ acts.sortByDate();
108
244
  ```
109
245
 
110
- ### DateAct, Day, Month, dateToDateLine, days, months
246
+ `ActConstructionOptions` controls the extras when building an `Act` manually:
111
247
 
112
248
  ```ts
113
- import { DateAct, Day, Month, dateToDateLine, days, months } from "gedcom-ts";
249
+ import { Act, DateAct, Identifier } from "gedcom-ts";
250
+
251
+ const act = new Act(
252
+ Identifier.EVEN,
253
+ new DateAct("12 JAN 1901"),
254
+ null,
255
+ null,
256
+ null,
257
+ undefined,
258
+ undefined,
259
+ {
260
+ evenDescription: "Won a medal",
261
+ evenTypeLabel: "Award",
262
+ sdateAct: new DateAct("13 JAN 1901"),
263
+ eventPhrases: ["family gathering"],
264
+ preservedSubrecordPrefix: [],
265
+ preservedSubrecordSuffix: [],
266
+ },
267
+ );
268
+ ```
269
+
270
+ #### `GEDCOM_7_ALL_EVENT_TAGS`, `GEDCOM_7_EVENT_SORT_ORDER`, `GEDCOM_7_EVENT_TAG_SET`, `Gedcom7EventTag`
271
+
272
+ Canonical lists of GEDCOM 7 event tags (`INDIVIDUAL_EVENT_STRUCTURE` ∪ `FAMILY_EVENT_STRUCTURE`, LDS ordinances excluded). Use them to build UI selects or guard custom logic.
273
+
274
+ ```ts
275
+ import {
276
+ GEDCOM_7_ALL_EVENT_TAGS,
277
+ GEDCOM_7_EVENT_SORT_ORDER,
278
+ GEDCOM_7_EVENT_TAG_SET,
279
+ type Gedcom7EventTag,
280
+ } from "gedcom-ts";
281
+
282
+ const options: Gedcom7EventTag[] = [...GEDCOM_7_ALL_EVENT_TAGS];
283
+ const isEvent = GEDCOM_7_EVENT_TAG_SET.has("MARR");
284
+ const sorted = [...GEDCOM_7_EVENT_SORT_ORDER];
285
+ ```
286
+
287
+ #### `DateAct`, `Day`, `Month`, `dateToDateLine`, `days`, `months`, `TypeDateActSimpleQualifier`
288
+
289
+ GEDCOM dates with full GEDCOM 7 / 5.5 support, including `INT`, `EST`, `CAL`, `BET … AND …`, `FROM … TO …`, ISO `YYYY-MM-DD`, `3 PHRASE` under `DATE`, `3 TIME`, and a verbatim fallback for unparseable payloads.
290
+
291
+ ```ts
292
+ import {
293
+ DateAct,
294
+ Day,
295
+ Month,
296
+ dateToDateLine,
297
+ days,
298
+ months,
299
+ } from "gedcom-ts";
114
300
 
115
301
  const day: Day = 12;
116
302
  const month: Month = months[0]; // JAN
@@ -120,132 +306,227 @@ const formattedDate = dateAct.date;
120
306
  const knownDaysCount = days.length;
121
307
  ```
122
308
 
123
- ### MultimediaFile, MultimediaFiles
309
+ `TypeDateActSimpleQualifier` is the union of single-anchor qualifiers (`BEF | ABT | AFT | INT | EST | CAL`) accepted by `DateAct.updateQualifiedDate` and by `DateActEdit.setQualified`.
310
+
311
+ #### `Place`, `CoordinateGPS`
124
312
 
125
313
  ```ts
126
- import { MultimediaFile, MultimediaFiles } from "gedcom-ts";
314
+ import { Place, CoordinateGPS } from "gedcom-ts";
127
315
 
128
- const bucket = new MultimediaFiles();
129
- bucket.relativePath = "1/BIRT";
130
- bucket.add(new MultimediaFile(new File(["img"], "birth.jpg")));
316
+ const place = new Place("Paris, FR", new CoordinateGPS(48.8566, 2.3522));
317
+ place.setFromGedcom7PlacPayload("Paris, Île-de-France, France");
318
+ const payload = place.toGedcom7PlacPayload(); // "Paris, , Île-de-France, France"
131
319
  ```
132
320
 
133
- ### Place, CoordinateGPS
321
+ `Place` understands the `City, County, State, Country` GEDCOM 7 list (1 to 4+ segments) and exposes `placPhrase` for `3 PHRASE` under `PLAC` (import + export).
322
+
323
+ #### `MultimediaFile`, `MultimediaFiles`
324
+
325
+ Wraps either a local `File`, an external URI (`sourceUri`) or an OBJE pointer (`objeXrefId`) so the same model can survive a GED / ZIP round-trip.
134
326
 
135
327
  ```ts
136
- import { Place, CoordinateGPS } from "gedcom-ts";
328
+ import { MultimediaFile, MultimediaFiles } from "gedcom-ts";
137
329
 
138
- const place = new Place("Paris, FR", new CoordinateGPS(48.8566, 2.3522));
330
+ const bucket = new MultimediaFiles();
331
+ bucket.relativePath = "1/BIRT";
332
+ bucket.add(new MultimediaFile(new File(["img"], "birth.jpg")));
333
+
334
+ const remote = new MultimediaFile(undefined, "https://example.com/p.jpg");
335
+ const isExportable = remote.hasExportablePayload();
139
336
  ```
140
337
 
141
- ### Notes, Note, TypeNote
338
+ #### `Note`, `Notes`, `TypeNote`
142
339
 
143
340
  ```ts
144
- import { Notes, Note, TypeNote, Identificator } from "gedcom-ts";
341
+ import { Notes, Note, Identifier, type TypeNote } from "gedcom-ts";
145
342
 
146
- const typeNote: TypeNote = Identificator.CONT;
343
+ const typeNote: TypeNote = Identifier.CONT;
147
344
  const note = new Note();
148
345
  note.updateType(typeNote);
149
346
  note.updateLines(["first line", "second line"]);
347
+
150
348
  const notes = new Notes();
151
349
  notes.addNote(note);
350
+ notes.removeFromIndex(0);
152
351
  ```
153
352
 
154
- ### Identificator
353
+ #### `Identifier` (and deprecated `Identificator`)
354
+
355
+ Enum of every GEDCOM tag the library refers to (`INDI`, `BIRT`, `MARR`, `DATE`, `PLAC`, …). Prefer `Identifier`; `Identificator` is kept as a deprecated alias.
155
356
 
156
357
  ```ts
157
- import { Identificator } from "gedcom-ts";
358
+ import { Identifier } from "gedcom-ts";
158
359
 
159
- const birthTag = Identificator.BIRT;
360
+ const birthTag = Identifier.BIRT;
160
361
  ```
161
362
 
162
- ### ExportGedcomFile, ExportGedzipFile
363
+ #### `EventsByYears`, `ActsByYear`
364
+
365
+ Group acts by year, deduplicating per individual. Handy to build chronological timelines.
163
366
 
164
367
  ```ts
165
- import { ExportGedcomFile, ExportGedzipFile, Person } from "gedcom-ts";
368
+ import { EventsByYears, ActsByYear } from "gedcom-ts";
369
+
370
+ const grouped = new EventsByYears(person.acts.list);
371
+ for (const bucket of grouped.events) {
372
+ console.log(bucket.year, bucket.list.length);
373
+ }
166
374
 
167
- const persons: Person[] = [];
168
- new ExportGedcomFile("tree", persons).download();
169
- await new ExportGedzipFile("tree", persons).download();
375
+ const yearBucket = new ActsByYear(1901);
170
376
  ```
171
377
 
172
- ### ReadGed
378
+ ### Edit layer (in-place, chainable)
379
+
380
+ Wrap an existing model object to mutate it with a fluent API. Every method returns `this`, so you can chain. `.value` exposes the underlying target.
173
381
 
174
382
  ```ts
175
- import { ReadGed } from "gedcom-ts";
383
+ import { editPerson, editDateAct, DateAct } from "gedcom-ts";
176
384
 
177
- function useReadGed(readGed: ReadGed) {
178
- return {
179
- persons: readGed.persons,
180
- places: readGed.placesMap,
181
- };
182
- }
385
+ editPerson(person)
386
+ .setLastname("Dupont")
387
+ .setFirstnames(["Jean", "Marie"])
388
+ .acts()
389
+ .at(0)
390
+ .setDateAct(new DateAct("1 JAN 1900"));
391
+
392
+ editDateAct(person.acts.list[0].dateAct!).setExactDate(1900, "JAN", 1);
183
393
  ```
184
394
 
185
- ### importGedFile
395
+ | Helper / class | Purpose |
396
+ | --- | --- |
397
+ | `editPerson(person)` / `PersonEdit` | identity (`setLastname`, `setFirstnames`, `setSex`, `setSosa`), links (`setFamc`, `setFams`, `clearFams`, `appendFams`), name variants (`setNameVariants`), attributes (`setAttributes`), entry points to `acts()` and `notes()`. |
398
+ | `editActs(acts)` / `ActsEdit` | `add`, `replaceAt`, `removeAt`, `clear`, `sortByDate`, `at(index)`. |
399
+ | `editAct(act)` / `ActEdit` | `setType`, `setIndis`, `setDateAct`, `setSdateAct`, `setPlace`, `setEvenDescription`, `setEvenTypeLabel`, `setEventPhrases` / `appendEventPhrase` / `clearEventPhrases`, plus `setPreservedPrefix` / `Suffix` helpers. |
400
+ | `editDateAct(dateAct)` / `DateActEdit` | `clear`, `applyGedcomPayload`, `setExactDate`, `setQualified`, `setBetween`, `setFromTo`, `setTime`, `setDatePhrase` / `appendDatePhrase`, `setVerbatimPayload`. |
401
+ | `editPlace(place)` / `PlaceEdit` | `setFromGedcom7Payload`, `setCity`, `setCounty`, `setState`, `setCountry`, `setPlacPhrase` / `appendPlacPhrase` / `clearPlacPhrase`, `setCoordinates` / `clearCoordinates` / `replaceCoordinateModel`, `clearStructured`. |
402
+ | `editNotes(notes)` / `NotesEdit` | `add`, `replaceAt`, `removeAt`, `clear`. |
186
403
 
187
- ```ts
188
- import { importGedFile } from "gedcom-ts";
404
+ ### Utilities
189
405
 
190
- const readGed = await importGedFile(fileInput.files![0]);
191
- ```
406
+ #### `createSosaMap(root, partnersMap)`
192
407
 
193
- ### createSosaMap
408
+ Computes a Sosa numbering (Ahnentafel) starting at `root` (Sosa 1) and recursively walking ancestors via `partnersMap`.
194
409
 
195
410
  ```ts
196
411
  import { createSosaMap, Person } from "gedcom-ts";
197
412
 
198
- const root = new Person();
199
- root.INDI = 1;
200
- const sosaMap = createSosaMap(root, new Map());
413
+ const sosaMap = createSosaMap(root, readGed.partnersMap);
414
+ // e.g. sosaMap.get(root.INDI) === 1
201
415
  ```
202
416
 
203
- ### remainingTypesAct
417
+ #### `remainingTypesAct(acts, actToUpdate?)`
418
+
419
+ Returns the list of event types still allowed for a person’s `Acts`, enforcing the “unique per individual” rule for `BIRT` / `DEAT` / `BURI` / `CHR` while keeping every other tag selectable. Pass the currently edited act as `actToUpdate` so its own type stays in the list when re-opening a form.
204
420
 
205
421
  ```ts
206
422
  import { remainingTypesAct, Acts } from "gedcom-ts";
207
423
 
208
- const availableTypes = remainingTypesAct(new Acts());
424
+ const available = remainingTypesAct(new Acts());
209
425
  ```
210
426
 
211
- ### EventsByYears, ActsByYear
427
+ #### `getCityCoordinates(cityName, callback)`
428
+
429
+ Queries OpenStreetMap’s Nominatim API and returns a list of candidate `Place` objects with coordinates. Browser-only (uses `XMLHttpRequest`).
212
430
 
213
431
  ```ts
214
- import { EventsByYears, ActsByYear } from "gedcom-ts";
432
+ import { getCityCoordinates } from "gedcom-ts";
215
433
 
216
- const grouped = new EventsByYears([]);
217
- const yearBucket = new ActsByYear(1901);
218
- const eventsByYear = grouped.events;
219
- const selectedYear = yearBucket.year;
434
+ getCityCoordinates("Paris", (places) => {
435
+ console.log(places[0]?.coordinate.latitude, places[0]?.coordinate.longitude);
436
+ });
220
437
  ```
221
438
 
222
- ## End-to-End Example
439
+ #### `resolveDatasetVersion(headerLines)` / `GedcomDatasetVersion`
440
+
441
+ Inspects the lines of a `0 HEAD` block and returns `"7.0"`, `"5.5"` or `"unknown"`. Useful to branch UI behaviour for legacy datasets.
223
442
 
224
443
  ```ts
225
- import { importGedFile, ExportGedzipFile } from "gedcom-ts";
444
+ import { resolveDatasetVersion } from "gedcom-ts";
445
+
446
+ const version = resolveDatasetVersion(headLines); // "7.0" | "5.5" | "unknown"
447
+ ```
448
+
449
+ #### `guessMediaFormFromUri(uri)`
450
+
451
+ Best-effort media type detection from a path/URI (jpg, png, gif, webp, mp3, mp4, pdf, …). Used internally to fill `OBJE`.`FILE`.`FORM`.
452
+
453
+ ```ts
454
+ import { guessMediaFormFromUri } from "gedcom-ts";
455
+
456
+ guessMediaFormFromUri("https://example.com/photo.jpg"); // "image/jpeg"
457
+ ```
458
+
459
+ #### `extractPersonNameVariants(personLines)` / `extractIndiAttributes(personLines)`
460
+
461
+ Low-level parsers used by `Person.createPersonJson`. They turn the raw GEDCOM lines of a single `INDI` record into structured `PersonNameVariant[]` / `IndiAttribute[]`. Reuse them when parsing custom GEDCOM fragments outside `ReadGed`.
462
+
463
+ ```ts
464
+ import { extractPersonNameVariants, extractIndiAttributes } from "gedcom-ts";
465
+
466
+ const variants = extractPersonNameVariants(rawIndiLines);
467
+ const attributes = extractIndiAttributes(rawIndiLines);
468
+ ```
469
+
470
+ #### `selectPrimaryNameVariant(variants)`
471
+
472
+ Picks the most relevant `1 NAME` block: priority to `2 TYPE BIRTH`, then to a name with a `/surname/` payload, otherwise the first variant.
473
+
474
+ ```ts
475
+ import { selectPrimaryNameVariant } from "gedcom-ts";
476
+
477
+ const primary = selectPrimaryNameVariant(person.nameVariants);
478
+ ```
479
+
480
+ #### `GEDCOM_LIBRARY_VERSION`
481
+
482
+ The library version embedded in the exported `HEAD`.`SOUR`.`VERS`. Kept aligned with the `version` field of `package.json` (a test enforces it).
483
+
484
+ ```ts
485
+ import { GEDCOM_LIBRARY_VERSION } from "gedcom-ts";
486
+
487
+ console.log(`gedcom-ts ${GEDCOM_LIBRARY_VERSION}`);
488
+ ```
489
+
490
+ ## End-to-end example
491
+
492
+ ```ts
493
+ import {
494
+ importGedFile,
495
+ ExportGedzipFile,
496
+ editPerson,
497
+ DateAct,
498
+ Identifier,
499
+ } from "gedcom-ts";
226
500
 
227
501
  async function importModifyExport(file: File) {
228
502
  const readGed = await importGedFile(file);
229
503
  const persons = readGed.persons;
230
504
 
231
505
  if (persons.length > 0) {
232
- persons[0].lastname = persons[0].lastname.toUpperCase();
506
+ editPerson(persons[0])
507
+ .setLastname(persons[0].lastname.toUpperCase())
508
+ .acts()
509
+ .at(0)
510
+ .setDateAct(new DateAct("1 JAN 1900"));
233
511
  }
234
512
 
235
513
  await new ExportGedzipFile("updated-tree", persons).download();
236
514
  }
237
515
  ```
238
516
 
239
- ## Error Handling
517
+ ## Error handling
240
518
 
241
519
  ```ts
242
- import { importGedFile } from "gedcom-ts";
520
+ import { importGedFile, IMPORT_ERR_ZIP_GED_UNREADABLE } from "gedcom-ts";
243
521
 
244
522
  try {
245
523
  const readGed = await importGedFile(file);
246
- const personsCount = readGed.persons.length;
524
+ console.log(readGed.persons.length);
247
525
  } catch (error) {
248
- console.error("GED import failed:", error);
526
+ if (error instanceof Error && error.message === IMPORT_ERR_ZIP_GED_UNREADABLE) {
527
+ console.error("Encrypted ZIP: please extract the .ged manually.");
528
+ } else {
529
+ console.error("GED import failed:", error);
530
+ }
249
531
  }
250
532
  ```
251
-