API index and search · Build metadata
Source snapshot
packages/client/src/operators.ts
1// Named value-operators (`eq`/`gt`/`like`/…) + boolean combinators (`or`/`and`) for the2// query builder. Operators are VALUE-ONLY — the value type `V` is inferred from the value,3// and the FIELD is bound separately (by `where.<field>(…)` or `table.<field>(…)`), so4// `or`/`exists` need no closure (WASM-CLIENT-DESIGN.md §6).56import type { Condition, LitValue, SimpleOp } from "./ast.ts";78const PRED: unique symbol = Symbol("rindle.pred");910/** A value predicate (an operator applied to a value). `V` is a phantom for type-checking. */11export interface Pred<V> {12 readonly [PRED]: true;13 readonly op: SimpleOp;14 readonly value: LitValue;15 readonly __v?: V;16}1718function mk<V>(op: SimpleOp, value: LitValue): Pred<V> {19 return { [PRED]: true, op, value } as Pred<V>;20}2122export function isPred(x: unknown): x is Pred<unknown> {23 return typeof x === "object" && x !== null && (x as Record<symbol, unknown>)[PRED] === true;24}2526export const eq = <V>(v: V): Pred<V> => mk("=", v as LitValue);27export const ne = <V>(v: V): Pred<V> => mk("!=", v as LitValue);28export const gt = <V extends number | string>(v: V): Pred<V> => mk(">", v as LitValue);29export const ge = <V extends number | string>(v: V): Pred<V> => mk(">=", v as LitValue);30export const lt = <V extends number | string>(v: V): Pred<V> => mk("<", v as LitValue);31export const le = <V extends number | string>(v: V): Pred<V> => mk("<=", v as LitValue);32export const like = (pattern: string): Pred<string> => mk("LIKE", pattern);33export const notLike = (pattern: string): Pred<string> => mk("NOT LIKE", pattern);34export const ilike = (pattern: string): Pred<string> => mk("ILIKE", pattern);35export const notIlike = (pattern: string): Pred<string> => mk("NOT ILIKE", pattern);36export const inList = <V>(values: V[]): Pred<V> => mk("IN", values as LitValue);37export const notInList = <V>(values: V[]): Pred<V> => mk("NOT IN", values as LitValue);38export const is = <V>(v: V): Pred<V> => mk("IS", v as LitValue);39export const isNot = <V>(v: V): Pred<V> => mk("IS NOT", v as LitValue);40/** `IS NULL` — a field-agnostic null check. `V` is inferred from the field's expected `Arg<V>`41 * (e.g. `where.signedAt(isNull())`), so it fits a column of any type without a cast. */42export const isNull = <V = never>(): Pred<V> => mk("IS", null);43/** `IS NOT NULL` — the negation of {@link isNull}; `V` is inferred from the field, like {@link isNull}. */44export const isNotNull = <V = never>(): Pred<V> => mk("IS NOT", null);4546/** A field argument: its typed predicate, or a bare value (bare = `eq` sugar). */47export type Arg<V> = Pred<V> | V;4849/** A condition over row `R`. The runtime value is the wire {@link Condition}; `R` is a50 * phantom brand so a condition for one table can't be `.where()`d onto another. */51export type Cond<R> = Condition & { readonly __row?: R };5253/** Build a `simple` condition from a field name + (predicate | bare value). */54export function fieldCondition(field: string, arg: unknown): Condition {55 const op: SimpleOp = isPred(arg) ? arg.op : "=";56 const value: LitValue = isPred(arg) ? arg.value : (arg as LitValue);57 return {58 type: "simple",59 op,60 left: { type: "column", name: field },61 right: { type: "literal", value },62 };63}6465/** All children must hold. */66export function and<R>(...conds: Cond<R>[]): Cond<R> {67 return { type: "and", conditions: conds } as Cond<R>;68}6970/** At least one child must hold. */71export function or<R>(...conds: Cond<R>[]): Cond<R> {72 return { type: "or", conditions: conds } as Cond<R>;73}74