querypipe
Core concepts

Error model

Result-based structured errors — the data path never throws; misuse throws TypeError.

querypipe draws a mechanical line between two failure regimes:

  • Data errors (untrusted input: strings, external specs) → returned as a structured, multi-error Result. The data path never throws.
  • Programmer misuse (invalid schema definitions, calling builder methods wrong) → throws TypeError, because that's a bug in your code, not a condition to handle.

The Result

import { isErr, isOk, type QueryResult, type QuerySpec } from "querypipe";

declare const result: QueryResult<QuerySpec>;

if (isOk(result)) {
  result.value; // QuerySpec — whitelisted, guard-limited, injection-safe
} else if (isErr(result)) {
  result.errors; // readonly QueryError[] — every problem, in one pass
}

Errors are collected, not short-circuited — a request with three problems yields three errors in input order, so API consumers can fix everything at once.

QueryError

import type { QueryError } from "querypipe";

declare const error: QueryError;
error.code;     // one of the closed set below
error.field;    // internal field name, or the offending raw token / segment key
error.op;       // raw operator token (may be an invalid one)
error.message;  // human-readable; wording is NOT part of the conformance contract
error.position; // 0-based UTF-16 index into the exact parse input; best effort

position is what powers the wavy underlines in the demos on this site — try the live widget:

  • NOT_SORTABLE secret @ 5

    not sortable

  • UNKNOWN_FIELD nope @ 16

    unknown field

  • INVALID_OPERATOR price like @ 38

    unknown operator

parsenever throws. You get every error, with positions, in one pass — the wavy underlines come from each error’s position field.

The closed code set

CodeMeaning
UNKNOWN_FIELDField/alias (or segment key) not in the schema whitelist.
NOT_SORTABLEDeclared field with sortable: false used in sort=.
NOT_FILTERABLEDeclared field with filterable: false used in filter=.
INVALID_OPERATORUnknown operator token, or a valid operator applied to an inapplicable field kind.
INVALID_VALUEMalformed value, arity mismatch, empty segments/items, duplicate segments, group nodes (v0.1), version ≠ 1, bad percent-escapes.
MAX_SORT_EXCEEDEDMore user sort columns than maxSortColumns.
MAX_FILTER_EXCEEDEDMore filter leaves than maxFilters.
VALUE_TOO_LONGA decoded value longer than maxValueLength, or an in/notIn list longer than maxInItems.
CONFLICTING_PAGINATIONoffset and cursor present together.

The set is closed and shared with the wire format — the Java port maps it 1:1.

What throws TypeError

Only structural misuse: invalid identifiers or duplicate wire names in createQuery, an unknown/unsortable stableBy, negative limit/offset, an empty cursor string, or calling apply() on a builder whose spec carries a cursor (keyset execution ships in v0.2). None of these depend on user input.

On this page