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

wizard-ql

v2.1.1

Published

WizardQL is a natural-language-like query language for constructing data queries for resources that meet conditions.

Readme

WizardQL is a natural-language-like query language for constructing data queries for resources that meet conditions.

Examples

parse('(rank >= 10 & role = admin) | banned')

{
  type: "group",
  operation: "OR",
  constituents: [
    {
      type: "group",
      operation: "AND",
      constituents: [
        {
          type: "condition",
          field: "rank",
          operation: "GEQ",
          value: 10,
          validated: false,
        }, {
          type: "condition",
          field: "role",
          operation: "EQUAL",
          value: "admin",
          validated: false,
        }
      ],
    }, {
      type: "condition",
      field: "banned",
      operation: "EQUAL",
      value: true,
      validated: false,
    }
  ],
}

parse('flagged OR (name MATCHES "decorative_.*" AND !(price >= 20 AND price <= 30))')

{
  type: "group",
  operation: "OR",
  constituents: [
    {
      type: "condition",
      field: "flagged",
      operation: "EQUAL",
      value: true,
      validated: false,
    }, {
      type: "group",
      operation: "AND",
      constituents: [
        {
          type: "condition",
          field: "name",
          operation: "MATCH",
          value: "decorative_.*",
          validated: false,
        }, {
          type: "group",
          operation: "OR",
          constituents: [
            {
              type: "condition",
              field: "price",
              operation: "LESS",
              value: 20,
              validated: false,
            }, {
              type: "condition",
              field: "price",
              operation: "GREATER",
              value: 30,
              validated: false,
            }
          ],
        }
      ],
    }
  ],
}

Basic Syntax

Condition

A condition is a check against a field using a condition operator such as EQUAL or LESS. {FIELD} {C_OPERATOR} {VALUE} For array operators, (IN, NOTIN), the value must be in brackets separated by commas.

Example: snack : [pizza, soda, chips]

A field OR a value can be wrapped in quotes if it contains a special operator.

Example: "vendor" = "H&M"

Example: "vendor" = H\&M

You can also escape characters

Example: 'speech' = "\"Hello\""

Example: 'speech' = '"Hello"'

Backslashes can be denoted with a double backslash

Example: \\

Implicit Boolean

Simply denoting a field name (field) transforms it into field = true

(!field) transforms it into field = false

Group

A group is multiple conditions joined by a junction operator such as AND or OR. ({CONDITION} [...{J_OPERATOR} {CONDITION}]) Groups are implicit when junction operators are used (follows PEMDAS [ANDs grouped before ORs]). Parentheses can be used to denote them explicitly.

Groups can be nested.

Example: user.activated & ((user.role = member & user.group : [abc, xyz]) | (user.role = admin & user.privileged))

Groups can also be negated

!(firstname = John & lastname = Doe)firstname != John | lastname != Doe

Operators

Junction Operators

  • AND
  • &
  • &&
  • ^
  • OR
  • |
  • ||
  • V

Comparison Operators

  • EQUAL
  • EQUALS
  • EQ
  • IS
  • ==
  • =
  • NOTEQUALS
  • NOTEQUAL
  • NEQ
  • ISNT
  • !==
  • !=
  • LESS
  • <
  • GREATER
  • >
  • MORE
  • GEQ
  • >=
  • =>
  • LEQ
  • <=
  • =<
  • IN
  • :
  • NOTIN
  • !:
  • MATCH
  • MATCHES
  • ~
  • NOTMATCH
  • NOTMATCHES
  • !~

Constraints

The parse function can be passed an object containing various constraints as its second parameter

restricted

A record mapping field names to restrictions. A value of true totally prohibits the usage of a field.

Otherwise, a tuple can be passed

['allow' | 'deny', [...values]]

Values can be direct values (string, number, boolean) or regex expressions

"allow" will allow the values/patterns and deny all others "deny" will deny the values/patterns and allow all others

types

A record mapping field names to (boolean, string, number, date). The value in the record can either be a single allowed type or an array of allowed types. Only operators that can function on that type can be used for that field. By default, fields will be treated as being able to be any of the three types.

Example:

parse('field1 = value', {
  types: {
    field1: 'string',
    field2: ['string', 'number']
  }
})

[!NOTE] A value will only be attempted to be parsed as a date if 'date' is included in the field's type record. If the value is a number, it will be parsed as number of milliseconds. Any non-number character will cause the value to be parsed as a string

The type coercion priority chain is as follows: boolean -> date -> number -> string

[!WARNING] This can cause issues for numbers with leading zeros. If this is a potential problem, make sure to exclude number from the types and include string

Regarding constraints: validated property

When a field has matched either a key in types or a field in restricted, the validated property on the parsed condition will be true. This is due to a limitation with TypeScript's type checking.

Therefore, type inference would look something like this:

const parsed = parse('field = value', {
  types: {
    field: ['string', 'number']
  }
})

if (parsed.validated) {
  switch (parsed.type) {
    case 'condition':
      switch (parsed.field) {
        case 'field':
          parsed.value
          // ^?: string | number
          break
      }
      break
  }
}

caseInsensitive

Type/constraint checks will be case-insensitive on the field name

[!NOTE] If enabled, all fields will be returned as their casing denoted by the types or restricted record

[!WARNING] Mismatching casing between the restricted record and the type record will prioritize the restricted record

disallowUnvalidated

Fields that are not present in the type or restriction record will considered invalid fields

dateInterpreter

A callback that determines how WizardQL interprets dates. Wizard will attempt to parse a value as a date if 'date' is supplied in its type record

By default, this is simply (v) => new Date(v)

Stringification

Parsed expressions can be converted back into strings using the stringify function. The stringify function comes with its own slew of options as its second parameter

junctionNotation

The notation to use for junction operators

  • Programmatic: &
  • Linguistic: AND
  • Formal: ^

comparisonNotation

The notation to use for comparison operators

  • Programmatic: =
  • Linguistic: EQUALS

alwaysParenthesize

Always put parentheses around every group

compact

Don't include spaces in the output (except for surrounding lingustic operators)

condenseBoolean

EQUAL/NOTEQUAL regarding booleans will be condensed into implicit form

Summarize

You can use the summarize function to summarize a parsed expression, aggregated by field name across groups

summarize(parse('(foo in [1, 2] and (bar = 2 or baz)) V (bar !: [1, 3] and foo = 3)'))

[
    ['foo', [
      {
        operation: 'IN',
        value: [1, 2],
        exclusionary: false
      },
      {
        operation: 'EQUAL',
        value: 3,
        exclusionary: false
      }
    ]],
    ['bar', [
      {
        operation: 'EQUAL',
        value: 2,
        exclusionary: false
      },
      {
        operation: 'NOTIN',
        value: [1, 3],
        exclusionary: true
      }
    ]],
    ['baz', [
      {
        operation: 'EQUAL',
        value: true,
        exclusionary: false
      }
    ]]
  ]

[!NOTE] exclusionary implies a negative operation (once that excludes the value)

Execution Example

Below is an example of how a Wizard query would be executed in the context of a KnexJS Query

https://github.com/guidance-analytics/wizard-ql/blob/bf8693e2cb5678ef855600d70bc495a614aa3d0b/src/execute.ts#L1-L35

DOM Input

Wizard comes pre-packaged with a DOM input that applies classes for tokens, making for query input with syntax highlighting (up to discretion)

// NOTE: The input element should be a regular div element, not an input element
const destructor = createDOMInput({ input: document.getElementById('input') })

destructor()

Token types

Depending on a token's type, attributes will be applied to the contents of the input for styling:

  • data-spacer - Whitespace
  • data-node - An actual token
    • data-quoted - Quoted text
    • data-number - A number
    • data-bracket - A parenthesis or array bracket
    • data-delimiter - A comma
    • data-negation - A negatory exclamation mark
    • data-operation - A comparison or junction operator

A token can also possess data-error if it is part of an error span

The input itself can have the following attributes:

  • data-error-message - The error message
  • data-error-start - The starting token index for the error
  • data-error-end - The end token index for the error