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

@domorium/language-service

v2.1.0

Published

Editor-independent GEDCOM language features

Readme

@domorium/language-service

npm License: MIT

Editor-independent GEDCOM language features built on @domorium/validator. It can power browser editors, IDE extensions, and note-taking plugins without depending on the Language Server Protocol runtime.

Part of Domorium — GEDCOM editor tooling for the browser, Obsidian, VS Code, and JetBrains IDEs.

Install

npm install @domorium/language-service

Usage

import { GedcomLanguageService } from "@domorium/language-service";

const gedcomText = "0 HEAD\n1 GEDC\n2 VERS 7.0\n0 TRLR\n";

const service = new GedcomLanguageService(gedcomText);
const diagnostics = service.getDiagnostics();

The service also provides completion, hover, definitions, folding ranges, document symbols, semantic tokens, indentation hints, and the edits that retarget a file a document points at. getDocument() returns the parse behind all of it.

The quick fixes for a pointer that names nothing

getCodeActions answers what a reader can do about a diagnostic. For an XREF that names no record, it offers in this order:

const service = new GedcomLanguageService(
  "0 HEAD\n1 GEDC\n2 VERS 7.0\n0 @I1@ INDI\n1 NAME Ada\n0 @F1@ FAM\n1 WIFE @I9@\n0 TRLR\n",
);
const [unresolved] = service
  .getDiagnostics()
  .filter(({ code }) => code === "unresolved-xref");

service.getCodeActions(unresolved.range, [unresolved], 0);
// "Create INDI record @I9@"      — the record the author named, first
// "Replace with @I1@ — Ada"      — only where one candidate is plausibly meant
// "Point at nothing (@VOID@)"    — GEDCOM 7 only, last

A replacement appears only where one candidate is nearer to the XREF than every other and within two edits of it. Where two are equally near, none is offered: applying the wrong one attaches a record to an unrelated one and the document then validates clean. Every record of the required type is reachable through completion inside @…@ instead, filtered as the reader types.

The record a pointer names

getRecordPreview answers what an XREF points at, so a host can show it where the reader points — a tooltip, a popover, an LSP hover.

const family = new GedcomLanguageService(
  "0 HEAD\n1 GEDC\n2 VERS 7.0\n0 @I1@ INDI\n1 FAMS @F1@\n0 @F1@ FAM\n1 HUSB @I1@\n0 TRLR\n",
);

// Character 9 of line 4 is inside the `@F1@` of `1 FAMS @F1@`.
const preview = family.getRecordPreview(
  { line: 4, character: 9 },
  { maxLines: 24 },
);
// preview.range   — line 5 character 0 through line 6 character 11, the FAM record
// preview.pointer — line 4 characters 7 to 11, the pointer pointed at
// preview.truncated — false; the record fitted in the lines asked for

It answers null where the position is not on a pointer, where the pointer names no record in this document, and where the record is declared on the line being pointed at — there, showing it would tell the reader nothing.

Ranges rather than text: the caller already holds the document, and slicing it is cheaper than a second copy of the same bytes. maxLines is what the host has room for, and truncated says whether the record outran it.

The media a line refers to

getMediaAt answers, for one position, the file that line refers to — the payload as written, how to read it, what the format says the file is, the caption the author gave it, and the rectangle a multimedia link asks for.

const album = new GedcomLanguageService(
  [
    "0 HEAD",
    "1 GEDC",
    "2 VERS 7.0",
    "0 @I1@ INDI",
    "1 OBJE @O1@",
    "2 CROP",
    "3 TOP 10",
    "3 LEFT 20",
    "3 HEIGHT 100",
    "3 WIDTH 200",
    "0 @O1@ OBJE",
    "1 FILE media/family.jpg",
    "2 FORM image/jpeg",
    "2 TITL The Simpson family",
    "0 TRLR",
  ].join("\n"),
);

// Character 9 of line 4 is inside the `@O1@` of `1 OBJE @O1@`.
const media = album.getMediaAt({ line: 4, character: 9 });
// media.targetText — "media/family.jpg", as the document wrote it
// media.kind       — "file-relative"; also "http" and "file-absolute"
// media.mediaKind  — "image"; also "audio", "video", "document", "unknown"
// media.title      — "The Simpson family", the caption
// media.crop       — { top: 10, left: 20, height: 100, width: 200 }
// media.range      — line 11 characters 7 to 23, the file's own payload

GEDCOM 7 puts CROP on the multimedia link, so one group photograph referenced by five people carries five different rectangles and each position answers with its own. A rectangle is named only where it can be applied: one with no extent, one written in GEDCOM 5.5.1 — whose specification describes none — and one on a link to a record carrying several files all answer with the file and no rectangle, so a host shows the whole image rather than nothing.

It answers null where the position names no media, where a pointer resolves to no record or to a record that is not multimedia, and where a multimedia record carries no file. HEAD.FILE names the transmission rather than media, so it answers nothing there either.

The answer describes and does not fetch: reading the file, resolving it against a workspace, reaching the network, and measuring the image stay with the caller. A rectangle is carried as written even where it names an extent larger than its image, because the extent of an image is not knowable from the document.