@vibeorm/schema 2.3.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.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +301 -15
- package/dist/index.js.map +7 -6
- package/dist/physical-names.d.ts +119 -0
- package/dist/physical-names.d.ts.map +1 -0
- package/dist/prisma-literal.d.ts +13 -0
- package/dist/prisma-literal.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type { MigrationObjectIR, QualifiedSqlName, SqlObjectDefinition, SqlObjec
|
|
|
13
13
|
export type { AdvisoryLockMode, DialectCapabilities, FullTextEngine, RowLockStrength, RowLockWaitPolicy, } from "./capabilities.ts";
|
|
14
14
|
export { READ_ONLY_REFUSAL_REASONS, ReadOnlyClientError, SCOPED_REFUSAL_REASONS, ScopedQueryError, VIBE_ERROR_CODES, VibeError, internalError, invalidStoredData, unsupportedCapability, } from "./errors.ts";
|
|
15
15
|
export type { ReadOnlyRefusalReason, ScopedRefusalReason, VibeErrorCode } from "./errors.ts";
|
|
16
|
-
export { decodePrismaString, encodePrismaString, exactIntegerLiteral } from "./prisma-literal.ts";
|
|
16
|
+
export { decodePrismaString, encodePrismaString, exactDecimalLiteral, exactIntegerLiteral } from "./prisma-literal.ts";
|
|
17
17
|
export { RESERVED_CLIENT_NAMES, RESERVED_DELEGATE_NAMES, isReservedClientName, isReservedDelegateName, } from "./reserved.ts";
|
|
18
18
|
export { EMPTY_EXTENSION_REGISTRY, buildExtensionRegistry, defineExtension, extensionDeclarations, extensionManifestEntry, extensionOptionsHash, mergeExtensionDeclarations, } from "./extension.ts";
|
|
19
19
|
export type { ExtensionArtifact, ExtensionArtifactOwner, ExtensionAttributeArgs, ExtensionAttributeValue, ExtensionClientContribution, ExtensionClientPlane, ExtensionDoctorCheck, ExtensionDoctorHook, ExtensionDoctorReport, ExtensionFieldType, ExtensionImportSpec, ExtensionManifestEntry, ExtensionMigratePlane, ExtensionModelAttribute, ExtensionModelTypeNames, ExtensionRegistry, ExtensionRuntimePlane, ExtensionSchemaContext, ExtensionSqlExecutor, ExtensionSqlPlane, ExtensionWhereOperator, SqlFragmentPart, SqlFragmentSpec, VibeExtension, } from "./extension.ts";
|
|
@@ -23,6 +23,8 @@ export { RESERVED_ENUM_NAMES, assertValidSchemaIR, validateSchemaIR } from "./va
|
|
|
23
23
|
export type { ValidationIssue } from "./validate.ts";
|
|
24
24
|
export { implicitJoinTable } from "./m2m.ts";
|
|
25
25
|
export type { ImplicitJoinTable } from "./m2m.ts";
|
|
26
|
+
export { IDENTIFIER_LIMITS, assertIdentifierFits, defaultForeignKeyName, defaultIndexName, defaultPrimaryKeyName, fitDefaultName, identifierLength, implicitJoinTableObjectNames, postgresGeneratedObjectName, } from "./physical-names.ts";
|
|
27
|
+
export type { IdentifierLimit, ImplicitJoinTableObjectNames } from "./physical-names.ts";
|
|
26
28
|
export type { NativeRlsIR, RlsContextFieldIR, RlsContextIR, RlsContextRefIR, RlsContextScalar, RlsPolicyIR } from "./ir.ts";
|
|
27
29
|
export { DIALECT_CAPABILITIES, MYSQL_CAPABILITIES, POSTGRES_CAPABILITIES, SQLITE_CAPABILITIES } from "./capabilities.ts";
|
|
28
30
|
export { RLS_CONTEXT_REASONS, RlsContextError } from "./errors.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,YAAY,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAElH,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAElG,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACnH,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,OAAO,EACP,MAAM,EACN,WAAW,EACX,eAAe,EACf,OAAO,EACP,SAAS,EACT,YAAY,EACZ,OAAO,EACP,OAAO,EACP,SAAS,EACT,OAAO,EACP,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,UAAU,EACV,QAAQ,GACT,MAAM,SAAS,CAAC;AAIjB,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAClH,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE9H,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC1H,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIrK,YAAY,EACV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAI7F,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -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",
|
|
@@ -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,
|
|
@@ -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;
|
|
@@ -1786,25 +2076,12 @@ function validRlsForeignKey(params) {
|
|
|
1786
2076
|
];
|
|
1787
2077
|
return validFields && uniqueSets.some((set) => set.length === refs.length && set.every((name) => refs.includes(name)));
|
|
1788
2078
|
}
|
|
1789
|
-
// packages/schema/src/m2m.ts
|
|
1790
|
-
function implicitJoinTable(params) {
|
|
1791
|
-
const sorted = [params.modelA, params.modelB].sort();
|
|
1792
|
-
const first = sorted[0];
|
|
1793
|
-
const second = sorted[1];
|
|
1794
|
-
const key = params.relationName ?? `${first}To${second}`;
|
|
1795
|
-
return {
|
|
1796
|
-
tableName: `_${key}`,
|
|
1797
|
-
modelA: first,
|
|
1798
|
-
modelB: second,
|
|
1799
|
-
columnA: "A",
|
|
1800
|
-
columnB: "B"
|
|
1801
|
-
};
|
|
1802
|
-
}
|
|
1803
2079
|
export {
|
|
1804
2080
|
DIALECTS,
|
|
1805
2081
|
DIALECT_CAPABILITIES,
|
|
1806
2082
|
EMPTY_EXTENSION_REGISTRY,
|
|
1807
2083
|
ENUM_STORAGES,
|
|
2084
|
+
IDENTIFIER_LIMITS,
|
|
1808
2085
|
MYSQL_CAPABILITIES,
|
|
1809
2086
|
POSTGRES_CAPABILITIES,
|
|
1810
2087
|
PREDICATE_COMPARISON_OPS,
|
|
@@ -1820,20 +2097,28 @@ export {
|
|
|
1820
2097
|
ScopedQueryError,
|
|
1821
2098
|
VIBE_ERROR_CODES,
|
|
1822
2099
|
VibeError,
|
|
2100
|
+
assertIdentifierFits,
|
|
1823
2101
|
assertMigrationObjects,
|
|
1824
2102
|
assertValidSchemaIR,
|
|
1825
2103
|
buildComputedManifest,
|
|
1826
2104
|
buildExtensionRegistry,
|
|
1827
2105
|
decodePrismaString,
|
|
2106
|
+
defaultForeignKeyName,
|
|
2107
|
+
defaultIndexName,
|
|
2108
|
+
defaultPrimaryKeyName,
|
|
1828
2109
|
defineExtension,
|
|
1829
2110
|
encodePrismaString,
|
|
1830
2111
|
enumStorageOf,
|
|
2112
|
+
exactDecimalLiteral,
|
|
1831
2113
|
exactIntegerLiteral,
|
|
1832
2114
|
extensionDeclarations,
|
|
1833
2115
|
extensionManifestEntry,
|
|
1834
2116
|
extensionOptionsHash,
|
|
1835
2117
|
fieldTypeIdentity,
|
|
2118
|
+
fitDefaultName,
|
|
2119
|
+
identifierLength,
|
|
1836
2120
|
implicitJoinTable,
|
|
2121
|
+
implicitJoinTableObjectNames,
|
|
1837
2122
|
internalError,
|
|
1838
2123
|
invalidStoredData,
|
|
1839
2124
|
isEnumType,
|
|
@@ -1844,6 +2129,7 @@ export {
|
|
|
1844
2129
|
isSqlObjectDefinition,
|
|
1845
2130
|
isSqlObjectIdentity,
|
|
1846
2131
|
mergeExtensionDeclarations,
|
|
2132
|
+
postgresGeneratedObjectName,
|
|
1847
2133
|
predicateFieldNames,
|
|
1848
2134
|
predicateKey,
|
|
1849
2135
|
providerToDialect,
|
|
@@ -1856,4 +2142,4 @@ export {
|
|
|
1856
2142
|
validateSchemaIR
|
|
1857
2143
|
};
|
|
1858
2144
|
|
|
1859
|
-
//# debugId=
|
|
2145
|
+
//# debugId=47DE6CAFB1D4DC2864756E2164756E21
|