@formspec/build 0.1.0-alpha.19 → 0.1.0-alpha.20

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.
Files changed (35) hide show
  1. package/dist/__tests__/fixtures/class-schema-regressions.d.ts +4 -0
  2. package/dist/__tests__/fixtures/class-schema-regressions.d.ts.map +1 -1
  3. package/dist/__tests__/parity/utils.d.ts.map +1 -1
  4. package/dist/analyzer/class-analyzer.d.ts +4 -1
  5. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  6. package/dist/analyzer/jsdoc-constraints.d.ts +2 -1
  7. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
  8. package/dist/analyzer/tsdoc-parser.d.ts +13 -3
  9. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  10. package/dist/browser.cjs +30 -748
  11. package/dist/browser.cjs.map +1 -1
  12. package/dist/browser.js +32 -748
  13. package/dist/browser.js.map +1 -1
  14. package/dist/build.d.ts +14 -14
  15. package/dist/cli.cjs +691 -1101
  16. package/dist/cli.cjs.map +1 -1
  17. package/dist/cli.js +704 -1100
  18. package/dist/cli.js.map +1 -1
  19. package/dist/generators/class-schema.d.ts.map +1 -1
  20. package/dist/index.cjs +689 -1095
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.js +703 -1095
  23. package/dist/index.js.map +1 -1
  24. package/dist/internals.cjs +663 -1069
  25. package/dist/internals.cjs.map +1 -1
  26. package/dist/internals.js +675 -1067
  27. package/dist/internals.js.map +1 -1
  28. package/dist/ui-schema/schema.d.ts +14 -14
  29. package/dist/validate/constraint-validator.d.ts +6 -45
  30. package/dist/validate/constraint-validator.d.ts.map +1 -1
  31. package/package.json +2 -1
  32. package/dist/__tests__/json-utils.test.d.ts +0 -5
  33. package/dist/__tests__/json-utils.test.d.ts.map +0 -1
  34. package/dist/analyzer/json-utils.d.ts +0 -22
  35. package/dist/analyzer/json-utils.d.ts.map +0 -1
package/dist/browser.cjs CHANGED
@@ -1181,760 +1181,42 @@ var jsonSchema7Schema = import_zod3.z.lazy(
1181
1181
  );
1182
1182
 
1183
1183
  // src/validate/constraint-validator.ts
1184
- var import_core3 = require("@formspec/core");
1185
- function addContradiction(ctx, message, primary, related) {
1186
- ctx.diagnostics.push({
1187
- code: "CONTRADICTING_CONSTRAINTS",
1188
- message,
1189
- severity: "error",
1190
- primaryLocation: primary,
1191
- relatedLocations: [related]
1192
- });
1193
- }
1194
- function addTypeMismatch(ctx, message, primary) {
1195
- ctx.diagnostics.push({
1196
- code: "TYPE_MISMATCH",
1197
- message,
1198
- severity: "error",
1199
- primaryLocation: primary,
1200
- relatedLocations: []
1201
- });
1202
- }
1203
- function addUnknownExtension(ctx, message, primary) {
1204
- ctx.diagnostics.push({
1205
- code: "UNKNOWN_EXTENSION",
1206
- message,
1207
- severity: "warning",
1208
- primaryLocation: primary,
1209
- relatedLocations: []
1210
- });
1211
- }
1212
- function addUnknownPathTarget(ctx, message, primary) {
1213
- ctx.diagnostics.push({
1214
- code: "UNKNOWN_PATH_TARGET",
1215
- message,
1216
- severity: "error",
1217
- primaryLocation: primary,
1218
- relatedLocations: []
1219
- });
1220
- }
1221
- function addConstraintBroadening(ctx, message, primary, related) {
1222
- ctx.diagnostics.push({
1223
- code: "CONSTRAINT_BROADENING",
1224
- message,
1225
- severity: "error",
1226
- primaryLocation: primary,
1227
- relatedLocations: [related]
1228
- });
1229
- }
1230
- function getExtensionIdFromConstraintId(constraintId) {
1231
- const separator = constraintId.lastIndexOf("/");
1232
- if (separator <= 0) {
1233
- return null;
1234
- }
1235
- return constraintId.slice(0, separator);
1236
- }
1237
- function findNumeric(constraints, constraintKind) {
1238
- return constraints.find((c) => c.constraintKind === constraintKind);
1239
- }
1240
- function findLength(constraints, constraintKind) {
1241
- return constraints.find((c) => c.constraintKind === constraintKind);
1242
- }
1243
- function findAllowedMembers(constraints) {
1244
- return constraints.filter(
1245
- (c) => c.constraintKind === "allowedMembers"
1246
- );
1247
- }
1248
- function findConstConstraints(constraints) {
1249
- return constraints.filter(
1250
- (c) => c.constraintKind === "const"
1251
- );
1252
- }
1253
- function jsonValueEquals(left, right) {
1254
- if (left === right) {
1255
- return true;
1256
- }
1257
- if (Array.isArray(left) || Array.isArray(right)) {
1258
- if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
1259
- return false;
1260
- }
1261
- return left.every((item, index) => jsonValueEquals(item, right[index]));
1262
- }
1263
- if (isJsonObject(left) || isJsonObject(right)) {
1264
- if (!isJsonObject(left) || !isJsonObject(right)) {
1265
- return false;
1266
- }
1267
- const leftKeys = Object.keys(left).sort();
1268
- const rightKeys = Object.keys(right).sort();
1269
- if (leftKeys.length !== rightKeys.length) {
1270
- return false;
1271
- }
1272
- return leftKeys.every((key, index) => {
1273
- const rightKey = rightKeys[index];
1274
- if (rightKey !== key) {
1275
- return false;
1276
- }
1277
- const leftValue = left[key];
1278
- const rightValue = right[rightKey];
1279
- return leftValue !== void 0 && rightValue !== void 0 && jsonValueEquals(leftValue, rightValue);
1280
- });
1281
- }
1282
- return false;
1283
- }
1284
- function isJsonObject(value) {
1285
- return typeof value === "object" && value !== null && !Array.isArray(value);
1286
- }
1287
- function isOrderedBoundConstraint(constraint) {
1288
- return constraint.constraintKind === "minimum" || constraint.constraintKind === "exclusiveMinimum" || constraint.constraintKind === "minLength" || constraint.constraintKind === "minItems" || constraint.constraintKind === "maximum" || constraint.constraintKind === "exclusiveMaximum" || constraint.constraintKind === "maxLength" || constraint.constraintKind === "maxItems";
1289
- }
1290
- function pathKey(constraint) {
1291
- return constraint.path?.segments.join(".") ?? "";
1292
- }
1293
- function orderedBoundFamily(kind) {
1294
- switch (kind) {
1295
- case "minimum":
1296
- case "exclusiveMinimum":
1297
- return "numeric-lower";
1298
- case "maximum":
1299
- case "exclusiveMaximum":
1300
- return "numeric-upper";
1301
- case "minLength":
1302
- return "minLength";
1303
- case "minItems":
1304
- return "minItems";
1305
- case "maxLength":
1306
- return "maxLength";
1307
- case "maxItems":
1308
- return "maxItems";
1309
- default: {
1310
- const _exhaustive = kind;
1311
- return _exhaustive;
1312
- }
1313
- }
1314
- }
1315
- function isNumericLowerKind(kind) {
1316
- return kind === "minimum" || kind === "exclusiveMinimum";
1317
- }
1318
- function isNumericUpperKind(kind) {
1319
- return kind === "maximum" || kind === "exclusiveMaximum";
1320
- }
1321
- function describeConstraintTag(constraint) {
1322
- return `@${constraint.constraintKind}`;
1323
- }
1324
- function compareConstraintStrength(current, previous) {
1325
- const family = orderedBoundFamily(current.constraintKind);
1326
- if (family === "numeric-lower") {
1327
- if (!isNumericLowerKind(current.constraintKind) || !isNumericLowerKind(previous.constraintKind)) {
1328
- throw new Error("numeric-lower family received non-numeric lower-bound constraint");
1329
- }
1330
- if (current.value !== previous.value) {
1331
- return current.value > previous.value ? 1 : -1;
1332
- }
1333
- if (current.constraintKind === "exclusiveMinimum" && previous.constraintKind === "minimum") {
1334
- return 1;
1335
- }
1336
- if (current.constraintKind === "minimum" && previous.constraintKind === "exclusiveMinimum") {
1337
- return -1;
1338
- }
1339
- return 0;
1340
- }
1341
- if (family === "numeric-upper") {
1342
- if (!isNumericUpperKind(current.constraintKind) || !isNumericUpperKind(previous.constraintKind)) {
1343
- throw new Error("numeric-upper family received non-numeric upper-bound constraint");
1344
- }
1345
- if (current.value !== previous.value) {
1346
- return current.value < previous.value ? 1 : -1;
1347
- }
1348
- if (current.constraintKind === "exclusiveMaximum" && previous.constraintKind === "maximum") {
1349
- return 1;
1350
- }
1351
- if (current.constraintKind === "maximum" && previous.constraintKind === "exclusiveMaximum") {
1352
- return -1;
1353
- }
1354
- return 0;
1355
- }
1356
- switch (family) {
1357
- case "minLength":
1358
- case "minItems":
1359
- if (current.value === previous.value) {
1360
- return 0;
1361
- }
1362
- return current.value > previous.value ? 1 : -1;
1363
- case "maxLength":
1364
- case "maxItems":
1365
- if (current.value === previous.value) {
1366
- return 0;
1367
- }
1368
- return current.value < previous.value ? 1 : -1;
1369
- default: {
1370
- const _exhaustive = family;
1371
- return _exhaustive;
1372
- }
1373
- }
1374
- }
1375
- function checkConstraintBroadening(ctx, fieldName, constraints) {
1376
- const strongestByKey = /* @__PURE__ */ new Map();
1377
- for (const constraint of constraints) {
1378
- if (!isOrderedBoundConstraint(constraint)) {
1379
- continue;
1380
- }
1381
- const key = `${orderedBoundFamily(constraint.constraintKind)}:${pathKey(constraint)}`;
1382
- const previous = strongestByKey.get(key);
1383
- if (previous === void 0) {
1384
- strongestByKey.set(key, constraint);
1385
- continue;
1386
- }
1387
- const strength = compareConstraintStrength(constraint, previous);
1388
- if (strength < 0) {
1389
- const displayFieldName = formatPathTargetFieldName(
1390
- fieldName,
1391
- constraint.path?.segments ?? []
1392
- );
1393
- addConstraintBroadening(
1394
- ctx,
1395
- `Field "${displayFieldName}": ${describeConstraintTag(constraint)} (${String(constraint.value)}) is broader than earlier ${describeConstraintTag(previous)} (${String(previous.value)}). Constraints can only narrow.`,
1396
- constraint.provenance,
1397
- previous.provenance
1398
- );
1399
- continue;
1400
- }
1401
- if (strength <= 0) {
1402
- continue;
1403
- }
1404
- strongestByKey.set(key, constraint);
1405
- }
1406
- }
1407
- function compareCustomConstraintStrength(current, previous) {
1408
- const order = current.comparePayloads(current.constraint.payload, previous.constraint.payload);
1409
- const equalPayloadTiebreaker = order === 0 ? compareSemanticInclusivity(current.role.inclusive, previous.role.inclusive) : order;
1410
- switch (current.role.bound) {
1411
- case "lower":
1412
- return equalPayloadTiebreaker;
1413
- case "upper":
1414
- return equalPayloadTiebreaker === 0 ? 0 : -equalPayloadTiebreaker;
1415
- case "exact":
1416
- return order === 0 ? 0 : Number.NaN;
1417
- default: {
1418
- const _exhaustive = current.role.bound;
1419
- return _exhaustive;
1420
- }
1421
- }
1422
- }
1423
- function compareSemanticInclusivity(currentInclusive, previousInclusive) {
1424
- if (currentInclusive === previousInclusive) {
1425
- return 0;
1426
- }
1427
- return currentInclusive ? -1 : 1;
1428
- }
1429
- function customConstraintsContradict(lower, upper) {
1430
- const order = lower.comparePayloads(lower.constraint.payload, upper.constraint.payload);
1431
- if (order > 0) {
1432
- return true;
1433
- }
1434
- if (order < 0) {
1435
- return false;
1436
- }
1437
- return !lower.role.inclusive || !upper.role.inclusive;
1438
- }
1439
- function describeCustomConstraintTag(constraint) {
1440
- return constraint.provenance.tagName ?? constraint.constraintId;
1441
- }
1442
- function checkCustomConstraintSemantics(ctx, fieldName, constraints) {
1443
- if (ctx.extensionRegistry === void 0) {
1444
- return;
1445
- }
1446
- const strongestByKey = /* @__PURE__ */ new Map();
1447
- const lowerByFamily = /* @__PURE__ */ new Map();
1448
- const upperByFamily = /* @__PURE__ */ new Map();
1449
- for (const constraint of constraints) {
1450
- if (constraint.constraintKind !== "custom") {
1451
- continue;
1452
- }
1453
- const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
1454
- if (registration?.comparePayloads === void 0 || registration.semanticRole === void 0) {
1455
- continue;
1456
- }
1457
- const entry = {
1458
- constraint,
1459
- comparePayloads: registration.comparePayloads,
1460
- role: registration.semanticRole
1461
- };
1462
- const familyKey = `${registration.semanticRole.family}:${pathKey(constraint)}`;
1463
- const boundKey = `${familyKey}:${registration.semanticRole.bound}`;
1464
- const previous = strongestByKey.get(boundKey);
1465
- if (previous !== void 0) {
1466
- const strength = compareCustomConstraintStrength(entry, previous);
1467
- if (Number.isNaN(strength)) {
1468
- addContradiction(
1469
- ctx,
1470
- `Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} conflicts with ${describeCustomConstraintTag(previous.constraint)}`,
1471
- constraint.provenance,
1472
- previous.constraint.provenance
1473
- );
1474
- continue;
1475
- }
1476
- if (strength < 0) {
1477
- addConstraintBroadening(
1478
- ctx,
1479
- `Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} is broader than earlier ${describeCustomConstraintTag(previous.constraint)}. Constraints can only narrow.`,
1480
- constraint.provenance,
1481
- previous.constraint.provenance
1482
- );
1483
- continue;
1484
- }
1485
- if (strength > 0) {
1486
- strongestByKey.set(boundKey, entry);
1487
- }
1488
- } else {
1489
- strongestByKey.set(boundKey, entry);
1490
- }
1491
- if (registration.semanticRole.bound === "lower") {
1492
- lowerByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
1493
- } else if (registration.semanticRole.bound === "upper") {
1494
- upperByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
1495
- }
1496
- }
1497
- for (const [familyKey, lower] of lowerByFamily) {
1498
- const upper = upperByFamily.get(familyKey);
1499
- if (upper === void 0) {
1500
- continue;
1501
- }
1502
- if (!customConstraintsContradict(lower, upper)) {
1503
- continue;
1504
- }
1505
- addContradiction(
1506
- ctx,
1507
- `Field "${formatPathTargetFieldName(fieldName, lower.constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(lower.constraint)} contradicts ${describeCustomConstraintTag(upper.constraint)}`,
1508
- lower.constraint.provenance,
1509
- upper.constraint.provenance
1510
- );
1511
- }
1512
- }
1513
- function checkNumericContradictions(ctx, fieldName, constraints) {
1514
- const min = findNumeric(constraints, "minimum");
1515
- const max = findNumeric(constraints, "maximum");
1516
- const exMin = findNumeric(constraints, "exclusiveMinimum");
1517
- const exMax = findNumeric(constraints, "exclusiveMaximum");
1518
- if (min !== void 0 && max !== void 0 && min.value > max.value) {
1519
- addContradiction(
1520
- ctx,
1521
- `Field "${fieldName}": minimum (${String(min.value)}) is greater than maximum (${String(max.value)})`,
1522
- min.provenance,
1523
- max.provenance
1524
- );
1525
- }
1526
- if (exMin !== void 0 && max !== void 0 && exMin.value >= max.value) {
1527
- addContradiction(
1528
- ctx,
1529
- `Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to maximum (${String(max.value)})`,
1530
- exMin.provenance,
1531
- max.provenance
1532
- );
1533
- }
1534
- if (min !== void 0 && exMax !== void 0 && min.value >= exMax.value) {
1535
- addContradiction(
1536
- ctx,
1537
- `Field "${fieldName}": minimum (${String(min.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
1538
- min.provenance,
1539
- exMax.provenance
1540
- );
1541
- }
1542
- if (exMin !== void 0 && exMax !== void 0 && exMin.value >= exMax.value) {
1543
- addContradiction(
1544
- ctx,
1545
- `Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
1546
- exMin.provenance,
1547
- exMax.provenance
1548
- );
1549
- }
1550
- }
1551
- function checkLengthContradictions(ctx, fieldName, constraints) {
1552
- const minLen = findLength(constraints, "minLength");
1553
- const maxLen = findLength(constraints, "maxLength");
1554
- if (minLen !== void 0 && maxLen !== void 0 && minLen.value > maxLen.value) {
1555
- addContradiction(
1556
- ctx,
1557
- `Field "${fieldName}": minLength (${String(minLen.value)}) is greater than maxLength (${String(maxLen.value)})`,
1558
- minLen.provenance,
1559
- maxLen.provenance
1560
- );
1561
- }
1562
- const minItems = findLength(constraints, "minItems");
1563
- const maxItems = findLength(constraints, "maxItems");
1564
- if (minItems !== void 0 && maxItems !== void 0 && minItems.value > maxItems.value) {
1565
- addContradiction(
1566
- ctx,
1567
- `Field "${fieldName}": minItems (${String(minItems.value)}) is greater than maxItems (${String(maxItems.value)})`,
1568
- minItems.provenance,
1569
- maxItems.provenance
1570
- );
1571
- }
1572
- }
1573
- function checkAllowedMembersContradiction(ctx, fieldName, constraints) {
1574
- const members = findAllowedMembers(constraints);
1575
- if (members.length < 2) return;
1576
- const firstSet = new Set(members[0]?.members ?? []);
1577
- for (let i = 1; i < members.length; i++) {
1578
- const current = members[i];
1579
- if (current === void 0) continue;
1580
- for (const m of firstSet) {
1581
- if (!current.members.includes(m)) {
1582
- firstSet.delete(m);
1583
- }
1584
- }
1585
- }
1586
- if (firstSet.size === 0) {
1587
- const first = members[0];
1588
- const second = members[1];
1589
- if (first !== void 0 && second !== void 0) {
1590
- addContradiction(
1591
- ctx,
1592
- `Field "${fieldName}": allowedMembers constraints have an empty intersection (no valid values remain)`,
1593
- first.provenance,
1594
- second.provenance
1595
- );
1596
- }
1597
- }
1598
- }
1599
- function checkConstContradictions(ctx, fieldName, constraints) {
1600
- const constConstraints = findConstConstraints(constraints);
1601
- if (constConstraints.length < 2) return;
1602
- const first = constConstraints[0];
1603
- if (first === void 0) return;
1604
- for (let i = 1; i < constConstraints.length; i++) {
1605
- const current = constConstraints[i];
1606
- if (current === void 0) continue;
1607
- if (jsonValueEquals(first.value, current.value)) {
1608
- continue;
1609
- }
1610
- addContradiction(
1611
- ctx,
1612
- `Field "${fieldName}": conflicting @const constraints require both ${JSON.stringify(first.value)} and ${JSON.stringify(current.value)}`,
1613
- first.provenance,
1614
- current.provenance
1615
- );
1616
- }
1617
- }
1618
- function typeLabel(type) {
1619
- switch (type.kind) {
1620
- case "primitive":
1621
- return type.primitiveKind;
1622
- case "enum":
1623
- return "enum";
1624
- case "array":
1625
- return "array";
1626
- case "object":
1627
- return "object";
1628
- case "record":
1629
- return "record";
1630
- case "union":
1631
- return "union";
1632
- case "reference":
1633
- return `reference(${type.name})`;
1634
- case "dynamic":
1635
- return `dynamic(${type.dynamicKind})`;
1636
- case "custom":
1637
- return `custom(${type.typeId})`;
1638
- default: {
1639
- const _exhaustive = type;
1640
- return String(_exhaustive);
1641
- }
1642
- }
1643
- }
1644
- function dereferenceType(ctx, type) {
1645
- let current = type;
1646
- const seen = /* @__PURE__ */ new Set();
1647
- while (current.kind === "reference") {
1648
- if (seen.has(current.name)) {
1649
- return current;
1650
- }
1651
- seen.add(current.name);
1652
- const definition = ctx.typeRegistry[current.name];
1653
- if (definition === void 0) {
1654
- return current;
1655
- }
1656
- current = definition.type;
1657
- }
1658
- return current;
1659
- }
1660
- function collectReferencedTypeConstraints(ctx, type) {
1661
- const collected = [];
1662
- let current = type;
1663
- const seen = /* @__PURE__ */ new Set();
1664
- while (current.kind === "reference") {
1665
- if (seen.has(current.name)) {
1666
- break;
1667
- }
1668
- seen.add(current.name);
1669
- const definition = ctx.typeRegistry[current.name];
1670
- if (definition === void 0) {
1671
- break;
1672
- }
1673
- if (definition.constraints !== void 0) {
1674
- collected.push(...definition.constraints);
1675
- }
1676
- current = definition.type;
1677
- }
1678
- return collected;
1679
- }
1680
- function resolvePathTargetType(ctx, type, segments) {
1681
- const effectiveType = dereferenceType(ctx, type);
1682
- if (segments.length === 0) {
1683
- return { kind: "resolved", type: effectiveType };
1684
- }
1685
- if (effectiveType.kind === "array") {
1686
- return resolvePathTargetType(ctx, effectiveType.items, segments);
1687
- }
1688
- if (effectiveType.kind === "object") {
1689
- const [segment, ...rest] = segments;
1690
- if (segment === void 0) {
1691
- throw new Error("Invariant violation: object path traversal requires a segment");
1692
- }
1693
- const property = effectiveType.properties.find((prop) => prop.name === segment);
1694
- if (property === void 0) {
1695
- return { kind: "missing-property", segment };
1696
- }
1697
- return resolvePathTargetType(ctx, property.type, rest);
1698
- }
1699
- return { kind: "unresolvable", type: effectiveType };
1700
- }
1701
- function isNullType(type) {
1702
- return type.kind === "primitive" && type.primitiveKind === "null";
1703
- }
1704
- function collectCustomConstraintCandidateTypes(ctx, type) {
1705
- const effectiveType = dereferenceType(ctx, type);
1706
- const candidates = [effectiveType];
1707
- if (effectiveType.kind === "array") {
1708
- candidates.push(...collectCustomConstraintCandidateTypes(ctx, effectiveType.items));
1709
- }
1710
- if (effectiveType.kind === "union") {
1711
- const memberTypes = effectiveType.members.map((member) => dereferenceType(ctx, member));
1712
- const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
1713
- if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
1714
- const [nullableMember] = nonNullMembers;
1715
- if (nullableMember !== void 0) {
1716
- candidates.push(...collectCustomConstraintCandidateTypes(ctx, nullableMember));
1717
- }
1718
- }
1719
- }
1720
- return candidates;
1721
- }
1722
- function formatPathTargetFieldName(fieldName, path) {
1723
- return path.length === 0 ? fieldName : `${fieldName}.${path.join(".")}`;
1724
- }
1725
- function checkConstraintOnType(ctx, fieldName, type, constraint) {
1726
- const effectiveType = dereferenceType(ctx, type);
1727
- const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
1728
- const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
1729
- const isArray = effectiveType.kind === "array";
1730
- const isEnum = effectiveType.kind === "enum";
1731
- const arrayItemType = effectiveType.kind === "array" ? dereferenceType(ctx, effectiveType.items) : void 0;
1732
- const isStringArray = arrayItemType?.kind === "primitive" && arrayItemType.primitiveKind === "string";
1733
- const label = typeLabel(effectiveType);
1734
- const ck = constraint.constraintKind;
1735
- switch (ck) {
1736
- case "minimum":
1737
- case "maximum":
1738
- case "exclusiveMinimum":
1739
- case "exclusiveMaximum":
1740
- case "multipleOf": {
1741
- if (!isNumber) {
1742
- addTypeMismatch(
1743
- ctx,
1744
- `Field "${fieldName}": constraint "${ck}" is only valid on number fields, but field type is "${label}"`,
1745
- constraint.provenance
1746
- );
1747
- }
1748
- break;
1749
- }
1750
- case "minLength":
1751
- case "maxLength":
1752
- case "pattern": {
1753
- if (!isString && !isStringArray) {
1754
- addTypeMismatch(
1755
- ctx,
1756
- `Field "${fieldName}": constraint "${ck}" is only valid on string fields or string array items, but field type is "${label}"`,
1757
- constraint.provenance
1758
- );
1759
- }
1760
- break;
1761
- }
1762
- case "minItems":
1763
- case "maxItems":
1764
- case "uniqueItems": {
1765
- if (!isArray) {
1766
- addTypeMismatch(
1767
- ctx,
1768
- `Field "${fieldName}": constraint "${ck}" is only valid on array fields, but field type is "${label}"`,
1769
- constraint.provenance
1770
- );
1771
- }
1772
- break;
1773
- }
1774
- case "allowedMembers": {
1775
- if (!isEnum) {
1776
- addTypeMismatch(
1777
- ctx,
1778
- `Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
1779
- constraint.provenance
1780
- );
1781
- }
1782
- break;
1783
- }
1784
- case "const": {
1785
- const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
1786
- effectiveType.primitiveKind
1787
- ) || effectiveType.kind === "enum";
1788
- if (!isPrimitiveConstType) {
1789
- addTypeMismatch(
1790
- ctx,
1791
- `Field "${fieldName}": constraint "const" is only valid on primitive or enum fields, but field type is "${label}"`,
1792
- constraint.provenance
1793
- );
1794
- break;
1795
- }
1796
- if (effectiveType.kind === "primitive") {
1797
- const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
1798
- const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
1799
- if (valueType !== expectedValueType) {
1800
- addTypeMismatch(
1801
- ctx,
1802
- `Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
1803
- constraint.provenance
1804
- );
1805
- }
1806
- break;
1807
- }
1808
- const memberValues = effectiveType.members.map((member) => member.value);
1809
- if (!memberValues.some((member) => jsonValueEquals(member, constraint.value))) {
1810
- addTypeMismatch(
1811
- ctx,
1812
- `Field "${fieldName}": @const value ${JSON.stringify(constraint.value)} is not one of the enum members`,
1813
- constraint.provenance
1814
- );
1815
- }
1816
- break;
1817
- }
1818
- case "custom": {
1819
- checkCustomConstraint(ctx, fieldName, effectiveType, constraint);
1820
- break;
1821
- }
1822
- default: {
1823
- const _exhaustive = constraint;
1824
- throw new Error(
1825
- `Unhandled constraint kind: ${_exhaustive.constraintKind}`
1826
- );
1827
- }
1828
- }
1829
- }
1830
- function checkTypeApplicability(ctx, fieldName, type, constraints) {
1831
- for (const constraint of constraints) {
1832
- if (constraint.path) {
1833
- const resolution = resolvePathTargetType(ctx, type, constraint.path.segments);
1834
- const targetFieldName = formatPathTargetFieldName(fieldName, constraint.path.segments);
1835
- if (resolution.kind === "missing-property") {
1836
- addUnknownPathTarget(
1837
- ctx,
1838
- `Field "${targetFieldName}": path-targeted constraint "${constraint.constraintKind}" references unknown path segment "${resolution.segment}"`,
1839
- constraint.provenance
1840
- );
1841
- continue;
1842
- }
1843
- if (resolution.kind === "unresolvable") {
1844
- addTypeMismatch(
1845
- ctx,
1846
- `Field "${targetFieldName}": path-targeted constraint "${constraint.constraintKind}" is invalid because type "${typeLabel(resolution.type)}" cannot be traversed`,
1847
- constraint.provenance
1848
- );
1849
- continue;
1850
- }
1851
- checkConstraintOnType(ctx, targetFieldName, resolution.type, constraint);
1852
- continue;
1853
- }
1854
- checkConstraintOnType(ctx, fieldName, type, constraint);
1855
- }
1856
- }
1857
- function checkCustomConstraint(ctx, fieldName, type, constraint) {
1858
- if (ctx.extensionRegistry === void 0) return;
1859
- const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
1860
- if (registration === void 0) {
1861
- addUnknownExtension(
1862
- ctx,
1863
- `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not registered in the extension registry`,
1864
- constraint.provenance
1865
- );
1866
- return;
1867
- }
1868
- const candidateTypes = collectCustomConstraintCandidateTypes(ctx, type);
1869
- const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : (0, import_core3.normalizeConstraintTagName)(constraint.provenance.tagName.replace(/^@/, ""));
1870
- if (normalizedTagName !== void 0) {
1871
- const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
1872
- const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
1873
- if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
1874
- (candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
1875
- )) {
1876
- addTypeMismatch(
1877
- ctx,
1878
- `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
1879
- constraint.provenance
1880
- );
1881
- return;
1882
- }
1883
- }
1884
- if (registration.applicableTypes === null) {
1885
- if (!candidateTypes.some((candidateType) => registration.isApplicableToType?.(candidateType) !== false)) {
1886
- addTypeMismatch(
1887
- ctx,
1888
- `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
1889
- constraint.provenance
1890
- );
1184
+ var import_analysis = require("@formspec/analysis");
1185
+ function validateFieldNode(ctx, field) {
1186
+ const analysis = (0, import_analysis.analyzeConstraintTargets)(
1187
+ field.name,
1188
+ field.type,
1189
+ field.constraints,
1190
+ ctx.typeRegistry,
1191
+ ctx.extensionRegistry === void 0 ? void 0 : {
1192
+ extensionRegistry: ctx.extensionRegistry
1891
1193
  }
1892
- return;
1893
- }
1894
- const applicableTypes = registration.applicableTypes;
1895
- const matchesApplicableType = candidateTypes.some(
1896
- (candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
1897
1194
  );
1898
- if (!matchesApplicableType) {
1899
- addTypeMismatch(
1900
- ctx,
1901
- `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
1902
- constraint.provenance
1903
- );
1904
- }
1905
- }
1906
- function validateFieldNode(ctx, field) {
1907
- validateConstraints(ctx, field.name, field.type, [
1908
- ...collectReferencedTypeConstraints(ctx, field.type),
1909
- ...field.constraints
1910
- ]);
1195
+ ctx.diagnostics.push(...analysis.diagnostics);
1911
1196
  if (field.type.kind === "object") {
1912
- for (const prop of field.type.properties) {
1913
- validateObjectProperty(ctx, field.name, prop);
1197
+ for (const property of field.type.properties) {
1198
+ validateObjectProperty(ctx, field.name, property);
1914
1199
  }
1915
1200
  }
1916
1201
  }
1917
- function validateObjectProperty(ctx, parentName, prop) {
1918
- const qualifiedName = `${parentName}.${prop.name}`;
1919
- validateConstraints(ctx, qualifiedName, prop.type, [
1920
- ...collectReferencedTypeConstraints(ctx, prop.type),
1921
- ...prop.constraints
1922
- ]);
1923
- if (prop.type.kind === "object") {
1924
- for (const nestedProp of prop.type.properties) {
1925
- validateObjectProperty(ctx, qualifiedName, nestedProp);
1202
+ function validateObjectProperty(ctx, parentName, property) {
1203
+ const qualifiedName = `${parentName}.${property.name}`;
1204
+ const analysis = (0, import_analysis.analyzeConstraintTargets)(
1205
+ qualifiedName,
1206
+ property.type,
1207
+ property.constraints,
1208
+ ctx.typeRegistry,
1209
+ ctx.extensionRegistry === void 0 ? void 0 : {
1210
+ extensionRegistry: ctx.extensionRegistry
1211
+ }
1212
+ );
1213
+ ctx.diagnostics.push(...analysis.diagnostics);
1214
+ if (property.type.kind === "object") {
1215
+ for (const nestedProperty of property.type.properties) {
1216
+ validateObjectProperty(ctx, qualifiedName, nestedProperty);
1926
1217
  }
1927
1218
  }
1928
1219
  }
1929
- function validateConstraints(ctx, name, type, constraints) {
1930
- checkNumericContradictions(ctx, name, constraints);
1931
- checkLengthContradictions(ctx, name, constraints);
1932
- checkAllowedMembersContradiction(ctx, name, constraints);
1933
- checkConstContradictions(ctx, name, constraints);
1934
- checkConstraintBroadening(ctx, name, constraints);
1935
- checkCustomConstraintSemantics(ctx, name, constraints);
1936
- checkTypeApplicability(ctx, name, type, constraints);
1937
- }
1938
1220
  function validateElement(ctx, element) {
1939
1221
  switch (element.kind) {
1940
1222
  case "field":
@@ -1951,8 +1233,8 @@ function validateElement(ctx, element) {
1951
1233
  }
1952
1234
  break;
1953
1235
  default: {
1954
- const _exhaustive = element;
1955
- throw new Error(`Unhandled element kind: ${_exhaustive.kind}`);
1236
+ const exhaustive = element;
1237
+ throw new Error(`Unhandled element kind: ${String(exhaustive)}`);
1956
1238
  }
1957
1239
  }
1958
1240
  }
@@ -1967,7 +1249,7 @@ function validateIR(ir, options) {
1967
1249
  }
1968
1250
  return {
1969
1251
  diagnostics: ctx.diagnostics,
1970
- valid: ctx.diagnostics.every((d) => d.severity !== "error")
1252
+ valid: ctx.diagnostics.every((diagnostic) => diagnostic.severity !== "error")
1971
1253
  };
1972
1254
  }
1973
1255