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

@genebe/sql-where-parser

v0.2.0

Published

Parser for SQL-like WHERE ... ORDER BY ... filters used by GeneBe. Produces a JSON condition tree (WhereQuery shape).

Readme

@genebe/sql-where-parser

Parser for SQL-like filters (WHERE conditions with an optional ORDER BY) used by GeneBe. It turns text written by a user (or an LLM) into a JSON condition tree. No SQL is executed here, and there is no dependency on a database.

import { parseWhere, ParseError } from '@genebe/sql-where-parser';

const query = parseWhere("gene IN ('GJB2','BRCA1') AND NOT (AF_gnomad > 0.01) ORDER BY AF_gnomad DESC");
// {
//   op: 'AND',
//   en: [
//     { column: 'gene', operator: 'IN', value: ['GJB2', 'BRCA1'] },
//     { op: 'NOT', en: [{ column: 'AF_gnomad', operator: 'GREATER_THAN', value: 0.01 }] }
//   ],
//   sort: [{ name: 'AF_gnomad', ascending: false }]
// }

try {
    parseWhere('a == 1');
} catch (e) {
    if (e instanceof ParseError) console.log(e.message);
    // Syntax error at line 1:3 – unexpected '='; expected ... (use a single '=' for equality)
}

parseWhere always returns a node or throws ParseError (it never returns undefined). Error messages contain the position, what was expected and a hint for common mistakes, so they can be shown to a user or fed back to an LLM.

Back to text

toSql(node, { quote }) is the inverse of parseWhere: for every tree produced by the parser, parseWhere(toSql(tree)) equals the tree.

import { toSql } from '@genebe/sql-where-parser';

toSql(parseWhere("(a=1 OR b=2) AND NOT c LIKE 'x%' ORDER BY a"));
// (a = 1 OR b = 2) AND NOT c LIKE 'x%' ORDER BY a
toSql(tree, { quote: 'always' }); // "a" = 1 ...

Columns are quoted only when necessary (spaces, keywords, odd characters); parentheses appear only where precedence (NOT > AND > OR) or the tree structure requires them; EQUAL/NOT_EQUAL with no value become IS NULL/IS NOT NULL; numbers never use an exponent. Operators without a text form (e.g. NOOP), a missing value or an empty group throw an Error. ORDER BY prints NULLS FIRST|LAST whenever nullsLast is set.

Result

WhereNode: a logic node { op: 'AND' | 'OR' | 'NOT', en: WhereNode[] } or a condition { column, operator, value? }. The top level is always a logic node (a single condition is wrapped in AND), or { sort } when only ORDER BY was given; sort ({ name, ascending?, nullsLast? }[]) sits on the top-level node. Operators: EQUAL NOT_EQUAL LESS_THAN LESS_THAN_OR_EQUAL GREATER_THAN GREATER_THAN_OR_EQUAL LIKE NOT_LIKE LIKE_ANY NOT_LIKE_ANY ILIKE NOT_ILIKE ILIKE_ANY NOT_ILIKE_ANY CONTAINS NOT_CONTAINS CONTAINS_ANY NOT_CONTAINS_ANY IN NOT_IN EMPTY NOT_EMPTY. BETWEEN is expanded to >= / <=.

Language

| Construct | Notes | |---|---| | col = <> != < <= > >= literal | literal: number (1, -0.5, 1e-5), 'string' ('' escapes a quote), TRUE/FALSE | | col [NOT] LIKE 'a%', [NOT] ILIKE | ILIKE ignores case | | col [NOT] LIKE\|ILIKE ANY ('a%','b%') | any pattern matches | | col [NOT] IN (v1, v2) | non-empty list | | col [NOT] BETWEEN a AND b | | | col IS [NOT] NULL | = NULL is rejected | | col [NOT] CONTAINS 'x', [NOT] CONTAINS ANY (...) | extension: substring / list membership | | AND, OR, NOT, ( ) | precedence NOT > AND > OR | | ORDER BY col [ASC\|DESC] [NULLS FIRST\|LAST], ... | | | leading WHERE, trailing ;, -- and /* */ comments | accepted |

Columns: name, table.name, or "quoted name". Keywords are case-insensitive. Not supported (with an explanatory error): SELECT/FROM/JOIN/GROUP BY, subqueries, functions, arithmetic, column-to-column comparison, LIMIT/OFFSET, ==. Integers beyond ±2^53 are rejected.

Semantics of the consuming GeneBe backend worth knowing: <>, NOT IN, NOT LIKE, NOT CONTAINS (and ANY forms) also return rows where the column is NULL, whereas NOT (...) follows SQL three-valued logic; on list columns =/IN mean exact list equality and CONTAINS ANY is membership.

Development

The grammar (FilterQuery.g4) is shared with the Java implementation in the same repository, which is the source of truth. npm run build generates the TypeScript lexer/parser from it with antlr4ng-cli (needs a JVM; consumers of the published package do not). npm test runs the shared conformance corpus used by the Java tests too.