@vibeorm/dsl 3.0.0 → 4.0.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/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  // @bun
2
2
  // packages/dsl/src/column.ts
3
+ import { VibeError, assertJsonSchemaRef, encodePrismaString } from "@vibeorm/schema";
3
4
  function cloneColumnDef(params) {
4
5
  const { column } = params;
5
6
  return {
6
7
  __type: "column",
7
8
  __columnType: column.__columnType,
8
9
  __name: column.__name,
9
- __config: { ...column.__config },
10
+ __config: { ...column.__config, ...column.__config.jsonSchema === undefined ? {} : { jsonSchema: assertJsonSchemaRef({ value: column.__config.jsonSchema }) } },
10
11
  __enumValues: column.__enumValues,
11
12
  __enumName: column.__enumName,
12
13
  __enumStorage: column.__enumStorage
@@ -165,10 +166,17 @@ function datetime(config = {}) {
165
166
 
166
167
  class ChainableJsonColumn extends ChainableColumn {
167
168
  constructor(config = {}) {
168
- super("json", config);
169
+ super("json", { ...config, ...config.jsonSchema === undefined ? {} : { jsonSchema: assertJsonSchemaRef({ value: config.jsonSchema }) } });
170
+ }
171
+ schema(reference) {
172
+ if (this.__config.jsonSchema !== undefined)
173
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "A field may declare only one JSON schema reference", meta: { issues: [{ rule: "duplicate-json-schema-ref" }] } });
174
+ this.__config.jsonSchema = assertJsonSchemaRef({ value: reference });
175
+ return this;
169
176
  }
170
177
  typed(config) {
171
- const annotation = `@type(${config.name} from "${config.from}")`;
178
+ const ref = assertJsonSchemaRef({ value: config });
179
+ const annotation = `@type(${ref.name} from ${encodePrismaString({ value: ref.from })})`;
172
180
  this.__config.comment = this.__config.comment === undefined ? annotation : `${this.__config.comment} ${annotation}`;
173
181
  return this;
174
182
  }
@@ -227,11 +235,11 @@ function createEnum(config) {
227
235
  }
228
236
 
229
237
  // packages/dsl/src/generator/prisma.ts
230
- import { VibeError as VibeError5, encodePrismaString, internalError, providerToDialect as providerToDialect2 } from "@vibeorm/schema";
238
+ import { VibeError as VibeError6, encodePrismaString as encodePrismaString2, internalError, normalizeJsonSchemaRef as normalizeJsonSchemaRef2, providerToDialect as providerToDialect2 } from "@vibeorm/schema";
231
239
  import { getSqlDialect as getSqlDialect2 } from "@vibeorm/sql";
232
240
 
233
241
  // packages/dsl/src/predicate.ts
234
- import { VibeError } from "@vibeorm/schema";
242
+ import { VibeError as VibeError2 } from "@vibeorm/schema";
235
243
  import { compileExclusion, renderPredicate } from "@vibeorm/sql";
236
244
  function isFieldExpr(operand) {
237
245
  return typeof operand === "object" && operand !== null && "kind" in operand && operand.kind === "field";
@@ -246,7 +254,7 @@ function toValue(params) {
246
254
  if (typeof input === "string" || typeof input === "number" || typeof input === "boolean") {
247
255
  return { kind: "value", value: input };
248
256
  }
249
- throw new VibeError({
257
+ throw new VibeError2({
250
258
  code: "VIBE_SCHEMA_INVALID",
251
259
  message: `${at}: ${input === null ? "null" : typeof input} is not a comparable literal \u2014 use isNull()/isNotNull() for null, and a string, number, bigint or boolean otherwise`,
252
260
  meta: { rule: "predicate-literal-invalid", at }
@@ -254,13 +262,27 @@ function toValue(params) {
254
262
  }
255
263
  function toOperand(params) {
256
264
  const { operand, at } = params;
257
- return isFieldExpr(operand) ? toRef({ expr: operand }) : toValue({ input: operand, at });
265
+ return typeof operand === "object" && operand !== null ? toExpression({ value: operand, at }) : toValue({ input: operand, at });
266
+ }
267
+ function toExpression(params) {
268
+ const { value, at } = params;
269
+ if (isFieldExpr(value))
270
+ return toRef({ expr: value });
271
+ if (typeof value === "object" && value !== null && "kind" in value) {
272
+ if (value.kind === "jsonText" && "field" in value && "path" in value && Array.isArray(value.path) && Array.from(value.path).every((segment) => typeof segment === "string" && !segment.includes("\x00"))) {
273
+ return { kind: "jsonText", field: toRef({ expr: requireFieldExpr({ value: value.field, at }) }), path: [...value.path] };
274
+ }
275
+ if (value.kind === "length" && "expression" in value) {
276
+ return { kind: "length", expression: toExpression({ value: value.expression, at }) };
277
+ }
278
+ }
279
+ throw new VibeError2({ code: "VIBE_SCHEMA_INVALID", message: `${at}: expected a field, jsonText or length expression with a string-only JSON path`, meta: { rule: "predicate-operand-invalid", at } });
258
280
  }
259
281
  function requireFieldExpr(params) {
260
282
  const { value, at } = params;
261
283
  if (isFieldExpr(value))
262
284
  return value;
263
- throw new VibeError({
285
+ throw new VibeError2({
264
286
  code: "VIBE_SCHEMA_INVALID",
265
287
  message: `${at}: the left side of a comparison must be c.field("\u2026"), not a bare value`,
266
288
  meta: { rule: "predicate-operand-invalid", at }
@@ -269,7 +291,7 @@ function requireFieldExpr(params) {
269
291
  function requireOperands(params) {
270
292
  const { operands, at } = params;
271
293
  if (operands.length === 0) {
272
- throw new VibeError({
294
+ throw new VibeError2({
273
295
  code: "VIBE_SCHEMA_INVALID",
274
296
  message: `${at}: an empty boolean group has no honest SQL spelling \u2014 pass at least one operand`,
275
297
  meta: { rule: "predicate-empty", at }
@@ -281,14 +303,14 @@ function createPredicateBuilder() {
281
303
  const compare = (op) => (left, right) => ({
282
304
  kind: "compare",
283
305
  op,
284
- left: toRef({ expr: requireFieldExpr({ value: left, at: `c.${op}()` }) }),
306
+ left: toExpression({ value: left, at: `c.${op}()` }),
285
307
  right: toOperand({ operand: right, at: `c.${op}()` })
286
308
  });
287
309
  const membership = (negate) => (field, values) => {
288
310
  const at = negate ? "c.notInList()" : "c.inList()";
289
311
  const ref = toRef({ expr: requireFieldExpr({ value: field, at }) });
290
312
  if (values.length === 0) {
291
- throw new VibeError({
313
+ throw new VibeError2({
292
314
  code: "VIBE_SCHEMA_INVALID",
293
315
  message: `${at}: an empty IN list is UNKNOWN on every row and is never what an author means`,
294
316
  meta: { rule: "predicate-empty", at, field: ref.name }
@@ -309,6 +331,9 @@ function createPredicateBuilder() {
309
331
  gte: compare("gte"),
310
332
  lt: compare("lt"),
311
333
  lte: compare("lte"),
334
+ jsonText: ({ field, path }) => ({ ...toExpression({ value: { kind: "jsonText", field: { kind: "field", name: field }, path }, at: "c.jsonText()" }), __kind: "String" }),
335
+ length: (expression) => ({ kind: "length", expression: toExpression({ value: expression, at: "c.length()" }), __kind: "Int" }),
336
+ isTrue: (operand) => ({ kind: "isTrue", operand }),
312
337
  inList: membership(false),
313
338
  notInList: membership(true),
314
339
  isNull: (name) => ({ kind: "isNull", field: { kind: "field", name } }),
@@ -318,7 +343,7 @@ function createPredicateBuilder() {
318
343
  not: (operand) => ({ kind: "not", operand }),
319
344
  named: (name, predicate) => {
320
345
  if (name.trim() === "") {
321
- throw new VibeError({
346
+ throw new VibeError2({
322
347
  code: "VIBE_SCHEMA_INVALID",
323
348
  message: "c.named(): a CHECK needs a non-empty name \u2014 the name is what keys the migration diff",
324
349
  meta: { rule: "predicate-unnamed-check" }
@@ -337,10 +362,13 @@ function evaluateChecks(params) {
337
362
  }
338
363
  const declared = checks(createPredicateBuilder());
339
364
  return declared.map((entry, index) => {
340
- if (entry === null || typeof entry !== "object" || entry.__type !== "check") {
341
- throw new VibeError({
365
+ if (entry !== null && typeof entry === "object" && !("__type" in entry) && typeof entry.name === "string" && "expression" in entry && typeof entry.expression === "string") {
366
+ return { kind: "raw", name: entry.name, expression: entry.expression };
367
+ }
368
+ if (entry === null || typeof entry !== "object" || !("__type" in entry) || entry.__type !== "check") {
369
+ throw new VibeError2({
342
370
  code: "VIBE_SCHEMA_INVALID",
343
- message: `Model "${model}": checks[${index}] is not a named CHECK \u2014 wrap the expression in c.named("name", \u2026)`,
371
+ message: `Model "${model}": checks[${index}] must be c.named("name", predicate) or a raw { name, expression } CHECK`,
344
372
  meta: { rule: "predicate-unnamed-check", model, index }
345
373
  });
346
374
  }
@@ -369,7 +397,7 @@ function compileChecks(params) {
369
397
  function compileIndexPredicate(params) {
370
398
  const { where, wherePredicate, columns, model, dialect, enums } = params;
371
399
  if (where !== undefined && wherePredicate !== undefined) {
372
- throw new VibeError({
400
+ throw new VibeError2({
373
401
  code: "VIBE_SCHEMA_INVALID",
374
402
  message: `Index on "${model.name}" [${columns.join(", ")}] carries both a raw where: string and a typed .where() \u2014 an index has ONE predicate`,
375
403
  meta: { rule: "predicate-duplicate-where", model: model.name, columns: [...columns] }
@@ -395,13 +423,14 @@ function compileIndexPredicate(params) {
395
423
  // packages/dsl/src/to-ir.ts
396
424
  import {
397
425
  EMPTY_EXTENSION_REGISTRY,
398
- VibeError as VibeError4,
426
+ VibeError as VibeError5,
399
427
  assertValidSchemaIR,
400
428
  exactIntegerLiteral,
401
429
  providerToDialect,
402
430
  scalarListDefault,
403
431
  serializeJsonDefault,
404
432
  mergeExtensionDeclarations,
433
+ normalizeJsonSchemaRef,
405
434
  unsupportedCapability
406
435
  } from "@vibeorm/schema";
407
436
  import { getSqlDialect } from "@vibeorm/sql";
@@ -442,10 +471,10 @@ function fieldTypeKey(params) {
442
471
  }
443
472
 
444
473
  // packages/dsl/src/relation-plan.ts
445
- import { VibeError as VibeError3, isSqlObjectIdentity, predicateKey } from "@vibeorm/schema";
474
+ import { VibeError as VibeError4, isSqlObjectIdentity, predicateKey } from "@vibeorm/schema";
446
475
 
447
476
  // packages/dsl/src/model.ts
448
- import { VibeError as VibeError2 } from "@vibeorm/schema";
477
+ import { VibeError as VibeError3 } from "@vibeorm/schema";
449
478
 
450
479
  // packages/dsl/src/naming.ts
451
480
  function capitalize(params) {
@@ -487,7 +516,7 @@ function createIndexBuilder() {
487
516
  const partial = (index) => {
488
517
  const where = (predicate) => {
489
518
  if (index.__where !== undefined) {
490
- throw new VibeError2({
519
+ throw new VibeError3({
491
520
  code: "VIBE_SCHEMA_INVALID",
492
521
  message: `Index on [${index.__columns.join(", ")}] already carries a raw where: predicate \u2014 an index has ONE predicate, so use either the raw string or .where(), not both`,
493
522
  meta: { rule: "predicate-duplicate-where", columns: [...index.__columns] }
@@ -525,7 +554,7 @@ function createIndexBuilder() {
525
554
  const [first, second] = args;
526
555
  if (args.length === 2 && Array.isArray(first) && typeof second === "object" && !Array.isArray(second)) {
527
556
  if ("ops" in second && second.ops !== undefined) {
528
- throw new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Custom unique operator classes can change client selector equality", meta: { feature: "uniqueIndexOperatorClasses" } });
557
+ throw new VibeError3({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Custom unique operator classes can change client selector equality", meta: { feature: "uniqueIndexOperatorClasses" } });
529
558
  }
530
559
  return partial({
531
560
  __type: "unique",
@@ -620,7 +649,7 @@ function detectOneToOnePairs(params) {
620
649
  for (const [modelName, modelRelations] of Object.entries(relations)) {
621
650
  for (const [relName, rel] of Object.entries(modelRelations)) {
622
651
  if (rel.__ownsForeignKey !== undefined && (typeof rel.__ownsForeignKey !== "boolean" || rel.__relationType !== "one" || rel.__ownsForeignKey && rel.__fromModel !== modelName)) {
623
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: `Invalid foreign-key ownership declaration on ${modelName}.${relName}` });
652
+ throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: `Invalid foreign-key ownership declaration on ${modelName}.${relName}` });
624
653
  }
625
654
  if (rel.__relationType === "one") {
626
655
  allOnes.push({ modelName, relName, rel });
@@ -695,7 +724,7 @@ function sameColumnTuple(params) {
695
724
  function resolveFkOwner(params) {
696
725
  const { a, b, models } = params;
697
726
  const ownershipError = () => {
698
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: `Ambiguous foreign-key ownership between ${a.modelName}.${a.relName} and ${b.modelName}.${b.relName}; set ownsForeignKey: true on the foreign-key side`, meta: { rule: "ambiguous-foreign-key-owner" } });
727
+ throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: `Ambiguous foreign-key ownership between ${a.modelName}.${a.relName} and ${b.modelName}.${b.relName}; set ownsForeignKey: true on the foreign-key side`, meta: { rule: "ambiguous-foreign-key-owner" } });
699
728
  };
700
729
  const aOwns = a.rel.__ownsForeignKey;
701
730
  const bOwns = b.rel.__ownsForeignKey;
@@ -866,14 +895,14 @@ function planIndexes(params) {
866
895
  for (const index of evaluateIndexes({ model })) {
867
896
  if (index.__ops !== undefined) {
868
897
  if (index.__ops === null || typeof index.__ops !== "object" || Array.isArray(index.__ops))
869
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Index ops must be a field-keyed qualified-name map", meta: { rule: "index-operator-class-map", model: model.__name } });
898
+ throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "Index ops must be a field-keyed qualified-name map", meta: { rule: "index-operator-class-map", model: model.__name } });
870
899
  if (index.__type === "unique")
871
- throw new VibeError3({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Custom unique operator classes can change client selector equality", meta: { feature: "uniqueIndexOperatorClasses", model: model.__name } });
900
+ throw new VibeError4({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Custom unique operator classes can change client selector equality", meta: { feature: "uniqueIndexOperatorClasses", model: model.__name } });
872
901
  for (const [field, operatorClass] of Object.entries(index.__ops)) {
873
902
  if (!index.__columns.includes(field))
874
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Operator class names a field outside the index", meta: { rule: "index-operator-class-field", model: model.__name, field } });
903
+ throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "Operator class names a field outside the index", meta: { rule: "index-operator-class-field", model: model.__name, field } });
875
904
  if (operatorClass !== undefined && !isSqlObjectIdentity({ kind: "table", name: operatorClass }))
876
- throw new VibeError3({ code: "VIBE_SCHEMA_INVALID", message: "Index operator class must be a qualified SQL name { namespace, name }", meta: { rule: "index-operator-class-identity", model: model.__name, field } });
905
+ throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "Index operator class must be a qualified SQL name { namespace, name }", meta: { rule: "index-operator-class-identity", model: model.__name, field } });
877
906
  }
878
907
  }
879
908
  const operatorClasses = index.__columns.map((field) => index.__ops?.[field]);
@@ -924,7 +953,7 @@ function assertForeignKeyIntegrity(params) {
924
953
  const references = relation.references ?? [];
925
954
  const at = `${relation.model}.${relation.name}`;
926
955
  const refuse = (message, rule, meta = {}) => {
927
- throw new VibeError3({
956
+ throw new VibeError4({
928
957
  code: "VIBE_SCHEMA_INVALID",
929
958
  message: `Relation ${at}: ${message}`,
930
959
  meta: { rule, model: relation.model, relation: relation.name, ...meta }
@@ -1086,16 +1115,16 @@ function fieldDefault(params) {
1086
1115
  const { column } = params;
1087
1116
  if (column.__columnType === "id") {
1088
1117
  if (column.__config.default !== undefined || column.__config.dbgenerated !== undefined || column.__config.autoincrement !== undefined)
1089
- throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "Use the id generator strategy instead of a conflicting id default" });
1118
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: "Use the id generator strategy instead of a conflicting id default" });
1090
1119
  return idDefault({ column });
1091
1120
  }
1092
1121
  const { dbgenerated, default: value, autoincrement } = column.__config;
1093
1122
  if ([dbgenerated !== undefined, value !== undefined, autoincrement === true].filter(Boolean).length > 1) {
1094
- throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: `Column "${column.__name ?? column.__columnType}" has conflicting defaults` });
1123
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: `Column "${column.__name ?? column.__columnType}" has conflicting defaults` });
1095
1124
  }
1096
1125
  if (autoincrement === true) {
1097
1126
  if (column.__columnType !== "int" && column.__columnType !== "bigint" || column.__config.array === true || column.__config.optional === true) {
1098
- throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "autoincrement requires a required scalar Int or BigInt column" });
1127
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: "autoincrement requires a required scalar Int or BigInt column" });
1099
1128
  }
1100
1129
  return { kind: "autoincrement" };
1101
1130
  }
@@ -1105,7 +1134,7 @@ function fieldDefault(params) {
1105
1134
  return;
1106
1135
  if (column.__config.array === true) {
1107
1136
  if (!Array.isArray(value))
1108
- throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "A scalar-list default must be an array" });
1137
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: "A scalar-list default must be an array" });
1109
1138
  return { kind: "list", values: scalarListDefault({ type: fieldType({ column }), values: column.__columnType === "json" ? value.map((entry) => typeof entry === "string" ? entry : serializeJsonDefault({ value: entry })) : value }) };
1110
1139
  }
1111
1140
  if (column.__columnType === "json" && typeof value !== "string") {
@@ -1120,20 +1149,20 @@ function fieldDefault(params) {
1120
1149
  return { kind: "enumValue", value };
1121
1150
  }
1122
1151
  if (typeof value === "number" && (!Number.isFinite(value) || (column.__columnType === "int" || column.__columnType === "bigint") && !Number.isSafeInteger(value))) {
1123
- throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: "Numeric defaults must be finite; integer defaults must be exact" });
1152
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: "Numeric defaults must be finite; integer defaults must be exact" });
1124
1153
  }
1125
1154
  if (typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
1126
1155
  return { kind: "literal", value };
1127
1156
  }
1128
1157
  if (typeof value === "bigint")
1129
1158
  return { kind: "literal", value: exactIntegerLiteral({ text: value.toString() }) };
1130
- throw new VibeError4({ code: "VIBE_SCHEMA_INVALID", message: `Unsupported default for ${column.__columnType}` });
1159
+ throw new VibeError5({ code: "VIBE_SCHEMA_INVALID", message: `Unsupported default for ${column.__columnType}` });
1131
1160
  }
1132
1161
  function datetimeNativeType(params) {
1133
1162
  const { tz, precision } = params.config;
1134
1163
  if (precision !== undefined && (tz !== true || !Number.isInteger(precision) || precision < 0 || precision > 6)) {
1135
1164
  const at = params.column === undefined ? "A datetime column" : `Column "${params.column}"`;
1136
- throw new VibeError4({
1165
+ throw new VibeError5({
1137
1166
  code: "VIBE_SCHEMA_INVALID",
1138
1167
  message: `${at}: a datetime precision must be an integer from 0 to 6 on a tz() column, as in dsl.datetime().tz({ precision: 3 })`,
1139
1168
  meta: { issues: [{ rule: "datetime-precision", column: params.column, precision, tz: tz === true }] }
@@ -1150,7 +1179,7 @@ function nativeType(params) {
1150
1179
  if (maxLength !== undefined || text !== undefined) {
1151
1180
  const invalid = column.__enumStorage !== "string" || maxLength !== undefined && (typeof maxLength !== "number" || !Number.isSafeInteger(maxLength) || maxLength <= 0) || text !== undefined && typeof text !== "boolean" || text === true && maxLength !== undefined;
1152
1181
  if (invalid) {
1153
- throw new VibeError4({
1182
+ throw new VibeError5({
1154
1183
  code: "VIBE_SCHEMA_INVALID",
1155
1184
  message: "Enum text/maxLength options require string storage and either a positive integer maxLength or text: true, not both",
1156
1185
  meta: { issues: [{ rule: "enum-string-storage-options", enum: column.__enumName }] }
@@ -1187,6 +1216,7 @@ function buildField(params) {
1187
1216
  const dbName = typeof config.map === "string" ? config.map : undefined;
1188
1217
  const documentation = typeof config.comment === "string" ? config.comment : undefined;
1189
1218
  const collation = typeof config.collation === "string" ? config.collation : undefined;
1219
+ const metadata = normalizeJsonSchemaRef({ documentation, reference: config.jsonSchema });
1190
1220
  const defaultValue = fieldDefault({ column });
1191
1221
  const native = extensionNativeType ?? nativeType({ column });
1192
1222
  return {
@@ -1202,7 +1232,7 @@ function buildField(params) {
1202
1232
  ...defaultValue === undefined ? {} : { default: defaultValue },
1203
1233
  ...native === undefined ? {} : { nativeType: native },
1204
1234
  ...column.__columnType === "unsupported" ? { isUnsupported: true } : {},
1205
- ...documentation === undefined ? {} : { documentation }
1235
+ ...metadata
1206
1236
  };
1207
1237
  }
1208
1238
  function buildRelation(params) {
@@ -1390,7 +1420,7 @@ function toIR(params) {
1390
1420
  }
1391
1421
  }
1392
1422
  if (issues.length > 0) {
1393
- throw new VibeError4({
1423
+ throw new VibeError5({
1394
1424
  code: "VIBE_SCHEMA_INVALID",
1395
1425
  message: `Schema validation failed with ${issues.length} issue(s): ${issues.map((issue) => issue.message).join("; ")}`,
1396
1426
  meta: { issues }
@@ -1447,7 +1477,7 @@ function prismaType(params) {
1447
1477
  const args = Array.isArray(rawArgs) ? rawArgs.map(String).join(", ") : "";
1448
1478
  return `${extensionTypeName}${args.length > 0 ? `(${args})` : ""}${list}`;
1449
1479
  }
1450
- return `Unsupported(${encodePrismaString({ value: String(column.__config.dbType) })})${list}`;
1480
+ return `Unsupported(${encodePrismaString2({ value: String(column.__config.dbType) })})${list}`;
1451
1481
  }
1452
1482
  }
1453
1483
  }
@@ -1462,11 +1492,11 @@ function defaultAttribute(params) {
1462
1492
  }
1463
1493
  const numericColumn = column.__columnType === "int" || column.__columnType === "bigint" || column.__columnType === "float" || column.__columnType === "decimal";
1464
1494
  if (value.kind === "literal")
1465
- return `@default(${typeof value.value === "string" && !(numericColumn && /^-?\d+(\.\d+)?$/.test(value.value)) ? encodePrismaString({ value: value.value }) : String(value.value)})`;
1495
+ return `@default(${typeof value.value === "string" && !(numericColumn && /^-?\d+(\.\d+)?$/.test(value.value)) ? encodePrismaString2({ value: value.value }) : String(value.value)})`;
1466
1496
  if (value.kind === "enumValue")
1467
1497
  return `@default(${value.value})`;
1468
1498
  if (value.kind === "dbgenerated")
1469
- return `@default(dbgenerated(${encodePrismaString({ value: value.sql })}))`;
1499
+ return `@default(dbgenerated(${encodePrismaString2({ value: value.sql })}))`;
1470
1500
  return `@default(${value.kind}())`;
1471
1501
  }
1472
1502
  function nativeTypeAttribute(params) {
@@ -1511,39 +1541,41 @@ function formatColumn(params) {
1511
1541
  parts.push("@unique");
1512
1542
  }
1513
1543
  if (typeof column.__config.map === "string") {
1514
- parts.push(`@map(${encodePrismaString({ value: column.__config.map })})`);
1544
+ parts.push(`@map(${encodePrismaString2({ value: column.__config.map })})`);
1515
1545
  }
1516
1546
  const nativeType = nativeTypeAttribute({ column });
1517
1547
  if (nativeType !== undefined)
1518
1548
  parts.push(nativeType);
1519
1549
  if (typeof column.__config.collation === "string") {
1520
- parts.push(`@collation(${encodePrismaString({ value: column.__config.collation })})`);
1550
+ parts.push(`@collation(${encodePrismaString2({ value: column.__config.collation })})`);
1521
1551
  }
1522
1552
  const columnLine = parts.join(" ");
1523
- const comment = column.__config.comment;
1524
- if (typeof comment === "string") {
1525
- const commentLines = comment.split(`
1526
- `).map((line) => ` /// ${line}`);
1527
- return [...commentLines, columnLine].join(`
1553
+ const metadata = normalizeJsonSchemaRef2({ documentation: typeof column.__config.comment === "string" ? column.__config.comment : undefined, reference: column.__config.jsonSchema });
1554
+ if (metadata.jsonSchema !== undefined && column.__columnType !== "json")
1555
+ throw new VibeError6({ code: "VIBE_SCHEMA_INVALID", message: "JSON schemas are only valid on Json fields", meta: { issues: [{ rule: "json-schema-non-json", field: name }] } });
1556
+ const comments = [
1557
+ ...metadata.documentation === undefined ? [] : metadata.documentation.split(`
1558
+ `),
1559
+ ...metadata.jsonSchema === undefined ? [] : [`@schema(${metadata.jsonSchema.name} from ${encodePrismaString2({ value: metadata.jsonSchema.from })})`]
1560
+ ];
1561
+ return [...comments.map((line) => ` /// ${line}`), columnLine].join(`
1528
1562
  `);
1529
- }
1530
- return columnLine;
1531
1563
  }
1532
1564
  function formatRelation(params) {
1533
1565
  const { relation } = params;
1534
1566
  const { name, targetModel, relationName } = relation;
1535
1567
  if (relation.kind === "list") {
1536
- const attribute = relationName === undefined ? "" : ` @relation(${encodePrismaString({ value: relationName })})`;
1568
+ const attribute = relationName === undefined ? "" : ` @relation(${encodePrismaString2({ value: relationName })})`;
1537
1569
  return ` ${name} ${targetModel}[]${attribute}`;
1538
1570
  }
1539
1571
  const optional = relation.isOptional ? "?" : "";
1540
1572
  if (relation.kind === "backOne") {
1541
- const attribute = relationName === undefined ? "" : ` @relation(${encodePrismaString({ value: relationName })})`;
1573
+ const attribute = relationName === undefined ? "" : ` @relation(${encodePrismaString2({ value: relationName })})`;
1542
1574
  return ` ${name} ${targetModel}${optional}${attribute}`;
1543
1575
  }
1544
1576
  const args = [];
1545
1577
  if (relationName !== undefined)
1546
- args.push(encodePrismaString({ value: relationName }));
1578
+ args.push(encodePrismaString2({ value: relationName }));
1547
1579
  if (relation.fields === undefined || relation.references === undefined) {
1548
1580
  throw internalError({
1549
1581
  message: `planned FK relation "${relation.model}.${name}" has no fields/references`,
@@ -1557,14 +1589,14 @@ function formatRelation(params) {
1557
1589
  if (relation.onUpdate !== undefined)
1558
1590
  args.push(`onUpdate: ${relation.onUpdate}`);
1559
1591
  if (relation.constraintName !== undefined) {
1560
- args.push(`map: ${encodePrismaString({ value: relation.constraintName })}`);
1592
+ args.push(`map: ${encodePrismaString2({ value: relation.constraintName })}`);
1561
1593
  }
1562
1594
  return ` ${name} ${targetModel}${optional} @relation(${args.join(", ")})`;
1563
1595
  }
1564
1596
  function formatIndex(params) {
1565
1597
  const { index } = params;
1566
1598
  if (index.ops !== undefined && Object.values(index.ops).some((operatorClass) => operatorClass !== undefined))
1567
- throw new VibeError5({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Prisma output cannot preserve qualified index operator classes; keep the DSL source", meta: { feature: "prismaIndexOperatorClasses", target: "prisma" } });
1599
+ throw new VibeError6({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Prisma output cannot preserve qualified index operator classes; keep the DSL source", meta: { feature: "prismaIndexOperatorClasses", target: "prisma" } });
1568
1600
  const columns = index.columns.join(", ");
1569
1601
  const predicate = compileIndexPredicate({
1570
1602
  where: index.where,
@@ -1574,10 +1606,10 @@ function formatIndex(params) {
1574
1606
  dialect: params.dialect,
1575
1607
  enums: params.enums
1576
1608
  });
1577
- const whereArg = predicate === undefined ? "" : `, where: ${encodePrismaString({ value: predicate.where })}`;
1578
- const mapArg = index.map === undefined ? "" : `, map: ${encodePrismaString({ value: index.map })}`;
1609
+ const whereArg = predicate === undefined ? "" : `, where: ${encodePrismaString2({ value: predicate.where })}`;
1610
+ const mapArg = index.map === undefined ? "" : `, map: ${encodePrismaString2({ value: index.map })}`;
1579
1611
  const nndArg = index.nullsNotDistinct === true ? ", nullsNotDistinct: true" : "";
1580
- const nameArg = index.clientName === undefined ? "" : `, name: ${encodePrismaString({ value: index.clientName })}`;
1612
+ const nameArg = index.clientName === undefined ? "" : `, name: ${encodePrismaString2({ value: index.clientName })}`;
1581
1613
  if (index.isUnique) {
1582
1614
  return ` @@unique([${columns}]${nameArg}${mapArg}${nndArg}${whereArg})`;
1583
1615
  }
@@ -1587,7 +1619,7 @@ function formatIndex(params) {
1587
1619
  function formatExtensionValue(params) {
1588
1620
  const { value } = params;
1589
1621
  if (typeof value === "string")
1590
- return encodePrismaString({ value });
1622
+ return encodePrismaString2({ value });
1591
1623
  if (Array.isArray(value)) {
1592
1624
  return `[${value.map((entry) => formatExtensionValue({ value: entry })).join(", ")}]`;
1593
1625
  }
@@ -1621,7 +1653,7 @@ function formatModel(params) {
1621
1653
  dialect: params.dialect,
1622
1654
  enums: params.enums
1623
1655
  })) {
1624
- lines.push(` @@check(${encodePrismaString({ value: check.expression })}, name: ${encodePrismaString({ value: check.name })})`);
1656
+ lines.push(` @@check(${encodePrismaString2({ value: check.expression })}, name: ${encodePrismaString2({ value: check.name })})`);
1625
1657
  }
1626
1658
  for (const use of model.__extensions ?? []) {
1627
1659
  const args = [];
@@ -1637,11 +1669,11 @@ function formatModel(params) {
1637
1669
  }
1638
1670
  if (model.__policy !== undefined) {
1639
1671
  const { field, context } = model.__policy;
1640
- const contextArg = context === undefined || context === field ? "" : `, context: ${encodePrismaString({ value: context })}`;
1672
+ const contextArg = context === undefined || context === field ? "" : `, context: ${encodePrismaString2({ value: context })}`;
1641
1673
  lines.push(` @@policy([${field}]${contextArg})`);
1642
1674
  }
1643
1675
  if (typeof model.__map === "string") {
1644
- lines.push(` @@map(${encodePrismaString({ value: model.__map })})`);
1676
+ lines.push(` @@map(${encodePrismaString2({ value: model.__map })})`);
1645
1677
  }
1646
1678
  lines.push("}");
1647
1679
  return lines.join(`
@@ -1656,18 +1688,18 @@ function generatePrisma(schema, config = {}) {
1656
1688
  for (const model of schema.__models.values()) {
1657
1689
  const exclusions = compileExclusions({ exclusions: model.__exclusions, model: { name: model.__name, fields: Array.from(model.__columns, ([name, column]) => buildField({ name, column })) }, dialect: getSqlDialect2({ dialect: providerToDialect2({ provider: config.provider ?? "postgresql" }) }) });
1658
1690
  if (exclusions.length > 0)
1659
- throw new VibeError5({ code: "VIBE_CONFIG", message: "Prisma output cannot preserve exclusion constraints; keep the DSL source", meta: { feature: "exclusionConstraints", target: "prisma" } });
1691
+ throw new VibeError6({ code: "VIBE_CONFIG", message: "Prisma output cannot preserve exclusion constraints; keep the DSL source", meta: { feature: "exclusionConstraints", target: "prisma" } });
1660
1692
  }
1661
1693
  const stringEnums = [...schema.__enumStorage].filter(([, storage]) => storage === "string").map(([name]) => name);
1662
1694
  if (stringEnums.length > 0) {
1663
- throw new VibeError5({
1695
+ throw new VibeError6({
1664
1696
  code: "VIBE_CONFIG",
1665
1697
  message: `Cannot print to Prisma: enum(s) ${stringEnums.join(", ")} use storage: "string", which .prisma cannot express losslessly. Keep the DSL as the source or switch the enum to database storage.`,
1666
1698
  meta: { enums: stringEnums, target: "prisma" }
1667
1699
  });
1668
1700
  }
1669
1701
  if (schema.__rls !== undefined) {
1670
- throw new VibeError5({
1702
+ throw new VibeError6({
1671
1703
  code: "VIBE_CONFIG",
1672
1704
  message: "Cannot print to Prisma: this schema uses native RLS, which .prisma cannot express losslessly. Keep the DSL as the source.",
1673
1705
  meta: { feature: "nativeRowLevelSecurity", target: "prisma" }
@@ -1700,7 +1732,7 @@ function generatePrisma(schema, config = {}) {
1700
1732
  }
1701
1733
 
1702
1734
  // packages/dsl/src/relations.ts
1703
- import { VibeError as VibeError6 } from "@vibeorm/schema";
1735
+ import { VibeError as VibeError7 } from "@vibeorm/schema";
1704
1736
  var REF_KIND_BY_COLUMN_TYPE = {
1705
1737
  string: "String",
1706
1738
  int: "Int",
@@ -1741,7 +1773,7 @@ function extractRefInfo(params) {
1741
1773
  }
1742
1774
  const members = ref;
1743
1775
  if (members.length === 0) {
1744
- throw new VibeError6({
1776
+ throw new VibeError7({
1745
1777
  code: "VIBE_SCHEMA_INVALID",
1746
1778
  message: `Relation to ${target}: "${side}" is an empty tuple \u2014 name at least one column`,
1747
1779
  meta: { rule: "relation-empty-tuple", side, targetModel: target }
@@ -1749,14 +1781,14 @@ function extractRefInfo(params) {
1749
1781
  }
1750
1782
  const models = [...new Set(members.map((member) => member.__model))].sort();
1751
1783
  if (models.length > 1) {
1752
- throw new VibeError6({
1784
+ throw new VibeError7({
1753
1785
  code: "VIBE_SCHEMA_INVALID",
1754
1786
  message: `Relation to ${target}: "${side}" mixes models ${models.join(", ")} \u2014 every member of a composite key must sit on one model`,
1755
1787
  meta: { rule: "relation-mixed-models", side, targetModel: target, models }
1756
1788
  });
1757
1789
  }
1758
1790
  if (members.some((member) => member.__type !== "columnRef")) {
1759
- throw new VibeError6({
1791
+ throw new VibeError7({
1760
1792
  code: "VIBE_SCHEMA_INVALID",
1761
1793
  message: `Relation to ${target}: "${side}" routes through a junction inside a tuple \u2014 implicit many-to-many is single-key; model the join with an explicit junction model`,
1762
1794
  meta: { rule: "relation-through-in-tuple", side, targetModel: target }
@@ -1770,7 +1802,7 @@ function createRelationFactory(params) {
1770
1802
  const from = extractRefInfo({ ref: config.from, side: "from", target: targetModelName });
1771
1803
  const to = extractRefInfo({ ref: config.to, side: "to", target: targetModelName });
1772
1804
  if (from.columns.length !== to.columns.length) {
1773
- throw new VibeError6({
1805
+ throw new VibeError7({
1774
1806
  code: "VIBE_SCHEMA_INVALID",
1775
1807
  message: `Relation to ${targetModelName}: "from" names ${from.columns.length} column(s) but "to" names ${to.columns.length} \u2014 a composite key pairs member by member`,
1776
1808
  meta: { rule: "relation-tuple-arity", targetModel: targetModelName, from: from.columns, to: to.columns }
@@ -1868,7 +1900,7 @@ function mergeRelations(...parts) {
1868
1900
  }
1869
1901
 
1870
1902
  // packages/dsl/src/schema.ts
1871
- import { VibeError as VibeError7, validateRlsContextIR } from "@vibeorm/schema";
1903
+ import { VibeError as VibeError8, validateRlsContextIR } from "@vibeorm/schema";
1872
1904
  function sameValues(left, right) {
1873
1905
  return left.length === right.length && left.every((value, index) => value === right[index]);
1874
1906
  }
@@ -1887,7 +1919,7 @@ function registerEnum(params) {
1887
1919
  ...valuesDiffer ? [`values [${existing.values.join(", ")}] vs [${entry.values.join(", ")}]`] : [],
1888
1920
  ...storageDiffers ? [`storage "${existing.storage}" vs "${entry.storage}"`] : []
1889
1921
  ].join("; ");
1890
- throw new VibeError7({
1922
+ throw new VibeError8({
1891
1923
  code: "VIBE_SCHEMA_INVALID",
1892
1924
  message: `Enum "${name}" is defined more than once with conflicting definitions (${origin}): ${detail} \u2014 share one enum object across modules, or give the definitions different names`,
1893
1925
  meta: {
@@ -1913,7 +1945,7 @@ function addModelTree(params) {
1913
1945
  const { model, models, origin } = params;
1914
1946
  const existing = models.get(model.__name);
1915
1947
  if (existing !== undefined && existing !== model) {
1916
- throw new VibeError7({
1948
+ throw new VibeError8({
1917
1949
  code: "VIBE_SCHEMA_INVALID",
1918
1950
  message: `Model "${model.__name}" is defined more than once with different definitions (${origin}) \u2014 ` + `share one model object across modules, or give the definitions different names`,
1919
1951
  meta: {
@@ -1991,7 +2023,7 @@ var RLS_CONTEXT_TYPES = {
1991
2023
  };
1992
2024
  function defineRls(params) {
1993
2025
  if (typeof params.context !== "object" || params.context === null || Array.isArray(params.context)) {
1994
- throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "RLS context must be a record of scalar declarations", meta: { issues: [{ rule: "rls-invalid-context" }] } });
2026
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "RLS context must be a record of scalar declarations", meta: { issues: [{ rule: "rls-invalid-context" }] } });
1995
2027
  }
1996
2028
  const entries = [];
1997
2029
  const refs = [];
@@ -1999,7 +2031,7 @@ function defineRls(params) {
1999
2031
  const descriptor = Object.getOwnPropertyDescriptor(params.context, key);
2000
2032
  const column = descriptor?.value;
2001
2033
  if (typeof key !== "string" || descriptor?.enumerable !== true || typeof column !== "object" || column === null || !("__columnType" in column) || !("__config" in column) || typeof column.__config !== "object" || column.__config === null || typeof column.__columnType !== "string" || !Object.hasOwn(RLS_CONTEXT_TYPES, column.__columnType) || "array" in column.__config && column.__config.array === true) {
2002
- throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "RLS context slots must be named scalar column declarations", meta: { issues: [{ rule: "rls-invalid-context" }] } });
2034
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "RLS context slots must be named scalar column declarations", meta: { issues: [{ rule: "rls-invalid-context" }] } });
2003
2035
  }
2004
2036
  entries.push([key, { type: RLS_CONTEXT_TYPES[column.__columnType], nullable: "optional" in column.__config && column.__config.optional === true }]);
2005
2037
  refs.push([key, Object.freeze({ context: key })]);
@@ -2007,22 +2039,22 @@ function defineRls(params) {
2007
2039
  const context = Object.freeze(Object.fromEntries(entries));
2008
2040
  const issues = validateRlsContextIR({ context });
2009
2041
  if (issues.length > 0) {
2010
- throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "Invalid RLS context declaration", meta: { issues } });
2042
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "Invalid RLS context declaration", meta: { issues } });
2011
2043
  }
2012
2044
  const policies = params.policies({ context: Object.freeze(Object.fromEntries(refs)) });
2013
2045
  if (typeof policies !== "object" || policies === null || Array.isArray(policies)) {
2014
- throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "RLS policies must be a record of model classifications", meta: { issues: [{ rule: "rls-invalid-rule" }] } });
2046
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "RLS policies must be a record of model classifications", meta: { issues: [{ rule: "rls-invalid-rule" }] } });
2015
2047
  }
2016
2048
  const named = [];
2017
2049
  const classified = new Set;
2018
2050
  for (const key of Reflect.ownKeys(policies)) {
2019
2051
  const descriptor = Object.getOwnPropertyDescriptor(policies, key);
2020
2052
  if (typeof key !== "string" || descriptor?.enumerable !== true || !("value" in descriptor)) {
2021
- throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: "RLS policies must be named data properties", meta: { issues: [{ rule: "rls-invalid-rule" }] } });
2053
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: "RLS policies must be named data properties", meta: { issues: [{ rule: "rls-invalid-rule" }] } });
2022
2054
  }
2023
2055
  const name = params.models[key]?.__name ?? key;
2024
2056
  if (classified.has(name)) {
2025
- throw new VibeError7({ code: "VIBE_SCHEMA_INVALID", message: `RLS classifies model "${name}" more than once`, meta: { issues: [{ rule: "rls-duplicate-classification", model: name }] } });
2057
+ throw new VibeError8({ code: "VIBE_SCHEMA_INVALID", message: `RLS classifies model "${name}" more than once`, meta: { issues: [{ rule: "rls-duplicate-classification", model: name }] } });
2026
2058
  }
2027
2059
  classified.add(name);
2028
2060
  named.push([name, descriptor.value]);
@@ -2334,4 +2366,4 @@ export {
2334
2366
  withNameDescription
2335
2367
  };
2336
2368
 
2337
- //# debugId=AC91338749EBA17764756E2164756E21
2369
+ //# debugId=21B90DDA6F5E192964756E2164756E21