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

@rgrannell1/tribbledb

v0.0.32

Published

[![CI](https://github.com/rgrannell1/tribbledb/workflows/Test/badge.svg)](https://github.com/rgrannell1/tribbledb/actions)

Readme

TribbleDB

CI codecov

A high-performance triple store database for Deno/TypeScript with indexed lookups and chainable search operations.

Installation

Deno

import { TribbleDB } from "jsr:@rgrannell1/tribbledb";

npm

npm install @rgrannell1/tribbledb
import { TribbleDB } from "@rgrannell1/tribbledb";

Creating a TribbleDB

const triples: Triple[] = [
  ["urn:ró:person:alice", "name", "Alice Johnson"],
  ["urn:ró:person:alice", "age", "30"],
  ["urn:ró:person:bob", "name", "Bob Smith"],
  ["urn:ró:company:acme", "name", "Acme Corp"],
];

const tdb = new TribbleDB(triples);
const objects = [
  {
    id: "urn:ró:person:alice",
    name: "Alice Johnson",
    age: "30",
  },
];

const tdb2 = TribbleDB.from(objects);

Basic Searches

// Search by source type
const people = tdb.search({ source: { type: "person" } });

// Search by relation
const names = tdb.search({ relation: "name" });

// Search by specific URN components
const alice = tdb.search({
  source: {
    type: "person",
    id: "alice",
  },
});
// Chain multiple searches
const adultNames = tdb
  .search({ source: { type: "person" }, relation: "age" })
  .search({ target: { predicate: (age) => parseInt(age) >= 18 } })
  .search({ relation: "name" });

// Transform results
const personObjects = tdb
  .search({ source: { type: "person" } })
  .objects();

// Multiple constraints
const specificData = tdb.search({
  source: { type: "person" },
  relation: "email",
  target: { predicate: (value) => value.includes("@") },
});

Advanced Usage

URN Structure

TribbleDB supports URN-based identifiers with the structure: urn:ró:type:id?key=value

const tdb = new TribbleDB([
  ["urn:ró:person:alice?dept=eng", "name", "Alice Johnson"],
  ["urn:ró:person:bob?dept=sales", "name", "Bob Smith"],
]);

// Search by URN type
const people = tdb.search({ source: { type: "person" } });

// Search by URN id
const alice = tdb.search({ source: { id: "alice" } });

// Search by query string
const engineers = tdb.search({ source: { qs: { dept: "eng" } } });

Validation

Add validation rules to ensure data integrity:

const validations = {
  age: (sourceType, relation, value) => {
    const parsedAge = parseInt(value);
    if (isNaN(parsedAge) || parsedAge < 0 || parsedAge > 150) {
      return `Invalid age: ${value}`;
    }
    return undefined;
  }
};

const tdb = new TribbleDB([], validations);
tdb.add([["person:alice", "age", "30"]]); // OK
// tdb.add([["person:bob", "age", "999"]]); // Throws validation error

Reading and Parsing Data

// Read a single entity as an object
const aliceObj = tdb.readThing("urn:ró:person:alice");

// Read multiple entities
const peopleObjs = tdb.readThings(["person:alice", "person:bob"]);

// Parse with a custom parser
type Person = {
  name: string;
  age: number;
};

const personParser = (obj: TripleObject): Person | undefined => {
  if (!obj.name || !obj.age) return undefined;
  return {
    name: obj.name as string,
    age: parseInt(obj.age as string)
  };
};

const alice = tdb.parseThing(personParser, "person:alice");
const people = tdb.parseThings(personParser, ["person:alice", "person:bob"]);

Database Operations

// Merge databases
const tdb1 = new TribbleDB(triples1);
const tdb2 = new TribbleDB(triples2);
tdb1.merge(tdb2);

// Clone a database
const copy = tdb.clone();

// Delete triples
tdb.delete([["person:alice", "age", "30"]]);

// Map transformations
const normalized = tdb.map(([src, rel, tgt]) =>
  [src.toLowerCase(), rel, tgt.toLowerCase()]
);

// FlatMap for expansions
const expanded = tdb.flatMap(([src, rel, tgt]) => [
  [src, rel, tgt],
  [tgt, `reverse_${rel}`, src]
]);

Complex Search Patterns

// Multiple constraints with predicates
const results = tdb.search({
  source: {
    type: "person",
    predicate: (src) => src.includes("alice")
  },
  relation: {
    relation: ["name", "age", "email"],
    predicate: (rel) => rel !== "internal_id"
  },
  target: {
    predicate: (val) => val.length > 0
  }
});

// Array-based search syntax
const results2 = tdb.search([
  { type: "person" },  // source constraint
  "works_at",          // relation constraint
  { type: "company" }  // target constraint
]);

// String shortcuts
const names = tdb.search({ relation: "name" });
const aliceData = tdb.search({ source: "person:alice" });

Accessing Results

// Get unique sources, relations, or targets
const allPeople = tdb.sources();
const allRelations = tdb.relations();
const allValues = tdb.targets();

// Get first results
const firstTriple = tdb.firstTriple();
const firstSource = tdb.firstSource();
const firstObject = tdb.firstObject();

// Convert to objects (with list handling)
const objects = tdb.objects(); // Single values where possible
const objectsWithLists = tdb.objects(true); // Always use arrays for relations

API Reference

Constructor Methods

new TribbleDB(triples, validations?)

Create a database from a triples array with optional validation rules.

  • triples: Array of [source, relation, target] tuples
  • validations: Optional record of validation functions per relation

TribbleDB.of(triples)

Static factory method, equivalent to new TribbleDB(triples).

TribbleDB.from(objects)

Create from an array of objects with id properties.

const tdb = TribbleDB.from([
  { id: "person:alice", name: "Alice", age: "30" }
]);

Search Methods

search(params)

Search and return a new TribbleDB instance with matching triples. Supports:

  • Object syntax: { source?, relation?, target? }
  • Array syntax: [source?, relation?, target?]
  • Each parameter can be a string, array of strings, or query object

Data Methods

add(triples)

Add new triples to the database (automatically deduplicated).

delete(triples)

Remove triples from the database.

triples()

Get all triples as an array.

objects(listOnly?)

Convert triples to object format.

  • listOnly: If true, always use arrays for relation values

sources(), relations(), targets()

Get unique values as Sets.

firstTriple(), firstSource(), firstRelation(), firstTarget(), firstObject()

Get the first matching result.

map(fn), flatMap(fn)

Transform triples and return a new TribbleDB.

merge(other), clone()

Combine or duplicate databases.

readThing(urn, opts?), readThings(urns, opts?)

Read entities as objects by URN.

  • opts.qs: If true, match URNs ignoring query strings

parseThing(parser, urn, opts?), parseThings(parser, urns, opts?)

Read and parse entities with a custom parser function.

Performance

TribbleDB uses multi-dimensional indices for efficient lookups:

  • Source type, id, and query string indices
  • Relation indices
  • Target type, id, and query string indices
  • Triple hash-based deduplication

Benchmark results are available in the benchmark_results/ directory.

Development

See dev commands with rs. For testing,

rs bench:record
rs test

Run

rs build

To produce JS artifacts.

License

MIT License

Copyright (c) 2025 Róisín Grannell

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.