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

identitydb

v0.2.2

Published

TypeScript memory graph database wrapper for topics, facts, and AI-assisted ingestion.

Downloads

676

Readme

IdentityDB

IdentityDB is a TypeScript package for building structured AI memory on top of relational databases.

What it is

IdentityDB stores memory as a graph made of:

  • Topics — named nodes such as TypeScript, programming language, 2025, or I
  • Facts — statements that connect multiple topics
  • Fact-topic links — the relationships that turn one fact into a bridge between many topics

A single fact like I have worked with TypeScript since 2025. can connect the topics I, TypeScript, and 2025 at the same time.

Current capabilities

  • SQLite, PostgreSQL, MySQL, and MariaDB connection adapters
  • Automatic schema initialization for spaces, topics, facts, fact_topics, topic_relations, topic_aliases, and fact_embeddings
  • High-level APIs for adding topics and facts
  • Hard space isolation so independent memory graphs can coexist without cross-linking
  • Topic alias and canonical resolution APIs so facts and queries can resolve alternate names
  • Semantic fact indexing and search APIs built around provider-agnostic embeddings
  • Dedup-aware ingestion hooks that can reuse an existing fact when a semantic near-duplicate is detected
  • Pluggable fact extraction so callers can use a small LLM or a deterministic extractor

Install

SQLite connections use built-in runtime drivers: bun:sqlite under Bun and node:sqlite under Node 22+. Run SQLite-backed IdentityDB workloads with Bun or Node 22+. PostgreSQL/MySQL/MariaDB adapters remain usable from Node without SQLite.

bun install

Quick start

import { IdentityDB, NaiveExtractor, type EmbeddingProvider } from 'identitydb';

const db = await IdentityDB.connect({
  client: 'sqlite',
  filename: ':memory:',
});

await db.initialize();

await db.ingestStatement('I have worked with TypeScript since 2025.', {
  extractor: new NaiveExtractor(),
});

await db.addFact({
  statement: 'TypeScript is a programming language.',
  topics: [
    {
      name: 'TypeScript',
      category: 'entity',
      granularity: 'concrete',
    },
    {
      name: 'programming language',
      category: 'concept',
      granularity: 'abstract',
    },
  ],
});

await db.linkTopics({
  parentName: 'programming language',
  childName: 'TypeScript',
});

await db.addTopicAlias('TypeScript', 'TS');

const provider: EmbeddingProvider = {
  model: 'example-embedding-v1',
  dimensions: 3,
  async embed(input) {
    if (input.toLowerCase().includes('typescript')) {
      return [1, 0, 0];
    }

    return [0, 1, 0];
  },
};

await db.indexFactEmbeddings({ provider });

const topic = await db.getTopicByName('TS', { includeFacts: true });
const children = await db.getTopicChildren('programming language');
const lineage = await db.getTopicLineage('TS');
const connected = await db.findConnectedTopics('TypeScript');
const matches = await db.searchFacts({
  query: 'TypeScript experience',
  provider,
  limit: 5,
});

console.log(topic?.name);
console.log(children.map((entry) => entry.name));
console.log(lineage.map((entry) => entry.name));
console.log(connected.map((entry) => [entry.name, entry.sharedFactCount]));
console.log(matches.map((entry) => [entry.statement, entry.score]));

await db.close();

Memory spaces

IdentityDB now supports hard isolation via spaces. If you write facts into spaceName: 'A' and spaceName: 'B', they behave like separate dimensions:

  • the same topic name can exist in both spaces
  • aliases resolve only inside the requested space
  • hierarchy, connected-topic traversal, semantic search, and duplicate detection stay inside the same space
await db.upsertSpace({ name: 'A' });
await db.upsertSpace({ name: 'B' });

await db.addFact({
  spaceName: 'A',
  statement: 'TypeScript belongs to A.',
  topics: [{ name: 'TypeScript', category: 'entity', granularity: 'concrete' }],
});

await db.addFact({
  spaceName: 'B',
  statement: 'TypeScript belongs to B.',
  topics: [{ name: 'TypeScript', category: 'entity', granularity: 'concrete' }],
});

const alphaFacts = await db.getTopicFacts('TypeScript', { spaceName: 'A' });
const betaFacts = await db.getTopicFacts('TypeScript', { spaceName: 'B' });

Semantic ingestion and duplicate detection

If you provide an embedding provider during ingestion, IdentityDB can index the new fact automatically and reuse an existing fact when a semantic near-duplicate is already present.

await db.ingestStatement('Bun makes TypeScript tooling fast.', {
  extractor: new NaiveExtractor(),
  embeddingProvider: provider,
  duplicateThreshold: 0.95,
});

LLM-backed extraction

You can bridge any text-generating model into IdentityDB by wrapping it with LlmFactExtractor.

import { LlmFactExtractor } from 'identitydb';

const extractor = new LlmFactExtractor({
  model: {
    async generateText(prompt) {
      return callYourFavoriteLlm(prompt);
    },
  },
  instructions: 'Prefer technology, product, and time topics over generic nouns.',
});

await db.ingestStatement('I have worked with Bun and TypeScript since 2025.', {
  extractor,
});

The adapter expects the model to return JSON and will validate the structured response before IdentityDB writes a fact.

Development

bun run test
bun run check
bun run build

Current status

This repository is in active MVP expansion development.

See these implementation plans for the current roadmap:

  • docs/plans/2026-05-11-identitydb-foundation.md
  • docs/plans/2026-05-11-identitydb-memory-expansion.md