@formspec/build 0.1.0-alpha.14 → 0.1.0-alpha.16
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/__tests__/extension-runtime.integration.test.d.ts +2 -0
- package/dist/__tests__/extension-runtime.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts +22 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -1
- package/dist/__tests__/fixtures/example-a-builtins.d.ts +6 -6
- package/dist/__tests__/fixtures/example-interface-types.d.ts +26 -26
- package/dist/__tests__/fixtures/example-interface-types.d.ts.map +1 -1
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts +30 -0
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -0
- package/dist/__tests__/mixed-authoring.test.d.ts +2 -0
- package/dist/__tests__/mixed-authoring.test.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/plan-status/chain-dsl.d.ts +19 -0
- package/dist/__tests__/parity/fixtures/plan-status/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/plan-status/expected-ir.d.ts +6 -0
- package/dist/__tests__/parity/fixtures/plan-status/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/plan-status/tsdoc.d.ts +17 -0
- package/dist/__tests__/parity/fixtures/plan-status/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/usd-cents/chain-dsl.d.ts +9 -0
- package/dist/__tests__/parity/fixtures/usd-cents/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/usd-cents/expected-ir.d.ts +6 -0
- package/dist/__tests__/parity/fixtures/usd-cents/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/usd-cents/tsdoc.d.ts +19 -0
- package/dist/__tests__/parity/fixtures/usd-cents/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/utils.d.ts +11 -4
- package/dist/__tests__/parity/utils.d.ts.map +1 -1
- package/dist/analyzer/class-analyzer.d.ts +5 -3
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts +7 -51
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +25 -9
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +546 -102
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +15 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +544 -102
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +170 -6
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts +3 -3
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +877 -128
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +876 -131
- package/dist/cli.js.map +1 -1
- package/dist/generators/mixed-authoring.d.ts +45 -0
- package/dist/generators/mixed-authoring.d.ts.map +1 -0
- package/dist/index.cjs +850 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +847 -129
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +946 -187
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +944 -189
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/generator.d.ts +8 -2
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +27 -4
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/json-schema/types.d.ts +1 -1
- package/dist/json-schema/types.d.ts.map +1 -1
- package/dist/ui-schema/ir-generator.d.ts.map +1 -1
- package/dist/validate/constraint-validator.d.ts +3 -7
- package/dist/validate/constraint-validator.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/__tests__/jsdoc-constraints.test.d.ts +0 -10
- package/dist/__tests__/jsdoc-constraints.test.d.ts.map +0 -1
package/dist/internals.cjs
CHANGED
|
@@ -228,7 +228,7 @@ function canonicalizeArrayField(field) {
|
|
|
228
228
|
const itemsType = {
|
|
229
229
|
kind: "object",
|
|
230
230
|
properties: itemProperties,
|
|
231
|
-
additionalProperties:
|
|
231
|
+
additionalProperties: true
|
|
232
232
|
};
|
|
233
233
|
const type = { kind: "array", items: itemsType };
|
|
234
234
|
const constraints = [];
|
|
@@ -263,7 +263,7 @@ function canonicalizeObjectField(field) {
|
|
|
263
263
|
const type = {
|
|
264
264
|
kind: "object",
|
|
265
265
|
properties,
|
|
266
|
-
additionalProperties:
|
|
266
|
+
additionalProperties: true
|
|
267
267
|
};
|
|
268
268
|
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
269
269
|
}
|
|
@@ -376,6 +376,7 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
376
376
|
irVersion: import_core2.IR_VERSION,
|
|
377
377
|
elements,
|
|
378
378
|
typeRegistry: analysis.typeRegistry,
|
|
379
|
+
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
379
380
|
provenance
|
|
380
381
|
};
|
|
381
382
|
}
|
|
@@ -511,7 +512,6 @@ var ts4 = __toESM(require("typescript"), 1);
|
|
|
511
512
|
|
|
512
513
|
// src/analyzer/jsdoc-constraints.ts
|
|
513
514
|
var ts3 = __toESM(require("typescript"), 1);
|
|
514
|
-
var import_core4 = require("@formspec/core");
|
|
515
515
|
|
|
516
516
|
// src/analyzer/tsdoc-parser.ts
|
|
517
517
|
var ts2 = __toESM(require("typescript"), 1);
|
|
@@ -541,7 +541,7 @@ var LENGTH_CONSTRAINT_MAP = {
|
|
|
541
541
|
minItems: "minItems",
|
|
542
542
|
maxItems: "maxItems"
|
|
543
543
|
};
|
|
544
|
-
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions"]);
|
|
544
|
+
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
545
545
|
function createFormSpecTSDocConfig() {
|
|
546
546
|
const config = new import_tsdoc.TSDocConfiguration();
|
|
547
547
|
for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
@@ -553,6 +553,15 @@ function createFormSpecTSDocConfig() {
|
|
|
553
553
|
})
|
|
554
554
|
);
|
|
555
555
|
}
|
|
556
|
+
for (const tagName of ["displayName", "description", "format", "placeholder"]) {
|
|
557
|
+
config.addTagDefinition(
|
|
558
|
+
new import_tsdoc.TSDocTagDefinition({
|
|
559
|
+
tagName: "@" + tagName,
|
|
560
|
+
syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
|
|
561
|
+
allowMultiple: true
|
|
562
|
+
})
|
|
563
|
+
);
|
|
564
|
+
}
|
|
556
565
|
return config;
|
|
557
566
|
}
|
|
558
567
|
var sharedParser;
|
|
@@ -563,6 +572,12 @@ function getParser() {
|
|
|
563
572
|
function parseTSDocTags(node, file = "") {
|
|
564
573
|
const constraints = [];
|
|
565
574
|
const annotations = [];
|
|
575
|
+
let displayName;
|
|
576
|
+
let description;
|
|
577
|
+
let placeholder;
|
|
578
|
+
let displayNameProvenance;
|
|
579
|
+
let descriptionProvenance;
|
|
580
|
+
let placeholderProvenance;
|
|
566
581
|
const sourceFile = node.getSourceFile();
|
|
567
582
|
const sourceText = sourceFile.getFullText();
|
|
568
583
|
const commentRanges = ts2.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
@@ -582,9 +597,37 @@ function parseTSDocTags(node, file = "") {
|
|
|
582
597
|
const docComment = parserContext.docComment;
|
|
583
598
|
for (const block of docComment.customBlocks) {
|
|
584
599
|
const tagName = (0, import_core3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
|
|
600
|
+
if (tagName === "displayName" || tagName === "description" || tagName === "format" || tagName === "placeholder") {
|
|
601
|
+
const text2 = extractBlockText(block).trim();
|
|
602
|
+
if (text2 === "") continue;
|
|
603
|
+
const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
|
|
604
|
+
if (tagName === "displayName") {
|
|
605
|
+
if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
|
|
606
|
+
displayName = text2;
|
|
607
|
+
displayNameProvenance = provenance2;
|
|
608
|
+
}
|
|
609
|
+
} else if (tagName === "format") {
|
|
610
|
+
annotations.push({
|
|
611
|
+
kind: "annotation",
|
|
612
|
+
annotationKind: "format",
|
|
613
|
+
value: text2,
|
|
614
|
+
provenance: provenance2
|
|
615
|
+
});
|
|
616
|
+
} else {
|
|
617
|
+
if (tagName === "description" && description === void 0) {
|
|
618
|
+
description = text2;
|
|
619
|
+
descriptionProvenance = provenance2;
|
|
620
|
+
} else if (tagName === "placeholder" && placeholder === void 0) {
|
|
621
|
+
placeholder = text2;
|
|
622
|
+
placeholderProvenance = provenance2;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
585
627
|
if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
586
628
|
const text = extractBlockText(block).trim();
|
|
587
|
-
|
|
629
|
+
const expectedType = (0, import_core3.isBuiltinConstraintName)(tagName) ? import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
630
|
+
if (text === "" && expectedType !== "boolean") continue;
|
|
588
631
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
589
632
|
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
590
633
|
if (constraintNode) {
|
|
@@ -592,14 +635,47 @@ function parseTSDocTags(node, file = "") {
|
|
|
592
635
|
}
|
|
593
636
|
}
|
|
594
637
|
if (docComment.deprecatedBlock !== void 0) {
|
|
638
|
+
const message = extractBlockText(docComment.deprecatedBlock).trim();
|
|
595
639
|
annotations.push({
|
|
596
640
|
kind: "annotation",
|
|
597
641
|
annotationKind: "deprecated",
|
|
642
|
+
...message !== "" && { message },
|
|
598
643
|
provenance: provenanceForComment(range, sourceFile, file, "deprecated")
|
|
599
644
|
});
|
|
600
645
|
}
|
|
646
|
+
if (description === void 0 && docComment.remarksBlock !== void 0) {
|
|
647
|
+
const remarks = extractBlockText(docComment.remarksBlock).trim();
|
|
648
|
+
if (remarks !== "") {
|
|
649
|
+
description = remarks;
|
|
650
|
+
descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
|
|
651
|
+
}
|
|
652
|
+
}
|
|
601
653
|
}
|
|
602
654
|
}
|
|
655
|
+
if (displayName !== void 0 && displayNameProvenance !== void 0) {
|
|
656
|
+
annotations.push({
|
|
657
|
+
kind: "annotation",
|
|
658
|
+
annotationKind: "displayName",
|
|
659
|
+
value: displayName,
|
|
660
|
+
provenance: displayNameProvenance
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
if (description !== void 0 && descriptionProvenance !== void 0) {
|
|
664
|
+
annotations.push({
|
|
665
|
+
kind: "annotation",
|
|
666
|
+
annotationKind: "description",
|
|
667
|
+
value: description,
|
|
668
|
+
provenance: descriptionProvenance
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
if (placeholder !== void 0 && placeholderProvenance !== void 0) {
|
|
672
|
+
annotations.push({
|
|
673
|
+
kind: "annotation",
|
|
674
|
+
annotationKind: "placeholder",
|
|
675
|
+
value: placeholder,
|
|
676
|
+
provenance: placeholderProvenance
|
|
677
|
+
});
|
|
678
|
+
}
|
|
603
679
|
const jsDocTagsAll = ts2.getJSDocTags(node);
|
|
604
680
|
for (const tag of jsDocTagsAll) {
|
|
605
681
|
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
@@ -608,47 +684,39 @@ function parseTSDocTags(node, file = "") {
|
|
|
608
684
|
if (commentText === void 0 || commentText.trim() === "") continue;
|
|
609
685
|
const text = commentText.trim();
|
|
610
686
|
const provenance = provenanceForJSDocTag(tag, file);
|
|
687
|
+
if (tagName === "defaultValue") {
|
|
688
|
+
const defaultValueNode = parseDefaultValueValue(text, provenance);
|
|
689
|
+
annotations.push(defaultValueNode);
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
611
692
|
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
612
693
|
if (constraintNode) {
|
|
613
694
|
constraints.push(constraintNode);
|
|
614
695
|
}
|
|
615
696
|
}
|
|
697
|
+
return { constraints, annotations };
|
|
698
|
+
}
|
|
699
|
+
function extractDisplayNameMetadata(node) {
|
|
616
700
|
let displayName;
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const tagName = tag.tagName.text;
|
|
701
|
+
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
702
|
+
for (const tag of ts2.getJSDocTags(node)) {
|
|
703
|
+
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
704
|
+
if (tagName !== "displayName") continue;
|
|
622
705
|
const commentText = getTagCommentText(tag);
|
|
623
|
-
if (commentText === void 0
|
|
706
|
+
if (commentText === void 0) continue;
|
|
707
|
+
const text = commentText.trim();
|
|
708
|
+
if (text === "") continue;
|
|
709
|
+
const memberTarget = parseMemberTargetDisplayName(text);
|
|
710
|
+
if (memberTarget) {
|
|
711
|
+
memberDisplayNames.set(memberTarget.target, memberTarget.label);
|
|
624
712
|
continue;
|
|
625
713
|
}
|
|
626
|
-
|
|
627
|
-
if (tagName === "Field_displayName") {
|
|
628
|
-
displayName = trimmed;
|
|
629
|
-
displayNameTag = tag;
|
|
630
|
-
} else if (tagName === "Field_description") {
|
|
631
|
-
description = trimmed;
|
|
632
|
-
descriptionTag = tag;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
if (displayName !== void 0 && displayNameTag) {
|
|
636
|
-
annotations.push({
|
|
637
|
-
kind: "annotation",
|
|
638
|
-
annotationKind: "displayName",
|
|
639
|
-
value: displayName,
|
|
640
|
-
provenance: provenanceForJSDocTag(displayNameTag, file)
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
if (description !== void 0 && descriptionTag) {
|
|
644
|
-
annotations.push({
|
|
645
|
-
kind: "annotation",
|
|
646
|
-
annotationKind: "description",
|
|
647
|
-
value: description,
|
|
648
|
-
provenance: provenanceForJSDocTag(descriptionTag, file)
|
|
649
|
-
});
|
|
714
|
+
displayName ??= text;
|
|
650
715
|
}
|
|
651
|
-
return {
|
|
716
|
+
return {
|
|
717
|
+
...displayName !== void 0 && { displayName },
|
|
718
|
+
memberDisplayNames
|
|
719
|
+
};
|
|
652
720
|
}
|
|
653
721
|
function extractPathTarget(text) {
|
|
654
722
|
const trimmed = text.trimStart();
|
|
@@ -712,7 +780,45 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
712
780
|
}
|
|
713
781
|
return null;
|
|
714
782
|
}
|
|
783
|
+
if (expectedType === "boolean") {
|
|
784
|
+
const trimmed = effectiveText.trim();
|
|
785
|
+
if (trimmed !== "" && trimmed !== "true") {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
if (tagName === "uniqueItems") {
|
|
789
|
+
return {
|
|
790
|
+
kind: "constraint",
|
|
791
|
+
constraintKind: "uniqueItems",
|
|
792
|
+
value: true,
|
|
793
|
+
...path2 && { path: path2 },
|
|
794
|
+
provenance
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
715
799
|
if (expectedType === "json") {
|
|
800
|
+
if (tagName === "const") {
|
|
801
|
+
const trimmedText = effectiveText.trim();
|
|
802
|
+
if (trimmedText === "") return null;
|
|
803
|
+
try {
|
|
804
|
+
const parsed2 = JSON.parse(trimmedText);
|
|
805
|
+
return {
|
|
806
|
+
kind: "constraint",
|
|
807
|
+
constraintKind: "const",
|
|
808
|
+
value: parsed2,
|
|
809
|
+
...path2 && { path: path2 },
|
|
810
|
+
provenance
|
|
811
|
+
};
|
|
812
|
+
} catch {
|
|
813
|
+
return {
|
|
814
|
+
kind: "constraint",
|
|
815
|
+
constraintKind: "const",
|
|
816
|
+
value: trimmedText,
|
|
817
|
+
...path2 && { path: path2 },
|
|
818
|
+
provenance
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
}
|
|
716
822
|
const parsed = tryParseJson(effectiveText);
|
|
717
823
|
if (!Array.isArray(parsed)) {
|
|
718
824
|
return null;
|
|
@@ -744,6 +850,34 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
744
850
|
provenance
|
|
745
851
|
};
|
|
746
852
|
}
|
|
853
|
+
function parseDefaultValueValue(text, provenance) {
|
|
854
|
+
const trimmed = text.trim();
|
|
855
|
+
let value;
|
|
856
|
+
if (trimmed === "null") {
|
|
857
|
+
value = null;
|
|
858
|
+
} else if (trimmed === "true") {
|
|
859
|
+
value = true;
|
|
860
|
+
} else if (trimmed === "false") {
|
|
861
|
+
value = false;
|
|
862
|
+
} else {
|
|
863
|
+
const parsed = tryParseJson(trimmed);
|
|
864
|
+
value = parsed !== null ? parsed : trimmed;
|
|
865
|
+
}
|
|
866
|
+
return {
|
|
867
|
+
kind: "annotation",
|
|
868
|
+
annotationKind: "defaultValue",
|
|
869
|
+
value,
|
|
870
|
+
provenance
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function isMemberTargetDisplayName(text) {
|
|
874
|
+
return parseMemberTargetDisplayName(text) !== null;
|
|
875
|
+
}
|
|
876
|
+
function parseMemberTargetDisplayName(text) {
|
|
877
|
+
const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(text);
|
|
878
|
+
if (!match?.[1] || !match[2]) return null;
|
|
879
|
+
return { target: match[1], label: match[2].trim() };
|
|
880
|
+
}
|
|
747
881
|
function provenanceForComment(range, sourceFile, file, tagName) {
|
|
748
882
|
const { line, character } = sourceFile.getLineAndCharacterOfPosition(range.pos);
|
|
749
883
|
return {
|
|
@@ -825,11 +959,17 @@ function isObjectType(type) {
|
|
|
825
959
|
function isTypeReference(type) {
|
|
826
960
|
return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
|
|
827
961
|
}
|
|
962
|
+
var RESOLVING_TYPE_PLACEHOLDER = {
|
|
963
|
+
kind: "object",
|
|
964
|
+
properties: [],
|
|
965
|
+
additionalProperties: true
|
|
966
|
+
};
|
|
828
967
|
function analyzeClassToIR(classDecl, checker, file = "") {
|
|
829
968
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
830
969
|
const fields = [];
|
|
831
970
|
const fieldLayouts = [];
|
|
832
971
|
const typeRegistry = {};
|
|
972
|
+
const annotations = extractJSDocAnnotationNodes(classDecl, file);
|
|
833
973
|
const visiting = /* @__PURE__ */ new Set();
|
|
834
974
|
const instanceMethods = [];
|
|
835
975
|
const staticMethods = [];
|
|
@@ -852,12 +992,21 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
852
992
|
}
|
|
853
993
|
}
|
|
854
994
|
}
|
|
855
|
-
return {
|
|
995
|
+
return {
|
|
996
|
+
name,
|
|
997
|
+
fields,
|
|
998
|
+
fieldLayouts,
|
|
999
|
+
typeRegistry,
|
|
1000
|
+
...annotations.length > 0 && { annotations },
|
|
1001
|
+
instanceMethods,
|
|
1002
|
+
staticMethods
|
|
1003
|
+
};
|
|
856
1004
|
}
|
|
857
1005
|
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
858
1006
|
const name = interfaceDecl.name.text;
|
|
859
1007
|
const fields = [];
|
|
860
1008
|
const typeRegistry = {};
|
|
1009
|
+
const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
|
|
861
1010
|
const visiting = /* @__PURE__ */ new Set();
|
|
862
1011
|
for (const member of interfaceDecl.members) {
|
|
863
1012
|
if (ts4.isPropertySignature(member)) {
|
|
@@ -868,7 +1017,15 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
868
1017
|
}
|
|
869
1018
|
}
|
|
870
1019
|
const fieldLayouts = fields.map(() => ({}));
|
|
871
|
-
return {
|
|
1020
|
+
return {
|
|
1021
|
+
name,
|
|
1022
|
+
fields,
|
|
1023
|
+
fieldLayouts,
|
|
1024
|
+
typeRegistry,
|
|
1025
|
+
...annotations.length > 0 && { annotations },
|
|
1026
|
+
instanceMethods: [],
|
|
1027
|
+
staticMethods: []
|
|
1028
|
+
};
|
|
872
1029
|
}
|
|
873
1030
|
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
874
1031
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
@@ -883,6 +1040,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
883
1040
|
const name = typeAlias.name.text;
|
|
884
1041
|
const fields = [];
|
|
885
1042
|
const typeRegistry = {};
|
|
1043
|
+
const annotations = extractJSDocAnnotationNodes(typeAlias, file);
|
|
886
1044
|
const visiting = /* @__PURE__ */ new Set();
|
|
887
1045
|
for (const member of typeAlias.type.members) {
|
|
888
1046
|
if (ts4.isPropertySignature(member)) {
|
|
@@ -899,6 +1057,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
899
1057
|
fields,
|
|
900
1058
|
fieldLayouts: fields.map(() => ({})),
|
|
901
1059
|
typeRegistry,
|
|
1060
|
+
...annotations.length > 0 && { annotations },
|
|
902
1061
|
instanceMethods: [],
|
|
903
1062
|
staticMethods: []
|
|
904
1063
|
}
|
|
@@ -912,18 +1071,19 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
912
1071
|
const tsType = checker.getTypeAtLocation(prop);
|
|
913
1072
|
const optional = prop.questionToken !== void 0;
|
|
914
1073
|
const provenance = provenanceForNode(prop, file);
|
|
915
|
-
|
|
1074
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
|
|
916
1075
|
const constraints = [];
|
|
917
1076
|
if (prop.type) {
|
|
918
1077
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
919
1078
|
}
|
|
920
1079
|
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
921
|
-
|
|
1080
|
+
let annotations = [];
|
|
922
1081
|
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
923
1082
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
924
|
-
if (defaultAnnotation) {
|
|
1083
|
+
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
925
1084
|
annotations.push(defaultAnnotation);
|
|
926
1085
|
}
|
|
1086
|
+
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
927
1087
|
return {
|
|
928
1088
|
kind: "field",
|
|
929
1089
|
name,
|
|
@@ -942,14 +1102,15 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
942
1102
|
const tsType = checker.getTypeAtLocation(prop);
|
|
943
1103
|
const optional = prop.questionToken !== void 0;
|
|
944
1104
|
const provenance = provenanceForNode(prop, file);
|
|
945
|
-
|
|
1105
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
|
|
946
1106
|
const constraints = [];
|
|
947
1107
|
if (prop.type) {
|
|
948
1108
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
949
1109
|
}
|
|
950
1110
|
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
951
|
-
|
|
1111
|
+
let annotations = [];
|
|
952
1112
|
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
1113
|
+
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
953
1114
|
return {
|
|
954
1115
|
kind: "field",
|
|
955
1116
|
name,
|
|
@@ -960,7 +1121,69 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
960
1121
|
provenance
|
|
961
1122
|
};
|
|
962
1123
|
}
|
|
963
|
-
function
|
|
1124
|
+
function applyEnumMemberDisplayNames(type, annotations) {
|
|
1125
|
+
if (!annotations.some(
|
|
1126
|
+
(annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
|
|
1127
|
+
)) {
|
|
1128
|
+
return { type, annotations: [...annotations] };
|
|
1129
|
+
}
|
|
1130
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
1131
|
+
const nextType = rewriteEnumDisplayNames(type, annotations, consumed);
|
|
1132
|
+
if (consumed.size === 0) {
|
|
1133
|
+
return { type, annotations: [...annotations] };
|
|
1134
|
+
}
|
|
1135
|
+
return {
|
|
1136
|
+
type: nextType,
|
|
1137
|
+
annotations: annotations.filter((annotation) => !consumed.has(annotation))
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
function rewriteEnumDisplayNames(type, annotations, consumed) {
|
|
1141
|
+
switch (type.kind) {
|
|
1142
|
+
case "enum":
|
|
1143
|
+
return applyEnumMemberDisplayNamesToEnum(type, annotations, consumed);
|
|
1144
|
+
case "union": {
|
|
1145
|
+
return {
|
|
1146
|
+
...type,
|
|
1147
|
+
members: type.members.map(
|
|
1148
|
+
(member) => rewriteEnumDisplayNames(member, annotations, consumed)
|
|
1149
|
+
)
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
default:
|
|
1153
|
+
return type;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
function applyEnumMemberDisplayNamesToEnum(type, annotations, consumed) {
|
|
1157
|
+
const displayNames = /* @__PURE__ */ new Map();
|
|
1158
|
+
for (const annotation of annotations) {
|
|
1159
|
+
if (annotation.annotationKind !== "displayName") continue;
|
|
1160
|
+
const parsed = parseEnumMemberDisplayName(annotation.value);
|
|
1161
|
+
if (!parsed) continue;
|
|
1162
|
+
consumed.add(annotation);
|
|
1163
|
+
const member = type.members.find((m) => String(m.value) === parsed.value);
|
|
1164
|
+
if (!member) continue;
|
|
1165
|
+
displayNames.set(String(member.value), parsed.label);
|
|
1166
|
+
}
|
|
1167
|
+
if (displayNames.size === 0) {
|
|
1168
|
+
return type;
|
|
1169
|
+
}
|
|
1170
|
+
return {
|
|
1171
|
+
...type,
|
|
1172
|
+
members: type.members.map((member) => {
|
|
1173
|
+
const displayName = displayNames.get(String(member.value));
|
|
1174
|
+
return displayName !== void 0 ? { ...member, displayName } : member;
|
|
1175
|
+
})
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
function parseEnumMemberDisplayName(value) {
|
|
1179
|
+
const trimmed = value.trim();
|
|
1180
|
+
const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(trimmed);
|
|
1181
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1182
|
+
const label = match[2].trim();
|
|
1183
|
+
if (label === "") return null;
|
|
1184
|
+
return { value: match[1], label };
|
|
1185
|
+
}
|
|
1186
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
964
1187
|
if (type.flags & ts4.TypeFlags.String) {
|
|
965
1188
|
return { kind: "primitive", primitiveKind: "string" };
|
|
966
1189
|
}
|
|
@@ -989,7 +1212,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
|
989
1212
|
};
|
|
990
1213
|
}
|
|
991
1214
|
if (type.isUnion()) {
|
|
992
|
-
return resolveUnionType(type, checker, file, typeRegistry, visiting);
|
|
1215
|
+
return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
|
|
993
1216
|
}
|
|
994
1217
|
if (checker.isArrayType(type)) {
|
|
995
1218
|
return resolveArrayType(type, checker, file, typeRegistry, visiting);
|
|
@@ -999,70 +1222,102 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
|
999
1222
|
}
|
|
1000
1223
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1001
1224
|
}
|
|
1002
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting) {
|
|
1225
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
1226
|
+
const typeName = getNamedTypeName(type);
|
|
1227
|
+
const namedDecl = getNamedTypeDeclaration(type);
|
|
1228
|
+
if (typeName && typeName in typeRegistry) {
|
|
1229
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1230
|
+
}
|
|
1003
1231
|
const allTypes = type.types;
|
|
1004
1232
|
const nonNullTypes = allTypes.filter(
|
|
1005
1233
|
(t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
1006
1234
|
);
|
|
1007
1235
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
1236
|
+
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1237
|
+
if (namedDecl) {
|
|
1238
|
+
for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
|
|
1239
|
+
memberDisplayNames.set(value, label);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (sourceNode) {
|
|
1243
|
+
for (const [value, label] of extractDisplayNameMetadata(sourceNode).memberDisplayNames) {
|
|
1244
|
+
memberDisplayNames.set(value, label);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
const registerNamed = (result) => {
|
|
1248
|
+
if (!typeName) {
|
|
1249
|
+
return result;
|
|
1250
|
+
}
|
|
1251
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
1252
|
+
typeRegistry[typeName] = {
|
|
1253
|
+
name: typeName,
|
|
1254
|
+
type: result,
|
|
1255
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
1256
|
+
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
1257
|
+
};
|
|
1258
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1259
|
+
};
|
|
1260
|
+
const applyMemberLabels = (members2) => members2.map((value) => {
|
|
1261
|
+
const displayName = memberDisplayNames.get(String(value));
|
|
1262
|
+
return displayName !== void 0 ? { value, displayName } : { value };
|
|
1263
|
+
});
|
|
1008
1264
|
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
|
|
1009
1265
|
if (isBooleanUnion2) {
|
|
1010
1266
|
const boolNode = { kind: "primitive", primitiveKind: "boolean" };
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
}
|
|
1017
|
-
return boolNode;
|
|
1267
|
+
const result = hasNull ? {
|
|
1268
|
+
kind: "union",
|
|
1269
|
+
members: [boolNode, { kind: "primitive", primitiveKind: "null" }]
|
|
1270
|
+
} : boolNode;
|
|
1271
|
+
return registerNamed(result);
|
|
1018
1272
|
}
|
|
1019
1273
|
const allStringLiterals = nonNullTypes.every((t) => t.isStringLiteral());
|
|
1020
1274
|
if (allStringLiterals && nonNullTypes.length > 0) {
|
|
1021
1275
|
const stringTypes = nonNullTypes.filter((t) => t.isStringLiteral());
|
|
1022
1276
|
const enumNode = {
|
|
1023
1277
|
kind: "enum",
|
|
1024
|
-
members: stringTypes.map((t) =>
|
|
1278
|
+
members: applyMemberLabels(stringTypes.map((t) => t.value))
|
|
1025
1279
|
};
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
}
|
|
1032
|
-
return enumNode;
|
|
1280
|
+
const result = hasNull ? {
|
|
1281
|
+
kind: "union",
|
|
1282
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
1283
|
+
} : enumNode;
|
|
1284
|
+
return registerNamed(result);
|
|
1033
1285
|
}
|
|
1034
1286
|
const allNumberLiterals = nonNullTypes.every((t) => t.isNumberLiteral());
|
|
1035
1287
|
if (allNumberLiterals && nonNullTypes.length > 0) {
|
|
1036
1288
|
const numberTypes = nonNullTypes.filter((t) => t.isNumberLiteral());
|
|
1037
1289
|
const enumNode = {
|
|
1038
1290
|
kind: "enum",
|
|
1039
|
-
members: numberTypes.map((t) =>
|
|
1291
|
+
members: applyMemberLabels(numberTypes.map((t) => t.value))
|
|
1040
1292
|
};
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1047
|
-
return enumNode;
|
|
1293
|
+
const result = hasNull ? {
|
|
1294
|
+
kind: "union",
|
|
1295
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
1296
|
+
} : enumNode;
|
|
1297
|
+
return registerNamed(result);
|
|
1048
1298
|
}
|
|
1049
1299
|
if (nonNullTypes.length === 1 && nonNullTypes[0]) {
|
|
1050
|
-
const inner = resolveTypeNode(
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1300
|
+
const inner = resolveTypeNode(
|
|
1301
|
+
nonNullTypes[0],
|
|
1302
|
+
checker,
|
|
1303
|
+
file,
|
|
1304
|
+
typeRegistry,
|
|
1305
|
+
visiting,
|
|
1306
|
+
sourceNode
|
|
1307
|
+
);
|
|
1308
|
+
const result = hasNull ? {
|
|
1309
|
+
kind: "union",
|
|
1310
|
+
members: [inner, { kind: "primitive", primitiveKind: "null" }]
|
|
1311
|
+
} : inner;
|
|
1312
|
+
return registerNamed(result);
|
|
1058
1313
|
}
|
|
1059
1314
|
const members = nonNullTypes.map(
|
|
1060
|
-
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting)
|
|
1315
|
+
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
|
|
1061
1316
|
);
|
|
1062
1317
|
if (hasNull) {
|
|
1063
1318
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
1064
1319
|
}
|
|
1065
|
-
return { kind: "union", members };
|
|
1320
|
+
return registerNamed({ kind: "union", members });
|
|
1066
1321
|
}
|
|
1067
1322
|
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
1068
1323
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
@@ -1070,15 +1325,92 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
|
1070
1325
|
const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
|
|
1071
1326
|
return { kind: "array", items };
|
|
1072
1327
|
}
|
|
1328
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
1329
|
+
if (type.getProperties().length > 0) {
|
|
1330
|
+
return null;
|
|
1331
|
+
}
|
|
1332
|
+
const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
|
|
1333
|
+
if (!indexInfo) {
|
|
1334
|
+
return null;
|
|
1335
|
+
}
|
|
1336
|
+
const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
|
|
1337
|
+
return { kind: "record", valueType };
|
|
1338
|
+
}
|
|
1339
|
+
function typeNodeContainsReference(type, targetName) {
|
|
1340
|
+
switch (type.kind) {
|
|
1341
|
+
case "reference":
|
|
1342
|
+
return type.name === targetName;
|
|
1343
|
+
case "array":
|
|
1344
|
+
return typeNodeContainsReference(type.items, targetName);
|
|
1345
|
+
case "record":
|
|
1346
|
+
return typeNodeContainsReference(type.valueType, targetName);
|
|
1347
|
+
case "union":
|
|
1348
|
+
return type.members.some((member) => typeNodeContainsReference(member, targetName));
|
|
1349
|
+
case "object":
|
|
1350
|
+
return type.properties.some(
|
|
1351
|
+
(property) => typeNodeContainsReference(property.type, targetName)
|
|
1352
|
+
);
|
|
1353
|
+
case "primitive":
|
|
1354
|
+
case "enum":
|
|
1355
|
+
case "dynamic":
|
|
1356
|
+
case "custom":
|
|
1357
|
+
return false;
|
|
1358
|
+
default: {
|
|
1359
|
+
const _exhaustive = type;
|
|
1360
|
+
return _exhaustive;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1073
1364
|
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
1365
|
+
const typeName = getNamedTypeName(type);
|
|
1366
|
+
const namedTypeName = typeName ?? void 0;
|
|
1367
|
+
const namedDecl = getNamedTypeDeclaration(type);
|
|
1368
|
+
const shouldRegisterNamedType = namedTypeName !== void 0 && !(namedTypeName === "Record" && namedDecl?.getSourceFile().fileName !== file);
|
|
1369
|
+
const clearNamedTypeRegistration = () => {
|
|
1370
|
+
if (namedTypeName === void 0 || !shouldRegisterNamedType) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
Reflect.deleteProperty(typeRegistry, namedTypeName);
|
|
1374
|
+
};
|
|
1074
1375
|
if (visiting.has(type)) {
|
|
1376
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
1377
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1378
|
+
}
|
|
1075
1379
|
return { kind: "object", properties: [], additionalProperties: false };
|
|
1076
1380
|
}
|
|
1381
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType && !typeRegistry[namedTypeName]) {
|
|
1382
|
+
typeRegistry[namedTypeName] = {
|
|
1383
|
+
name: namedTypeName,
|
|
1384
|
+
type: RESOLVING_TYPE_PLACEHOLDER,
|
|
1385
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1077
1388
|
visiting.add(type);
|
|
1078
|
-
|
|
1079
|
-
|
|
1389
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType && typeRegistry[namedTypeName]?.type !== void 0) {
|
|
1390
|
+
if (typeRegistry[namedTypeName].type !== RESOLVING_TYPE_PLACEHOLDER) {
|
|
1391
|
+
visiting.delete(type);
|
|
1392
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
|
|
1396
|
+
if (recordNode) {
|
|
1080
1397
|
visiting.delete(type);
|
|
1081
|
-
|
|
1398
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
1399
|
+
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType, namedTypeName);
|
|
1400
|
+
if (!isRecursiveRecord) {
|
|
1401
|
+
clearNamedTypeRegistration();
|
|
1402
|
+
return recordNode;
|
|
1403
|
+
}
|
|
1404
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
1405
|
+
typeRegistry[namedTypeName] = {
|
|
1406
|
+
name: namedTypeName,
|
|
1407
|
+
type: recordNode,
|
|
1408
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
1409
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
1410
|
+
};
|
|
1411
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1412
|
+
}
|
|
1413
|
+
return recordNode;
|
|
1082
1414
|
}
|
|
1083
1415
|
const properties = [];
|
|
1084
1416
|
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
|
|
@@ -1087,7 +1419,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1087
1419
|
if (!declaration) continue;
|
|
1088
1420
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1089
1421
|
const optional = !!(prop.flags & ts4.SymbolFlags.Optional);
|
|
1090
|
-
const propTypeNode = resolveTypeNode(
|
|
1422
|
+
const propTypeNode = resolveTypeNode(
|
|
1423
|
+
propType,
|
|
1424
|
+
checker,
|
|
1425
|
+
file,
|
|
1426
|
+
typeRegistry,
|
|
1427
|
+
visiting,
|
|
1428
|
+
declaration
|
|
1429
|
+
);
|
|
1091
1430
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
1092
1431
|
properties.push({
|
|
1093
1432
|
name: prop.name,
|
|
@@ -1102,15 +1441,17 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1102
1441
|
const objectNode = {
|
|
1103
1442
|
kind: "object",
|
|
1104
1443
|
properties,
|
|
1105
|
-
additionalProperties:
|
|
1444
|
+
additionalProperties: true
|
|
1106
1445
|
};
|
|
1107
|
-
if (
|
|
1108
|
-
|
|
1109
|
-
|
|
1446
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
1447
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
1448
|
+
typeRegistry[namedTypeName] = {
|
|
1449
|
+
name: namedTypeName,
|
|
1110
1450
|
type: objectNode,
|
|
1111
|
-
|
|
1451
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
1452
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
1112
1453
|
};
|
|
1113
|
-
return { kind: "reference", name:
|
|
1454
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1114
1455
|
}
|
|
1115
1456
|
return objectNode;
|
|
1116
1457
|
}
|
|
@@ -1202,6 +1543,12 @@ function provenanceForNode(node, file) {
|
|
|
1202
1543
|
function provenanceForFile(file) {
|
|
1203
1544
|
return { surface: "tsdoc", file, line: 0, column: 0 };
|
|
1204
1545
|
}
|
|
1546
|
+
function provenanceForDeclaration(node, file) {
|
|
1547
|
+
if (!node) {
|
|
1548
|
+
return provenanceForFile(file);
|
|
1549
|
+
}
|
|
1550
|
+
return provenanceForNode(node, file);
|
|
1551
|
+
}
|
|
1205
1552
|
function getNamedTypeName(type) {
|
|
1206
1553
|
const symbol = type.getSymbol();
|
|
1207
1554
|
if (symbol?.declarations) {
|
|
@@ -1220,6 +1567,20 @@ function getNamedTypeName(type) {
|
|
|
1220
1567
|
}
|
|
1221
1568
|
return null;
|
|
1222
1569
|
}
|
|
1570
|
+
function getNamedTypeDeclaration(type) {
|
|
1571
|
+
const symbol = type.getSymbol();
|
|
1572
|
+
if (symbol?.declarations) {
|
|
1573
|
+
const decl = symbol.declarations[0];
|
|
1574
|
+
if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
|
|
1575
|
+
return decl;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
const aliasSymbol = type.aliasSymbol;
|
|
1579
|
+
if (aliasSymbol?.declarations) {
|
|
1580
|
+
return aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
1581
|
+
}
|
|
1582
|
+
return void 0;
|
|
1583
|
+
}
|
|
1223
1584
|
function analyzeMethod(method, checker) {
|
|
1224
1585
|
if (!ts4.isIdentifier(method.name)) {
|
|
1225
1586
|
return null;
|
|
@@ -1262,13 +1623,26 @@ function detectFormSpecReference(typeNode) {
|
|
|
1262
1623
|
}
|
|
1263
1624
|
|
|
1264
1625
|
// src/json-schema/ir-generator.ts
|
|
1265
|
-
function makeContext() {
|
|
1266
|
-
|
|
1626
|
+
function makeContext(options) {
|
|
1627
|
+
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
1628
|
+
if (!vendorPrefix.startsWith("x-")) {
|
|
1629
|
+
throw new Error(
|
|
1630
|
+
`Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
return {
|
|
1634
|
+
defs: {},
|
|
1635
|
+
extensionRegistry: options?.extensionRegistry,
|
|
1636
|
+
vendorPrefix
|
|
1637
|
+
};
|
|
1267
1638
|
}
|
|
1268
|
-
function generateJsonSchemaFromIR(ir) {
|
|
1269
|
-
const ctx = makeContext();
|
|
1639
|
+
function generateJsonSchemaFromIR(ir, options) {
|
|
1640
|
+
const ctx = makeContext(options);
|
|
1270
1641
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
1271
1642
|
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
1643
|
+
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
1644
|
+
applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
|
|
1645
|
+
}
|
|
1272
1646
|
}
|
|
1273
1647
|
const properties = {};
|
|
1274
1648
|
const required = [];
|
|
@@ -1280,6 +1654,9 @@ function generateJsonSchemaFromIR(ir) {
|
|
|
1280
1654
|
properties,
|
|
1281
1655
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
1282
1656
|
};
|
|
1657
|
+
if (ir.annotations && ir.annotations.length > 0) {
|
|
1658
|
+
applyAnnotations(result, ir.annotations, ctx);
|
|
1659
|
+
}
|
|
1283
1660
|
if (Object.keys(ctx.defs).length > 0) {
|
|
1284
1661
|
result.$defs = ctx.defs;
|
|
1285
1662
|
}
|
|
@@ -1309,25 +1686,54 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
1309
1686
|
}
|
|
1310
1687
|
function generateFieldSchema(field, ctx) {
|
|
1311
1688
|
const schema = generateTypeNode(field.type, ctx);
|
|
1689
|
+
const itemStringSchema = schema.type === "array" && schema.items?.type === "string" ? schema.items : void 0;
|
|
1312
1690
|
const directConstraints = [];
|
|
1691
|
+
const itemConstraints = [];
|
|
1313
1692
|
const pathConstraints = [];
|
|
1314
1693
|
for (const c of field.constraints) {
|
|
1315
1694
|
if (c.path) {
|
|
1316
1695
|
pathConstraints.push(c);
|
|
1696
|
+
} else if (itemStringSchema !== void 0 && isStringItemConstraint(c)) {
|
|
1697
|
+
itemConstraints.push(c);
|
|
1317
1698
|
} else {
|
|
1318
1699
|
directConstraints.push(c);
|
|
1319
1700
|
}
|
|
1320
1701
|
}
|
|
1321
|
-
applyConstraints(schema, directConstraints);
|
|
1322
|
-
|
|
1702
|
+
applyConstraints(schema, directConstraints, ctx);
|
|
1703
|
+
if (itemStringSchema !== void 0) {
|
|
1704
|
+
applyConstraints(itemStringSchema, itemConstraints, ctx);
|
|
1705
|
+
}
|
|
1706
|
+
const rootAnnotations = [];
|
|
1707
|
+
const itemAnnotations = [];
|
|
1708
|
+
for (const annotation of field.annotations) {
|
|
1709
|
+
if (itemStringSchema !== void 0 && annotation.annotationKind === "format") {
|
|
1710
|
+
itemAnnotations.push(annotation);
|
|
1711
|
+
} else {
|
|
1712
|
+
rootAnnotations.push(annotation);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
applyAnnotations(schema, rootAnnotations, ctx);
|
|
1716
|
+
if (itemStringSchema !== void 0) {
|
|
1717
|
+
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
1718
|
+
}
|
|
1323
1719
|
if (pathConstraints.length === 0) {
|
|
1324
1720
|
return schema;
|
|
1325
1721
|
}
|
|
1326
|
-
return applyPathTargetedConstraints(schema, pathConstraints);
|
|
1722
|
+
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
1327
1723
|
}
|
|
1328
|
-
function
|
|
1724
|
+
function isStringItemConstraint(constraint) {
|
|
1725
|
+
switch (constraint.constraintKind) {
|
|
1726
|
+
case "minLength":
|
|
1727
|
+
case "maxLength":
|
|
1728
|
+
case "pattern":
|
|
1729
|
+
return true;
|
|
1730
|
+
default:
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
1329
1735
|
if (schema.type === "array" && schema.items) {
|
|
1330
|
-
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
|
|
1736
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
|
|
1331
1737
|
return schema;
|
|
1332
1738
|
}
|
|
1333
1739
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -1341,7 +1747,7 @@ function applyPathTargetedConstraints(schema, pathConstraints) {
|
|
|
1341
1747
|
const propertyOverrides = {};
|
|
1342
1748
|
for (const [target, constraints] of byTarget) {
|
|
1343
1749
|
const subSchema = {};
|
|
1344
|
-
applyConstraints(subSchema, constraints);
|
|
1750
|
+
applyConstraints(subSchema, constraints, ctx);
|
|
1345
1751
|
propertyOverrides[target] = subSchema;
|
|
1346
1752
|
}
|
|
1347
1753
|
if (schema.$ref) {
|
|
@@ -1385,6 +1791,8 @@ function generateTypeNode(type, ctx) {
|
|
|
1385
1791
|
return generateArrayType(type, ctx);
|
|
1386
1792
|
case "object":
|
|
1387
1793
|
return generateObjectType(type, ctx);
|
|
1794
|
+
case "record":
|
|
1795
|
+
return generateRecordType(type, ctx);
|
|
1388
1796
|
case "union":
|
|
1389
1797
|
return generateUnionType(type, ctx);
|
|
1390
1798
|
case "reference":
|
|
@@ -1392,7 +1800,7 @@ function generateTypeNode(type, ctx) {
|
|
|
1392
1800
|
case "dynamic":
|
|
1393
1801
|
return generateDynamicType(type);
|
|
1394
1802
|
case "custom":
|
|
1395
|
-
return generateCustomType(type);
|
|
1803
|
+
return generateCustomType(type, ctx);
|
|
1396
1804
|
default: {
|
|
1397
1805
|
const _exhaustive = type;
|
|
1398
1806
|
return _exhaustive;
|
|
@@ -1441,16 +1849,27 @@ function generateObjectType(type, ctx) {
|
|
|
1441
1849
|
}
|
|
1442
1850
|
return schema;
|
|
1443
1851
|
}
|
|
1852
|
+
function generateRecordType(type, ctx) {
|
|
1853
|
+
return {
|
|
1854
|
+
type: "object",
|
|
1855
|
+
additionalProperties: generateTypeNode(type.valueType, ctx)
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1444
1858
|
function generatePropertySchema(prop, ctx) {
|
|
1445
1859
|
const schema = generateTypeNode(prop.type, ctx);
|
|
1446
|
-
applyConstraints(schema, prop.constraints);
|
|
1447
|
-
applyAnnotations(schema, prop.annotations);
|
|
1860
|
+
applyConstraints(schema, prop.constraints, ctx);
|
|
1861
|
+
applyAnnotations(schema, prop.annotations, ctx);
|
|
1448
1862
|
return schema;
|
|
1449
1863
|
}
|
|
1450
1864
|
function generateUnionType(type, ctx) {
|
|
1451
1865
|
if (isBooleanUnion(type)) {
|
|
1452
1866
|
return { type: "boolean" };
|
|
1453
1867
|
}
|
|
1868
|
+
if (isNullableUnion(type)) {
|
|
1869
|
+
return {
|
|
1870
|
+
oneOf: type.members.map((m) => generateTypeNode(m, ctx))
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1454
1873
|
return {
|
|
1455
1874
|
anyOf: type.members.map((m) => generateTypeNode(m, ctx))
|
|
1456
1875
|
};
|
|
@@ -1460,6 +1879,13 @@ function isBooleanUnion(type) {
|
|
|
1460
1879
|
const kinds = type.members.map((m) => m.kind);
|
|
1461
1880
|
return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
|
|
1462
1881
|
}
|
|
1882
|
+
function isNullableUnion(type) {
|
|
1883
|
+
if (type.members.length !== 2) return false;
|
|
1884
|
+
const nullCount = type.members.filter(
|
|
1885
|
+
(m) => m.kind === "primitive" && m.primitiveKind === "null"
|
|
1886
|
+
).length;
|
|
1887
|
+
return nullCount === 1;
|
|
1888
|
+
}
|
|
1463
1889
|
function generateReferenceType(type) {
|
|
1464
1890
|
return { $ref: `#/$defs/${type.name}` };
|
|
1465
1891
|
}
|
|
@@ -1480,10 +1906,7 @@ function generateDynamicType(type) {
|
|
|
1480
1906
|
"x-formspec-schemaSource": type.sourceKey
|
|
1481
1907
|
};
|
|
1482
1908
|
}
|
|
1483
|
-
function
|
|
1484
|
-
return { type: "object" };
|
|
1485
|
-
}
|
|
1486
|
-
function applyConstraints(schema, constraints) {
|
|
1909
|
+
function applyConstraints(schema, constraints, ctx) {
|
|
1487
1910
|
for (const constraint of constraints) {
|
|
1488
1911
|
switch (constraint.constraintKind) {
|
|
1489
1912
|
case "minimum":
|
|
@@ -1525,9 +1948,13 @@ function applyConstraints(schema, constraints) {
|
|
|
1525
1948
|
case "uniqueItems":
|
|
1526
1949
|
schema.uniqueItems = constraint.value;
|
|
1527
1950
|
break;
|
|
1951
|
+
case "const":
|
|
1952
|
+
schema.const = constraint.value;
|
|
1953
|
+
break;
|
|
1528
1954
|
case "allowedMembers":
|
|
1529
1955
|
break;
|
|
1530
1956
|
case "custom":
|
|
1957
|
+
applyCustomConstraint(schema, constraint, ctx);
|
|
1531
1958
|
break;
|
|
1532
1959
|
default: {
|
|
1533
1960
|
const _exhaustive = constraint;
|
|
@@ -1536,7 +1963,7 @@ function applyConstraints(schema, constraints) {
|
|
|
1536
1963
|
}
|
|
1537
1964
|
}
|
|
1538
1965
|
}
|
|
1539
|
-
function applyAnnotations(schema, annotations) {
|
|
1966
|
+
function applyAnnotations(schema, annotations, ctx) {
|
|
1540
1967
|
for (const annotation of annotations) {
|
|
1541
1968
|
switch (annotation.annotationKind) {
|
|
1542
1969
|
case "displayName":
|
|
@@ -1548,14 +1975,21 @@ function applyAnnotations(schema, annotations) {
|
|
|
1548
1975
|
case "defaultValue":
|
|
1549
1976
|
schema.default = annotation.value;
|
|
1550
1977
|
break;
|
|
1978
|
+
case "format":
|
|
1979
|
+
schema.format = annotation.value;
|
|
1980
|
+
break;
|
|
1551
1981
|
case "deprecated":
|
|
1552
1982
|
schema.deprecated = true;
|
|
1983
|
+
if (annotation.message !== void 0 && annotation.message !== "") {
|
|
1984
|
+
schema["x-formspec-deprecation-description"] = annotation.message;
|
|
1985
|
+
}
|
|
1553
1986
|
break;
|
|
1554
1987
|
case "placeholder":
|
|
1555
1988
|
break;
|
|
1556
1989
|
case "formatHint":
|
|
1557
1990
|
break;
|
|
1558
1991
|
case "custom":
|
|
1992
|
+
applyCustomAnnotation(schema, annotation, ctx);
|
|
1559
1993
|
break;
|
|
1560
1994
|
default: {
|
|
1561
1995
|
const _exhaustive = annotation;
|
|
@@ -1564,6 +1998,36 @@ function applyAnnotations(schema, annotations) {
|
|
|
1564
1998
|
}
|
|
1565
1999
|
}
|
|
1566
2000
|
}
|
|
2001
|
+
function generateCustomType(type, ctx) {
|
|
2002
|
+
const registration = ctx.extensionRegistry?.findType(type.typeId);
|
|
2003
|
+
if (registration === void 0) {
|
|
2004
|
+
throw new Error(
|
|
2005
|
+
`Cannot generate JSON Schema for custom type "${type.typeId}" without a matching extension registration`
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
|
|
2009
|
+
}
|
|
2010
|
+
function applyCustomConstraint(schema, constraint, ctx) {
|
|
2011
|
+
const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
|
|
2012
|
+
if (registration === void 0) {
|
|
2013
|
+
throw new Error(
|
|
2014
|
+
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
|
|
2018
|
+
}
|
|
2019
|
+
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
2020
|
+
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
2021
|
+
if (registration === void 0) {
|
|
2022
|
+
throw new Error(
|
|
2023
|
+
`Cannot generate JSON Schema for custom annotation "${annotation.annotationId}" without a matching extension registration`
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
if (registration.toJsonSchema === void 0) {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
|
|
2030
|
+
}
|
|
1567
2031
|
|
|
1568
2032
|
// src/ui-schema/schema.ts
|
|
1569
2033
|
var import_zod = require("zod");
|
|
@@ -1700,25 +2164,31 @@ function createShowRule(fieldName, value) {
|
|
|
1700
2164
|
}
|
|
1701
2165
|
};
|
|
1702
2166
|
}
|
|
2167
|
+
function flattenConditionSchema(scope, schema) {
|
|
2168
|
+
if (schema.allOf === void 0) {
|
|
2169
|
+
if (scope === "#") {
|
|
2170
|
+
return [schema];
|
|
2171
|
+
}
|
|
2172
|
+
const fieldName = scope.replace("#/properties/", "");
|
|
2173
|
+
return [
|
|
2174
|
+
{
|
|
2175
|
+
properties: {
|
|
2176
|
+
[fieldName]: schema
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
];
|
|
2180
|
+
}
|
|
2181
|
+
return schema.allOf.flatMap((member) => flattenConditionSchema(scope, member));
|
|
2182
|
+
}
|
|
1703
2183
|
function combineRules(parentRule, childRule) {
|
|
1704
|
-
const parentCondition = parentRule.condition;
|
|
1705
|
-
const childCondition = childRule.condition;
|
|
1706
2184
|
return {
|
|
1707
2185
|
effect: "SHOW",
|
|
1708
2186
|
condition: {
|
|
1709
2187
|
scope: "#",
|
|
1710
2188
|
schema: {
|
|
1711
2189
|
allOf: [
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
[parentCondition.scope.replace("#/properties/", "")]: parentCondition.schema
|
|
1715
|
-
}
|
|
1716
|
-
},
|
|
1717
|
-
{
|
|
1718
|
-
properties: {
|
|
1719
|
-
[childCondition.scope.replace("#/properties/", "")]: childCondition.schema
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
2190
|
+
...flattenConditionSchema(parentRule.condition.scope, parentRule.condition.schema),
|
|
2191
|
+
...flattenConditionSchema(childRule.condition.scope, childRule.condition.schema)
|
|
1722
2192
|
]
|
|
1723
2193
|
}
|
|
1724
2194
|
}
|
|
@@ -1726,10 +2196,14 @@ function combineRules(parentRule, childRule) {
|
|
|
1726
2196
|
}
|
|
1727
2197
|
function fieldNodeToControl(field, parentRule) {
|
|
1728
2198
|
const displayNameAnnotation = field.annotations.find((a) => a.annotationKind === "displayName");
|
|
2199
|
+
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
1729
2200
|
const control = {
|
|
1730
2201
|
type: "Control",
|
|
1731
2202
|
scope: fieldToScope(field.name),
|
|
1732
2203
|
...displayNameAnnotation !== void 0 && { label: displayNameAnnotation.value },
|
|
2204
|
+
...placeholderAnnotation !== void 0 && {
|
|
2205
|
+
options: { placeholder: placeholderAnnotation.value }
|
|
2206
|
+
},
|
|
1733
2207
|
...parentRule !== void 0 && { rule: parentRule }
|
|
1734
2208
|
};
|
|
1735
2209
|
return control;
|
|
@@ -1788,12 +2262,9 @@ function generateClassSchemas(analysis, source) {
|
|
|
1788
2262
|
}
|
|
1789
2263
|
|
|
1790
2264
|
// src/validate/constraint-validator.ts
|
|
1791
|
-
function makeCode(ctx, category, number) {
|
|
1792
|
-
return `${ctx.vendorPrefix}-${category}-${String(number).padStart(3, "0")}`;
|
|
1793
|
-
}
|
|
1794
2265
|
function addContradiction(ctx, message, primary, related) {
|
|
1795
2266
|
ctx.diagnostics.push({
|
|
1796
|
-
code:
|
|
2267
|
+
code: "CONTRADICTING_CONSTRAINTS",
|
|
1797
2268
|
message,
|
|
1798
2269
|
severity: "error",
|
|
1799
2270
|
primaryLocation: primary,
|
|
@@ -1802,7 +2273,7 @@ function addContradiction(ctx, message, primary, related) {
|
|
|
1802
2273
|
}
|
|
1803
2274
|
function addTypeMismatch(ctx, message, primary) {
|
|
1804
2275
|
ctx.diagnostics.push({
|
|
1805
|
-
code:
|
|
2276
|
+
code: "TYPE_MISMATCH",
|
|
1806
2277
|
message,
|
|
1807
2278
|
severity: "error",
|
|
1808
2279
|
primaryLocation: primary,
|
|
@@ -1811,13 +2282,31 @@ function addTypeMismatch(ctx, message, primary) {
|
|
|
1811
2282
|
}
|
|
1812
2283
|
function addUnknownExtension(ctx, message, primary) {
|
|
1813
2284
|
ctx.diagnostics.push({
|
|
1814
|
-
code:
|
|
2285
|
+
code: "UNKNOWN_EXTENSION",
|
|
1815
2286
|
message,
|
|
1816
2287
|
severity: "warning",
|
|
1817
2288
|
primaryLocation: primary,
|
|
1818
2289
|
relatedLocations: []
|
|
1819
2290
|
});
|
|
1820
2291
|
}
|
|
2292
|
+
function addUnknownPathTarget(ctx, message, primary) {
|
|
2293
|
+
ctx.diagnostics.push({
|
|
2294
|
+
code: "UNKNOWN_PATH_TARGET",
|
|
2295
|
+
message,
|
|
2296
|
+
severity: "error",
|
|
2297
|
+
primaryLocation: primary,
|
|
2298
|
+
relatedLocations: []
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
function addConstraintBroadening(ctx, message, primary, related) {
|
|
2302
|
+
ctx.diagnostics.push({
|
|
2303
|
+
code: "CONSTRAINT_BROADENING",
|
|
2304
|
+
message,
|
|
2305
|
+
severity: "error",
|
|
2306
|
+
primaryLocation: primary,
|
|
2307
|
+
relatedLocations: [related]
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
1821
2310
|
function findNumeric(constraints, constraintKind) {
|
|
1822
2311
|
return constraints.find((c) => c.constraintKind === constraintKind);
|
|
1823
2312
|
}
|
|
@@ -1829,6 +2318,165 @@ function findAllowedMembers(constraints) {
|
|
|
1829
2318
|
(c) => c.constraintKind === "allowedMembers"
|
|
1830
2319
|
);
|
|
1831
2320
|
}
|
|
2321
|
+
function findConstConstraints(constraints) {
|
|
2322
|
+
return constraints.filter(
|
|
2323
|
+
(c) => c.constraintKind === "const"
|
|
2324
|
+
);
|
|
2325
|
+
}
|
|
2326
|
+
function jsonValueEquals(left, right) {
|
|
2327
|
+
if (left === right) {
|
|
2328
|
+
return true;
|
|
2329
|
+
}
|
|
2330
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
2331
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
|
|
2332
|
+
return false;
|
|
2333
|
+
}
|
|
2334
|
+
return left.every((item, index) => jsonValueEquals(item, right[index]));
|
|
2335
|
+
}
|
|
2336
|
+
if (isJsonObject(left) || isJsonObject(right)) {
|
|
2337
|
+
if (!isJsonObject(left) || !isJsonObject(right)) {
|
|
2338
|
+
return false;
|
|
2339
|
+
}
|
|
2340
|
+
const leftKeys = Object.keys(left).sort();
|
|
2341
|
+
const rightKeys = Object.keys(right).sort();
|
|
2342
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
2343
|
+
return false;
|
|
2344
|
+
}
|
|
2345
|
+
return leftKeys.every((key, index) => {
|
|
2346
|
+
const rightKey = rightKeys[index];
|
|
2347
|
+
if (rightKey !== key) {
|
|
2348
|
+
return false;
|
|
2349
|
+
}
|
|
2350
|
+
const leftValue = left[key];
|
|
2351
|
+
const rightValue = right[rightKey];
|
|
2352
|
+
return leftValue !== void 0 && rightValue !== void 0 && jsonValueEquals(leftValue, rightValue);
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
return false;
|
|
2356
|
+
}
|
|
2357
|
+
function isJsonObject(value) {
|
|
2358
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2359
|
+
}
|
|
2360
|
+
function isOrderedBoundConstraint(constraint) {
|
|
2361
|
+
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";
|
|
2362
|
+
}
|
|
2363
|
+
function pathKey(constraint) {
|
|
2364
|
+
return constraint.path?.segments.join(".") ?? "";
|
|
2365
|
+
}
|
|
2366
|
+
function orderedBoundFamily(kind) {
|
|
2367
|
+
switch (kind) {
|
|
2368
|
+
case "minimum":
|
|
2369
|
+
case "exclusiveMinimum":
|
|
2370
|
+
return "numeric-lower";
|
|
2371
|
+
case "maximum":
|
|
2372
|
+
case "exclusiveMaximum":
|
|
2373
|
+
return "numeric-upper";
|
|
2374
|
+
case "minLength":
|
|
2375
|
+
return "minLength";
|
|
2376
|
+
case "minItems":
|
|
2377
|
+
return "minItems";
|
|
2378
|
+
case "maxLength":
|
|
2379
|
+
return "maxLength";
|
|
2380
|
+
case "maxItems":
|
|
2381
|
+
return "maxItems";
|
|
2382
|
+
default: {
|
|
2383
|
+
const _exhaustive = kind;
|
|
2384
|
+
return _exhaustive;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
function isNumericLowerKind(kind) {
|
|
2389
|
+
return kind === "minimum" || kind === "exclusiveMinimum";
|
|
2390
|
+
}
|
|
2391
|
+
function isNumericUpperKind(kind) {
|
|
2392
|
+
return kind === "maximum" || kind === "exclusiveMaximum";
|
|
2393
|
+
}
|
|
2394
|
+
function describeConstraintTag(constraint) {
|
|
2395
|
+
return `@${constraint.constraintKind}`;
|
|
2396
|
+
}
|
|
2397
|
+
function compareConstraintStrength(current, previous) {
|
|
2398
|
+
const family = orderedBoundFamily(current.constraintKind);
|
|
2399
|
+
if (family === "numeric-lower") {
|
|
2400
|
+
if (!isNumericLowerKind(current.constraintKind) || !isNumericLowerKind(previous.constraintKind)) {
|
|
2401
|
+
throw new Error("numeric-lower family received non-numeric lower-bound constraint");
|
|
2402
|
+
}
|
|
2403
|
+
if (current.value !== previous.value) {
|
|
2404
|
+
return current.value > previous.value ? 1 : -1;
|
|
2405
|
+
}
|
|
2406
|
+
if (current.constraintKind === "exclusiveMinimum" && previous.constraintKind === "minimum") {
|
|
2407
|
+
return 1;
|
|
2408
|
+
}
|
|
2409
|
+
if (current.constraintKind === "minimum" && previous.constraintKind === "exclusiveMinimum") {
|
|
2410
|
+
return -1;
|
|
2411
|
+
}
|
|
2412
|
+
return 0;
|
|
2413
|
+
}
|
|
2414
|
+
if (family === "numeric-upper") {
|
|
2415
|
+
if (!isNumericUpperKind(current.constraintKind) || !isNumericUpperKind(previous.constraintKind)) {
|
|
2416
|
+
throw new Error("numeric-upper family received non-numeric upper-bound constraint");
|
|
2417
|
+
}
|
|
2418
|
+
if (current.value !== previous.value) {
|
|
2419
|
+
return current.value < previous.value ? 1 : -1;
|
|
2420
|
+
}
|
|
2421
|
+
if (current.constraintKind === "exclusiveMaximum" && previous.constraintKind === "maximum") {
|
|
2422
|
+
return 1;
|
|
2423
|
+
}
|
|
2424
|
+
if (current.constraintKind === "maximum" && previous.constraintKind === "exclusiveMaximum") {
|
|
2425
|
+
return -1;
|
|
2426
|
+
}
|
|
2427
|
+
return 0;
|
|
2428
|
+
}
|
|
2429
|
+
switch (family) {
|
|
2430
|
+
case "minLength":
|
|
2431
|
+
case "minItems":
|
|
2432
|
+
if (current.value === previous.value) {
|
|
2433
|
+
return 0;
|
|
2434
|
+
}
|
|
2435
|
+
return current.value > previous.value ? 1 : -1;
|
|
2436
|
+
case "maxLength":
|
|
2437
|
+
case "maxItems":
|
|
2438
|
+
if (current.value === previous.value) {
|
|
2439
|
+
return 0;
|
|
2440
|
+
}
|
|
2441
|
+
return current.value < previous.value ? 1 : -1;
|
|
2442
|
+
default: {
|
|
2443
|
+
const _exhaustive = family;
|
|
2444
|
+
return _exhaustive;
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
function checkConstraintBroadening(ctx, fieldName, constraints) {
|
|
2449
|
+
const strongestByKey = /* @__PURE__ */ new Map();
|
|
2450
|
+
for (const constraint of constraints) {
|
|
2451
|
+
if (!isOrderedBoundConstraint(constraint)) {
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
const key = `${orderedBoundFamily(constraint.constraintKind)}:${pathKey(constraint)}`;
|
|
2455
|
+
const previous = strongestByKey.get(key);
|
|
2456
|
+
if (previous === void 0) {
|
|
2457
|
+
strongestByKey.set(key, constraint);
|
|
2458
|
+
continue;
|
|
2459
|
+
}
|
|
2460
|
+
const strength = compareConstraintStrength(constraint, previous);
|
|
2461
|
+
if (strength < 0) {
|
|
2462
|
+
const displayFieldName = formatPathTargetFieldName(
|
|
2463
|
+
fieldName,
|
|
2464
|
+
constraint.path?.segments ?? []
|
|
2465
|
+
);
|
|
2466
|
+
addConstraintBroadening(
|
|
2467
|
+
ctx,
|
|
2468
|
+
`Field "${displayFieldName}": ${describeConstraintTag(constraint)} (${String(constraint.value)}) is broader than earlier ${describeConstraintTag(previous)} (${String(previous.value)}). Constraints can only narrow.`,
|
|
2469
|
+
constraint.provenance,
|
|
2470
|
+
previous.provenance
|
|
2471
|
+
);
|
|
2472
|
+
continue;
|
|
2473
|
+
}
|
|
2474
|
+
if (strength <= 0) {
|
|
2475
|
+
continue;
|
|
2476
|
+
}
|
|
2477
|
+
strongestByKey.set(key, constraint);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
1832
2480
|
function checkNumericContradictions(ctx, fieldName, constraints) {
|
|
1833
2481
|
const min = findNumeric(constraints, "minimum");
|
|
1834
2482
|
const max = findNumeric(constraints, "maximum");
|
|
@@ -1915,6 +2563,25 @@ function checkAllowedMembersContradiction(ctx, fieldName, constraints) {
|
|
|
1915
2563
|
}
|
|
1916
2564
|
}
|
|
1917
2565
|
}
|
|
2566
|
+
function checkConstContradictions(ctx, fieldName, constraints) {
|
|
2567
|
+
const constConstraints = findConstConstraints(constraints);
|
|
2568
|
+
if (constConstraints.length < 2) return;
|
|
2569
|
+
const first = constConstraints[0];
|
|
2570
|
+
if (first === void 0) return;
|
|
2571
|
+
for (let i = 1; i < constConstraints.length; i++) {
|
|
2572
|
+
const current = constConstraints[i];
|
|
2573
|
+
if (current === void 0) continue;
|
|
2574
|
+
if (jsonValueEquals(first.value, current.value)) {
|
|
2575
|
+
continue;
|
|
2576
|
+
}
|
|
2577
|
+
addContradiction(
|
|
2578
|
+
ctx,
|
|
2579
|
+
`Field "${fieldName}": conflicting @const constraints require both ${JSON.stringify(first.value)} and ${JSON.stringify(current.value)}`,
|
|
2580
|
+
first.provenance,
|
|
2581
|
+
current.provenance
|
|
2582
|
+
);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
1918
2585
|
function typeLabel(type) {
|
|
1919
2586
|
switch (type.kind) {
|
|
1920
2587
|
case "primitive":
|
|
@@ -1925,6 +2592,8 @@ function typeLabel(type) {
|
|
|
1925
2592
|
return "array";
|
|
1926
2593
|
case "object":
|
|
1927
2594
|
return "object";
|
|
2595
|
+
case "record":
|
|
2596
|
+
return "record";
|
|
1928
2597
|
case "union":
|
|
1929
2598
|
return "union";
|
|
1930
2599
|
case "reference":
|
|
@@ -1939,85 +2608,173 @@ function typeLabel(type) {
|
|
|
1939
2608
|
}
|
|
1940
2609
|
}
|
|
1941
2610
|
}
|
|
1942
|
-
function
|
|
1943
|
-
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2611
|
+
function dereferenceType(ctx, type) {
|
|
2612
|
+
let current = type;
|
|
2613
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2614
|
+
while (current.kind === "reference") {
|
|
2615
|
+
if (seen.has(current.name)) {
|
|
2616
|
+
return current;
|
|
2617
|
+
}
|
|
2618
|
+
seen.add(current.name);
|
|
2619
|
+
const definition = ctx.typeRegistry[current.name];
|
|
2620
|
+
if (definition === void 0) {
|
|
2621
|
+
return current;
|
|
2622
|
+
}
|
|
2623
|
+
current = definition.type;
|
|
2624
|
+
}
|
|
2625
|
+
return current;
|
|
2626
|
+
}
|
|
2627
|
+
function resolvePathTargetType(ctx, type, segments) {
|
|
2628
|
+
const effectiveType = dereferenceType(ctx, type);
|
|
2629
|
+
if (segments.length === 0) {
|
|
2630
|
+
return { kind: "resolved", type: effectiveType };
|
|
2631
|
+
}
|
|
2632
|
+
if (effectiveType.kind === "array") {
|
|
2633
|
+
return resolvePathTargetType(ctx, effectiveType.items, segments);
|
|
2634
|
+
}
|
|
2635
|
+
if (effectiveType.kind === "object") {
|
|
2636
|
+
const [segment, ...rest] = segments;
|
|
2637
|
+
if (segment === void 0) {
|
|
2638
|
+
throw new Error("Invariant violation: object path traversal requires a segment");
|
|
2639
|
+
}
|
|
2640
|
+
const property = effectiveType.properties.find((prop) => prop.name === segment);
|
|
2641
|
+
if (property === void 0) {
|
|
2642
|
+
return { kind: "missing-property", segment };
|
|
2643
|
+
}
|
|
2644
|
+
return resolvePathTargetType(ctx, property.type, rest);
|
|
2645
|
+
}
|
|
2646
|
+
return { kind: "unresolvable", type: effectiveType };
|
|
2647
|
+
}
|
|
2648
|
+
function formatPathTargetFieldName(fieldName, path2) {
|
|
2649
|
+
return path2.length === 0 ? fieldName : `${fieldName}.${path2.join(".")}`;
|
|
2650
|
+
}
|
|
2651
|
+
function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
2652
|
+
const effectiveType = dereferenceType(ctx, type);
|
|
2653
|
+
const isNumber = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "number";
|
|
2654
|
+
const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
|
|
2655
|
+
const isArray = effectiveType.kind === "array";
|
|
2656
|
+
const isEnum = effectiveType.kind === "enum";
|
|
2657
|
+
const arrayItemType = effectiveType.kind === "array" ? dereferenceType(ctx, effectiveType.items) : void 0;
|
|
2658
|
+
const isStringArray = arrayItemType?.kind === "primitive" && arrayItemType.primitiveKind === "string";
|
|
2659
|
+
const label = typeLabel(effectiveType);
|
|
2660
|
+
const ck = constraint.constraintKind;
|
|
2661
|
+
switch (ck) {
|
|
2662
|
+
case "minimum":
|
|
2663
|
+
case "maximum":
|
|
2664
|
+
case "exclusiveMinimum":
|
|
2665
|
+
case "exclusiveMaximum":
|
|
2666
|
+
case "multipleOf": {
|
|
2667
|
+
if (!isNumber) {
|
|
1952
2668
|
addTypeMismatch(
|
|
1953
2669
|
ctx,
|
|
1954
|
-
`Field "${fieldName}":
|
|
2670
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on number fields, but field type is "${label}"`,
|
|
1955
2671
|
constraint.provenance
|
|
1956
2672
|
);
|
|
1957
2673
|
}
|
|
1958
|
-
|
|
2674
|
+
break;
|
|
1959
2675
|
}
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
ctx,
|
|
1970
|
-
`Field "${fieldName}": constraint "${ck}" is only valid on number fields, but field type is "${label}"`,
|
|
1971
|
-
constraint.provenance
|
|
1972
|
-
);
|
|
1973
|
-
}
|
|
1974
|
-
break;
|
|
2676
|
+
case "minLength":
|
|
2677
|
+
case "maxLength":
|
|
2678
|
+
case "pattern": {
|
|
2679
|
+
if (!isString && !isStringArray) {
|
|
2680
|
+
addTypeMismatch(
|
|
2681
|
+
ctx,
|
|
2682
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on string fields or string array items, but field type is "${label}"`,
|
|
2683
|
+
constraint.provenance
|
|
2684
|
+
);
|
|
1975
2685
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
2686
|
+
break;
|
|
2687
|
+
}
|
|
2688
|
+
case "minItems":
|
|
2689
|
+
case "maxItems":
|
|
2690
|
+
case "uniqueItems": {
|
|
2691
|
+
if (!isArray) {
|
|
2692
|
+
addTypeMismatch(
|
|
2693
|
+
ctx,
|
|
2694
|
+
`Field "${fieldName}": constraint "${ck}" is only valid on array fields, but field type is "${label}"`,
|
|
2695
|
+
constraint.provenance
|
|
2696
|
+
);
|
|
1987
2697
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2698
|
+
break;
|
|
2699
|
+
}
|
|
2700
|
+
case "allowedMembers": {
|
|
2701
|
+
if (!isEnum) {
|
|
2702
|
+
addTypeMismatch(
|
|
2703
|
+
ctx,
|
|
2704
|
+
`Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
|
|
2705
|
+
constraint.provenance
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
break;
|
|
2709
|
+
}
|
|
2710
|
+
case "const": {
|
|
2711
|
+
const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "boolean", "null"].includes(effectiveType.primitiveKind) || effectiveType.kind === "enum";
|
|
2712
|
+
if (!isPrimitiveConstType) {
|
|
2713
|
+
addTypeMismatch(
|
|
2714
|
+
ctx,
|
|
2715
|
+
`Field "${fieldName}": constraint "const" is only valid on primitive or enum fields, but field type is "${label}"`,
|
|
2716
|
+
constraint.provenance
|
|
2717
|
+
);
|
|
1998
2718
|
break;
|
|
1999
2719
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2720
|
+
if (effectiveType.kind === "primitive") {
|
|
2721
|
+
const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
|
|
2722
|
+
if (valueType !== effectiveType.primitiveKind) {
|
|
2002
2723
|
addTypeMismatch(
|
|
2003
2724
|
ctx,
|
|
2004
|
-
`Field "${fieldName}":
|
|
2725
|
+
`Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
|
|
2005
2726
|
constraint.provenance
|
|
2006
2727
|
);
|
|
2007
2728
|
}
|
|
2008
2729
|
break;
|
|
2009
2730
|
}
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2731
|
+
const memberValues = effectiveType.members.map((member) => member.value);
|
|
2732
|
+
if (!memberValues.some((member) => jsonValueEquals(member, constraint.value))) {
|
|
2733
|
+
addTypeMismatch(
|
|
2734
|
+
ctx,
|
|
2735
|
+
`Field "${fieldName}": @const value ${JSON.stringify(constraint.value)} is not one of the enum members`,
|
|
2736
|
+
constraint.provenance
|
|
2737
|
+
);
|
|
2013
2738
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2739
|
+
break;
|
|
2740
|
+
}
|
|
2741
|
+
case "custom": {
|
|
2742
|
+
checkCustomConstraint(ctx, fieldName, effectiveType, constraint);
|
|
2743
|
+
break;
|
|
2744
|
+
}
|
|
2745
|
+
default: {
|
|
2746
|
+
const _exhaustive = constraint;
|
|
2747
|
+
throw new Error(
|
|
2748
|
+
`Unhandled constraint kind: ${_exhaustive.constraintKind}`
|
|
2749
|
+
);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
function checkTypeApplicability(ctx, fieldName, type, constraints) {
|
|
2754
|
+
for (const constraint of constraints) {
|
|
2755
|
+
if (constraint.path) {
|
|
2756
|
+
const resolution = resolvePathTargetType(ctx, type, constraint.path.segments);
|
|
2757
|
+
const targetFieldName = formatPathTargetFieldName(fieldName, constraint.path.segments);
|
|
2758
|
+
if (resolution.kind === "missing-property") {
|
|
2759
|
+
addUnknownPathTarget(
|
|
2760
|
+
ctx,
|
|
2761
|
+
`Field "${targetFieldName}": path-targeted constraint "${constraint.constraintKind}" references unknown path segment "${resolution.segment}"`,
|
|
2762
|
+
constraint.provenance
|
|
2018
2763
|
);
|
|
2764
|
+
continue;
|
|
2019
2765
|
}
|
|
2766
|
+
if (resolution.kind === "unresolvable") {
|
|
2767
|
+
addTypeMismatch(
|
|
2768
|
+
ctx,
|
|
2769
|
+
`Field "${targetFieldName}": path-targeted constraint "${constraint.constraintKind}" is invalid because type "${typeLabel(resolution.type)}" cannot be traversed`,
|
|
2770
|
+
constraint.provenance
|
|
2771
|
+
);
|
|
2772
|
+
continue;
|
|
2773
|
+
}
|
|
2774
|
+
checkConstraintOnType(ctx, targetFieldName, resolution.type, constraint);
|
|
2775
|
+
continue;
|
|
2020
2776
|
}
|
|
2777
|
+
checkConstraintOnType(ctx, fieldName, type, constraint);
|
|
2021
2778
|
}
|
|
2022
2779
|
}
|
|
2023
2780
|
function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
@@ -2061,6 +2818,8 @@ function validateConstraints(ctx, name, type, constraints) {
|
|
|
2061
2818
|
checkNumericContradictions(ctx, name, constraints);
|
|
2062
2819
|
checkLengthContradictions(ctx, name, constraints);
|
|
2063
2820
|
checkAllowedMembersContradiction(ctx, name, constraints);
|
|
2821
|
+
checkConstContradictions(ctx, name, constraints);
|
|
2822
|
+
checkConstraintBroadening(ctx, name, constraints);
|
|
2064
2823
|
checkTypeApplicability(ctx, name, type, constraints);
|
|
2065
2824
|
}
|
|
2066
2825
|
function validateElement(ctx, element) {
|
|
@@ -2087,8 +2846,8 @@ function validateElement(ctx, element) {
|
|
|
2087
2846
|
function validateIR(ir, options) {
|
|
2088
2847
|
const ctx = {
|
|
2089
2848
|
diagnostics: [],
|
|
2090
|
-
|
|
2091
|
-
|
|
2849
|
+
extensionRegistry: options?.extensionRegistry,
|
|
2850
|
+
typeRegistry: ir.typeRegistry
|
|
2092
2851
|
};
|
|
2093
2852
|
for (const element of ir.elements) {
|
|
2094
2853
|
validateElement(ctx, element);
|
|
@@ -2142,7 +2901,7 @@ function createExtensionRegistry(extensions) {
|
|
|
2142
2901
|
}
|
|
2143
2902
|
|
|
2144
2903
|
// src/generators/method-schema.ts
|
|
2145
|
-
var
|
|
2904
|
+
var import_core4 = require("@formspec/core");
|
|
2146
2905
|
function typeToJsonSchema(type, checker) {
|
|
2147
2906
|
const typeRegistry = {};
|
|
2148
2907
|
const visiting = /* @__PURE__ */ new Set();
|
|
@@ -2150,7 +2909,7 @@ function typeToJsonSchema(type, checker) {
|
|
|
2150
2909
|
const fieldProvenance = { surface: "tsdoc", file: "", line: 0, column: 0 };
|
|
2151
2910
|
const ir = {
|
|
2152
2911
|
kind: "form-ir",
|
|
2153
|
-
irVersion:
|
|
2912
|
+
irVersion: import_core4.IR_VERSION,
|
|
2154
2913
|
elements: [
|
|
2155
2914
|
{
|
|
2156
2915
|
kind: "field",
|