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

@qadi/predicate-prisma

v0.11.0

Published

Compiles a @qadi/core Predicate to a Prisma WhereInput

Readme

@qadi/predicate-prisma

Compiles a @qadi/core Predicate into a Prisma WhereInput.

pnpm add @qadi/predicate-prisma @qadi/core effect

@qadi/core's toPredicate emits an abstract, dialect-free AST and stops there on purpose (ADR-QD-024). This package is the optional, separately versioned companion that compiles it — @qadi/core gains no dependency on Prisma through this package existing.

import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import {
  AttributeResolverNone,
  DecisionHistoryUnknown,
  allOf,
  currentSubjectLayer,
  eq,
  hasResourceAttribute,
  makeSubject,
  subject,
  toPredicate,
} from "@qadi/core";
import { compilePrismaWhere } from "@qadi/predicate-prisma";

// Tenancy, compiled once and pushed into the query — nothing here mentions
// a query until `compilePrismaWhere` is called.
const visible = allOf([hasResourceAttribute("tenantId", eq(subject("tenantId")))]);

const services = Layer.mergeAll(
  currentSubjectLayer(makeSubject({ id: "u-1", attributes: { tenantId: "t-1" } })),
  AttributeResolverNone,
  DecisionHistoryUnknown,
);

// Which columns accept NULL — a declaration, never an inspection: this package
// opens no connection and reads no schema. Write the set out, or derive it with
// `nullableFieldsOf` from a DMMF model that keeps `isRequired` (see below).
const nullable = new Set(["deletedAt", "note"]);
// Which columns are `Float`/`Decimal` fields — derive it with `floatingFieldsOf`
// (see below), which reads Prisma's runtime DMMF directly.
const floating = new Set(["amount"]);

const where = await Effect.runPromise(
  toPredicate(visible).pipe(
    Effect.provide(services),
    Effect.flatMap((predicate) => compilePrismaWhere(predicate, { nullable, floating })),
  ),
);
// { tenantId: "t-1" }

const rows = await prisma.invoice.findMany({
  // `PrismaWhereInput` is `Record<string, unknown>` deliberately (see below);
  // the assertion is the one cast every consumer writes, not something this
  // package can avoid on your behalf.
  where: where as Prisma.InvoiceWhereInput,
});

PrismaWhereInput is Record<string, unknown> deliberately: this package never sees a generated Prisma schema, so it cannot claim a narrower type. Assigning the result to your own model's generated WhereInput needs an explicit assertion (or a thin typed wrapper around compilePrismaWhere in your own code) — Record<string, unknown> is not structurally assignable to a generated WhereInput on its own.

Declare which columns accept NULL

compilePrismaWhere's second argument is required: { nullable }, the set of columns that accept NULL. nullableFieldsOf(model) builds it from a DMMF model that keeps isRequired (getDMMF from @prisma/internals; Prisma 7's runtime Prisma.dmmf strips it, so write the set out by hand there). Prisma refuses any filter that mentions null on a required field, and a plain NOT over a nullable column's comparison silently drops the NULL rows the evaluator admits — so the compiler needs this one schema fact. A wrong declaration can only lose rows or fail loudly; it never admits a row the predicate denies. A null comparison on a column declared NOT NULL fails PredicateNotRenderable.

Declare which columns hold floating-point numbers

compilePrismaWhere also requires { floating }, the set of Float and Decimal columns: the ones that can hold Infinity/-Infinity. Derive it with floatingFieldsOf(Prisma.dmmf.datamodel.models[i]) — Prisma 7's runtime DMMF keeps each field's type, so no @prisma/internals is needed.

A gte/lt on a declared column fails PredicateNotRenderable with refusal: "NonFiniteColumn". The evaluator denies an infinite value under every range, but a plain {gte: 3} returns an Infinity row and {lt: 3} a -Infinity one, and Prisma has no filter that keeps them out: {gte: 3, lte: Number.MAX_VALUE} still returns the Infinity row, because Prisma binds the bound as a decimal string SQLite reads back as Infinity. Equality and in on the column are unaffected. Leaving a Float column out of the declaration renders a plain range that can admit an infinite row, which is why the option is required.

Refuses rather than approximates

A Predicate's comparison values are unknown. A value outside the safe allowlist (string | finite number | boolean | null; Date is refused) fails PredicateNotRenderable rather than being handed to Prisma's query engine. So does a MemberOf past maxInValues (default 1000), and a column name outside the identifier rule ("Ascii" by default, identifiers: "UnicodeBmp" opts a Prisma field like é in) or one of Prisma's own operator keywords (AND, not, gte, …). PredicateNotRenderable is @qadi/core's, re-exported here.

Agreement with the evaluator

Every WhereInput this package renders is checked, by property, against @qadi/core's own evaluatePredicate — by running it through a real Prisma Client over SQLite, not a JavaScript model of one. See 31 — Predicate Compilation.

License

MIT