npm package: @qezor/synquery
Description: Zero-dependency query-pattern matching and query normalization utilities
Author: Abdul Ahad
Latest version: 1.0.1

# @qezor/synquery

Zero-dependency query-pattern matching for strings, URLs, `URLSearchParams`, and plain objects.

`synquery` is query-only. It does not depend on `urlex`, and it does not try to route paths or hosts. Its job is simple:
- parse query inputs into a predictable map
- compile query-patterns
- match query objects against query strings
- capture values without caring about query key order
- stringify normalized query input back into query strings

## Installation

```bash
npm install @qezor/synquery
```

## Import / Require

```js
const { compile, match, parseQuery, stringifyQuery } = require("@qezor/synquery")
```

```js
import { compile, match, parseQuery, stringifyQuery } from "@qezor/synquery"
```

## Quick Start

```js
const { compile, match } = require("@qezor/synquery")

const pattern = compile("?page=*&mode={preview,full}&token=:token")

const result = match(pattern, "https://qezor.pro/app?mode=preview&page=2&token=abc", true)

console.log(result)
// { token: "abc" }
```

## Pattern Syntax

Leading `?` is optional.

```text
page=*&mode={preview,full}&token=:token
```

Supported value patterns:
- literal: `mode=preview`
- any: `page=*`
- capture: `token=:token`
- optional capture: `cursor=*:cursor`
- literal set: `mode={preview,full,raw}`

Backslash escapes special query syntax characters:
- `\&`
- `\=`
- `\*`
- `\:`
- `\{`
- `\}`
- `\,`
- `\\`

## Object-Friendly Matching

Plain objects can be used as either the pattern source or the actual input.

```js
const { match } = require("@qezor/synquery")

match(
  { page: "*", mode: "{preview,full}", token: ":token" },
  { mode: "preview", page: 2, token: "abc" },
  true
)
// { token: "abc" }
```

Repeated values are supported with arrays:

```js
match(
  { tag: ["{a,b}", "{b,c}"] },
  "tag=c&tag=a",
  false
)
// true
```

## API

### `compile(pattern)`

Compiles a query-pattern string or pattern object.

```js
const compiled = compile("page=*&sort={asc,desc}")
```

```js
const compiled = compile({ page: "*", sort: "{asc,desc}" })
```

### `compileQuery(pattern)`

Alias of `compile()`.

### `parse(input)`
### `parseQuery(input)`

Parses and normalizes query input into:

```js
{
  key: ["value1", "value2"]
}
```

Accepted inputs:
- raw query strings
- full URLs
- `URL` instances
- `URLSearchParams`
- plain objects with scalar or array values

```js
parseQuery("https://qezor.pro/app?page=2&tag=a&tag=b")
// { page: ["2"], tag: ["a", "b"] }
```

### `stringify(input, options)`
### `stringifyQuery(input, options)`

Normalizes input and serializes it back to a query string.

```js
stringifyQuery({ page: 2, tag: ["a", "b"] })
// "page=2&tag=a&tag=b"
```

Options:
- `leadingQuestionMark`
- `sortKeys`

### `match(compiledOrPattern, input, save)`
### `matchQuery(compiledOrPattern, input, save)`

Matches a compiled pattern or raw pattern against a query input.

With `save=false`, returns `true` or `null`.

With `save=true`, returns only query captures:

```js
match("token=:token&mode={preview,full}", "mode=preview&token=abc", true)
// { token: "abc" }
```

## Matching Rules

- query key order does not matter
- repeated-key matching is order-insensitive
- extra query keys are ignored
- extra values for the same key are ignored as long as all pattern entries can match
- query keys and values are case-sensitive

## Performance Notes

`synquery` is built to stay small and cheap:
- zero dependencies
- no path or host routing layer
- plain loops and low allocation in hot paths
- query parsing and matching kept separate for easier reuse

Use `npm run bench` for a local micro-benchmark and `npm test` for behavior checks.