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

@statedelta-libs/expressions

v3.2.0

Published

JSON DSL compiler for optimized functions - StateDelta expression engine

Readme

@statedelta-libs/expressions

Compilador de JSON DSL puro para funções otimizadas.

npm version License: MIT

O que é

Um compilador que transforma expressões declarativas em JSON puro em funções JavaScript de alta performance. A entrada é sempre JSON serializável. A saída é sempre uma (data) => result.

O compilador não conhece nenhuma função — todas vêm via scope fornecido pelo consumer. O mesmo motor serve para matemática, queries de game engine, ETL, NoCode, ou qualquer domínio.

Instalação

pnpm add @statedelta-libs/expressions

Quick Start

import { ExpressionCompiler } from '@statedelta-libs/expressions';

const compiler = new ExpressionCompiler({
  scope: {
    add: (a, b) => a + b,
    multiply: (a, b) => a * b,
    filter: (pred) => (arr) => arr.filter(pred),
    sum: (arr) => arr.reduce((a, b) => a + b, 0),
  },
});

// Compila uma vez
const compiled = compiler.compile({
  $pipe: [
    { $: "items" },
    { $fn: "filter", args: [{ $arrow: { $: "item.active" }, args: ["item"] }] },
    { $fn: "sum" }
  ]
});

// Bind ao contexto → BoundFn callable
const fn = compiled.bind();

// Executa milhões de vezes
fn({ items: [{ active: true, price: 10 }, { active: false, price: 20 }] });
fn.deps;  // ["items"]

Os 5 Primitivos

| Primitivo | O que faz | Exemplo DSL | JS equivalente | |-----------|-----------|-------------|----------------| | $ | Referência a path | { $: "user.name" } | data.user.name | | $fn | Invocação/referência de função | { $fn: "add", args: [...] } | add(a, b) | | $if | Condicional | { $if: cond, then: a, else: b } | cond ? a : b | | $pipe | Composição left-to-right | { $pipe: [x, f, g] } | g(f(x)) | | $arrow | Closure deferida | { $arrow: body, args: ["x"] } | (x) => body |

Tudo 100% JSON-serializável. Funções nunca aparecem no DSL — são referenciadas por nome e resolvidas contra o scope em runtime.

Dois Modos de Compilação

compiler.compile(expr);     // closures — compilação rápida, ~9-20M ops/s
compiler.jit(expr);         // JIT — compilação lenta, ~25-27M ops/s de execução

Closures compõe funções JavaScript aninhadas. Ideal para expressões executadas poucas vezes ou ambientes com CSP restritivo.

JIT gera código JavaScript via AST e new Function(). Ideal para hot paths executados muitas vezes. Break-even em ~8 execuções.

3 Fases: Compile → Bind → Run

Compilação e contexto são independentes. Compile uma vez, bind N vezes com contextos diferentes — zero recompilação.

const compiler = new ExpressionCompiler({ scope });

// Fase 1: Compile (pesado — uma vez) → Artifact
const artifact = compiler.jit(expr);

// Fase 2: Bind (barato — só DI) → BoundFn callable
const forTenant1 = artifact.bind({ scope, accessor: tenant1Accessor });
const forTenant2 = artifact.bind({ scope, accessor: tenant2Accessor });

// Fase 3: Run
forTenant1(data);  // usa accessor do tenant 1
forTenant2(data);  // usa accessor do tenant 2

BoundFn é callable direto — sem .fn. Funciona com .map(), .filter() e qualquer API que espera funções. Metadata como propriedades:

const fn = compiler.compile(expr).bind();
fn(data);    // executa
fn.deps;     // paths observados
fn.hash;     // hash estrutural

DI-first — construtor sem contexto

O compilador funciona sem contexto no construtor. Contexto é injetado via bind():

const compiler = new ExpressionCompiler();  // sem scope/accessor/handlers

const artifact = compiler.compile(expr);
const fn = artifact.bind({ scope, accessor });
fn(data);

Compile cache compartilhável

CompileCache armazena artefatos context-free compartilháveis entre instâncias:

import { ExpressionCompiler, CompileCache } from '@statedelta-libs/expressions';

const sharedCache = new CompileCache(5000);

const tenantA = new ExpressionCompiler({ scope: scopeA, compileCache: sharedCache });
const tenantB = new ExpressionCompiler({ scope: scopeB, compileCache: sharedCache });

// Mesma expressão compilada uma vez, bind por tenant
tenantA.jit(expr).bind()(data);  // compile cache miss → compile + bind
tenantB.jit(expr).bind()(data);  // compile cache hit → só bind

Cenários habilitados:

  • Multi-tenantCompileCache compartilhado, bind por tenant
  • Hot-swap de accessor por tick — compile uma vez, bind a cada tick
  • Testing — mesmo artifact, scope mockado via bind
  • Lazy bind — compile no boot, bind sob demanda

Extensibilidade

Scope — funções disponíveis

const compiler = new ExpressionCompiler({
  scope: {
    add: (a, b) => a + b,
    filter: (pred) => (arr) => arr.filter(pred),
    tryCatch: (arrow, params) => {
      try { return arrow(); }
      catch { return params?.fallback ?? null; }
    },
  },
});

Normalize — DSL customizado

Transforma sugar syntax em DSL puro antes da compilação. Para quando o resultado é expression DSL válido.

const transforms = {
  $double: (node) => ({ $fn: "multiply", args: [node.$double, 2] }),
};

const pure = compiler.normalize({ $double: { $: "value" } }, transforms);
compiler.compile(pure);

Boundaries — compiladores externos

Intercepta nós durante a compilação e terceiriza para outro algoritmo. Para quando o nó precisa de um compilador completamente diferente. Boundaries são compile-time — ficam fixos entre bind() calls.

import type { BoundaryDef } from '@statedelta-libs/expressions';

// $raw — passthrough, nada é compilado
const rawBoundary: BoundaryDef = {
  check: (node) => "$raw" in node,
  handle: (node) => () => node.$raw,
};

// $rules — DSL estrangeiro com compilador próprio
const rulesBoundary: BoundaryDef = {
  check: (node) => "$rules" in node,
  handle: (node) => {
    const compiled = ruleEngine.compile(node.$rules);
    return (data) => compiled.evaluate(data);
  },
};

const compiler = new ExpressionCompiler({
  scope,
  boundaries: [rawBoundary, rulesBoundary],
});

// $rules é interceptado pelo boundary, compilado pelo ruleEngine
compiler.compile({
  $fn: "add",
  args: [{ $: "base" }, { $rules: { when: "vip", then: 20 } }]
});

O handler é uma closure auto-suficiente — captura o que precisa (outros compiladores, databases, etc.) por fora. Zero overhead quando não há boundaries registrados.

| | normalize() | BoundaryDef | |---|---|---| | Quando roda | Antes da compilação | Durante a compilação (no walk) | | Retorno | Expression (DSL puro) | CompiledFn (função pronta) | | Uso típico | Sugar syntax | DSL estrangeiro, $raw, rule engines | | bind() | N/A | Fixo (compile-time) |

Handlers — services com contexto

O scope é para funções puras e stateless. Quando o consumer precisa de services inteligentes que acessam o sistema (accessor, outros handlers, o compilador, o scope), usa handlers.

import type { HandlerContext } from '@statedelta-libs/expressions';

const compiler = new ExpressionCompiler({
  scope: { add: (a, b) => a + b },
  handlers: {
    query: {
      find(key: string) {
        return db.find(key);
      },
      findAll() {
        // chamar outro handler
        const valid = this.handlers.validation.check("all");
        return valid ? db.findAll() : [];
      },
    },
    validation: {
      check(value: unknown) {
        // chamar scope fn
        return this.scope.add(value != null ? 1 : 0, 0) > 0;
      },
    },
  },
});

Handlers são invocados via $fn com sintaxe "namespace:method":

{ "$fn": "query:find", "args": [{ "$": "userId" }] }
{ "$fn": "validation:check", "args": [{ "$": "value" }] }

O contexto é acessado via this, injetado automaticamente via .bind() no construtor. O HandlerContext é criado uma vez — zero alocação por chamada:

| Campo | O que contém | |-------|-------------| | this.accessor | Resolver de paths customizado | | this.handlers | Todos os handlers (wrapped) — permite composição entre handlers | | this.compiler | Instância do compilador — permite compilar sub-expressões | | this.scope | Funções puras do scope |

Handlers devem ser regular functions ou method shorthand (arrow functions ignoram .bind()):

// method shorthand — funciona
handlers: { query: { find(key) { this.scope... } } }

// regular function — funciona
handlers: { query: { find: function(key) { this.scope... } } }

// arrow function — NÃO funciona (this é undefined)
handlers: { query: { find: (key) => { this.scope... } } }

| | scope | handlers | |---|---|---| | Natureza | Funções puras (Ramda-style) | Services com contexto | | Acessa | Apenas args compilados | this (HandlerContext) + args | | DSL | { $fn: "add", args: [...] } | { $fn: "query:find", args: [...] } | | Binding | Nenhum | .bind(ctx) uma vez no construtor | | Overhead | Zero | Zero (ctx e bindings criados uma vez) | | bind() | Trocável via ctx.scope | Trocável via ctx.handlers |

Zero overhead quando nenhum handler é registrado.

Accessor — objetos inteligentes

const compiler = new ExpressionCompiler({
  scope,
  accessor: (path) => tickContext.get(path),  // closure auto-suficiente
});

// Hot-swap por tick via bind()
const artifact = compiler.jit(expr, { useAccessor: true });
const fn = artifact.bind({
  scope,
  accessor: (path) => newTickContext.get(path),
});

Documentação

| Doc | Conteúdo | |-----|----------| | EXPRESSIONS-API.md | Referência completa da API pública | | docs/ARCHITECTURE.md | Arquitetura interna (IR, backends, cache) | | docs/TEMPLATE.md | Template compiler (compileDefinition) |

Licença

MIT © Anderson D. Rosa