@vibeorm/schema 2.2.0 → 2.4.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
@@ -278,7 +278,7 @@ function isSqlObjectIdentity(value) {
278
278
  }
279
279
  }
280
280
  function isSqlObjectDefinition(value) {
281
- return record(value) && value.format === "catalog-v1" && isSqlObjectIdentity(value.identity) && record(value.facts) && Object.keys(value.facts).length > 0 && Object.values(value.facts).every((fact) => fact === null || typeof fact === "string" || typeof fact === "boolean" || typeof fact === "number" && Number.isFinite(fact) || Array.isArray(fact) && fact.every((entry) => typeof entry === "string")) && Array.isArray(value.dependencies) && value.dependencies.every(isSqlObjectIdentity);
281
+ return record(value) && (value.format === "catalog-v1" || value.format === "catalog-v2") && isSqlObjectIdentity(value.identity) && record(value.facts) && Object.keys(value.facts).length > 0 && Object.values(value.facts).every((fact) => fact === null || typeof fact === "string" || typeof fact === "boolean" || typeof fact === "number" && Number.isFinite(fact) || Array.isArray(fact) && fact.every((entry) => typeof entry === "string")) && Array.isArray(value.dependencies) && value.dependencies.every(isSqlObjectIdentity);
282
282
  }
283
283
  function assertMigrationObjects(params) {
284
284
  if (!Array.isArray(params.objects))
@@ -389,6 +389,21 @@ function exactIntegerLiteral(params) {
389
389
  return asNumber;
390
390
  return BigInt(text).toString();
391
391
  }
392
+ var DECIMAL_LITERAL = /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/;
393
+ function exactDecimalLiteral(params) {
394
+ const { text } = params;
395
+ if (!DECIMAL_LITERAL.test(text)) {
396
+ throw new VibeError({
397
+ code: "VIBE_SCHEMA_INVALID",
398
+ message: `Not a numeric literal: ${JSON.stringify(text)}`,
399
+ meta: { issues: [{ rule: "malformed-numeric-literal", message: text }], literal: text }
400
+ });
401
+ }
402
+ if (INTEGER_LITERAL.test(text))
403
+ return exactIntegerLiteral({ text });
404
+ const mantissa = text.replace(/[eE].*$/, "").replace("-", "").replace(".", "").replace(/^0+/, "");
405
+ return mantissa.length <= 15 ? Number(text) : text;
406
+ }
392
407
  // packages/schema/src/reserved.ts
393
408
  var RESERVED_DELEGATE_NAMES = [
394
409
  "findMany",
@@ -469,26 +484,26 @@ function extensionManifestEntry(params) {
469
484
  }
470
485
  var EXTENSION_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
471
486
  function defineExtension(extension) {
472
- const { name: name2 } = extension;
473
- if (!EXTENSION_NAME_PATTERN.test(name2)) {
487
+ const { name } = extension;
488
+ if (!EXTENSION_NAME_PATTERN.test(name)) {
474
489
  throw new VibeError({
475
490
  code: "VIBE_CONFIG",
476
- message: `extension name "${name2}" is invalid \u2014 must match ${EXTENSION_NAME_PATTERN} (it becomes a client identifier)`,
477
- meta: { name: name2 }
491
+ message: `extension name "${name}" is invalid \u2014 must match ${EXTENSION_NAME_PATTERN} (it becomes a client identifier)`,
492
+ meta: { name }
478
493
  });
479
494
  }
480
- if (RESERVED_DELEGATE_NAMES.includes(name2)) {
495
+ if (RESERVED_DELEGATE_NAMES.includes(name)) {
481
496
  throw new VibeError({
482
497
  code: "VIBE_CONFIG",
483
- message: `extension name "${name2}" collides with a core delegate method`,
484
- meta: { name: name2 }
498
+ message: `extension name "${name}" collides with a core delegate method`,
499
+ meta: { name }
485
500
  });
486
501
  }
487
- if (RESERVED_CLIENT_NAMES.includes(`$${name2}`)) {
502
+ if (RESERVED_CLIENT_NAMES.includes(`$${name}`)) {
488
503
  throw new VibeError({
489
504
  code: "VIBE_CONFIG",
490
- message: `extension name "${name2}" collides with the core client method "$${name2}"`,
491
- meta: { name: name2 }
505
+ message: `extension name "${name}" collides with the core client method "$${name}"`,
506
+ meta: { name }
492
507
  });
493
508
  }
494
509
  return Object.freeze(extension);
@@ -710,6 +725,102 @@ function scalarListDefault(params) {
710
725
  });
711
726
  }
712
727
 
728
+ // packages/schema/src/m2m.ts
729
+ function implicitJoinTable(params) {
730
+ const sorted = [params.modelA, params.modelB].sort();
731
+ const first = sorted[0];
732
+ const second = sorted[1];
733
+ const key = params.relationName ?? `${first}To${second}`;
734
+ return {
735
+ tableName: `_${key}`,
736
+ modelA: first,
737
+ modelB: second,
738
+ columnA: "A",
739
+ columnB: "B"
740
+ };
741
+ }
742
+
743
+ // packages/schema/src/physical-names.ts
744
+ var IDENTIFIER_LIMITS = Object.freeze({
745
+ postgres: { max: 63, unit: "bytes" },
746
+ mysql: { max: 64, unit: "characters" },
747
+ sqlite: undefined
748
+ });
749
+ var ENCODER = new TextEncoder;
750
+ function identifierLength(params) {
751
+ const limit = IDENTIFIER_LIMITS[params.dialect];
752
+ return limit?.unit === "bytes" ? ENCODER.encode(params.name).length : [...params.name].length;
753
+ }
754
+ function cutIdentifier(params) {
755
+ let out = "";
756
+ let used = 0;
757
+ for (const char of params.name) {
758
+ const size = params.unit === "bytes" ? ENCODER.encode(char).length : 1;
759
+ if (used + size > params.length)
760
+ break;
761
+ out += char;
762
+ used += size;
763
+ }
764
+ return out;
765
+ }
766
+ function fitDefaultName(params) {
767
+ const limit = IDENTIFIER_LIMITS[params.dialect];
768
+ if (limit === undefined)
769
+ return `${params.prefix}${params.suffix}`;
770
+ const room = limit.max - identifierLength({ name: params.suffix, dialect: params.dialect });
771
+ if (identifierLength({ name: params.prefix, dialect: params.dialect }) <= room)
772
+ return `${params.prefix}${params.suffix}`;
773
+ return `${cutIdentifier({ name: params.prefix, length: room, unit: limit.unit })}${params.suffix}`;
774
+ }
775
+ function assertIdentifierFits(params) {
776
+ const limit = IDENTIFIER_LIMITS[params.dialect];
777
+ if (limit === undefined)
778
+ return;
779
+ const length = identifierLength({ name: params.name, dialect: params.dialect });
780
+ if (length <= limit.max)
781
+ return;
782
+ throw new VibeError({
783
+ code: "VIBE_SCHEMA_INVALID",
784
+ message: `${params.what} "${params.name}" is ${length} ${limit.unit} \u2014 ${params.dialect} keeps at most ${limit.max}; the created object would never carry this name and migrations could not converge. ${params.remedy}`,
785
+ meta: { name: params.name, dialect: params.dialect, length, max: limit.max }
786
+ });
787
+ }
788
+ function defaultIndexName(params) {
789
+ return fitDefaultName({
790
+ prefix: `${params.table}_${params.columns.join("_")}`,
791
+ suffix: params.unique ? "_key" : "_idx",
792
+ dialect: params.dialect
793
+ });
794
+ }
795
+ function defaultForeignKeyName(params) {
796
+ return fitDefaultName({ prefix: `${params.table}_${params.columns.join("_")}`, suffix: "_fkey", dialect: params.dialect });
797
+ }
798
+ function defaultPrimaryKeyName(params) {
799
+ return fitDefaultName({ prefix: params.table, suffix: "_pkey", dialect: params.dialect });
800
+ }
801
+ function implicitJoinTableObjectNames(params) {
802
+ return {
803
+ uniqueIndex: `${params.table}_AB_unique`,
804
+ indexB: `${params.table}_B_index`,
805
+ foreignKeyA: `${params.table}_A_fkey`,
806
+ foreignKeyB: `${params.table}_B_fkey`
807
+ };
808
+ }
809
+ function postgresGeneratedObjectName(params) {
810
+ const limit = IDENTIFIER_LIMITS.postgres;
811
+ const overhead = params.label.length + 2;
812
+ let chars1 = [...params.name1];
813
+ let chars2 = [...params.name2];
814
+ const bytes = (chars) => ENCODER.encode(chars.join("")).length;
815
+ while (bytes(chars1) + bytes(chars2) + overhead > limit.max) {
816
+ if (bytes(chars1) > bytes(chars2))
817
+ chars1 = chars1.slice(0, -1);
818
+ else
819
+ chars2 = chars2.slice(0, -1);
820
+ }
821
+ return `${chars1.join("")}_${chars2.join("")}_${params.label}`;
822
+ }
823
+
713
824
  // packages/schema/src/capabilities.ts
714
825
  var POSTGRES_CAPABILITIES = Object.freeze({
715
826
  exclusionConstraints: true,
@@ -1038,21 +1149,21 @@ var INVALID_BINDING_NAMES = new Set([
1038
1149
  ]);
1039
1150
  function validateGeneratedNames(params) {
1040
1151
  const { schema, issues } = params;
1041
- const owners = new Map(RESERVED_ENUM_NAMES.map((name2) => [name2, "generated client"]));
1152
+ const owners = new Map(RESERVED_ENUM_NAMES.map((name) => [name, "generated client"]));
1042
1153
  if (schema.rls !== undefined) {
1043
1154
  owners.set("RlsContext", "native RLS client");
1044
1155
  owners.set("BoundVibeClientInstance", "native RLS client");
1045
1156
  owners.set("BoundVibeTransactionClientInstance", "native RLS client");
1046
1157
  }
1047
- const register = (params2) => {
1048
- if (!/^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(params2.name) || INVALID_BINDING_NAMES.has(params2.name)) {
1049
- issues.push({ rule: "invalid-generated-identifier", message: `Generated name "${params2.name}" from ${params2.owner} is not a legal TypeScript identifier` });
1158
+ const register = (params) => {
1159
+ if (!/^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(params.name) || INVALID_BINDING_NAMES.has(params.name)) {
1160
+ issues.push({ rule: "invalid-generated-identifier", message: `Generated name "${params.name}" from ${params.owner} is not a legal TypeScript identifier` });
1050
1161
  }
1051
- const previous = owners.get(params2.name);
1052
- if (previous !== undefined && previous !== params2.owner) {
1053
- issues.push({ rule: "generated-name-collision", message: `Generated name "${params2.name}" from ${params2.owner} conflicts with ${previous}` });
1162
+ const previous = owners.get(params.name);
1163
+ if (previous !== undefined && previous !== params.owner) {
1164
+ issues.push({ rule: "generated-name-collision", message: `Generated name "${params.name}" from ${params.owner} conflicts with ${previous}` });
1054
1165
  } else {
1055
- owners.set(params2.name, params2.owner);
1166
+ owners.set(params.name, params.owner);
1056
1167
  }
1057
1168
  };
1058
1169
  const aliases = new Map;
@@ -1200,8 +1311,8 @@ function validateSchemaIR(params) {
1200
1311
  }
1201
1312
  }
1202
1313
  const seenSelectorNames = new Set;
1203
- const checkSelectorName = (params2) => {
1204
- const { clientName, fieldCount, where, isUnique, site } = params2;
1314
+ const checkSelectorName = (params) => {
1315
+ const { clientName, fieldCount, where, isUnique, site } = params;
1205
1316
  if (!isUnique) {
1206
1317
  issues.push({
1207
1318
  rule: "client-name-non-unique",
@@ -1256,10 +1367,189 @@ function validateSchemaIR(params) {
1256
1367
  });
1257
1368
  }
1258
1369
  }
1370
+ validateIdentifierLengths({ schema, issues });
1371
+ validatePhysicalNameUniqueness({ schema, issues });
1259
1372
  validateGeneratedNames({ schema, issues });
1260
1373
  validateNativeRls({ schema, issues });
1261
1374
  return issues;
1262
1375
  }
1376
+ function validatePhysicalNameUniqueness(params) {
1377
+ const { schema, issues } = params;
1378
+ const dialect = providerToDialect({ provider: schema.datasource.provider });
1379
+ const owners = new Map;
1380
+ const claim = (entry) => {
1381
+ const key = `${entry.namespace}\x00${entry.name}`;
1382
+ const previous = owners.get(key);
1383
+ if (previous === undefined) {
1384
+ owners.set(key, `${entry.what} of ${entry.owner}`);
1385
+ return true;
1386
+ }
1387
+ issues.push({
1388
+ rule: "duplicate-physical-name",
1389
+ message: `${entry.what} "${entry.name}" of ${entry.owner} collides with the ${previous} \u2014 both resolve to one physical ${entry.namespace === "relation" ? "object" : "name"}, and only one can exist`,
1390
+ ...entry.model === undefined ? {} : { model: entry.model },
1391
+ ...entry.field === undefined ? {} : { field: entry.field }
1392
+ });
1393
+ return false;
1394
+ };
1395
+ const indexNamespace = (table) => dialect === "mysql" ? `index:${table}` : "relation";
1396
+ const constraintNamespace = (table) => dialect === "mysql" ? "constraint" : `constraint:${table}`;
1397
+ for (const model of schema.models) {
1398
+ const table = model.dbName ?? model.name;
1399
+ if (!claim({ namespace: "relation", name: table, what: model.isView ? "view" : "table", owner: `model "${model.name}"`, model: model.name }))
1400
+ continue;
1401
+ for (const fieldIR of model.fields) {
1402
+ claim({ namespace: `column:${table}`, name: fieldIR.dbName ?? fieldIR.name, what: "column", owner: `field "${model.name}.${fieldIR.name}"`, model: model.name, field: fieldIR.name });
1403
+ }
1404
+ if (model.isView)
1405
+ continue;
1406
+ const columnOf = (name) => model.fields.find((candidate) => candidate.name === name)?.dbName ?? name;
1407
+ const seenIndexKeys = new Set;
1408
+ const declarations = [
1409
+ ...model.indexes.map((index) => ({ index, site: index.kind === "unique" ? "@@unique" : "@@index" })),
1410
+ ...model.fields.filter((fieldIR) => fieldIR.isUnique).map((fieldIR) => ({ index: { kind: "unique", fields: [{ name: fieldIR.name }] }, site: `@unique on "${fieldIR.name}"` }))
1411
+ ];
1412
+ for (const { index, site } of declarations) {
1413
+ const unique = index.kind === "unique";
1414
+ const columns = index.fields.map((entry) => `${columnOf(entry.name)}${entry.sort === "desc" ? " desc" : ""}`);
1415
+ const key = `${unique ? "u" : "i"}${index.nullsNotDistinct === true ? "n" : ""}:${unique ? "btree" : index.kind}:${columns.join(",")}:${index.where?.trim() ?? ""}`;
1416
+ if (seenIndexKeys.has(key))
1417
+ continue;
1418
+ seenIndexKeys.add(key);
1419
+ const name = index.name ?? defaultIndexName({ table, columns: index.fields.map((entry) => columnOf(entry.name)), unique, dialect });
1420
+ claim({ namespace: indexNamespace(table), name, what: unique ? "unique index" : "index", owner: `${site} on "${model.name}"`, model: model.name });
1421
+ if (index.constraintName !== undefined) {
1422
+ claim({ namespace: constraintNamespace(table), name: index.constraintName, what: "unique constraint", owner: `${site} on "${model.name}"`, model: model.name });
1423
+ }
1424
+ }
1425
+ if (dialect === "postgres" && (model.primaryKey !== undefined || model.fields.some((fieldIR) => fieldIR.isId))) {
1426
+ const name = model.primaryKeyConstraintName ?? defaultPrimaryKeyName({ table, dialect });
1427
+ claim({ namespace: "relation", name, what: "primary key", owner: `model "${model.name}"`, model: model.name });
1428
+ claim({ namespace: constraintNamespace(table), name, what: "primary key", owner: `model "${model.name}"`, model: model.name });
1429
+ }
1430
+ for (const relation of model.relations) {
1431
+ if (relation.fields === undefined || relation.fields.length === 0)
1432
+ continue;
1433
+ const target = schema.models.find((candidate) => candidate.name === relation.targetModel);
1434
+ if (target !== undefined && target.isView)
1435
+ continue;
1436
+ const name = relation.constraintName ?? defaultForeignKeyName({ table, columns: relation.fields.map((entry) => columnOf(entry)), dialect });
1437
+ claim({ namespace: constraintNamespace(table), name, what: "foreign key", owner: `relation "${model.name}.${relation.name}"`, model: model.name });
1438
+ }
1439
+ for (const checkIR of model.checks ?? []) {
1440
+ claim({ namespace: constraintNamespace(table), name: checkIR.name, what: "CHECK constraint", owner: `model "${model.name}"`, model: model.name });
1441
+ }
1442
+ for (const exclusion of model.exclusions ?? []) {
1443
+ claim({ namespace: constraintNamespace(table), name: exclusion.name, what: "exclusion constraint", owner: `model "${model.name}"`, model: model.name });
1444
+ if (dialect === "postgres")
1445
+ claim({ namespace: "relation", name: exclusion.name, what: "exclusion constraint", owner: `model "${model.name}"`, model: model.name });
1446
+ }
1447
+ }
1448
+ if (dialect === "postgres") {
1449
+ for (const enumDef of schema.enums) {
1450
+ if (enumStorageOf({ enumDef }) !== "database")
1451
+ continue;
1452
+ claim({ namespace: "relation", name: enumDef.dbName ?? enumDef.name, what: "enum type", owner: `enum "${enumDef.name}"` });
1453
+ }
1454
+ }
1455
+ const seenJoinTables = new Set;
1456
+ for (const model of schema.models) {
1457
+ for (const relation of model.relations) {
1458
+ if (relation.targetModel === model.name)
1459
+ continue;
1460
+ if (!isImplicitManyToManyRelation({ model, relation, models: schema.models }))
1461
+ continue;
1462
+ const join = implicitJoinTable({ modelA: model.name, modelB: relation.targetModel, ...relation.relationName === undefined ? {} : { relationName: relation.relationName } });
1463
+ if (seenJoinTables.has(join.tableName))
1464
+ continue;
1465
+ seenJoinTables.add(join.tableName);
1466
+ const owner = `implicit relation "${model.name}.${relation.name}"`;
1467
+ claim({ namespace: "relation", name: join.tableName, what: "join table", owner, model: model.name });
1468
+ const derived = implicitJoinTableObjectNames({ table: join.tableName });
1469
+ claim({ namespace: indexNamespace(join.tableName), name: derived.uniqueIndex, what: "unique index", owner, model: model.name });
1470
+ claim({ namespace: indexNamespace(join.tableName), name: derived.indexB, what: "index", owner, model: model.name });
1471
+ claim({ namespace: constraintNamespace(join.tableName), name: derived.foreignKeyA, what: "foreign key", owner, model: model.name });
1472
+ claim({ namespace: constraintNamespace(join.tableName), name: derived.foreignKeyB, what: "foreign key", owner, model: model.name });
1473
+ }
1474
+ }
1475
+ }
1476
+ function validateIdentifierLengths(params) {
1477
+ const { schema, issues } = params;
1478
+ const dialect = providerToDialect({ provider: schema.datasource.provider });
1479
+ const limit = IDENTIFIER_LIMITS[dialect];
1480
+ if (limit === undefined)
1481
+ return;
1482
+ const { max, unit } = limit;
1483
+ const consequence = dialect === "postgres" ? `PostgreSQL silently truncates identifiers to ${max} bytes (NAMEDATALEN - 1), so the created object never carries this name and migrations cannot converge` : `MySQL refuses identifiers longer than ${max} characters (ER_TOO_LONG_IDENT)`;
1484
+ const check = (entry) => {
1485
+ const length = identifierLength({ name: entry.name, dialect });
1486
+ if (length <= max)
1487
+ return;
1488
+ issues.push({
1489
+ rule: "identifier-too-long",
1490
+ message: `${entry.what} "${entry.name}" is ${length} ${unit} \u2014 ${consequence}. ${entry.remedy}`,
1491
+ ...entry.model === undefined ? {} : { model: entry.model }
1492
+ });
1493
+ };
1494
+ for (const model of schema.models) {
1495
+ const table = model.dbName ?? model.name;
1496
+ check({ name: table, what: model.isView ? "View name" : "Table name", model: model.name, remedy: `Map "${model.name}" to a shorter physical name (@@map).` });
1497
+ for (const fieldIR of model.fields) {
1498
+ check({ name: fieldIR.dbName ?? fieldIR.name, what: "Column name", model: model.name, remedy: `Map "${model.name}.${fieldIR.name}" to a shorter column name (@map).` });
1499
+ }
1500
+ if (model.isView)
1501
+ continue;
1502
+ for (const index of model.indexes) {
1503
+ if (index.name !== undefined) {
1504
+ check({ name: index.name, what: index.kind === "unique" ? "Unique index name" : "Index name", model: model.name, remedy: "Shorten the index name (map:)." });
1505
+ }
1506
+ if (index.constraintName !== undefined) {
1507
+ check({ name: index.constraintName, what: "Unique constraint name", model: model.name, remedy: "Shorten the constraint name." });
1508
+ }
1509
+ }
1510
+ if (model.primaryKeyConstraintName !== undefined) {
1511
+ check({ name: model.primaryKeyConstraintName, what: "Primary key constraint name", model: model.name, remedy: "Shorten the constraint name (map:)." });
1512
+ }
1513
+ for (const relation of model.relations) {
1514
+ if (relation.constraintName === undefined)
1515
+ continue;
1516
+ check({ name: relation.constraintName, what: "Foreign key constraint name", model: model.name, remedy: "Shorten the constraint name (map:)." });
1517
+ }
1518
+ for (const checkIR of model.checks ?? []) {
1519
+ check({ name: checkIR.name, what: "CHECK constraint name", model: model.name, remedy: "Shorten the CHECK name." });
1520
+ }
1521
+ for (const exclusion of model.exclusions ?? []) {
1522
+ check({ name: exclusion.name, what: "Exclusion constraint name", model: model.name, remedy: "Shorten the exclusion name." });
1523
+ }
1524
+ }
1525
+ if (dialect === "postgres") {
1526
+ for (const enumDef of schema.enums) {
1527
+ if (enumStorageOf({ enumDef }) !== "database")
1528
+ continue;
1529
+ check({ name: enumDef.dbName ?? enumDef.name, what: "Enum type name", remedy: `Map enum "${enumDef.name}" to a shorter type name (@@map).` });
1530
+ }
1531
+ }
1532
+ const seenJoinTables = new Set;
1533
+ for (const model of schema.models) {
1534
+ for (const relation of model.relations) {
1535
+ if (relation.targetModel === model.name)
1536
+ continue;
1537
+ if (!isImplicitManyToManyRelation({ model, relation, models: schema.models }))
1538
+ continue;
1539
+ const join = implicitJoinTable({ modelA: model.name, modelB: relation.targetModel, ...relation.relationName === undefined ? {} : { relationName: relation.relationName } });
1540
+ if (seenJoinTables.has(join.tableName))
1541
+ continue;
1542
+ seenJoinTables.add(join.tableName);
1543
+ const remedy = `Give the relation an explicit shorter name (@relation("...")).`;
1544
+ check({ name: join.tableName, what: "Implicit join table name", model: model.name, remedy });
1545
+ const derived = implicitJoinTableObjectNames({ table: join.tableName });
1546
+ check({ name: derived.uniqueIndex, what: "Implicit join table index name", model: model.name, remedy });
1547
+ check({ name: derived.indexB, what: "Implicit join table index name", model: model.name, remedy });
1548
+ check({ name: derived.foreignKeyA, what: "Implicit join table foreign key name", model: model.name, remedy });
1549
+ check({ name: derived.foreignKeyB, what: "Implicit join table foreign key name", model: model.name, remedy });
1550
+ }
1551
+ }
1552
+ }
1263
1553
  function validateModel(params) {
1264
1554
  const { model, modelByName, enumByName, issues } = params;
1265
1555
  const memberNames = new Set;
@@ -1273,6 +1563,10 @@ function validateModel(params) {
1273
1563
  });
1274
1564
  }
1275
1565
  memberNames.add(field.name);
1566
+ const typeIdentity = field.nativeTypeIdentity;
1567
+ if (typeIdentity !== undefined && (typeIdentity === null || typeof typeIdentity !== "object" || field.isUnsupported !== true || typeof typeIdentity.source !== "string" || typeIdentity.source !== field.nativeType || typeof typeIdentity.namespace !== "string" || typeIdentity.namespace.length === 0 || typeIdentity.namespace.includes("\x00"))) {
1568
+ issues.push({ rule: "invalid-native-type-identity", message: `PostgreSQL type identity on "${model.name}.${field.name}" must be bound to its unsupported declaration`, model: model.name, field: field.name });
1569
+ }
1276
1570
  const defaultValue = field.default;
1277
1571
  if (defaultValue !== undefined && defaultValue.kind !== "dbgenerated" && defaultValue.kind !== "list") {
1278
1572
  let valid = !field.isList;
@@ -1326,8 +1620,8 @@ function validateModel(params) {
1326
1620
  field: field.name
1327
1621
  });
1328
1622
  } else {
1329
- const defaultValue2 = field.default;
1330
- const candidate = defaultValue2 === undefined ? undefined : defaultValue2.kind === "enumValue" ? defaultValue2.value : defaultValue2.kind === "literal" && typeof defaultValue2.value === "string" ? defaultValue2.value : undefined;
1623
+ const defaultValue = field.default;
1624
+ const candidate = defaultValue === undefined ? undefined : defaultValue.kind === "enumValue" ? defaultValue.value : defaultValue.kind === "literal" && typeof defaultValue.value === "string" ? defaultValue.value : undefined;
1331
1625
  if (candidate !== undefined && !enumDef.values.includes(candidate)) {
1332
1626
  issues.push({
1333
1627
  rule: "enum-default-not-member",
@@ -1519,22 +1813,22 @@ function validateModel(params) {
1519
1813
  }
1520
1814
  function validatePredicateFields(params) {
1521
1815
  const { predicate, model, fieldNames, site, issues } = params;
1522
- for (const name2 of predicateFieldNames({ predicate })) {
1523
- if (!fieldNames.has(name2)) {
1816
+ for (const name of predicateFieldNames({ predicate })) {
1817
+ if (!fieldNames.has(name)) {
1524
1818
  issues.push({
1525
1819
  rule: "predicate-unknown-field",
1526
- message: `${site} on "${model.name}" references unknown field "${name2}"`,
1820
+ message: `${site} on "${model.name}" references unknown field "${name}"`,
1527
1821
  model: model.name,
1528
- field: name2
1822
+ field: name
1529
1823
  });
1530
1824
  continue;
1531
1825
  }
1532
- if (model.fields.find((field) => field.name === name2)?.isList === true) {
1826
+ if (model.fields.find((field) => field.name === name)?.isList === true) {
1533
1827
  issues.push({
1534
1828
  rule: "predicate-list-field",
1535
- message: `${site} on "${model.name}" compares scalar list "${name2}" \u2014 use a raw expression for array operators`,
1829
+ message: `${site} on "${model.name}" compares scalar list "${name}" \u2014 use a raw expression for array operators`,
1536
1830
  model: model.name,
1537
- field: name2
1831
+ field: name
1538
1832
  });
1539
1833
  }
1540
1834
  }
@@ -1556,10 +1850,10 @@ function assertValidSchemaIR(params) {
1556
1850
  const source = exclusion.source;
1557
1851
  if (source !== undefined) {
1558
1852
  const references = [...source.keys.flatMap((key) => ("field" in key) ? [key.field] : key.fields), ...[source.range.lower, source.range.upper].flatMap((endpoint) => ("field" in endpoint) ? [endpoint.field] : []), ...source.where === undefined ? [] : predicateFieldNames({ predicate: source.where })];
1559
- if (references.some((name2) => !model.fields.some((field) => field.name === name2 && !field.isList)))
1853
+ if (references.some((name) => !model.fields.some((field) => field.name === name && !field.isList)))
1560
1854
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Exclusion references an unknown or list field", meta: { rule: "exclusion-field", model: model.name } });
1561
1855
  }
1562
- if (!exclusion.name || names.has(exclusion.name) || typeof exclusion.definition !== "string" || !exclusion.definition.startsWith("EXCLUDE USING ") || !Array.isArray(exclusion.requiredExtensions) || exclusion.requiredExtensions.some((name2) => name2 !== "btree_gist"))
1856
+ if (!exclusion.name || names.has(exclusion.name) || typeof exclusion.definition !== "string" || !exclusion.definition.startsWith("EXCLUDE USING ") || !Array.isArray(exclusion.requiredExtensions) || exclusion.requiredExtensions.some((name) => name !== "btree_gist"))
1563
1857
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Invalid exclusion declaration", meta: { rule: "exclusion-definition", model: model.name } });
1564
1858
  names.add(exclusion.name);
1565
1859
  }
@@ -1581,8 +1875,8 @@ function assertValidSchemaIR(params) {
1581
1875
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `List default on non-list field ${model.name}.${field.name}` });
1582
1876
  scalarListDefault({ type: field.type, values: field.default.values });
1583
1877
  if (isEnumType(field.type)) {
1584
- const name2 = field.type.enum;
1585
- const members = params.schema.enums.find((entry) => entry.name === name2)?.values ?? [];
1878
+ const name = field.type.enum;
1879
+ const members = params.schema.enums.find((entry) => entry.name === name)?.values ?? [];
1586
1880
  if (field.default.values.some((value) => typeof value !== "string" || !members.includes(value)))
1587
1881
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `Invalid enum-list default on ${model.name}.${field.name}` });
1588
1882
  }
@@ -1673,8 +1967,8 @@ function validateNativeRls(params) {
1673
1967
  const tables = new Set;
1674
1968
  const throughTargets = new Map;
1675
1969
  for (const model of schema.models) {
1676
- const add = (params2) => {
1677
- issues.push({ ...params2, model: model.name });
1970
+ const add = (params) => {
1971
+ issues.push({ ...params, model: model.name });
1678
1972
  };
1679
1973
  const table = model.dbName ?? model.name;
1680
1974
  if (tables.has(table))
@@ -1682,10 +1976,10 @@ function validateNativeRls(params) {
1682
1976
  tables.add(table);
1683
1977
  const columns = new Set;
1684
1978
  for (const field of model.fields) {
1685
- const name2 = field.dbName ?? field.name;
1686
- if (columns.has(name2))
1687
- add({ rule: "rls-duplicate-column", message: `RLS model ${model.name} repeats physical column ${name2}`, field: field.name });
1688
- columns.add(name2);
1979
+ const name = field.dbName ?? field.name;
1980
+ if (columns.has(name))
1981
+ add({ rule: "rls-duplicate-column", message: `RLS model ${model.name} repeats physical column ${name}`, field: field.name });
1982
+ columns.add(name);
1689
1983
  }
1690
1984
  if (model.policy !== undefined)
1691
1985
  add({ rule: "rls-policy-conflict", message: "Native RLS cannot combine with application-level ModelIR.policy" });
@@ -1738,7 +2032,7 @@ function validateNativeRls(params) {
1738
2032
  add({ rule: "rls-invalid-through", message: "Native RLS through requires a to-one relation with an actual, complete foreign key on this model" });
1739
2033
  continue;
1740
2034
  }
1741
- if (relation.isOptional || relation.fields?.some((name2) => model.fields.find((field) => field.name === name2)?.isOptional)) {
2035
+ if (relation.isOptional || relation.fields?.some((name) => model.fields.find((field) => field.name === name)?.isOptional)) {
1742
2036
  add({ rule: "rls-nullable-through", message: "Native RLS through requires a non-nullable parent relation and foreign key; NULL parent links cannot satisfy WITH CHECK. Make the link required or use a direct ownership rule", field: relation.name });
1743
2037
  continue;
1744
2038
  }
@@ -1770,8 +2064,8 @@ function validRlsForeignKey(params) {
1770
2064
  const refs = relation.references;
1771
2065
  if (fields === undefined || refs === undefined || fields.length === 0 || fields.length !== refs.length || new Set(fields).size !== fields.length || new Set(refs).size !== refs.length)
1772
2066
  return false;
1773
- const validFields = fields.every((name2, index) => {
1774
- const field = model.fields.find((f) => f.name === name2);
2067
+ const validFields = fields.every((name, index) => {
2068
+ const field = model.fields.find((f) => f.name === name);
1775
2069
  const ref = target.fields.find((f) => f.name === refs[index]);
1776
2070
  return field !== undefined && ref !== undefined && !field.isList && !ref.isList && !field.isUnsupported && !ref.isUnsupported && (field.type === ref.type || isEnumType(field.type) && isEnumType(ref.type) && field.type.enum === ref.type.enum);
1777
2071
  });
@@ -1780,76 +2074,72 @@ function validRlsForeignKey(params) {
1780
2074
  ...target.primaryKey === undefined ? [] : [target.primaryKey.fields],
1781
2075
  ...target.indexes.filter((index) => index.kind === "unique" && index.where === undefined).map((index) => index.fields.map((f) => f.name))
1782
2076
  ];
1783
- return validFields && uniqueSets.some((set) => set.length === refs.length && set.every((name2) => refs.includes(name2)));
1784
- }
1785
- // packages/schema/src/m2m.ts
1786
- function implicitJoinTable(params) {
1787
- const sorted = [params.modelA, params.modelB].sort();
1788
- const first = sorted[0];
1789
- const second = sorted[1];
1790
- const key = params.relationName ?? `${first}To${second}`;
1791
- return {
1792
- tableName: `_${key}`,
1793
- modelA: first,
1794
- modelB: second,
1795
- columnA: "A",
1796
- columnB: "B"
1797
- };
2077
+ return validFields && uniqueSets.some((set) => set.length === refs.length && set.every((name) => refs.includes(name)));
1798
2078
  }
1799
2079
  export {
1800
- validateSchemaIR,
1801
- validateRlsContextIR,
1802
- validateComputedConfig,
1803
- unsupportedCapability,
1804
- sqlObjectKey,
1805
- serializeJsonDefault,
1806
- scalarListDefault,
1807
- providerToDialect,
1808
- predicateKey,
1809
- predicateFieldNames,
1810
- mergeExtensionDeclarations,
1811
- isSqlObjectIdentity,
1812
- isSqlObjectDefinition,
1813
- isReservedDelegateName,
1814
- isReservedClientName,
1815
- isPredicateFieldRef,
1816
- isImplicitManyToManyRelation,
1817
- isEnumType,
1818
- invalidStoredData,
1819
- internalError,
1820
- implicitJoinTable,
1821
- fieldTypeIdentity,
1822
- extensionOptionsHash,
1823
- extensionManifestEntry,
1824
- extensionDeclarations,
1825
- exactIntegerLiteral,
1826
- enumStorageOf,
1827
- encodePrismaString,
1828
- defineExtension,
1829
- decodePrismaString,
1830
- buildExtensionRegistry,
1831
- buildComputedManifest,
1832
- assertValidSchemaIR,
1833
- assertMigrationObjects,
1834
- VibeError,
1835
- VIBE_ERROR_CODES,
1836
- ScopedQueryError,
1837
- SQLITE_CAPABILITIES,
1838
- SCOPED_REFUSAL_REASONS,
1839
- RlsContextError,
1840
- ReadOnlyClientError,
1841
- RLS_CONTEXT_REASONS,
1842
- RESERVED_ENUM_NAMES,
1843
- RESERVED_DELEGATE_NAMES,
1844
- RESERVED_CLIENT_NAMES,
1845
- READ_ONLY_REFUSAL_REASONS,
1846
- PREDICATE_COMPARISON_OPS,
1847
- POSTGRES_CAPABILITIES,
1848
- MYSQL_CAPABILITIES,
1849
- ENUM_STORAGES,
1850
- EMPTY_EXTENSION_REGISTRY,
2080
+ DIALECTS,
1851
2081
  DIALECT_CAPABILITIES,
1852
- DIALECTS
2082
+ EMPTY_EXTENSION_REGISTRY,
2083
+ ENUM_STORAGES,
2084
+ IDENTIFIER_LIMITS,
2085
+ MYSQL_CAPABILITIES,
2086
+ POSTGRES_CAPABILITIES,
2087
+ PREDICATE_COMPARISON_OPS,
2088
+ READ_ONLY_REFUSAL_REASONS,
2089
+ RESERVED_CLIENT_NAMES,
2090
+ RESERVED_DELEGATE_NAMES,
2091
+ RESERVED_ENUM_NAMES,
2092
+ RLS_CONTEXT_REASONS,
2093
+ ReadOnlyClientError,
2094
+ RlsContextError,
2095
+ SCOPED_REFUSAL_REASONS,
2096
+ SQLITE_CAPABILITIES,
2097
+ ScopedQueryError,
2098
+ VIBE_ERROR_CODES,
2099
+ VibeError,
2100
+ assertIdentifierFits,
2101
+ assertMigrationObjects,
2102
+ assertValidSchemaIR,
2103
+ buildComputedManifest,
2104
+ buildExtensionRegistry,
2105
+ decodePrismaString,
2106
+ defaultForeignKeyName,
2107
+ defaultIndexName,
2108
+ defaultPrimaryKeyName,
2109
+ defineExtension,
2110
+ encodePrismaString,
2111
+ enumStorageOf,
2112
+ exactDecimalLiteral,
2113
+ exactIntegerLiteral,
2114
+ extensionDeclarations,
2115
+ extensionManifestEntry,
2116
+ extensionOptionsHash,
2117
+ fieldTypeIdentity,
2118
+ fitDefaultName,
2119
+ identifierLength,
2120
+ implicitJoinTable,
2121
+ implicitJoinTableObjectNames,
2122
+ internalError,
2123
+ invalidStoredData,
2124
+ isEnumType,
2125
+ isImplicitManyToManyRelation,
2126
+ isPredicateFieldRef,
2127
+ isReservedClientName,
2128
+ isReservedDelegateName,
2129
+ isSqlObjectDefinition,
2130
+ isSqlObjectIdentity,
2131
+ mergeExtensionDeclarations,
2132
+ postgresGeneratedObjectName,
2133
+ predicateFieldNames,
2134
+ predicateKey,
2135
+ providerToDialect,
2136
+ scalarListDefault,
2137
+ serializeJsonDefault,
2138
+ sqlObjectKey,
2139
+ unsupportedCapability,
2140
+ validateComputedConfig,
2141
+ validateRlsContextIR,
2142
+ validateSchemaIR
1853
2143
  };
1854
2144
 
1855
- //# debugId=21FF102BB1B417EA64756E2164756E21
2145
+ //# debugId=47DE6CAFB1D4DC2864756E2164756E21