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

dyno-cql

v1.2.3

Published

A TypeScript library for building CQL queries

Readme

Dyno CQL

Build type-safe OGC Common Query Language (CQL) filter expressions with a fluent TypeScript API.

npm version License: MIT

Why Dyno CQL?

Working with geospatial and temporal data shouldn't mean wrestling with raw CQL strings. Dyno CQL gives you:

  • Full TypeScript support - Catch errors at compile time, not runtime
  • Fluent chaining - Build complex filters that read like sentences
  • Zero string concatenation - No more manual escaping or formatting
  • OGC CQL2 compliant - Works with modern geospatial APIs

Installation

npm install dyno-cql

Quick Start

import { queryBuilder, eq, gt, and } from 'dyno-cql';

// Simple filter
const simple = queryBuilder()
  .filter(eq("status", "ACTIVE"))
  .toCQL();
// → status = 'ACTIVE'

// Complex filter
const complex = queryBuilder()
  .filter(
    and(
      eq("status", "ACTIVE"),
      gt("age", 18)
    )
  )
  .toCQL();
// → (status = 'ACTIVE' AND age > 18)

Type Safety

Define interfaces for your data to get full type safety on attribute names and value types

interface Product {
  id: string;
  name: string;
  price: number;
  inStock: boolean;
}

// Type-safe query builder
const productQuery = queryBuilder<Product>()
  .filter((op) => 
    op.and(
      // the attribute name and value types are checked
      op.eq("name", "Laptop"),        // ✓ Valid attribute
      op.gte("price", 500),           // ✓ Valid type (number)
      op.eq("inStock", true),         // ✓ Valid type (boolean)
      // op.eq("invalid", "val")      // ✗ Compile error
    )
  )

Comparison Operators

Standard value comparisons for filtering your data.

import { eq, ne, lt, lte, gt, gte, between, isNull, isNotNull } from 'dyno-cql';

// Equality
eq("status", "ACTIVE")        // → status = 'ACTIVE'
ne("status", "DELETED")       // → status <> 'DELETED'

// Numeric comparisons
lt("age", 18)                 // → age < 18
lte("score", 100)             // → score <= 100
gt("price", 50)               // → price > 50
gte("quantity", 5)            // → quantity >= 5

// Range queries
between("age", 18, 65)        // → age BETWEEN 18 AND 65

// Null checks
isNull("deletedAt")           // → deletedAt IS NULL
isNotNull("email")            // → email IS NOT NULL

Text Operators

String matching for search functionality.

import { like, contains } from 'dyno-cql';

// Prefix matching
like("name", "A")                    // → name LIKE 'A%'

// Substring search
contains("description", "important")  // → description LIKE '%important%'

Logical Operators

Combine multiple conditions to build complex filters.

import { and, or, not, eq, gt } from 'dyno-cql';

// AND - all conditions must match
and(
  eq("status", "ACTIVE"),
  gt("age", 18)
)
// → (status = 'ACTIVE' AND age > 18)

// OR - any condition can match
or(
  eq("status", "PENDING"),
  eq("status", "PROCESSING")
)
// → (status = 'PENDING' OR status = 'PROCESSING')

// NOT - negate a condition
not(eq("status", "DELETED"))
// → NOT (status = 'DELETED')

Spatial Operators

Filter geospatial data using GeoJSON geometries. Input uses GeoJSON, output converts to WKT (Well-Known Text) format.

import {
  intersects,
  disjoint,
  spatialContains,
  within,
  touches,
  overlaps,
  crosses,
  spatialEquals
} from 'dyno-cql';

// Point geometry
const point = { type: "Point", coordinates: [0, 0] };
intersects("geometry", point)
// → INTERSECTS(geometry, POINT(0 0))

// Polygon geometry
const polygon = {
  type: "Polygon",
  coordinates: [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]
};
within("geometry", polygon)
// → WITHIN(geometry, POLYGON((0 0, 1 0, 1 1, 0 1, 0 0)))

// Line geometry
const line = {
  type: "LineString",
  coordinates: [[0, 0], [1, 1]]
};
crosses("geometry", line)
// → CROSSES(geometry, LINESTRING(0 0, 1 1))

Available spatial operators

| Operator | Description | |----------|-------------| | intersects | Geometries share any space | | disjoint | Geometries share no space | | spatialContains | First geometry contains the second | | within | First geometry is within the second | | touches | Geometries touch at boundary only | | overlaps | Geometries overlap but neither contains the other | | crosses | Geometries cross each other | | spatialEquals | Geometries are spatially equal |

Temporal Operators

Filter data by time and date relationships. Supports ISO 8601 timestamps, Date objects, and intervals.

Simple temporal filtering

import { anyinteracts } from 'dyno-cql';

// Match a specific timestamp
anyinteracts("eventDate", "2023-01-01T00:00:00Z")
// → ANYINTERACTS(eventDate, TIMESTAMP('2023-01-01T00:00:00Z'))

// Match a time interval
anyinteracts("eventDate", { start: "2023-01-01", end: "2023-12-31" })
// → ANYINTERACTS(eventDate, INTERVAL('2023-01-01', '2023-12-31'))

Point-in-time operators

import { after, before, tequals } from 'dyno-cql';

// After a timestamp
after("eventDate", "2023-01-01T00:00:00Z")
// → AFTER(eventDate, TIMESTAMP('2023-01-01T00:00:00Z'))

// Before a timestamp
before("eventDate", "2023-12-31T23:59:59Z")
// → BEFORE(eventDate, TIMESTAMP('2023-12-31T23:59:59Z'))

// Equal to a timestamp
tequals("eventDate", "2023-06-15T12:00:00Z")
// → TEQUALS(eventDate, TIMESTAMP('2023-06-15T12:00:00Z'))

// Works with Date objects too
const date = new Date("2023-06-15");
after("eventDate", date)
// → AFTER(eventDate, TIMESTAMP('2023-06-15T00:00:00.000Z'))

Interval operators

import { during, toverlaps, overlappedby, tcontains } from 'dyno-cql';

// Event occurs during an interval
during("eventDate", { start: "2023-01-01", end: "2023-12-31" })
// → DURING(eventDate, INTERVAL('2023-01-01', '2023-12-31'))

// Event period overlaps an interval
toverlaps("eventPeriod", { start: "2023-06-01", end: "2023-12-31" })
// → TOVERLAPS(eventPeriod, INTERVAL('2023-06-01', '2023-12-31'))

// Event period is overlapped by an interval
overlappedby("eventPeriod", { start: "2022-06-01", end: "2023-06-30" })
// → OVERLAPPEDBY(eventPeriod, INTERVAL('2022-06-01', '2023-06-30'))

// Event period contains a timestamp
tcontains("eventPeriod", "2023-06-15T12:00:00Z")
// → TCONTAINS(eventPeriod, TIMESTAMP('2023-06-15T12:00:00Z'))

Boundary operators

import { begins, begunby, ends, endedby, meets, metby, tintersects } from 'dyno-cql';

// Period begins at timestamp
begins("eventPeriod", "2023-01-01T00:00:00Z")
// → BEGINS(eventPeriod, TIMESTAMP('2023-01-01T00:00:00Z'))

// Period is begun by timestamp
begunby("eventPeriod", "2023-01-01T00:00:00Z")
// → BEGUNBY(eventPeriod, TIMESTAMP('2023-01-01T00:00:00Z'))

// Period ends at timestamp
ends("eventPeriod", "2023-12-31T23:59:59Z")
// → ENDS(eventPeriod, TIMESTAMP('2023-12-31T23:59:59Z'))

// Period is ended by timestamp
endedby("eventPeriod", "2023-12-31T23:59:59Z")
// → ENDEDBY(eventPeriod, TIMESTAMP('2023-12-31T23:59:59Z'))

// Periods meet (one ends when other begins)
meets("eventPeriod", { start: "2023-07-01", end: "2023-12-31" })
// → MEETS(eventPeriod, INTERVAL('2023-07-01', '2023-12-31'))

// Periods are met by (other ends when one begins)
metby("eventPeriod", { start: "2022-07-01", end: "2023-01-01" })
// → METBY(eventPeriod, INTERVAL('2022-07-01', '2023-01-01'))

// Temporal intersection
tintersects("eventDate", "2023-06-15T12:00:00Z")
// → TINTERSECTS(eventDate, TIMESTAMP('2023-06-15T12:00:00Z'))

Advanced Usage

Reusable conditions

Build conditions once, use them everywhere.

import { queryBuilder, eq, and, gt, ne } from 'dyno-cql';

// Define reusable conditions
const isActive = eq("status", "ACTIVE");
const isAdult = gt("age", 18);
const notDeleted = ne("deleted", true);

// Combine them
const standardFilters = and(isActive, notDeleted);

// Use in multiple queries
const query1 = queryBuilder()
  .filter(and(standardFilters, eq("type", "premium")))
  .toCQL();

const query2 = queryBuilder()
  .filter(and(standardFilters, isAdult))
  .toCQL();

Clone and modify queries

Start with a base query and create variations.

import { queryBuilder, eq, and } from 'dyno-cql';

// Base query
const baseQuery = queryBuilder()
  .filter(eq("type", "product"));

// Create variations
const activeProducts = baseQuery.clone()
  .filter(and(eq("type", "product"), eq("status", "ACTIVE")));

const archivedProducts = baseQuery.clone()
  .filter(and(eq("type", "product"), eq("archived", true)));

URL-safe output

Generate encoded strings ready for URL parameters.

import { queryBuilder, and, eq, contains } from 'dyno-cql';

const query = queryBuilder()
  .filter(
    and(
      eq("name", "John & Jane"),
      contains("description", "100% satisfaction")
    )
  )
  .toCQLUrlSafe();

// Use directly in fetch
fetch(`/api/products?filter=${query}`);

API Reference

Comparison Operators

  • eq(attr, value) - Equal to
  • ne(attr, value) - Not equal to
  • lt(attr, value) - Less than
  • lte(attr, value) - Less than or equal to
  • gt(attr, value) - Greater than
  • gte(attr, value) - Greater than or equal to
  • between(attr, min, max) - Between two values
  • isNull(attr) - Is null
  • isNotNull(attr) - Is not null

Text Operators

  • like(attr, value) - Prefix match (value%)
  • contains(attr, value) - Substring match (%value%)

Logical Operators

  • and(...conditions) - All conditions must match
  • or(...conditions) - Any condition must match
  • not(condition) - Negate a condition

Spatial Operators

  • intersects(attr, geometry) - Geometries intersect
  • disjoint(attr, geometry) - Geometries are disjoint
  • spatialContains(attr, geometry) - Contains geometry
  • within(attr, geometry) - Within geometry
  • touches(attr, geometry) - Touches geometry
  • overlaps(attr, geometry) - Overlaps geometry
  • crosses(attr, geometry) - Crosses geometry
  • spatialEquals(attr, geometry) - Spatially equal

Temporal Operators

Simple CQL:

  • anyinteracts(attr, temporal) - Any temporal interaction

Enhanced operators:

  • after(attr, timestamp) - After timestamp
  • before(attr, timestamp) - Before timestamp
  • tequals(attr, timestamp) - Temporally equal
  • during(attr, interval) - During interval
  • toverlaps(attr, interval) - Overlaps interval
  • overlappedby(attr, interval) - Overlapped by interval
  • tcontains(attr, temporal) - Contains temporal
  • begins(attr, timestamp) - Begins at timestamp
  • begunby(attr, timestamp) - Begun by timestamp
  • ends(attr, timestamp) - Ends at timestamp
  • endedby(attr, timestamp) - Ended by timestamp
  • meets(attr, interval) - Meets interval
  • metby(attr, interval) - Met by interval
  • tintersects(attr, temporal) - Temporally intersects

License

MIT