@maybit/quicklit (0.0.4)

Published 2026-02-24 16:49:53 +00:00 by patrick

Installation

@maybit:registry=
npm install @maybit/quicklit@0.0.4
"@maybit/quicklit": "0.0.4"

About this package

Quicklit

A type-safe query parser, builder, and stringifier for Quickwit and Tantivy.

Quicklit allows you to parse raw Lucene-like query strings into a structured Abstract Syntax Tree (AST), programmatically build complex queries using a fluent API, and stringify them back into safe, escaped query strings ready for your search engine.

Features

  • Type-Safe AST: Full TypeScript definitions for every node type (Term, Range, Boolean, etc.).
  • Robust Parser: Handles complex nesting, operator precedence, and edge cases.
  • Fluent Builder: Construct queries programmatically (q.and(...)) without messy string concatenation.
  • Safe Stringification: Automatically escapes special characters to prevent syntax errors.
  • Feature Complete: Supports:
    • Boolean Logic (AND, OR, NOT, Grouping ())
    • Ranges ([10 TO 20], >, <=)
    • Sets (IN [a, b, c])
    • Phrase Queries & Slop ("foo bar"~2)
    • Prefix (term*)
    • Wildcards (field:*)

Installation

npm install quicklit
# or
yarn add quicklit
# or
pnpm add quicklit

Usage

Parsing Query Strings

Turn a raw string input (e.g., from a search bar) into a structured AST. This validates the syntax and allows you to analyze or modify the query before sending it.

This is the complete round-trip from a raw user input, to a parsed Abstract syntax tree, over a modification of that AST to the rendering of the final search query.

import { parse } from 'quicklit';

const query = 'level:ERROR AND (duration:>1000 OR http_status:408)'
try {
  const ast = parse(query);
  console.log(JSON.stringify(ast, null, 2);
} catch (e) {
  console.error("Invalid query syntax:", e.message);
}

Output AST:

{
  "type": "And",
  "children": [
    {
      "type": "Term",
      "field": "level",
      "value": "ERROR"
    },
    {
      "type": "Group",
      "child": {
        "type": "Or",
        "children": [
          {
            "type": "Range",
            "field": "duration",
            "lower": "1000",
            "includeLower": false,
            "includeUpper": false
          },
          {
            "type": "Term",
            "field": "http_status",
            "value": "408"
          }
        ]
      }
    }
  ]
}

Modify the query programmatically:

Use the q helper to build queries safely. This is perfect for constructing filters in your UI logic.

import { q } from "quicklit";

const modifiedAst = q.and(ast, q.prefix("service_name", "customer"));

Output modifiedAst:

{
  "type": "And",
  "children": [
    {
      "type": "And",
      "children": [
        {
          "type": "Term",
          "field": "level",
          "value": "ERROR"
        },
        {
          // ...
        }
      ]
    },
    {
      "type": "Prefix",
      "value": "customer",
      "field": "service_name"
    }
  ]
}

Type Guards: Quicklit exports type guards to help you traverse or validate the AST at runtime.

import { isRangeNode, isTermNode, type QueryNode } from 'quicklit';

function analyzeNode(node: QueryNode) {
  if (isRangeNode(node)) {
    console.log(`Filtering range on field ${node.field} from ${node.from}`);
  } else if (isTermNode(node)) {
    console.log(`Searching for term: ${node.value}`);
  }
}

Supported Syntax:

Quicklit supports the standard Quickwit/Tantivy syntax:

Feature Example Input Builder Helper
Term status:active q.term('active', 'status')
And a AND b q.and(a, b)
Or a OR b q.or(a, b)
Not NOT lvl:WARN -lvl:WARN q.not(node)
Group (a OR b) q.group(node)
Phrase "hello world" q.phrase("hello world")
Slop "foo bar"~2 q.phrase("foo bar", field, 2)
Range val:[1 TO 5] q.range('val', 1, 5)
Comparison val > 10 q.greaterThan('val', 10)
Set id:IN [1, 2] q.in('id', ['1', '2'])
Wildcard work* q.prefix('work')
Exists author:* q.exists('author)

Stringify an AST to a Quickwit Query:

import { stringify } from "quicklit";

const queryString = stringify(modifiedAst);
console.log(queryString);

Output of queryString:

level:ERROR AND (duration:{1000 TO *} OR http_status:408) AND customer:service*

Syntax Highlighting & UI Building

Quicklit exposes its internal Lexer and TokenType enum. This allows to build rich search inputs with syntax highlighting, auto-completion, or validation overlays without writing your own tokenizer.

The Lexer preserves whitespace and positions, making it perfect for mapping styles to text ranges.


import { tokenize, TokenType } from 'quicklit';

const query = 'status:active AND "hello world"';
const tokens = tokenize(query);

// Map tokens to CSS classes (e.g., Tailwind)
const html = tokens.map(token => {
  let className = 'text-gray-800';

  switch (token.type) {
    case TokenType.Operator:  // AND, OR
      className = 'text-purple-600 font-bold';
      break;
    case TokenType.String: // "..."
      className = 'text-green-600';
      break;
    case TokenType.Keyword: // IN, TO
      className = 'text-blue-600 italic';
      break;
    case TokenType.Error:
      className = 'text-red-500 underline decoration-wavy';
      break;
    // ...
  }

  return `<span class="${className}">${token.value}</span>`;
}).join('');

// Render `html` in your component...

Comparison: Why to use Quicklit?

Feature Template Strings Quicklit
Syntax Validation (Server errors) (Fails fast locally)
Escaping (Vulnerable) (Automatic)
Composability (Hard to merge) (Easy tree merging)
Syntax Highlighting (Via Lexer API)
Type Safety

Known issues/open tasks

  • [] Add sample query editor implementation for documentation purpose

Development / Contributing

  • Install dependencies:
npm install
  • Run the unit tests (uses Vitest):
npm run test
  • Build the library (uses tsdown):
npm run build

License

License: MIT

Dependencies

Development dependencies

ID Version
@types/node ^25.3.0
bumpp ^10.4.1
tsdown ^0.20.3
typescript ^5.9.3
vitest ^4.0.18
Details
npm
2026-02-24 16:49:53 +00:00
32
Patrick von Lieres und Wilkau
MIT
latest
7.9 KiB
Assets (1)
Versions (1) View all
0.0.4 2026-02-24