Semantic Planning
The planner chooses relation filters by cardinality and include strategies by query shape and dialect capabilities. You describe what, it decides how.
Declare what you want. The planner decides how. Then shows you why.
Write what you need in TypeScript or NQL — tables, filters, relations, aggregations.
orm.select('posts').where(eq('published', true)).include('author')The semantic planner selects relation filters by cardinality, selects includes by query shape and capabilities, extracts CTEs, optimizes.
include-strategy: json_agg (PostgreSQL default)Every decision is visible via dump() — SQL, parameters, plan reasoning. Debug before you execute.
SELECT posts.*, COALESCE((SELECT json_agg(...) FROM users ...), '[]') AS author_json FROM posts WHERE posts.published = $1No codegen step. Full SQL observability via dump(). The planner shows you why it chose a strategy, not just what SQL it generated. Native multi-tenant with withSchema().
// See every decision the planner makes
const dump = orm.select('posts')
.where(eq('published', true))
.include('author')
.dump();
dump.plan.decisions
// → [{ type: 'include-strategy',
// choice: 'json_agg',
// context: { relation: 'author', ... },
// reasoning: 'Relation posts.author (belongsTo, cardinality: one) - using json_agg selected by nested output', ... }]Automatic include strategy selection — no manual JOINs for relations. The planner resolves explicit include joins, relation hints, and applicable defaults before query shape and dialect capabilities; flat output uses join or lateral, nested output defaults to json_agg when supported, and recursive includes use cte. Built-in pgvector and ParadeDB helpers.
// Relations just work — planner decides strategy
const users = await orm
.select('users')
.include('posts.comments.author')
.dump();
// Nested 3-level include — zero manual JOINs
// Result: users[].posts[].comments[].authorFirst-class relation handling with .include(). Schema-level type inference without manual interface definitions. Multi-tenant isolation built-in, not bolted on.
// Multi-tenant in one line
const tenantOrm = orm.withSchema('acme_corp');
const users = await tenantOrm
.select('users')
.where(eq('active', true))
.dump();
// → SELECT * FROM "acme_corp"."users" WHERE ...The core planner, schema DSL, query builders, and NQL work independently of any database. Write once, swap adapters.
v1.0.1 highlights: Range operators (rangeOverlaps, rangeContains, rangeContainedBy), dump({ queryName, correlationId }) for end-to-end request tracking, relation-column distinctOn(), NQL tag mutations, and conditional upsert guards.