@vibeorm/dsl 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/model.d.ts CHANGED
@@ -27,7 +27,7 @@ import type { CreateModelConfig, IndexBuilder, IndexDef, ModelDef, SchemaDefinit
27
27
  * i.on(["content"], { type: "Gist" }),
28
28
  * ]
29
29
  */
30
- export declare function createIndexBuilder<T extends string = string>(): IndexBuilder<T>;
30
+ export declare function createIndexBuilder<T extends string = string, TSchema extends SchemaDefinition = SchemaDefinition>(): IndexBuilder<T, TSchema>;
31
31
  /**
32
32
  * Creates a model definition. `indexes` and `primaryKey` are constrained to
33
33
  * the model's own column names.
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAIV,iBAAiB,EACjB,YAAY,EAEZ,QAAQ,EAGR,QAAQ,EAER,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAkBpB;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,CAsE/E;AAyCD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,CAAC,OAAO,SAAS,gBAAgB,EAC3D,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,GACjC,QAAQ,CAAC,OAAO,CAAC,CAoBnB;AAID,yDAAyD;AACzD,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG,MAAM,EAAE,CAEpE;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG,QAAQ,EAAE,CAMvE"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAKV,iBAAiB,EAEjB,YAAY,EAEZ,QAAQ,EAGR,QAAQ,EAIR,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAkBpB;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,KAChD,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAkG5B;AAyCD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,CAAC,OAAO,SAAS,gBAAgB,EAC3D,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,GACjC,QAAQ,CAAC,OAAO,CAAC,CAwBnB;AAID,yDAAyD;AACzD,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG,MAAM,EAAE,CAEpE;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,GAAG,QAAQ,EAAE,CAMvE"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The typed predicate builder — the `c` handed to a model's `checks` callback
3
+ * and to an index's `.where()`.
4
+ *
5
+ * Every combinator returns plain {@link PredicateIR}: serializable data, never
6
+ * a JavaScript callback. Nothing downstream evaluates a function, and a
7
+ * predicate survives JSON unchanged. The SQL text is produced exactly once, by
8
+ * `renderPredicate` in @vibeorm/sql, when the schema is compiled and the
9
+ * dialect is finally known.
10
+ *
11
+ * The builder is structural at RUNTIME — it only records names and values — so
12
+ * one instance serves every model. The type parameters on
13
+ * {@link PredicateBuilder} are what bind it to a particular model's columns.
14
+ */
15
+ import type { ExclusionIR, CheckIR, EnumIR, FieldIR, PredicateIR } from "@vibeorm/schema";
16
+ import type { SqlDialect } from "@vibeorm/sql";
17
+ import type { ModelDef, ChecksCallback, ModelCheckConfig, PredicateBuilder, SchemaDefinition } from "./types.ts";
18
+ /**
19
+ * One predicate builder instance. Stateless and structural — the type
20
+ * parameters, not the value, bind it to a model.
21
+ */
22
+ export declare function createPredicateBuilder<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition>(): PredicateBuilder<TColumns, TSchema>;
23
+ /** A model's CHECKs, still un-rendered: raw text stays raw, typed stays typed. */
24
+ export type EvaluatedCheck = {
25
+ readonly kind: "raw";
26
+ readonly name: string;
27
+ readonly expression: string;
28
+ } | {
29
+ readonly kind: "typed";
30
+ readonly name: string;
31
+ readonly predicate: PredicateIR;
32
+ };
33
+ /**
34
+ * Run a model's `checks` declaration, whichever form it took.
35
+ *
36
+ * The raw array form and the typed callback form produce the same shape of
37
+ * answer, so every consumer downstream handles ONE list — and raw text is
38
+ * carried through untouched rather than parsed into a predicate.
39
+ */
40
+ export declare function evaluateChecks(params: {
41
+ checks: readonly ModelCheckConfig[] | ChecksCallback | undefined;
42
+ model: string;
43
+ }): readonly EvaluatedCheck[];
44
+ /** What a predicate needs to know about the model it belongs to. */
45
+ export type PredicateModel = {
46
+ readonly name: string;
47
+ readonly fields: readonly FieldIR[];
48
+ };
49
+ /**
50
+ * A model's CHECKs as IR, with every typed expression compiled to SQL ONCE.
51
+ *
52
+ * `CheckIR.expression` always carries the AUTHORED text — what the schema says,
53
+ * not what a database deparsed back — because that is the currency EPIC 1's
54
+ * differ, desired-state records and DDL renderers all read. The predicate rides
55
+ * alongside as the structured source, so editing a typed expression changes the
56
+ * text and produces a real migration, while re-rendering an unchanged one
57
+ * produces byte-identical text and cannot churn.
58
+ */
59
+ /** Compile exclusions beside CHECKs and index predicates at the schema boundary. */
60
+ export declare function compileExclusions(params: {
61
+ exclusions: ModelDef["__exclusions"];
62
+ model: PredicateModel;
63
+ dialect: SqlDialect;
64
+ }): readonly ExclusionIR[];
65
+ export declare function compileChecks(params: {
66
+ checks: readonly ModelCheckConfig[] | ChecksCallback | undefined;
67
+ model: PredicateModel;
68
+ dialect: SqlDialect;
69
+ enums: readonly EnumIR[];
70
+ }): readonly CheckIR[];
71
+ /**
72
+ * An index's partial predicate as IR: the raw string verbatim, or a typed
73
+ * expression compiled to SQL text plus its structured source.
74
+ */
75
+ export declare function compileIndexPredicate(params: {
76
+ where: string | undefined;
77
+ wherePredicate: PredicateIR | undefined;
78
+ columns: readonly string[];
79
+ model: PredicateModel;
80
+ dialect: SqlDialect;
81
+ enums: readonly EnumIR[];
82
+ }): {
83
+ readonly where: string;
84
+ readonly wherePredicate?: PredicateIR;
85
+ } | undefined;
86
+ //# sourceMappingURL=predicate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicate.d.ts","sourceRoot":"","sources":["../src/predicate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAkB,MAAM,iBAAiB,CAAC;AAE1G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,gBAAgB,EAEhB,gBAAgB,EAEhB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAyEpB;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,SAAS,MAAM,GAAG,MAAM,EAChC,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,KAChD,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CA8DvC;AAID,kFAAkF;AAClF,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;CAAE,CAAC;AAEvF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,cAAc,GAAG,SAAS,CAAC;IACjE,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,SAAS,cAAc,EAAE,CAmB5B;AAID,oEAAoE;AACpE,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACrC,CAAC;AAEF;;;;;;;;;GASG;AACH,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAAC,KAAK,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GAAG,SAAS,WAAW,EAAE,CAGtJ;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,cAAc,GAAG,SAAS,CAAC;IACjE,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,GAAG,SAAS,OAAO,EAAE,CAkBrB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,cAAc,EAAE,WAAW,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG,SAAS,CAwBhF"}
@@ -20,7 +20,7 @@
20
20
  * parent) use the `@relation` alias when there is one, else the source model
21
21
  * name, suffixed on collision.
22
22
  */
23
- import type { CascadeAction, RelationType } from "@vibeorm/schema";
23
+ import type { CascadeAction, PredicateIR, RelationType } from "@vibeorm/schema";
24
24
  import type { IndexType, ModelDef, RelationsMap, SchemaDef } from "./types.ts";
25
25
  /**
26
26
  * Structural role of a planned relation field:
@@ -43,7 +43,11 @@ export type PlannedRelation = {
43
43
  readonly isOptional: boolean;
44
44
  /** `@relation("Name")` — the DSL's `alias`. */
45
45
  readonly relationName?: string;
46
+ /** Physical FOREIGN KEY name — the DSL's `constraintName`, never the alias. */
47
+ readonly constraintName?: string;
48
+ /** FK columns on this model, in declared order; member `i` pairs with `references[i]`. */
46
49
  readonly fields?: readonly string[];
50
+ /** Referenced columns on the target model, in declared order. */
47
51
  readonly references?: readonly string[];
48
52
  readonly onDelete?: CascadeAction;
49
53
  readonly onUpdate?: CascadeAction;
@@ -56,6 +60,8 @@ export type PlannedIndex = {
56
60
  readonly indexType?: IndexType;
57
61
  /** Partial-index predicate (raw SQL) — postgres/pglite/sqlite only. */
58
62
  readonly where?: string;
63
+ /** Partial-index predicate as a typed expression, rendered when the dialect is known. */
64
+ readonly wherePredicate?: PredicateIR;
59
65
  /** `UNIQUE NULLS NOT DISTINCT` — unique indexes on postgres/pglite (15+) only. */
60
66
  readonly nullsNotDistinct?: boolean;
61
67
  /** Client selector name for a compound unique (Prisma `@@unique(..., name:)`). */
@@ -74,6 +80,8 @@ export type MergedOneToOneSide = {
74
80
  /** Merged referential actions — the FK owner's win. */
75
81
  readonly onDelete?: CascadeAction;
76
82
  readonly onUpdate?: CascadeAction;
83
+ /** Merged physical FK name — the FK owner's wins; either side may declare it. */
84
+ readonly constraintName?: string;
77
85
  };
78
86
  /**
79
87
  * Finds pairs of `r.one` relations that describe the same 1:1 relationship
@@ -1 +1 @@
1
- {"version":3,"file":"relation-plan.d.ts","sourceRoot":"","sources":["../src/relation-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EACV,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,SAAS,EAEV,MAAM,YAAY,CAAC;AAIpB;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjE,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;CACnC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC,CAAC;IACpE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC,CAAC;CAChE,CAAC;AAIF,+DAA+D;AAC/D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;CACnC,CAAC;AAQF;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACvC,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAmElC;AA4QD,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,UAAU,CA8BpE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB,GAAG,YAAY,CAgBf"}
1
+ {"version":3,"file":"relation-plan.d.ts","sourceRoot":"","sources":["../src/relation-plan.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAGV,QAAQ,EACR,eAAe,EAEf,gBAAgB,EAChB,YAAY,EACZ,eAAe,EAGhB,MAAM,YAAY,CAAC;AAgIpB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,SAAS,YAAY,EAC1G,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,GAC7E,OAAO,CAGT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,SAAS,YAAY,EAC9G,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,GACtF,OAAO,CAGT;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,KAAK,CAAC,MAAM,SAAS,SAAS,YAAY,EAAE,EAAE,GAAG,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAWtH"}
1
+ {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAKV,QAAQ,EACR,eAAe,EAGf,gBAAgB,EAChB,YAAY,EACZ,eAAe,EAGhB,MAAM,YAAY,CAAC;AAmMpB;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,SAAS,YAAY,EAC1G,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,GAC7E,OAAO,CAGT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,SAAS,YAAY,EAC9G,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,GACtF,OAAO,CAGT;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,KAAK,CAAC,MAAM,SAAS,SAAS,YAAY,EAAE,EAAE,GAAG,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAWtH"}
package/dist/to-ir.d.ts CHANGED
@@ -26,12 +26,25 @@
26
26
  * - `.collation()` / `{ collation }` → `FieldIR.collation`.
27
27
  * - relation types are derived structurally by `resolveRelationType`.
28
28
  */
29
- import type { DefaultValueIR, ExtensionRegistry, Provider, SchemaIR } from "@vibeorm/schema";
29
+ import type { DefaultValueIR, ExtensionRegistry, FieldIR, Provider, SchemaIR } from "@vibeorm/schema";
30
30
  import type { ColumnDef, SchemaDef } from "./types.ts";
31
31
  /** Compile a column default once for both DSL output paths. */
32
32
  export declare function fieldDefault(params: {
33
33
  column: ColumnDef;
34
34
  }): DefaultValueIR | undefined;
35
+ /**
36
+ * One column as `FieldIR`.
37
+ *
38
+ * Exported so the `.prisma` printer compiles typed CHECK and partial-index
39
+ * expressions against exactly the same physical names and scalar types the IR
40
+ * path uses — a second mapping is how the two would drift apart.
41
+ */
42
+ export declare function buildField(params: {
43
+ name: string;
44
+ column: ColumnDef;
45
+ /** Native type resolved by an extension field-type lowering, when present. */
46
+ extensionNativeType?: string;
47
+ }): FieldIR;
35
48
  /**
36
49
  * Compiles a composed DSL schema to the canonical `SchemaIR`, then validates it.
37
50
  *
@@ -1 +1 @@
1
- {"version":3,"file":"to-ir.d.ts","sourceRoot":"","sources":["../src/to-ir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAWH,OAAO,KAAK,EACV,cAAc,EAGd,iBAAiB,EAMjB,QAAQ,EAER,QAAQ,EAET,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EACV,SAAS,EAKT,SAAS,EAEV,MAAM,YAAY,CAAC;AAwDpB,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,cAAc,GAAG,SAAS,CAyCtF;AA+RD;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC,GAAG,QAAQ,CA+CX"}
1
+ {"version":3,"file":"to-ir.d.ts","sourceRoot":"","sources":["../src/to-ir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAYH,OAAO,KAAK,EACV,cAAc,EAGd,iBAAiB,EACjB,OAAO,EAKP,QAAQ,EAER,QAAQ,EAET,MAAM,iBAAiB,CAAC;AAQzB,OAAO,KAAK,EACV,SAAS,EAKT,SAAS,EAEV,MAAM,YAAY,CAAC;AA6BpB,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,cAAc,GAAG,SAAS,CAyCtF;AA6CD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,8EAA8E;IAC9E,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,GAAG,OAAO,CA8BV;AA0OD;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC,GAAG,QAAQ,CAmDX"}
package/dist/types.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * through `defineRelations` (Drizzle-style), which keeps column references
6
6
  * type-safe without inference-heavy model types.
7
7
  */
8
- import type { CascadeAction, EnumStorage, ExtensionAttributeValue, RlsContextIR, RlsContextRefIR, RlsPolicyIR } from "@vibeorm/schema";
8
+ import type { ExclusionSpec, ExclusionEndpoint, CascadeAction, EnumStorage, ExtensionAttributeValue, PredicateIR, Provider, RlsContextIR, RlsContextRefIR, RlsPolicyIR } from "@vibeorm/schema";
9
9
  import type { ChainableEnumColumn } from "./column.ts";
10
10
  /** Referential actions, re-exported from the IR so both sources agree. */
11
11
  export type { CascadeAction };
@@ -175,11 +175,59 @@ export type ChildrenDef = {
175
175
  export type ChildrenBuilder = {
176
176
  children(config: ChildrenRelationConfig): ChildrenDef;
177
177
  };
178
- /** A named CHECK constraint (raw SQL expression) on a model. */
178
+ /**
179
+ * A named CHECK constraint written as a RAW SQL expression.
180
+ *
181
+ * This is the documented escape hatch for anything the typed expression
182
+ * language cannot say (database-specific functions, operators VibeORM does not
183
+ * model). The text is carried through VERBATIM: never parsed, never
184
+ * re-rendered, never checked against the model — so physical column names and
185
+ * dialect-specific syntax are the author's responsibility here.
186
+ */
187
+ /** Range operands are restricted to the schema's matching scalar columns. */
188
+ export type ExclusionConfig<TSchema extends SchemaDefinition> = Omit<ExclusionSpec<SchemaColumnNames<TSchema>>, "range"> & {
189
+ readonly range: ({
190
+ readonly type: "int8range";
191
+ readonly lower: ExclusionEndpoint<ExclusionColumns<TSchema, "bigint">>;
192
+ readonly upper: ExclusionEndpoint<ExclusionColumns<TSchema, "bigint">>;
193
+ } | {
194
+ readonly type: "tsrange" | "tstzrange";
195
+ readonly lower: ExclusionEndpoint<ExclusionColumns<TSchema, "datetime">>;
196
+ readonly upper: ExclusionEndpoint<ExclusionColumns<TSchema, "datetime">>;
197
+ }) & {
198
+ readonly bounds: "[)" | "[]" | "()" | "(]";
199
+ };
200
+ };
201
+ type ExclusionColumns<TSchema extends SchemaDefinition, TType extends ColumnType> = {
202
+ [K in keyof TSchema]: TSchema[K] extends ColumnDef<TType> ? K : never;
203
+ }[keyof TSchema] & string;
204
+ /** Predicate callback for a model's exclusion declarations. */
205
+ export type ExclusionsCallback<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition> = (c: PredicateBuilder<TColumns, TSchema>) => readonly ExclusionSpec<TColumns>[];
179
206
  export type ModelCheckConfig = {
180
207
  readonly name: string;
181
208
  readonly expression: string;
182
209
  };
210
+ /**
211
+ * A named CHECK constraint built from a TYPED expression (`c.named(...)`).
212
+ *
213
+ * The predicate is serializable data compiled to SQL once, when the schema
214
+ * becomes IR or `.prisma` text — never a JavaScript callback carried
215
+ * downstream.
216
+ */
217
+ export type NamedCheckDef = {
218
+ readonly __type: "check";
219
+ readonly name: string;
220
+ readonly predicate: PredicateIR;
221
+ };
222
+ /**
223
+ * The `c` handed to a model's `checks` callback, returning named typed CHECKs.
224
+ *
225
+ * @example
226
+ * checks: (c) => [
227
+ * c.named("dispatch_clock", c.gt(c.field("deadlineAt"), c.field("startedAt"))),
228
+ * ]
229
+ */
230
+ export type ChecksCallback<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition> = (c: PredicateBuilder<TColumns, TSchema>) => NamedCheckDef[];
183
231
  /**
184
232
  * Application-level row-scoping policy — the DSL twin of
185
233
  * `@@policy([field], context: "key")`. The runtime fails closed on policied
@@ -200,8 +248,18 @@ export type IndexDef = {
200
248
  readonly __type: "index" | "unique";
201
249
  readonly __columns: readonly string[];
202
250
  readonly __indexType?: IndexType;
203
- /** Partial-index predicate (raw SQL) — postgres/pglite/sqlite only. */
251
+ /**
252
+ * Partial-index predicate as RAW SQL — postgres/pglite/sqlite only. Carried
253
+ * verbatim, exactly like a raw CHECK: the escape hatch for predicates the
254
+ * typed expression language cannot say.
255
+ */
204
256
  readonly __where?: string;
257
+ /**
258
+ * Partial-index predicate as a TYPED expression (`.where((c) => …)`),
259
+ * compiled to SQL once when the schema becomes IR or `.prisma` text. Refused
260
+ * alongside `__where` — one index has one predicate.
261
+ */
262
+ readonly __wherePredicate?: PredicateIR;
205
263
  /** `UNIQUE NULLS NOT DISTINCT` — unique indexes on postgres/pglite (15+) only. */
206
264
  readonly __nullsNotDistinct?: boolean;
207
265
  /** Client selector name for a compound unique (Prisma `@@unique(..., name:)`). */
@@ -230,20 +288,116 @@ export type UniqueIndexOptions = {
230
288
  */
231
289
  name?: string;
232
290
  };
291
+ /**
292
+ * An index that can still be narrowed by a TYPED partial predicate.
293
+ *
294
+ * `.where()` is terminal — it returns the plain {@link IndexDef} — so an index
295
+ * carries at most one predicate and the chain cannot be reopened.
296
+ *
297
+ * @example
298
+ * i.index("deadlineAt").where((c) => c.isNull("cancelledAt"))
299
+ */
300
+ export type PartialIndexDef<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition> = IndexDef & {
301
+ where(predicate: (c: PredicateBuilder<TColumns, TSchema>) => PredicateIR): IndexDef;
302
+ };
233
303
  /** Index builder; column names are constrained to the model's own columns. */
234
- export type IndexBuilder<TColumns extends string = string> = {
235
- on<T extends TColumns>(...columns: T[]): IndexDef;
236
- on<T extends TColumns>(columns: T[], options: IndexOptions): IndexDef;
304
+ export type IndexBuilder<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition> = {
305
+ on<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
306
+ on<T extends TColumns>(columns: T[], options: IndexOptions): PartialIndexDef<TColumns, TSchema>;
237
307
  /** Alias of `on(...)` — reads better next to `unique(...)`. */
238
- index<T extends TColumns>(...columns: T[]): IndexDef;
239
- unique<T extends TColumns>(...columns: T[]): IndexDef;
240
- unique<T extends TColumns>(columns: T[], options: UniqueIndexOptions): IndexDef;
241
- gin<T extends TColumns>(...columns: T[]): IndexDef;
242
- gist<T extends TColumns>(...columns: T[]): IndexDef;
243
- hash<T extends TColumns>(...columns: T[]): IndexDef;
244
- brin<T extends TColumns>(...columns: T[]): IndexDef;
245
- };
246
- export type IndexCallback<TColumns extends string = string> = (i: IndexBuilder<TColumns>) => IndexDef[];
308
+ index<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
309
+ unique<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
310
+ unique<T extends TColumns>(columns: T[], options: UniqueIndexOptions): PartialIndexDef<TColumns, TSchema>;
311
+ gin<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
312
+ gist<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
313
+ hash<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
314
+ brin<T extends TColumns>(...columns: T[]): PartialIndexDef<TColumns, TSchema>;
315
+ };
316
+ export type IndexCallback<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition> = (i: IndexBuilder<TColumns, TSchema>) => IndexDef[];
317
+ /**
318
+ * The JavaScript literal a column of each pairing tag admits.
319
+ *
320
+ * `DateTime` is deliberately `never`: the same instant is spelled three
321
+ * different ways across postgres, mysql and sqlite, and guessing wrong is a
322
+ * silent data bug rather than an error — so a fixed instant goes through the
323
+ * raw-expression escape hatch, while "end after start" is a column-to-column
324
+ * comparison that needs no literal at all. `Json`, `Bytes` and `Unsupported`
325
+ * are `never` because VibeORM does not model their operators.
326
+ *
327
+ * `Decimal` takes exact decimal TEXT (`"19.99"`) for the reason column defaults
328
+ * do: a JS number cannot carry an exact decimal.
329
+ */
330
+ type PredicateLiteralByRefKind = {
331
+ readonly String: string;
332
+ readonly Boolean: boolean;
333
+ readonly Int: number;
334
+ readonly BigInt: bigint | number;
335
+ readonly Float: number;
336
+ readonly Decimal: string;
337
+ readonly DateTime: never;
338
+ readonly Json: never;
339
+ readonly Bytes: never;
340
+ readonly Enum: string;
341
+ readonly Id: string | number;
342
+ readonly Unsupported: never;
343
+ };
344
+ /**
345
+ * A reference to one of the model's own fields inside a predicate.
346
+ *
347
+ * Structurally a `PredicateFieldRef` (`{ kind: "field", name }`) plus the
348
+ * pairing tag comparisons are checked against. The tag carries no value and is
349
+ * stripped when the builder writes the IR, so nothing `__kind`-shaped is ever
350
+ * serialized.
351
+ */
352
+ export type PredicateFieldExpr<TColumn extends string = string, TKind extends ColumnRefKind = ColumnRefKind> = {
353
+ readonly kind: "field";
354
+ readonly name: TColumn;
355
+ readonly __kind: TKind;
356
+ };
357
+ /**
358
+ * What may sit opposite a field of a given tag: a literal of its own scalar, or
359
+ * another field whose scalar can be compared with it.
360
+ *
361
+ * Column pairing reads {@link PairableRefKind} — the SAME table foreign keys
362
+ * use — because a foreign key and a comparison ask one question: are these two
363
+ * columns the same scalar family?
364
+ */
365
+ type ComparisonOperand<TKind extends ColumnRefKind> = PredicateLiteralByRefKind[TKind] | PredicateFieldExpr<string, PairableRefKind[TKind]>;
366
+ /**
367
+ * The `c` handed to `checks` and to an index's `.where()`.
368
+ *
369
+ * Every combinator returns serializable {@link PredicateIR} — never a callback
370
+ * — and the SQL text is produced once, when the schema is compiled and the
371
+ * dialect is known.
372
+ *
373
+ * SQL three-valued logic applies unchanged: a comparison against NULL is
374
+ * UNKNOWN, and a CHECK passes on TRUE **or** UNKNOWN, so a comparison alone
375
+ * never rejects a NULL row. Nothing here adds a hidden null guard — write
376
+ * all-or-none out with {@link PredicateBuilder.isNull} / `isNotNull`.
377
+ */
378
+ export type PredicateBuilder<TColumns extends string = string, TSchema extends SchemaDefinition = SchemaDefinition> = {
379
+ /** Reference one of the model's own fields, by LOGICAL name. */
380
+ field<C extends TColumns>(name: C): PredicateFieldExpr<C, SchemaColumnRefKind<TSchema, C>>;
381
+ eq<TKind extends ColumnRefKind>(left: PredicateFieldExpr<string, TKind>, right: ComparisonOperand<TKind>): PredicateIR;
382
+ ne<TKind extends ColumnRefKind>(left: PredicateFieldExpr<string, TKind>, right: ComparisonOperand<TKind>): PredicateIR;
383
+ gt<TKind extends ColumnRefKind>(left: PredicateFieldExpr<string, TKind>, right: ComparisonOperand<TKind>): PredicateIR;
384
+ gte<TKind extends ColumnRefKind>(left: PredicateFieldExpr<string, TKind>, right: ComparisonOperand<TKind>): PredicateIR;
385
+ lt<TKind extends ColumnRefKind>(left: PredicateFieldExpr<string, TKind>, right: ComparisonOperand<TKind>): PredicateIR;
386
+ lte<TKind extends ColumnRefKind>(left: PredicateFieldExpr<string, TKind>, right: ComparisonOperand<TKind>): PredicateIR;
387
+ /** `col IN (…)`. Membership, not a set operation — every value is a literal. */
388
+ inList<TKind extends ColumnRefKind>(field: PredicateFieldExpr<string, TKind>, values: readonly PredicateLiteralByRefKind[TKind][]): PredicateIR;
389
+ /** `col NOT IN (…)`. NULL in the column stays UNKNOWN, as SQL defines it. */
390
+ notInList<TKind extends ColumnRefKind>(field: PredicateFieldExpr<string, TKind>, values: readonly PredicateLiteralByRefKind[TKind][]): PredicateIR;
391
+ /** `col IS NULL` — the EXPLICIT null test; a comparison never implies one. */
392
+ isNull<C extends TColumns>(name: C): PredicateIR;
393
+ /** `col IS NOT NULL`. */
394
+ isNotNull<C extends TColumns>(name: C): PredicateIR;
395
+ and(...operands: PredicateIR[]): PredicateIR;
396
+ or(...operands: PredicateIR[]): PredicateIR;
397
+ not(operand: PredicateIR): PredicateIR;
398
+ /** Name a predicate as a CHECK constraint. The name keys the migration diff. */
399
+ named(name: string, predicate: PredicateIR): NamedCheckDef;
400
+ };
247
401
  /** A model. `TSchema` is preserved so relations can reference real columns. */
248
402
  export type ModelDef<TSchema extends SchemaDefinition = SchemaDefinition> = {
249
403
  readonly __type: "model";
@@ -256,8 +410,12 @@ export type ModelDef<TSchema extends SchemaDefinition = SchemaDefinition> = {
256
410
  readonly __map?: string;
257
411
  /** `@@id([...])` — compound primary key. */
258
412
  readonly __primaryKey?: readonly string[];
259
- /** Named CHECK constraints (`@@check`-equivalent). */
260
- readonly __checks?: readonly ModelCheckConfig[];
413
+ /**
414
+ * Named CHECK constraints (`@@check`-equivalent): the raw-SQL array form, or
415
+ * the typed-expression callback. Evaluated once, where the dialect is known.
416
+ */
417
+ readonly __exclusions?: readonly ExclusionSpec[] | ExclusionsCallback;
418
+ readonly __checks?: readonly ModelCheckConfig[] | ChecksCallback;
261
419
  /** Extension attributes declared on the model (`@@search`-style). */
262
420
  readonly __extensions?: readonly ExtensionAttributeUse[];
263
421
  /** `@@policy([field], context: "key")` — application-level row scoping. */
@@ -275,13 +433,23 @@ export type SchemaColumnNames<T> = Extract<ColumnKeys<T>, string>;
275
433
  export type CreateModelConfig<TSchema extends SchemaDefinition = SchemaDefinition> = {
276
434
  name: string;
277
435
  schema: TSchema | ((s: ChildrenBuilder) => TSchema);
278
- indexes?: IndexCallback<SchemaColumnNames<TSchema>>;
436
+ indexes?: IndexCallback<SchemaColumnNames<TSchema>, TSchema>;
279
437
  /** `@@map("table_name")` — database table name when it differs. */
280
438
  map?: string;
281
439
  /** `@@id([...])` — compound primary key columns. */
282
440
  primaryKey?: SchemaColumnNames<TSchema>[];
283
- /** Named CHECK constraints — raw SQL expressions, name-keyed in migrations. */
284
- checks?: readonly ModelCheckConfig[];
441
+ /**
442
+ * Named CHECK constraints, name-keyed in migrations. Either the TYPED
443
+ * expression callback (checked against this model's fields) or the raw-SQL
444
+ * array form, which is carried verbatim as the escape hatch.
445
+ *
446
+ * @example
447
+ * checks: (c) => [c.named("qty_nonnegative", c.gte(c.field("qty"), 0))]
448
+ * @example
449
+ * checks: [{ name: "qty_nonnegative", expression: '"qty" >= 0' }]
450
+ */
451
+ exclusions?: readonly ExclusionConfig<TSchema>[] | ((c: PredicateBuilder<SchemaColumnNames<TSchema>, TSchema>) => readonly ExclusionConfig<TSchema>[]);
452
+ checks?: readonly ModelCheckConfig[] | ChecksCallback<SchemaColumnNames<TSchema>, TSchema>;
285
453
  /** Extension model attributes (`@@search`-style declarations). */
286
454
  extensions?: readonly ExtensionAttributeUse[];
287
455
  /**
@@ -292,11 +460,61 @@ export type CreateModelConfig<TSchema extends SchemaDefinition = SchemaDefinitio
292
460
  /** Model doc comment — emitted as `///` lines above the model, kept as `ModelIR.documentation`. */
293
461
  comment?: string;
294
462
  };
463
+ /**
464
+ * The scalar identity relation pairing compares.
465
+ *
466
+ * Deliberately coarser than `FieldType`: `dsl.id()` carries the single tag
467
+ * `"Id"` because the DSL type does not record whether an id is String-backed or
468
+ * `autoincrement`, and an enum column carries `"Enum"` without its name. The
469
+ * type level rejects the pairs no id strategy and no enum could ever make legal
470
+ * (String↔Int, DateTime↔String); relation planning compares the real
471
+ * `FieldType` — id strategy and enum name included — when it builds the IR.
472
+ */
473
+ export type ColumnRefKind = "String" | "Boolean" | "Int" | "BigInt" | "Float" | "Decimal" | "DateTime" | "Json" | "Bytes" | "Enum" | "Id" | "Unsupported";
474
+ /** `ColumnType` → `ColumnRefKind`, as a table: one indexed access, never a conditional chain. */
475
+ type RefKindByColumnType = {
476
+ readonly string: "String";
477
+ readonly int: "Int";
478
+ readonly bigint: "BigInt";
479
+ readonly float: "Float";
480
+ readonly decimal: "Decimal";
481
+ readonly boolean: "Boolean";
482
+ readonly datetime: "DateTime";
483
+ readonly json: "Json";
484
+ readonly bytes: "Bytes";
485
+ readonly id: "Id";
486
+ readonly enum: "Enum";
487
+ readonly unsupported: "Unsupported";
488
+ };
489
+ /** The pairing tag of a column kind. */
490
+ export type ColumnRefKindOf<TColumn extends ColumnType> = RefKindByColumnType[TColumn];
491
+ /**
492
+ * Which tags may sit OPPOSITE a given tag in a relation pair. Symmetric, and
493
+ * `"Id"` pairs with both scalars an id column can be backed by. `Json` and an
494
+ * `Unsupported` column can never be a foreign key on any dialect here, so they
495
+ * pair with nothing.
496
+ */
497
+ type PairableRefKind = {
498
+ readonly String: "String" | "Id";
499
+ readonly Boolean: "Boolean";
500
+ readonly Int: "Int" | "Id";
501
+ readonly BigInt: "BigInt";
502
+ readonly Float: "Float";
503
+ readonly Decimal: "Decimal";
504
+ readonly DateTime: "DateTime";
505
+ readonly Json: never;
506
+ readonly Bytes: "Bytes";
507
+ readonly Enum: "Enum";
508
+ readonly Id: "Id" | "String" | "Int";
509
+ readonly Unsupported: never;
510
+ };
295
511
  /** A reference to `Model.column`, used for relation `from` / `to`. */
296
- export type ColumnRef<TModel extends string = string, TColumn extends string = string> = {
512
+ export type ColumnRef<TModel extends string = string, TColumn extends string = string, TKind extends ColumnRefKind = ColumnRefKind> = {
297
513
  readonly __type: "columnRef";
298
514
  readonly __model: TModel;
299
515
  readonly __column: TColumn;
516
+ /** Pairing tag; carries no value, only the scalar identity `to` is checked against. */
517
+ readonly __kind: TKind;
300
518
  through<TJunction extends string, TJunctionCol extends string>(junction: ColumnRef<TJunction, TJunctionCol>): ThroughRef<TModel, TColumn, TJunction, TJunctionCol>;
301
519
  };
302
520
  /** A column reference routed through a junction model (many-to-many). */
@@ -307,24 +525,48 @@ export type ThroughRef<TModel extends string = string, TColumn extends string =
307
525
  readonly __junction: TJunction;
308
526
  readonly __junctionColumn: TJunctionCol;
309
527
  };
528
+ /**
529
+ * One end of a relation reference: a single column (today's syntax) or an
530
+ * ordered tuple of columns from ONE model (a composite key).
531
+ */
532
+ export type RelationRefInput = ColumnRef | ThroughRef | readonly ColumnRef[];
533
+ /** The `to` partner a single `from` column admits. */
534
+ type PairedColumnRef<TRef> = TRef extends ColumnRef<string, string, infer TKind extends ColumnRefKind> ? ColumnRef<string, string, PairableRefKind[TKind]> : never;
535
+ /**
536
+ * The `to` a given `from` admits: the same arity, each position restricted to
537
+ * the scalar tags its partner can pair with. Mapping over a tuple keeps its
538
+ * length, so a `to` of the wrong size is a type error at the `to:` property.
539
+ */
540
+ export type RelationTarget<TFrom extends RelationRefInput> = TFrom extends readonly ColumnRef[] ? {
541
+ readonly [TIndex in keyof TFrom]: PairedColumnRef<TFrom[TIndex]>;
542
+ } : TFrom extends ColumnRef ? PairedColumnRef<TFrom> | ThroughRef : ColumnRef | ThroughRef;
310
543
  /** Config for `r.one.Model({ from, to })`. */
311
- export type OneRelationDefConfig<TFrom extends ColumnRef | ThroughRef = ColumnRef, TTo extends ColumnRef | ThroughRef = ColumnRef> = {
544
+ export type OneRelationDefConfig<TFrom extends RelationRefInput = ColumnRef> = {
312
545
  /** Explicitly identifies the foreign-key side of a one-to-one pair. */
313
546
  ownsForeignKey?: boolean;
314
547
  from: TFrom;
315
- to: TTo;
548
+ to: RelationTarget<TFrom>;
316
549
  optional?: boolean;
317
550
  /** `@relation("Name")` — disambiguates multiple relations between a pair. */
318
551
  alias?: string;
552
+ /**
553
+ * Physical FOREIGN KEY constraint name (`@relation(map: "…")` in .prisma).
554
+ * Independent of `alias` and of the relation key: those name the client
555
+ * field, this names the database object. Set it to adopt an existing
556
+ * constraint under the name the database already gave it.
557
+ */
558
+ constraintName?: string;
319
559
  onDelete?: CascadeAction;
320
560
  onUpdate?: CascadeAction;
321
561
  };
322
562
  /** Config for `r.many.Model({ from, to })`. */
323
- export type ManyRelationDefConfig<TFrom extends ColumnRef | ThroughRef = ColumnRef | ThroughRef, TTo extends ColumnRef | ThroughRef = ColumnRef | ThroughRef> = {
563
+ export type ManyRelationDefConfig<TFrom extends RelationRefInput = ColumnRef | ThroughRef> = {
324
564
  from: TFrom;
325
- to: TTo;
565
+ to: RelationTarget<TFrom>;
326
566
  optional?: boolean;
327
567
  alias?: string;
568
+ /** Physical FOREIGN KEY constraint name; see `OneRelationDefConfig.constraintName`. */
569
+ constraintName?: string;
328
570
  onDelete?: CascadeAction;
329
571
  onUpdate?: CascadeAction;
330
572
  };
@@ -334,14 +576,22 @@ export type StoredRelationDef<TKind extends "one" | "many" = "one" | "many", TTa
334
576
  readonly __relationType: TKind;
335
577
  readonly __targetModel: TTarget;
336
578
  readonly __fromModel: string;
579
+ /** First `from` member — `__fromColumns[0]`. The tuple is authoritative. */
337
580
  readonly __fromColumn: string;
581
+ /** Every `from` member, in declared order. Length 1 for a single-column relation. */
582
+ readonly __fromColumns: readonly string[];
338
583
  readonly __toModel: string;
584
+ /** First `to` member — `__toColumns[0]`. The tuple is authoritative. */
339
585
  readonly __toColumn: string;
586
+ /** Every `to` member, in declared order; member `i` pairs with `__fromColumns[i]`. */
587
+ readonly __toColumns: readonly string[];
340
588
  readonly __throughJunction?: string;
341
589
  readonly __throughFromCol?: string;
342
590
  readonly __throughToCol?: string;
343
591
  readonly __optional: boolean;
344
592
  readonly __alias?: string;
593
+ /** Physical FOREIGN KEY constraint name, independent of `__alias`. */
594
+ readonly __constraintName?: string;
345
595
  readonly __ownsForeignKey?: boolean;
346
596
  readonly __onDelete?: CascadeAction;
347
597
  readonly __onUpdate?: CascadeAction;
@@ -350,17 +600,19 @@ export type StoredRelationDef<TKind extends "one" | "many" = "one" | "many", TTa
350
600
  export type RelationsMap = Record<string, Record<string, StoredRelationDef>>;
351
601
  /** Column names of a `ModelDef`, extracted from its schema type. */
352
602
  export type ExtractModelColumns<T> = T extends ModelDef<infer TSchema> ? Extract<keyof TSchema, string> : never;
603
+ /** The pairing tag of one column of a model schema. */
604
+ type SchemaColumnRefKind<TSchema, TColumn extends PropertyKey> = TColumn extends keyof TSchema ? (TSchema[TColumn] extends ColumnDef<infer TColumnType> ? ColumnRefKindOf<TColumnType> : ColumnRefKind) : ColumnRefKind;
353
605
  /** `r.User.id`, `r.User.email`, … */
354
- export type ModelColumnRefs<TModelName extends string, TModel extends ModelDef> = {
355
- [C in ExtractModelColumns<TModel>]: ColumnRef<TModelName, C>;
356
- };
606
+ export type ModelColumnRefs<TModelName extends string, TModel extends ModelDef> = TModel extends ModelDef<infer TSchema> ? {
607
+ [C in Extract<keyof TSchema, string>]: ColumnRef<TModelName, C, SchemaColumnRefKind<TSchema, C>>;
608
+ } : Record<string, ColumnRef<TModelName>>;
357
609
  /** The `r` handed to a `defineRelations` callback. */
358
610
  export type RelationsBuilder<TSchema extends Record<string, ModelDef>> = {
359
611
  one: {
360
- [K in keyof TSchema & string]: <TFrom extends ColumnRef | ThroughRef, TTo extends ColumnRef | ThroughRef>(config: OneRelationDefConfig<TFrom, TTo>) => StoredRelationDef<"one", K>;
612
+ [K in keyof TSchema & string]: <const TFrom extends RelationRefInput>(config: OneRelationDefConfig<TFrom>) => StoredRelationDef<"one", K>;
361
613
  };
362
614
  many: {
363
- [K in keyof TSchema & string]: <TFrom extends ColumnRef | ThroughRef, TTo extends ColumnRef | ThroughRef>(config: ManyRelationDefConfig<TFrom, TTo>) => StoredRelationDef<"many", K>;
615
+ [K in keyof TSchema & string]: <const TFrom extends RelationRefInput>(config: ManyRelationDefConfig<TFrom>) => StoredRelationDef<"many", K>;
364
616
  };
365
617
  } & {
366
618
  [K in keyof TSchema & string]: ModelColumnRefs<K, TSchema[K]>;
@@ -432,6 +684,13 @@ export type SchemaDef = {
432
684
  };
433
685
  export type PrismaGeneratorConfig = {
434
686
  output?: string;
687
+ /**
688
+ * Datasource provider the printed schema targets. Only typed CHECK and
689
+ * partial-index expressions read it — they become SQL text while printing,
690
+ * and identifier quoting and literal spelling are dialect decisions. Defaults
691
+ * to `"postgresql"`, matching `toIR`.
692
+ */
693
+ provider?: Provider;
435
694
  };
436
695
  export type GeneratorOutput = {
437
696
  prisma?: string;