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

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 (55) hide show
  1. package/README.md +74 -128
  2. package/dist/__tests__/class-schema.test.d.ts +2 -0
  3. package/dist/__tests__/class-schema.test.d.ts.map +1 -0
  4. package/dist/__tests__/date-extension.integration.test.d.ts +2 -0
  5. package/dist/__tests__/date-extension.integration.test.d.ts.map +1 -0
  6. package/dist/__tests__/fixtures/class-schema-regressions.d.ts +83 -0
  7. package/dist/__tests__/fixtures/class-schema-regressions.d.ts.map +1 -0
  8. package/dist/__tests__/fixtures/example-date-extension.d.ts +12 -0
  9. package/dist/__tests__/fixtures/example-date-extension.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/extension-forms.d.ts +7 -0
  11. package/dist/__tests__/fixtures/extension-forms.d.ts.map +1 -0
  12. package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
  13. package/dist/__tests__/fixtures/named-primitive-aliases.d.ts +15 -0
  14. package/dist/__tests__/fixtures/named-primitive-aliases.d.ts.map +1 -0
  15. package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts +14 -0
  16. package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts.map +1 -0
  17. package/dist/__tests__/fixtures/sample-forms.d.ts +10 -0
  18. package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -1
  19. package/dist/__tests__/generate-schemas.test.d.ts +2 -0
  20. package/dist/__tests__/generate-schemas.test.d.ts.map +1 -0
  21. package/dist/__tests__/parity/parity.test.d.ts +6 -2
  22. package/dist/__tests__/parity/parity.test.d.ts.map +1 -1
  23. package/dist/__tests__/parity/utils.d.ts +9 -4
  24. package/dist/__tests__/parity/utils.d.ts.map +1 -1
  25. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  26. package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
  27. package/dist/analyzer/program.d.ts +15 -0
  28. package/dist/analyzer/program.d.ts.map +1 -1
  29. package/dist/analyzer/tsdoc-parser.d.ts +5 -0
  30. package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
  31. package/dist/browser.cjs +73 -10
  32. package/dist/browser.cjs.map +1 -1
  33. package/dist/browser.js +73 -10
  34. package/dist/browser.js.map +1 -1
  35. package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
  36. package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
  37. package/dist/cli.cjs +1147 -252
  38. package/dist/cli.cjs.map +1 -1
  39. package/dist/cli.js +1142 -248
  40. package/dist/cli.js.map +1 -1
  41. package/dist/extensions/registry.d.ts.map +1 -1
  42. package/dist/generators/class-schema.d.ts.map +1 -1
  43. package/dist/generators/method-schema.d.ts.map +1 -1
  44. package/dist/generators/mixed-authoring.d.ts.map +1 -1
  45. package/dist/index.cjs +1121 -239
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.js +1121 -239
  48. package/dist/index.js.map +1 -1
  49. package/dist/internals.cjs +377 -195
  50. package/dist/internals.cjs.map +1 -1
  51. package/dist/internals.js +377 -195
  52. package/dist/internals.js.map +1 -1
  53. package/dist/json-schema/ir-generator.d.ts.map +1 -1
  54. package/dist/validate/constraint-validator.d.ts.map +1 -1
  55. package/package.json +3 -3
package/dist/browser.js CHANGED
@@ -20,6 +20,7 @@ function canonicalizeChainDSL(form) {
20
20
  kind: "form-ir",
21
21
  irVersion: IR_VERSION,
22
22
  elements: canonicalizeElements(form.elements),
23
+ rootAnnotations: [],
23
24
  typeRegistry: {},
24
25
  provenance: CHAIN_DSL_PROVENANCE
25
26
  };
@@ -330,6 +331,9 @@ function generateJsonSchemaFromIR(ir, options) {
330
331
  const ctx = makeContext(options);
331
332
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
332
333
  ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
334
+ if (typeDef.constraints && typeDef.constraints.length > 0) {
335
+ applyConstraints(ctx.defs[name], typeDef.constraints, ctx);
336
+ }
333
337
  if (typeDef.annotations && typeDef.annotations.length > 0) {
334
338
  applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
335
339
  }
@@ -498,7 +502,9 @@ function generateTypeNode(type, ctx) {
498
502
  }
499
503
  }
500
504
  function generatePrimitiveType(type) {
501
- return { type: type.primitiveKind };
505
+ return {
506
+ type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
507
+ };
502
508
  }
503
509
  function generateEnumType(type) {
504
510
  const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
@@ -671,7 +677,7 @@ function applyAnnotations(schema, annotations, ctx) {
671
677
  case "deprecated":
672
678
  schema.deprecated = true;
673
679
  if (annotation.message !== void 0 && annotation.message !== "") {
674
- schema["x-formspec-deprecation-description"] = annotation.message;
680
+ schema[`${ctx.vendorPrefix}-deprecation-description`] = annotation.message;
675
681
  }
676
682
  break;
677
683
  case "placeholder":
@@ -1601,6 +1607,26 @@ function dereferenceType(ctx, type) {
1601
1607
  }
1602
1608
  return current;
1603
1609
  }
1610
+ function collectReferencedTypeConstraints(ctx, type) {
1611
+ const collected = [];
1612
+ let current = type;
1613
+ const seen = /* @__PURE__ */ new Set();
1614
+ while (current.kind === "reference") {
1615
+ if (seen.has(current.name)) {
1616
+ break;
1617
+ }
1618
+ seen.add(current.name);
1619
+ const definition = ctx.typeRegistry[current.name];
1620
+ if (definition === void 0) {
1621
+ break;
1622
+ }
1623
+ if (definition.constraints !== void 0) {
1624
+ collected.push(...definition.constraints);
1625
+ }
1626
+ current = definition.type;
1627
+ }
1628
+ return collected;
1629
+ }
1604
1630
  function resolvePathTargetType(ctx, type, segments) {
1605
1631
  const effectiveType = dereferenceType(ctx, type);
1606
1632
  if (segments.length === 0) {
@@ -1622,12 +1648,33 @@ function resolvePathTargetType(ctx, type, segments) {
1622
1648
  }
1623
1649
  return { kind: "unresolvable", type: effectiveType };
1624
1650
  }
1651
+ function isNullType(type) {
1652
+ return type.kind === "primitive" && type.primitiveKind === "null";
1653
+ }
1654
+ function collectCustomConstraintCandidateTypes(ctx, type) {
1655
+ const effectiveType = dereferenceType(ctx, type);
1656
+ const candidates = [effectiveType];
1657
+ if (effectiveType.kind === "array") {
1658
+ candidates.push(...collectCustomConstraintCandidateTypes(ctx, effectiveType.items));
1659
+ }
1660
+ if (effectiveType.kind === "union") {
1661
+ const memberTypes = effectiveType.members.map((member) => dereferenceType(ctx, member));
1662
+ const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
1663
+ if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
1664
+ const [nullableMember] = nonNullMembers;
1665
+ if (nullableMember !== void 0) {
1666
+ candidates.push(...collectCustomConstraintCandidateTypes(ctx, nullableMember));
1667
+ }
1668
+ }
1669
+ }
1670
+ return candidates;
1671
+ }
1625
1672
  function formatPathTargetFieldName(fieldName, path) {
1626
1673
  return path.length === 0 ? fieldName : `${fieldName}.${path.join(".")}`;
1627
1674
  }
1628
1675
  function checkConstraintOnType(ctx, fieldName, type, constraint) {
1629
1676
  const effectiveType = dereferenceType(ctx, type);
1630
- const isNumber = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "number";
1677
+ const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
1631
1678
  const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
1632
1679
  const isArray = effectiveType.kind === "array";
1633
1680
  const isEnum = effectiveType.kind === "enum";
@@ -1685,7 +1732,9 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
1685
1732
  break;
1686
1733
  }
1687
1734
  case "const": {
1688
- const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "boolean", "null"].includes(effectiveType.primitiveKind) || effectiveType.kind === "enum";
1735
+ const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
1736
+ effectiveType.primitiveKind
1737
+ ) || effectiveType.kind === "enum";
1689
1738
  if (!isPrimitiveConstType) {
1690
1739
  addTypeMismatch(
1691
1740
  ctx,
@@ -1696,7 +1745,8 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
1696
1745
  }
1697
1746
  if (effectiveType.kind === "primitive") {
1698
1747
  const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
1699
- if (valueType !== effectiveType.primitiveKind) {
1748
+ const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
1749
+ if (valueType !== expectedValueType) {
1700
1750
  addTypeMismatch(
1701
1751
  ctx,
1702
1752
  `Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
@@ -1765,11 +1815,14 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
1765
1815
  );
1766
1816
  return;
1767
1817
  }
1818
+ const candidateTypes = collectCustomConstraintCandidateTypes(ctx, type);
1768
1819
  const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : normalizeConstraintTagName(constraint.provenance.tagName.replace(/^@/, ""));
1769
1820
  if (normalizedTagName !== void 0) {
1770
1821
  const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
1771
1822
  const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
1772
- if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && tagRegistration.registration.isApplicableToType?.(type) === false) {
1823
+ if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
1824
+ (candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
1825
+ )) {
1773
1826
  addTypeMismatch(
1774
1827
  ctx,
1775
1828
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -1779,7 +1832,7 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
1779
1832
  }
1780
1833
  }
1781
1834
  if (registration.applicableTypes === null) {
1782
- if (registration.isApplicableToType?.(type) === false) {
1835
+ if (!candidateTypes.some((candidateType) => registration.isApplicableToType?.(candidateType) !== false)) {
1783
1836
  addTypeMismatch(
1784
1837
  ctx,
1785
1838
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -1788,7 +1841,11 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
1788
1841
  }
1789
1842
  return;
1790
1843
  }
1791
- if (!registration.applicableTypes.includes(type.kind) || registration.isApplicableToType?.(type) === false) {
1844
+ const applicableTypes = registration.applicableTypes;
1845
+ const matchesApplicableType = candidateTypes.some(
1846
+ (candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
1847
+ );
1848
+ if (!matchesApplicableType) {
1792
1849
  addTypeMismatch(
1793
1850
  ctx,
1794
1851
  `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
@@ -1797,7 +1854,10 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
1797
1854
  }
1798
1855
  }
1799
1856
  function validateFieldNode(ctx, field) {
1800
- validateConstraints(ctx, field.name, field.type, field.constraints);
1857
+ validateConstraints(ctx, field.name, field.type, [
1858
+ ...collectReferencedTypeConstraints(ctx, field.type),
1859
+ ...field.constraints
1860
+ ]);
1801
1861
  if (field.type.kind === "object") {
1802
1862
  for (const prop of field.type.properties) {
1803
1863
  validateObjectProperty(ctx, field.name, prop);
@@ -1806,7 +1866,10 @@ function validateFieldNode(ctx, field) {
1806
1866
  }
1807
1867
  function validateObjectProperty(ctx, parentName, prop) {
1808
1868
  const qualifiedName = `${parentName}.${prop.name}`;
1809
- validateConstraints(ctx, qualifiedName, prop.type, prop.constraints);
1869
+ validateConstraints(ctx, qualifiedName, prop.type, [
1870
+ ...collectReferencedTypeConstraints(ctx, prop.type),
1871
+ ...prop.constraints
1872
+ ]);
1810
1873
  if (prop.type.kind === "object") {
1811
1874
  for (const nestedProp of prop.type.properties) {
1812
1875
  validateObjectProperty(ctx, qualifiedName, nestedProp);