@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 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.primaryKeyType !== void 0) {
645
- options.primaryKeyType = data.primaryKeyType;
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", "target"],
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 ManyToMany"
1413
+ "Add relation: OneToOne, OneToMany, ManyToOne, ManyToMany, MorphTo, MorphOne, MorphMany, MorphToMany, or MorphedByMany"
1394
1414
  )
1395
1415
  );
1396
- } else if (typeof relation !== "string") {
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
- } else if (!VALID_RELATIONS.includes(relation)) {
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
- if (target === void 0) {
1413
- errors.push(
1414
- validationError(
1415
- `Property '${propertyName}' of type 'Association' requires 'target' field`,
1416
- buildLocation5(filePath),
1417
- "Add target schema name: target: User"
1418
- )
1419
- );
1420
- } else if (typeof target !== "string") {
1421
- errors.push(
1422
- validationError(
1423
- `Property '${propertyName}' target field must be a string`,
1424
- buildLocation5(filePath)
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 VALID_PRIMARY_KEY_TYPES = ["Int", "BigInt", "Uuid", "String"];
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
- if (assocProp.target && !availableSchemas.includes(assocProp.target)) {
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.primaryKeyType !== void 0) {
1674
- if (!VALID_PRIMARY_KEY_TYPES.includes(options.primaryKeyType)) {
1838
+ if (options.idType !== void 0) {
1839
+ if (!VALID_ID_TYPES.includes(options.idType)) {
1675
1840
  errors.push(
1676
1841
  validationError(
1677
- `Invalid primaryKeyType '${options.primaryKeyType}'`,
1842
+ `Invalid idType '${options.idType}'`,
1678
1843
  buildLocation6(filePath),
1679
- `Use one of: ${VALID_PRIMARY_KEY_TYPES.join(", ")}`
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
- primaryKeyType: schema.options?.primaryKeyType ?? "Int"
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 (!targets.includes(association.target)) {
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.reverse();
2798
+ return result;
2620
2799
  }
2621
2800
 
2622
2801
  // src/api/omnify.ts