@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.
- package/README.md +74 -128
- package/dist/__tests__/class-schema.test.d.ts +2 -0
- package/dist/__tests__/class-schema.test.d.ts.map +1 -0
- package/dist/__tests__/date-extension.integration.test.d.ts +2 -0
- package/dist/__tests__/date-extension.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/class-schema-regressions.d.ts +83 -0
- package/dist/__tests__/fixtures/class-schema-regressions.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-date-extension.d.ts +12 -0
- package/dist/__tests__/fixtures/example-date-extension.d.ts.map +1 -0
- package/dist/__tests__/fixtures/extension-forms.d.ts +7 -0
- package/dist/__tests__/fixtures/extension-forms.d.ts.map +1 -0
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
- package/dist/__tests__/fixtures/named-primitive-aliases.d.ts +15 -0
- package/dist/__tests__/fixtures/named-primitive-aliases.d.ts.map +1 -0
- package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts +14 -0
- package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts.map +1 -0
- package/dist/__tests__/fixtures/sample-forms.d.ts +10 -0
- package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -1
- package/dist/__tests__/generate-schemas.test.d.ts +2 -0
- package/dist/__tests__/generate-schemas.test.d.ts.map +1 -0
- package/dist/__tests__/parity/parity.test.d.ts +6 -2
- package/dist/__tests__/parity/parity.test.d.ts.map +1 -1
- package/dist/__tests__/parity/utils.d.ts +9 -4
- package/dist/__tests__/parity/utils.d.ts.map +1 -1
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/program.d.ts +15 -0
- package/dist/analyzer/program.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +5 -0
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +73 -10
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +73 -10
- package/dist/browser.js.map +1 -1
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +1147 -252
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1142 -248
- package/dist/cli.js.map +1 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/generators/method-schema.d.ts.map +1 -1
- package/dist/generators/mixed-authoring.d.ts.map +1 -1
- package/dist/index.cjs +1121 -239
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1121 -239
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +377 -195
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +377 -195
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/validate/constraint-validator.d.ts.map +1 -1
- 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 {
|
|
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[
|
|
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" &&
|
|
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(
|
|
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
|
-
|
|
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 &&
|
|
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?.(
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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);
|