@formspec/analysis 0.1.0-alpha.20 → 0.1.0-alpha.21
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/analysis.d.ts +69 -450
- package/dist/compiler-signatures.d.ts +48 -0
- package/dist/compiler-signatures.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/file-snapshots.d.ts +3 -0
- package/dist/file-snapshots.d.ts.map +1 -1
- package/dist/index.cjs +169 -2800
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +168 -2761
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +3995 -0
- package/dist/internal.cjs.map +1 -0
- package/dist/internal.d.ts +21 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +3902 -0
- package/dist/internal.js.map +1 -0
- package/dist/perf-tracing.d.ts +16 -0
- package/dist/perf-tracing.d.ts.map +1 -0
- package/dist/protocol.cjs +951 -0
- package/dist/protocol.cjs.map +1 -0
- package/dist/protocol.d.ts +4 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +904 -0
- package/dist/protocol.js.map +1 -0
- package/dist/semantic-protocol.d.ts +49 -1
- package/dist/semantic-protocol.d.ts.map +1 -1
- package/dist/tag-registry.d.ts +2 -0
- package/dist/tag-registry.d.ts.map +1 -1
- package/dist/workspace-runtime.d.ts +6 -0
- package/dist/workspace-runtime.d.ts.map +1 -1
- package/package.json +12 -2
package/dist/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
// src/path-target.ts
|
|
2
|
-
function extractPathTarget(text) {
|
|
3
|
-
const trimmed = text.trimStart();
|
|
4
|
-
const match = /^:([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)(?:\s+([\s\S]*))?$/u.exec(trimmed);
|
|
5
|
-
if (!match?.[1]) {
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
8
|
-
return {
|
|
9
|
-
path: { segments: match[1].split(".") },
|
|
10
|
-
remainingText: match[2] ?? ""
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
function formatPathTarget(path2) {
|
|
14
|
-
if ("segments" in path2) {
|
|
15
|
-
return path2.segments.join(".");
|
|
16
|
-
}
|
|
17
|
-
return path2.join(".");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
1
|
// src/tag-registry.ts
|
|
21
2
|
import {
|
|
22
3
|
BUILTIN_CONSTRAINT_DEFINITIONS,
|
|
23
4
|
normalizeConstraintTagName
|
|
24
5
|
} from "@formspec/core";
|
|
6
|
+
var FORM_SPEC_PLACEMENTS = [
|
|
7
|
+
"class",
|
|
8
|
+
"class-field",
|
|
9
|
+
"class-method",
|
|
10
|
+
"interface",
|
|
11
|
+
"interface-field",
|
|
12
|
+
"type-alias",
|
|
13
|
+
"type-alias-field",
|
|
14
|
+
"variable",
|
|
15
|
+
"function",
|
|
16
|
+
"function-parameter",
|
|
17
|
+
"method-parameter"
|
|
18
|
+
];
|
|
19
|
+
var FORM_SPEC_TARGET_KINDS = [
|
|
20
|
+
"none",
|
|
21
|
+
"path",
|
|
22
|
+
"member",
|
|
23
|
+
"variant"
|
|
24
|
+
];
|
|
25
25
|
var FIELD_PLACEMENTS = [
|
|
26
26
|
"class-field",
|
|
27
27
|
"interface-field",
|
|
@@ -601,2528 +601,203 @@ var EXTRA_TAG_DEFINITIONS = Object.fromEntries(
|
|
|
601
601
|
buildExtraTagDefinition(canonicalName, spec)
|
|
602
602
|
])
|
|
603
603
|
);
|
|
604
|
-
function normalizeFormSpecTagName(rawName) {
|
|
605
|
-
return normalizeConstraintTagName(rawName);
|
|
606
|
-
}
|
|
607
|
-
function getTagDefinition(rawName, extensions) {
|
|
608
|
-
const normalized = normalizeFormSpecTagName(rawName);
|
|
609
|
-
const builtin = BUILTIN_TAG_DEFINITIONS[normalized];
|
|
610
|
-
if (builtin !== void 0) {
|
|
611
|
-
return builtin;
|
|
612
|
-
}
|
|
613
|
-
const extra = EXTRA_TAG_DEFINITIONS[normalized];
|
|
614
|
-
if (extra !== void 0) {
|
|
615
|
-
return extra;
|
|
616
|
-
}
|
|
617
|
-
const extensionRegistration = getExtensionConstraintTags(extensions).find(
|
|
618
|
-
(tag) => tag.tagName === normalized
|
|
619
|
-
);
|
|
620
|
-
if (extensionRegistration === void 0) {
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
|
-
return {
|
|
624
|
-
canonicalName: extensionRegistration.tagName,
|
|
625
|
-
valueKind: null,
|
|
626
|
-
requiresArgument: true,
|
|
627
|
-
supportedTargets: ["none"],
|
|
628
|
-
allowDuplicates: true,
|
|
629
|
-
category: "constraint",
|
|
630
|
-
placements: FIELD_PLACEMENTS,
|
|
631
|
-
capabilities: [],
|
|
632
|
-
completionDetail: `Extension constraint tag from ${extensionRegistration.extensionId}`,
|
|
633
|
-
hoverMarkdown: [
|
|
634
|
-
`**@${extensionRegistration.tagName}** \`<value>\``,
|
|
635
|
-
"",
|
|
636
|
-
`Extension-defined constraint tag from \`${extensionRegistration.extensionId}\`.`,
|
|
637
|
-
"",
|
|
638
|
-
`**Signature:** \`@${extensionRegistration.tagName} <value>\``
|
|
639
|
-
].join("\n"),
|
|
640
|
-
signatures: [
|
|
641
|
-
{
|
|
642
|
-
label: `@${extensionRegistration.tagName} <value>`,
|
|
643
|
-
placements: FIELD_PLACEMENTS,
|
|
644
|
-
parameters: [{ kind: "value", label: "<value>" }]
|
|
645
|
-
}
|
|
646
|
-
]
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
function getConstraintTagDefinitions(extensions) {
|
|
650
|
-
const builtins = Object.values(BUILTIN_TAG_DEFINITIONS);
|
|
651
|
-
const custom = getExtensionConstraintTags(extensions).map((tag) => getTagDefinition(tag.tagName, extensions)).filter((tag) => tag !== null);
|
|
652
|
-
return [...builtins, ...custom];
|
|
653
|
-
}
|
|
654
|
-
function getAllTagDefinitions(extensions) {
|
|
655
|
-
const builtins = Object.values(BUILTIN_TAG_DEFINITIONS);
|
|
656
|
-
const extras = Object.values(EXTRA_TAG_DEFINITIONS);
|
|
657
|
-
const custom = getExtensionConstraintTags(extensions).map((tag) => getTagDefinition(tag.tagName, extensions)).filter((tag) => tag !== null);
|
|
658
|
-
return [...builtins, ...extras, ...custom];
|
|
659
|
-
}
|
|
660
|
-
function getTagHoverMarkdown(rawName, extensions) {
|
|
661
|
-
return getTagDefinition(rawName, extensions)?.hoverMarkdown ?? null;
|
|
662
|
-
}
|
|
663
|
-
function getExtensionConstraintTags(extensions) {
|
|
664
|
-
return extensions?.flatMap((extension) => {
|
|
665
|
-
const tagRecords = extension.constraintTags ?? [];
|
|
666
|
-
return tagRecords.map((tag) => ({
|
|
667
|
-
extensionId: extension.extensionId,
|
|
668
|
-
tagName: tag.tagName
|
|
669
|
-
}));
|
|
670
|
-
}) ?? [];
|
|
671
|
-
}
|
|
672
604
|
|
|
673
|
-
// src/
|
|
674
|
-
|
|
675
|
-
|
|
605
|
+
// src/semantic-protocol.ts
|
|
606
|
+
var FORMSPEC_ANALYSIS_PROTOCOL_VERSION = 1;
|
|
607
|
+
var FORMSPEC_ANALYSIS_SCHEMA_VERSION = 1;
|
|
608
|
+
function isObjectRecord(value) {
|
|
609
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
676
610
|
}
|
|
677
|
-
function
|
|
678
|
-
if (
|
|
679
|
-
return false;
|
|
680
|
-
}
|
|
681
|
-
const nextChar = lineText[index + 1];
|
|
682
|
-
if (nextChar === void 0 || !/[A-Za-z]/u.test(nextChar)) {
|
|
611
|
+
function isCommentSpan(value) {
|
|
612
|
+
if (!isObjectRecord(value)) {
|
|
683
613
|
return false;
|
|
684
614
|
}
|
|
685
|
-
const
|
|
686
|
-
return
|
|
687
|
-
}
|
|
688
|
-
function findTagEnd(lineText, index) {
|
|
689
|
-
let cursor = index + 1;
|
|
690
|
-
while (cursor < lineText.length && /[A-Za-z0-9]/u.test(lineText[cursor] ?? "")) {
|
|
691
|
-
cursor += 1;
|
|
692
|
-
}
|
|
693
|
-
return cursor;
|
|
615
|
+
const candidate = value;
|
|
616
|
+
return typeof candidate.start === "number" && typeof candidate.end === "number";
|
|
694
617
|
}
|
|
695
|
-
function
|
|
696
|
-
|
|
697
|
-
while (cursor > 0 && isWhitespace(lineText[cursor - 1])) {
|
|
698
|
-
cursor -= 1;
|
|
699
|
-
}
|
|
700
|
-
return cursor;
|
|
618
|
+
function isStringArray(value) {
|
|
619
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
701
620
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
const rawEnd = end >= line.text.length ? line.rawContentEnd : (line.rawOffsets[end - 1] ?? line.rawContentEnd - 1) + 1;
|
|
708
|
-
return {
|
|
709
|
-
start: baseOffset + rawStart,
|
|
710
|
-
end: baseOffset + rawEnd
|
|
711
|
-
};
|
|
621
|
+
var FORM_SPEC_PLACEMENT_VALUES = new Set(FORM_SPEC_PLACEMENTS);
|
|
622
|
+
var FORM_SPEC_TARGET_KIND_VALUES = new Set(FORM_SPEC_TARGET_KINDS);
|
|
623
|
+
function isPlacementValue(value) {
|
|
624
|
+
return typeof value === "string" && FORM_SPEC_PLACEMENT_VALUES.has(value);
|
|
712
625
|
}
|
|
713
|
-
function
|
|
714
|
-
|
|
715
|
-
return "variant";
|
|
716
|
-
}
|
|
717
|
-
if (targetText.includes(".")) {
|
|
718
|
-
return "path";
|
|
719
|
-
}
|
|
720
|
-
const definition = getTagDefinition(canonicalName, extensions);
|
|
721
|
-
const supportedTargets = definition?.supportedTargets.filter((target) => target !== "none") ?? [];
|
|
722
|
-
if (supportedTargets.includes("path")) {
|
|
723
|
-
return "path";
|
|
724
|
-
}
|
|
725
|
-
if (supportedTargets.includes("member") && supportedTargets.includes("variant")) {
|
|
726
|
-
return "ambiguous";
|
|
727
|
-
}
|
|
728
|
-
if (supportedTargets.includes("member")) {
|
|
729
|
-
return "member";
|
|
730
|
-
}
|
|
731
|
-
if (supportedTargets.includes("variant")) {
|
|
732
|
-
return "variant";
|
|
733
|
-
}
|
|
734
|
-
return "path";
|
|
626
|
+
function isTargetKindValue(value) {
|
|
627
|
+
return typeof value === "string" && FORM_SPEC_TARGET_KIND_VALUES.has(value);
|
|
735
628
|
}
|
|
736
|
-
function
|
|
737
|
-
|
|
738
|
-
return null;
|
|
739
|
-
}
|
|
740
|
-
let targetEnd = payloadStart + 1;
|
|
741
|
-
while (targetEnd < payloadEnd && !isWhitespace(line.text[targetEnd])) {
|
|
742
|
-
targetEnd += 1;
|
|
743
|
-
}
|
|
744
|
-
const fullText = line.text.slice(payloadStart, targetEnd);
|
|
745
|
-
const targetText = fullText.slice(1);
|
|
746
|
-
const parsedPath = extractPathTarget(fullText);
|
|
747
|
-
const specifierSpan = spanFromLine(line, payloadStart + 1, targetEnd, baseOffset);
|
|
748
|
-
return {
|
|
749
|
-
rawText: targetText,
|
|
750
|
-
valid: parsedPath !== null && parsedPath.remainingText === "",
|
|
751
|
-
kind: classifyTargetKind(canonicalName, targetText, extensions),
|
|
752
|
-
fullSpan: spanFromLine(line, payloadStart, targetEnd, baseOffset),
|
|
753
|
-
colonSpan: spanFromLine(line, payloadStart, payloadStart + 1, baseOffset),
|
|
754
|
-
span: specifierSpan,
|
|
755
|
-
path: parsedPath?.path ?? null,
|
|
756
|
-
localEnd: targetEnd
|
|
757
|
-
};
|
|
629
|
+
function isPlacementArray(value) {
|
|
630
|
+
return Array.isArray(value) && value.every(isPlacementValue);
|
|
758
631
|
}
|
|
759
|
-
function
|
|
760
|
-
|
|
761
|
-
const commentBodyStart = commentText.startsWith("/**") ? 3 : commentText.startsWith("/*") ? 2 : 0;
|
|
762
|
-
const commentBodyEnd = commentText.endsWith("*/") ? commentText.length - 2 : commentText.length;
|
|
763
|
-
let cursor = commentBodyStart;
|
|
764
|
-
while (cursor <= commentBodyEnd) {
|
|
765
|
-
const lineStart = cursor;
|
|
766
|
-
let lineEnd = cursor;
|
|
767
|
-
while (lineEnd < commentBodyEnd && commentText[lineEnd] !== "\n") {
|
|
768
|
-
lineEnd += 1;
|
|
769
|
-
}
|
|
770
|
-
let contentEnd = lineEnd;
|
|
771
|
-
if (contentEnd > lineStart && commentText[contentEnd - 1] === "\r") {
|
|
772
|
-
contentEnd -= 1;
|
|
773
|
-
}
|
|
774
|
-
let contentStart = lineStart;
|
|
775
|
-
while (contentStart < contentEnd && (commentText[contentStart] === " " || commentText[contentStart] === " ")) {
|
|
776
|
-
contentStart += 1;
|
|
777
|
-
}
|
|
778
|
-
if (contentStart < contentEnd && commentText[contentStart] === "*") {
|
|
779
|
-
contentStart += 1;
|
|
780
|
-
while (contentStart < contentEnd && (commentText[contentStart] === " " || commentText[contentStart] === " ")) {
|
|
781
|
-
contentStart += 1;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
const rawOffsets = [];
|
|
785
|
-
let text = "";
|
|
786
|
-
for (let index = contentStart; index < contentEnd; index += 1) {
|
|
787
|
-
rawOffsets.push(index);
|
|
788
|
-
text += commentText[index] ?? "";
|
|
789
|
-
}
|
|
790
|
-
projections.push({
|
|
791
|
-
text,
|
|
792
|
-
rawOffsets,
|
|
793
|
-
rawContentEnd: contentEnd
|
|
794
|
-
});
|
|
795
|
-
if (lineEnd >= commentBodyEnd) {
|
|
796
|
-
break;
|
|
797
|
-
}
|
|
798
|
-
cursor = lineEnd + 1;
|
|
799
|
-
}
|
|
800
|
-
return projections;
|
|
632
|
+
function isTargetKindArray(value) {
|
|
633
|
+
return Array.isArray(value) && value.every(isTargetKindValue);
|
|
801
634
|
}
|
|
802
|
-
function
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
for (const line of projectCommentLines(commentText)) {
|
|
806
|
-
const tagStarts = [];
|
|
807
|
-
for (let index = 0; index < line.text.length; index += 1) {
|
|
808
|
-
if (isTagStart(line.text, index)) {
|
|
809
|
-
tagStarts.push(index);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
for (let tagIndex = 0; tagIndex < tagStarts.length; tagIndex += 1) {
|
|
813
|
-
const tagStart = tagStarts[tagIndex];
|
|
814
|
-
if (tagStart === void 0) {
|
|
815
|
-
continue;
|
|
816
|
-
}
|
|
817
|
-
const tagEnd = findTagEnd(line.text, tagStart);
|
|
818
|
-
const nextTagStart = tagStarts[tagIndex + 1] ?? line.text.length;
|
|
819
|
-
const trimmedTagSegmentEnd = trimTrailingWhitespace(line.text, nextTagStart);
|
|
820
|
-
const rawName = line.text.slice(tagStart + 1, tagEnd);
|
|
821
|
-
const canonicalName = normalizeFormSpecTagName(rawName);
|
|
822
|
-
let payloadStart = tagEnd;
|
|
823
|
-
while (payloadStart < trimmedTagSegmentEnd && isWhitespace(line.text[payloadStart])) {
|
|
824
|
-
payloadStart += 1;
|
|
825
|
-
}
|
|
826
|
-
const target = parseTargetSpecifier(
|
|
827
|
-
line,
|
|
828
|
-
payloadStart,
|
|
829
|
-
trimmedTagSegmentEnd,
|
|
830
|
-
canonicalName,
|
|
831
|
-
baseOffset,
|
|
832
|
-
options?.extensions
|
|
833
|
-
);
|
|
834
|
-
let valueStart = payloadStart;
|
|
835
|
-
if (target !== null) {
|
|
836
|
-
valueStart = target.localEnd;
|
|
837
|
-
while (valueStart < trimmedTagSegmentEnd && isWhitespace(line.text[valueStart])) {
|
|
838
|
-
valueStart += 1;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
const payloadSpan = payloadStart < trimmedTagSegmentEnd ? spanFromLine(line, payloadStart, trimmedTagSegmentEnd, baseOffset) : null;
|
|
842
|
-
const valueSpan = valueStart < trimmedTagSegmentEnd ? spanFromLine(line, valueStart, trimmedTagSegmentEnd, baseOffset) : null;
|
|
843
|
-
const parsedTarget = target === null ? null : {
|
|
844
|
-
rawText: target.rawText,
|
|
845
|
-
valid: target.valid,
|
|
846
|
-
kind: target.kind,
|
|
847
|
-
fullSpan: target.fullSpan,
|
|
848
|
-
colonSpan: target.colonSpan,
|
|
849
|
-
span: target.span,
|
|
850
|
-
path: target.path
|
|
851
|
-
};
|
|
852
|
-
tags.push({
|
|
853
|
-
rawTagName: rawName,
|
|
854
|
-
normalizedTagName: canonicalName,
|
|
855
|
-
recognized: getTagDefinition(canonicalName, options?.extensions) !== null,
|
|
856
|
-
fullSpan: spanFromLine(line, tagStart, trimmedTagSegmentEnd, baseOffset),
|
|
857
|
-
tagNameSpan: spanFromLine(line, tagStart, tagEnd, baseOffset),
|
|
858
|
-
payloadSpan,
|
|
859
|
-
colonSpan: parsedTarget?.colonSpan ?? null,
|
|
860
|
-
target: parsedTarget,
|
|
861
|
-
argumentSpan: valueSpan,
|
|
862
|
-
argumentText: valueSpan === null ? "" : commentText.slice(valueSpan.start - baseOffset, valueSpan.end - baseOffset)
|
|
863
|
-
});
|
|
864
|
-
}
|
|
635
|
+
function isIpcEndpoint(value) {
|
|
636
|
+
if (!isObjectRecord(value)) {
|
|
637
|
+
return false;
|
|
865
638
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
offset: baseOffset,
|
|
869
|
-
tags
|
|
870
|
-
};
|
|
639
|
+
const candidate = value;
|
|
640
|
+
return (candidate.kind === "unix-socket" || candidate.kind === "windows-pipe") && typeof candidate.address === "string";
|
|
871
641
|
}
|
|
872
|
-
function
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const [tag] = parsed.tags;
|
|
876
|
-
if (tag === void 0) {
|
|
877
|
-
throw new Error(`Unable to parse synthetic tag syntax for @${rawTagName}`);
|
|
642
|
+
function isSerializedTagDefinition(value) {
|
|
643
|
+
if (!isObjectRecord(value)) {
|
|
644
|
+
return false;
|
|
878
645
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
function sliceCommentSpan(commentText, span, options) {
|
|
882
|
-
const baseOffset = options?.offset ?? 0;
|
|
883
|
-
return commentText.slice(span.start - baseOffset, span.end - baseOffset);
|
|
646
|
+
const candidate = value;
|
|
647
|
+
return typeof candidate.canonicalName === "string" && typeof candidate.completionDetail === "string" && typeof candidate.hoverMarkdown === "string";
|
|
884
648
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
function stripNullishUnion(type) {
|
|
889
|
-
if (!type.isUnion()) {
|
|
890
|
-
return type;
|
|
891
|
-
}
|
|
892
|
-
const nonNullish = type.types.filter(
|
|
893
|
-
(member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0
|
|
894
|
-
);
|
|
895
|
-
if (nonNullish.length === 1 && nonNullish[0] !== void 0) {
|
|
896
|
-
return nonNullish[0];
|
|
649
|
+
function isSerializedTagSignature(value) {
|
|
650
|
+
if (!isObjectRecord(value)) {
|
|
651
|
+
return false;
|
|
897
652
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
function isIntersectionWithBase(type, predicate) {
|
|
901
|
-
return type.isIntersection() && type.types.some((member) => predicate(member));
|
|
653
|
+
const candidate = value;
|
|
654
|
+
return typeof candidate.label === "string" && isPlacementArray(candidate.placements);
|
|
902
655
|
}
|
|
903
|
-
function
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
return true;
|
|
907
|
-
}
|
|
908
|
-
if (stripped.isUnion()) {
|
|
909
|
-
return stripped.types.every((member) => isNumberLike(member));
|
|
656
|
+
function isSerializedCommentTargetSpecifier(value) {
|
|
657
|
+
if (!isObjectRecord(value)) {
|
|
658
|
+
return false;
|
|
910
659
|
}
|
|
911
|
-
|
|
660
|
+
const candidate = value;
|
|
661
|
+
return typeof candidate.rawText === "string" && typeof candidate.valid === "boolean" && typeof candidate.kind === "string" && isCommentSpan(candidate.fullSpan) && isCommentSpan(candidate.colonSpan) && isCommentSpan(candidate.span);
|
|
912
662
|
}
|
|
913
|
-
function
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
return true;
|
|
917
|
-
}
|
|
918
|
-
if (stripped.isUnion()) {
|
|
919
|
-
return stripped.types.every((member) => isStringLike(member));
|
|
663
|
+
function isSerializedTagSemanticContext(value) {
|
|
664
|
+
if (!isObjectRecord(value)) {
|
|
665
|
+
return false;
|
|
920
666
|
}
|
|
921
|
-
|
|
667
|
+
const candidate = value;
|
|
668
|
+
return typeof candidate.tagName === "string" && (candidate.tagDefinition === null || isSerializedTagDefinition(candidate.tagDefinition)) && (candidate.placement === null || isPlacementValue(candidate.placement)) && isTargetKindArray(candidate.supportedTargets) && isStringArray(candidate.targetCompletions) && isStringArray(candidate.compatiblePathTargets) && isStringArray(candidate.valueLabels) && Array.isArray(candidate.signatures) && candidate.signatures.every(isSerializedTagSignature) && (candidate.tagHoverMarkdown === null || typeof candidate.tagHoverMarkdown === "string") && (candidate.targetHoverMarkdown === null || typeof candidate.targetHoverMarkdown === "string") && (candidate.argumentHoverMarkdown === null || typeof candidate.argumentHoverMarkdown === "string");
|
|
922
669
|
}
|
|
923
|
-
function
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
return true;
|
|
927
|
-
}
|
|
928
|
-
if (stripped.isUnion()) {
|
|
929
|
-
return stripped.types.every((member) => isBooleanLike(member));
|
|
670
|
+
function isSerializedCompletionContext(value) {
|
|
671
|
+
if (!isObjectRecord(value)) {
|
|
672
|
+
return false;
|
|
930
673
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
const stripped = stripNullishUnion(type);
|
|
935
|
-
if (stripped.flags & ts.TypeFlags.Null) {
|
|
936
|
-
return true;
|
|
674
|
+
const candidate = value;
|
|
675
|
+
if (typeof candidate.kind !== "string") {
|
|
676
|
+
return false;
|
|
937
677
|
}
|
|
938
|
-
|
|
939
|
-
|
|
678
|
+
switch (candidate.kind) {
|
|
679
|
+
case "tag-name":
|
|
680
|
+
return typeof candidate.prefix === "string" && Array.isArray(candidate.availableTags) ? candidate.availableTags.every(isSerializedTagDefinition) : false;
|
|
681
|
+
case "target":
|
|
682
|
+
return isSerializedTagSemanticContext(candidate.semantic);
|
|
683
|
+
case "argument":
|
|
684
|
+
return isSerializedTagSemanticContext(candidate.semantic) && isStringArray(candidate.valueLabels);
|
|
685
|
+
case "none":
|
|
686
|
+
return true;
|
|
687
|
+
default:
|
|
688
|
+
return false;
|
|
940
689
|
}
|
|
941
|
-
return isIntersectionWithBase(stripped, isNullLike);
|
|
942
690
|
}
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
return true;
|
|
691
|
+
function isSerializedHoverInfo(value) {
|
|
692
|
+
if (!isObjectRecord(value)) {
|
|
693
|
+
return false;
|
|
947
694
|
}
|
|
948
|
-
const
|
|
949
|
-
return
|
|
950
|
-
}
|
|
951
|
-
function isStringLiteralUnion(type) {
|
|
952
|
-
const stripped = stripNullishUnion(type);
|
|
953
|
-
return stripped.isUnion() && stripped.types.length > 0 && stripped.types.every(isStringLike);
|
|
695
|
+
const candidate = value;
|
|
696
|
+
return (candidate.kind === "tag-name" || candidate.kind === "target" || candidate.kind === "argument") && typeof candidate.markdown === "string";
|
|
954
697
|
}
|
|
955
|
-
function
|
|
956
|
-
|
|
957
|
-
return !isArrayLike(stripped, checker) && (stripped.flags & ts.TypeFlags.Object) !== 0;
|
|
698
|
+
function hasCurrentProtocolVersion(value) {
|
|
699
|
+
return isObjectRecord(value) && value["protocolVersion"] === FORMSPEC_ANALYSIS_PROTOCOL_VERSION;
|
|
958
700
|
}
|
|
959
|
-
function
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
return true;
|
|
963
|
-
}
|
|
964
|
-
if (stripped.isUnion()) {
|
|
965
|
-
return stripped.types.every((member) => isJsonLike(member, checker));
|
|
966
|
-
}
|
|
967
|
-
if (isArrayLike(stripped, checker) || isObjectLike(stripped, checker)) {
|
|
968
|
-
return true;
|
|
701
|
+
function isAnalysisDiagnostic(value) {
|
|
702
|
+
if (!isObjectRecord(value)) {
|
|
703
|
+
return false;
|
|
969
704
|
}
|
|
970
|
-
|
|
705
|
+
const candidate = value;
|
|
706
|
+
return typeof candidate.code === "string" && typeof candidate.message === "string" && isCommentSpan(candidate.range) && (candidate.severity === "error" || candidate.severity === "warning" || candidate.severity === "info");
|
|
971
707
|
}
|
|
972
|
-
function
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
return null;
|
|
708
|
+
function isAnalysisTagSnapshot(value) {
|
|
709
|
+
if (!isObjectRecord(value)) {
|
|
710
|
+
return false;
|
|
976
711
|
}
|
|
977
|
-
|
|
712
|
+
const candidate = value;
|
|
713
|
+
return typeof candidate.rawTagName === "string" && typeof candidate.normalizedTagName === "string" && typeof candidate.recognized === "boolean" && isCommentSpan(candidate.fullSpan) && isCommentSpan(candidate.tagNameSpan) && (candidate.payloadSpan === null || isCommentSpan(candidate.payloadSpan)) && (candidate.target === null || isSerializedCommentTargetSpecifier(candidate.target)) && (candidate.argumentSpan === null || isCommentSpan(candidate.argumentSpan)) && typeof candidate.argumentText === "string" && isSerializedTagSemanticContext(candidate.semantic);
|
|
978
714
|
}
|
|
979
|
-
function
|
|
980
|
-
if (
|
|
981
|
-
|
|
982
|
-
if (ts.isMethodDeclaration(node)) return "class-method";
|
|
983
|
-
if (ts.isInterfaceDeclaration(node)) return "interface";
|
|
984
|
-
if (ts.isPropertySignature(node)) return "interface-field";
|
|
985
|
-
if (ts.isTypeAliasDeclaration(node)) return "type-alias";
|
|
986
|
-
if (ts.isVariableDeclaration(node)) return "variable";
|
|
987
|
-
if (ts.isFunctionDeclaration(node)) return "function";
|
|
988
|
-
if (ts.isParameter(node)) {
|
|
989
|
-
if (ts.isMethodDeclaration(node.parent) || ts.isConstructorDeclaration(node.parent)) {
|
|
990
|
-
return "method-parameter";
|
|
991
|
-
}
|
|
992
|
-
return "function-parameter";
|
|
715
|
+
function isAnalysisCommentSnapshot(value) {
|
|
716
|
+
if (!isObjectRecord(value)) {
|
|
717
|
+
return false;
|
|
993
718
|
}
|
|
994
|
-
|
|
719
|
+
const candidate = value;
|
|
720
|
+
return isCommentSpan(candidate.commentSpan) && isCommentSpan(candidate.declarationSpan) && (candidate.placement === null || isPlacementValue(candidate.placement)) && (candidate.subjectType === null || typeof candidate.subjectType === "string") && (candidate.hostType === null || typeof candidate.hostType === "string") && Array.isArray(candidate.tags) && candidate.tags.every(isAnalysisTagSnapshot);
|
|
995
721
|
}
|
|
996
|
-
function
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
capabilities.add("numeric-comparable");
|
|
1000
|
-
}
|
|
1001
|
-
if (isStringLike(type)) {
|
|
1002
|
-
capabilities.add("string-like");
|
|
1003
|
-
}
|
|
1004
|
-
if (isJsonLike(type, checker)) {
|
|
1005
|
-
capabilities.add("json-like");
|
|
1006
|
-
}
|
|
1007
|
-
if (isArrayLike(type, checker)) {
|
|
1008
|
-
capabilities.add("array-like");
|
|
1009
|
-
}
|
|
1010
|
-
if (isStringLiteralUnion(type)) {
|
|
1011
|
-
capabilities.add("enum-member-addressable");
|
|
1012
|
-
}
|
|
1013
|
-
if (isObjectLike(type, checker)) {
|
|
1014
|
-
capabilities.add("object-like");
|
|
722
|
+
function isAnalysisFileSnapshot(value) {
|
|
723
|
+
if (!isObjectRecord(value)) {
|
|
724
|
+
return false;
|
|
1015
725
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
function hasTypeSemanticCapability(type, checker, capability) {
|
|
1019
|
-
return getTypeSemanticCapabilities(type, checker).includes(capability);
|
|
726
|
+
const candidate = value;
|
|
727
|
+
return typeof candidate.filePath === "string" && typeof candidate.sourceHash === "string" && typeof candidate.generatedAt === "string" && Array.isArray(candidate.comments) && candidate.comments.every(isAnalysisCommentSnapshot) && Array.isArray(candidate.diagnostics) && candidate.diagnostics.every(isAnalysisDiagnostic);
|
|
1020
728
|
}
|
|
1021
|
-
function
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
return {
|
|
1025
|
-
kind: "resolved",
|
|
1026
|
-
type: stripped
|
|
1027
|
-
};
|
|
1028
|
-
}
|
|
1029
|
-
const arrayElementType = getArrayElementType(stripped, checker);
|
|
1030
|
-
if (arrayElementType !== null) {
|
|
1031
|
-
return resolvePathTargetType(arrayElementType, checker, segments);
|
|
1032
|
-
}
|
|
1033
|
-
if ((stripped.flags & ts.TypeFlags.Object) === 0) {
|
|
1034
|
-
return {
|
|
1035
|
-
kind: "unresolvable",
|
|
1036
|
-
type: stripped
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
const [segment, ...rest] = segments;
|
|
1040
|
-
if (segment === void 0) {
|
|
1041
|
-
throw new Error("Invariant violation: path traversal requires a segment");
|
|
1042
|
-
}
|
|
1043
|
-
const property = stripped.getProperty(segment);
|
|
1044
|
-
if (property === void 0) {
|
|
1045
|
-
return {
|
|
1046
|
-
kind: "missing-property",
|
|
1047
|
-
segment
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
1051
|
-
if (declaration === void 0) {
|
|
1052
|
-
return {
|
|
1053
|
-
kind: "unresolvable",
|
|
1054
|
-
type: stripped
|
|
1055
|
-
};
|
|
729
|
+
function isFormSpecAnalysisManifest(value) {
|
|
730
|
+
if (!isObjectRecord(value)) {
|
|
731
|
+
return false;
|
|
1056
732
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
checker,
|
|
1060
|
-
rest
|
|
1061
|
-
);
|
|
733
|
+
const candidate = value;
|
|
734
|
+
return candidate.protocolVersion === FORMSPEC_ANALYSIS_PROTOCOL_VERSION && candidate.analysisSchemaVersion === FORMSPEC_ANALYSIS_SCHEMA_VERSION && typeof candidate.workspaceRoot === "string" && typeof candidate.workspaceId === "string" && isIpcEndpoint(candidate.endpoint) && typeof candidate.typescriptVersion === "string" && typeof candidate.extensionFingerprint === "string" && typeof candidate.generation === "number" && typeof candidate.updatedAt === "string";
|
|
1062
735
|
}
|
|
1063
|
-
function
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
return [];
|
|
736
|
+
function isFormSpecSemanticQuery(value) {
|
|
737
|
+
if (!hasCurrentProtocolVersion(value)) {
|
|
738
|
+
return false;
|
|
1067
739
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
suggestions.push(prefix.join("."));
|
|
740
|
+
const candidate = value;
|
|
741
|
+
if (typeof candidate.kind !== "string") {
|
|
742
|
+
return false;
|
|
1072
743
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
744
|
+
switch (candidate.kind) {
|
|
745
|
+
case "health":
|
|
746
|
+
return true;
|
|
747
|
+
case "completion":
|
|
748
|
+
case "hover":
|
|
749
|
+
return typeof candidate.filePath === "string" && typeof candidate.offset === "number";
|
|
750
|
+
case "diagnostics":
|
|
751
|
+
case "file-snapshot":
|
|
752
|
+
return typeof candidate.filePath === "string";
|
|
753
|
+
default:
|
|
754
|
+
return false;
|
|
1083
755
|
}
|
|
1084
|
-
return suggestions;
|
|
1085
|
-
}
|
|
1086
|
-
function collectCompatiblePathTargets(type, checker, capability) {
|
|
1087
|
-
return collectPropertyPaths(type, checker, capability, [], /* @__PURE__ */ new Set());
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
// src/cursor-context.ts
|
|
1091
|
-
function isWordChar(char) {
|
|
1092
|
-
return char !== void 0 && /[A-Za-z0-9]/u.test(char);
|
|
1093
|
-
}
|
|
1094
|
-
function isWhitespaceLike(char) {
|
|
1095
|
-
return char === void 0 || /\s/u.test(char) || char === "*";
|
|
1096
756
|
}
|
|
1097
|
-
function
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
function filterSignaturesByPlacement(signatures, placement) {
|
|
1101
|
-
if (placement === void 0 || placement === null) {
|
|
1102
|
-
return signatures;
|
|
757
|
+
function isFormSpecSemanticResponse(value) {
|
|
758
|
+
if (!hasCurrentProtocolVersion(value)) {
|
|
759
|
+
return false;
|
|
1103
760
|
}
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
function getCompatiblePathTargetsForSignatures(signatures, checker, subjectType) {
|
|
1108
|
-
if (checker === void 0 || subjectType === void 0) {
|
|
1109
|
-
return [];
|
|
761
|
+
const candidate = value;
|
|
762
|
+
if (typeof candidate.kind !== "string") {
|
|
763
|
+
return false;
|
|
1110
764
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
)
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
765
|
+
switch (candidate.kind) {
|
|
766
|
+
case "health":
|
|
767
|
+
return isFormSpecAnalysisManifest(candidate.manifest);
|
|
768
|
+
case "completion":
|
|
769
|
+
return typeof candidate.sourceHash === "string" && isSerializedCompletionContext(candidate.context);
|
|
770
|
+
case "hover":
|
|
771
|
+
return typeof candidate.sourceHash === "string" && (candidate.hover === null || isSerializedHoverInfo(candidate.hover));
|
|
772
|
+
case "diagnostics":
|
|
773
|
+
return typeof candidate.sourceHash === "string" && Array.isArray(candidate.diagnostics) && candidate.diagnostics.every(isAnalysisDiagnostic);
|
|
774
|
+
case "file-snapshot":
|
|
775
|
+
return candidate.snapshot === null || isAnalysisFileSnapshot(candidate.snapshot);
|
|
776
|
+
case "error":
|
|
777
|
+
return typeof candidate.error === "string";
|
|
778
|
+
default:
|
|
779
|
+
return false;
|
|
1125
780
|
}
|
|
1126
|
-
return [...suggestions].sort();
|
|
1127
781
|
}
|
|
1128
|
-
function
|
|
1129
|
-
|
|
1130
|
-
for (
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
case "target-path":
|
|
1134
|
-
supportedTargets.add("path");
|
|
1135
|
-
break;
|
|
1136
|
-
case "target-member":
|
|
1137
|
-
supportedTargets.add("member");
|
|
1138
|
-
break;
|
|
1139
|
-
case "target-variant":
|
|
1140
|
-
supportedTargets.add("variant");
|
|
1141
|
-
break;
|
|
1142
|
-
default:
|
|
1143
|
-
break;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
782
|
+
function computeFormSpecTextHash(text) {
|
|
783
|
+
let hash = 2166136261;
|
|
784
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
785
|
+
hash ^= text.charCodeAt(index);
|
|
786
|
+
hash = Math.imul(hash, 16777619);
|
|
1146
787
|
}
|
|
1147
|
-
return
|
|
788
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1148
789
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
for (const parameter of signature.parameters) {
|
|
1153
|
-
switch (parameter.kind) {
|
|
1154
|
-
case "target-path":
|
|
1155
|
-
for (const target of compatiblePathTargets) {
|
|
1156
|
-
completions.add(target);
|
|
1157
|
-
}
|
|
1158
|
-
break;
|
|
1159
|
-
case "target-variant":
|
|
1160
|
-
completions.add("singular");
|
|
1161
|
-
completions.add("plural");
|
|
1162
|
-
break;
|
|
1163
|
-
default:
|
|
1164
|
-
break;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
790
|
+
function serializeCommentTargetSpecifier(target) {
|
|
791
|
+
if (target === null) {
|
|
792
|
+
return null;
|
|
1167
793
|
}
|
|
1168
|
-
return [...completions];
|
|
1169
|
-
}
|
|
1170
|
-
function getCommentTagSemanticContext(tag, options) {
|
|
1171
|
-
const tagDefinition = getTagDefinition(tag.normalizedTagName, options?.extensions);
|
|
1172
|
-
const signatures = filterSignaturesByPlacement(
|
|
1173
|
-
tagDefinition?.signatures ?? [],
|
|
1174
|
-
options?.placement
|
|
1175
|
-
);
|
|
1176
|
-
const compatiblePathTargets = getCompatiblePathTargetsForSignatures(
|
|
1177
|
-
signatures,
|
|
1178
|
-
options?.checker,
|
|
1179
|
-
options?.subjectType
|
|
1180
|
-
);
|
|
1181
|
-
const semantic = {
|
|
1182
|
-
tag,
|
|
1183
|
-
tagDefinition,
|
|
1184
|
-
placement: options?.placement ?? null,
|
|
1185
|
-
signatures,
|
|
1186
|
-
supportedTargets: getSupportedTargets(signatures),
|
|
1187
|
-
targetCompletions: getTargetCompletions(signatures, compatiblePathTargets),
|
|
1188
|
-
compatiblePathTargets,
|
|
1189
|
-
valueLabels: getValueLabels(signatures),
|
|
1190
|
-
tagHoverMarkdown: tagDefinition?.hoverMarkdown ?? null,
|
|
1191
|
-
targetHoverMarkdown: null,
|
|
1192
|
-
argumentHoverMarkdown: null
|
|
1193
|
-
};
|
|
1194
794
|
return {
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
const labels = /* @__PURE__ */ new Set();
|
|
1202
|
-
for (const signature of signatures) {
|
|
1203
|
-
for (const parameter of signature.parameters) {
|
|
1204
|
-
if (parameter.kind === "value") {
|
|
1205
|
-
labels.add(parameter.label);
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
return [...labels];
|
|
1210
|
-
}
|
|
1211
|
-
function getTargetKindLabels(supportedTargets) {
|
|
1212
|
-
const labels = supportedTargets.filter((kind) => kind !== "none").map((kind) => `\`${kind}\``);
|
|
1213
|
-
return labels.length === 0 ? "none" : labels.join(", ");
|
|
1214
|
-
}
|
|
1215
|
-
function buildTargetHoverMarkdown(semantic) {
|
|
1216
|
-
if (semantic.tagDefinition === null) {
|
|
1217
|
-
return null;
|
|
1218
|
-
}
|
|
1219
|
-
const currentTarget = semantic.tag.target?.rawText ?? "";
|
|
1220
|
-
const lines = [
|
|
1221
|
-
`**Target for @${semantic.tagDefinition.canonicalName}**`,
|
|
1222
|
-
"",
|
|
1223
|
-
`Supported target forms: ${getTargetKindLabels(semantic.supportedTargets)}`
|
|
1224
|
-
];
|
|
1225
|
-
if (currentTarget !== "") {
|
|
1226
|
-
lines.push("", `Current target: \`:${currentTarget}\``);
|
|
1227
|
-
}
|
|
1228
|
-
const MAX_HOVER_PATH_TARGETS = 8;
|
|
1229
|
-
if (semantic.compatiblePathTargets.length > 0) {
|
|
1230
|
-
lines.push("", "**Compatible path targets:**");
|
|
1231
|
-
for (const target of semantic.compatiblePathTargets.slice(0, MAX_HOVER_PATH_TARGETS)) {
|
|
1232
|
-
lines.push(`- \`:${target}\``);
|
|
1233
|
-
}
|
|
1234
|
-
} else if (semantic.supportedTargets.includes("variant")) {
|
|
1235
|
-
lines.push("", "Use `:singular` or `:plural` for variant-specific names.");
|
|
1236
|
-
} else if (semantic.supportedTargets.includes("path")) {
|
|
1237
|
-
lines.push(
|
|
1238
|
-
"",
|
|
1239
|
-
"Type-aware path completions become available when TypeScript binding is provided."
|
|
1240
|
-
);
|
|
1241
|
-
}
|
|
1242
|
-
return lines.join("\n");
|
|
1243
|
-
}
|
|
1244
|
-
function buildArgumentHoverMarkdown(semantic) {
|
|
1245
|
-
if (semantic.tagDefinition === null) {
|
|
1246
|
-
return null;
|
|
1247
|
-
}
|
|
1248
|
-
const valueLabels = getValueLabels(semantic.signatures);
|
|
1249
|
-
const formattedValueLabels = valueLabels.map((label) => `\`${label}\``);
|
|
1250
|
-
const soleSignature = semantic.signatures.length === 1 ? semantic.signatures[0] : void 0;
|
|
1251
|
-
const signatureLines = semantic.signatures.length === 0 ? [] : soleSignature !== void 0 ? [`**Signature:** \`${soleSignature.label}\``] : [
|
|
1252
|
-
"**Signatures:**",
|
|
1253
|
-
...semantic.signatures.map((signature) => `- \`${signature.label}\``)
|
|
1254
|
-
];
|
|
1255
|
-
return [
|
|
1256
|
-
`**Argument for @${semantic.tagDefinition.canonicalName}**`,
|
|
1257
|
-
"",
|
|
1258
|
-
`Expected value: ${formattedValueLabels.join(" or ") || "`<value>`"}`,
|
|
1259
|
-
"",
|
|
1260
|
-
...signatureLines
|
|
1261
|
-
].join("\n");
|
|
1262
|
-
}
|
|
1263
|
-
function findEnclosingDocComment(documentText, offset, options) {
|
|
1264
|
-
const commentPattern = /\/\*\*[\s\S]*?\*\//gu;
|
|
1265
|
-
for (const match of documentText.matchAll(commentPattern)) {
|
|
1266
|
-
const fullMatch = match[0];
|
|
1267
|
-
const index = match.index;
|
|
1268
|
-
const start = index;
|
|
1269
|
-
const end = start + fullMatch.length;
|
|
1270
|
-
if (offset >= start && offset <= end) {
|
|
1271
|
-
return {
|
|
1272
|
-
text: fullMatch,
|
|
1273
|
-
start,
|
|
1274
|
-
end,
|
|
1275
|
-
parsed: parseCommentBlock(fullMatch, {
|
|
1276
|
-
offset: start,
|
|
1277
|
-
...options?.extensions !== void 0 ? { extensions: options.extensions } : {}
|
|
1278
|
-
})
|
|
1279
|
-
};
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
return null;
|
|
1283
|
-
}
|
|
1284
|
-
function findCommentTagAtOffset(documentText, offset, options) {
|
|
1285
|
-
const comment = findEnclosingDocComment(documentText, offset, options);
|
|
1286
|
-
if (comment === null) {
|
|
1287
|
-
return null;
|
|
1288
|
-
}
|
|
1289
|
-
return comment.parsed.tags.find((tag) => containsOffset(tag, offset)) ?? null;
|
|
1290
|
-
}
|
|
1291
|
-
function getCommentCursorTargetAtOffset(documentText, offset, options) {
|
|
1292
|
-
const comment = findEnclosingDocComment(documentText, offset, options);
|
|
1293
|
-
if (comment === null) {
|
|
1294
|
-
return null;
|
|
1295
|
-
}
|
|
1296
|
-
for (const tag of comment.parsed.tags) {
|
|
1297
|
-
if (containsOffset(tag, offset)) {
|
|
1298
|
-
return {
|
|
1299
|
-
kind: "tag-name",
|
|
1300
|
-
tag
|
|
1301
|
-
};
|
|
1302
|
-
}
|
|
1303
|
-
if (tag.colonSpan !== null && offset >= tag.colonSpan.start && offset <= tag.colonSpan.end) {
|
|
1304
|
-
return {
|
|
1305
|
-
kind: "colon",
|
|
1306
|
-
tag
|
|
1307
|
-
};
|
|
1308
|
-
}
|
|
1309
|
-
if (tag.target !== null && offset >= tag.target.span.start && offset <= tag.target.span.end) {
|
|
1310
|
-
return {
|
|
1311
|
-
kind: "target",
|
|
1312
|
-
tag
|
|
1313
|
-
};
|
|
1314
|
-
}
|
|
1315
|
-
if (tag.argumentSpan !== null && offset >= tag.argumentSpan.start && offset <= tag.argumentSpan.end) {
|
|
1316
|
-
return {
|
|
1317
|
-
kind: "argument",
|
|
1318
|
-
tag
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
return null;
|
|
1323
|
-
}
|
|
1324
|
-
function getTagCompletionPrefixAtOffset(documentText, offset) {
|
|
1325
|
-
const comment = findEnclosingDocComment(documentText, offset);
|
|
1326
|
-
if (comment === null) {
|
|
1327
|
-
return null;
|
|
1328
|
-
}
|
|
1329
|
-
const relativeOffset = offset - comment.start;
|
|
1330
|
-
if (relativeOffset < 0 || relativeOffset > comment.text.length) {
|
|
1331
|
-
return null;
|
|
1332
|
-
}
|
|
1333
|
-
let cursor = relativeOffset;
|
|
1334
|
-
while (cursor > 0 && isWordChar(comment.text[cursor - 1])) {
|
|
1335
|
-
cursor -= 1;
|
|
1336
|
-
}
|
|
1337
|
-
const atIndex = cursor - 1;
|
|
1338
|
-
if (atIndex < 0 || comment.text[atIndex] !== "@") {
|
|
1339
|
-
return null;
|
|
1340
|
-
}
|
|
1341
|
-
const previousChar = atIndex > 0 ? comment.text[atIndex - 1] : void 0;
|
|
1342
|
-
if (!isWhitespaceLike(previousChar)) {
|
|
1343
|
-
return null;
|
|
1344
|
-
}
|
|
1345
|
-
return comment.text.slice(cursor, relativeOffset);
|
|
1346
|
-
}
|
|
1347
|
-
function getCommentCompletionContextAtOffset(documentText, offset, options) {
|
|
1348
|
-
const prefix = getTagCompletionPrefixAtOffset(documentText, offset);
|
|
1349
|
-
if (prefix !== null) {
|
|
1350
|
-
return {
|
|
1351
|
-
kind: "tag-name",
|
|
1352
|
-
prefix
|
|
1353
|
-
};
|
|
1354
|
-
}
|
|
1355
|
-
const target = getCommentCursorTargetAtOffset(documentText, offset, options);
|
|
1356
|
-
if (target?.kind === "target" || target?.kind === "colon") {
|
|
1357
|
-
return {
|
|
1358
|
-
kind: "target",
|
|
1359
|
-
tag: target.tag
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
if (target?.kind === "argument") {
|
|
1363
|
-
return {
|
|
1364
|
-
kind: "argument",
|
|
1365
|
-
tag: target.tag
|
|
1366
|
-
};
|
|
1367
|
-
}
|
|
1368
|
-
return {
|
|
1369
|
-
kind: "none"
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
function getSemanticCommentCompletionContextAtOffset(documentText, offset, options) {
|
|
1373
|
-
const prefix = getTagCompletionPrefixAtOffset(documentText, offset);
|
|
1374
|
-
if (prefix !== null) {
|
|
1375
|
-
return {
|
|
1376
|
-
kind: "tag-name",
|
|
1377
|
-
prefix,
|
|
1378
|
-
availableTags: getAllTagDefinitions(options?.extensions)
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
|
-
const target = getCommentCursorTargetAtOffset(
|
|
1382
|
-
documentText,
|
|
1383
|
-
offset,
|
|
1384
|
-
options?.extensions ? { extensions: options.extensions } : void 0
|
|
1385
|
-
);
|
|
1386
|
-
if (target?.kind === "target" || target?.kind === "colon") {
|
|
1387
|
-
return {
|
|
1388
|
-
kind: "target",
|
|
1389
|
-
semantic: getCommentTagSemanticContext(target.tag, options)
|
|
1390
|
-
};
|
|
1391
|
-
}
|
|
1392
|
-
if (target?.kind === "argument") {
|
|
1393
|
-
const semantic = getCommentTagSemanticContext(target.tag, options);
|
|
1394
|
-
return {
|
|
1395
|
-
kind: "argument",
|
|
1396
|
-
semantic,
|
|
1397
|
-
valueLabels: semantic.valueLabels
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
return { kind: "none" };
|
|
1401
|
-
}
|
|
1402
|
-
function getCommentHoverInfoAtOffset(documentText, offset, options) {
|
|
1403
|
-
const target = getCommentCursorTargetAtOffset(
|
|
1404
|
-
documentText,
|
|
1405
|
-
offset,
|
|
1406
|
-
options?.extensions ? { extensions: options.extensions } : void 0
|
|
1407
|
-
);
|
|
1408
|
-
if (target === null) {
|
|
1409
|
-
return null;
|
|
1410
|
-
}
|
|
1411
|
-
const semantic = getCommentTagSemanticContext(target.tag, options);
|
|
1412
|
-
let markdown = null;
|
|
1413
|
-
switch (target.kind) {
|
|
1414
|
-
case "tag-name":
|
|
1415
|
-
markdown = semantic.tagHoverMarkdown;
|
|
1416
|
-
break;
|
|
1417
|
-
case "colon":
|
|
1418
|
-
case "target":
|
|
1419
|
-
markdown = semantic.targetHoverMarkdown;
|
|
1420
|
-
break;
|
|
1421
|
-
case "argument":
|
|
1422
|
-
markdown = semantic.argumentHoverMarkdown;
|
|
1423
|
-
break;
|
|
1424
|
-
default: {
|
|
1425
|
-
const exhaustive = target.kind;
|
|
1426
|
-
void exhaustive;
|
|
1427
|
-
break;
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
return markdown === null ? null : {
|
|
1431
|
-
kind: target.kind === "colon" ? "target" : target.kind,
|
|
1432
|
-
markdown
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
// src/tag-value-parser.ts
|
|
1437
|
-
import {
|
|
1438
|
-
BUILTIN_CONSTRAINT_DEFINITIONS as BUILTIN_CONSTRAINT_DEFINITIONS2,
|
|
1439
|
-
isBuiltinConstraintName
|
|
1440
|
-
} from "@formspec/core";
|
|
1441
|
-
var NUMERIC_CONSTRAINT_MAP = {
|
|
1442
|
-
minimum: "minimum",
|
|
1443
|
-
maximum: "maximum",
|
|
1444
|
-
exclusiveMinimum: "exclusiveMinimum",
|
|
1445
|
-
exclusiveMaximum: "exclusiveMaximum",
|
|
1446
|
-
multipleOf: "multipleOf"
|
|
1447
|
-
};
|
|
1448
|
-
var LENGTH_CONSTRAINT_MAP = {
|
|
1449
|
-
minLength: "minLength",
|
|
1450
|
-
maxLength: "maxLength",
|
|
1451
|
-
minItems: "minItems",
|
|
1452
|
-
maxItems: "maxItems"
|
|
1453
|
-
};
|
|
1454
|
-
function tryParseJson(text) {
|
|
1455
|
-
try {
|
|
1456
|
-
return JSON.parse(text);
|
|
1457
|
-
} catch {
|
|
1458
|
-
return null;
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
function syntaxOptions(registry) {
|
|
1462
|
-
return registry?.extensions !== void 0 ? { extensions: registry.extensions } : void 0;
|
|
1463
|
-
}
|
|
1464
|
-
function parseConstraintTagValue(tagName, text, provenance, options) {
|
|
1465
|
-
const customConstraint = parseExtensionConstraintTagValue(tagName, text, provenance, options);
|
|
1466
|
-
if (customConstraint !== null) {
|
|
1467
|
-
return customConstraint;
|
|
1468
|
-
}
|
|
1469
|
-
if (!isBuiltinConstraintName(tagName)) {
|
|
1470
|
-
return null;
|
|
1471
|
-
}
|
|
1472
|
-
const parsedTag = parseTagSyntax(tagName, text, syntaxOptions(options?.registry));
|
|
1473
|
-
if (parsedTag.target !== null && !parsedTag.target.valid) {
|
|
1474
|
-
return null;
|
|
1475
|
-
}
|
|
1476
|
-
const effectiveText = parsedTag.argumentText;
|
|
1477
|
-
const path2 = parsedTag.target?.path ?? void 0;
|
|
1478
|
-
const expectedType = BUILTIN_CONSTRAINT_DEFINITIONS2[tagName];
|
|
1479
|
-
if (expectedType !== "boolean" && effectiveText.trim() === "") {
|
|
1480
|
-
return null;
|
|
1481
|
-
}
|
|
1482
|
-
if (expectedType === "number") {
|
|
1483
|
-
const value = Number(effectiveText);
|
|
1484
|
-
if (Number.isNaN(value)) {
|
|
1485
|
-
return null;
|
|
1486
|
-
}
|
|
1487
|
-
const numericKind = NUMERIC_CONSTRAINT_MAP[tagName];
|
|
1488
|
-
if (numericKind !== void 0) {
|
|
1489
|
-
return {
|
|
1490
|
-
kind: "constraint",
|
|
1491
|
-
constraintKind: numericKind,
|
|
1492
|
-
value,
|
|
1493
|
-
...path2 !== void 0 && { path: path2 },
|
|
1494
|
-
provenance
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
|
-
const lengthKind = LENGTH_CONSTRAINT_MAP[tagName];
|
|
1498
|
-
if (lengthKind !== void 0) {
|
|
1499
|
-
return {
|
|
1500
|
-
kind: "constraint",
|
|
1501
|
-
constraintKind: lengthKind,
|
|
1502
|
-
value,
|
|
1503
|
-
...path2 !== void 0 && { path: path2 },
|
|
1504
|
-
provenance
|
|
1505
|
-
};
|
|
1506
|
-
}
|
|
1507
|
-
return null;
|
|
1508
|
-
}
|
|
1509
|
-
if (expectedType === "boolean") {
|
|
1510
|
-
const trimmed = effectiveText.trim();
|
|
1511
|
-
if (trimmed !== "" && trimmed !== "true") {
|
|
1512
|
-
return null;
|
|
1513
|
-
}
|
|
1514
|
-
if (tagName === "uniqueItems") {
|
|
1515
|
-
return {
|
|
1516
|
-
kind: "constraint",
|
|
1517
|
-
constraintKind: "uniqueItems",
|
|
1518
|
-
value: true,
|
|
1519
|
-
...path2 !== void 0 && { path: path2 },
|
|
1520
|
-
provenance
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
1523
|
-
return null;
|
|
1524
|
-
}
|
|
1525
|
-
if (expectedType === "json") {
|
|
1526
|
-
if (tagName === "const") {
|
|
1527
|
-
const trimmedText = effectiveText.trim();
|
|
1528
|
-
if (trimmedText === "") {
|
|
1529
|
-
return null;
|
|
1530
|
-
}
|
|
1531
|
-
try {
|
|
1532
|
-
const parsed2 = JSON.parse(trimmedText);
|
|
1533
|
-
return {
|
|
1534
|
-
kind: "constraint",
|
|
1535
|
-
constraintKind: "const",
|
|
1536
|
-
value: parsed2,
|
|
1537
|
-
...path2 !== void 0 && { path: path2 },
|
|
1538
|
-
provenance
|
|
1539
|
-
};
|
|
1540
|
-
} catch {
|
|
1541
|
-
return {
|
|
1542
|
-
kind: "constraint",
|
|
1543
|
-
constraintKind: "const",
|
|
1544
|
-
value: trimmedText,
|
|
1545
|
-
...path2 !== void 0 && { path: path2 },
|
|
1546
|
-
provenance
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
const parsed = tryParseJson(effectiveText);
|
|
1551
|
-
if (!Array.isArray(parsed)) {
|
|
1552
|
-
return null;
|
|
1553
|
-
}
|
|
1554
|
-
const members = [];
|
|
1555
|
-
for (const item of parsed) {
|
|
1556
|
-
if (typeof item === "string" || typeof item === "number") {
|
|
1557
|
-
members.push(item);
|
|
1558
|
-
continue;
|
|
1559
|
-
}
|
|
1560
|
-
if (typeof item === "object" && item !== null && "id" in item) {
|
|
1561
|
-
const id = item["id"];
|
|
1562
|
-
if (typeof id === "string" || typeof id === "number") {
|
|
1563
|
-
members.push(id);
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
return {
|
|
1568
|
-
kind: "constraint",
|
|
1569
|
-
constraintKind: "allowedMembers",
|
|
1570
|
-
members,
|
|
1571
|
-
...path2 !== void 0 && { path: path2 },
|
|
1572
|
-
provenance
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
|
-
return {
|
|
1576
|
-
kind: "constraint",
|
|
1577
|
-
constraintKind: "pattern",
|
|
1578
|
-
pattern: effectiveText,
|
|
1579
|
-
...path2 !== void 0 && { path: path2 },
|
|
1580
|
-
provenance
|
|
1581
|
-
};
|
|
1582
|
-
}
|
|
1583
|
-
function parseDefaultValueTagValue(text, provenance) {
|
|
1584
|
-
const trimmed = text.trim();
|
|
1585
|
-
let value;
|
|
1586
|
-
if (trimmed === "null") {
|
|
1587
|
-
value = null;
|
|
1588
|
-
} else if (trimmed === "true") {
|
|
1589
|
-
value = true;
|
|
1590
|
-
} else if (trimmed === "false") {
|
|
1591
|
-
value = false;
|
|
1592
|
-
} else {
|
|
1593
|
-
const parsed = tryParseJson(trimmed);
|
|
1594
|
-
value = parsed !== null ? parsed : trimmed;
|
|
1595
|
-
}
|
|
1596
|
-
return {
|
|
1597
|
-
kind: "annotation",
|
|
1598
|
-
annotationKind: "defaultValue",
|
|
1599
|
-
value,
|
|
1600
|
-
provenance
|
|
1601
|
-
};
|
|
1602
|
-
}
|
|
1603
|
-
function parseExtensionConstraintTagValue(tagName, text, provenance, options) {
|
|
1604
|
-
const parsedTag = parseTagSyntax(tagName, text, syntaxOptions(options?.registry));
|
|
1605
|
-
if (parsedTag.target !== null && !parsedTag.target.valid) {
|
|
1606
|
-
return null;
|
|
1607
|
-
}
|
|
1608
|
-
const effectiveText = parsedTag.argumentText;
|
|
1609
|
-
const path2 = parsedTag.target?.path ?? void 0;
|
|
1610
|
-
const registry = options?.registry;
|
|
1611
|
-
if (registry === void 0) {
|
|
1612
|
-
return null;
|
|
1613
|
-
}
|
|
1614
|
-
if (effectiveText.trim() === "") {
|
|
1615
|
-
return null;
|
|
1616
|
-
}
|
|
1617
|
-
const directTag = registry.findConstraintTag(tagName);
|
|
1618
|
-
if (directTag !== void 0) {
|
|
1619
|
-
return makeCustomConstraintNode(
|
|
1620
|
-
directTag.extensionId,
|
|
1621
|
-
directTag.registration.constraintName,
|
|
1622
|
-
directTag.registration.parseValue(effectiveText),
|
|
1623
|
-
provenance,
|
|
1624
|
-
path2,
|
|
1625
|
-
registry
|
|
1626
|
-
);
|
|
1627
|
-
}
|
|
1628
|
-
if (!isBuiltinConstraintName(tagName)) {
|
|
1629
|
-
return null;
|
|
1630
|
-
}
|
|
1631
|
-
const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
|
|
1632
|
-
if (broadenedTypeId === void 0) {
|
|
1633
|
-
return null;
|
|
1634
|
-
}
|
|
1635
|
-
const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
|
|
1636
|
-
if (broadened === void 0) {
|
|
1637
|
-
return null;
|
|
1638
|
-
}
|
|
1639
|
-
return makeCustomConstraintNode(
|
|
1640
|
-
broadened.extensionId,
|
|
1641
|
-
broadened.registration.constraintName,
|
|
1642
|
-
broadened.registration.parseValue(effectiveText),
|
|
1643
|
-
provenance,
|
|
1644
|
-
path2,
|
|
1645
|
-
registry
|
|
1646
|
-
);
|
|
1647
|
-
}
|
|
1648
|
-
function getBroadenedCustomTypeId(fieldType) {
|
|
1649
|
-
if (fieldType?.kind === "custom") {
|
|
1650
|
-
return fieldType.typeId;
|
|
1651
|
-
}
|
|
1652
|
-
if (fieldType?.kind !== "union") {
|
|
1653
|
-
return void 0;
|
|
1654
|
-
}
|
|
1655
|
-
const customMembers = fieldType.members.filter(
|
|
1656
|
-
(member) => member.kind === "custom"
|
|
1657
|
-
);
|
|
1658
|
-
if (customMembers.length !== 1) {
|
|
1659
|
-
return void 0;
|
|
1660
|
-
}
|
|
1661
|
-
const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
|
|
1662
|
-
const allOtherMembersAreNull = nonCustomMembers.every(
|
|
1663
|
-
(member) => member.kind === "primitive" && member.primitiveKind === "null"
|
|
1664
|
-
);
|
|
1665
|
-
const customMember = customMembers[0];
|
|
1666
|
-
return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
|
|
1667
|
-
}
|
|
1668
|
-
function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path2, registry) {
|
|
1669
|
-
const constraintId = `${extensionId}/${constraintName}`;
|
|
1670
|
-
const registration = registry.findConstraint(constraintId);
|
|
1671
|
-
if (registration === void 0) {
|
|
1672
|
-
throw new Error(
|
|
1673
|
-
`Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
|
|
1674
|
-
);
|
|
1675
|
-
}
|
|
1676
|
-
return {
|
|
1677
|
-
kind: "constraint",
|
|
1678
|
-
constraintKind: "custom",
|
|
1679
|
-
constraintId,
|
|
1680
|
-
payload,
|
|
1681
|
-
compositionRule: registration.compositionRule,
|
|
1682
|
-
...path2 !== void 0 && { path: path2 },
|
|
1683
|
-
provenance
|
|
1684
|
-
};
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
// src/semantic-targets.ts
|
|
1688
|
-
import { normalizeConstraintTagName as normalizeConstraintTagName2 } from "@formspec/core";
|
|
1689
|
-
function pathKey(path2) {
|
|
1690
|
-
return path2?.segments.join(".") ?? "";
|
|
1691
|
-
}
|
|
1692
|
-
function formatConstraintTargetName(fieldName, path2) {
|
|
1693
|
-
if (path2 === null || path2.segments.length === 0) {
|
|
1694
|
-
return fieldName;
|
|
1695
|
-
}
|
|
1696
|
-
return `${fieldName}.${path2.segments.join(".")}`;
|
|
1697
|
-
}
|
|
1698
|
-
function dereferenceAnalysisType(type, typeRegistry) {
|
|
1699
|
-
let current = type;
|
|
1700
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1701
|
-
while (current.kind === "reference") {
|
|
1702
|
-
if (seen.has(current.name)) {
|
|
1703
|
-
return current;
|
|
1704
|
-
}
|
|
1705
|
-
seen.add(current.name);
|
|
1706
|
-
const definition = typeRegistry[current.name];
|
|
1707
|
-
if (definition === void 0) {
|
|
1708
|
-
return current;
|
|
1709
|
-
}
|
|
1710
|
-
current = definition.type;
|
|
1711
|
-
}
|
|
1712
|
-
return current;
|
|
1713
|
-
}
|
|
1714
|
-
function collectReferencedTypeConstraints(type, typeRegistry) {
|
|
1715
|
-
const collected = [];
|
|
1716
|
-
let current = type;
|
|
1717
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1718
|
-
while (current.kind === "reference") {
|
|
1719
|
-
if (seen.has(current.name)) {
|
|
1720
|
-
break;
|
|
1721
|
-
}
|
|
1722
|
-
seen.add(current.name);
|
|
1723
|
-
const definition = typeRegistry[current.name];
|
|
1724
|
-
if (definition === void 0) {
|
|
1725
|
-
break;
|
|
1726
|
-
}
|
|
1727
|
-
if (definition.constraints !== void 0) {
|
|
1728
|
-
collected.push(...definition.constraints);
|
|
1729
|
-
}
|
|
1730
|
-
current = definition.type;
|
|
1731
|
-
}
|
|
1732
|
-
return collected;
|
|
1733
|
-
}
|
|
1734
|
-
function collectReferencedTypeAnnotations(type, typeRegistry) {
|
|
1735
|
-
const collected = [];
|
|
1736
|
-
let current = type;
|
|
1737
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1738
|
-
while (current.kind === "reference") {
|
|
1739
|
-
if (seen.has(current.name)) {
|
|
1740
|
-
break;
|
|
1741
|
-
}
|
|
1742
|
-
seen.add(current.name);
|
|
1743
|
-
const definition = typeRegistry[current.name];
|
|
1744
|
-
if (definition === void 0) {
|
|
1745
|
-
break;
|
|
1746
|
-
}
|
|
1747
|
-
if (definition.annotations !== void 0) {
|
|
1748
|
-
collected.push(...definition.annotations);
|
|
1749
|
-
}
|
|
1750
|
-
current = definition.type;
|
|
1751
|
-
}
|
|
1752
|
-
return collected;
|
|
1753
|
-
}
|
|
1754
|
-
function resolveProperty(type, typeRegistry, segments) {
|
|
1755
|
-
const effectiveType = dereferenceAnalysisType(type, typeRegistry);
|
|
1756
|
-
if (segments.length === 0) {
|
|
1757
|
-
return { kind: "resolved", property: null, rawType: type, type: effectiveType };
|
|
1758
|
-
}
|
|
1759
|
-
if (effectiveType.kind === "array") {
|
|
1760
|
-
return resolveProperty(effectiveType.items, typeRegistry, segments);
|
|
1761
|
-
}
|
|
1762
|
-
if (effectiveType.kind !== "object") {
|
|
1763
|
-
return { kind: "unresolvable", type: effectiveType };
|
|
1764
|
-
}
|
|
1765
|
-
const [segment, ...rest] = segments;
|
|
1766
|
-
if (segment === void 0) {
|
|
1767
|
-
throw new Error("Invariant violation: object traversal requires a segment");
|
|
1768
|
-
}
|
|
1769
|
-
const property = effectiveType.properties.find((candidate) => candidate.name === segment);
|
|
1770
|
-
if (property === void 0) {
|
|
1771
|
-
return { kind: "missing-property", segment };
|
|
1772
|
-
}
|
|
1773
|
-
if (rest.length === 0) {
|
|
1774
|
-
return {
|
|
1775
|
-
kind: "resolved",
|
|
1776
|
-
property,
|
|
1777
|
-
rawType: property.type,
|
|
1778
|
-
type: dereferenceAnalysisType(property.type, typeRegistry)
|
|
1779
|
-
};
|
|
1780
|
-
}
|
|
1781
|
-
return resolveProperty(property.type, typeRegistry, rest);
|
|
1782
|
-
}
|
|
1783
|
-
function resolveConstraintTargetState(fieldName, fieldType, path2, localConstraints, typeRegistry) {
|
|
1784
|
-
if (path2 === null) {
|
|
1785
|
-
const inheritedConstraints2 = collectReferencedTypeConstraints(fieldType, typeRegistry);
|
|
1786
|
-
const inheritedAnnotations2 = collectReferencedTypeAnnotations(fieldType, typeRegistry);
|
|
1787
|
-
const type = dereferenceAnalysisType(fieldType, typeRegistry);
|
|
1788
|
-
return {
|
|
1789
|
-
kind: "resolved",
|
|
1790
|
-
fieldName,
|
|
1791
|
-
path: path2,
|
|
1792
|
-
targetName: fieldName,
|
|
1793
|
-
type,
|
|
1794
|
-
inheritedConstraints: inheritedConstraints2,
|
|
1795
|
-
inheritedAnnotations: inheritedAnnotations2,
|
|
1796
|
-
localConstraints,
|
|
1797
|
-
effectiveConstraints: [...inheritedConstraints2, ...localConstraints]
|
|
1798
|
-
};
|
|
1799
|
-
}
|
|
1800
|
-
const resolution = resolveProperty(fieldType, typeRegistry, path2.segments);
|
|
1801
|
-
const targetName = formatConstraintTargetName(fieldName, path2);
|
|
1802
|
-
if (resolution.kind === "missing-property") {
|
|
1803
|
-
return {
|
|
1804
|
-
kind: "missing-property",
|
|
1805
|
-
fieldName,
|
|
1806
|
-
path: path2,
|
|
1807
|
-
targetName,
|
|
1808
|
-
segment: resolution.segment,
|
|
1809
|
-
localConstraints
|
|
1810
|
-
};
|
|
1811
|
-
}
|
|
1812
|
-
if (resolution.kind === "unresolvable") {
|
|
1813
|
-
return {
|
|
1814
|
-
kind: "unresolvable",
|
|
1815
|
-
fieldName,
|
|
1816
|
-
path: path2,
|
|
1817
|
-
targetName,
|
|
1818
|
-
type: resolution.type,
|
|
1819
|
-
localConstraints
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
const propertyConstraints = resolution.property?.constraints ?? [];
|
|
1823
|
-
const propertyAnnotations = resolution.property?.annotations ?? [];
|
|
1824
|
-
const referencedConstraints = collectReferencedTypeConstraints(resolution.rawType, typeRegistry);
|
|
1825
|
-
const referencedAnnotations = collectReferencedTypeAnnotations(resolution.rawType, typeRegistry);
|
|
1826
|
-
const inheritedConstraints = [...propertyConstraints, ...referencedConstraints];
|
|
1827
|
-
const inheritedAnnotations = [...propertyAnnotations, ...referencedAnnotations];
|
|
1828
|
-
return {
|
|
1829
|
-
kind: "resolved",
|
|
1830
|
-
fieldName,
|
|
1831
|
-
path: path2,
|
|
1832
|
-
targetName,
|
|
1833
|
-
type: resolution.type,
|
|
1834
|
-
inheritedConstraints,
|
|
1835
|
-
inheritedAnnotations,
|
|
1836
|
-
localConstraints,
|
|
1837
|
-
effectiveConstraints: [...inheritedConstraints, ...localConstraints]
|
|
1838
|
-
};
|
|
1839
|
-
}
|
|
1840
|
-
function cloneTargetPath(path2) {
|
|
1841
|
-
if (path2 === void 0) {
|
|
1842
|
-
return null;
|
|
1843
|
-
}
|
|
1844
|
-
return { segments: [...path2.segments] };
|
|
1845
|
-
}
|
|
1846
|
-
function buildConstraintTargetStates(fieldName, fieldType, constraints, typeRegistry) {
|
|
1847
|
-
const grouped = /* @__PURE__ */ new Map([
|
|
1848
|
-
["", { path: null, constraints: [] }]
|
|
1849
|
-
]);
|
|
1850
|
-
for (const constraint of constraints) {
|
|
1851
|
-
const path2 = cloneTargetPath(constraint.path);
|
|
1852
|
-
const key = pathKey(path2);
|
|
1853
|
-
let bucket = grouped.get(key);
|
|
1854
|
-
if (bucket === void 0) {
|
|
1855
|
-
bucket = { path: path2, constraints: [] };
|
|
1856
|
-
grouped.set(key, bucket);
|
|
1857
|
-
}
|
|
1858
|
-
bucket.constraints.push(constraint);
|
|
1859
|
-
}
|
|
1860
|
-
return [...grouped.values()].map(
|
|
1861
|
-
(group) => resolveConstraintTargetState(fieldName, fieldType, group.path, group.constraints, typeRegistry)
|
|
1862
|
-
);
|
|
1863
|
-
}
|
|
1864
|
-
function addContradiction(diagnostics, message, primary, related) {
|
|
1865
|
-
diagnostics.push({
|
|
1866
|
-
code: "CONTRADICTING_CONSTRAINTS",
|
|
1867
|
-
message,
|
|
1868
|
-
severity: "error",
|
|
1869
|
-
primaryLocation: primary,
|
|
1870
|
-
relatedLocations: [related]
|
|
1871
|
-
});
|
|
1872
|
-
}
|
|
1873
|
-
function addTypeMismatch(diagnostics, message, primary) {
|
|
1874
|
-
diagnostics.push({
|
|
1875
|
-
code: "TYPE_MISMATCH",
|
|
1876
|
-
message,
|
|
1877
|
-
severity: "error",
|
|
1878
|
-
primaryLocation: primary,
|
|
1879
|
-
relatedLocations: []
|
|
1880
|
-
});
|
|
1881
|
-
}
|
|
1882
|
-
function addUnknownExtension(diagnostics, message, primary) {
|
|
1883
|
-
diagnostics.push({
|
|
1884
|
-
code: "UNKNOWN_EXTENSION",
|
|
1885
|
-
message,
|
|
1886
|
-
severity: "warning",
|
|
1887
|
-
primaryLocation: primary,
|
|
1888
|
-
relatedLocations: []
|
|
1889
|
-
});
|
|
1890
|
-
}
|
|
1891
|
-
function addUnknownPathTarget(diagnostics, message, primary) {
|
|
1892
|
-
diagnostics.push({
|
|
1893
|
-
code: "UNKNOWN_PATH_TARGET",
|
|
1894
|
-
message,
|
|
1895
|
-
severity: "error",
|
|
1896
|
-
primaryLocation: primary,
|
|
1897
|
-
relatedLocations: []
|
|
1898
|
-
});
|
|
1899
|
-
}
|
|
1900
|
-
function addConstraintBroadening(diagnostics, message, primary, related) {
|
|
1901
|
-
diagnostics.push({
|
|
1902
|
-
code: "CONSTRAINT_BROADENING",
|
|
1903
|
-
message,
|
|
1904
|
-
severity: "error",
|
|
1905
|
-
primaryLocation: primary,
|
|
1906
|
-
relatedLocations: [related]
|
|
1907
|
-
});
|
|
1908
|
-
}
|
|
1909
|
-
function getExtensionIdFromConstraintId(constraintId) {
|
|
1910
|
-
const separator = constraintId.lastIndexOf("/");
|
|
1911
|
-
if (separator <= 0) {
|
|
1912
|
-
return null;
|
|
1913
|
-
}
|
|
1914
|
-
return constraintId.slice(0, separator);
|
|
1915
|
-
}
|
|
1916
|
-
function typeLabel(type) {
|
|
1917
|
-
switch (type.kind) {
|
|
1918
|
-
case "primitive":
|
|
1919
|
-
return type.primitiveKind;
|
|
1920
|
-
case "enum":
|
|
1921
|
-
return "enum";
|
|
1922
|
-
case "array":
|
|
1923
|
-
return "array";
|
|
1924
|
-
case "object":
|
|
1925
|
-
return "object";
|
|
1926
|
-
case "record":
|
|
1927
|
-
return "record";
|
|
1928
|
-
case "union":
|
|
1929
|
-
return "union";
|
|
1930
|
-
case "reference":
|
|
1931
|
-
return `reference(${type.name})`;
|
|
1932
|
-
case "dynamic":
|
|
1933
|
-
return `dynamic(${type.dynamicKind})`;
|
|
1934
|
-
case "custom":
|
|
1935
|
-
return `custom(${type.typeId})`;
|
|
1936
|
-
default: {
|
|
1937
|
-
const exhaustive = type;
|
|
1938
|
-
return String(exhaustive);
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
function isJsonObject(value) {
|
|
1943
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1944
|
-
}
|
|
1945
|
-
function isJsonArray(value) {
|
|
1946
|
-
return Array.isArray(value);
|
|
1947
|
-
}
|
|
1948
|
-
function jsonValueEquals(left, right) {
|
|
1949
|
-
if (left === right) {
|
|
1950
|
-
return true;
|
|
1951
|
-
}
|
|
1952
|
-
if (isJsonArray(left) || isJsonArray(right)) {
|
|
1953
|
-
if (!isJsonArray(left) || !isJsonArray(right) || left.length !== right.length) {
|
|
1954
|
-
return false;
|
|
1955
|
-
}
|
|
1956
|
-
for (const [index, item] of left.entries()) {
|
|
1957
|
-
const rightItem = right[index];
|
|
1958
|
-
if (rightItem === void 0 || !jsonValueEquals(item, rightItem)) {
|
|
1959
|
-
return false;
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
return true;
|
|
1963
|
-
}
|
|
1964
|
-
if (isJsonObject(left) || isJsonObject(right)) {
|
|
1965
|
-
if (!isJsonObject(left) || !isJsonObject(right)) {
|
|
1966
|
-
return false;
|
|
1967
|
-
}
|
|
1968
|
-
const leftKeys = Object.keys(left).sort();
|
|
1969
|
-
const rightKeys = Object.keys(right).sort();
|
|
1970
|
-
if (leftKeys.length !== rightKeys.length) {
|
|
1971
|
-
return false;
|
|
1972
|
-
}
|
|
1973
|
-
return leftKeys.every((key, index) => {
|
|
1974
|
-
const rightKey = rightKeys[index];
|
|
1975
|
-
if (rightKey !== key) {
|
|
1976
|
-
return false;
|
|
1977
|
-
}
|
|
1978
|
-
const leftValue = left[key];
|
|
1979
|
-
const rightValue = right[rightKey];
|
|
1980
|
-
return leftValue !== void 0 && rightValue !== void 0 && jsonValueEquals(leftValue, rightValue);
|
|
1981
|
-
});
|
|
1982
|
-
}
|
|
1983
|
-
return false;
|
|
1984
|
-
}
|
|
1985
|
-
function findNumeric(constraints, constraintKind) {
|
|
1986
|
-
return constraints.find(
|
|
1987
|
-
(constraint) => constraint.constraintKind === constraintKind
|
|
1988
|
-
);
|
|
1989
|
-
}
|
|
1990
|
-
function findLength(constraints, constraintKind) {
|
|
1991
|
-
return constraints.find(
|
|
1992
|
-
(constraint) => constraint.constraintKind === constraintKind
|
|
1993
|
-
);
|
|
1994
|
-
}
|
|
1995
|
-
function findAllowedMembers(constraints) {
|
|
1996
|
-
return constraints.filter(
|
|
1997
|
-
(constraint) => constraint.constraintKind === "allowedMembers"
|
|
1998
|
-
);
|
|
1999
|
-
}
|
|
2000
|
-
function findConstConstraints(constraints) {
|
|
2001
|
-
return constraints.filter(
|
|
2002
|
-
(constraint) => constraint.constraintKind === "const"
|
|
2003
|
-
);
|
|
2004
|
-
}
|
|
2005
|
-
function isOrderedBoundConstraint(constraint) {
|
|
2006
|
-
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";
|
|
2007
|
-
}
|
|
2008
|
-
function constraintPathKey(constraint) {
|
|
2009
|
-
return constraint.path?.segments.join(".") ?? "";
|
|
2010
|
-
}
|
|
2011
|
-
function orderedBoundFamily(kind) {
|
|
2012
|
-
switch (kind) {
|
|
2013
|
-
case "minimum":
|
|
2014
|
-
case "exclusiveMinimum":
|
|
2015
|
-
return "numeric-lower";
|
|
2016
|
-
case "maximum":
|
|
2017
|
-
case "exclusiveMaximum":
|
|
2018
|
-
return "numeric-upper";
|
|
2019
|
-
case "minLength":
|
|
2020
|
-
return "minLength";
|
|
2021
|
-
case "minItems":
|
|
2022
|
-
return "minItems";
|
|
2023
|
-
case "maxLength":
|
|
2024
|
-
return "maxLength";
|
|
2025
|
-
case "maxItems":
|
|
2026
|
-
return "maxItems";
|
|
2027
|
-
default: {
|
|
2028
|
-
const exhaustive = kind;
|
|
2029
|
-
return exhaustive;
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
function isNumericLowerKind(kind) {
|
|
2034
|
-
return kind === "minimum" || kind === "exclusiveMinimum";
|
|
2035
|
-
}
|
|
2036
|
-
function isNumericUpperKind(kind) {
|
|
2037
|
-
return kind === "maximum" || kind === "exclusiveMaximum";
|
|
2038
|
-
}
|
|
2039
|
-
function describeConstraintTag(constraint) {
|
|
2040
|
-
return `@${constraint.constraintKind}`;
|
|
2041
|
-
}
|
|
2042
|
-
function compareConstraintStrength(current, previous) {
|
|
2043
|
-
const family = orderedBoundFamily(current.constraintKind);
|
|
2044
|
-
if (family === "numeric-lower") {
|
|
2045
|
-
if (!isNumericLowerKind(current.constraintKind) || !isNumericLowerKind(previous.constraintKind)) {
|
|
2046
|
-
throw new Error("numeric-lower family received non-numeric lower-bound constraint");
|
|
2047
|
-
}
|
|
2048
|
-
if (current.value !== previous.value) {
|
|
2049
|
-
return current.value > previous.value ? 1 : -1;
|
|
2050
|
-
}
|
|
2051
|
-
if (current.constraintKind === "exclusiveMinimum" && previous.constraintKind === "minimum") {
|
|
2052
|
-
return 1;
|
|
2053
|
-
}
|
|
2054
|
-
if (current.constraintKind === "minimum" && previous.constraintKind === "exclusiveMinimum") {
|
|
2055
|
-
return -1;
|
|
2056
|
-
}
|
|
2057
|
-
return 0;
|
|
2058
|
-
}
|
|
2059
|
-
if (family === "numeric-upper") {
|
|
2060
|
-
if (!isNumericUpperKind(current.constraintKind) || !isNumericUpperKind(previous.constraintKind)) {
|
|
2061
|
-
throw new Error("numeric-upper family received non-numeric upper-bound constraint");
|
|
2062
|
-
}
|
|
2063
|
-
if (current.value !== previous.value) {
|
|
2064
|
-
return current.value < previous.value ? 1 : -1;
|
|
2065
|
-
}
|
|
2066
|
-
if (current.constraintKind === "exclusiveMaximum" && previous.constraintKind === "maximum") {
|
|
2067
|
-
return 1;
|
|
2068
|
-
}
|
|
2069
|
-
if (current.constraintKind === "maximum" && previous.constraintKind === "exclusiveMaximum") {
|
|
2070
|
-
return -1;
|
|
2071
|
-
}
|
|
2072
|
-
return 0;
|
|
2073
|
-
}
|
|
2074
|
-
switch (family) {
|
|
2075
|
-
case "minLength":
|
|
2076
|
-
case "minItems":
|
|
2077
|
-
if (current.value === previous.value) {
|
|
2078
|
-
return 0;
|
|
2079
|
-
}
|
|
2080
|
-
return current.value > previous.value ? 1 : -1;
|
|
2081
|
-
case "maxLength":
|
|
2082
|
-
case "maxItems":
|
|
2083
|
-
if (current.value === previous.value) {
|
|
2084
|
-
return 0;
|
|
2085
|
-
}
|
|
2086
|
-
return current.value < previous.value ? 1 : -1;
|
|
2087
|
-
default: {
|
|
2088
|
-
const exhaustive = family;
|
|
2089
|
-
return exhaustive;
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
}
|
|
2093
|
-
function compareSemanticInclusivity(currentInclusive, previousInclusive) {
|
|
2094
|
-
if (currentInclusive === previousInclusive) {
|
|
2095
|
-
return 0;
|
|
2096
|
-
}
|
|
2097
|
-
return currentInclusive ? -1 : 1;
|
|
2098
|
-
}
|
|
2099
|
-
function compareCustomConstraintStrength(current, previous) {
|
|
2100
|
-
const order = current.comparePayloads(current.constraint.payload, previous.constraint.payload);
|
|
2101
|
-
const equalPayloadTiebreaker = order === 0 ? compareSemanticInclusivity(current.role.inclusive, previous.role.inclusive) : order;
|
|
2102
|
-
switch (current.role.bound) {
|
|
2103
|
-
case "lower":
|
|
2104
|
-
return equalPayloadTiebreaker;
|
|
2105
|
-
case "upper":
|
|
2106
|
-
return equalPayloadTiebreaker === 0 ? 0 : -equalPayloadTiebreaker;
|
|
2107
|
-
case "exact":
|
|
2108
|
-
return order === 0 ? 0 : Number.NaN;
|
|
2109
|
-
default: {
|
|
2110
|
-
const exhaustive = current.role.bound;
|
|
2111
|
-
return exhaustive;
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
function customConstraintsContradict(lower, upper) {
|
|
2116
|
-
const order = lower.comparePayloads(lower.constraint.payload, upper.constraint.payload);
|
|
2117
|
-
if (order > 0) {
|
|
2118
|
-
return true;
|
|
2119
|
-
}
|
|
2120
|
-
if (order < 0) {
|
|
2121
|
-
return false;
|
|
2122
|
-
}
|
|
2123
|
-
return !lower.role.inclusive || !upper.role.inclusive;
|
|
2124
|
-
}
|
|
2125
|
-
function describeCustomConstraintTag(constraint) {
|
|
2126
|
-
return constraint.provenance.tagName ?? constraint.constraintId;
|
|
2127
|
-
}
|
|
2128
|
-
function isNullType(type) {
|
|
2129
|
-
return type.kind === "primitive" && type.primitiveKind === "null";
|
|
2130
|
-
}
|
|
2131
|
-
function collectCustomConstraintCandidateTypes(type, typeRegistry) {
|
|
2132
|
-
const effectiveType = dereferenceAnalysisType(type, typeRegistry);
|
|
2133
|
-
const candidates = [effectiveType];
|
|
2134
|
-
if (effectiveType.kind === "array") {
|
|
2135
|
-
candidates.push(...collectCustomConstraintCandidateTypes(effectiveType.items, typeRegistry));
|
|
2136
|
-
}
|
|
2137
|
-
if (effectiveType.kind === "union") {
|
|
2138
|
-
const memberTypes = effectiveType.members.map(
|
|
2139
|
-
(member) => dereferenceAnalysisType(member, typeRegistry)
|
|
2140
|
-
);
|
|
2141
|
-
const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
|
|
2142
|
-
if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
|
|
2143
|
-
const [nullableMember] = nonNullMembers;
|
|
2144
|
-
if (nullableMember !== void 0) {
|
|
2145
|
-
candidates.push(...collectCustomConstraintCandidateTypes(nullableMember, typeRegistry));
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
return candidates;
|
|
2150
|
-
}
|
|
2151
|
-
function checkNumericContradictions(diagnostics, fieldName, constraints) {
|
|
2152
|
-
const min = findNumeric(constraints, "minimum");
|
|
2153
|
-
const max = findNumeric(constraints, "maximum");
|
|
2154
|
-
const exMin = findNumeric(constraints, "exclusiveMinimum");
|
|
2155
|
-
const exMax = findNumeric(constraints, "exclusiveMaximum");
|
|
2156
|
-
if (min !== void 0 && max !== void 0 && min.value > max.value) {
|
|
2157
|
-
addContradiction(
|
|
2158
|
-
diagnostics,
|
|
2159
|
-
`Field "${fieldName}": minimum (${String(min.value)}) is greater than maximum (${String(max.value)})`,
|
|
2160
|
-
min.provenance,
|
|
2161
|
-
max.provenance
|
|
2162
|
-
);
|
|
2163
|
-
}
|
|
2164
|
-
if (exMin !== void 0 && max !== void 0 && exMin.value >= max.value) {
|
|
2165
|
-
addContradiction(
|
|
2166
|
-
diagnostics,
|
|
2167
|
-
`Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to maximum (${String(max.value)})`,
|
|
2168
|
-
exMin.provenance,
|
|
2169
|
-
max.provenance
|
|
2170
|
-
);
|
|
2171
|
-
}
|
|
2172
|
-
if (min !== void 0 && exMax !== void 0 && min.value >= exMax.value) {
|
|
2173
|
-
addContradiction(
|
|
2174
|
-
diagnostics,
|
|
2175
|
-
`Field "${fieldName}": minimum (${String(min.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
|
|
2176
|
-
min.provenance,
|
|
2177
|
-
exMax.provenance
|
|
2178
|
-
);
|
|
2179
|
-
}
|
|
2180
|
-
if (exMin !== void 0 && exMax !== void 0 && exMin.value >= exMax.value) {
|
|
2181
|
-
addContradiction(
|
|
2182
|
-
diagnostics,
|
|
2183
|
-
`Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
|
|
2184
|
-
exMin.provenance,
|
|
2185
|
-
exMax.provenance
|
|
2186
|
-
);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
function checkLengthContradictions(diagnostics, fieldName, constraints) {
|
|
2190
|
-
const minLen = findLength(constraints, "minLength");
|
|
2191
|
-
const maxLen = findLength(constraints, "maxLength");
|
|
2192
|
-
if (minLen !== void 0 && maxLen !== void 0 && minLen.value > maxLen.value) {
|
|
2193
|
-
addContradiction(
|
|
2194
|
-
diagnostics,
|
|
2195
|
-
`Field "${fieldName}": minLength (${String(minLen.value)}) is greater than maxLength (${String(maxLen.value)})`,
|
|
2196
|
-
minLen.provenance,
|
|
2197
|
-
maxLen.provenance
|
|
2198
|
-
);
|
|
2199
|
-
}
|
|
2200
|
-
const minItems = findLength(constraints, "minItems");
|
|
2201
|
-
const maxItems = findLength(constraints, "maxItems");
|
|
2202
|
-
if (minItems !== void 0 && maxItems !== void 0 && minItems.value > maxItems.value) {
|
|
2203
|
-
addContradiction(
|
|
2204
|
-
diagnostics,
|
|
2205
|
-
`Field "${fieldName}": minItems (${String(minItems.value)}) is greater than maxItems (${String(maxItems.value)})`,
|
|
2206
|
-
minItems.provenance,
|
|
2207
|
-
maxItems.provenance
|
|
2208
|
-
);
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
function checkAllowedMembersContradiction(diagnostics, fieldName, constraints) {
|
|
2212
|
-
const members = findAllowedMembers(constraints);
|
|
2213
|
-
if (members.length < 2) {
|
|
2214
|
-
return;
|
|
2215
|
-
}
|
|
2216
|
-
const firstSet = new Set(members[0]?.members ?? []);
|
|
2217
|
-
for (let index = 1; index < members.length; index += 1) {
|
|
2218
|
-
const current = members[index];
|
|
2219
|
-
if (current === void 0) {
|
|
2220
|
-
continue;
|
|
2221
|
-
}
|
|
2222
|
-
for (const member of firstSet) {
|
|
2223
|
-
if (!current.members.includes(member)) {
|
|
2224
|
-
firstSet.delete(member);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
if (firstSet.size === 0) {
|
|
2229
|
-
const first = members[0];
|
|
2230
|
-
const second = members[1];
|
|
2231
|
-
if (first !== void 0 && second !== void 0) {
|
|
2232
|
-
addContradiction(
|
|
2233
|
-
diagnostics,
|
|
2234
|
-
`Field "${fieldName}": allowedMembers constraints have an empty intersection (no valid values remain)`,
|
|
2235
|
-
first.provenance,
|
|
2236
|
-
second.provenance
|
|
2237
|
-
);
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
function checkConstContradictions(diagnostics, fieldName, constraints) {
|
|
2242
|
-
const constConstraints = findConstConstraints(constraints);
|
|
2243
|
-
if (constConstraints.length < 2) {
|
|
2244
|
-
return;
|
|
2245
|
-
}
|
|
2246
|
-
const first = constConstraints[0];
|
|
2247
|
-
if (first === void 0) {
|
|
2248
|
-
return;
|
|
2249
|
-
}
|
|
2250
|
-
for (let index = 1; index < constConstraints.length; index += 1) {
|
|
2251
|
-
const current = constConstraints[index];
|
|
2252
|
-
if (current === void 0 || jsonValueEquals(first.value, current.value)) {
|
|
2253
|
-
continue;
|
|
2254
|
-
}
|
|
2255
|
-
addContradiction(
|
|
2256
|
-
diagnostics,
|
|
2257
|
-
`Field "${fieldName}": conflicting @const constraints require both ${JSON.stringify(first.value)} and ${JSON.stringify(current.value)}`,
|
|
2258
|
-
first.provenance,
|
|
2259
|
-
current.provenance
|
|
2260
|
-
);
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
function checkConstraintBroadening(diagnostics, fieldName, constraints) {
|
|
2264
|
-
const strongestByKey = /* @__PURE__ */ new Map();
|
|
2265
|
-
for (const constraint of constraints) {
|
|
2266
|
-
if (!isOrderedBoundConstraint(constraint)) {
|
|
2267
|
-
continue;
|
|
2268
|
-
}
|
|
2269
|
-
const key = `${orderedBoundFamily(constraint.constraintKind)}:${constraintPathKey(constraint)}`;
|
|
2270
|
-
const previous = strongestByKey.get(key);
|
|
2271
|
-
if (previous === void 0) {
|
|
2272
|
-
strongestByKey.set(key, constraint);
|
|
2273
|
-
continue;
|
|
2274
|
-
}
|
|
2275
|
-
const strength = compareConstraintStrength(constraint, previous);
|
|
2276
|
-
if (strength < 0) {
|
|
2277
|
-
addConstraintBroadening(
|
|
2278
|
-
diagnostics,
|
|
2279
|
-
`Field "${fieldName}": ${describeConstraintTag(constraint)} (${String(constraint.value)}) is broader than earlier ${describeConstraintTag(previous)} (${String(previous.value)}). Constraints can only narrow.`,
|
|
2280
|
-
constraint.provenance,
|
|
2281
|
-
previous.provenance
|
|
2282
|
-
);
|
|
2283
|
-
continue;
|
|
2284
|
-
}
|
|
2285
|
-
if (strength > 0) {
|
|
2286
|
-
strongestByKey.set(key, constraint);
|
|
2287
|
-
}
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
function checkCustomConstraintSemantics(diagnostics, fieldName, constraints, extensionRegistry) {
|
|
2291
|
-
if (extensionRegistry === void 0) {
|
|
2292
|
-
return;
|
|
2293
|
-
}
|
|
2294
|
-
const strongestByKey = /* @__PURE__ */ new Map();
|
|
2295
|
-
const lowerByFamily = /* @__PURE__ */ new Map();
|
|
2296
|
-
const upperByFamily = /* @__PURE__ */ new Map();
|
|
2297
|
-
for (const constraint of constraints) {
|
|
2298
|
-
if (constraint.constraintKind !== "custom") {
|
|
2299
|
-
continue;
|
|
2300
|
-
}
|
|
2301
|
-
const registration = extensionRegistry.findConstraint(constraint.constraintId);
|
|
2302
|
-
if (registration?.comparePayloads === void 0 || registration.semanticRole === void 0) {
|
|
2303
|
-
continue;
|
|
2304
|
-
}
|
|
2305
|
-
const entry = {
|
|
2306
|
-
constraint,
|
|
2307
|
-
comparePayloads: registration.comparePayloads,
|
|
2308
|
-
role: registration.semanticRole
|
|
2309
|
-
};
|
|
2310
|
-
const familyKey = `${registration.semanticRole.family}:${constraintPathKey(constraint)}`;
|
|
2311
|
-
const boundKey = `${familyKey}:${registration.semanticRole.bound}`;
|
|
2312
|
-
const previous = strongestByKey.get(boundKey);
|
|
2313
|
-
if (previous !== void 0) {
|
|
2314
|
-
const strength = compareCustomConstraintStrength(entry, previous);
|
|
2315
|
-
if (Number.isNaN(strength)) {
|
|
2316
|
-
addContradiction(
|
|
2317
|
-
diagnostics,
|
|
2318
|
-
`Field "${fieldName}": ${describeCustomConstraintTag(constraint)} conflicts with ${describeCustomConstraintTag(previous.constraint)}`,
|
|
2319
|
-
constraint.provenance,
|
|
2320
|
-
previous.constraint.provenance
|
|
2321
|
-
);
|
|
2322
|
-
continue;
|
|
2323
|
-
}
|
|
2324
|
-
if (strength < 0) {
|
|
2325
|
-
addConstraintBroadening(
|
|
2326
|
-
diagnostics,
|
|
2327
|
-
`Field "${fieldName}": ${describeCustomConstraintTag(constraint)} is broader than earlier ${describeCustomConstraintTag(previous.constraint)}. Constraints can only narrow.`,
|
|
2328
|
-
constraint.provenance,
|
|
2329
|
-
previous.constraint.provenance
|
|
2330
|
-
);
|
|
2331
|
-
continue;
|
|
2332
|
-
}
|
|
2333
|
-
if (strength > 0) {
|
|
2334
|
-
strongestByKey.set(boundKey, entry);
|
|
2335
|
-
}
|
|
2336
|
-
} else {
|
|
2337
|
-
strongestByKey.set(boundKey, entry);
|
|
2338
|
-
}
|
|
2339
|
-
if (registration.semanticRole.bound === "lower") {
|
|
2340
|
-
lowerByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
|
|
2341
|
-
} else if (registration.semanticRole.bound === "upper") {
|
|
2342
|
-
upperByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
for (const [familyKey, lower] of lowerByFamily) {
|
|
2346
|
-
const upper = upperByFamily.get(familyKey);
|
|
2347
|
-
if (upper === void 0 || !customConstraintsContradict(lower, upper)) {
|
|
2348
|
-
continue;
|
|
2349
|
-
}
|
|
2350
|
-
addContradiction(
|
|
2351
|
-
diagnostics,
|
|
2352
|
-
`Field "${fieldName}": ${describeCustomConstraintTag(lower.constraint)} contradicts ${describeCustomConstraintTag(upper.constraint)}`,
|
|
2353
|
-
lower.constraint.provenance,
|
|
2354
|
-
upper.constraint.provenance
|
|
2355
|
-
);
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
function checkCustomConstraint(diagnostics, fieldName, type, constraint, typeRegistry, extensionRegistry) {
|
|
2359
|
-
if (extensionRegistry === void 0) {
|
|
2360
|
-
return;
|
|
2361
|
-
}
|
|
2362
|
-
const registration = extensionRegistry.findConstraint(constraint.constraintId);
|
|
2363
|
-
if (registration === void 0) {
|
|
2364
|
-
addUnknownExtension(
|
|
2365
|
-
diagnostics,
|
|
2366
|
-
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not registered in the extension registry`,
|
|
2367
|
-
constraint.provenance
|
|
2368
|
-
);
|
|
2369
|
-
return;
|
|
2370
|
-
}
|
|
2371
|
-
const candidateTypes = collectCustomConstraintCandidateTypes(type, typeRegistry);
|
|
2372
|
-
const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : normalizeConstraintTagName2(constraint.provenance.tagName.replace(/^@/, ""));
|
|
2373
|
-
if (normalizedTagName !== void 0) {
|
|
2374
|
-
const tagRegistration = extensionRegistry.findConstraintTag(normalizedTagName);
|
|
2375
|
-
const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
|
|
2376
|
-
if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
|
|
2377
|
-
(candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
|
|
2378
|
-
)) {
|
|
2379
|
-
addTypeMismatch(
|
|
2380
|
-
diagnostics,
|
|
2381
|
-
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
2382
|
-
constraint.provenance
|
|
2383
|
-
);
|
|
2384
|
-
return;
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
if (registration.applicableTypes === null) {
|
|
2388
|
-
if (!candidateTypes.some(
|
|
2389
|
-
(candidateType) => registration.isApplicableToType?.(candidateType) !== false
|
|
2390
|
-
)) {
|
|
2391
|
-
addTypeMismatch(
|
|
2392
|
-
diagnostics,
|
|
2393
|
-
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
2394
|
-
constraint.provenance
|
|
2395
|
-
);
|
|
2396
|
-
}
|
|
2397
|
-
return;
|
|
2398
|
-
}
|
|
2399
|
-
const applicableTypes = registration.applicableTypes;
|
|
2400
|
-
const matchesApplicableType = candidateTypes.some(
|
|
2401
|
-
(candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
|
|
2402
|
-
);
|
|
2403
|
-
if (!matchesApplicableType) {
|
|
2404
|
-
addTypeMismatch(
|
|
2405
|
-
diagnostics,
|
|
2406
|
-
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
2407
|
-
constraint.provenance
|
|
2408
|
-
);
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
function checkConstraintOnType(diagnostics, fieldName, type, constraint, typeRegistry, extensionRegistry) {
|
|
2412
|
-
const effectiveType = dereferenceAnalysisType(type, typeRegistry);
|
|
2413
|
-
const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
|
|
2414
|
-
const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
|
|
2415
|
-
const isArray = effectiveType.kind === "array";
|
|
2416
|
-
const isEnum = effectiveType.kind === "enum";
|
|
2417
|
-
const arrayItemType = effectiveType.kind === "array" ? dereferenceAnalysisType(effectiveType.items, typeRegistry) : void 0;
|
|
2418
|
-
const isStringArray2 = arrayItemType?.kind === "primitive" && arrayItemType.primitiveKind === "string";
|
|
2419
|
-
const label = typeLabel(effectiveType);
|
|
2420
|
-
switch (constraint.constraintKind) {
|
|
2421
|
-
case "minimum":
|
|
2422
|
-
case "maximum":
|
|
2423
|
-
case "exclusiveMinimum":
|
|
2424
|
-
case "exclusiveMaximum":
|
|
2425
|
-
case "multipleOf":
|
|
2426
|
-
if (!isNumber) {
|
|
2427
|
-
addTypeMismatch(
|
|
2428
|
-
diagnostics,
|
|
2429
|
-
`Field "${fieldName}": constraint "${constraint.constraintKind}" is only valid on number fields, but field type is "${label}"`,
|
|
2430
|
-
constraint.provenance
|
|
2431
|
-
);
|
|
2432
|
-
}
|
|
2433
|
-
break;
|
|
2434
|
-
case "minLength":
|
|
2435
|
-
case "maxLength":
|
|
2436
|
-
case "pattern":
|
|
2437
|
-
if (!isString && !isStringArray2) {
|
|
2438
|
-
addTypeMismatch(
|
|
2439
|
-
diagnostics,
|
|
2440
|
-
`Field "${fieldName}": constraint "${constraint.constraintKind}" is only valid on string fields or string array items, but field type is "${label}"`,
|
|
2441
|
-
constraint.provenance
|
|
2442
|
-
);
|
|
2443
|
-
}
|
|
2444
|
-
break;
|
|
2445
|
-
case "minItems":
|
|
2446
|
-
case "maxItems":
|
|
2447
|
-
case "uniqueItems":
|
|
2448
|
-
if (!isArray) {
|
|
2449
|
-
addTypeMismatch(
|
|
2450
|
-
diagnostics,
|
|
2451
|
-
`Field "${fieldName}": constraint "${constraint.constraintKind}" is only valid on array fields, but field type is "${label}"`,
|
|
2452
|
-
constraint.provenance
|
|
2453
|
-
);
|
|
2454
|
-
}
|
|
2455
|
-
break;
|
|
2456
|
-
case "allowedMembers":
|
|
2457
|
-
if (!isEnum) {
|
|
2458
|
-
addTypeMismatch(
|
|
2459
|
-
diagnostics,
|
|
2460
|
-
`Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
|
|
2461
|
-
constraint.provenance
|
|
2462
|
-
);
|
|
2463
|
-
}
|
|
2464
|
-
break;
|
|
2465
|
-
case "const": {
|
|
2466
|
-
const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
|
|
2467
|
-
effectiveType.primitiveKind
|
|
2468
|
-
) || effectiveType.kind === "enum";
|
|
2469
|
-
if (!isPrimitiveConstType) {
|
|
2470
|
-
addTypeMismatch(
|
|
2471
|
-
diagnostics,
|
|
2472
|
-
`Field "${fieldName}": constraint "const" is only valid on primitive or enum fields, but field type is "${label}"`,
|
|
2473
|
-
constraint.provenance
|
|
2474
|
-
);
|
|
2475
|
-
break;
|
|
2476
|
-
}
|
|
2477
|
-
if (effectiveType.kind === "primitive") {
|
|
2478
|
-
const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
|
|
2479
|
-
const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
|
|
2480
|
-
if (valueType !== expectedValueType) {
|
|
2481
|
-
addTypeMismatch(
|
|
2482
|
-
diagnostics,
|
|
2483
|
-
`Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
|
|
2484
|
-
constraint.provenance
|
|
2485
|
-
);
|
|
2486
|
-
}
|
|
2487
|
-
break;
|
|
2488
|
-
}
|
|
2489
|
-
const memberValues = effectiveType.members.map((member) => member.value);
|
|
2490
|
-
if (!memberValues.some((member) => jsonValueEquals(member, constraint.value))) {
|
|
2491
|
-
addTypeMismatch(
|
|
2492
|
-
diagnostics,
|
|
2493
|
-
`Field "${fieldName}": @const value ${JSON.stringify(constraint.value)} is not one of the enum members`,
|
|
2494
|
-
constraint.provenance
|
|
2495
|
-
);
|
|
2496
|
-
}
|
|
2497
|
-
break;
|
|
2498
|
-
}
|
|
2499
|
-
case "custom":
|
|
2500
|
-
checkCustomConstraint(
|
|
2501
|
-
diagnostics,
|
|
2502
|
-
fieldName,
|
|
2503
|
-
effectiveType,
|
|
2504
|
-
constraint,
|
|
2505
|
-
typeRegistry,
|
|
2506
|
-
extensionRegistry
|
|
2507
|
-
);
|
|
2508
|
-
break;
|
|
2509
|
-
default: {
|
|
2510
|
-
const exhaustive = constraint;
|
|
2511
|
-
throw new Error(`Unhandled constraint: ${JSON.stringify(exhaustive)}`);
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
function analyzeResolvedTargetState(diagnostics, state, typeRegistry, extensionRegistry) {
|
|
2516
|
-
checkNumericContradictions(diagnostics, state.targetName, state.effectiveConstraints);
|
|
2517
|
-
checkLengthContradictions(diagnostics, state.targetName, state.effectiveConstraints);
|
|
2518
|
-
checkAllowedMembersContradiction(diagnostics, state.targetName, state.effectiveConstraints);
|
|
2519
|
-
checkConstContradictions(diagnostics, state.targetName, state.effectiveConstraints);
|
|
2520
|
-
checkConstraintBroadening(diagnostics, state.targetName, state.effectiveConstraints);
|
|
2521
|
-
checkCustomConstraintSemantics(
|
|
2522
|
-
diagnostics,
|
|
2523
|
-
state.targetName,
|
|
2524
|
-
state.effectiveConstraints,
|
|
2525
|
-
extensionRegistry
|
|
2526
|
-
);
|
|
2527
|
-
for (const constraint of state.effectiveConstraints) {
|
|
2528
|
-
checkConstraintOnType(
|
|
2529
|
-
diagnostics,
|
|
2530
|
-
state.targetName,
|
|
2531
|
-
state.type,
|
|
2532
|
-
constraint,
|
|
2533
|
-
typeRegistry,
|
|
2534
|
-
extensionRegistry
|
|
2535
|
-
);
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
function analyzeConstraintTargets(fieldName, fieldType, constraints, typeRegistry, options) {
|
|
2539
|
-
const diagnostics = [];
|
|
2540
|
-
const targetStates = buildConstraintTargetStates(fieldName, fieldType, constraints, typeRegistry);
|
|
2541
|
-
for (const targetState of targetStates) {
|
|
2542
|
-
switch (targetState.kind) {
|
|
2543
|
-
case "resolved":
|
|
2544
|
-
analyzeResolvedTargetState(
|
|
2545
|
-
diagnostics,
|
|
2546
|
-
targetState,
|
|
2547
|
-
typeRegistry,
|
|
2548
|
-
options?.extensionRegistry
|
|
2549
|
-
);
|
|
2550
|
-
break;
|
|
2551
|
-
case "missing-property":
|
|
2552
|
-
for (const constraint of targetState.localConstraints) {
|
|
2553
|
-
addUnknownPathTarget(
|
|
2554
|
-
diagnostics,
|
|
2555
|
-
`Field "${targetState.targetName}": path-targeted constraint "${constraint.constraintKind}" references unknown path segment "${targetState.segment}"`,
|
|
2556
|
-
constraint.provenance
|
|
2557
|
-
);
|
|
2558
|
-
}
|
|
2559
|
-
break;
|
|
2560
|
-
case "unresolvable":
|
|
2561
|
-
for (const constraint of targetState.localConstraints) {
|
|
2562
|
-
addTypeMismatch(
|
|
2563
|
-
diagnostics,
|
|
2564
|
-
`Field "${targetState.targetName}": path-targeted constraint "${constraint.constraintKind}" is invalid because type "${typeLabel(targetState.type)}" cannot be traversed`,
|
|
2565
|
-
constraint.provenance
|
|
2566
|
-
);
|
|
2567
|
-
}
|
|
2568
|
-
break;
|
|
2569
|
-
default: {
|
|
2570
|
-
const exhaustive = targetState;
|
|
2571
|
-
throw new Error(`Unhandled target state: ${String(exhaustive)}`);
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
return {
|
|
2576
|
-
diagnostics,
|
|
2577
|
-
targetStates
|
|
2578
|
-
};
|
|
2579
|
-
}
|
|
2580
|
-
|
|
2581
|
-
// src/file-snapshots.ts
|
|
2582
|
-
import * as ts4 from "typescript";
|
|
2583
|
-
|
|
2584
|
-
// src/compiler-signatures.ts
|
|
2585
|
-
import * as ts2 from "typescript";
|
|
2586
|
-
var PRELUDE_LINES = [
|
|
2587
|
-
"type FormSpecPlacement =",
|
|
2588
|
-
' | "class"',
|
|
2589
|
-
' | "class-field"',
|
|
2590
|
-
' | "class-method"',
|
|
2591
|
-
' | "interface"',
|
|
2592
|
-
' | "interface-field"',
|
|
2593
|
-
' | "type-alias"',
|
|
2594
|
-
' | "type-alias-field"',
|
|
2595
|
-
' | "variable"',
|
|
2596
|
-
' | "function"',
|
|
2597
|
-
' | "function-parameter"',
|
|
2598
|
-
' | "method-parameter";',
|
|
2599
|
-
"",
|
|
2600
|
-
"type FormSpecCapability =",
|
|
2601
|
-
' | "numeric-comparable"',
|
|
2602
|
-
' | "string-like"',
|
|
2603
|
-
' | "array-like"',
|
|
2604
|
-
' | "enum-member-addressable"',
|
|
2605
|
-
' | "json-like"',
|
|
2606
|
-
' | "condition-like"',
|
|
2607
|
-
' | "object-like";',
|
|
2608
|
-
"",
|
|
2609
|
-
"interface TagContext<P extends FormSpecPlacement, Host, Subject> {",
|
|
2610
|
-
" readonly placement: P;",
|
|
2611
|
-
" readonly hostType: Host;",
|
|
2612
|
-
" readonly subjectType: Subject;",
|
|
2613
|
-
"}",
|
|
2614
|
-
"",
|
|
2615
|
-
"type NonNullish<T> = Exclude<T, null | undefined>;",
|
|
2616
|
-
"",
|
|
2617
|
-
"type ProvidesCapability<T, Capability extends FormSpecCapability> =",
|
|
2618
|
-
' Capability extends "numeric-comparable"',
|
|
2619
|
-
" ? NonNullish<T> extends number | bigint",
|
|
2620
|
-
" ? true",
|
|
2621
|
-
" : false",
|
|
2622
|
-
' : Capability extends "string-like"',
|
|
2623
|
-
" ? NonNullish<T> extends string",
|
|
2624
|
-
" ? true",
|
|
2625
|
-
" : false",
|
|
2626
|
-
' : Capability extends "array-like"',
|
|
2627
|
-
" ? NonNullish<T> extends readonly unknown[]",
|
|
2628
|
-
" ? true",
|
|
2629
|
-
" : false",
|
|
2630
|
-
' : Capability extends "enum-member-addressable"',
|
|
2631
|
-
" ? NonNullish<T> extends string",
|
|
2632
|
-
" ? true",
|
|
2633
|
-
" : false",
|
|
2634
|
-
' : Capability extends "json-like"',
|
|
2635
|
-
" ? true",
|
|
2636
|
-
' : Capability extends "condition-like"',
|
|
2637
|
-
" ? true",
|
|
2638
|
-
' : Capability extends "object-like"',
|
|
2639
|
-
" ? NonNullish<T> extends readonly unknown[]",
|
|
2640
|
-
" ? false",
|
|
2641
|
-
" : NonNullish<T> extends object",
|
|
2642
|
-
" ? true",
|
|
2643
|
-
" : false",
|
|
2644
|
-
" : false;",
|
|
2645
|
-
"",
|
|
2646
|
-
"type NestedPathOfCapability<Subject, Capability extends FormSpecCapability> =",
|
|
2647
|
-
" NonNullish<Subject> extends readonly (infer Item)[]",
|
|
2648
|
-
" ? NestedPathOfCapability<Item, Capability>",
|
|
2649
|
-
" : NonNullish<Subject> extends object",
|
|
2650
|
-
" ? {",
|
|
2651
|
-
" [Key in Extract<keyof NonNullish<Subject>, string>]:",
|
|
2652
|
-
" | (ProvidesCapability<NonNullish<Subject>[Key], Capability> extends true ? Key : never)",
|
|
2653
|
-
" | (NestedPathOfCapability<NonNullish<Subject>[Key], Capability> extends never",
|
|
2654
|
-
" ? never",
|
|
2655
|
-
" : `${Key}.${NestedPathOfCapability<NonNullish<Subject>[Key], Capability>}`);",
|
|
2656
|
-
" }[Extract<keyof NonNullish<Subject>, string>]",
|
|
2657
|
-
" : never;",
|
|
2658
|
-
"",
|
|
2659
|
-
"type PathOfCapability<Subject, Capability extends FormSpecCapability> =",
|
|
2660
|
-
" NestedPathOfCapability<Subject, Capability>;",
|
|
2661
|
-
"",
|
|
2662
|
-
"type MemberTarget<Subject> = Extract<keyof NonNullish<Subject>, string>;",
|
|
2663
|
-
"",
|
|
2664
|
-
'type VariantTarget<Subject> = "singular" | "plural";',
|
|
2665
|
-
"",
|
|
2666
|
-
"type FormSpecCondition = unknown;",
|
|
2667
|
-
"type JsonValue = unknown;",
|
|
2668
|
-
"",
|
|
2669
|
-
"declare function __ctx<P extends FormSpecPlacement, Host, Subject>(): TagContext<P, Host, Subject>;",
|
|
2670
|
-
"declare function __path<Subject, Capability extends FormSpecCapability>(",
|
|
2671
|
-
" path: PathOfCapability<Subject, Capability>",
|
|
2672
|
-
"): PathOfCapability<Subject, Capability>;",
|
|
2673
|
-
"declare function __member<Subject>(member: MemberTarget<Subject>): MemberTarget<Subject>;",
|
|
2674
|
-
"declare function __variant<Subject>(variant: VariantTarget<Subject>): VariantTarget<Subject>;"
|
|
2675
|
-
];
|
|
2676
|
-
function placementUnion(placements) {
|
|
2677
|
-
return placements.map((placement) => JSON.stringify(placement)).join(" | ");
|
|
2678
|
-
}
|
|
2679
|
-
function renderValueType(valueKind) {
|
|
2680
|
-
switch (valueKind) {
|
|
2681
|
-
case "number":
|
|
2682
|
-
case "integer":
|
|
2683
|
-
case "signedInteger":
|
|
2684
|
-
return "number";
|
|
2685
|
-
case "string":
|
|
2686
|
-
return "string";
|
|
2687
|
-
case "json":
|
|
2688
|
-
return "JsonValue";
|
|
2689
|
-
case "boolean":
|
|
2690
|
-
return "boolean";
|
|
2691
|
-
case "condition":
|
|
2692
|
-
return "FormSpecCondition";
|
|
2693
|
-
case void 0:
|
|
2694
|
-
return "unknown";
|
|
2695
|
-
default: {
|
|
2696
|
-
const exhaustive = valueKind;
|
|
2697
|
-
return exhaustive;
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
|
-
function renderTargetParameterType(parameter) {
|
|
2702
|
-
switch (parameter.kind) {
|
|
2703
|
-
case "target-path":
|
|
2704
|
-
return parameter.capability === void 0 ? "PathOfCapability<Subject, FormSpecCapability>" : `PathOfCapability<Subject, ${JSON.stringify(parameter.capability)}>`;
|
|
2705
|
-
case "target-member":
|
|
2706
|
-
return "MemberTarget<Subject>";
|
|
2707
|
-
case "target-variant":
|
|
2708
|
-
return "VariantTarget<Subject>";
|
|
2709
|
-
case "value":
|
|
2710
|
-
return renderValueType(parameter.valueKind);
|
|
2711
|
-
default: {
|
|
2712
|
-
const exhaustive = parameter.kind;
|
|
2713
|
-
return exhaustive;
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
function renderSignature(tagName, signature) {
|
|
2718
|
-
const parameters = signature.parameters.map((parameter, index) => {
|
|
2719
|
-
const name = parameter.kind === "value" ? "value" : `target${String(index)}`;
|
|
2720
|
-
return `${name}: ${renderTargetParameterType(parameter)}`;
|
|
2721
|
-
});
|
|
2722
|
-
return [
|
|
2723
|
-
` function ${getSyntheticTagHelperName(tagName)}<Host, Subject>(`,
|
|
2724
|
-
` ctx: TagContext<${placementUnion(signature.placements)}, Host, Subject>${parameters.length > 0 ? "," : ""}`,
|
|
2725
|
-
...parameters.map(
|
|
2726
|
-
(parameter, index) => ` ${parameter}${index === parameters.length - 1 ? "" : ","}`
|
|
2727
|
-
),
|
|
2728
|
-
" ): void;"
|
|
2729
|
-
].join("\n");
|
|
2730
|
-
}
|
|
2731
|
-
function getSyntheticTagHelperName(tagName) {
|
|
2732
|
-
return `tag_${tagName}`;
|
|
2733
|
-
}
|
|
2734
|
-
function targetKindForParameter(parameter) {
|
|
2735
|
-
switch (parameter.kind) {
|
|
2736
|
-
case "target-path":
|
|
2737
|
-
return "path";
|
|
2738
|
-
case "target-member":
|
|
2739
|
-
return "member";
|
|
2740
|
-
case "target-variant":
|
|
2741
|
-
return "variant";
|
|
2742
|
-
case "value":
|
|
2743
|
-
return null;
|
|
2744
|
-
default: {
|
|
2745
|
-
const exhaustive = parameter.kind;
|
|
2746
|
-
return exhaustive;
|
|
2747
|
-
}
|
|
2748
|
-
}
|
|
2749
|
-
}
|
|
2750
|
-
function getSignatureTargetKind(signature) {
|
|
2751
|
-
for (const parameter of signature.parameters) {
|
|
2752
|
-
const targetKind = targetKindForParameter(parameter);
|
|
2753
|
-
if (targetKind !== null) {
|
|
2754
|
-
return targetKind;
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
return null;
|
|
2758
|
-
}
|
|
2759
|
-
function getTargetParameter(signature) {
|
|
2760
|
-
return signature.parameters.find(
|
|
2761
|
-
(parameter) => parameter.kind !== "value"
|
|
2762
|
-
) ?? null;
|
|
2763
|
-
}
|
|
2764
|
-
function getPathTargetCapability(signature) {
|
|
2765
|
-
const parameter = getTargetParameter(signature);
|
|
2766
|
-
if (parameter?.kind !== "target-path") {
|
|
2767
|
-
throw new Error(`Invariant violation: expected a path-target synthetic signature`);
|
|
2768
|
-
}
|
|
2769
|
-
if (parameter.capability === void 0) {
|
|
2770
|
-
throw new Error(
|
|
2771
|
-
`Invariant violation: path-target synthetic signatures must declare a capability`
|
|
2772
|
-
);
|
|
2773
|
-
}
|
|
2774
|
-
return JSON.stringify(parameter.capability);
|
|
2775
|
-
}
|
|
2776
|
-
function renderTargetArgument(target, signature, subjectType) {
|
|
2777
|
-
switch (target.kind) {
|
|
2778
|
-
case "path":
|
|
2779
|
-
return `__path<${subjectType}, ${getPathTargetCapability(signature)}>(${JSON.stringify(
|
|
2780
|
-
target.text
|
|
2781
|
-
)})`;
|
|
2782
|
-
case "member":
|
|
2783
|
-
return `__member<${subjectType}>(${JSON.stringify(target.text)})`;
|
|
2784
|
-
case "variant":
|
|
2785
|
-
return `__variant<${subjectType}>(${JSON.stringify(target.text)})`;
|
|
2786
|
-
}
|
|
2787
|
-
}
|
|
2788
|
-
function getMatchingTagSignatures(definition, placement, targetKind) {
|
|
2789
|
-
return definition.signatures.filter(
|
|
2790
|
-
(signature) => signature.placements.includes(placement) && getSignatureTargetKind(signature) === targetKind
|
|
2791
|
-
);
|
|
2792
|
-
}
|
|
2793
|
-
function buildSyntheticHelperPrelude(extensions) {
|
|
2794
|
-
const lines = [...PRELUDE_LINES, "", "declare namespace __formspec {"];
|
|
2795
|
-
for (const definition of getAllTagDefinitions(extensions)) {
|
|
2796
|
-
for (const signature of definition.signatures) {
|
|
2797
|
-
lines.push(renderSignature(definition.canonicalName, signature));
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
2800
|
-
lines.push("}");
|
|
2801
|
-
return lines.join("\n");
|
|
2802
|
-
}
|
|
2803
|
-
function lowerTagApplicationToSyntheticCall(options) {
|
|
2804
|
-
const definition = getTagDefinition(options.tagName, options.extensions);
|
|
2805
|
-
if (definition === null) {
|
|
2806
|
-
throw new Error(`Unknown FormSpec tag: ${options.tagName}`);
|
|
2807
|
-
}
|
|
2808
|
-
const targetKind = options.target?.kind ?? null;
|
|
2809
|
-
const matchingSignatures = getMatchingTagSignatures(definition, options.placement, targetKind);
|
|
2810
|
-
if (matchingSignatures.length === 0) {
|
|
2811
|
-
throw new Error(
|
|
2812
|
-
`No synthetic signature for @${definition.canonicalName} on placement "${options.placement}"` + (targetKind === null ? "" : ` with target kind "${targetKind}"`)
|
|
2813
|
-
);
|
|
2814
|
-
}
|
|
2815
|
-
const args = [
|
|
2816
|
-
`__ctx<${JSON.stringify(options.placement)}, ${options.hostType}, ${options.subjectType}>()`
|
|
2817
|
-
];
|
|
2818
|
-
const signature = matchingSignatures[0];
|
|
2819
|
-
if (signature === void 0) {
|
|
2820
|
-
throw new Error(
|
|
2821
|
-
`Invariant violation: missing synthetic signature for @${definition.canonicalName}`
|
|
2822
|
-
);
|
|
2823
|
-
}
|
|
2824
|
-
if (options.target !== void 0 && options.target !== null) {
|
|
2825
|
-
args.push(renderTargetArgument(options.target, signature, options.subjectType));
|
|
2826
|
-
}
|
|
2827
|
-
if (options.argumentExpression !== void 0 && options.argumentExpression !== null) {
|
|
2828
|
-
args.push(options.argumentExpression);
|
|
2829
|
-
}
|
|
2830
|
-
return {
|
|
2831
|
-
definition,
|
|
2832
|
-
matchingSignatures,
|
|
2833
|
-
callExpression: `__formspec.${getSyntheticTagHelperName(definition.canonicalName)}(${args.join(", ")});`
|
|
2834
|
-
};
|
|
2835
|
-
}
|
|
2836
|
-
function createSyntheticCompilerHost(fileName, sourceText, compilerOptions) {
|
|
2837
|
-
const host = ts2.createCompilerHost(compilerOptions, true);
|
|
2838
|
-
const originalGetSourceFile = host.getSourceFile.bind(host);
|
|
2839
|
-
host.getSourceFile = (requestedFileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
2840
|
-
if (requestedFileName === fileName) {
|
|
2841
|
-
return ts2.createSourceFile(requestedFileName, sourceText, languageVersion, true);
|
|
2842
|
-
}
|
|
2843
|
-
return originalGetSourceFile(
|
|
2844
|
-
requestedFileName,
|
|
2845
|
-
languageVersion,
|
|
2846
|
-
onError,
|
|
2847
|
-
shouldCreateNewSourceFile
|
|
2848
|
-
);
|
|
2849
|
-
};
|
|
2850
|
-
host.readFile = (requestedFileName) => {
|
|
2851
|
-
if (requestedFileName === fileName) {
|
|
2852
|
-
return sourceText;
|
|
2853
|
-
}
|
|
2854
|
-
return ts2.sys.readFile(requestedFileName);
|
|
2855
|
-
};
|
|
2856
|
-
host.fileExists = (requestedFileName) => requestedFileName === fileName || ts2.sys.fileExists(requestedFileName);
|
|
2857
|
-
host.writeFile = () => void 0;
|
|
2858
|
-
return host;
|
|
2859
|
-
}
|
|
2860
|
-
function flattenDiagnosticMessage(message) {
|
|
2861
|
-
return ts2.flattenDiagnosticMessageText(message, "\n");
|
|
2862
|
-
}
|
|
2863
|
-
var syntheticCheckCache = /* @__PURE__ */ new Map();
|
|
2864
|
-
function checkSyntheticTagApplication(options) {
|
|
2865
|
-
const lowered = lowerTagApplicationToSyntheticCall(options);
|
|
2866
|
-
const sourceText = [
|
|
2867
|
-
buildSyntheticHelperPrelude(options.extensions),
|
|
2868
|
-
"",
|
|
2869
|
-
...options.supportingDeclarations ?? [],
|
|
2870
|
-
"",
|
|
2871
|
-
lowered.callExpression
|
|
2872
|
-
].join("\n");
|
|
2873
|
-
const cached = syntheticCheckCache.get(sourceText);
|
|
2874
|
-
if (cached !== void 0) {
|
|
2875
|
-
return cached;
|
|
2876
|
-
}
|
|
2877
|
-
const fileName = "/virtual/formspec-synthetic.ts";
|
|
2878
|
-
const compilerOptions = {
|
|
2879
|
-
strict: true,
|
|
2880
|
-
noEmit: true,
|
|
2881
|
-
target: ts2.ScriptTarget.ES2022,
|
|
2882
|
-
module: ts2.ModuleKind.ESNext,
|
|
2883
|
-
lib: ["lib.es2022.d.ts"]
|
|
2884
|
-
};
|
|
2885
|
-
const host = createSyntheticCompilerHost(fileName, sourceText, compilerOptions);
|
|
2886
|
-
const program = ts2.createProgram([fileName], compilerOptions, host);
|
|
2887
|
-
const diagnostics = ts2.getPreEmitDiagnostics(program).filter((diagnostic) => diagnostic.file === void 0 || diagnostic.file.fileName === fileName).map((diagnostic) => ({
|
|
2888
|
-
code: diagnostic.code,
|
|
2889
|
-
message: flattenDiagnosticMessage(diagnostic.messageText)
|
|
2890
|
-
}));
|
|
2891
|
-
const result = {
|
|
2892
|
-
sourceText,
|
|
2893
|
-
diagnostics
|
|
2894
|
-
};
|
|
2895
|
-
syntheticCheckCache.set(sourceText, result);
|
|
2896
|
-
return result;
|
|
2897
|
-
}
|
|
2898
|
-
|
|
2899
|
-
// src/source-bindings.ts
|
|
2900
|
-
import * as ts3 from "typescript";
|
|
2901
|
-
function getLastLeadingDocCommentRange(node, sourceFile) {
|
|
2902
|
-
const ranges = ts3.getLeadingCommentRanges(sourceFile.text, node.getFullStart()) ?? [];
|
|
2903
|
-
const docRanges = ranges.filter(
|
|
2904
|
-
(range) => sourceFile.text.slice(range.pos, range.end).startsWith("/**")
|
|
2905
|
-
);
|
|
2906
|
-
return docRanges.length === 0 ? null : docRanges[docRanges.length - 1] ?? null;
|
|
2907
|
-
}
|
|
2908
|
-
function getSubjectType(node, checker) {
|
|
2909
|
-
if (ts3.isClassDeclaration(node) || ts3.isInterfaceDeclaration(node) || ts3.isTypeAliasDeclaration(node) || ts3.isPropertyDeclaration(node) || ts3.isPropertySignature(node) || ts3.isMethodDeclaration(node) || ts3.isFunctionDeclaration(node) || ts3.isVariableDeclaration(node) || ts3.isParameter(node)) {
|
|
2910
|
-
return checker.getTypeAtLocation(node);
|
|
2911
|
-
}
|
|
2912
|
-
return void 0;
|
|
2913
|
-
}
|
|
2914
|
-
function getHostType(node, checker) {
|
|
2915
|
-
const parent = node.parent;
|
|
2916
|
-
if (ts3.isClassDeclaration(parent) || ts3.isInterfaceDeclaration(parent) || ts3.isTypeLiteralNode(parent) || ts3.isTypeAliasDeclaration(parent)) {
|
|
2917
|
-
return checker.getTypeAtLocation(parent);
|
|
2918
|
-
}
|
|
2919
|
-
return getSubjectType(node, checker);
|
|
2920
|
-
}
|
|
2921
|
-
function findDeclarationForCommentOffset(sourceFile, offset) {
|
|
2922
|
-
let bestMatch = null;
|
|
2923
|
-
const visit = (node) => {
|
|
2924
|
-
if (resolveDeclarationPlacement(node) !== null) {
|
|
2925
|
-
const range = getLastLeadingDocCommentRange(node, sourceFile);
|
|
2926
|
-
if (range !== null && offset >= range.pos && offset <= range.end) {
|
|
2927
|
-
if (bestMatch === null || node.getWidth(sourceFile) < bestMatch.getWidth(sourceFile)) {
|
|
2928
|
-
bestMatch = node;
|
|
2929
|
-
}
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
2932
|
-
ts3.forEachChild(node, visit);
|
|
2933
|
-
};
|
|
2934
|
-
visit(sourceFile);
|
|
2935
|
-
return bestMatch;
|
|
2936
|
-
}
|
|
2937
|
-
|
|
2938
|
-
// src/semantic-protocol.ts
|
|
2939
|
-
var FORMSPEC_ANALYSIS_PROTOCOL_VERSION = 1;
|
|
2940
|
-
var FORMSPEC_ANALYSIS_SCHEMA_VERSION = 1;
|
|
2941
|
-
function isObjectRecord(value) {
|
|
2942
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2943
|
-
}
|
|
2944
|
-
function isCommentSpan(value) {
|
|
2945
|
-
if (!isObjectRecord(value)) {
|
|
2946
|
-
return false;
|
|
2947
|
-
}
|
|
2948
|
-
const candidate = value;
|
|
2949
|
-
return typeof candidate.start === "number" && typeof candidate.end === "number";
|
|
2950
|
-
}
|
|
2951
|
-
function isStringArray(value) {
|
|
2952
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
2953
|
-
}
|
|
2954
|
-
function isPlacementArray(value) {
|
|
2955
|
-
return isStringArray(value);
|
|
2956
|
-
}
|
|
2957
|
-
function isTargetKindArray(value) {
|
|
2958
|
-
return isStringArray(value);
|
|
2959
|
-
}
|
|
2960
|
-
function isIpcEndpoint(value) {
|
|
2961
|
-
if (!isObjectRecord(value)) {
|
|
2962
|
-
return false;
|
|
2963
|
-
}
|
|
2964
|
-
const candidate = value;
|
|
2965
|
-
return (candidate.kind === "unix-socket" || candidate.kind === "windows-pipe") && typeof candidate.address === "string";
|
|
2966
|
-
}
|
|
2967
|
-
function isSerializedTagDefinition(value) {
|
|
2968
|
-
if (!isObjectRecord(value)) {
|
|
2969
|
-
return false;
|
|
2970
|
-
}
|
|
2971
|
-
const candidate = value;
|
|
2972
|
-
return typeof candidate.canonicalName === "string" && typeof candidate.completionDetail === "string" && typeof candidate.hoverMarkdown === "string";
|
|
2973
|
-
}
|
|
2974
|
-
function isSerializedTagSignature(value) {
|
|
2975
|
-
if (!isObjectRecord(value)) {
|
|
2976
|
-
return false;
|
|
2977
|
-
}
|
|
2978
|
-
const candidate = value;
|
|
2979
|
-
return typeof candidate.label === "string" && isPlacementArray(candidate.placements);
|
|
2980
|
-
}
|
|
2981
|
-
function isSerializedCommentTargetSpecifier(value) {
|
|
2982
|
-
if (!isObjectRecord(value)) {
|
|
2983
|
-
return false;
|
|
2984
|
-
}
|
|
2985
|
-
const candidate = value;
|
|
2986
|
-
return typeof candidate.rawText === "string" && typeof candidate.valid === "boolean" && typeof candidate.kind === "string" && isCommentSpan(candidate.fullSpan) && isCommentSpan(candidate.colonSpan) && isCommentSpan(candidate.span);
|
|
2987
|
-
}
|
|
2988
|
-
function isSerializedTagSemanticContext(value) {
|
|
2989
|
-
if (!isObjectRecord(value)) {
|
|
2990
|
-
return false;
|
|
2991
|
-
}
|
|
2992
|
-
const candidate = value;
|
|
2993
|
-
return typeof candidate.tagName === "string" && (candidate.tagDefinition === null || isSerializedTagDefinition(candidate.tagDefinition)) && (candidate.placement === null || typeof candidate.placement === "string") && isTargetKindArray(candidate.supportedTargets) && isStringArray(candidate.targetCompletions) && isStringArray(candidate.compatiblePathTargets) && isStringArray(candidate.valueLabels) && Array.isArray(candidate.signatures) && candidate.signatures.every(isSerializedTagSignature) && (candidate.tagHoverMarkdown === null || typeof candidate.tagHoverMarkdown === "string") && (candidate.targetHoverMarkdown === null || typeof candidate.targetHoverMarkdown === "string") && (candidate.argumentHoverMarkdown === null || typeof candidate.argumentHoverMarkdown === "string");
|
|
2994
|
-
}
|
|
2995
|
-
function isSerializedCompletionContext(value) {
|
|
2996
|
-
if (!isObjectRecord(value)) {
|
|
2997
|
-
return false;
|
|
2998
|
-
}
|
|
2999
|
-
const candidate = value;
|
|
3000
|
-
if (typeof candidate.kind !== "string") {
|
|
3001
|
-
return false;
|
|
3002
|
-
}
|
|
3003
|
-
switch (candidate.kind) {
|
|
3004
|
-
case "tag-name":
|
|
3005
|
-
return typeof candidate.prefix === "string" && Array.isArray(candidate.availableTags) ? candidate.availableTags.every(isSerializedTagDefinition) : false;
|
|
3006
|
-
case "target":
|
|
3007
|
-
return isSerializedTagSemanticContext(candidate.semantic);
|
|
3008
|
-
case "argument":
|
|
3009
|
-
return isSerializedTagSemanticContext(candidate.semantic) && isStringArray(candidate.valueLabels);
|
|
3010
|
-
case "none":
|
|
3011
|
-
return true;
|
|
3012
|
-
default:
|
|
3013
|
-
return false;
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
function isSerializedHoverInfo(value) {
|
|
3017
|
-
if (!isObjectRecord(value)) {
|
|
3018
|
-
return false;
|
|
3019
|
-
}
|
|
3020
|
-
const candidate = value;
|
|
3021
|
-
return (candidate.kind === "tag-name" || candidate.kind === "target" || candidate.kind === "argument") && typeof candidate.markdown === "string";
|
|
3022
|
-
}
|
|
3023
|
-
function hasCurrentProtocolVersion(value) {
|
|
3024
|
-
return isObjectRecord(value) && value["protocolVersion"] === FORMSPEC_ANALYSIS_PROTOCOL_VERSION;
|
|
3025
|
-
}
|
|
3026
|
-
function isAnalysisDiagnostic(value) {
|
|
3027
|
-
if (!isObjectRecord(value)) {
|
|
3028
|
-
return false;
|
|
3029
|
-
}
|
|
3030
|
-
const candidate = value;
|
|
3031
|
-
return typeof candidate.code === "string" && typeof candidate.message === "string" && isCommentSpan(candidate.range) && (candidate.severity === "error" || candidate.severity === "warning" || candidate.severity === "info");
|
|
3032
|
-
}
|
|
3033
|
-
function isAnalysisTagSnapshot(value) {
|
|
3034
|
-
if (!isObjectRecord(value)) {
|
|
3035
|
-
return false;
|
|
3036
|
-
}
|
|
3037
|
-
const candidate = value;
|
|
3038
|
-
return typeof candidate.rawTagName === "string" && typeof candidate.normalizedTagName === "string" && typeof candidate.recognized === "boolean" && isCommentSpan(candidate.fullSpan) && isCommentSpan(candidate.tagNameSpan) && (candidate.payloadSpan === null || isCommentSpan(candidate.payloadSpan)) && (candidate.target === null || isSerializedCommentTargetSpecifier(candidate.target)) && (candidate.argumentSpan === null || isCommentSpan(candidate.argumentSpan)) && typeof candidate.argumentText === "string" && isSerializedTagSemanticContext(candidate.semantic);
|
|
3039
|
-
}
|
|
3040
|
-
function isAnalysisCommentSnapshot(value) {
|
|
3041
|
-
if (!isObjectRecord(value)) {
|
|
3042
|
-
return false;
|
|
3043
|
-
}
|
|
3044
|
-
const candidate = value;
|
|
3045
|
-
return isCommentSpan(candidate.commentSpan) && isCommentSpan(candidate.declarationSpan) && (candidate.placement === null || typeof candidate.placement === "string") && (candidate.subjectType === null || typeof candidate.subjectType === "string") && (candidate.hostType === null || typeof candidate.hostType === "string") && Array.isArray(candidate.tags) && candidate.tags.every(isAnalysisTagSnapshot);
|
|
3046
|
-
}
|
|
3047
|
-
function isAnalysisFileSnapshot(value) {
|
|
3048
|
-
if (!isObjectRecord(value)) {
|
|
3049
|
-
return false;
|
|
3050
|
-
}
|
|
3051
|
-
const candidate = value;
|
|
3052
|
-
return typeof candidate.filePath === "string" && typeof candidate.sourceHash === "string" && typeof candidate.generatedAt === "string" && Array.isArray(candidate.comments) && candidate.comments.every(isAnalysisCommentSnapshot) && Array.isArray(candidate.diagnostics) && candidate.diagnostics.every(isAnalysisDiagnostic);
|
|
3053
|
-
}
|
|
3054
|
-
function isFormSpecAnalysisManifest(value) {
|
|
3055
|
-
if (!isObjectRecord(value)) {
|
|
3056
|
-
return false;
|
|
3057
|
-
}
|
|
3058
|
-
const candidate = value;
|
|
3059
|
-
return candidate.protocolVersion === FORMSPEC_ANALYSIS_PROTOCOL_VERSION && candidate.analysisSchemaVersion === FORMSPEC_ANALYSIS_SCHEMA_VERSION && typeof candidate.workspaceRoot === "string" && typeof candidate.workspaceId === "string" && isIpcEndpoint(candidate.endpoint) && typeof candidate.typescriptVersion === "string" && typeof candidate.extensionFingerprint === "string" && typeof candidate.generation === "number" && typeof candidate.updatedAt === "string";
|
|
3060
|
-
}
|
|
3061
|
-
function isFormSpecSemanticQuery(value) {
|
|
3062
|
-
if (!hasCurrentProtocolVersion(value)) {
|
|
3063
|
-
return false;
|
|
3064
|
-
}
|
|
3065
|
-
const candidate = value;
|
|
3066
|
-
if (typeof candidate.kind !== "string") {
|
|
3067
|
-
return false;
|
|
3068
|
-
}
|
|
3069
|
-
switch (candidate.kind) {
|
|
3070
|
-
case "health":
|
|
3071
|
-
return true;
|
|
3072
|
-
case "completion":
|
|
3073
|
-
case "hover":
|
|
3074
|
-
return typeof candidate.filePath === "string" && typeof candidate.offset === "number";
|
|
3075
|
-
case "diagnostics":
|
|
3076
|
-
case "file-snapshot":
|
|
3077
|
-
return typeof candidate.filePath === "string";
|
|
3078
|
-
default:
|
|
3079
|
-
return false;
|
|
3080
|
-
}
|
|
3081
|
-
}
|
|
3082
|
-
function isFormSpecSemanticResponse(value) {
|
|
3083
|
-
if (!hasCurrentProtocolVersion(value)) {
|
|
3084
|
-
return false;
|
|
3085
|
-
}
|
|
3086
|
-
const candidate = value;
|
|
3087
|
-
if (typeof candidate.kind !== "string") {
|
|
3088
|
-
return false;
|
|
3089
|
-
}
|
|
3090
|
-
switch (candidate.kind) {
|
|
3091
|
-
case "health":
|
|
3092
|
-
return isFormSpecAnalysisManifest(candidate.manifest);
|
|
3093
|
-
case "completion":
|
|
3094
|
-
return typeof candidate.sourceHash === "string" && isSerializedCompletionContext(candidate.context);
|
|
3095
|
-
case "hover":
|
|
3096
|
-
return typeof candidate.sourceHash === "string" && (candidate.hover === null || isSerializedHoverInfo(candidate.hover));
|
|
3097
|
-
case "diagnostics":
|
|
3098
|
-
return typeof candidate.sourceHash === "string" && Array.isArray(candidate.diagnostics) && candidate.diagnostics.every(isAnalysisDiagnostic);
|
|
3099
|
-
case "file-snapshot":
|
|
3100
|
-
return candidate.snapshot === null || isAnalysisFileSnapshot(candidate.snapshot);
|
|
3101
|
-
case "error":
|
|
3102
|
-
return typeof candidate.error === "string";
|
|
3103
|
-
default:
|
|
3104
|
-
return false;
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
function computeFormSpecTextHash(text) {
|
|
3108
|
-
let hash = 2166136261;
|
|
3109
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
3110
|
-
hash ^= text.charCodeAt(index);
|
|
3111
|
-
hash = Math.imul(hash, 16777619);
|
|
3112
|
-
}
|
|
3113
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
3114
|
-
}
|
|
3115
|
-
function serializeCommentTargetSpecifier(target) {
|
|
3116
|
-
if (target === null) {
|
|
3117
|
-
return null;
|
|
3118
|
-
}
|
|
3119
|
-
return {
|
|
3120
|
-
rawText: target.rawText,
|
|
3121
|
-
valid: target.valid,
|
|
3122
|
-
kind: target.kind,
|
|
3123
|
-
fullSpan: target.fullSpan,
|
|
3124
|
-
colonSpan: target.colonSpan,
|
|
3125
|
-
span: target.span
|
|
795
|
+
rawText: target.rawText,
|
|
796
|
+
valid: target.valid,
|
|
797
|
+
kind: target.kind,
|
|
798
|
+
fullSpan: target.fullSpan,
|
|
799
|
+
colonSpan: target.colonSpan,
|
|
800
|
+
span: target.span
|
|
3126
801
|
};
|
|
3127
802
|
}
|
|
3128
803
|
function serializeCommentTagSemanticContext(semantic) {
|
|
@@ -3199,233 +874,6 @@ function serializeParsedCommentTag(tag, semantic) {
|
|
|
3199
874
|
};
|
|
3200
875
|
}
|
|
3201
876
|
|
|
3202
|
-
// src/file-snapshots.ts
|
|
3203
|
-
function spanFromPos(start, end) {
|
|
3204
|
-
return { start, end };
|
|
3205
|
-
}
|
|
3206
|
-
function typeToString(type, checker) {
|
|
3207
|
-
if (type === void 0) {
|
|
3208
|
-
return null;
|
|
3209
|
-
}
|
|
3210
|
-
return checker.typeToString(type, void 0, ts4.TypeFormatFlags.NoTruncation);
|
|
3211
|
-
}
|
|
3212
|
-
function supportingDeclarationsForType(type) {
|
|
3213
|
-
if (type === void 0) {
|
|
3214
|
-
return [];
|
|
3215
|
-
}
|
|
3216
|
-
const symbol = type.aliasSymbol ?? type.getSymbol();
|
|
3217
|
-
const declarations = symbol?.declarations ?? [];
|
|
3218
|
-
return declarations.map(
|
|
3219
|
-
(declaration) => declaration.getSourceFile().text.slice(declaration.getFullStart(), declaration.getEnd())
|
|
3220
|
-
).filter((declarationText) => declarationText.trim().length > 0);
|
|
3221
|
-
}
|
|
3222
|
-
function getSyntheticTargetForTag(tag) {
|
|
3223
|
-
if (tag.target === null) {
|
|
3224
|
-
return null;
|
|
3225
|
-
}
|
|
3226
|
-
switch (tag.target.kind) {
|
|
3227
|
-
case "path":
|
|
3228
|
-
case "member":
|
|
3229
|
-
case "variant":
|
|
3230
|
-
return {
|
|
3231
|
-
kind: tag.target.kind,
|
|
3232
|
-
text: tag.target.rawText
|
|
3233
|
-
};
|
|
3234
|
-
case "ambiguous":
|
|
3235
|
-
return {
|
|
3236
|
-
kind: "path",
|
|
3237
|
-
text: tag.target.rawText
|
|
3238
|
-
};
|
|
3239
|
-
default: {
|
|
3240
|
-
const exhaustive = tag.target.kind;
|
|
3241
|
-
return exhaustive;
|
|
3242
|
-
}
|
|
3243
|
-
}
|
|
3244
|
-
}
|
|
3245
|
-
function getArgumentExpression(argumentText, valueLabels, capabilityTargets) {
|
|
3246
|
-
const trimmed = argumentText.trim();
|
|
3247
|
-
if (trimmed === "") {
|
|
3248
|
-
return null;
|
|
3249
|
-
}
|
|
3250
|
-
if (valueLabels.some((label) => label.includes("number") || label.includes("integer"))) {
|
|
3251
|
-
return trimmed;
|
|
3252
|
-
}
|
|
3253
|
-
if (valueLabels.some((label) => label.includes("boolean"))) {
|
|
3254
|
-
return trimmed === "true" || trimmed === "false" ? trimmed : null;
|
|
3255
|
-
}
|
|
3256
|
-
if (valueLabels.some((label) => label.includes("json"))) {
|
|
3257
|
-
try {
|
|
3258
|
-
return JSON.stringify(JSON.parse(trimmed));
|
|
3259
|
-
} catch {
|
|
3260
|
-
return null;
|
|
3261
|
-
}
|
|
3262
|
-
}
|
|
3263
|
-
if (valueLabels.some((label) => label.includes("condition"))) {
|
|
3264
|
-
return "undefined as unknown as FormSpecCondition";
|
|
3265
|
-
}
|
|
3266
|
-
if (capabilityTargets.length > 0 || valueLabels.some((label) => label.includes("string"))) {
|
|
3267
|
-
return JSON.stringify(trimmed);
|
|
3268
|
-
}
|
|
3269
|
-
return JSON.stringify(trimmed);
|
|
3270
|
-
}
|
|
3271
|
-
function diagnosticSeverity(code) {
|
|
3272
|
-
switch (code) {
|
|
3273
|
-
case "INVALID_TAG_ARGUMENT":
|
|
3274
|
-
case "INVALID_TAG_PLACEMENT":
|
|
3275
|
-
case "TYPE_MISMATCH":
|
|
3276
|
-
case "UNKNOWN_PATH_TARGET":
|
|
3277
|
-
return "error";
|
|
3278
|
-
default:
|
|
3279
|
-
return "warning";
|
|
3280
|
-
}
|
|
3281
|
-
}
|
|
3282
|
-
function buildTagDiagnostics(sourceFile, checker, placement, hostType, subjectType, commentTags, semanticOptions) {
|
|
3283
|
-
if (placement === null || subjectType === void 0) {
|
|
3284
|
-
return [];
|
|
3285
|
-
}
|
|
3286
|
-
const diagnostics = [];
|
|
3287
|
-
const hostTypeText = typeToString(hostType, checker) ?? "unknown";
|
|
3288
|
-
const subjectTypeText = typeToString(subjectType, checker) ?? "unknown";
|
|
3289
|
-
const supportingDeclarations = [
|
|
3290
|
-
...supportingDeclarationsForType(hostType),
|
|
3291
|
-
...supportingDeclarationsForType(subjectType)
|
|
3292
|
-
];
|
|
3293
|
-
for (const tag of commentTags) {
|
|
3294
|
-
const semantic = getCommentTagSemanticContext(tag, semanticOptions);
|
|
3295
|
-
if (semantic.tagDefinition === null) {
|
|
3296
|
-
continue;
|
|
3297
|
-
}
|
|
3298
|
-
const target = getSyntheticTargetForTag(tag);
|
|
3299
|
-
const argumentExpression = getArgumentExpression(
|
|
3300
|
-
tag.argumentText,
|
|
3301
|
-
semantic.valueLabels,
|
|
3302
|
-
semantic.compatiblePathTargets
|
|
3303
|
-
);
|
|
3304
|
-
try {
|
|
3305
|
-
const result = checkSyntheticTagApplication({
|
|
3306
|
-
tagName: tag.normalizedTagName,
|
|
3307
|
-
placement,
|
|
3308
|
-
hostType: hostTypeText,
|
|
3309
|
-
subjectType: subjectTypeText,
|
|
3310
|
-
supportingDeclarations,
|
|
3311
|
-
...target === null ? {} : { target },
|
|
3312
|
-
...argumentExpression === null ? {} : { argumentExpression },
|
|
3313
|
-
...semanticOptions.extensions === void 0 ? {} : { extensions: semanticOptions.extensions }
|
|
3314
|
-
});
|
|
3315
|
-
for (const diagnostic of result.diagnostics) {
|
|
3316
|
-
const code = target !== null && diagnostic.message.includes("not assignable") ? target.kind === "path" ? "UNKNOWN_PATH_TARGET" : "TYPE_MISMATCH" : diagnostic.message.includes("Expected") ? "INVALID_TAG_ARGUMENT" : diagnostic.message.includes("No overload") ? "INVALID_TAG_PLACEMENT" : "TYPE_MISMATCH";
|
|
3317
|
-
diagnostics.push({
|
|
3318
|
-
code,
|
|
3319
|
-
message: diagnostic.message,
|
|
3320
|
-
range: tag.fullSpan,
|
|
3321
|
-
severity: diagnosticSeverity(code)
|
|
3322
|
-
});
|
|
3323
|
-
}
|
|
3324
|
-
} catch (error) {
|
|
3325
|
-
diagnostics.push({
|
|
3326
|
-
code: "INVALID_TAG_PLACEMENT",
|
|
3327
|
-
message: error instanceof Error ? error.message : String(error),
|
|
3328
|
-
range: tag.fullSpan,
|
|
3329
|
-
severity: "error"
|
|
3330
|
-
});
|
|
3331
|
-
}
|
|
3332
|
-
}
|
|
3333
|
-
return diagnostics;
|
|
3334
|
-
}
|
|
3335
|
-
function buildCommentSnapshot(node, sourceFile, checker, extensions) {
|
|
3336
|
-
const docComment = getLastLeadingDocCommentRange(node, sourceFile);
|
|
3337
|
-
if (docComment === null) {
|
|
3338
|
-
return null;
|
|
3339
|
-
}
|
|
3340
|
-
const commentText = sourceFile.text.slice(docComment.pos, docComment.end);
|
|
3341
|
-
const parsed = parseCommentBlock(commentText, {
|
|
3342
|
-
offset: docComment.pos,
|
|
3343
|
-
...extensions === void 0 ? {} : { extensions }
|
|
3344
|
-
});
|
|
3345
|
-
if (parsed.tags.length === 0) {
|
|
3346
|
-
return null;
|
|
3347
|
-
}
|
|
3348
|
-
const placement = resolveDeclarationPlacement(node);
|
|
3349
|
-
const subjectType = getSubjectType(node, checker);
|
|
3350
|
-
const hostType = getHostType(node, checker);
|
|
3351
|
-
const semanticOptions = {
|
|
3352
|
-
checker,
|
|
3353
|
-
...subjectType === void 0 ? {} : { subjectType },
|
|
3354
|
-
...placement === null ? {} : { placement },
|
|
3355
|
-
...extensions === void 0 ? {} : { extensions }
|
|
3356
|
-
};
|
|
3357
|
-
const tags = parsed.tags.map(
|
|
3358
|
-
(tag) => serializeParsedCommentTag(tag, getCommentTagSemanticContext(tag, semanticOptions))
|
|
3359
|
-
);
|
|
3360
|
-
return {
|
|
3361
|
-
commentSpan: spanFromPos(docComment.pos, docComment.end),
|
|
3362
|
-
declarationSpan: spanFromPos(node.getStart(sourceFile), node.getEnd()),
|
|
3363
|
-
placement,
|
|
3364
|
-
subjectType: typeToString(subjectType, checker),
|
|
3365
|
-
hostType: typeToString(hostType, checker),
|
|
3366
|
-
tags
|
|
3367
|
-
};
|
|
3368
|
-
}
|
|
3369
|
-
function buildFormSpecAnalysisFileSnapshot(sourceFile, options) {
|
|
3370
|
-
const comments = [];
|
|
3371
|
-
const diagnostics = [];
|
|
3372
|
-
const visit = (node) => {
|
|
3373
|
-
const placement = resolveDeclarationPlacement(node);
|
|
3374
|
-
if (placement !== null) {
|
|
3375
|
-
const snapshot = buildCommentSnapshot(node, sourceFile, options.checker, options.extensions);
|
|
3376
|
-
if (snapshot !== null) {
|
|
3377
|
-
comments.push(snapshot);
|
|
3378
|
-
const subjectType = getSubjectType(node, options.checker);
|
|
3379
|
-
const hostType = getHostType(node, options.checker);
|
|
3380
|
-
diagnostics.push(
|
|
3381
|
-
...buildTagDiagnostics(
|
|
3382
|
-
sourceFile,
|
|
3383
|
-
options.checker,
|
|
3384
|
-
placement,
|
|
3385
|
-
hostType,
|
|
3386
|
-
subjectType,
|
|
3387
|
-
snapshot.tags.map((tag) => ({
|
|
3388
|
-
rawTagName: tag.rawTagName,
|
|
3389
|
-
normalizedTagName: tag.normalizedTagName,
|
|
3390
|
-
recognized: tag.recognized,
|
|
3391
|
-
fullSpan: tag.fullSpan,
|
|
3392
|
-
tagNameSpan: tag.tagNameSpan,
|
|
3393
|
-
payloadSpan: tag.payloadSpan,
|
|
3394
|
-
colonSpan: tag.target?.colonSpan ?? null,
|
|
3395
|
-
target: tag.target === null ? null : {
|
|
3396
|
-
rawText: tag.target.rawText,
|
|
3397
|
-
valid: tag.target.valid,
|
|
3398
|
-
kind: tag.target.kind,
|
|
3399
|
-
fullSpan: tag.target.fullSpan,
|
|
3400
|
-
colonSpan: tag.target.colonSpan,
|
|
3401
|
-
span: tag.target.span,
|
|
3402
|
-
path: null
|
|
3403
|
-
},
|
|
3404
|
-
argumentSpan: tag.argumentSpan,
|
|
3405
|
-
argumentText: tag.argumentText
|
|
3406
|
-
})),
|
|
3407
|
-
{
|
|
3408
|
-
checker: options.checker,
|
|
3409
|
-
...subjectType === void 0 ? {} : { subjectType },
|
|
3410
|
-
placement,
|
|
3411
|
-
...options.extensions === void 0 ? {} : { extensions: options.extensions }
|
|
3412
|
-
}
|
|
3413
|
-
)
|
|
3414
|
-
);
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
ts4.forEachChild(node, visit);
|
|
3418
|
-
};
|
|
3419
|
-
visit(sourceFile);
|
|
3420
|
-
return {
|
|
3421
|
-
filePath: sourceFile.fileName,
|
|
3422
|
-
sourceHash: computeFormSpecTextHash(sourceFile.text),
|
|
3423
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3424
|
-
comments,
|
|
3425
|
-
diagnostics
|
|
3426
|
-
};
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
877
|
// src/workspace-runtime.ts
|
|
3430
878
|
import path from "path";
|
|
3431
879
|
function getFormSpecWorkspaceId(workspaceRoot) {
|
|
@@ -3440,58 +888,17 @@ function getFormSpecManifestPath(workspaceRoot) {
|
|
|
3440
888
|
export {
|
|
3441
889
|
FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
3442
890
|
FORMSPEC_ANALYSIS_SCHEMA_VERSION,
|
|
3443
|
-
analyzeConstraintTargets,
|
|
3444
|
-
buildConstraintTargetStates,
|
|
3445
|
-
buildFormSpecAnalysisFileSnapshot,
|
|
3446
|
-
buildSyntheticHelperPrelude,
|
|
3447
|
-
checkSyntheticTagApplication,
|
|
3448
|
-
collectCompatiblePathTargets,
|
|
3449
|
-
collectReferencedTypeAnnotations,
|
|
3450
|
-
collectReferencedTypeConstraints,
|
|
3451
891
|
computeFormSpecTextHash,
|
|
3452
|
-
dereferenceAnalysisType,
|
|
3453
|
-
extractPathTarget,
|
|
3454
|
-
findCommentTagAtOffset,
|
|
3455
|
-
findDeclarationForCommentOffset,
|
|
3456
|
-
findEnclosingDocComment,
|
|
3457
|
-
formatConstraintTargetName,
|
|
3458
|
-
formatPathTarget,
|
|
3459
|
-
getAllTagDefinitions,
|
|
3460
|
-
getCommentCompletionContextAtOffset,
|
|
3461
|
-
getCommentCursorTargetAtOffset,
|
|
3462
|
-
getCommentHoverInfoAtOffset,
|
|
3463
|
-
getCommentTagSemanticContext,
|
|
3464
|
-
getConstraintTagDefinitions,
|
|
3465
892
|
getFormSpecManifestPath,
|
|
3466
893
|
getFormSpecWorkspaceId,
|
|
3467
894
|
getFormSpecWorkspaceRuntimeDirectory,
|
|
3468
|
-
getHostType,
|
|
3469
|
-
getLastLeadingDocCommentRange,
|
|
3470
|
-
getMatchingTagSignatures,
|
|
3471
|
-
getSemanticCommentCompletionContextAtOffset,
|
|
3472
|
-
getSubjectType,
|
|
3473
|
-
getTagCompletionPrefixAtOffset,
|
|
3474
|
-
getTagDefinition,
|
|
3475
|
-
getTagHoverMarkdown,
|
|
3476
|
-
getTypeSemanticCapabilities,
|
|
3477
|
-
hasTypeSemanticCapability,
|
|
3478
895
|
isFormSpecAnalysisManifest,
|
|
3479
896
|
isFormSpecSemanticQuery,
|
|
3480
897
|
isFormSpecSemanticResponse,
|
|
3481
|
-
lowerTagApplicationToSyntheticCall,
|
|
3482
|
-
normalizeFormSpecTagName,
|
|
3483
|
-
parseCommentBlock,
|
|
3484
|
-
parseConstraintTagValue,
|
|
3485
|
-
parseDefaultValueTagValue,
|
|
3486
|
-
parseTagSyntax,
|
|
3487
|
-
resolveConstraintTargetState,
|
|
3488
|
-
resolveDeclarationPlacement,
|
|
3489
|
-
resolvePathTargetType,
|
|
3490
898
|
serializeCommentTagSemanticContext,
|
|
3491
899
|
serializeCommentTargetSpecifier,
|
|
3492
900
|
serializeCompletionContext,
|
|
3493
901
|
serializeHoverInfo,
|
|
3494
|
-
serializeParsedCommentTag
|
|
3495
|
-
sliceCommentSpan
|
|
902
|
+
serializeParsedCommentTag
|
|
3496
903
|
};
|
|
3497
904
|
//# sourceMappingURL=index.js.map
|