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

@kevel/zerkel

v1.9.1

Published

A compiler for the zerkel language.

Readme

@kevel/zerkel

The Kevel custom targeting query language: a compiler that turns Zerkel expressions into JavaScript, and a runtime that evaluates the compiled form.

Originally developed in adzerk/zerkel.

Language

Zerkel provides primitive integer and string literals, identifiers, sets, and a selection of boolean operators:

| operator/type | comment | examples | |-------------------|-----------------------------|------------| | integers | syntax | 42, -7 | | strings | syntax | "foo", "C:\\\\Windows\\System32", "^(foo\|bar)\s+" | | variables | syntax | count, _foo_bar, $location.postalCode | | . | property accessor | $location.postalCode, foo.bar.baz | | [, ] | set construction | [42, "foo"] | | (, ) | expression grouping | x < 100 AND (y < 50 OR z < 10) | | = | equality | foo = 42 | | <> | not equal | foo <> "bar" | | =~ | regex match | foo =~ "^(some\|regular\|expression).*$" | | !~ | not regex match | foo !~ "^(some\|regular\|expression).*$" | | > | | foo > 42 | | < | | foo < 42 | | >= | | foo >= 42 | | <= | | foo <= 42 | | AND | | foo = 42 AND bar < 100 | | OR | | foo = 42 OR bar < 100 | | NOT | | NOT (bar =~ "^foo") | | LIKE | wildcard match | foo LIKE "ba*" | | CONTAINS | set membership / substring | ["foo", "bar"] CONTAINS "foo", "foobar" CONTAINS "foo" |

Thus, queries may be written like:

count > 43 and (user = "bob" or user = "alice")
keywords contains "awesome"

All operators are available in upper and lowercase forms.

Installation

npm install @kevel/zerkel

Requires Node.js 24 or later.

Usage

@kevel/zerkel has the same API as the zerkel module of adzerk/zerkel:

const zerkel = require('@kevel/zerkel');

const matches = zerkel.compile('count > 43 and (user = "bob" or user = "alice")');

matches({ count: 50, user: 'bob' });    // true
matches({ count: 12, user: 'alice' });  // false
matches({ count: 50, user: 'george' }); // false

Properties of nested objects can be accessed with .:

const matches = zerkel.compile('user.location = "open field west of a white house"');

matches({ user: { name: 'bob', location: 'open field west of a white house' } }); // true
matches({ user: { name: 'alice', location: 'middle earth' } });                    // false

| Export | Description | |---|---| | compile(expression) | Compiles an expression and returns a Boolean predicate. | | compileDetailed(expression) | Compiles an expression and returns a detailed predicate. | | makePredicate(compiled) | Returns a Boolean predicate for an already compiled expression, including a gzipped (GZ:) one. | | makeDetailedPredicate(compiled) | Returns a detailed predicate for an already compiled expression, including a gzipped one. | | parser | The compiler: parser.parse(expression) returns the compiled expression as a string. |

compile, compileDetailed and parser.parse throw on an invalid expression.

To store compiled expressions and evaluate them later, compile with parser.parse and evaluate with makePredicate:

const compiled = zerkel.parser.parse('count > 43');
zerkel.makePredicate(compiled)({ count: 50 }); // true

Two more entry points hold just the compiler and the runtime:

| Import | Contents | |---|---| | @kevel/zerkel/parser | The compiler on its own: parse(expression). | | @kevel/zerkel/runtime | The runtime on its own: makePredicate(compiled) and makeDetailedPredicate(compiled). It doesn't unzip GZ: strings, and also runs in a browser (see Clientside). |

Detailed evaluation

Use compileDetailed or makeDetailedPredicate when a caller also needs to know which segment IDs an expression references:

const detailed = zerkel.compileDetailed('$user.segments CONTAINS 42');

detailed({ $user: { segments: [42] } });
// { matched: true, referencedSegmentIds: [42], metadataComplete: true }

detailed({ $user: { segments: [7] } });
// { matched: false, referencedSegmentIds: [42], metadataComplete: true }
  • compile and makePredicate always return Boolean predicates; compileDetailed and makeDetailedPredicate return {matched, referencedSegmentIds, metadataComplete}.

  • A reference is collected only from the exact form $user.segments CONTAINS <integer literal>, across the whole expression (including under NOT, AND and OR) and independently of evaluation order. Any other use of $user.segments, such as a variable on the right ($user.segments CONTAINS foo), sets metadataComplete: false for the whole expression instead of reporting a partial set.

  • The references are computed at compile time and embedded in the compiled string as a leading _helpers['captureSegmentMetadata'](...) call, so the compiled form is still a plain string:

    zerkel.parser.parse('$user.segments CONTAINS 42');
    // "(_helpers['captureSegmentMetadata']([42],true),_helpers['idxof']((_env.$user||{}).segments,42))"
  • An expression compiled by an older version has no embedded metadata, so detailed evaluation returns referencedSegmentIds: [] with metadataComplete: false.

Compatibility

Expressions compiled by older versions run unchanged on this runtime. Expressions compiled by this version do not run on runtimes older than 1.9.0, because captureSegmentMetadata doesn't exist there. Deploy the runtime everywhere before newly compiled expressions reach it.

Clientside

Compiled Zerkel expressions can be evaluated in a browser, too. The runtime (dist/zerkel-runtime.min.js) defines zerkelRuntime when loaded with a <script> tag, and also works as an AMD or CommonJS module.

In Node.js:

const { parse } = require('@kevel/zerkel/parser');

const compiled = parse('count > 43 and ["bob", "alice"] contains user');

In the client:

<script src="zerkel-runtime.min.js"></script>
<script>
var compiled    = '...'; // The compiled expression from Node.js above.
var predicateFn = zerkelRuntime.makePredicate(compiled);
var result      = predicateFn({count: 50, user: 'bob'});

var detailedFn = zerkelRuntime.makeDetailedPredicate(compiled);
var details    = detailedFn({count: 50, user: 'bob'});
</script>

To compile in the browser as well, load the browser build after the runtime. demo/demo.js is the parser plus the module, and sets window.zerkel to compile, compileDetailed, makePredicate and makeDetailedPredicate, as adzerk/zerkel's browser build does. As in adzerk/zerkel's index.html, declare module, exports and require first, because the generated parser writes to exports:

<script>
  var module = {exports: {}};
  var exports = module.exports;
  var require = function() {};
</script>
<script src="src/zerkel-runtime.js"></script>
<script src="demo/demo.js"></script>
<script>
var matches = zerkel.compile('count > 43');
matches({count: 50}); // true
</script>

dist/zerkel-runtime.min.js works in place of src/zerkel-runtime.js. In a browser there's no zlib, so gzipped (GZ:) expressions can't be evaluated.

Gzip

Set MIN_GZIP_SIZE on the parser to a number, and any compiled expression at least that long is gzipped, base64-encoded and prefixed with GZ:. The default is Infinity, so nothing is gzipped unless you opt in.

const zerkel = require('@kevel/zerkel');

zerkel.parser.MIN_GZIP_SIZE = 100;
zerkel.parser.parse('foo = 42');
// "(_helpers['captureSegmentMetadata']([],true),_env.foo==42)"
const compiled = zerkel.parser.parse('[42, 43] contains foo and [100, 200, 300] contains bar');
// "GZ:H4sIAAAAAAAAA..."
zerkel.makePredicate(compiled)({ foo: 42, bar: 100 }); // true

makePredicate and makeDetailedPredicate unzip GZ: strings automatically. @kevel/zerkel/runtime on its own doesn't, so decompress before using it directly:

const zlib = require('zlib');
const { makePredicate } = require('@kevel/zerkel/runtime');

function makePredicateFrom(compiled) {
  if (compiled.startsWith('GZ:')) {
    compiled = zlib.unzipSync(Buffer.from(compiled.slice(3), 'base64')).toString();
  }
  return makePredicate(compiled);
}

The leading captureSegmentMetadata(...) call adds at least 46 characters to every compiled expression (foo = 42 compiles to 58 characters, up from 12), so a threshold tuned for versions before 1.9.0 will gzip more expressions than it used to.

Development

From the repository root, install dependencies with npm install (see the root README). Then, in this directory:

| Command | Description | |---|---| | npm run build | Generate dist/zerkel-parser.js (with jison) and dist/zerkel-runtime.min.js (with uglify-js). | | npm test | Compile the CoffeeScript tests and run them with mocha. The tests load dist/, so run npm run build first. | | npm run clean | Remove dist/. |

The published package contains dist/ (the module, the generated parser and the minified runtime), the browser build demo/demo.js, the unminified runtime src/zerkel-runtime.js, package.json, this README and LICENSE. demo/demo.js and src/zerkel-runtime.js keep the paths they have in adzerk/zerkel, and @kevel/zerkel/src/zerkel-parser is an alias of @kevel/zerkel/parser, so code that reads or requires those paths works unchanged.

The test suite includes a version-replay check: every corpus in test-data/ (compiled by earlier versions) is evaluated by the current runtime, with both makePredicate and makeDetailedPredicate. Each run also writes test-data/<version>.json for the current version.

License

Copyright © Kevel, Inc. Distributed under the Apache License, Version 2.0.