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

@dxos/datalog

v0.16.0

Published

Datalog engine with no third-party dependencies: parser, static checks, incremental stratified semi-naive evaluation with provenance and pluggable built-ins.

Downloads

815

Readme

@dxos/datalog

A small Datalog engine with no third-party dependencies: a parser for a Soufflé-like dialect, static checks, and an incremental stratified semi-naive evaluator with provenance and pluggable built-ins. It is plain TypeScript with no Node built-ins, eval or threads, so it runs unchanged in the browser, in a workerd Durable Object and in Node.

Dialect

% Facts and rules; lowercase words and "strings" are the same symbol, numbers are numbers.
edge(a, b).
path(X, Y) :- edge(X, Y).
path(X, Z) :- path(X, Y), edge(Y, Z).

% Stratified negation (`not p(…)`, `!p(…)`), anonymous variables and comparisons.
isolated(X) :- node(X), not path(X, _), X != root.

% Aggregates: count, min, max and sum over a body; variables shared with the rule correlate it.
degree(X, N) :- node(X), N = count : { edge(X, _) }.

% Built-ins are called like relations; `2d` is a symbol, so durations need no quotes.
followup :- elapsed(goal, 2d), not achieved(goal).

No disjunction, functions or existential heads: every program terminates.

Usage

import { Builtin, Checker, Engine, Parser } from '@dxos/datalog';

const program = Parser.parse(source); // throws Parser.ParseError with line and column
const diagnostics = Checker.check(program, { builtins, relations: { edge: 2 } });

const engine = Engine.make({ program, builtins });
const changes = engine.insert('edge', ['a', 'b']); // only the tuples this insert added or removed
engine.update({ refresh: true }); // re-evaluate volatile built-ins (a clock tick)
engine.provenance('path', ['a', 'b']); // the base tuples one derivation rests on
  • Checks (Checker.check) return structured diagnostics: arity, undefined (when base relations are declared), builtin-head, unsafe (every head variable and every variable under not or in a comparison must be bound by a positive literal or a built-in mode that binds it) and unstratifiable (negation or aggregation through recursion).
  • Built-ins (Builtin.make) declare binding modes: "fb" means the first argument may be free and is bound by the call. A volatile built-in (one that reads a clock) is re-evaluated on refresh.
  • Incremental evaluation: insertions propagate semi-naively; a stratum is recomputed and diffed only when a relation it reads under negation or aggregation changed, or a relation it reads lost tuples. Changes.added is the "new binding" signal callers wake on.
  • Provenance: one derivation is kept per derived tuple (the rule and the tuples its positive literals matched). Aggregates and negation contribute no premises.
  • Rewrites: the AST (Ast) is plain data, so a caller rewrites a parsed program (for example, expanding predicate shorthand) before checking it.

Why synchronous

The engine is a pure function of its inputs with no I/O, so it exposes plain synchronous calls rather than Effects; callers that need Effect wrap a call where it is used. This keeps the hot path (one evaluation per appended fact) free of fiber overhead and the package free of third-party runtime dependencies.