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

@shipfox/shipql-parser

v0.2.0

Published

A Lucene-inspired query language parser for observability facet search. Parses a ShipQL query string into a typed AST.

Downloads

536

Readme

@shipfox/shipql-parser

A Lucene-inspired query language parser for observability facet search. Parses a ShipQL query string into a typed AST.

Usage

import { parse } from '@shipfox/shipql-parser';

const ast = parse('env:prod status:>=400');

parse(input: string) returns an AstNode or null for empty input.

ShipQL syntax

Facet match

Match a facet against an exact value. Unquoted values run until the next whitespace. Use double quotes for values containing spaces.

status:success
http.method:GET
message:"hello world"

Comparison operators

Prefix a value with >=, <=, >, <, or = to perform numeric or lexicographic comparisons.

latency:>500
status:>=400
timestamp:<="2025-12-31"

Range queries

Use bracket syntax to match a facet within an inclusive range.

status:[200 TO 299]
timestamp:["2025-01-01" TO "2025-12-31"]

Boolean operators

Consecutive terms are joined with an implicit AND. Both AND and OR can be written explicitly. AND binds tighter than OR.

env:prod status:success          # implicit AND
env:prod AND status:success      # explicit AND
status:success OR status:error   # explicit OR
a:1 b:2 OR c:3                  # parsed as (a:1 AND b:2) OR c:3

Negation

Negate a term with the NOT keyword or the - shorthand prefix.

NOT status:error
-status:error            # equivalent to NOT status:error
env:prod -status:error   # env:prod AND NOT status:error

Grouped values

Use parentheses after a facet to match multiple values without repeating the facet name. All boolean operators work inside groups.

env:(prod OR staging)
tag:(web AND api)
tag:(web api)                    # implicit AND
http.method:(GET OR POST OR PUT)

Parenthesized expressions

Use parentheses to override operator precedence in the top-level expression.

status:error AND (env:prod OR env:staging)

Free text

Terms without a facet prefix are parsed as free text. Quoted strings are supported.

hello
"error occurred"
error service:api                # implicit AND between text and facet match

Facet names

Facet names start with a letter or underscore, followed by alphanumeric characters or underscores. Dots are allowed for nested facets.

status
http.status_code
_internal.flag

AST node types

The parser produces a tree of the following node types.

MatchNode

A facet compared against a value. The op field indicates the comparison operator. Plain facet:value syntax uses =.

type MatchNode = {
  type: 'match';
  facet: string;
  op: '>=' | '<=' | '>' | '<' | '=';
  value: string;
};

| Input | op | facet | value | |---|---|---|---| | status:success | = | status | success | | status:=success | = | status | success | | latency:>500 | > | latency | 500 | | status:>=400 | >= | status | 400 |

RangeNode

An inclusive range query on a facet.

type RangeNode = {
  type: 'range';
  facet: string;
  min: string;
  max: string;
};

| Input | facet | min | max | |---|---|---|---| | status:[200 TO 299] | status | 200 | 299 |

TextNode

Free text not bound to a facet.

type TextNode = {
  type: 'text';
  value: string;
};

| Input | value | |---|---| | hello | hello | | "error occurred" | error occurred |

AndNode

A conjunction of two sub-expressions. Produced by implicit adjacency or explicit AND.

type AndNode = {
  type: 'and';
  left: AstNode;
  right: AstNode;
};

OrNode

A disjunction of two sub-expressions. Always explicit.

type OrNode = {
  type: 'or';
  left: AstNode;
  right: AstNode;
};

NotNode

Negation of a sub-expression. Produced by NOT or -.

type NotNode = {
  type: 'not';
  expr: AstNode;
};

Full union type

type AstNode =
  | MatchNode
  | RangeNode
  | TextNode
  | AndNode
  | OrNode
  | NotNode;

Operator precedence

From lowest to highest:

  1. OR
  2. AND (implicit or explicit)
  3. NOT / -
  4. Primary (facet match, range, grouped expression, parenthesized expression, free text)

Reserved words

AND, OR, and NOT are reserved when they appear as standalone tokens. They can still be used as part of facet names (e.g. ANDROID:true, ORDER:asc).