@samveys/orm

Schema-first models, relations, eager loading, mass-assignment and lazy-loading guards, factories

Version
1.0.3Nov 5, 2026
Install
pnpm add @samveys/orm

This list was read from version 1.0.2; run pnpm sync to refresh it.

Import
import { defineFactory, diffSchema, isEmptyDiff, orm, prunable, pruneModels, … } from '@samveys/orm'

Function (9)

defineFactory Function

A model factory for tests and seeders. `defaults` receives a sequence number so unique columns stay unique: `defineFactory(User, ({ sequence }) => ({ email: \`user\${sequence}@test\` }))`.

function defineFactory<M extends ModelCtor>(model: M, defaults: Defaults<M>): Factory<M>;

Exported from packages/orm/src/factory.ts

diffSchema Function

Compares model schemas with the live database. Missing tables and columns are reported for creation; columns no model defines are only listed, never dropped.

function diffSchema(db: Database, models: readonly ModelCtor[]): Promise<SchemaDiff>;

Exported from packages/orm/src/diff.ts

isEmptyDiff Function

True when the diff would change nothing.

function isEmptyDiff(diff: SchemaDiff): boolean;

Exported from packages/orm/src/diff.ts

orm Function

The `@samveys/orm` plugin: connects models to the `'db'` binding of `database()` and adds `svs make:migration [--diff]`. List it after `database()` in `samveys.config.ts`.

function orm(options?: OrmPluginOptions): import("@samveys/core").Plugin<OrmPluginOptions | undefined>;

Exported from packages/orm/src/plugin.ts

prunable Function

Declares which rows of a model to delete when the scheduled prune job runs.

function prunable<M extends ModelCtor>(model: M, query: (query: ModelQuery<M>) => ModelQuery<M>, options?: {

Exported from packages/orm/src/prune.ts

pruneModels Function

Deletes every row matched by each rule. Pruning is permanent by default, also for soft-deleting models (`force: false` soft-deletes instead). Returns the number of rows deleted per table. Run it from the scheduler, e.g. `schedule.call('model:prune', () => pruneModels(rules)).daily()`.

function pruneModels(rules: readonly PruneRule[]): Promise<Record<string, number>>;

Exported from packages/orm/src/prune.ts

relationsOf Function

Every relation a model class declares, resolved (for schema diff and `svs model:show`).

function relationsOf(model: ModelCtor): readonly ResolvedRelation[];

Exported from packages/orm/src/model.ts

renderMigration Function

Source of one migration file for `diff`. New columns on existing tables are made nullable-safe only if declared so; a NOT NULL column without a default on a filled table fails on the engine, which is reported.

function renderMigration(diff: SchemaDiff, importFrom?: string): string;

Exported from packages/orm/src/diff.ts

useDatabase Function

Connects models to a database manager (the `orm()` plugin calls this on boot). Tests may call it directly. `lazyLoading` decides what reading an unloaded relation does.

function useDatabase(db: Database | undefined, options?: {

Exported from packages/orm/src/model.ts

Value (2)

col Value

Column builders for `Model.define`. The TypeScript type of each attribute is inferred from its builder; dates and times are Temporal values.

const col: {
    /** Auto-increment integer primary key. */
    increments: () => Column<number, true>;

Exported from packages/orm/src/columns.ts

Model Value

Schema-first models: `class User extends Model.define('users', { … }) {}`.

const Model: {
    define: typeof defineModel;
};

Exported from packages/orm/src/model.ts

Interface (25)

BelongsToManyOptions Interface

No documentation comment.

interface BelongsToManyOptions {
    /** Pivot table. Default: both class names in snake_case, sorted, joined with `_` (`role_user`). */
    readonly pivot?: string;
    /** Pivot column pointing to this model. Default: class name in snake_case + `_id`. */
    readonly foreignPivotKey?: string;
    /** Pivot column pointing to the related model. Default: related class name in snake_case + `_id`. */
    readonly relatedPivotKey?: string;
}

Exported from packages/orm/src/contracts.ts

BelongsToOptions Interface

No documentation comment.

interface BelongsToOptions {
    /** Attribute on this model. Default: the relation name + `Id` (`author` → `authorId`). */
    readonly foreignKey?: string;
    /** Attribute on the related model. Default: its primary key. */
    readonly ownerKey?: string;
}

Exported from packages/orm/src/contracts.ts

Column Interface

A column of type `T`. `Optional` is true when `create()` may leave it out (nullable, defaulted or generated). Builders are immutable.

interface Column<T, Optional extends boolean = false> {
    readonly meta: ColumnMeta;
    /** Type-level only. */
    readonly '~type'?: T;
    readonly '~optional'?: Optional;
    nullable(): Column<T | null, true>;
    unique(): Column<T, Optional>;
    default(value: T & DbValue): Column<T, true>;
    /** Overrides the snake_case database column name. */
    column(name: string): Column<T, Optional>;
    references(table: string, column?: string, onDelete?: 'cascade' | 'restrict' | 'set null'): Column<T, Optional>;
}

Exported from packages/orm/src/contracts.ts

ColumnMeta Interface

No documentation comment.

interface ColumnMeta {
    readonly type: ColumnType;
    readonly nullable: boolean;
    readonly unique: boolean;
    readonly primary: boolean;
    readonly default?: DbValue;
    readonly length?: number;
    readonly precision?: readonly [number, number];
    readonly generated?: Generated;
    /** Database column name; defaults to the attribute name in snake_case (`createdAt` → `created_at`). */
  …
Show the whole declaration (17 lines)
interface ColumnMeta {
    readonly type: ColumnType;
    readonly nullable: boolean;
    readonly unique: boolean;
    readonly primary: boolean;
    readonly default?: DbValue;
    readonly length?: number;
    readonly precision?: readonly [number, number];
    readonly generated?: Generated;
    /** Database column name; defaults to the attribute name in snake_case (`createdAt` → `created_at`). */
    readonly column?: string;
    readonly references?: {
        readonly table: string;
        readonly column: string;
        readonly onDelete?: 'cascade' | 'restrict' | 'set null';
    };
}

Exported from packages/orm/src/contracts.ts

CursorPage Interface

Keyset page of models; `next` is an opaque cursor or `null` on the last page.

interface CursorPage<T> {
    readonly data: readonly T[];
    readonly next: string | null;
}

Exported from packages/orm/src/contracts.ts

DefineOptions Interface

No documentation comment.

interface DefineOptions {
    /** Named connection from `config/database.ts`. */
    readonly connection?: string;
    /**
     * Soft deletes: `true` uses a `deletedAt` attribute (add `deletedAt: col.timestamp().nullable()`),
     * or name another nullable timestamp attribute. Deleted rows are excluded from queries by default.
     */
    readonly softDeletes?: boolean | string;
}

Exported from packages/orm/src/contracts.ts

Factory Interface

No documentation comment.

interface Factory<M extends ModelCtor> {
    /** An unsaved instance. */
    make(overrides?: Partial<AttributesOf<M>>): InstanceType<M>;
    create(overrides?: Partial<AttributesOf<M>>, client?: QueryClient): Promise<InstanceType<M>>;
    createMany(count: number, overrides?: Partial<AttributesOf<M>>, client?: QueryClient): Promise<InstanceType<M>[]>;
    /** A factory whose defaults are merged with `state`. */
    state(state: Partial<AttributesOf<M>>): Factory<M>;
}

Exported from packages/orm/src/contracts.ts

FactoryContext Interface

No documentation comment.

interface FactoryContext {
    /** 1, 2, 3 … per factory. */
    readonly sequence: number;
}

Exported from packages/orm/src/contracts.ts

HasManyOptions Interface

No documentation comment.

interface HasManyOptions {
    /** Attribute on the related model. Default: the parent class name in camelCase + `Id` (`User` → `userId`). */
    readonly foreignKey?: string;
    /** Attribute on this model. Default: the primary key. */
    readonly localKey?: string;
}

Exported from packages/orm/src/contracts.ts

ModelBase Interface

Static side of `Model.define(table, columns)`.

interface ModelBase<C extends Columns> {
    new (): Attributes<C> & ModelInstance & RelationFactories;
    readonly '~attributes'?: Attributes<C>;
    readonly definition: ModelDefinition;
    query<M extends ModelCtor>(this: M, client?: QueryClient): ModelQuery<M>;
    all<M extends ModelCtor>(this: M, client?: QueryClient): Promise<InstanceType<M>[]>;
    find<M extends ModelCtor>(this: M, id: string | number | bigint, client?: QueryClient): Promise<InstanceType<M> | undefined>;
    /** Throws SVS_E073 (404). */
    findOrFail<M extends ModelCtor>(this: M, id: string | number | bigint, client?: QueryClient): Promise<InstanceType<M>>;
    /**
  …
Show the whole declaration (19 lines)
interface ModelBase<C extends Columns> {
    new (): Attributes<C> & ModelInstance & RelationFactories;
    readonly '~attributes'?: Attributes<C>;
    readonly definition: ModelDefinition;
    query<M extends ModelCtor>(this: M, client?: QueryClient): ModelQuery<M>;
    all<M extends ModelCtor>(this: M, client?: QueryClient): Promise<InstanceType<M>[]>;
    find<M extends ModelCtor>(this: M, id: string | number | bigint, client?: QueryClient): Promise<InstanceType<M> | undefined>;
    /** Throws SVS_E073 (404). */
    findOrFail<M extends ModelCtor>(this: M, id: string | number | bigint, client?: QueryClient): Promise<InstanceType<M>>;
    /**
     * Inserts one row from checked input: validator output, `fill(input, allowList)`, or an object
     * written in code. `any` and `unknown` are type errors.
     */
    create<M extends ModelCtor, I>(this: M, input: Checked<I, CreateInput<C>>, client?: QueryClient): Promise<InstanceType<M>>;
    /** Keeps only `allowList` attributes of untrusted input (Ecto `cast`). Values are not validated. */
    fill<const K extends keyof C & string>(input: unknown, allowList: readonly K[]): Filled<Partial<Pick<Attributes<C>, K>>>;
    /** A new, unsaved instance. */
    make<M extends ModelCtor, I>(this: M, input?: Checked<I, Partial<Attributes<C>>>): InstanceType<M>;
    scope(build: (query: ScopeQuery<Attributes<C>>) => ScopeQuery<Attributes<C>>): Scope<Attributes<C>>;

Exported from packages/orm/src/contracts.ts

ModelCtor Interface

Any model class.

interface ModelCtor<I = unknown> {
    new (): I;
    readonly definition: ModelDefinition;
}

Exported from packages/orm/src/contracts.ts

ModelDefinition Interface

No documentation comment.

interface ModelDefinition {
    readonly table: string;
    /** Named connection; default the manager's default connection. */
    readonly connection: string | undefined;
    readonly columns: Readonly<Record<string, ColumnMeta>>;
    /** Attribute name of the primary key. */
    readonly primaryKey: string;
    /** Attribute holding the deletion time when the model soft-deletes, else undefined. */
    readonly softDeletes: string | undefined;
}

Exported from packages/orm/src/contracts.ts

ModelInstance Interface

Methods every model instance has. Attribute names may not collide with them (SVS_E082).

interface ModelInstance {
    /** True once the row exists in the database. */
    readonly $exists: boolean;
    /** Inserts or updates (only changed attributes). Constraint violations become SVS_E083 / SVS_E084 (422). */
    save(client?: QueryClient): Promise<this>;
    /** Deletes the row by primary key; a soft-deleting model sets its deletion time instead. */
    delete(client?: QueryClient): Promise<void>;
    /** Soft deletes: deletes the row for real. */
    forceDelete(client?: QueryClient): Promise<void>;
    /** Soft deletes: clears the deletion time. */
  …
Show the whole declaration (23 lines)
interface ModelInstance {
    /** True once the row exists in the database. */
    readonly $exists: boolean;
    /** Inserts or updates (only changed attributes). Constraint violations become SVS_E083 / SVS_E084 (422). */
    save(client?: QueryClient): Promise<this>;
    /** Deletes the row by primary key; a soft-deleting model sets its deletion time instead. */
    delete(client?: QueryClient): Promise<void>;
    /** Soft deletes: deletes the row for real. */
    forceDelete(client?: QueryClient): Promise<void>;
    /** Soft deletes: clears the deletion time. */
    restore(client?: QueryClient): Promise<this>;
    /** Soft deletes: true when the deletion time is set. */
    readonly $trashed: boolean;
    /** Reloads the attributes from the database. */
    refresh(client?: QueryClient): Promise<this>;
    /** Loads relations on this instance (the explicit, allowed form of lazy loading). */
    load(...relations: readonly string[]): Promise<this>;
    /** Assigns checked input to attributes; like `create()`, refuses `any` and `unknown` at the type level. */
    merge(input: Readonly<Record<string, unknown>>): this;
    related<T = unknown>(relation: string): RelatedHandle<T>;
    /** Attributes and loaded relations; bigint as string, Temporal as ISO text. */
    toJSON(): Record<string, unknown>;
}

Exported from packages/orm/src/contracts.ts

ModelQuery Interface

A typed query over one model.

interface ModelQuery<M extends ModelCtor, A = AttributesOf<M>, I = InstanceType<M>> {
    where<K extends keyof A & string>(attribute: K, value: A[K]): ModelQuery<M, A, I>;
    where<K extends keyof A & string>(attribute: K, operator: Operator, value: A[K]): ModelQuery<M, A, I>;
    orWhere<K extends keyof A & string>(attribute: K, value: A[K]): ModelQuery<M, A, I>;
    orWhere<K extends keyof A & string>(attribute: K, operator: Operator, value: A[K]): ModelQuery<M, A, I>;
    whereIn<K extends keyof A & string>(attribute: K, values: readonly A[K][]): ModelQuery<M, A, I>;
    whereNotIn<K extends keyof A & string>(attribute: K, values: readonly A[K][]): ModelQuery<M, A, I>;
    whereNull<K extends keyof A & string>(attribute: K): ModelQuery<M, A, I>;
    whereNotNull<K extends keyof A & string>(attribute: K): ModelQuery<M, A, I>;
    whereBetween<K extends keyof A & string>(attribute: K, range: readonly [A[K], A[K]]): ModelQuery<M, A, I>;
  …
Show the whole declaration (34 lines)
interface ModelQuery<M extends ModelCtor, A = AttributesOf<M>, I = InstanceType<M>> {
    where<K extends keyof A & string>(attribute: K, value: A[K]): ModelQuery<M, A, I>;
    where<K extends keyof A & string>(attribute: K, operator: Operator, value: A[K]): ModelQuery<M, A, I>;
    orWhere<K extends keyof A & string>(attribute: K, value: A[K]): ModelQuery<M, A, I>;
    orWhere<K extends keyof A & string>(attribute: K, operator: Operator, value: A[K]): ModelQuery<M, A, I>;
    whereIn<K extends keyof A & string>(attribute: K, values: readonly A[K][]): ModelQuery<M, A, I>;
    whereNotIn<K extends keyof A & string>(attribute: K, values: readonly A[K][]): ModelQuery<M, A, I>;
    whereNull<K extends keyof A & string>(attribute: K): ModelQuery<M, A, I>;
    whereNotNull<K extends keyof A & string>(attribute: K): ModelQuery<M, A, I>;
    whereBetween<K extends keyof A & string>(attribute: K, range: readonly [A[K], A[K]]): ModelQuery<M, A, I>;
    orderBy<K extends keyof A & string>(attribute: K, direction?: Direction): ModelQuery<M, A, I>;
    limit(count: number): ModelQuery<M, A, I>;
    offset(count: number): ModelQuery<M, A, I>;
    /** Eager-loads relations (and nested ones with `.`) in one query per relation. */
    with(...relations: readonly RelationPath<I>[]): ModelQuery<M, A, I>;
    apply(scope: Scope<A>): ModelQuery<M, A, I>;
    get(): Promise<I[]>;
    first(): Promise<I | undefined>;
    /** Throws SVS_E073 (404) when nothing matches. */
    firstOrFail(): Promise<I>;
    count(): Promise<number>;
    paginate(page: number, perPage: number): Promise<Page<I>>;
    cursorPaginate(perPage: number, cursor?: string | null): Promise<CursorPage<I>>;
    /** Bulk update of matching rows (no model events, no timestamps). */
    update(values: Partial<A>): Promise<number>;
    /** Bulk delete of matching rows. On a soft-deleting model this sets the deletion time instead. */
    delete(): Promise<number>;
    /** Soft deletes: include deleted rows too. */
    withTrashed(): ModelQuery<M, A, I>;
    /** Soft deletes: only deleted rows. */
    onlyTrashed(): ModelQuery<M, A, I>;
    /** Streams matching models in pages of `size`, ordered by primary key, without loading all rows. */
    each(size?: number): AsyncIterable<I>;
}

Exported from packages/orm/src/contracts.ts

OrmPluginOptions Interface

No documentation comment.

interface OrmPluginOptions {
    /** Model classes for `svs make:migration --diff`, or a directory (relative to the app root) to import them from. Default `app/models`. */
    readonly models?: readonly ModelCtor[] | string;
    /** Where generated migrations go, relative to the app root. Default `database/migrations`. */
    readonly migrations?: string;
    /** `throw` (default outside production) or `log` (default in production) for unloaded relations. */
    readonly lazyLoading?: 'throw' | 'log';
    /** Rules for `svs model:prune`, made with `prunable()`; schedule that command. */
    readonly prune?: readonly PruneRule[];
}

Exported from packages/orm/src/contracts.ts

Page Interface

Offset page of models (same shape as the database `Page`, which is limited to rows).

interface Page<T> {
    readonly data: readonly T[];
    readonly page: number;
    readonly perPage: number;
    readonly total: number;
    readonly lastPage: number;
}

Exported from packages/orm/src/contracts.ts

PruneRule Interface

A prune rule: which rows of a model to delete when the scheduled job runs.

interface PruneRule<M extends ModelCtor = ModelCtor> {
    readonly model: M;
    /** The rows to delete, e.g. `(q) => q.where('createdAt', '<', cutoff)`. */
    readonly query: (query: ModelQuery<M>) => ModelQuery<M>;

Exported from packages/orm/src/contracts.ts

RelatedHandle Interface

`user.related('roles')`: work on one relation of one loaded model.

interface RelatedHandle<T> {
    /** A query for the related models. */
    query(): ModelQuery<ModelCtor<T>>;
    /** Inserts a related model with the foreign key set (hasMany, hasOne). */
    create(input: Readonly<Record<string, unknown>>): Promise<T>;
    /** Adds pivot rows (belongsToMany). Existing pairs are skipped. */
    attach(ids: readonly (string | number | bigint)[]): Promise<void>;
    /** Removes pivot rows; every row of this model when `ids` is omitted (belongsToMany). */
    detach(ids?: readonly (string | number | bigint)[]): Promise<number>;
}

Exported from packages/orm/src/contracts.ts

RelationBrand Interface

Marks a property as a relation for `with()` typing.

interface RelationBrand {
    readonly [relationBrand]?: true;
}

Exported from packages/orm/src/contracts.ts

RelationFactories Interface

Relation declarations, used in field initializers: `posts = this.hasMany(() => Post)`.

interface RelationFactories {
    hasMany<T>(target: () => abstract new () => T, options?: HasManyOptions): HasMany<T>;

Exported from packages/orm/src/contracts.ts

SchemaDiff Interface

No documentation comment.

interface SchemaDiff {
    /** Models whose table does not exist. */
    readonly createTables: readonly {
        readonly table: string;
        readonly columns: readonly (ColumnMeta & {
            readonly name: string;
        })[];
    }[];
    /** Columns a model defines that the table lacks. */
    readonly addColumns: readonly {
  …
Show the whole declaration (21 lines)
interface SchemaDiff {
    /** Models whose table does not exist. */
    readonly createTables: readonly {
        readonly table: string;
        readonly columns: readonly (ColumnMeta & {
            readonly name: string;
        })[];
    }[];
    /** Columns a model defines that the table lacks. */
    readonly addColumns: readonly {
        readonly table: string;
        readonly columns: readonly (ColumnMeta & {
            readonly name: string;
        })[];
    }[];
    /** Columns the table has that no model defines; never dropped automatically. */
    readonly unknownColumns: readonly {
        readonly table: string;
        readonly columns: readonly string[];
    }[];
}

Exported from packages/orm/src/contracts.ts

Scope Interface

A reusable query fragment (`static active = this.scope(q => q.where('active', true))`).

interface Scope<A> {
    readonly '~scope': (query: ScopeQuery<A>) => ScopeQuery<A>;

Exported from packages/orm/src/contracts.ts

ScopeQuery Interface

The query methods a scope may use.

interface ScopeQuery<A> {
    where<K extends keyof A & string>(attribute: K, value: A[K]): ScopeQuery<A>;
    where<K extends keyof A & string>(attribute: K, operator: Operator, value: A[K]): ScopeQuery<A>;
    orWhere<K extends keyof A & string>(attribute: K, value: A[K]): ScopeQuery<A>;
    orWhere<K extends keyof A & string>(attribute: K, operator: Operator, value: A[K]): ScopeQuery<A>;
    whereIn<K extends keyof A & string>(attribute: K, values: readonly A[K][]): ScopeQuery<A>;
    whereNull<K extends keyof A & string>(attribute: K): ScopeQuery<A>;
    whereNotNull<K extends keyof A & string>(attribute: K): ScopeQuery<A>;
    orderBy<K extends keyof A & string>(attribute: K, direction?: Direction): ScopeQuery<A>;
    limit(count: number): ScopeQuery<A>;
}

Exported from packages/orm/src/contracts.ts

TimestampColumn Interface

`col.timestamp()`: a `timestamptz` read as `Temporal.Instant`.

interface TimestampColumn extends Column<Temporal.Instant, false> {
    /** Set to now on insert. */
    autoCreate(): Column<Temporal.Instant, true>;
    /** Set to now on insert and on every update. */
    autoUpdate(): Column<Temporal.Instant, true>;
}

Exported from packages/orm/src/contracts.ts

UuidColumn Interface

`col.uuid()`: `.primary()` makes it a UUIDv7 primary key generated on insert (#31).

interface UuidColumn extends Column<string, false> {
    primary(): Column<string, true>;
}

Exported from packages/orm/src/contracts.ts

Type (14)

Attributes Type

Attribute types of a column set.

type Attributes<C extends Columns> = {
    -readonly [K in keyof C]: C[K] extends Column<infer T, boolean> ? T : never;
};

Exported from packages/orm/src/contracts.ts

AttributesOf Type

Attribute map of a model class.

type AttributesOf<M> = M extends {
    readonly '~attributes'?: infer A;
} ? NonNullable<A> : never;

Exported from packages/orm/src/contracts.ts

BelongsTo Type

No documentation comment.

type BelongsTo<T> = (T & RelationBrand) | null;

Exported from packages/orm/src/contracts.ts

BelongsToMany Type

No documentation comment.

type BelongsToMany<T> = readonly T[] & RelationBrand;

Exported from packages/orm/src/contracts.ts

Checked Type

Refuses `any` and `unknown`: raw request data must be validated or filled first.

type Checked<Input, Expected> = 0 extends 1 & Input ? never : unknown extends Input ? never : Input extends Expected ? Input : Expected;

Exported from packages/orm/src/contracts.ts

Columns Type

No documentation comment.

type Columns = Readonly<Record<string, Column<unknown, boolean>>>;

Exported from packages/orm/src/contracts.ts

CreateInput Type

What `create()` needs: required columns, plus any optional ones.

type CreateInput<C extends Columns> = {
    [K in RequiredKeys<C>]: Attributes<C>[K];
} & {
    [K in OptionalKeys<C>]?: Attributes<C>[K];
};

Exported from packages/orm/src/contracts.ts

Filled Type

Returned by `Model.fill(input, allowList)`: only listed attributes survive.

type Filled<T> = T & {
    readonly [filledBrand]: true;
};

Exported from packages/orm/src/contracts.ts

Generated Type

How a column gets its value when the caller leaves it out.

type Generated = 'increments' | 'uuidv7' | 'createdAt' | 'updatedAt';

Exported from packages/orm/src/contracts.ts

HasMany Type

No documentation comment.

type HasMany<T> = readonly T[] & RelationBrand;

Exported from packages/orm/src/contracts.ts

HasOne Type

No documentation comment.

type HasOne<T> = (T & RelationBrand) | null;

Exported from packages/orm/src/contracts.ts

QueryClient Type

Where a model query runs: the default database, another connection, or a transaction.

type QueryClient = Pick<Database, 'from' | 'dialect'> | Pick<Transaction, 'from' | 'dialect'>;

Exported from packages/orm/src/contracts.ts

RelationKeys Type

Relation property names of a model instance.

type RelationKeys<I> = {
    [K in keyof I]-?: [Extract<I[K], RelationBrand>] extends [never] ? never : K;
}[keyof I] & string;

Exported from packages/orm/src/contracts.ts

RelationPath Type

Relation names, with `.` for nested eager loading (`posts.comments`).

type RelationPath<I> = RelationKeys<I> | `${RelationKeys<I>}.${string}`;

Exported from packages/orm/src/contracts.ts