@famgia/omnify-core 0.0.7 → 0.0.8
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.cjs +242 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +242 -63
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -641,8 +641,11 @@ function buildSchemaOptions(data) {
|
|
|
641
641
|
if (data.tableName !== void 0 && typeof data.tableName === "string") {
|
|
642
642
|
options.tableName = data.tableName;
|
|
643
643
|
}
|
|
644
|
-
if (data.
|
|
645
|
-
options.
|
|
644
|
+
if (data.id !== void 0 && typeof data.id === "boolean") {
|
|
645
|
+
options.id = data.id;
|
|
646
|
+
}
|
|
647
|
+
if (data.idType !== void 0) {
|
|
648
|
+
options.idType = data.idType;
|
|
646
649
|
}
|
|
647
650
|
if (data.authenticatable !== void 0 && typeof data.authenticatable === "boolean") {
|
|
648
651
|
options.authenticatable = data.authenticatable;
|
|
@@ -1352,7 +1355,18 @@ var VALID_RELATIONS = [
|
|
|
1352
1355
|
"OneToOne",
|
|
1353
1356
|
"OneToMany",
|
|
1354
1357
|
"ManyToOne",
|
|
1355
|
-
"ManyToMany"
|
|
1358
|
+
"ManyToMany",
|
|
1359
|
+
// Polymorphic relations
|
|
1360
|
+
"MorphTo",
|
|
1361
|
+
"MorphOne",
|
|
1362
|
+
"MorphMany",
|
|
1363
|
+
"MorphToMany",
|
|
1364
|
+
"MorphedByMany"
|
|
1365
|
+
];
|
|
1366
|
+
var MORPH_TO_RELATIONS = ["MorphTo"];
|
|
1367
|
+
var MORPH_INVERSE_RELATIONS = [
|
|
1368
|
+
"MorphOne",
|
|
1369
|
+
"MorphMany"
|
|
1356
1370
|
];
|
|
1357
1371
|
var VALID_REFERENTIAL_ACTIONS = [
|
|
1358
1372
|
"CASCADE",
|
|
@@ -1364,6 +1378,10 @@ var VALID_REFERENTIAL_ACTIONS = [
|
|
|
1364
1378
|
var ASSOCIATION_FIELDS = [
|
|
1365
1379
|
"relation",
|
|
1366
1380
|
"target",
|
|
1381
|
+
"targets",
|
|
1382
|
+
// For MorphTo: array of target schema names
|
|
1383
|
+
"morphName",
|
|
1384
|
+
// For MorphOne/MorphMany/MorphedByMany: polymorphic name
|
|
1367
1385
|
"inversedBy",
|
|
1368
1386
|
"mappedBy",
|
|
1369
1387
|
"onDelete",
|
|
@@ -1375,13 +1393,15 @@ var AssociationType = {
|
|
|
1375
1393
|
name: "Association",
|
|
1376
1394
|
category: "relation",
|
|
1377
1395
|
validFields: createValidFields(ASSOCIATION_FIELDS),
|
|
1378
|
-
requiredFields: ["relation"
|
|
1396
|
+
requiredFields: ["relation"],
|
|
1379
1397
|
dbCompatibility: fullDbCompatibility(),
|
|
1380
1398
|
validate(propertyName, property, filePath) {
|
|
1381
1399
|
const errors = [];
|
|
1382
1400
|
const {
|
|
1383
1401
|
relation,
|
|
1384
1402
|
target,
|
|
1403
|
+
targets,
|
|
1404
|
+
morphName,
|
|
1385
1405
|
onDelete,
|
|
1386
1406
|
onUpdate
|
|
1387
1407
|
} = property;
|
|
@@ -1390,17 +1410,21 @@ var AssociationType = {
|
|
|
1390
1410
|
validationError(
|
|
1391
1411
|
`Property '${propertyName}' of type 'Association' requires 'relation' field`,
|
|
1392
1412
|
buildLocation5(filePath),
|
|
1393
|
-
"Add relation: OneToOne, OneToMany, ManyToOne, or
|
|
1413
|
+
"Add relation: OneToOne, OneToMany, ManyToOne, ManyToMany, MorphTo, MorphOne, MorphMany, MorphToMany, or MorphedByMany"
|
|
1394
1414
|
)
|
|
1395
1415
|
);
|
|
1396
|
-
|
|
1416
|
+
return errors;
|
|
1417
|
+
}
|
|
1418
|
+
if (typeof relation !== "string") {
|
|
1397
1419
|
errors.push(
|
|
1398
1420
|
validationError(
|
|
1399
1421
|
`Property '${propertyName}' relation field must be a string`,
|
|
1400
1422
|
buildLocation5(filePath)
|
|
1401
1423
|
)
|
|
1402
1424
|
);
|
|
1403
|
-
|
|
1425
|
+
return errors;
|
|
1426
|
+
}
|
|
1427
|
+
if (!VALID_RELATIONS.includes(relation)) {
|
|
1404
1428
|
errors.push(
|
|
1405
1429
|
validationError(
|
|
1406
1430
|
`Property '${propertyName}' has invalid relation '${relation}'`,
|
|
@@ -1408,22 +1432,147 @@ var AssociationType = {
|
|
|
1408
1432
|
`Use one of: ${VALID_RELATIONS.join(", ")}`
|
|
1409
1433
|
)
|
|
1410
1434
|
);
|
|
1435
|
+
return errors;
|
|
1411
1436
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1437
|
+
const relationTyped = relation;
|
|
1438
|
+
if (MORPH_TO_RELATIONS.includes(relationTyped)) {
|
|
1439
|
+
if (targets === void 0) {
|
|
1440
|
+
errors.push(
|
|
1441
|
+
validationError(
|
|
1442
|
+
`Property '${propertyName}' with relation 'MorphTo' requires 'targets' field`,
|
|
1443
|
+
buildLocation5(filePath),
|
|
1444
|
+
"Add targets: [Post, Video, Image]"
|
|
1445
|
+
)
|
|
1446
|
+
);
|
|
1447
|
+
} else if (!Array.isArray(targets)) {
|
|
1448
|
+
errors.push(
|
|
1449
|
+
validationError(
|
|
1450
|
+
`Property '${propertyName}' targets field must be an array of schema names`,
|
|
1451
|
+
buildLocation5(filePath),
|
|
1452
|
+
"Example: targets: [Post, Video, Image]"
|
|
1453
|
+
)
|
|
1454
|
+
);
|
|
1455
|
+
} else if (targets.length === 0) {
|
|
1456
|
+
errors.push(
|
|
1457
|
+
validationError(
|
|
1458
|
+
`Property '${propertyName}' targets array cannot be empty`,
|
|
1459
|
+
buildLocation5(filePath),
|
|
1460
|
+
"Add at least one target schema"
|
|
1461
|
+
)
|
|
1462
|
+
);
|
|
1463
|
+
} else {
|
|
1464
|
+
for (const t of targets) {
|
|
1465
|
+
if (typeof t !== "string") {
|
|
1466
|
+
errors.push(
|
|
1467
|
+
validationError(
|
|
1468
|
+
`Property '${propertyName}' targets array contains non-string value`,
|
|
1469
|
+
buildLocation5(filePath)
|
|
1470
|
+
)
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
if (target !== void 0) {
|
|
1476
|
+
errors.push(
|
|
1477
|
+
validationError(
|
|
1478
|
+
`Property '${propertyName}' with relation 'MorphTo' should use 'targets' (plural) not 'target'`,
|
|
1479
|
+
buildLocation5(filePath),
|
|
1480
|
+
"Change target to targets array"
|
|
1481
|
+
)
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
} else if (MORPH_INVERSE_RELATIONS.includes(relationTyped)) {
|
|
1485
|
+
if (target === void 0) {
|
|
1486
|
+
errors.push(
|
|
1487
|
+
validationError(
|
|
1488
|
+
`Property '${propertyName}' with relation '${relation}' requires 'target' field`,
|
|
1489
|
+
buildLocation5(filePath),
|
|
1490
|
+
"Add target schema name: target: Comment"
|
|
1491
|
+
)
|
|
1492
|
+
);
|
|
1493
|
+
} else if (typeof target !== "string") {
|
|
1494
|
+
errors.push(
|
|
1495
|
+
validationError(
|
|
1496
|
+
`Property '${propertyName}' target field must be a string`,
|
|
1497
|
+
buildLocation5(filePath)
|
|
1498
|
+
)
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1501
|
+
if (morphName === void 0) {
|
|
1502
|
+
errors.push(
|
|
1503
|
+
validationError(
|
|
1504
|
+
`Property '${propertyName}' with relation '${relation}' requires 'morphName' field`,
|
|
1505
|
+
buildLocation5(filePath),
|
|
1506
|
+
"Add morphName to match the MorphTo property name (e.g., morphName: commentable)"
|
|
1507
|
+
)
|
|
1508
|
+
);
|
|
1509
|
+
} else if (typeof morphName !== "string") {
|
|
1510
|
+
errors.push(
|
|
1511
|
+
validationError(
|
|
1512
|
+
`Property '${propertyName}' morphName field must be a string`,
|
|
1513
|
+
buildLocation5(filePath)
|
|
1514
|
+
)
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
} else if (relationTyped === "MorphToMany") {
|
|
1518
|
+
if (target === void 0) {
|
|
1519
|
+
errors.push(
|
|
1520
|
+
validationError(
|
|
1521
|
+
`Property '${propertyName}' with relation 'MorphToMany' requires 'target' field`,
|
|
1522
|
+
buildLocation5(filePath),
|
|
1523
|
+
"Add target schema name: target: Tag"
|
|
1524
|
+
)
|
|
1525
|
+
);
|
|
1526
|
+
} else if (typeof target !== "string") {
|
|
1527
|
+
errors.push(
|
|
1528
|
+
validationError(
|
|
1529
|
+
`Property '${propertyName}' target field must be a string`,
|
|
1530
|
+
buildLocation5(filePath)
|
|
1531
|
+
)
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
} else if (relationTyped === "MorphedByMany") {
|
|
1535
|
+
if (target === void 0) {
|
|
1536
|
+
errors.push(
|
|
1537
|
+
validationError(
|
|
1538
|
+
`Property '${propertyName}' with relation 'MorphedByMany' requires 'target' field`,
|
|
1539
|
+
buildLocation5(filePath),
|
|
1540
|
+
"Add target schema name: target: Post"
|
|
1541
|
+
)
|
|
1542
|
+
);
|
|
1543
|
+
} else if (typeof target !== "string") {
|
|
1544
|
+
errors.push(
|
|
1545
|
+
validationError(
|
|
1546
|
+
`Property '${propertyName}' target field must be a string`,
|
|
1547
|
+
buildLocation5(filePath)
|
|
1548
|
+
)
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
if (morphName !== void 0 && typeof morphName !== "string") {
|
|
1552
|
+
errors.push(
|
|
1553
|
+
validationError(
|
|
1554
|
+
`Property '${propertyName}' morphName field must be a string`,
|
|
1555
|
+
buildLocation5(filePath)
|
|
1556
|
+
)
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
} else {
|
|
1560
|
+
if (target === void 0) {
|
|
1561
|
+
errors.push(
|
|
1562
|
+
validationError(
|
|
1563
|
+
`Property '${propertyName}' of type 'Association' requires 'target' field`,
|
|
1564
|
+
buildLocation5(filePath),
|
|
1565
|
+
"Add target schema name: target: User"
|
|
1566
|
+
)
|
|
1567
|
+
);
|
|
1568
|
+
} else if (typeof target !== "string") {
|
|
1569
|
+
errors.push(
|
|
1570
|
+
validationError(
|
|
1571
|
+
`Property '${propertyName}' target field must be a string`,
|
|
1572
|
+
buildLocation5(filePath)
|
|
1573
|
+
)
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1427
1576
|
}
|
|
1428
1577
|
if (onDelete !== void 0 && typeof onDelete === "string") {
|
|
1429
1578
|
if (!VALID_REFERENTIAL_ACTIONS.includes(onDelete)) {
|
|
@@ -1450,37 +1599,8 @@ var AssociationType = {
|
|
|
1450
1599
|
return errors;
|
|
1451
1600
|
}
|
|
1452
1601
|
};
|
|
1453
|
-
var PolymorphicType = {
|
|
1454
|
-
name: "Polymorphic",
|
|
1455
|
-
category: "relation",
|
|
1456
|
-
validFields: createValidFields(ASSOCIATION_FIELDS),
|
|
1457
|
-
dbCompatibility: fullDbCompatibility(),
|
|
1458
|
-
validate(propertyName, property, filePath) {
|
|
1459
|
-
const errors = [];
|
|
1460
|
-
const { relation, target } = property;
|
|
1461
|
-
if (relation === void 0) {
|
|
1462
|
-
errors.push(
|
|
1463
|
-
validationError(
|
|
1464
|
-
`Property '${propertyName}' of type 'Polymorphic' requires 'relation' field`,
|
|
1465
|
-
buildLocation5(filePath),
|
|
1466
|
-
"Add relation type"
|
|
1467
|
-
)
|
|
1468
|
-
);
|
|
1469
|
-
}
|
|
1470
|
-
if (target !== void 0 && typeof target !== "string") {
|
|
1471
|
-
errors.push(
|
|
1472
|
-
validationError(
|
|
1473
|
-
`Property '${propertyName}' target field must be a string`,
|
|
1474
|
-
buildLocation5(filePath)
|
|
1475
|
-
)
|
|
1476
|
-
);
|
|
1477
|
-
}
|
|
1478
|
-
return errors;
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
1602
|
var relationTypes = [
|
|
1482
|
-
AssociationType
|
|
1483
|
-
PolymorphicType
|
|
1603
|
+
AssociationType
|
|
1484
1604
|
];
|
|
1485
1605
|
|
|
1486
1606
|
// src/validation/types/index.ts
|
|
@@ -1503,7 +1623,7 @@ function registerBuiltInTypes() {
|
|
|
1503
1623
|
registerBuiltInTypes();
|
|
1504
1624
|
|
|
1505
1625
|
// src/validation/validator.ts
|
|
1506
|
-
var
|
|
1626
|
+
var VALID_ID_TYPES = ["Int", "BigInt", "Uuid", "String"];
|
|
1507
1627
|
function buildLocation6(file, line, column) {
|
|
1508
1628
|
const loc = { file };
|
|
1509
1629
|
if (line !== void 0) {
|
|
@@ -1599,6 +1719,8 @@ function validateProperties(schema, filePath, customTypes = []) {
|
|
|
1599
1719
|
}
|
|
1600
1720
|
return errors;
|
|
1601
1721
|
}
|
|
1722
|
+
var MORPH_TO_RELATIONS2 = ["MorphTo"];
|
|
1723
|
+
var MORPH_INVERSE_RELATIONS2 = ["MorphOne", "MorphMany"];
|
|
1602
1724
|
function validateAssociations(schema, allSchemas) {
|
|
1603
1725
|
const errors = [];
|
|
1604
1726
|
if (!schema.properties) {
|
|
@@ -1619,7 +1741,22 @@ function validateAssociations(schema, allSchemas) {
|
|
|
1619
1741
|
)
|
|
1620
1742
|
);
|
|
1621
1743
|
}
|
|
1622
|
-
|
|
1744
|
+
const relation = assocProp.relation;
|
|
1745
|
+
if (relation && MORPH_TO_RELATIONS2.includes(relation)) {
|
|
1746
|
+
if (assocProp.targets) {
|
|
1747
|
+
for (const target of assocProp.targets) {
|
|
1748
|
+
if (!availableSchemas.includes(target)) {
|
|
1749
|
+
errors.push(
|
|
1750
|
+
invalidAssociationTargetError(
|
|
1751
|
+
target,
|
|
1752
|
+
buildLocation6(schema.filePath),
|
|
1753
|
+
availableSchemas
|
|
1754
|
+
)
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
} else if (assocProp.target && !availableSchemas.includes(assocProp.target)) {
|
|
1623
1760
|
errors.push(
|
|
1624
1761
|
invalidAssociationTargetError(
|
|
1625
1762
|
assocProp.target,
|
|
@@ -1628,6 +1765,34 @@ function validateAssociations(schema, allSchemas) {
|
|
|
1628
1765
|
)
|
|
1629
1766
|
);
|
|
1630
1767
|
}
|
|
1768
|
+
if (relation && MORPH_INVERSE_RELATIONS2.includes(relation)) {
|
|
1769
|
+
if (assocProp.morphName && assocProp.target) {
|
|
1770
|
+
const targetSchema = allSchemas[assocProp.target];
|
|
1771
|
+
if (targetSchema?.properties) {
|
|
1772
|
+
const morphProperty = targetSchema.properties[assocProp.morphName];
|
|
1773
|
+
if (!morphProperty) {
|
|
1774
|
+
errors.push(
|
|
1775
|
+
validationError(
|
|
1776
|
+
`Property '${name}' references non-existent morphName '${assocProp.morphName}' on '${assocProp.target}'`,
|
|
1777
|
+
buildLocation6(schema.filePath),
|
|
1778
|
+
`Add a MorphTo property '${assocProp.morphName}' to ${assocProp.target} schema`
|
|
1779
|
+
)
|
|
1780
|
+
);
|
|
1781
|
+
} else if (morphProperty.type === "Association") {
|
|
1782
|
+
const morphAssoc = morphProperty;
|
|
1783
|
+
if (morphAssoc.relation !== "MorphTo") {
|
|
1784
|
+
errors.push(
|
|
1785
|
+
validationError(
|
|
1786
|
+
`Property '${name}' morphName '${assocProp.morphName}' on '${assocProp.target}' must be a MorphTo relation`,
|
|
1787
|
+
buildLocation6(schema.filePath),
|
|
1788
|
+
`Change ${assocProp.target}.${assocProp.morphName} relation to MorphTo`
|
|
1789
|
+
)
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1631
1796
|
if (assocProp.inversedBy && assocProp.target) {
|
|
1632
1797
|
const targetSchema = allSchemas[assocProp.target];
|
|
1633
1798
|
if (targetSchema?.properties) {
|
|
@@ -1670,13 +1835,13 @@ function validateOptions(schema, filePath) {
|
|
|
1670
1835
|
if (!options) {
|
|
1671
1836
|
return errors;
|
|
1672
1837
|
}
|
|
1673
|
-
if (options.
|
|
1674
|
-
if (!
|
|
1838
|
+
if (options.idType !== void 0) {
|
|
1839
|
+
if (!VALID_ID_TYPES.includes(options.idType)) {
|
|
1675
1840
|
errors.push(
|
|
1676
1841
|
validationError(
|
|
1677
|
-
`Invalid
|
|
1842
|
+
`Invalid idType '${options.idType}'`,
|
|
1678
1843
|
buildLocation6(filePath),
|
|
1679
|
-
`Use one of: ${
|
|
1844
|
+
`Use one of: ${VALID_ID_TYPES.join(", ")}`
|
|
1680
1845
|
)
|
|
1681
1846
|
);
|
|
1682
1847
|
}
|
|
@@ -2422,7 +2587,7 @@ function getSchemaMetadata(schema) {
|
|
|
2422
2587
|
associationNames,
|
|
2423
2588
|
hasTimestamps: schema.options?.timestamps ?? false,
|
|
2424
2589
|
hasSoftDelete: schema.options?.softDelete ?? false,
|
|
2425
|
-
|
|
2590
|
+
idType: schema.options?.idType ?? "BigInt"
|
|
2426
2591
|
};
|
|
2427
2592
|
if (schema.displayName !== void 0) {
|
|
2428
2593
|
result.displayName = schema.displayName;
|
|
@@ -2464,9 +2629,17 @@ function getPropertyMetadata(name, property, registry) {
|
|
|
2464
2629
|
function getAssociationMetadata(name, association) {
|
|
2465
2630
|
const result = {
|
|
2466
2631
|
name,
|
|
2467
|
-
relation: association.relation
|
|
2468
|
-
target: association.target
|
|
2632
|
+
relation: association.relation
|
|
2469
2633
|
};
|
|
2634
|
+
if (association.target !== void 0) {
|
|
2635
|
+
result.target = association.target;
|
|
2636
|
+
}
|
|
2637
|
+
if (association.targets !== void 0) {
|
|
2638
|
+
result.targets = association.targets;
|
|
2639
|
+
}
|
|
2640
|
+
if (association.morphName !== void 0) {
|
|
2641
|
+
result.morphName = association.morphName;
|
|
2642
|
+
}
|
|
2470
2643
|
if (association.inversedBy !== void 0) {
|
|
2471
2644
|
result.inversedBy = association.inversedBy;
|
|
2472
2645
|
}
|
|
@@ -2571,7 +2744,13 @@ function getRelationshipGraph(schemas) {
|
|
|
2571
2744
|
const targets = [];
|
|
2572
2745
|
const associations = getSchemaAssociations(schema);
|
|
2573
2746
|
for (const association of associations) {
|
|
2574
|
-
if (
|
|
2747
|
+
if (association.targets) {
|
|
2748
|
+
for (const target of association.targets) {
|
|
2749
|
+
if (!targets.includes(target)) {
|
|
2750
|
+
targets.push(target);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
} else if (association.target && !targets.includes(association.target)) {
|
|
2575
2754
|
targets.push(association.target);
|
|
2576
2755
|
}
|
|
2577
2756
|
}
|
|
@@ -2616,7 +2795,7 @@ function getTopologicalOrder(schemas) {
|
|
|
2616
2795
|
for (const schemaName of graph.keys()) {
|
|
2617
2796
|
visit(schemaName);
|
|
2618
2797
|
}
|
|
2619
|
-
return result
|
|
2798
|
+
return result;
|
|
2620
2799
|
}
|
|
2621
2800
|
|
|
2622
2801
|
// src/api/omnify.ts
|