gedcom-ts
v2026.5.2
Published
TypeScript GEDCOM toolkit for browser apps: import .ged/.zip, edit typed genealogy data, and export GEDCOM or GEDZIP.
Maintainers
Readme
gedcom-ts
gedcom-ts is a browser-oriented TypeScript library to:
- import genealogy data from GEDCOM (
.ged) or ZIP (.zip/.gdz) - work with a typed JSON model (persons, acts, dates, places, notes, media, name variants, attributes)
- edit the model in-place through a fluent, chainable API (
editPerson,editAct, …) - export data back to GEDCOM (
.ged) or GEDZIP (.zip)
Live demo
A graphical demo showcasing the public API (import a .ged / .zip, browse the typed model, export back) is available at https://gedcomts.jaunet.me
Contents
Package
- NPM: gedcom-ts
- Version format CalVer
AAAA.M.micro(e.g.2026.5.2= May 2026). See CHANGELOG.md.
Installation
npm install gedcom-tsRuntime Requirements
- modern browser runtime (
File,Blob,XMLHttpRequest,URL.createObjectURL) - for pure Node.js usage, DOM polyfills are required (the library targets browsers)
Quick start
import { importGedFile, ExportGedzipFile } from "gedcom-ts";
async function roundTrip(file: File) {
const readGed = await importGedFile(file);
const persons = readGed.persons;
if (persons.length > 0) {
persons[0].lastname = persons[0].lastname.toUpperCase();
}
await new ExportGedzipFile("updated-tree", persons).download();
}Typical workflow:
- import a file with
importGedFile(or start fromcreateEmptyReadGed()) - read / mutate the typed
Person,Act,Place,Note,MultimediaFileobjects (directly or througheditPerson/editAct/editPlace/ …) - export as
.ged(ExportGedcomFile) or.zip(ExportGedzipFile)
Geocoding places
Use this section when your app needs GPS coordinates on event places, a list of cities still without coordinates, map markers, or a data-quality view for inconsistent place names.
You work with a ReadGed (after import) and a flat Act[] built from every person’s events (see Prepare act list).
How city names are matched
The library groups variants of the same place with citiesAreSimilar, not exact string equality.
| Label A | Label B | Same place? |
| ----------- | ------------------------------------ | --------------------------- |
| Pleurtuit | Pleurtuit, Ille-et-Vilaine, France | Yes |
| Paris | Paris, TX, USA | Depends on similarity rules |
Use the functions in the tables below for grouping, geocoding, and maps. Do not compare raw strings yourself, and do not use normalizeCityKey for grouping (spelling normalization only).
Prepare act list
import type { ReadGed, Act } from "gedcom-ts";
function collectAllActs(ged: ReadGed): Act[] {
const acts: Act[] = [];
for (const person of ged.persons) {
for (const act of person.acts.list) acts.push(act);
}
return acts;
}Geocode one city (typical flow)
When the user picks a city and a Nominatim result, update every similar act that still has no GPS — not only acts with the exact same label.
| Step | Function |
| ---------------------------------------------- | ----------------------------------------------------------- |
| 1. Context from the tree (countries, centroid) | inferGeocodeContext(ged) |
| 2. Search OpenStreetMap | searchPlacesWithContext(cityName, context) |
| 3. Acts to update | actsNeedingGeocodeForCity(allActs, cityName) |
| 4. Write coordinates | applyGeocodeCandidateToActs(targets, candidate, cityName) |
import {
inferGeocodeContext,
searchPlacesWithContext,
actsNeedingGeocodeForCity,
applyGeocodeCandidateToActs,
} from "gedcom-ts";
import type { ReadGed, Act } from "gedcom-ts";
async function geocodeCity(ged: ReadGed, allActs: Act[], cityName: string) {
const context = inferGeocodeContext(ged);
const candidates = await searchPlacesWithContext(cityName, context);
const chosen = candidates[0];
if (!chosen) return;
const targets = actsNeedingGeocodeForCity(allActs, cityName);
applyGeocodeCandidateToActs(targets, chosen, cityName);
}Tip: Do not pass only acts from a harmonization cluster to
applyGeocodeCandidateToActs. Longer labels (e.g.Pleurtuit, Ille-et-Vilaine, France) would stay without GPS. Always useactsNeedingGeocodeForCity.
List cities missing coordinates
One row per city; withoutCoordCount is how many acts still need GPS.
import { groupActsBySimilarCity } from "gedcom-ts";
const groups = groupActsBySimilarCity(allActs, { onlyWithoutCoordinates: true });
for (const group of groups) {
console.log(group.cityLabel, group.withoutCoordCount);
// group.acts — acts in this group without GPS
}Largest groups first (group.acts.length).
Map: one marker per city
import { clusterKeyForCity } from "gedcom-ts";
const markerKey = clusterKeyForCity(act.place?.city ?? "");
// merge markers that share the same markerKeyHarmonization (data quality, optional)
Use when spellings, GPS positions, or “some acts with / without GPS” disagree for the same similar city.
import { findHarmonizationClusters } from "gedcom-ts";
for (const cluster of findHarmonizationClusters(ged)) {
console.log(cluster.labels, cluster.coordVariants, cluster.actsWithoutCoord);
}After a full geocode via actsNeedingGeocodeForCity, you should not get a cluster that only reports missing GPS for that city.
Geocoding API cheat sheet
| Goal | Call |
| ------------------------- | ---------------------------------------------------------------- |
| Search | searchPlacesWithContext(city, inferGeocodeContext(ged)) |
| Acts to update on confirm | actsNeedingGeocodeForCity(acts, city) |
| Apply lat/lng | applyGeocodeCandidateToActs(targets, candidate, city) |
| Cities without GPS | groupActsBySimilarCity(acts, { onlyWithoutCoordinates: true }) |
| Map marker id | clusterKeyForCity(city) |
| Inconsistencies | findHarmonizationClusters(ged) |
Network: GET https://nominatim.openstreetmap.org/search?q=…&format=jsonv2 with a User-Agent (gedcom-ts/<version> (genealogy library)). For offline or mocked search, pass fetchFn in searchPlaces / searchPlacesWithContext options.
The legacy callback getCityCoordinates is deprecated — use the flow above.
API reference
Short description and a minimal snippet for each public export.
Importing a file
importGedFile(file: File): Promise<ReadGed>
Detects the format from the file MIME / extension and dispatches to the right reader.
- accepts a single
.gedfile - accepts a
.zip/.gdzarchive containing one.ged+ optional media files - throws
Error(IMPORT_ERR_ZIP_GED_UNREADABLE)when the embedded.gedcannot be decoded (typical cause: password-protected ZIP)
import { importGedFile } from "gedcom-ts";
const readGed = await importGedFile(fileInput.files![0]);
console.log(readGed.persons.length, readGed.datasetVersion);createEmptyReadGed(options?): ReadGed
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".
import { createEmptyReadGed } from "gedcom-ts";
const readGed = createEmptyReadGed();IMPORT_ERR_ZIP_GED_UNREADABLE: string
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.
import { importGedFile, IMPORT_ERR_ZIP_GED_UNREADABLE } from "gedcom-ts";
try {
await importGedFile(file);
} catch (error) {
if (error instanceof Error && error.message === IMPORT_ERR_ZIP_GED_UNREADABLE) {
alert("Please unzip the archive manually and import the .ged file.");
}
}ReadGed
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.
Notable members:
| Member | Description |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| persons: Person[] | Imported individuals. |
| mapPersons: Map<number, Person> | INDI (integer) → Person. |
| partnersMap: Map<number, Person[]> | Family id → spouses. |
| childsMap: Map<number, Person[]> | Family id → children. |
| placesMap: Map<string, Place> | City name → Place (first occurrence wins). |
| mapFiles: Map<string, File> | Relative path → media File (for ZIP imports). |
| datasetVersion: GedcomDatasetVersion | "7.0", "5.5" or "unknown". |
| preservedTopLevelRecords: string[] | Raw blocks for partial round-trip. |
| resolveIndividualPointer(raw) | Resolves @I12@, I12, @Homer_Simpson@, Homer_Simpson to a Person. |
| getChildrenForParent(parent) | All children attached to a parent (via FAMS). |
| getChildrenOfFamily(familyId) | Children of a single family. |
| groupPartners() | Rebuilds partnersMap / childsMap after editing links. |
| generateUniqueIndi() | Next free INDI number for new persons. |
| rehydratePlacesFromActs() | Rebuilds placesMap from act places (e.g. after manual graph edits). Use editReadGed(readGed).addPerson(...) to register persons so maps stay coherent. |
import { ReadGed } from "gedcom-ts";
function describe(readGed: ReadGed) {
return {
version: readGed.datasetVersion,
persons: readGed.persons.length,
places: readGed.placesMap.size,
};
}Exporting
ExportGedcomFile
Writes a GEDCOM 7 (.ged) file. Three call signatures are supported:
new ExportGedcomFile(persons);
new ExportGedcomFile(title, persons);
new ExportGedcomFile(title, persons, options);.download() triggers a browser download. .toString() returns the GEDCOM text.
import { ExportGedcomFile, type ReadGed } from "gedcom-ts";
function exportGed(readGed: ReadGed) {
new ExportGedcomFile("my-tree", readGed.persons, {
extraTopLevelRecords: readGed.preservedTopLevelRecords,
headLanguageTag: "fr-FR",
headCopyright: "© 2026 Family Archive",
headDestination: "https://gedcom.io/",
headSchemaTagDefs: [{ tag: "_FOO", uri: "https://example.com/foo" }],
}).download();
}ExportGedzipFile
Writes a .zip (GEDZIP) bundling the GEDCOM and all attached MultimediaFile payloads.
import { ExportGedzipFile, Person } from "gedcom-ts";
async function exportZip(persons: Person[]) {
await new ExportGedzipFile("my-tree", persons).download();
}GedcomExportOptions
Options shared by both exporters:
| Option | Effect |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| extraTopLevelRecords | Raw 0 … blocks re-emitted before SUBM / TRLR (round-trip with readGed.preservedTopLevelRecords). |
| headLanguageTag | BCP 47 tag for HEAD.LANG (defaults to en-US). |
| headCopyright | One-line 1 COPR notice. |
| headDestination | Value of HEAD.DEST (target app / URI). |
| headSchemaTagDefs | Extension tag definitions emitted as HEAD.SCHMA / 2 TAG …. |
Domain model
Person, Sex
import { Person, Sex } from "gedcom-ts";
const person = new Person();
person.INDI = 1;
person.SEX = Sex.M; // M | F | U | X
person.firstnames = ["Jean"];
person.lastname = "DUPONT";
person.addMultimedia(/* MultimediaFile */);
person.deleteMultimedia("1/photo.jpg");Key fields: INDI, sosa, SEX, firstnames, lastname, FAMC, FAMS, acts, notes, multimediaFiles, nameVariants, attributes.
PersonGedcomImportOptions (type)
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.
PersonNameVariant, PersonNameTranslation
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.
import { PersonNameVariant, PersonNameTranslation } from "gedcom-ts";
const variant = new PersonNameVariant("Jean /Dupont/", "BIRTH");
variant.parts.push({ level: "2", tag: "GIVN", value: "Jean" });
variant.translations.push(new PersonNameTranslation("ジャン /デュポン/", "jp"));IndiAttribute, IndiGedcomSubLine
Generic level-1 individual attributes (FACT, DSCR, CAST, EDUC, OCCU, RELI, TITL, RESN, …) with their sub-lines preserved (IndiGedcomSubLine = { level; tag; value }).
import { IndiAttribute } from "gedcom-ts";
const occ = new IndiAttribute("OCCU", "Blacksmith", [
{ level: "2", tag: "DATE", value: "1820" },
]);Act, Acts, TypeAct, ActConstructionOptions
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). Event-level notes live on act.notes (2 NOTE / 3 CONT in GEDCOM); they are distinct from person.notes (1 NOTE on INDI) and round-trip through ZIP export/import.
import { Act, Acts, Identifier, type TypeAct } from "gedcom-ts";
const acts = new Acts();
const type: TypeAct = Identifier.BIRT;
acts.add(new Act(type));
acts.sortByDate();ActConstructionOptions controls the extras when building an Act manually:
import { Act, DateAct, Identifier } from "gedcom-ts";
const act = new Act(
Identifier.EVEN,
new DateAct("12 JAN 1901"),
null,
null,
null,
undefined,
undefined,
{
evenDescription: "Won a medal",
evenTypeLabel: "Award",
sdateAct: new DateAct("13 JAN 1901"),
eventPhrases: ["family gathering"],
preservedSubrecordPrefix: [],
preservedSubrecordSuffix: [],
},
);GEDCOM_7_ALL_EVENT_TAGS, GEDCOM_7_EVENT_SORT_ORDER, GEDCOM_7_EVENT_TAG_SET, GEDCOM_7_PAIR_UNION_EVENT_TAGS, Gedcom7EventTag, Gedcom7PairUnionEventTag
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. GEDCOM_7_PAIR_UNION_EVENT_TAGS lists tags commonly used when creating a family union via createMarriageFamily (ENGA, MARB, MARC, MARL, MARR, MARS).
import {
GEDCOM_7_ALL_EVENT_TAGS,
GEDCOM_7_EVENT_SORT_ORDER,
GEDCOM_7_EVENT_TAG_SET,
type Gedcom7EventTag,
} from "gedcom-ts";
const options: Gedcom7EventTag[] = [...GEDCOM_7_ALL_EVENT_TAGS];
const isEvent = GEDCOM_7_EVENT_TAG_SET.has("MARR");
const sorted = [...GEDCOM_7_EVENT_SORT_ORDER];DateAct, Day, Month, dateToDateLine, days, months, TypeDateActSimpleQualifier
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.
import {
DateAct,
Day,
Month,
dateToDateLine,
days,
months,
} from "gedcom-ts";
const day: Day = 12;
const month: Month = months[0]; // JAN
const dateLine = dateToDateLine(1901, month, day); // "12 JAN 1901"
const dateAct = new DateAct(dateLine);
const formattedDate = dateAct.date;
const knownDaysCount = days.length;TypeDateActSimpleQualifier is the union of single-anchor qualifiers (BEF | ABT | AFT | INT | EST | CAL) accepted by DateAct.updateQualifiedDate and by DateActEdit.setQualified.
Place, CoordinateGPS
import { Place, CoordinateGPS } from "gedcom-ts";
const place = new Place("Paris, FR", new CoordinateGPS(48.8566, 2.3522));
place.setFromGedcom7PlacPayload("Paris, Île-de-France, France");
const payload = place.toGedcom7PlacPayload(); // "Paris, , Île-de-France, France"Place understands the City, County, State, Country GEDCOM 7 list (1 to 4+ segments) and exposes placPhrase for 3 PHRASE under PLAC (import + export).
MultimediaFile, MultimediaFiles
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.
import { MultimediaFile, MultimediaFiles } from "gedcom-ts";
const bucket = new MultimediaFiles();
bucket.relativePath = "1/BIRT";
bucket.add(new MultimediaFile(new File(["img"], "birth.jpg")));
const remote = new MultimediaFile(undefined, "https://example.com/p.jpg");
const isExportable = remote.hasExportablePayload();Note, Notes, TypeNote
import { Notes, Note, Identifier, type TypeNote } from "gedcom-ts";
const typeNote: TypeNote = Identifier.CONT;
const note = new Note();
note.updateType(typeNote);
note.updateLines(["first line", "second line"]);
const notes = new Notes();
notes.addNote(note);
notes.removeFromIndex(0);Identifier (and deprecated Identificator)
Enum of every GEDCOM tag the library refers to (INDI, BIRT, MARR, DATE, PLAC, …). Prefer Identifier; Identificator is kept as a deprecated alias.
import { Identifier } from "gedcom-ts";
const birthTag = Identifier.BIRT;EventsByYears, ActsByYear
Group acts by year, deduplicating per individual. Handy to build chronological timelines.
import { EventsByYears, ActsByYear } from "gedcom-ts";
const grouped = new EventsByYears(person.acts.list);
for (const bucket of grouped.events) {
console.log(bucket.year, bucket.list.length);
}
const yearBucket = new ActsByYear(1901);Edit layer (in-place, chainable)
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.
import { editPerson, editDateAct, DateAct } from "gedcom-ts";
editPerson(person)
.setLastname("Dupont")
.setFirstnames(["Jean", "Marie"])
.acts()
.at(0)
.setDateAct(new DateAct("1 JAN 1900"));
editDateAct(person.acts.list[0].dateAct!).setExactDate(1900, "JAN", 1);| Helper / class | Purpose |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| editPerson(person) / PersonEdit | Identity, FAMS / FAMC / removeFams*, nameVariants() / attributes() / multimedia(), bulk clear*, entry points to acts() and notes(). |
| editActs(acts) / ActsEdit | add, addNew, insertAt, insertNewAt, replaceAt, removeAt, removeAct, removeLast, clear, sortByDate, at, indexOfAct. |
| editAct(act) / ActEdit | setType, setIndis, dates, place, EVEN fields, preserved lines, notes(), multimedia(), clearNotes, clearMultimedia. |
| editDateAct(dateAct) / DateActEdit | clear, applyGedcomPayload, setExactDate, setQualified, setBetween, setFromTo, setTime, setDatePhrase / appendDatePhrase, setVerbatimPayload. |
| editPlace(place) / PlaceEdit | setFromGedcom7Payload, setCity, setCounty, setState, setCountry, setPlacPhrase / appendPlacPhrase / clearPlacPhrase, setCoordinates / clearCoordinates / replaceCoordinateModel, clearStructured. |
| editNotes(notes) / NotesEdit | add, addNew, insertAt, insertNewAt, replaceAt, removeAt, removeNote, removeLast, clear, at, indexOfNote. |
Dataset editing: ReadGed, graph, clone, export options, validation
Higher-level helpers complement the per-object edit facades:
import type { GedcomExportOptions } from "gedcom-ts";
import {
createEmptyReadGed,
createPersonStub,
DateAct,
Sex,
editReadGed,
editGedcomExportOptions,
clonePerson,
cloneAct,
createMarriageFamily,
linkChildToFamily,
removeFamilyReferencesFromDataset,
addPersonToReadGed,
validateReadGed,
Identifier,
Act,
} from "gedcom-ts";
const readGed = createEmptyReadGed();
const p = createPersonStub(readGed.generateUniqueIndi(), { sex: Sex.M, firstnames: ["Jean"], lastname: "Dupont" });
editReadGed(readGed).addPerson(p);
p.acts.add(new Act(Identifier.BIRT, new DateAct("1900")));
editReadGed(readGed).preserved().append("0 @S42@ SOUR Custom");
const opts: GedcomExportOptions = {};
editGedcomExportOptions(opts)
.setHeadCopyright("© 2026")
.setExtraTopLevelRecords([...readGed.preservedTopLevelRecords]);
const twin = clonePerson(p, readGed.generateUniqueIndi());
const birthCopy = cloneAct(p.acts.list[0]!);
// const fam = createMarriageFamily(readGed, spouseA, spouseB, { dateAct: new DateAct("1 JAN 2000") }, { eventTag: Identifier.ENGA });
// linkChildToFamily(readGed, child, fam);
// removeFamilyReferencesFromDataset(readGed, fam);
validateReadGed(readGed, {
checkMarrParticipants: true,
checkFamcWithoutSpouses: true,
checkFamsWithoutSpouses: true,
checkDuplicateFamsEntries: true,
checkAncestorCycles: true,
});Commands (invariants before mutation)
tryAddPersonToReadGed, tryRemovePersonFromReadGedByIndi, tryLinkChildToFamily, tryUnlinkChildFromFamily, and tryCreateMarriageFamily return a CommandResult (ok + issues or value + optional warnings). editReadGed(...).addPerson uses these checks internally (throws on blocking errors). For UI or transactional flows, prefer the try* APIs and inspect commandBlockingIssues.
| Export | Role |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| editReadGed(readGed) / ReadGedEdit | addPerson, removePersonByIndi, preserved() → PreservedTopLevelEdit (append, insertAt, replaceAt, removeAt, clear) on preservedTopLevelRecords. Low-level helpers: addPersonToReadGed, removePersonFromReadGedByIndi. |
| editGedcomExportOptions(opts) / GedcomExportOptionsEdit | Fluent setters for extraTopLevelRecords, headLanguageTag, headCopyright, headDestination, headSchemaTagDefs. |
| clonePerson(person, newIndi) / cloneAct(act) / person.clone / act.clone | Deep copies for templates or undo stacks. |
| nextFamilyId(persons) | Next internal family id F. |
| createMarriageFamily, linkChildToFamily, unlinkChildFromFamily, removeFamilyReferencesFromDataset | Create family F and spouse acts; optional { eventTag } (default MARR, also ENGA, banns, contract, EVEN + CreateActInit, …). See GEDCOM_7_PAIR_UNION_EVENT_TAGS. |
| validateReadGed, assertReadGedConsistent, validatePerson, assertPersonConsistent | Typed code / severity (error | warn). Options: checkMarrParticipants, checkDuplicateIndis, checkFamcWithoutSpouses, checkFamsWithoutSpouses, checkDuplicateFamsEntries, checkAncestorCycles. Assertions: failOn (default: error). |
| tryAddPersonToReadGed, tryRemovePersonFromReadGedByIndi, tryLinkChildToFamily, tryUnlinkChildFromFamily, tryCreateMarriageFamily, commandBlockingIssues | Command layer with CommandResult / validate*Command prechecks. |
Utilities
createSosaMap(root, partnersMap)
Computes a Sosa numbering (Ahnentafel) starting at root (Sosa 1) and recursively walking ancestors via partnersMap.
import { createSosaMap, Person } from "gedcom-ts";
const sosaMap = createSosaMap(root, readGed.partnersMap);
// e.g. sosaMap.get(root.INDI) === 1remainingTypesAct(acts, actToUpdate?)
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.
import { remainingTypesAct, Acts } from "gedcom-ts";
const available = remainingTypesAct(new Acts());getCityCoordinates(cityName, callback) (deprecated)
Legacy callback API. Use Geocoding places instead.
resolveDatasetVersion(headerLines) / GedcomDatasetVersion
Inspects the lines of a 0 HEAD block and returns "7.0", "5.5" or "unknown". Useful to branch UI behaviour for legacy datasets.
import { resolveDatasetVersion } from "gedcom-ts";
const version = resolveDatasetVersion(headLines); // "7.0" | "5.5" | "unknown"guessMediaFormFromUri(uri)
Best-effort media type detection from a path/URI (jpg, png, gif, webp, mp3, mp4, pdf, …). Used internally to fill OBJE.FILE.FORM.
import { guessMediaFormFromUri } from "gedcom-ts";
guessMediaFormFromUri("https://example.com/photo.jpg"); // "image/jpeg"extractPersonNameVariants(personLines) / extractIndiAttributes(personLines)
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.
import { extractPersonNameVariants, extractIndiAttributes } from "gedcom-ts";
const variants = extractPersonNameVariants(rawIndiLines);
const attributes = extractIndiAttributes(rawIndiLines);selectPrimaryNameVariant(variants)
Picks the most relevant 1 NAME block: priority to 2 TYPE BIRTH, then to a name with a /surname/ payload, otherwise the first variant.
import { selectPrimaryNameVariant } from "gedcom-ts";
const primary = selectPrimaryNameVariant(person.nameVariants);GEDCOM_LIBRARY_VERSION
CalVer string written in exported HEAD.SOUR.VERS (same value as the npm package version, e.g. 2026.5.2).
import { GEDCOM_LIBRARY_VERSION } from "gedcom-ts";
console.log(`gedcom-ts ${GEDCOM_LIBRARY_VERSION}`);Error handling
import { importGedFile, IMPORT_ERR_ZIP_GED_UNREADABLE } from "gedcom-ts";
try {
const readGed = await importGedFile(file);
console.log(readGed.persons.length);
} catch (error) {
if (error instanceof Error && error.message === IMPORT_ERR_ZIP_GED_UNREADABLE) {
console.error("Encrypted ZIP: please extract the .ged manually.");
} else {
console.error("GED import failed:", error);
}
}