npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gedcom-ts

v2026.5.2

Published

TypeScript GEDCOM toolkit for browser apps: import .ged/.zip, edit typed genealogy data, and export GEDCOM or GEDZIP.

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

Installation

npm install gedcom-ts

Runtime 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:

  1. import a file with importGedFile (or start from createEmptyReadGed())
  2. read / mutate the typed Person, Act, Place, Note, MultimediaFile objects (directly or through editPerson / editAct / editPlace / …)
  3. 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 use actsNeedingGeocodeForCity.

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 markerKey

Harmonization (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 .ged file
  • accepts a .zip / .gdz archive containing one .ged + optional media files
  • throws Error(IMPORT_ERR_ZIP_GED_UNREADABLE) when the embedded .ged cannot 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_STRUCTUREFAMILY_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) === 1

remainingTypesAct(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);
  }
}