@openpkg-ts/extract 0.23.2 → 0.24.0
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/bin/tspec.js +24 -2
- package/dist/shared/{chunk-8898fs2f.js → chunk-bh378f7b.js} +1524 -1039
- package/dist/src/index.d.ts +78 -65
- package/dist/src/index.js +6 -2
- package/package.json +2 -2
|
@@ -89,9 +89,76 @@ var BUILTIN_TYPES = new Set([
|
|
|
89
89
|
"BigInt64Array",
|
|
90
90
|
"BigUint64Array"
|
|
91
91
|
]);
|
|
92
|
+
var ARRAY_PROTOTYPE_METHODS = new Set([
|
|
93
|
+
"pop",
|
|
94
|
+
"push",
|
|
95
|
+
"shift",
|
|
96
|
+
"unshift",
|
|
97
|
+
"splice",
|
|
98
|
+
"sort",
|
|
99
|
+
"reverse",
|
|
100
|
+
"fill",
|
|
101
|
+
"copyWithin",
|
|
102
|
+
"concat",
|
|
103
|
+
"join",
|
|
104
|
+
"slice",
|
|
105
|
+
"indexOf",
|
|
106
|
+
"lastIndexOf",
|
|
107
|
+
"includes",
|
|
108
|
+
"find",
|
|
109
|
+
"findIndex",
|
|
110
|
+
"findLast",
|
|
111
|
+
"findLastIndex",
|
|
112
|
+
"filter",
|
|
113
|
+
"map",
|
|
114
|
+
"reduce",
|
|
115
|
+
"reduceRight",
|
|
116
|
+
"every",
|
|
117
|
+
"some",
|
|
118
|
+
"flat",
|
|
119
|
+
"flatMap",
|
|
120
|
+
"forEach",
|
|
121
|
+
"entries",
|
|
122
|
+
"keys",
|
|
123
|
+
"values",
|
|
124
|
+
"at",
|
|
125
|
+
"with",
|
|
126
|
+
"toReversed",
|
|
127
|
+
"toSorted",
|
|
128
|
+
"toSpliced",
|
|
129
|
+
"length",
|
|
130
|
+
Symbol.iterator.toString(),
|
|
131
|
+
"toString",
|
|
132
|
+
"toLocaleString"
|
|
133
|
+
]);
|
|
92
134
|
function isPrimitiveName(name) {
|
|
93
135
|
return PRIMITIVES.has(name);
|
|
94
136
|
}
|
|
137
|
+
function isBuiltinSymbol(symbol) {
|
|
138
|
+
if (!symbol)
|
|
139
|
+
return false;
|
|
140
|
+
const declarations = symbol.getDeclarations();
|
|
141
|
+
if (!declarations || declarations.length === 0)
|
|
142
|
+
return false;
|
|
143
|
+
const sourceFile = declarations[0].getSourceFile();
|
|
144
|
+
const fileName = sourceFile.fileName;
|
|
145
|
+
return fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.");
|
|
146
|
+
}
|
|
147
|
+
function getTypeOrigin(type, _checker) {
|
|
148
|
+
const symbol = type.getSymbol() ?? type.aliasSymbol;
|
|
149
|
+
if (!symbol)
|
|
150
|
+
return;
|
|
151
|
+
const declarations = symbol.getDeclarations();
|
|
152
|
+
if (!declarations || declarations.length === 0)
|
|
153
|
+
return;
|
|
154
|
+
const fileName = declarations[0].getSourceFile().fileName;
|
|
155
|
+
const match = fileName.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)/);
|
|
156
|
+
if (!match)
|
|
157
|
+
return;
|
|
158
|
+
if (match[1] === "typescript")
|
|
159
|
+
return;
|
|
160
|
+
return match[1];
|
|
161
|
+
}
|
|
95
162
|
function isBuiltinGeneric(name) {
|
|
96
163
|
return BUILTIN_GENERICS.has(name);
|
|
97
164
|
}
|
|
@@ -170,8 +237,8 @@ function buildSchema(type, checker, ctx, _depth = 0) {
|
|
|
170
237
|
return { type: "number", enum: [literal] };
|
|
171
238
|
}
|
|
172
239
|
if (type.flags & ts.TypeFlags.BooleanLiteral) {
|
|
173
|
-
const
|
|
174
|
-
return { type: "boolean", enum: [
|
|
240
|
+
const typeString2 = checker.typeToString(type);
|
|
241
|
+
return { type: "boolean", enum: [typeString2 === "true"] };
|
|
175
242
|
}
|
|
176
243
|
if (type.isUnion()) {
|
|
177
244
|
const types = type.types;
|
|
@@ -193,12 +260,27 @@ function buildSchema(type, checker, ctx, _depth = 0) {
|
|
|
193
260
|
return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
|
|
194
261
|
}
|
|
195
262
|
if (type.isIntersection()) {
|
|
263
|
+
const filteredTypes = type.types.filter((t) => !(t.flags & ts.TypeFlags.Never));
|
|
264
|
+
if (filteredTypes.length === 0) {
|
|
265
|
+
return { type: "never" };
|
|
266
|
+
}
|
|
267
|
+
if (filteredTypes.length === 1) {
|
|
268
|
+
return buildSchema(filteredTypes[0], checker, ctx);
|
|
269
|
+
}
|
|
196
270
|
if (ctx) {
|
|
197
271
|
return withDepth(ctx, () => ({
|
|
198
|
-
allOf:
|
|
272
|
+
allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx))
|
|
199
273
|
}));
|
|
200
274
|
}
|
|
201
|
-
return { allOf:
|
|
275
|
+
return { allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx)) };
|
|
276
|
+
}
|
|
277
|
+
const typeString = checker.typeToString(type);
|
|
278
|
+
if (typeString === "never[]" || typeString === "[]") {
|
|
279
|
+
return { type: "array", prefixedItems: [], minItems: 0, maxItems: 0 };
|
|
280
|
+
}
|
|
281
|
+
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
282
|
+
if (symbol?.getName() === "Array" && isBuiltinSymbol(symbol)) {
|
|
283
|
+
return { type: "array", items: {} };
|
|
202
284
|
}
|
|
203
285
|
if (checker.isArrayType(type)) {
|
|
204
286
|
const typeRef2 = type;
|
|
@@ -218,12 +300,20 @@ function buildSchema(type, checker, ctx, _depth = 0) {
|
|
|
218
300
|
const typeRef2 = type;
|
|
219
301
|
const elementTypes = typeRef2.typeArguments ?? [];
|
|
220
302
|
if (ctx) {
|
|
221
|
-
return withDepth(ctx, () =>
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
303
|
+
return withDepth(ctx, () => {
|
|
304
|
+
const prevInTupleElement = ctx.inTupleElement;
|
|
305
|
+
ctx.inTupleElement = true;
|
|
306
|
+
try {
|
|
307
|
+
return {
|
|
308
|
+
type: "array",
|
|
309
|
+
prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
|
|
310
|
+
minItems: elementTypes.length,
|
|
311
|
+
maxItems: elementTypes.length
|
|
312
|
+
};
|
|
313
|
+
} finally {
|
|
314
|
+
ctx.inTupleElement = prevInTupleElement;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
227
317
|
}
|
|
228
318
|
return {
|
|
229
319
|
type: "array",
|
|
@@ -240,16 +330,27 @@ function buildSchema(type, checker, ctx, _depth = 0) {
|
|
|
240
330
|
return { $ref: `#/types/${name}` };
|
|
241
331
|
}
|
|
242
332
|
if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
|
|
333
|
+
const packageOrigin = getTypeOrigin(typeRef.target, checker);
|
|
243
334
|
if (ctx) {
|
|
244
|
-
return withDepth(ctx, () =>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
335
|
+
return withDepth(ctx, () => {
|
|
336
|
+
const schema2 = {
|
|
337
|
+
$ref: `#/types/${name}`,
|
|
338
|
+
typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
|
|
339
|
+
};
|
|
340
|
+
if (packageOrigin) {
|
|
341
|
+
schema2["x-ts-package"] = packageOrigin;
|
|
342
|
+
}
|
|
343
|
+
return schema2;
|
|
344
|
+
});
|
|
248
345
|
}
|
|
249
|
-
|
|
346
|
+
const schema = {
|
|
250
347
|
$ref: `#/types/${name}`,
|
|
251
348
|
typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
|
|
252
349
|
};
|
|
350
|
+
if (packageOrigin) {
|
|
351
|
+
schema["x-ts-package"] = packageOrigin;
|
|
352
|
+
}
|
|
353
|
+
return schema;
|
|
253
354
|
}
|
|
254
355
|
}
|
|
255
356
|
if (type.flags & ts.TypeFlags.Object) {
|
|
@@ -258,7 +359,6 @@ function buildSchema(type, checker, ctx, _depth = 0) {
|
|
|
258
359
|
return buildFunctionSchema(callSignatures, checker, ctx);
|
|
259
360
|
}
|
|
260
361
|
}
|
|
261
|
-
const symbol = type.getSymbol() || type.aliasSymbol;
|
|
262
362
|
if (symbol && !isAnonymous(type)) {
|
|
263
363
|
const name = symbol.getName();
|
|
264
364
|
if (isPrimitiveName(name)) {
|
|
@@ -268,7 +368,12 @@ function buildSchema(type, checker, ctx, _depth = 0) {
|
|
|
268
368
|
return { $ref: `#/types/${name}` };
|
|
269
369
|
}
|
|
270
370
|
if (!name.startsWith("__")) {
|
|
271
|
-
|
|
371
|
+
const packageOrigin = getTypeOrigin(type, checker);
|
|
372
|
+
const schema = { $ref: `#/types/${name}` };
|
|
373
|
+
if (packageOrigin) {
|
|
374
|
+
schema["x-ts-package"] = packageOrigin;
|
|
375
|
+
}
|
|
376
|
+
return schema;
|
|
272
377
|
}
|
|
273
378
|
}
|
|
274
379
|
if (type.flags & ts.TypeFlags.Object) {
|
|
@@ -314,6 +419,9 @@ function buildObjectSchema(properties, checker, ctx) {
|
|
|
314
419
|
const propName = prop.getName();
|
|
315
420
|
if (propName.startsWith("_"))
|
|
316
421
|
continue;
|
|
422
|
+
if (ARRAY_PROTOTYPE_METHODS.has(propName)) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
317
425
|
const propType = checker.getTypeOfSymbol(prop);
|
|
318
426
|
props[propName] = buildSchema(propType, checker, ctx);
|
|
319
427
|
if (!(prop.flags & ts.SymbolFlags.Optional)) {
|
|
@@ -632,6 +740,8 @@ class TypeRegistry {
|
|
|
632
740
|
const propName = prop.getName();
|
|
633
741
|
if (propName.startsWith("_"))
|
|
634
742
|
continue;
|
|
743
|
+
if (ARRAY_PROTOTYPE_METHODS.has(propName))
|
|
744
|
+
continue;
|
|
635
745
|
const propType = checker.getTypeOfSymbol(prop);
|
|
636
746
|
this.registerType(propType, ctx);
|
|
637
747
|
props[propName] = buildSchema(propType, checker, ctx);
|
|
@@ -685,6 +795,42 @@ function stripTypeParamSeparator(text) {
|
|
|
685
795
|
}
|
|
686
796
|
return text.trim() || undefined;
|
|
687
797
|
}
|
|
798
|
+
function extractSeeTagText(tag) {
|
|
799
|
+
const fullText = tag.getText();
|
|
800
|
+
const seeMatch = fullText.match(/@see\s+(.+?)(?:\s*\*\s*@|\s*\*\/|$)/s);
|
|
801
|
+
if (seeMatch) {
|
|
802
|
+
let text = seeMatch[1].trim();
|
|
803
|
+
text = text.replace(/\s*\*\s*$/gm, "").trim();
|
|
804
|
+
if (text)
|
|
805
|
+
return text;
|
|
806
|
+
}
|
|
807
|
+
if (tag.comment) {
|
|
808
|
+
if (typeof tag.comment === "string") {
|
|
809
|
+
if (!tag.comment.startsWith("://")) {
|
|
810
|
+
return tag.comment;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (Array.isArray(tag.comment)) {
|
|
814
|
+
const parts = [];
|
|
815
|
+
for (const part of tag.comment) {
|
|
816
|
+
if (ts3.isJSDocLink(part) || ts3.isJSDocLinkCode(part) || ts3.isJSDocLinkPlain(part)) {
|
|
817
|
+
if (part.name) {
|
|
818
|
+
parts.push(part.name.getText());
|
|
819
|
+
}
|
|
820
|
+
if (part.text) {
|
|
821
|
+
parts.push(part.text);
|
|
822
|
+
}
|
|
823
|
+
} else if (part.kind === ts3.SyntaxKind.JSDocText) {
|
|
824
|
+
parts.push(part.text);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const result = parts.join("").trim();
|
|
828
|
+
if (result && !result.startsWith("://"))
|
|
829
|
+
return result;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return typeof tag.comment === "string" ? tag.comment : ts3.getTextOfJSDocComment(tag.comment) ?? "";
|
|
833
|
+
}
|
|
688
834
|
function getJSDocComment(node) {
|
|
689
835
|
const jsDocTags = ts3.getJSDocTags(node);
|
|
690
836
|
const tags = jsDocTags.map((tag) => {
|
|
@@ -709,6 +855,10 @@ function getJSDocComment(node) {
|
|
|
709
855
|
const text = stripTypeParamSeparator(rawText) ?? "";
|
|
710
856
|
return { name: tag.tagName.text, text };
|
|
711
857
|
}
|
|
858
|
+
if (tag.tagName.text === "see") {
|
|
859
|
+
const text = extractSeeTagText(tag);
|
|
860
|
+
return { name: tag.tagName.text, text };
|
|
861
|
+
}
|
|
712
862
|
return { name: tag.tagName.text, text: rawText };
|
|
713
863
|
});
|
|
714
864
|
const jsDocComments = ts3.getJSDocCommentsAndTags(node).filter(ts3.isJSDoc);
|
|
@@ -759,10 +909,33 @@ function extractTypeParameters(node, checker) {
|
|
|
759
909
|
const defType = checker.getTypeAtLocation(tp.default);
|
|
760
910
|
defaultType = checker.typeToString(defType);
|
|
761
911
|
}
|
|
912
|
+
let variance;
|
|
913
|
+
let isConst;
|
|
914
|
+
const modifiers = ts3.getModifiers(tp);
|
|
915
|
+
if (modifiers) {
|
|
916
|
+
let hasIn = false;
|
|
917
|
+
let hasOut = false;
|
|
918
|
+
for (const mod of modifiers) {
|
|
919
|
+
if (mod.kind === ts3.SyntaxKind.InKeyword)
|
|
920
|
+
hasIn = true;
|
|
921
|
+
if (mod.kind === ts3.SyntaxKind.OutKeyword)
|
|
922
|
+
hasOut = true;
|
|
923
|
+
if (mod.kind === ts3.SyntaxKind.ConstKeyword)
|
|
924
|
+
isConst = true;
|
|
925
|
+
}
|
|
926
|
+
if (hasIn && hasOut)
|
|
927
|
+
variance = "inout";
|
|
928
|
+
else if (hasIn)
|
|
929
|
+
variance = "in";
|
|
930
|
+
else if (hasOut)
|
|
931
|
+
variance = "out";
|
|
932
|
+
}
|
|
762
933
|
return {
|
|
763
934
|
name,
|
|
764
935
|
...constraint ? { constraint } : {},
|
|
765
|
-
...defaultType ? { default: defaultType } : {}
|
|
936
|
+
...defaultType ? { default: defaultType } : {},
|
|
937
|
+
...variance ? { variance } : {},
|
|
938
|
+
...isConst ? { const: isConst } : {}
|
|
766
939
|
};
|
|
767
940
|
});
|
|
768
941
|
}
|
|
@@ -781,6 +954,67 @@ function isSymbolDeprecated(symbol) {
|
|
|
781
954
|
}
|
|
782
955
|
return false;
|
|
783
956
|
}
|
|
957
|
+
function getJSDocForSignature(signature) {
|
|
958
|
+
const decl = signature.getDeclaration();
|
|
959
|
+
if (!decl) {
|
|
960
|
+
return { tags: [], examples: [] };
|
|
961
|
+
}
|
|
962
|
+
return getJSDocComment(decl);
|
|
963
|
+
}
|
|
964
|
+
function extractTypeParametersFromSignature(signature, checker) {
|
|
965
|
+
const typeParams = signature.getTypeParameters();
|
|
966
|
+
if (!typeParams || typeParams.length === 0) {
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
return typeParams.map((tp) => {
|
|
970
|
+
const name = tp.getSymbol()?.getName() ?? "T";
|
|
971
|
+
let constraint;
|
|
972
|
+
const constraintType = tp.getConstraint();
|
|
973
|
+
if (constraintType) {
|
|
974
|
+
constraint = checker.typeToString(constraintType);
|
|
975
|
+
}
|
|
976
|
+
let defaultType;
|
|
977
|
+
const defaultT = tp.getDefault();
|
|
978
|
+
if (defaultT) {
|
|
979
|
+
defaultType = checker.typeToString(defaultT);
|
|
980
|
+
}
|
|
981
|
+
let variance;
|
|
982
|
+
let isConst;
|
|
983
|
+
const tpSymbol = tp.getSymbol();
|
|
984
|
+
const declarations = tpSymbol?.getDeclarations() ?? [];
|
|
985
|
+
for (const decl of declarations) {
|
|
986
|
+
if (ts3.isTypeParameterDeclaration(decl)) {
|
|
987
|
+
const modifiers = ts3.getModifiers(decl);
|
|
988
|
+
if (modifiers) {
|
|
989
|
+
let hasIn = false;
|
|
990
|
+
let hasOut = false;
|
|
991
|
+
for (const mod of modifiers) {
|
|
992
|
+
if (mod.kind === ts3.SyntaxKind.InKeyword)
|
|
993
|
+
hasIn = true;
|
|
994
|
+
if (mod.kind === ts3.SyntaxKind.OutKeyword)
|
|
995
|
+
hasOut = true;
|
|
996
|
+
if (mod.kind === ts3.SyntaxKind.ConstKeyword)
|
|
997
|
+
isConst = true;
|
|
998
|
+
}
|
|
999
|
+
if (hasIn && hasOut)
|
|
1000
|
+
variance = "inout";
|
|
1001
|
+
else if (hasIn)
|
|
1002
|
+
variance = "in";
|
|
1003
|
+
else if (hasOut)
|
|
1004
|
+
variance = "out";
|
|
1005
|
+
}
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
name,
|
|
1011
|
+
...constraint ? { constraint } : {},
|
|
1012
|
+
...defaultType ? { default: defaultType } : {},
|
|
1013
|
+
...variance ? { variance } : {},
|
|
1014
|
+
...isConst ? { const: isConst } : {}
|
|
1015
|
+
};
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
784
1018
|
|
|
785
1019
|
// src/compiler/program.ts
|
|
786
1020
|
import * as path from "node:path";
|
|
@@ -855,142 +1089,600 @@ function createProgram({
|
|
|
855
1089
|
};
|
|
856
1090
|
}
|
|
857
1091
|
|
|
858
|
-
// src/
|
|
859
|
-
import
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
}
|
|
880
|
-
return result;
|
|
881
|
-
}
|
|
882
|
-
function expandBindingPattern(paramDecl, paramType, jsdocTags, ctx) {
|
|
883
|
-
const { typeChecker: checker } = ctx;
|
|
884
|
-
const result = [];
|
|
885
|
-
const bindingPattern = paramDecl.name;
|
|
886
|
-
const allProperties = getEffectiveProperties(paramType, checker);
|
|
887
|
-
const inferredAlias = inferParamAlias(jsdocTags);
|
|
888
|
-
for (const element of bindingPattern.elements) {
|
|
889
|
-
if (!ts5.isBindingElement(element))
|
|
890
|
-
continue;
|
|
891
|
-
const propertyName = element.propertyName ? ts5.isIdentifier(element.propertyName) ? element.propertyName.text : element.propertyName.getText() : ts5.isIdentifier(element.name) ? element.name.text : element.name.getText();
|
|
892
|
-
const propSymbol = allProperties.get(propertyName);
|
|
893
|
-
if (!propSymbol)
|
|
894
|
-
continue;
|
|
895
|
-
const propType = checker.getTypeOfSymbol(propSymbol);
|
|
896
|
-
registerReferencedTypes(propType, ctx);
|
|
897
|
-
const isOptional = !!(propSymbol.flags & ts5.SymbolFlags.Optional) || element.initializer !== undefined;
|
|
898
|
-
const description = getParamDescription(propertyName, jsdocTags, inferredAlias);
|
|
899
|
-
const param = {
|
|
900
|
-
name: propertyName,
|
|
901
|
-
schema: buildSchema(propType, checker, ctx),
|
|
902
|
-
required: !isOptional
|
|
903
|
-
};
|
|
904
|
-
if (description) {
|
|
905
|
-
param.description = description;
|
|
906
|
-
}
|
|
907
|
-
if (element.initializer) {
|
|
908
|
-
param.default = extractDefaultValue(element.initializer);
|
|
909
|
-
}
|
|
910
|
-
result.push(param);
|
|
911
|
-
}
|
|
912
|
-
return result;
|
|
913
|
-
}
|
|
914
|
-
function getEffectiveProperties(type, _checker) {
|
|
915
|
-
const properties = new Map;
|
|
916
|
-
if (type.isIntersection()) {
|
|
917
|
-
for (const subType of type.types) {
|
|
918
|
-
for (const prop of subType.getProperties()) {
|
|
919
|
-
properties.set(prop.getName(), prop);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
} else {
|
|
923
|
-
for (const prop of type.getProperties()) {
|
|
924
|
-
properties.set(prop.getName(), prop);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
return properties;
|
|
1092
|
+
// src/schema/standard-schema.ts
|
|
1093
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
1094
|
+
import * as fs from "node:fs";
|
|
1095
|
+
import * as os from "node:os";
|
|
1096
|
+
import * as path2 from "node:path";
|
|
1097
|
+
function isStandardJSONSchema(obj) {
|
|
1098
|
+
if (typeof obj !== "object" || obj === null)
|
|
1099
|
+
return false;
|
|
1100
|
+
const std = obj["~standard"];
|
|
1101
|
+
if (typeof std !== "object" || std === null)
|
|
1102
|
+
return false;
|
|
1103
|
+
const stdObj = std;
|
|
1104
|
+
if (stdObj.version !== 1)
|
|
1105
|
+
return false;
|
|
1106
|
+
if (typeof stdObj.vendor !== "string")
|
|
1107
|
+
return false;
|
|
1108
|
+
const jsonSchema = stdObj.jsonSchema;
|
|
1109
|
+
if (typeof jsonSchema !== "object" || jsonSchema === null)
|
|
1110
|
+
return false;
|
|
1111
|
+
const jsObj = jsonSchema;
|
|
1112
|
+
return typeof jsObj.output === "function" && typeof jsObj.input === "function";
|
|
928
1113
|
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
const prefix = paramName.split(".")[0];
|
|
939
|
-
if (prefix && !prefix.startsWith("__")) {
|
|
940
|
-
prefixes.push(prefix);
|
|
941
|
-
}
|
|
942
|
-
} else if (tagText.includes(".")) {
|
|
943
|
-
const match = tagText.match(/^(\w+)\./);
|
|
944
|
-
if (match && !match[1].startsWith("__")) {
|
|
945
|
-
prefixes.push(match[1]);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
1114
|
+
var cachedRuntime;
|
|
1115
|
+
function commandExists(cmd) {
|
|
1116
|
+
try {
|
|
1117
|
+
const result = spawnSync(process.platform === "win32" ? "where" : "which", [cmd], {
|
|
1118
|
+
stdio: "ignore"
|
|
1119
|
+
});
|
|
1120
|
+
return result.status === 0;
|
|
1121
|
+
} catch {
|
|
1122
|
+
return false;
|
|
948
1123
|
}
|
|
949
|
-
if (prefixes.length === 0)
|
|
950
|
-
return;
|
|
951
|
-
const counts = new Map;
|
|
952
|
-
for (const p of prefixes)
|
|
953
|
-
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
954
|
-
return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
955
1124
|
}
|
|
956
|
-
function
|
|
957
|
-
if (
|
|
958
|
-
return
|
|
1125
|
+
function detectTsRuntime() {
|
|
1126
|
+
if (cachedRuntime !== undefined) {
|
|
1127
|
+
return cachedRuntime;
|
|
959
1128
|
}
|
|
960
|
-
|
|
961
|
-
|
|
1129
|
+
const nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
1130
|
+
if (nodeVersion >= 22) {
|
|
1131
|
+
cachedRuntime = {
|
|
1132
|
+
cmd: "node",
|
|
1133
|
+
args: ["--experimental-strip-types", "--no-warnings"],
|
|
1134
|
+
name: "node (native)"
|
|
1135
|
+
};
|
|
1136
|
+
return cachedRuntime;
|
|
962
1137
|
}
|
|
963
|
-
if (
|
|
964
|
-
|
|
1138
|
+
if (commandExists("bun")) {
|
|
1139
|
+
cachedRuntime = {
|
|
1140
|
+
cmd: "bun",
|
|
1141
|
+
args: ["run"],
|
|
1142
|
+
name: "bun"
|
|
1143
|
+
};
|
|
1144
|
+
return cachedRuntime;
|
|
965
1145
|
}
|
|
966
|
-
if (
|
|
967
|
-
|
|
1146
|
+
if (commandExists("tsx")) {
|
|
1147
|
+
cachedRuntime = {
|
|
1148
|
+
cmd: "tsx",
|
|
1149
|
+
args: [],
|
|
1150
|
+
name: "tsx"
|
|
1151
|
+
};
|
|
1152
|
+
return cachedRuntime;
|
|
968
1153
|
}
|
|
969
|
-
if (
|
|
970
|
-
|
|
1154
|
+
if (commandExists("ts-node")) {
|
|
1155
|
+
cachedRuntime = {
|
|
1156
|
+
cmd: "ts-node",
|
|
1157
|
+
args: ["--transpile-only"],
|
|
1158
|
+
name: "ts-node"
|
|
1159
|
+
};
|
|
1160
|
+
return cachedRuntime;
|
|
971
1161
|
}
|
|
972
|
-
|
|
1162
|
+
cachedRuntime = null;
|
|
1163
|
+
return null;
|
|
973
1164
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1165
|
+
var WORKER_SCRIPT = `
|
|
1166
|
+
const path = require('path');
|
|
1167
|
+
const { pathToFileURL } = require('url');
|
|
1168
|
+
|
|
1169
|
+
// TypeBox detection: schemas have Symbol.for('TypeBox.Kind') and are JSON Schema
|
|
1170
|
+
const TYPEBOX_KIND = Symbol.for('TypeBox.Kind');
|
|
1171
|
+
|
|
1172
|
+
function isTypeBoxSchema(obj) {
|
|
1173
|
+
if (!obj || typeof obj !== 'object') return false;
|
|
1174
|
+
// TypeBox schemas always have Kind symbol (Union, Object, String, etc.)
|
|
1175
|
+
// Also check for common JSON Schema props to avoid false positives
|
|
1176
|
+
if (!obj[TYPEBOX_KIND]) return false;
|
|
1177
|
+
return typeof obj.type === 'string' || 'anyOf' in obj || 'oneOf' in obj || 'allOf' in obj;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function sanitizeTypeBoxSchema(schema) {
|
|
1181
|
+
// JSON.stringify removes symbol keys, keeping only JSON Schema props
|
|
1182
|
+
return JSON.parse(JSON.stringify(schema));
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
async function extract() {
|
|
1186
|
+
// With node -e, argv is: [node, arg1, arg2, ...]
|
|
1187
|
+
// (the -e script is NOT in argv)
|
|
1188
|
+
const [modulePath, optionsJson] = process.argv.slice(1);
|
|
1189
|
+
const { target, libraryOptions } = JSON.parse(optionsJson || '{}');
|
|
1190
|
+
|
|
1191
|
+
try {
|
|
1192
|
+
// Import the module using dynamic import (works with ESM and CJS)
|
|
1193
|
+
const absPath = path.resolve(modulePath);
|
|
1194
|
+
const mod = await import(pathToFileURL(absPath).href);
|
|
1195
|
+
const results = [];
|
|
1196
|
+
|
|
1197
|
+
// Build exports map - handle both ESM and CJS (where exports are in mod.default)
|
|
1198
|
+
const exports = {};
|
|
1199
|
+
for (const [name, value] of Object.entries(mod)) {
|
|
1200
|
+
if (name === 'default' && typeof value === 'object' && value !== null) {
|
|
1201
|
+
// CJS module: spread default exports
|
|
1202
|
+
Object.assign(exports, value);
|
|
1203
|
+
} else if (name !== 'default') {
|
|
1204
|
+
exports[name] = value;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// Check each export
|
|
1209
|
+
for (const [name, value] of Object.entries(exports)) {
|
|
1210
|
+
if (name.startsWith('_')) continue;
|
|
1211
|
+
if (typeof value !== 'object' || value === null) continue;
|
|
1212
|
+
|
|
1213
|
+
// Priority 1: Standard JSON Schema (Zod 4.2+, ArkType 2.1.28+, Valibot 1.2+)
|
|
1214
|
+
const std = value['~standard'];
|
|
1215
|
+
if (std && typeof std === 'object' && std.version === 1 && typeof std.vendor === 'string' && std.jsonSchema && typeof std.jsonSchema.output === 'function') {
|
|
1216
|
+
try {
|
|
1217
|
+
// Per spec: pass options object with target and optional libraryOptions
|
|
1218
|
+
const options = { target: target || 'draft-2020-12', ...(libraryOptions && { libraryOptions }) };
|
|
1219
|
+
const outputSchema = std.jsonSchema.output(options);
|
|
1220
|
+
const inputSchema = typeof std.jsonSchema.input === 'function' ? std.jsonSchema.input(options) : undefined;
|
|
1221
|
+
results.push({
|
|
1222
|
+
exportName: name,
|
|
1223
|
+
vendor: std.vendor,
|
|
1224
|
+
outputSchema,
|
|
1225
|
+
inputSchema
|
|
1226
|
+
});
|
|
1227
|
+
} catch (e) {
|
|
1228
|
+
// Skip schemas that fail to extract
|
|
1229
|
+
}
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// Priority 2: TypeBox (schema IS JSON Schema)
|
|
1234
|
+
if (isTypeBoxSchema(value)) {
|
|
1235
|
+
try {
|
|
1236
|
+
results.push({
|
|
1237
|
+
exportName: name,
|
|
1238
|
+
vendor: 'typebox',
|
|
1239
|
+
outputSchema: sanitizeTypeBoxSchema(value)
|
|
1240
|
+
});
|
|
1241
|
+
} catch (e) {
|
|
1242
|
+
// Skip schemas that fail to extract
|
|
1243
|
+
}
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
console.log(JSON.stringify({ success: true, results }));
|
|
1249
|
+
} catch (e) {
|
|
1250
|
+
console.log(JSON.stringify({ success: false, error: e.message }));
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
extract();
|
|
1255
|
+
`;
|
|
1256
|
+
var TS_WORKER_SCRIPT = `
|
|
1257
|
+
import * as path from 'path';
|
|
1258
|
+
import { pathToFileURL } from 'url';
|
|
1259
|
+
|
|
1260
|
+
// TypeBox detection
|
|
1261
|
+
const TYPEBOX_KIND = Symbol.for('TypeBox.Kind');
|
|
1262
|
+
|
|
1263
|
+
function isTypeBoxSchema(obj: unknown): boolean {
|
|
1264
|
+
if (!obj || typeof obj !== 'object') return false;
|
|
1265
|
+
const o = obj as Record<string | symbol, unknown>;
|
|
1266
|
+
if (!o[TYPEBOX_KIND]) return false;
|
|
1267
|
+
return typeof o.type === 'string' || 'anyOf' in o || 'oneOf' in o || 'allOf' in o;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function sanitizeTypeBoxSchema(schema: unknown): unknown {
|
|
1271
|
+
return JSON.parse(JSON.stringify(schema));
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
async function extract() {
|
|
1275
|
+
const [,, modulePath, optionsJson] = process.argv;
|
|
1276
|
+
const { target, libraryOptions } = JSON.parse(optionsJson || '{}');
|
|
1277
|
+
|
|
1278
|
+
try {
|
|
1279
|
+
const absPath = path.resolve(modulePath);
|
|
1280
|
+
const mod = await import(pathToFileURL(absPath).href);
|
|
1281
|
+
const results: Array<{exportName: string; vendor: string; outputSchema: unknown; inputSchema?: unknown}> = [];
|
|
1282
|
+
|
|
1283
|
+
// Build exports map
|
|
1284
|
+
const exports: Record<string, unknown> = {};
|
|
1285
|
+
for (const [name, value] of Object.entries(mod)) {
|
|
1286
|
+
if (name === 'default' && typeof value === 'object' && value !== null) {
|
|
1287
|
+
Object.assign(exports, value);
|
|
1288
|
+
} else if (name !== 'default') {
|
|
1289
|
+
exports[name] = value;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// Check each export
|
|
1294
|
+
for (const [name, value] of Object.entries(exports)) {
|
|
1295
|
+
if (name.startsWith('_')) continue;
|
|
1296
|
+
if (typeof value !== 'object' || value === null) continue;
|
|
1297
|
+
|
|
1298
|
+
const v = value as Record<string, unknown>;
|
|
1299
|
+
const std = v['~standard'] as Record<string, unknown> | undefined;
|
|
1300
|
+
|
|
1301
|
+
// Standard JSON Schema
|
|
1302
|
+
if (std && typeof std === 'object' && std.version === 1 && typeof std.vendor === 'string') {
|
|
1303
|
+
const jsonSchema = std.jsonSchema as Record<string, unknown> | undefined;
|
|
1304
|
+
if (jsonSchema && typeof jsonSchema.output === 'function') {
|
|
1305
|
+
try {
|
|
1306
|
+
const options = { target: target || 'draft-2020-12', ...(libraryOptions && { libraryOptions }) };
|
|
1307
|
+
const outputSchema = (jsonSchema.output as Function)(options);
|
|
1308
|
+
const inputSchema = typeof jsonSchema.input === 'function' ? (jsonSchema.input as Function)(options) : undefined;
|
|
1309
|
+
results.push({ exportName: name, vendor: std.vendor as string, outputSchema, inputSchema });
|
|
1310
|
+
} catch {}
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// TypeBox
|
|
1316
|
+
if (isTypeBoxSchema(value)) {
|
|
1317
|
+
try {
|
|
1318
|
+
results.push({ exportName: name, vendor: 'typebox', outputSchema: sanitizeTypeBoxSchema(value) });
|
|
1319
|
+
} catch {}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
console.log(JSON.stringify({ success: true, results }));
|
|
1324
|
+
} catch (e) {
|
|
1325
|
+
console.log(JSON.stringify({ success: false, error: (e as Error).message }));
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
extract();
|
|
1330
|
+
`;
|
|
1331
|
+
async function extractStandardSchemasFromTs(tsFilePath, options = {}) {
|
|
1332
|
+
const { timeout = 1e4, target = "draft-2020-12", libraryOptions } = options;
|
|
1333
|
+
const result = {
|
|
1334
|
+
schemas: new Map,
|
|
1335
|
+
errors: []
|
|
1336
|
+
};
|
|
1337
|
+
const runtime = detectTsRuntime();
|
|
1338
|
+
if (!runtime) {
|
|
1339
|
+
result.errors.push("No TypeScript runtime available. Install bun, tsx, or ts-node, or use Node 22+.");
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
if (!fs.existsSync(tsFilePath)) {
|
|
1343
|
+
result.errors.push(`TypeScript file not found: ${tsFilePath}`);
|
|
1344
|
+
return result;
|
|
1345
|
+
}
|
|
1346
|
+
const tempDir = os.tmpdir();
|
|
1347
|
+
const workerPath = path2.join(tempDir, `openpkg-extract-worker-${Date.now()}.ts`);
|
|
1348
|
+
try {
|
|
1349
|
+
fs.writeFileSync(workerPath, TS_WORKER_SCRIPT);
|
|
1350
|
+
const optionsJson = JSON.stringify({ target, libraryOptions });
|
|
1351
|
+
const args = [...runtime.args, workerPath, tsFilePath, optionsJson];
|
|
1352
|
+
return await new Promise((resolve) => {
|
|
1353
|
+
const child = spawn(runtime.cmd, args, {
|
|
1354
|
+
timeout,
|
|
1355
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1356
|
+
cwd: path2.dirname(tsFilePath)
|
|
1357
|
+
});
|
|
1358
|
+
let stdout = "";
|
|
1359
|
+
let stderr = "";
|
|
1360
|
+
child.stdout.on("data", (data) => {
|
|
1361
|
+
stdout += data.toString();
|
|
1362
|
+
});
|
|
1363
|
+
child.stderr.on("data", (data) => {
|
|
1364
|
+
stderr += data.toString();
|
|
1365
|
+
});
|
|
1366
|
+
child.on("close", (code) => {
|
|
1367
|
+
try {
|
|
1368
|
+
fs.unlinkSync(workerPath);
|
|
1369
|
+
} catch {}
|
|
1370
|
+
if (code !== 0) {
|
|
1371
|
+
result.errors.push(`Extraction failed (${runtime.name}): ${stderr || `exit code ${code}`}`);
|
|
1372
|
+
resolve(result);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
try {
|
|
1376
|
+
const parsed = JSON.parse(stdout);
|
|
1377
|
+
if (!parsed.success) {
|
|
1378
|
+
result.errors.push(`Extraction failed: ${parsed.error}`);
|
|
1379
|
+
resolve(result);
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
for (const item of parsed.results) {
|
|
1383
|
+
result.schemas.set(item.exportName, {
|
|
1384
|
+
exportName: item.exportName,
|
|
1385
|
+
vendor: item.vendor,
|
|
1386
|
+
outputSchema: item.outputSchema,
|
|
1387
|
+
inputSchema: item.inputSchema
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
} catch (e) {
|
|
1391
|
+
result.errors.push(`Failed to parse extraction output: ${e}`);
|
|
1392
|
+
}
|
|
1393
|
+
resolve(result);
|
|
1394
|
+
});
|
|
1395
|
+
child.on("error", (err) => {
|
|
1396
|
+
try {
|
|
1397
|
+
fs.unlinkSync(workerPath);
|
|
1398
|
+
} catch {}
|
|
1399
|
+
result.errors.push(`Subprocess error: ${err.message}`);
|
|
1400
|
+
resolve(result);
|
|
1401
|
+
});
|
|
1402
|
+
});
|
|
1403
|
+
} catch (e) {
|
|
1404
|
+
try {
|
|
1405
|
+
fs.unlinkSync(workerPath);
|
|
1406
|
+
} catch {}
|
|
1407
|
+
result.errors.push(`Failed to create worker script: ${e}`);
|
|
1408
|
+
return result;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
function readTsconfigOutDir(baseDir) {
|
|
1412
|
+
const tsconfigPath = path2.join(baseDir, "tsconfig.json");
|
|
1413
|
+
try {
|
|
1414
|
+
if (!fs.existsSync(tsconfigPath)) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
const content = fs.readFileSync(tsconfigPath, "utf-8");
|
|
1418
|
+
const stripped = content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
1419
|
+
const tsconfig = JSON.parse(stripped);
|
|
1420
|
+
if (tsconfig.compilerOptions?.outDir) {
|
|
1421
|
+
return tsconfig.compilerOptions.outDir.replace(/^\.\//, "");
|
|
1422
|
+
}
|
|
1423
|
+
} catch {}
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
function resolveCompiledPath(tsPath, baseDir) {
|
|
1427
|
+
const relativePath = path2.relative(baseDir, tsPath);
|
|
1428
|
+
const withoutExt = relativePath.replace(/\.tsx?$/, "");
|
|
1429
|
+
const srcPrefix = withoutExt.replace(/^src\//, "");
|
|
1430
|
+
const tsconfigOutDir = readTsconfigOutDir(baseDir);
|
|
1431
|
+
const extensions = [".js", ".mjs", ".cjs"];
|
|
1432
|
+
const candidates = [];
|
|
1433
|
+
if (tsconfigOutDir) {
|
|
1434
|
+
for (const ext of extensions) {
|
|
1435
|
+
candidates.push(path2.join(baseDir, tsconfigOutDir, `${srcPrefix}${ext}`));
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
const commonOutDirs = ["dist", "build", "lib", "out"];
|
|
1439
|
+
for (const outDir of commonOutDirs) {
|
|
1440
|
+
if (outDir === tsconfigOutDir)
|
|
1441
|
+
continue;
|
|
1442
|
+
for (const ext of extensions) {
|
|
1443
|
+
candidates.push(path2.join(baseDir, outDir, `${srcPrefix}${ext}`));
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
for (const ext of extensions) {
|
|
1447
|
+
candidates.push(path2.join(baseDir, `${withoutExt}${ext}`));
|
|
1448
|
+
}
|
|
1449
|
+
const workspaceMatch = baseDir.match(/^(.+\/packages\/[^/]+)$/);
|
|
1450
|
+
if (workspaceMatch) {
|
|
1451
|
+
const pkgRoot = workspaceMatch[1];
|
|
1452
|
+
for (const ext of extensions) {
|
|
1453
|
+
candidates.push(path2.join(pkgRoot, "dist", `${srcPrefix}${ext}`));
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
for (const candidate of candidates) {
|
|
1457
|
+
if (fs.existsSync(candidate)) {
|
|
1458
|
+
return candidate;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
return null;
|
|
1462
|
+
}
|
|
1463
|
+
async function extractStandardSchemas(compiledJsPath, options = {}) {
|
|
1464
|
+
const { timeout = 1e4, target = "draft-2020-12", libraryOptions } = options;
|
|
1465
|
+
const result = {
|
|
1466
|
+
schemas: new Map,
|
|
1467
|
+
errors: []
|
|
1468
|
+
};
|
|
1469
|
+
if (!fs.existsSync(compiledJsPath)) {
|
|
1470
|
+
result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
|
|
1471
|
+
return result;
|
|
1472
|
+
}
|
|
1473
|
+
const optionsJson = JSON.stringify({ target, libraryOptions });
|
|
1474
|
+
return new Promise((resolve) => {
|
|
1475
|
+
const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, optionsJson], {
|
|
1476
|
+
timeout,
|
|
1477
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1478
|
+
});
|
|
1479
|
+
let stdout = "";
|
|
1480
|
+
let stderr = "";
|
|
1481
|
+
child.stdout.on("data", (data) => {
|
|
1482
|
+
stdout += data.toString();
|
|
1483
|
+
});
|
|
1484
|
+
child.stderr.on("data", (data) => {
|
|
1485
|
+
stderr += data.toString();
|
|
1486
|
+
});
|
|
1487
|
+
child.on("close", (code) => {
|
|
1488
|
+
if (code !== 0) {
|
|
1489
|
+
result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
|
|
1490
|
+
resolve(result);
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
try {
|
|
1494
|
+
const parsed = JSON.parse(stdout);
|
|
1495
|
+
if (!parsed.success) {
|
|
1496
|
+
result.errors.push(`Extraction failed: ${parsed.error}`);
|
|
1497
|
+
resolve(result);
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
for (const item of parsed.results) {
|
|
1501
|
+
result.schemas.set(item.exportName, {
|
|
1502
|
+
exportName: item.exportName,
|
|
1503
|
+
vendor: item.vendor,
|
|
1504
|
+
outputSchema: item.outputSchema,
|
|
1505
|
+
inputSchema: item.inputSchema
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
} catch (e) {
|
|
1509
|
+
result.errors.push(`Failed to parse extraction output: ${e}`);
|
|
1510
|
+
}
|
|
1511
|
+
resolve(result);
|
|
1512
|
+
});
|
|
1513
|
+
child.on("error", (err) => {
|
|
1514
|
+
result.errors.push(`Subprocess error: ${err.message}`);
|
|
1515
|
+
resolve(result);
|
|
1516
|
+
});
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
|
|
1520
|
+
const { preferDirectTs, ...extractOptions } = options;
|
|
1521
|
+
const isTypeScript = /\.tsx?$/.test(entryFile);
|
|
1522
|
+
if (!preferDirectTs) {
|
|
1523
|
+
const compiledPath = resolveCompiledPath(entryFile, baseDir);
|
|
1524
|
+
if (compiledPath) {
|
|
1525
|
+
const result = await extractStandardSchemas(compiledPath, extractOptions);
|
|
1526
|
+
return {
|
|
1527
|
+
...result,
|
|
1528
|
+
info: { method: "compiled", path: compiledPath }
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
if (isTypeScript) {
|
|
1533
|
+
const runtime2 = detectTsRuntime();
|
|
1534
|
+
if (runtime2) {
|
|
1535
|
+
const result = await extractStandardSchemasFromTs(entryFile, extractOptions);
|
|
1536
|
+
return {
|
|
1537
|
+
...result,
|
|
1538
|
+
info: { method: "direct-ts", runtime: runtime2.name, path: entryFile }
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
const runtime = detectTsRuntime();
|
|
1543
|
+
const hint = isTypeScript && !runtime ? " Install bun, tsx, or ts-node for direct TS execution." : "";
|
|
1544
|
+
return {
|
|
1545
|
+
schemas: new Map,
|
|
1546
|
+
errors: [`Could not find compiled JS for ${entryFile}.${hint}`]
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// src/types/parameters.ts
|
|
1551
|
+
import ts5 from "typescript";
|
|
1552
|
+
function extractParameters(signature, ctx) {
|
|
1553
|
+
const { typeChecker: checker } = ctx;
|
|
1554
|
+
const result = [];
|
|
1555
|
+
const signatureDecl = signature.getDeclaration();
|
|
1556
|
+
const jsdocTags = signatureDecl ? ts5.getJSDocTags(signatureDecl) : [];
|
|
1557
|
+
for (const param of signature.getParameters()) {
|
|
1558
|
+
const decl = param.valueDeclaration;
|
|
1559
|
+
const type = checker.getTypeOfSymbolAtLocation(param, decl ?? param.valueDeclaration);
|
|
1560
|
+
if (decl && ts5.isObjectBindingPattern(decl.name)) {
|
|
1561
|
+
const expandedParams = expandBindingPattern(decl, type, jsdocTags, ctx);
|
|
1562
|
+
result.push(...expandedParams);
|
|
1563
|
+
} else {
|
|
1564
|
+
registerReferencedTypes(type, ctx);
|
|
1565
|
+
result.push({
|
|
1566
|
+
name: param.getName(),
|
|
1567
|
+
schema: buildSchema(type, checker, ctx),
|
|
1568
|
+
required: !(param.flags & 16777216)
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
return result;
|
|
1573
|
+
}
|
|
1574
|
+
function expandBindingPattern(paramDecl, paramType, jsdocTags, ctx) {
|
|
1575
|
+
const { typeChecker: checker } = ctx;
|
|
1576
|
+
const result = [];
|
|
1577
|
+
const bindingPattern = paramDecl.name;
|
|
1578
|
+
const allProperties = getEffectiveProperties(paramType, checker);
|
|
1579
|
+
const inferredAlias = inferParamAlias(jsdocTags);
|
|
1580
|
+
for (const element of bindingPattern.elements) {
|
|
1581
|
+
if (!ts5.isBindingElement(element))
|
|
1582
|
+
continue;
|
|
1583
|
+
const propertyName = element.propertyName ? ts5.isIdentifier(element.propertyName) ? element.propertyName.text : element.propertyName.getText() : ts5.isIdentifier(element.name) ? element.name.text : element.name.getText();
|
|
1584
|
+
const propSymbol = allProperties.get(propertyName);
|
|
1585
|
+
if (!propSymbol)
|
|
1586
|
+
continue;
|
|
1587
|
+
const propType = checker.getTypeOfSymbol(propSymbol);
|
|
1588
|
+
registerReferencedTypes(propType, ctx);
|
|
1589
|
+
const isOptional = !!(propSymbol.flags & ts5.SymbolFlags.Optional) || element.initializer !== undefined;
|
|
1590
|
+
const description = getParamDescription(propertyName, jsdocTags, inferredAlias);
|
|
1591
|
+
const param = {
|
|
1592
|
+
name: propertyName,
|
|
1593
|
+
schema: buildSchema(propType, checker, ctx),
|
|
1594
|
+
required: !isOptional
|
|
1595
|
+
};
|
|
1596
|
+
if (description) {
|
|
1597
|
+
param.description = description;
|
|
1598
|
+
}
|
|
1599
|
+
if (element.initializer) {
|
|
1600
|
+
param.default = extractDefaultValue(element.initializer);
|
|
1601
|
+
}
|
|
1602
|
+
result.push(param);
|
|
1603
|
+
}
|
|
1604
|
+
return result;
|
|
1605
|
+
}
|
|
1606
|
+
function getEffectiveProperties(type, _checker) {
|
|
1607
|
+
const properties = new Map;
|
|
1608
|
+
if (type.isIntersection()) {
|
|
1609
|
+
for (const subType of type.types) {
|
|
1610
|
+
for (const prop of subType.getProperties()) {
|
|
1611
|
+
properties.set(prop.getName(), prop);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
} else {
|
|
1615
|
+
for (const prop of type.getProperties()) {
|
|
1616
|
+
properties.set(prop.getName(), prop);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
return properties;
|
|
1620
|
+
}
|
|
1621
|
+
function inferParamAlias(jsdocTags) {
|
|
1622
|
+
const prefixes = [];
|
|
1623
|
+
for (const tag of jsdocTags) {
|
|
1624
|
+
if (tag.tagName.text !== "param")
|
|
1625
|
+
continue;
|
|
1626
|
+
const tagText = typeof tag.comment === "string" ? tag.comment : ts5.getTextOfJSDocComment(tag.comment) ?? "";
|
|
1627
|
+
const paramTag = tag;
|
|
1628
|
+
const paramName = paramTag.name?.getText() ?? "";
|
|
1629
|
+
if (paramName.includes(".")) {
|
|
1630
|
+
const prefix = paramName.split(".")[0];
|
|
1631
|
+
if (prefix && !prefix.startsWith("__")) {
|
|
1632
|
+
prefixes.push(prefix);
|
|
1633
|
+
}
|
|
1634
|
+
} else if (tagText.includes(".")) {
|
|
1635
|
+
const match = tagText.match(/^(\w+)\./);
|
|
1636
|
+
if (match && !match[1].startsWith("__")) {
|
|
1637
|
+
prefixes.push(match[1]);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
if (prefixes.length === 0)
|
|
1642
|
+
return;
|
|
1643
|
+
const counts = new Map;
|
|
1644
|
+
for (const p of prefixes)
|
|
1645
|
+
counts.set(p, (counts.get(p) ?? 0) + 1);
|
|
1646
|
+
return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
1647
|
+
}
|
|
1648
|
+
function extractDefaultValue(initializer) {
|
|
1649
|
+
if (ts5.isStringLiteral(initializer)) {
|
|
1650
|
+
return initializer.text;
|
|
1651
|
+
}
|
|
1652
|
+
if (ts5.isNumericLiteral(initializer)) {
|
|
1653
|
+
return Number(initializer.text);
|
|
1654
|
+
}
|
|
1655
|
+
if (initializer.kind === ts5.SyntaxKind.TrueKeyword) {
|
|
1656
|
+
return true;
|
|
1657
|
+
}
|
|
1658
|
+
if (initializer.kind === ts5.SyntaxKind.FalseKeyword) {
|
|
1659
|
+
return false;
|
|
1660
|
+
}
|
|
1661
|
+
if (initializer.kind === ts5.SyntaxKind.NullKeyword) {
|
|
1662
|
+
return null;
|
|
1663
|
+
}
|
|
1664
|
+
return initializer.getText();
|
|
1665
|
+
}
|
|
1666
|
+
function registerReferencedTypes(type, ctx, depth = 0) {
|
|
1667
|
+
if (depth > ctx.maxTypeDepth)
|
|
1668
|
+
return;
|
|
1669
|
+
if (ctx.visitedTypes.has(type))
|
|
1670
|
+
return;
|
|
1671
|
+
const isPrimitive = type.flags & (ts5.TypeFlags.String | ts5.TypeFlags.Number | ts5.TypeFlags.Boolean | ts5.TypeFlags.Void | ts5.TypeFlags.Undefined | ts5.TypeFlags.Null | ts5.TypeFlags.Any | ts5.TypeFlags.Unknown | ts5.TypeFlags.Never | ts5.TypeFlags.StringLiteral | ts5.TypeFlags.NumberLiteral | ts5.TypeFlags.BooleanLiteral);
|
|
1672
|
+
if (!isPrimitive) {
|
|
1673
|
+
ctx.visitedTypes.add(type);
|
|
1674
|
+
}
|
|
1675
|
+
const { typeChecker: checker, typeRegistry } = ctx;
|
|
1676
|
+
typeRegistry.registerType(type, ctx);
|
|
1677
|
+
const typeArgs = type.typeArguments;
|
|
1678
|
+
if (typeArgs) {
|
|
1679
|
+
for (const arg of typeArgs) {
|
|
1680
|
+
registerReferencedTypes(arg, ctx, depth + 1);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
if (type.isUnion()) {
|
|
1684
|
+
for (const t of type.types) {
|
|
1685
|
+
registerReferencedTypes(t, ctx, depth + 1);
|
|
994
1686
|
}
|
|
995
1687
|
}
|
|
996
1688
|
if (type.isIntersection()) {
|
|
@@ -1008,7 +1700,149 @@ function registerReferencedTypes(type, ctx, depth = 0) {
|
|
|
1008
1700
|
}
|
|
1009
1701
|
|
|
1010
1702
|
// src/serializers/classes.ts
|
|
1703
|
+
import ts7 from "typescript";
|
|
1704
|
+
|
|
1705
|
+
// src/serializers/context.ts
|
|
1011
1706
|
import ts6 from "typescript";
|
|
1707
|
+
function createContext(program, sourceFile, options = {}) {
|
|
1708
|
+
return {
|
|
1709
|
+
typeChecker: program.getTypeChecker(),
|
|
1710
|
+
program,
|
|
1711
|
+
sourceFile,
|
|
1712
|
+
maxTypeDepth: options.maxTypeDepth ?? 4,
|
|
1713
|
+
maxExternalTypeDepth: options.maxExternalTypeDepth ?? 2,
|
|
1714
|
+
currentDepth: 0,
|
|
1715
|
+
resolveExternalTypes: options.resolveExternalTypes ?? true,
|
|
1716
|
+
typeRegistry: new TypeRegistry,
|
|
1717
|
+
exportedIds: new Set,
|
|
1718
|
+
visitedTypes: new Set
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function getInheritedMembers(classType, ownMemberNames, ctx, isStatic = false) {
|
|
1722
|
+
const { typeChecker: checker } = ctx;
|
|
1723
|
+
const inherited = [];
|
|
1724
|
+
const visited = new Set;
|
|
1725
|
+
const typeToWalk = isStatic ? classType.getSymbol()?.valueDeclaration && checker.getTypeOfSymbolAtLocation(classType.getSymbol(), classType.getSymbol().valueDeclaration) : classType;
|
|
1726
|
+
if (!typeToWalk)
|
|
1727
|
+
return inherited;
|
|
1728
|
+
walkBaseTypes(typeToWalk, ownMemberNames, inherited, visited, ctx, isStatic);
|
|
1729
|
+
return inherited;
|
|
1730
|
+
}
|
|
1731
|
+
function walkBaseTypes(type, ownMemberNames, inherited, visited, ctx, isStatic) {
|
|
1732
|
+
if (visited.has(type))
|
|
1733
|
+
return;
|
|
1734
|
+
visited.add(type);
|
|
1735
|
+
const { typeChecker: checker } = ctx;
|
|
1736
|
+
const baseTypes = type.getBaseTypes?.() ?? [];
|
|
1737
|
+
for (const baseType of baseTypes) {
|
|
1738
|
+
const baseSymbol = baseType.getSymbol();
|
|
1739
|
+
const baseName = baseSymbol?.getName() ?? "unknown";
|
|
1740
|
+
const properties = isStatic ? getStaticMembers(baseType, checker) : baseType.getProperties();
|
|
1741
|
+
for (const prop of properties) {
|
|
1742
|
+
const propName = prop.getName();
|
|
1743
|
+
if (ownMemberNames.has(propName))
|
|
1744
|
+
continue;
|
|
1745
|
+
if (inherited.some((m) => m.name === propName))
|
|
1746
|
+
continue;
|
|
1747
|
+
if (propName.startsWith("#") || propName.startsWith("__"))
|
|
1748
|
+
continue;
|
|
1749
|
+
const member = serializeInheritedMember(prop, baseName, ctx, isStatic);
|
|
1750
|
+
if (member) {
|
|
1751
|
+
inherited.push(member);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
walkBaseTypes(baseType, ownMemberNames, inherited, visited, ctx, isStatic);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
function getStaticMembers(classType, checker) {
|
|
1758
|
+
const symbol = classType.getSymbol();
|
|
1759
|
+
if (!symbol)
|
|
1760
|
+
return [];
|
|
1761
|
+
const decl = symbol.valueDeclaration;
|
|
1762
|
+
if (!decl)
|
|
1763
|
+
return [];
|
|
1764
|
+
const constructorType = checker.getTypeOfSymbolAtLocation(symbol, decl);
|
|
1765
|
+
return constructorType.getProperties().filter((prop) => {
|
|
1766
|
+
const name = prop.getName();
|
|
1767
|
+
return name !== "prototype" && name !== "constructor" && !name.startsWith("__");
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
function serializeInheritedMember(symbol, inheritedFrom, ctx, isStatic) {
|
|
1771
|
+
const { typeChecker: checker } = ctx;
|
|
1772
|
+
const name = symbol.getName();
|
|
1773
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
1774
|
+
const decl = declarations[0];
|
|
1775
|
+
if (!decl)
|
|
1776
|
+
return null;
|
|
1777
|
+
const type = checker.getTypeOfSymbol(symbol);
|
|
1778
|
+
registerReferencedTypes(type, ctx);
|
|
1779
|
+
let visibility;
|
|
1780
|
+
if (decl && ts6.canHaveModifiers(decl)) {
|
|
1781
|
+
const modifiers = ts6.getModifiers(decl);
|
|
1782
|
+
if (modifiers) {
|
|
1783
|
+
for (const mod of modifiers) {
|
|
1784
|
+
if (mod.kind === ts6.SyntaxKind.PrivateKeyword)
|
|
1785
|
+
visibility = "private";
|
|
1786
|
+
else if (mod.kind === ts6.SyntaxKind.ProtectedKeyword)
|
|
1787
|
+
visibility = "protected";
|
|
1788
|
+
else if (mod.kind === ts6.SyntaxKind.PublicKeyword)
|
|
1789
|
+
visibility = "public";
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
if (visibility === "private")
|
|
1794
|
+
return null;
|
|
1795
|
+
const { description, tags } = getJSDocComment(decl);
|
|
1796
|
+
let kind = "property";
|
|
1797
|
+
const callSigs = type.getCallSignatures();
|
|
1798
|
+
if (callSigs.length > 0) {
|
|
1799
|
+
kind = "method";
|
|
1800
|
+
} else if (ts6.isGetAccessorDeclaration(decl)) {
|
|
1801
|
+
kind = "getter";
|
|
1802
|
+
} else if (ts6.isSetAccessorDeclaration(decl)) {
|
|
1803
|
+
kind = "setter";
|
|
1804
|
+
}
|
|
1805
|
+
const flags = {};
|
|
1806
|
+
if (isStatic)
|
|
1807
|
+
flags.static = true;
|
|
1808
|
+
if (decl && ts6.canHaveModifiers(decl)) {
|
|
1809
|
+
const modifiers = ts6.getModifiers(decl);
|
|
1810
|
+
if (modifiers?.some((m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword)) {
|
|
1811
|
+
flags.readonly = true;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
let signatures;
|
|
1815
|
+
if (kind === "method" && callSigs.length > 0) {
|
|
1816
|
+
signatures = callSigs.map((sig, index) => {
|
|
1817
|
+
const params = extractParameters(sig, ctx);
|
|
1818
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1819
|
+
registerReferencedTypes(returnType, ctx);
|
|
1820
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
1821
|
+
return {
|
|
1822
|
+
parameters: params.length > 0 ? params : undefined,
|
|
1823
|
+
returns: {
|
|
1824
|
+
schema: buildSchema(returnType, checker, ctx)
|
|
1825
|
+
},
|
|
1826
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
1827
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
1828
|
+
...callSigs.length > 1 ? { overloadIndex: index } : {}
|
|
1829
|
+
};
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
return {
|
|
1833
|
+
name,
|
|
1834
|
+
kind,
|
|
1835
|
+
inheritedFrom,
|
|
1836
|
+
description,
|
|
1837
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
1838
|
+
visibility,
|
|
1839
|
+
schema: kind !== "method" ? buildSchema(type, checker, ctx) : undefined,
|
|
1840
|
+
signatures,
|
|
1841
|
+
flags: Object.keys(flags).length > 0 ? flags : undefined
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// src/serializers/classes.ts
|
|
1012
1846
|
function serializeClass(node, ctx) {
|
|
1013
1847
|
const { typeChecker: checker } = ctx;
|
|
1014
1848
|
const symbol = checker.getSymbolAtLocation(node.name ?? node);
|
|
@@ -1027,11 +1861,11 @@ function serializeClass(node, ctx) {
|
|
|
1027
1861
|
const memberName = getMemberName(member);
|
|
1028
1862
|
if (memberName?.startsWith("#"))
|
|
1029
1863
|
continue;
|
|
1030
|
-
if (
|
|
1864
|
+
if (ts7.isPropertyDeclaration(member)) {
|
|
1031
1865
|
const propMember = serializeProperty(member, ctx);
|
|
1032
1866
|
if (propMember)
|
|
1033
1867
|
members.push(propMember);
|
|
1034
|
-
} else if (
|
|
1868
|
+
} else if (ts7.isMethodDeclaration(member)) {
|
|
1035
1869
|
const methodMember = serializeMethod(member, ctx);
|
|
1036
1870
|
if (methodMember?.name) {
|
|
1037
1871
|
if (!methodsByName.has(methodMember.name)) {
|
|
@@ -1046,17 +1880,28 @@ function serializeClass(node, ctx) {
|
|
|
1046
1880
|
}
|
|
1047
1881
|
}
|
|
1048
1882
|
}
|
|
1049
|
-
} else if (
|
|
1883
|
+
} else if (ts7.isConstructorDeclaration(member)) {
|
|
1050
1884
|
const ctorSig = serializeConstructor(member, ctx);
|
|
1051
1885
|
if (ctorSig)
|
|
1052
1886
|
signatures.push(ctorSig);
|
|
1053
|
-
} else if (
|
|
1887
|
+
} else if (ts7.isGetAccessorDeclaration(member) || ts7.isSetAccessorDeclaration(member)) {
|
|
1054
1888
|
const accessorMember = serializeAccessor(member, ctx);
|
|
1055
1889
|
if (accessorMember)
|
|
1056
1890
|
members.push(accessorMember);
|
|
1057
1891
|
}
|
|
1058
1892
|
}
|
|
1059
1893
|
members.push(...methodsByName.values());
|
|
1894
|
+
const ownMemberNames = new Set;
|
|
1895
|
+
for (const member of node.members) {
|
|
1896
|
+
const memberName = getMemberName(member);
|
|
1897
|
+
if (memberName)
|
|
1898
|
+
ownMemberNames.add(memberName);
|
|
1899
|
+
}
|
|
1900
|
+
const classType = checker.getTypeAtLocation(node);
|
|
1901
|
+
const inheritedInstance = getInheritedMembers(classType, ownMemberNames, ctx, false);
|
|
1902
|
+
members.push(...inheritedInstance);
|
|
1903
|
+
const inheritedStatic = getInheritedMembers(classType, ownMemberNames, ctx, true);
|
|
1904
|
+
members.push(...inheritedStatic);
|
|
1060
1905
|
const extendsClause = getExtendsClause(node, checker);
|
|
1061
1906
|
const implementsClause = getImplementsClause(node, checker);
|
|
1062
1907
|
return {
|
|
@@ -1076,37 +1921,37 @@ function serializeClass(node, ctx) {
|
|
|
1076
1921
|
};
|
|
1077
1922
|
}
|
|
1078
1923
|
function getMemberName(member) {
|
|
1079
|
-
if (
|
|
1924
|
+
if (ts7.isConstructorDeclaration(member))
|
|
1080
1925
|
return "constructor";
|
|
1081
1926
|
if (!member.name)
|
|
1082
1927
|
return;
|
|
1083
|
-
if (
|
|
1928
|
+
if (ts7.isIdentifier(member.name))
|
|
1084
1929
|
return member.name.text;
|
|
1085
|
-
if (
|
|
1930
|
+
if (ts7.isPrivateIdentifier(member.name))
|
|
1086
1931
|
return member.name.text;
|
|
1087
1932
|
return member.name.getText();
|
|
1088
1933
|
}
|
|
1089
1934
|
function getVisibility(member) {
|
|
1090
|
-
const modifiers =
|
|
1935
|
+
const modifiers = ts7.canHaveModifiers(member) ? ts7.getModifiers(member) : undefined;
|
|
1091
1936
|
if (!modifiers)
|
|
1092
1937
|
return;
|
|
1093
1938
|
for (const mod of modifiers) {
|
|
1094
|
-
if (mod.kind ===
|
|
1939
|
+
if (mod.kind === ts7.SyntaxKind.PrivateKeyword)
|
|
1095
1940
|
return "private";
|
|
1096
|
-
if (mod.kind ===
|
|
1941
|
+
if (mod.kind === ts7.SyntaxKind.ProtectedKeyword)
|
|
1097
1942
|
return "protected";
|
|
1098
|
-
if (mod.kind ===
|
|
1943
|
+
if (mod.kind === ts7.SyntaxKind.PublicKeyword)
|
|
1099
1944
|
return "public";
|
|
1100
1945
|
}
|
|
1101
1946
|
return;
|
|
1102
1947
|
}
|
|
1103
1948
|
function isStatic(member) {
|
|
1104
|
-
const modifiers =
|
|
1105
|
-
return modifiers?.some((m) => m.kind ===
|
|
1949
|
+
const modifiers = ts7.canHaveModifiers(member) ? ts7.getModifiers(member) : undefined;
|
|
1950
|
+
return modifiers?.some((m) => m.kind === ts7.SyntaxKind.StaticKeyword) ?? false;
|
|
1106
1951
|
}
|
|
1107
1952
|
function isReadonly(member) {
|
|
1108
|
-
const modifiers =
|
|
1109
|
-
return modifiers?.some((m) => m.kind ===
|
|
1953
|
+
const modifiers = ts7.canHaveModifiers(member) ? ts7.getModifiers(member) : undefined;
|
|
1954
|
+
return modifiers?.some((m) => m.kind === ts7.SyntaxKind.ReadonlyKeyword) ?? false;
|
|
1110
1955
|
}
|
|
1111
1956
|
function serializeProperty(node, ctx) {
|
|
1112
1957
|
const { typeChecker: checker } = ctx;
|
|
@@ -1144,15 +1989,22 @@ function serializeMethod(node, ctx) {
|
|
|
1144
1989
|
const visibility = getVisibility(node);
|
|
1145
1990
|
const type = checker.getTypeAtLocation(node);
|
|
1146
1991
|
const callSignatures = type.getCallSignatures();
|
|
1147
|
-
const signatures = callSignatures.map((sig) => {
|
|
1992
|
+
const signatures = callSignatures.map((sig, index) => {
|
|
1148
1993
|
const params = extractParameters(sig, ctx);
|
|
1149
1994
|
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1150
1995
|
registerReferencedTypes(returnType, ctx);
|
|
1996
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
1997
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, checker);
|
|
1151
1998
|
return {
|
|
1152
1999
|
parameters: params.length > 0 ? params : undefined,
|
|
1153
2000
|
returns: {
|
|
1154
2001
|
schema: buildSchema(returnType, checker, ctx)
|
|
1155
|
-
}
|
|
2002
|
+
},
|
|
2003
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
2004
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
2005
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
2006
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
2007
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
1156
2008
|
};
|
|
1157
2009
|
});
|
|
1158
2010
|
const flags = {};
|
|
@@ -1160,8 +2012,8 @@ function serializeMethod(node, ctx) {
|
|
|
1160
2012
|
flags.static = true;
|
|
1161
2013
|
if (node.asteriskToken)
|
|
1162
2014
|
flags.generator = true;
|
|
1163
|
-
const modifiers =
|
|
1164
|
-
if (modifiers?.some((m) => m.kind ===
|
|
2015
|
+
const modifiers = ts7.getModifiers(node);
|
|
2016
|
+
if (modifiers?.some((m) => m.kind === ts7.SyntaxKind.AsyncKeyword)) {
|
|
1165
2017
|
flags.async = true;
|
|
1166
2018
|
}
|
|
1167
2019
|
return {
|
|
@@ -1196,7 +2048,7 @@ function serializeAccessor(node, ctx) {
|
|
|
1196
2048
|
const type = checker.getTypeAtLocation(node);
|
|
1197
2049
|
const schema = buildSchema(type, checker, ctx);
|
|
1198
2050
|
registerReferencedTypes(type, ctx);
|
|
1199
|
-
const kind =
|
|
2051
|
+
const kind = ts7.isGetAccessorDeclaration(node) ? "getter" : "setter";
|
|
1200
2052
|
const flags = {};
|
|
1201
2053
|
if (isStatic(node))
|
|
1202
2054
|
flags.static = true;
|
|
@@ -1214,7 +2066,7 @@ function getExtendsClause(node, checker) {
|
|
|
1214
2066
|
if (!node.heritageClauses)
|
|
1215
2067
|
return;
|
|
1216
2068
|
for (const clause of node.heritageClauses) {
|
|
1217
|
-
if (clause.token ===
|
|
2069
|
+
if (clause.token === ts7.SyntaxKind.ExtendsKeyword) {
|
|
1218
2070
|
const expr = clause.types[0];
|
|
1219
2071
|
if (expr) {
|
|
1220
2072
|
const type = checker.getTypeAtLocation(expr);
|
|
@@ -1229,7 +2081,7 @@ function getImplementsClause(node, checker) {
|
|
|
1229
2081
|
if (!node.heritageClauses)
|
|
1230
2082
|
return;
|
|
1231
2083
|
for (const clause of node.heritageClauses) {
|
|
1232
|
-
if (clause.token ===
|
|
2084
|
+
if (clause.token === ts7.SyntaxKind.ImplementsKeyword) {
|
|
1233
2085
|
return clause.types.map((expr) => {
|
|
1234
2086
|
const type = checker.getTypeAtLocation(expr);
|
|
1235
2087
|
const symbol = type.getSymbol();
|
|
@@ -1268,226 +2120,54 @@ function serializeEnum(node, ctx) {
|
|
|
1268
2120
|
id: memberName,
|
|
1269
2121
|
name: memberName,
|
|
1270
2122
|
kind: "enum-member",
|
|
1271
|
-
...schema ? { schema } : {},
|
|
1272
|
-
...memberDesc ? { description: memberDesc } : {}
|
|
1273
|
-
};
|
|
1274
|
-
});
|
|
1275
|
-
return {
|
|
1276
|
-
id: name,
|
|
1277
|
-
name,
|
|
1278
|
-
kind: "enum",
|
|
1279
|
-
description,
|
|
1280
|
-
tags,
|
|
1281
|
-
source,
|
|
1282
|
-
members,
|
|
1283
|
-
...deprecated ? { deprecated: true } : {},
|
|
1284
|
-
...examples.length > 0 ? { examples } : {}
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// src/serializers/functions.ts
|
|
1289
|
-
function serializeFunctionExport(node, ctx) {
|
|
1290
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
1291
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
1292
|
-
if (!name)
|
|
1293
|
-
return null;
|
|
1294
|
-
const deprecated = isSymbolDeprecated(symbol);
|
|
1295
|
-
const declSourceFile = node.getSourceFile();
|
|
1296
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
1297
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
1298
|
-
const typeParameters = extractTypeParameters(node, ctx.typeChecker);
|
|
1299
|
-
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
1300
|
-
const callSignatures = type.getCallSignatures();
|
|
1301
|
-
const signatures = callSignatures.map((sig) => {
|
|
1302
|
-
const params = extractParameters(sig, ctx);
|
|
1303
|
-
const returnType = ctx.typeChecker.getReturnTypeOfSignature(sig);
|
|
1304
|
-
registerReferencedTypes(returnType, ctx);
|
|
1305
|
-
return {
|
|
1306
|
-
parameters: params,
|
|
1307
|
-
returns: {
|
|
1308
|
-
schema: buildSchema(returnType, ctx.typeChecker, ctx)
|
|
1309
|
-
}
|
|
1310
|
-
};
|
|
1311
|
-
});
|
|
1312
|
-
return {
|
|
1313
|
-
id: name,
|
|
1314
|
-
name,
|
|
1315
|
-
kind: "function",
|
|
1316
|
-
description,
|
|
1317
|
-
tags,
|
|
1318
|
-
source,
|
|
1319
|
-
typeParameters,
|
|
1320
|
-
signatures,
|
|
1321
|
-
...deprecated ? { deprecated: true } : {},
|
|
1322
|
-
...examples.length > 0 ? { examples } : {}
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
// src/serializers/interfaces.ts
|
|
1327
|
-
import ts7 from "typescript";
|
|
1328
|
-
function serializeInterface(node, ctx) {
|
|
1329
|
-
const { typeChecker: checker } = ctx;
|
|
1330
|
-
const symbol = checker.getSymbolAtLocation(node.name ?? node);
|
|
1331
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
1332
|
-
if (!name)
|
|
1333
|
-
return null;
|
|
1334
|
-
const deprecated = isSymbolDeprecated(symbol);
|
|
1335
|
-
const declSourceFile = node.getSourceFile();
|
|
1336
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
1337
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
1338
|
-
const typeParameters = extractTypeParameters(node, checker);
|
|
1339
|
-
const members = [];
|
|
1340
|
-
const methodsByName = new Map;
|
|
1341
|
-
for (const member of node.members) {
|
|
1342
|
-
if (ts7.isPropertySignature(member)) {
|
|
1343
|
-
const propMember = serializePropertySignature(member, ctx);
|
|
1344
|
-
if (propMember)
|
|
1345
|
-
members.push(propMember);
|
|
1346
|
-
} else if (ts7.isMethodSignature(member)) {
|
|
1347
|
-
const methodMember = serializeMethodSignature(member, ctx);
|
|
1348
|
-
if (methodMember?.name) {
|
|
1349
|
-
if (!methodsByName.has(methodMember.name)) {
|
|
1350
|
-
methodsByName.set(methodMember.name, methodMember);
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
} else if (ts7.isCallSignatureDeclaration(member)) {
|
|
1354
|
-
const callMember = serializeCallSignature(member, ctx);
|
|
1355
|
-
if (callMember)
|
|
1356
|
-
members.push(callMember);
|
|
1357
|
-
} else if (ts7.isIndexSignatureDeclaration(member)) {
|
|
1358
|
-
const indexMember = serializeIndexSignature(member, ctx);
|
|
1359
|
-
if (indexMember)
|
|
1360
|
-
members.push(indexMember);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
members.push(...methodsByName.values());
|
|
1364
|
-
const extendsClause = getInterfaceExtends(node, checker);
|
|
1365
|
-
return {
|
|
1366
|
-
id: name,
|
|
1367
|
-
name,
|
|
1368
|
-
kind: "interface",
|
|
1369
|
-
description,
|
|
1370
|
-
tags,
|
|
1371
|
-
source,
|
|
1372
|
-
typeParameters,
|
|
1373
|
-
members: members.length > 0 ? members : undefined,
|
|
1374
|
-
extends: extendsClause,
|
|
1375
|
-
...deprecated ? { deprecated: true } : {},
|
|
1376
|
-
...examples.length > 0 ? { examples } : {}
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1379
|
-
function serializePropertySignature(node, ctx) {
|
|
1380
|
-
const { typeChecker: checker } = ctx;
|
|
1381
|
-
const name = node.name.getText();
|
|
1382
|
-
const { description, tags } = getJSDocComment(node);
|
|
1383
|
-
const type = checker.getTypeAtLocation(node);
|
|
1384
|
-
const schema = buildSchema(type, checker, ctx);
|
|
1385
|
-
registerReferencedTypes(type, ctx);
|
|
1386
|
-
const flags = {};
|
|
1387
|
-
if (node.questionToken)
|
|
1388
|
-
flags.optional = true;
|
|
1389
|
-
if (node.modifiers?.some((m) => m.kind === ts7.SyntaxKind.ReadonlyKeyword)) {
|
|
1390
|
-
flags.readonly = true;
|
|
1391
|
-
}
|
|
1392
|
-
return {
|
|
1393
|
-
name,
|
|
1394
|
-
kind: "property",
|
|
1395
|
-
description,
|
|
1396
|
-
tags: tags.length > 0 ? tags : undefined,
|
|
1397
|
-
schema,
|
|
1398
|
-
flags: Object.keys(flags).length > 0 ? flags : undefined
|
|
1399
|
-
};
|
|
1400
|
-
}
|
|
1401
|
-
function serializeMethodSignature(node, ctx) {
|
|
1402
|
-
const { typeChecker: checker } = ctx;
|
|
1403
|
-
const name = node.name.getText();
|
|
1404
|
-
const { description, tags } = getJSDocComment(node);
|
|
1405
|
-
const type = checker.getTypeAtLocation(node);
|
|
1406
|
-
const callSignatures = type.getCallSignatures();
|
|
1407
|
-
const signatures = callSignatures.map((sig) => {
|
|
1408
|
-
const params = extractParameters(sig, ctx);
|
|
1409
|
-
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1410
|
-
registerReferencedTypes(returnType, ctx);
|
|
1411
|
-
return {
|
|
1412
|
-
parameters: params.length > 0 ? params : undefined,
|
|
1413
|
-
returns: {
|
|
1414
|
-
schema: buildSchema(returnType, checker, ctx)
|
|
1415
|
-
}
|
|
1416
|
-
};
|
|
1417
|
-
});
|
|
1418
|
-
const flags = {};
|
|
1419
|
-
if (node.questionToken)
|
|
1420
|
-
flags.optional = true;
|
|
1421
|
-
return {
|
|
1422
|
-
name,
|
|
1423
|
-
kind: "method",
|
|
1424
|
-
description,
|
|
1425
|
-
tags: tags.length > 0 ? tags : undefined,
|
|
1426
|
-
signatures: signatures.length > 0 ? signatures : undefined,
|
|
1427
|
-
flags: Object.keys(flags).length > 0 ? flags : undefined
|
|
1428
|
-
};
|
|
1429
|
-
}
|
|
1430
|
-
function serializeCallSignature(node, ctx) {
|
|
1431
|
-
const { typeChecker: checker } = ctx;
|
|
1432
|
-
const { description, tags } = getJSDocComment(node);
|
|
1433
|
-
const sig = checker.getSignatureFromDeclaration(node);
|
|
1434
|
-
if (!sig)
|
|
1435
|
-
return null;
|
|
1436
|
-
const params = extractParameters(sig, ctx);
|
|
1437
|
-
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1438
|
-
registerReferencedTypes(returnType, ctx);
|
|
1439
|
-
return {
|
|
1440
|
-
name: "()",
|
|
1441
|
-
kind: "call-signature",
|
|
1442
|
-
description,
|
|
1443
|
-
tags: tags.length > 0 ? tags : undefined,
|
|
1444
|
-
signatures: [
|
|
1445
|
-
{
|
|
1446
|
-
parameters: params.length > 0 ? params : undefined,
|
|
1447
|
-
returns: {
|
|
1448
|
-
schema: buildSchema(returnType, checker, ctx)
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
]
|
|
1452
|
-
};
|
|
1453
|
-
}
|
|
1454
|
-
function serializeIndexSignature(node, ctx) {
|
|
1455
|
-
const { typeChecker: checker } = ctx;
|
|
1456
|
-
const { description, tags } = getJSDocComment(node);
|
|
1457
|
-
const valueType = node.type ? checker.getTypeAtLocation(node.type) : checker.getAnyType();
|
|
1458
|
-
const valueSchema = buildSchema(valueType, checker, ctx);
|
|
1459
|
-
registerReferencedTypes(valueType, ctx);
|
|
1460
|
-
const keyParam = node.parameters[0];
|
|
1461
|
-
const keyType = keyParam?.type ? checker.getTypeAtLocation(keyParam.type) : checker.getStringType();
|
|
1462
|
-
const keyTypeName = checker.typeToString(keyType);
|
|
2123
|
+
...schema ? { schema } : {},
|
|
2124
|
+
...memberDesc ? { description: memberDesc } : {}
|
|
2125
|
+
};
|
|
2126
|
+
});
|
|
1463
2127
|
return {
|
|
1464
|
-
|
|
1465
|
-
|
|
2128
|
+
id: name,
|
|
2129
|
+
name,
|
|
2130
|
+
kind: "enum",
|
|
1466
2131
|
description,
|
|
1467
|
-
tags
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
}
|
|
2132
|
+
tags,
|
|
2133
|
+
source,
|
|
2134
|
+
members,
|
|
2135
|
+
...deprecated ? { deprecated: true } : {},
|
|
2136
|
+
...examples.length > 0 ? { examples } : {}
|
|
1472
2137
|
};
|
|
1473
2138
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
2139
|
+
|
|
2140
|
+
// src/serializers/functions.ts
|
|
2141
|
+
import ts8 from "typescript";
|
|
2142
|
+
function buildReturnSchema(sig, ctx) {
|
|
2143
|
+
const returnType = ctx.typeChecker.getReturnTypeOfSignature(sig);
|
|
2144
|
+
registerReferencedTypes(returnType, ctx);
|
|
2145
|
+
const schema = buildSchema(returnType, ctx.typeChecker, ctx);
|
|
2146
|
+
const declaration = sig.getDeclaration();
|
|
2147
|
+
if (declaration && ts8.isFunctionLike(declaration) && declaration.type) {
|
|
2148
|
+
const returnTypeNode = declaration.type;
|
|
2149
|
+
if (ts8.isTypePredicateNode(returnTypeNode)) {
|
|
2150
|
+
const parameterName = ts8.isIdentifier(returnTypeNode.parameterName) ? returnTypeNode.parameterName.text : returnTypeNode.parameterName.getText();
|
|
2151
|
+
let predicateTypeSchema = { type: "unknown" };
|
|
2152
|
+
if (returnTypeNode.type) {
|
|
2153
|
+
const predicateType = ctx.typeChecker.getTypeAtLocation(returnTypeNode.type);
|
|
2154
|
+
predicateTypeSchema = buildSchema(predicateType, ctx.typeChecker, ctx);
|
|
2155
|
+
registerReferencedTypes(predicateType, ctx);
|
|
2156
|
+
}
|
|
2157
|
+
const baseSchema = typeof schema === "string" ? { type: schema } : schema;
|
|
2158
|
+
const schemaWithPredicate = {
|
|
2159
|
+
...baseSchema,
|
|
2160
|
+
"x-ts-type-predicate": {
|
|
2161
|
+
parameterName,
|
|
2162
|
+
type: predicateTypeSchema
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
2165
|
+
return { schema: schemaWithPredicate };
|
|
1484
2166
|
}
|
|
1485
2167
|
}
|
|
1486
|
-
return;
|
|
2168
|
+
return { schema };
|
|
1487
2169
|
}
|
|
1488
|
-
|
|
1489
|
-
// src/serializers/type-aliases.ts
|
|
1490
|
-
function serializeTypeAlias(node, ctx) {
|
|
2170
|
+
function serializeFunctionExport(node, ctx) {
|
|
1491
2171
|
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
1492
2172
|
const name = symbol?.getName() ?? node.name?.getText();
|
|
1493
2173
|
if (!name)
|
|
@@ -1498,673 +2178,448 @@ function serializeTypeAlias(node, ctx) {
|
|
|
1498
2178
|
const source = getSourceLocation(node, declSourceFile);
|
|
1499
2179
|
const typeParameters = extractTypeParameters(node, ctx.typeChecker);
|
|
1500
2180
|
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
1501
|
-
|
|
1502
|
-
const
|
|
2181
|
+
const callSignatures = type.getCallSignatures();
|
|
2182
|
+
const signatures = callSignatures.map((sig, index) => {
|
|
2183
|
+
const params = extractParameters(sig, ctx);
|
|
2184
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
2185
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
|
|
2186
|
+
return {
|
|
2187
|
+
parameters: params,
|
|
2188
|
+
returns: buildReturnSchema(sig, ctx),
|
|
2189
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
2190
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
2191
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
2192
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
2193
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
2194
|
+
};
|
|
2195
|
+
});
|
|
1503
2196
|
return {
|
|
1504
2197
|
id: name,
|
|
1505
2198
|
name,
|
|
1506
|
-
kind: "
|
|
2199
|
+
kind: "function",
|
|
1507
2200
|
description,
|
|
1508
2201
|
tags,
|
|
1509
2202
|
source,
|
|
1510
2203
|
typeParameters,
|
|
1511
|
-
|
|
2204
|
+
signatures,
|
|
1512
2205
|
...deprecated ? { deprecated: true } : {},
|
|
1513
2206
|
...examples.length > 0 ? { examples } : {}
|
|
1514
2207
|
};
|
|
1515
2208
|
}
|
|
1516
2209
|
|
|
1517
|
-
// src/
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
|
|
1524
|
-
if (nonNullable.length === 1) {
|
|
1525
|
-
return nonNullable[0];
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
return type;
|
|
1529
|
-
}
|
|
1530
|
-
var adapters = [];
|
|
1531
|
-
function registerAdapter(adapter) {
|
|
1532
|
-
adapters.push(adapter);
|
|
1533
|
-
}
|
|
1534
|
-
function findAdapter(type, checker) {
|
|
1535
|
-
return adapters.find((a) => a.matches(type, checker));
|
|
1536
|
-
}
|
|
1537
|
-
function isSchemaType(type, checker) {
|
|
1538
|
-
return adapters.some((a) => a.matches(type, checker));
|
|
1539
|
-
}
|
|
1540
|
-
function extractSchemaType(type, checker) {
|
|
1541
|
-
const adapter = findAdapter(type, checker);
|
|
1542
|
-
if (!adapter)
|
|
1543
|
-
return null;
|
|
1544
|
-
const outputType = adapter.extractOutputType(type, checker);
|
|
1545
|
-
if (!outputType)
|
|
1546
|
-
return null;
|
|
1547
|
-
const inputType = adapter.extractInputType?.(type, checker) ?? undefined;
|
|
1548
|
-
return {
|
|
1549
|
-
adapter,
|
|
1550
|
-
outputType,
|
|
1551
|
-
inputType
|
|
1552
|
-
};
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
// src/schema/adapters/arktype.ts
|
|
1556
|
-
var ARKTYPE_TYPE_PATTERN = /^Type</;
|
|
1557
|
-
var arktypeAdapter = {
|
|
1558
|
-
id: "arktype",
|
|
1559
|
-
packages: ["arktype"],
|
|
1560
|
-
matches(type, checker) {
|
|
1561
|
-
const typeName = checker.typeToString(type);
|
|
1562
|
-
return ARKTYPE_TYPE_PATTERN.test(typeName);
|
|
1563
|
-
},
|
|
1564
|
-
extractOutputType(type, checker) {
|
|
1565
|
-
if (!isTypeReference(type)) {
|
|
1566
|
-
return null;
|
|
1567
|
-
}
|
|
1568
|
-
const args = checker.getTypeArguments(type);
|
|
1569
|
-
if (args.length < 1) {
|
|
1570
|
-
return null;
|
|
1571
|
-
}
|
|
1572
|
-
return args[0];
|
|
1573
|
-
},
|
|
1574
|
-
extractInputType(type, checker) {
|
|
1575
|
-
if (!isTypeReference(type)) {
|
|
1576
|
-
return null;
|
|
1577
|
-
}
|
|
1578
|
-
const args = checker.getTypeArguments(type);
|
|
1579
|
-
if (args.length < 2) {
|
|
1580
|
-
return null;
|
|
1581
|
-
}
|
|
1582
|
-
return args[1];
|
|
1583
|
-
}
|
|
1584
|
-
};
|
|
1585
|
-
|
|
1586
|
-
// src/schema/adapters/typebox.ts
|
|
1587
|
-
var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
|
|
1588
|
-
var typeboxAdapter = {
|
|
1589
|
-
id: "typebox",
|
|
1590
|
-
packages: ["@sinclair/typebox"],
|
|
1591
|
-
matches(type, checker) {
|
|
1592
|
-
const typeName = checker.typeToString(type);
|
|
1593
|
-
if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
|
|
1594
|
-
return false;
|
|
1595
|
-
}
|
|
1596
|
-
const typeProperty = type.getProperty("type");
|
|
1597
|
-
return typeProperty !== undefined;
|
|
1598
|
-
},
|
|
1599
|
-
extractOutputType(type, checker) {
|
|
1600
|
-
const staticSymbol = type.getProperty("static");
|
|
1601
|
-
if (staticSymbol) {
|
|
1602
|
-
return checker.getTypeOfSymbol(staticSymbol);
|
|
1603
|
-
}
|
|
1604
|
-
return null;
|
|
1605
|
-
}
|
|
1606
|
-
};
|
|
1607
|
-
|
|
1608
|
-
// src/schema/adapters/valibot.ts
|
|
1609
|
-
var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
|
|
1610
|
-
var valibotAdapter = {
|
|
1611
|
-
id: "valibot",
|
|
1612
|
-
packages: ["valibot"],
|
|
1613
|
-
matches(type, checker) {
|
|
1614
|
-
const typeName = checker.typeToString(type);
|
|
1615
|
-
return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
|
|
1616
|
-
},
|
|
1617
|
-
extractOutputType(type, checker) {
|
|
1618
|
-
const typesSymbol = type.getProperty("~types");
|
|
1619
|
-
if (!typesSymbol) {
|
|
1620
|
-
return null;
|
|
1621
|
-
}
|
|
1622
|
-
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
1623
|
-
typesType = getNonNullableType(typesType);
|
|
1624
|
-
const outputSymbol = typesType.getProperty("output");
|
|
1625
|
-
if (!outputSymbol) {
|
|
1626
|
-
return null;
|
|
1627
|
-
}
|
|
1628
|
-
return checker.getTypeOfSymbol(outputSymbol);
|
|
1629
|
-
},
|
|
1630
|
-
extractInputType(type, checker) {
|
|
1631
|
-
const typesSymbol = type.getProperty("~types");
|
|
1632
|
-
if (!typesSymbol) {
|
|
1633
|
-
return null;
|
|
1634
|
-
}
|
|
1635
|
-
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
1636
|
-
typesType = getNonNullableType(typesType);
|
|
1637
|
-
const inputSymbol = typesType.getProperty("input");
|
|
1638
|
-
if (!inputSymbol) {
|
|
1639
|
-
return null;
|
|
1640
|
-
}
|
|
1641
|
-
return checker.getTypeOfSymbol(inputSymbol);
|
|
1642
|
-
}
|
|
1643
|
-
};
|
|
1644
|
-
|
|
1645
|
-
// src/schema/adapters/zod.ts
|
|
1646
|
-
var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
|
|
1647
|
-
var zodAdapter = {
|
|
1648
|
-
id: "zod",
|
|
1649
|
-
packages: ["zod"],
|
|
1650
|
-
matches(type, checker) {
|
|
1651
|
-
const typeName = checker.typeToString(type);
|
|
1652
|
-
return ZOD_TYPE_PATTERN.test(typeName);
|
|
1653
|
-
},
|
|
1654
|
-
extractOutputType(type, checker) {
|
|
1655
|
-
const outputSymbol = type.getProperty("_output");
|
|
1656
|
-
if (outputSymbol) {
|
|
1657
|
-
return checker.getTypeOfSymbol(outputSymbol);
|
|
1658
|
-
}
|
|
1659
|
-
const typeSymbol = type.getProperty("_type");
|
|
1660
|
-
if (typeSymbol) {
|
|
1661
|
-
return checker.getTypeOfSymbol(typeSymbol);
|
|
1662
|
-
}
|
|
1663
|
-
return null;
|
|
1664
|
-
},
|
|
1665
|
-
extractInputType(type, checker) {
|
|
1666
|
-
const inputSymbol = type.getProperty("_input");
|
|
1667
|
-
if (inputSymbol) {
|
|
1668
|
-
return checker.getTypeOfSymbol(inputSymbol);
|
|
1669
|
-
}
|
|
1670
|
-
return null;
|
|
1671
|
-
}
|
|
1672
|
-
};
|
|
1673
|
-
|
|
1674
|
-
// src/schema/adapters/index.ts
|
|
1675
|
-
registerAdapter(zodAdapter);
|
|
1676
|
-
registerAdapter(valibotAdapter);
|
|
1677
|
-
registerAdapter(arktypeAdapter);
|
|
1678
|
-
registerAdapter(typeboxAdapter);
|
|
1679
|
-
|
|
1680
|
-
// src/serializers/variables.ts
|
|
1681
|
-
function serializeVariable(node, statement, ctx) {
|
|
1682
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
|
|
1683
|
-
const name = symbol?.getName() ?? node.name.getText();
|
|
2210
|
+
// src/serializers/interfaces.ts
|
|
2211
|
+
import ts9 from "typescript";
|
|
2212
|
+
function serializeInterface(node, ctx) {
|
|
2213
|
+
const { typeChecker: checker } = ctx;
|
|
2214
|
+
const symbol = checker.getSymbolAtLocation(node.name ?? node);
|
|
2215
|
+
const name = symbol?.getName() ?? node.name?.getText();
|
|
1684
2216
|
if (!name)
|
|
1685
2217
|
return null;
|
|
1686
2218
|
const deprecated = isSymbolDeprecated(symbol);
|
|
1687
2219
|
const declSourceFile = node.getSourceFile();
|
|
1688
|
-
const { description, tags, examples } = getJSDocComment(
|
|
2220
|
+
const { description, tags, examples } = getJSDocComment(node);
|
|
1689
2221
|
const source = getSourceLocation(node, declSourceFile);
|
|
1690
|
-
const
|
|
1691
|
-
const
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
2222
|
+
const typeParameters = extractTypeParameters(node, checker);
|
|
2223
|
+
const members = [];
|
|
2224
|
+
const methodsByName = new Map;
|
|
2225
|
+
for (const member of node.members) {
|
|
2226
|
+
if (ts9.isPropertySignature(member)) {
|
|
2227
|
+
const propMember = serializePropertySignature(member, ctx);
|
|
2228
|
+
if (propMember)
|
|
2229
|
+
members.push(propMember);
|
|
2230
|
+
} else if (ts9.isMethodSignature(member)) {
|
|
2231
|
+
const methodMember = serializeMethodSignature(member, ctx);
|
|
2232
|
+
if (methodMember?.name) {
|
|
2233
|
+
if (!methodsByName.has(methodMember.name)) {
|
|
2234
|
+
methodsByName.set(methodMember.name, methodMember);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
} else if (ts9.isCallSignatureDeclaration(member)) {
|
|
2238
|
+
const callMember = serializeCallSignature(member, ctx);
|
|
2239
|
+
if (callMember)
|
|
2240
|
+
members.push(callMember);
|
|
2241
|
+
} else if (ts9.isIndexSignatureDeclaration(member)) {
|
|
2242
|
+
const indexMember = serializeIndexSignature(member, ctx);
|
|
2243
|
+
if (indexMember)
|
|
2244
|
+
members.push(indexMember);
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
members.push(...methodsByName.values());
|
|
2248
|
+
const extendsClause = getInterfaceExtends(node, checker);
|
|
1699
2249
|
return {
|
|
1700
2250
|
id: name,
|
|
1701
2251
|
name,
|
|
1702
|
-
kind: "
|
|
2252
|
+
kind: "interface",
|
|
1703
2253
|
description,
|
|
1704
2254
|
tags,
|
|
1705
2255
|
source,
|
|
1706
|
-
|
|
1707
|
-
|
|
2256
|
+
typeParameters,
|
|
2257
|
+
members: members.length > 0 ? members : undefined,
|
|
2258
|
+
extends: extendsClause,
|
|
1708
2259
|
...deprecated ? { deprecated: true } : {},
|
|
1709
2260
|
...examples.length > 0 ? { examples } : {}
|
|
1710
2261
|
};
|
|
1711
2262
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
if (
|
|
1723
|
-
|
|
1724
|
-
const stdObj = std;
|
|
1725
|
-
if (stdObj.version !== 1)
|
|
1726
|
-
return false;
|
|
1727
|
-
if (typeof stdObj.vendor !== "string")
|
|
1728
|
-
return false;
|
|
1729
|
-
const jsonSchema = stdObj.jsonSchema;
|
|
1730
|
-
if (typeof jsonSchema !== "object" || jsonSchema === null)
|
|
1731
|
-
return false;
|
|
1732
|
-
const jsObj = jsonSchema;
|
|
1733
|
-
return typeof jsObj.output === "function" && typeof jsObj.input === "function";
|
|
1734
|
-
}
|
|
1735
|
-
var cachedRuntime;
|
|
1736
|
-
function commandExists(cmd) {
|
|
1737
|
-
try {
|
|
1738
|
-
const result = spawnSync(process.platform === "win32" ? "where" : "which", [cmd], {
|
|
1739
|
-
stdio: "ignore"
|
|
1740
|
-
});
|
|
1741
|
-
return result.status === 0;
|
|
1742
|
-
} catch {
|
|
1743
|
-
return false;
|
|
2263
|
+
function serializePropertySignature(node, ctx) {
|
|
2264
|
+
const { typeChecker: checker } = ctx;
|
|
2265
|
+
const name = node.name.getText();
|
|
2266
|
+
const { description, tags } = getJSDocComment(node);
|
|
2267
|
+
const type = checker.getTypeAtLocation(node);
|
|
2268
|
+
const schema = buildSchema(type, checker, ctx);
|
|
2269
|
+
registerReferencedTypes(type, ctx);
|
|
2270
|
+
const flags = {};
|
|
2271
|
+
if (node.questionToken)
|
|
2272
|
+
flags.optional = true;
|
|
2273
|
+
if (node.modifiers?.some((m) => m.kind === ts9.SyntaxKind.ReadonlyKeyword)) {
|
|
2274
|
+
flags.readonly = true;
|
|
1744
2275
|
}
|
|
2276
|
+
return {
|
|
2277
|
+
name,
|
|
2278
|
+
kind: "property",
|
|
2279
|
+
description,
|
|
2280
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
2281
|
+
schema,
|
|
2282
|
+
flags: Object.keys(flags).length > 0 ? flags : undefined
|
|
2283
|
+
};
|
|
1745
2284
|
}
|
|
1746
|
-
function
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
}
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
cachedRuntime = {
|
|
1769
|
-
cmd: "tsx",
|
|
1770
|
-
args: [],
|
|
1771
|
-
name: "tsx"
|
|
1772
|
-
};
|
|
1773
|
-
return cachedRuntime;
|
|
1774
|
-
}
|
|
1775
|
-
if (commandExists("ts-node")) {
|
|
1776
|
-
cachedRuntime = {
|
|
1777
|
-
cmd: "ts-node",
|
|
1778
|
-
args: ["--transpile-only"],
|
|
1779
|
-
name: "ts-node"
|
|
2285
|
+
function serializeMethodSignature(node, ctx) {
|
|
2286
|
+
const { typeChecker: checker } = ctx;
|
|
2287
|
+
const name = node.name.getText();
|
|
2288
|
+
const { description, tags } = getJSDocComment(node);
|
|
2289
|
+
const type = checker.getTypeAtLocation(node);
|
|
2290
|
+
const callSignatures = type.getCallSignatures();
|
|
2291
|
+
const signatures = callSignatures.map((sig, index) => {
|
|
2292
|
+
const params = extractParameters(sig, ctx);
|
|
2293
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
2294
|
+
registerReferencedTypes(returnType, ctx);
|
|
2295
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
2296
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, checker);
|
|
2297
|
+
return {
|
|
2298
|
+
parameters: params.length > 0 ? params : undefined,
|
|
2299
|
+
returns: {
|
|
2300
|
+
schema: buildSchema(returnType, checker, ctx)
|
|
2301
|
+
},
|
|
2302
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
2303
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
2304
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
2305
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
2306
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
1780
2307
|
};
|
|
1781
|
-
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
function isTypeBoxSchema(obj) {
|
|
1794
|
-
if (!obj || typeof obj !== 'object') return false;
|
|
1795
|
-
// TypeBox schemas always have Kind symbol (Union, Object, String, etc.)
|
|
1796
|
-
// Also check for common JSON Schema props to avoid false positives
|
|
1797
|
-
if (!obj[TYPEBOX_KIND]) return false;
|
|
1798
|
-
return typeof obj.type === 'string' || 'anyOf' in obj || 'oneOf' in obj || 'allOf' in obj;
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
function sanitizeTypeBoxSchema(schema) {
|
|
1802
|
-
// JSON.stringify removes symbol keys, keeping only JSON Schema props
|
|
1803
|
-
return JSON.parse(JSON.stringify(schema));
|
|
2308
|
+
});
|
|
2309
|
+
const flags = {};
|
|
2310
|
+
if (node.questionToken)
|
|
2311
|
+
flags.optional = true;
|
|
2312
|
+
return {
|
|
2313
|
+
name,
|
|
2314
|
+
kind: "method",
|
|
2315
|
+
description,
|
|
2316
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
2317
|
+
signatures: signatures.length > 0 ? signatures : undefined,
|
|
2318
|
+
flags: Object.keys(flags).length > 0 ? flags : undefined
|
|
2319
|
+
};
|
|
1804
2320
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
} else if (name !== 'default') {
|
|
1825
|
-
exports[name] = value;
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
// Check each export
|
|
1830
|
-
for (const [name, value] of Object.entries(exports)) {
|
|
1831
|
-
if (name.startsWith('_')) continue;
|
|
1832
|
-
if (typeof value !== 'object' || value === null) continue;
|
|
1833
|
-
|
|
1834
|
-
// Priority 1: Standard JSON Schema (Zod 4.2+, ArkType 2.1.28+, Valibot 1.2+)
|
|
1835
|
-
const std = value['~standard'];
|
|
1836
|
-
if (std && typeof std === 'object' && std.version === 1 && typeof std.vendor === 'string' && std.jsonSchema && typeof std.jsonSchema.output === 'function') {
|
|
1837
|
-
try {
|
|
1838
|
-
// Per spec: pass options object with target and optional libraryOptions
|
|
1839
|
-
const options = { target: target || 'draft-2020-12', ...(libraryOptions && { libraryOptions }) };
|
|
1840
|
-
const outputSchema = std.jsonSchema.output(options);
|
|
1841
|
-
const inputSchema = typeof std.jsonSchema.input === 'function' ? std.jsonSchema.input(options) : undefined;
|
|
1842
|
-
results.push({
|
|
1843
|
-
exportName: name,
|
|
1844
|
-
vendor: std.vendor,
|
|
1845
|
-
outputSchema,
|
|
1846
|
-
inputSchema
|
|
1847
|
-
});
|
|
1848
|
-
} catch (e) {
|
|
1849
|
-
// Skip schemas that fail to extract
|
|
1850
|
-
}
|
|
1851
|
-
continue;
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
// Priority 2: TypeBox (schema IS JSON Schema)
|
|
1855
|
-
if (isTypeBoxSchema(value)) {
|
|
1856
|
-
try {
|
|
1857
|
-
results.push({
|
|
1858
|
-
exportName: name,
|
|
1859
|
-
vendor: 'typebox',
|
|
1860
|
-
outputSchema: sanitizeTypeBoxSchema(value)
|
|
1861
|
-
});
|
|
1862
|
-
} catch (e) {
|
|
1863
|
-
// Skip schemas that fail to extract
|
|
2321
|
+
function serializeCallSignature(node, ctx) {
|
|
2322
|
+
const { typeChecker: checker } = ctx;
|
|
2323
|
+
const { description, tags } = getJSDocComment(node);
|
|
2324
|
+
const sig = checker.getSignatureFromDeclaration(node);
|
|
2325
|
+
if (!sig)
|
|
2326
|
+
return null;
|
|
2327
|
+
const params = extractParameters(sig, ctx);
|
|
2328
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
2329
|
+
registerReferencedTypes(returnType, ctx);
|
|
2330
|
+
return {
|
|
2331
|
+
name: "()",
|
|
2332
|
+
kind: "call-signature",
|
|
2333
|
+
description,
|
|
2334
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
2335
|
+
signatures: [
|
|
2336
|
+
{
|
|
2337
|
+
parameters: params.length > 0 ? params : undefined,
|
|
2338
|
+
returns: {
|
|
2339
|
+
schema: buildSchema(returnType, checker, ctx)
|
|
1864
2340
|
}
|
|
1865
|
-
continue;
|
|
1866
2341
|
}
|
|
2342
|
+
]
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
function serializeIndexSignature(node, ctx) {
|
|
2346
|
+
const { typeChecker: checker } = ctx;
|
|
2347
|
+
const { description, tags } = getJSDocComment(node);
|
|
2348
|
+
const valueType = node.type ? checker.getTypeAtLocation(node.type) : checker.getAnyType();
|
|
2349
|
+
const valueSchema = buildSchema(valueType, checker, ctx);
|
|
2350
|
+
registerReferencedTypes(valueType, ctx);
|
|
2351
|
+
const keyParam = node.parameters[0];
|
|
2352
|
+
const keyType = keyParam?.type ? checker.getTypeAtLocation(keyParam.type) : checker.getStringType();
|
|
2353
|
+
const keyTypeName = checker.typeToString(keyType);
|
|
2354
|
+
return {
|
|
2355
|
+
name: `[${keyTypeName}]`,
|
|
2356
|
+
kind: "index-signature",
|
|
2357
|
+
description,
|
|
2358
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
2359
|
+
schema: {
|
|
2360
|
+
type: "object",
|
|
2361
|
+
additionalProperties: valueSchema
|
|
2362
|
+
}
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
function getInterfaceExtends(node, checker) {
|
|
2366
|
+
if (!node.heritageClauses)
|
|
2367
|
+
return;
|
|
2368
|
+
for (const clause of node.heritageClauses) {
|
|
2369
|
+
if (clause.token === ts9.SyntaxKind.ExtendsKeyword && clause.types.length > 0) {
|
|
2370
|
+
const names = clause.types.map((expr) => {
|
|
2371
|
+
const type = checker.getTypeAtLocation(expr);
|
|
2372
|
+
return type.getSymbol()?.getName() ?? expr.expression.getText();
|
|
2373
|
+
});
|
|
2374
|
+
return names.join(" & ");
|
|
1867
2375
|
}
|
|
1868
|
-
|
|
1869
|
-
console.log(JSON.stringify({ success: true, results }));
|
|
1870
|
-
} catch (e) {
|
|
1871
|
-
console.log(JSON.stringify({ success: false, error: e.message }));
|
|
1872
2376
|
}
|
|
2377
|
+
return;
|
|
1873
2378
|
}
|
|
1874
2379
|
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
if (
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2380
|
+
// src/serializers/type-aliases.ts
|
|
2381
|
+
import ts10 from "typescript";
|
|
2382
|
+
function buildIntersectionSchemaFromNode(node, ctx) {
|
|
2383
|
+
const types = node.types;
|
|
2384
|
+
const schemas = [];
|
|
2385
|
+
for (const typeNode of types) {
|
|
2386
|
+
const type = ctx.typeChecker.getTypeAtLocation(typeNode);
|
|
2387
|
+
registerReferencedTypes(type, ctx);
|
|
2388
|
+
schemas.push(buildSchema(type, ctx.typeChecker, ctx));
|
|
2389
|
+
}
|
|
2390
|
+
if (schemas.length === 0) {
|
|
2391
|
+
return { type: "never" };
|
|
2392
|
+
}
|
|
2393
|
+
if (schemas.length === 1) {
|
|
2394
|
+
return schemas[0];
|
|
2395
|
+
}
|
|
2396
|
+
return { allOf: schemas };
|
|
1889
2397
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
2398
|
+
function serializeTypeAlias(node, ctx) {
|
|
2399
|
+
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
2400
|
+
const name = symbol?.getName() ?? node.name?.getText();
|
|
2401
|
+
if (!name)
|
|
2402
|
+
return null;
|
|
2403
|
+
const deprecated = isSymbolDeprecated(symbol);
|
|
2404
|
+
const declSourceFile = node.getSourceFile();
|
|
2405
|
+
const { description, tags, examples } = getJSDocComment(node);
|
|
2406
|
+
const source = getSourceLocation(node, declSourceFile);
|
|
2407
|
+
const typeParameters = extractTypeParameters(node, ctx.typeChecker);
|
|
2408
|
+
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
2409
|
+
registerReferencedTypes(type, ctx);
|
|
2410
|
+
let schema;
|
|
2411
|
+
if (ts10.isIntersectionTypeNode(node.type)) {
|
|
2412
|
+
schema = buildIntersectionSchemaFromNode(node.type, ctx);
|
|
2413
|
+
} else {
|
|
2414
|
+
schema = buildSchema(type, ctx.typeChecker, ctx);
|
|
2415
|
+
}
|
|
2416
|
+
return {
|
|
2417
|
+
id: name,
|
|
2418
|
+
name,
|
|
2419
|
+
kind: "type",
|
|
2420
|
+
description,
|
|
2421
|
+
tags,
|
|
2422
|
+
source,
|
|
2423
|
+
typeParameters,
|
|
2424
|
+
schema,
|
|
2425
|
+
...deprecated ? { deprecated: true } : {},
|
|
2426
|
+
...examples.length > 0 ? { examples } : {}
|
|
2427
|
+
};
|
|
1893
2428
|
}
|
|
1894
2429
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
// Build exports map
|
|
1905
|
-
const exports: Record<string, unknown> = {};
|
|
1906
|
-
for (const [name, value] of Object.entries(mod)) {
|
|
1907
|
-
if (name === 'default' && typeof value === 'object' && value !== null) {
|
|
1908
|
-
Object.assign(exports, value);
|
|
1909
|
-
} else if (name !== 'default') {
|
|
1910
|
-
exports[name] = value;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
// Check each export
|
|
1915
|
-
for (const [name, value] of Object.entries(exports)) {
|
|
1916
|
-
if (name.startsWith('_')) continue;
|
|
1917
|
-
if (typeof value !== 'object' || value === null) continue;
|
|
1918
|
-
|
|
1919
|
-
const v = value as Record<string, unknown>;
|
|
1920
|
-
const std = v['~standard'] as Record<string, unknown> | undefined;
|
|
1921
|
-
|
|
1922
|
-
// Standard JSON Schema
|
|
1923
|
-
if (std && typeof std === 'object' && std.version === 1 && typeof std.vendor === 'string') {
|
|
1924
|
-
const jsonSchema = std.jsonSchema as Record<string, unknown> | undefined;
|
|
1925
|
-
if (jsonSchema && typeof jsonSchema.output === 'function') {
|
|
1926
|
-
try {
|
|
1927
|
-
const options = { target: target || 'draft-2020-12', ...(libraryOptions && { libraryOptions }) };
|
|
1928
|
-
const outputSchema = (jsonSchema.output as Function)(options);
|
|
1929
|
-
const inputSchema = typeof jsonSchema.input === 'function' ? (jsonSchema.input as Function)(options) : undefined;
|
|
1930
|
-
results.push({ exportName: name, vendor: std.vendor as string, outputSchema, inputSchema });
|
|
1931
|
-
} catch {}
|
|
1932
|
-
continue;
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
// TypeBox
|
|
1937
|
-
if (isTypeBoxSchema(value)) {
|
|
1938
|
-
try {
|
|
1939
|
-
results.push({ exportName: name, vendor: 'typebox', outputSchema: sanitizeTypeBoxSchema(value) });
|
|
1940
|
-
} catch {}
|
|
1941
|
-
}
|
|
2430
|
+
// src/schema/registry.ts
|
|
2431
|
+
function isTypeReference(type) {
|
|
2432
|
+
return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
|
|
2433
|
+
}
|
|
2434
|
+
function getNonNullableType(type) {
|
|
2435
|
+
if (type.isUnion()) {
|
|
2436
|
+
const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
|
|
2437
|
+
if (nonNullable.length === 1) {
|
|
2438
|
+
return nonNullable[0];
|
|
1942
2439
|
}
|
|
1943
|
-
|
|
1944
|
-
console.log(JSON.stringify({ success: true, results }));
|
|
1945
|
-
} catch (e) {
|
|
1946
|
-
console.log(JSON.stringify({ success: false, error: (e as Error).message }));
|
|
1947
2440
|
}
|
|
2441
|
+
return type;
|
|
1948
2442
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2443
|
+
var adapters = [];
|
|
2444
|
+
function registerAdapter(adapter) {
|
|
2445
|
+
adapters.push(adapter);
|
|
2446
|
+
}
|
|
2447
|
+
function findAdapter(type, checker) {
|
|
2448
|
+
return adapters.find((a) => a.matches(type, checker));
|
|
2449
|
+
}
|
|
2450
|
+
function isSchemaType(type, checker) {
|
|
2451
|
+
return adapters.some((a) => a.matches(type, checker));
|
|
2452
|
+
}
|
|
2453
|
+
function extractSchemaType(type, checker) {
|
|
2454
|
+
const adapter = findAdapter(type, checker);
|
|
2455
|
+
if (!adapter)
|
|
2456
|
+
return null;
|
|
2457
|
+
const outputType = adapter.extractOutputType(type, checker);
|
|
2458
|
+
if (!outputType)
|
|
2459
|
+
return null;
|
|
2460
|
+
const inputType = adapter.extractInputType?.(type, checker) ?? undefined;
|
|
2461
|
+
return {
|
|
2462
|
+
adapter,
|
|
2463
|
+
outputType,
|
|
2464
|
+
inputType
|
|
1957
2465
|
};
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// src/schema/adapters/arktype.ts
|
|
2469
|
+
var ARKTYPE_TYPE_PATTERN = /^Type</;
|
|
2470
|
+
var arktypeAdapter = {
|
|
2471
|
+
id: "arktype",
|
|
2472
|
+
packages: ["arktype"],
|
|
2473
|
+
matches(type, checker) {
|
|
2474
|
+
const typeName = checker.typeToString(type);
|
|
2475
|
+
return ARKTYPE_TYPE_PATTERN.test(typeName);
|
|
2476
|
+
},
|
|
2477
|
+
extractOutputType(type, checker) {
|
|
2478
|
+
if (!isTypeReference(type)) {
|
|
2479
|
+
return null;
|
|
2480
|
+
}
|
|
2481
|
+
const args = checker.getTypeArguments(type);
|
|
2482
|
+
if (args.length < 1) {
|
|
2483
|
+
return null;
|
|
2484
|
+
}
|
|
2485
|
+
return args[0];
|
|
2486
|
+
},
|
|
2487
|
+
extractInputType(type, checker) {
|
|
2488
|
+
if (!isTypeReference(type)) {
|
|
2489
|
+
return null;
|
|
2490
|
+
}
|
|
2491
|
+
const args = checker.getTypeArguments(type);
|
|
2492
|
+
if (args.length < 2) {
|
|
2493
|
+
return null;
|
|
2494
|
+
}
|
|
2495
|
+
return args[1];
|
|
1966
2496
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
try {
|
|
1989
|
-
fs.unlinkSync(workerPath);
|
|
1990
|
-
} catch {}
|
|
1991
|
-
if (code !== 0) {
|
|
1992
|
-
result.errors.push(`Extraction failed (${runtime.name}): ${stderr || `exit code ${code}`}`);
|
|
1993
|
-
resolve(result);
|
|
1994
|
-
return;
|
|
1995
|
-
}
|
|
1996
|
-
try {
|
|
1997
|
-
const parsed = JSON.parse(stdout);
|
|
1998
|
-
if (!parsed.success) {
|
|
1999
|
-
result.errors.push(`Extraction failed: ${parsed.error}`);
|
|
2000
|
-
resolve(result);
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
for (const item of parsed.results) {
|
|
2004
|
-
result.schemas.set(item.exportName, {
|
|
2005
|
-
exportName: item.exportName,
|
|
2006
|
-
vendor: item.vendor,
|
|
2007
|
-
outputSchema: item.outputSchema,
|
|
2008
|
-
inputSchema: item.inputSchema
|
|
2009
|
-
});
|
|
2010
|
-
}
|
|
2011
|
-
} catch (e) {
|
|
2012
|
-
result.errors.push(`Failed to parse extraction output: ${e}`);
|
|
2013
|
-
}
|
|
2014
|
-
resolve(result);
|
|
2015
|
-
});
|
|
2016
|
-
child.on("error", (err) => {
|
|
2017
|
-
try {
|
|
2018
|
-
fs.unlinkSync(workerPath);
|
|
2019
|
-
} catch {}
|
|
2020
|
-
result.errors.push(`Subprocess error: ${err.message}`);
|
|
2021
|
-
resolve(result);
|
|
2022
|
-
});
|
|
2023
|
-
});
|
|
2024
|
-
} catch (e) {
|
|
2025
|
-
try {
|
|
2026
|
-
fs.unlinkSync(workerPath);
|
|
2027
|
-
} catch {}
|
|
2028
|
-
result.errors.push(`Failed to create worker script: ${e}`);
|
|
2029
|
-
return result;
|
|
2497
|
+
};
|
|
2498
|
+
|
|
2499
|
+
// src/schema/adapters/typebox.ts
|
|
2500
|
+
var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
|
|
2501
|
+
var typeboxAdapter = {
|
|
2502
|
+
id: "typebox",
|
|
2503
|
+
packages: ["@sinclair/typebox"],
|
|
2504
|
+
matches(type, checker) {
|
|
2505
|
+
const typeName = checker.typeToString(type);
|
|
2506
|
+
if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
|
|
2507
|
+
return false;
|
|
2508
|
+
}
|
|
2509
|
+
const typeProperty = type.getProperty("type");
|
|
2510
|
+
return typeProperty !== undefined;
|
|
2511
|
+
},
|
|
2512
|
+
extractOutputType(type, checker) {
|
|
2513
|
+
const staticSymbol = type.getProperty("static");
|
|
2514
|
+
if (staticSymbol) {
|
|
2515
|
+
return checker.getTypeOfSymbol(staticSymbol);
|
|
2516
|
+
}
|
|
2517
|
+
return null;
|
|
2030
2518
|
}
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2519
|
+
};
|
|
2520
|
+
|
|
2521
|
+
// src/schema/adapters/valibot.ts
|
|
2522
|
+
var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
|
|
2523
|
+
var valibotAdapter = {
|
|
2524
|
+
id: "valibot",
|
|
2525
|
+
packages: ["valibot"],
|
|
2526
|
+
matches(type, checker) {
|
|
2527
|
+
const typeName = checker.typeToString(type);
|
|
2528
|
+
return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
|
|
2529
|
+
},
|
|
2530
|
+
extractOutputType(type, checker) {
|
|
2531
|
+
const typesSymbol = type.getProperty("~types");
|
|
2532
|
+
if (!typesSymbol) {
|
|
2036
2533
|
return null;
|
|
2037
2534
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
const
|
|
2041
|
-
if (
|
|
2042
|
-
return
|
|
2043
|
-
}
|
|
2044
|
-
} catch {}
|
|
2045
|
-
return null;
|
|
2046
|
-
}
|
|
2047
|
-
function resolveCompiledPath(tsPath, baseDir) {
|
|
2048
|
-
const relativePath = path2.relative(baseDir, tsPath);
|
|
2049
|
-
const withoutExt = relativePath.replace(/\.tsx?$/, "");
|
|
2050
|
-
const srcPrefix = withoutExt.replace(/^src\//, "");
|
|
2051
|
-
const tsconfigOutDir = readTsconfigOutDir(baseDir);
|
|
2052
|
-
const extensions = [".js", ".mjs", ".cjs"];
|
|
2053
|
-
const candidates = [];
|
|
2054
|
-
if (tsconfigOutDir) {
|
|
2055
|
-
for (const ext of extensions) {
|
|
2056
|
-
candidates.push(path2.join(baseDir, tsconfigOutDir, `${srcPrefix}${ext}`));
|
|
2535
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
2536
|
+
typesType = getNonNullableType(typesType);
|
|
2537
|
+
const outputSymbol = typesType.getProperty("output");
|
|
2538
|
+
if (!outputSymbol) {
|
|
2539
|
+
return null;
|
|
2057
2540
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
candidates.push(path2.join(baseDir, outDir, `${srcPrefix}${ext}`));
|
|
2541
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
2542
|
+
},
|
|
2543
|
+
extractInputType(type, checker) {
|
|
2544
|
+
const typesSymbol = type.getProperty("~types");
|
|
2545
|
+
if (!typesSymbol) {
|
|
2546
|
+
return null;
|
|
2065
2547
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
if (workspaceMatch) {
|
|
2072
|
-
const pkgRoot = workspaceMatch[1];
|
|
2073
|
-
for (const ext of extensions) {
|
|
2074
|
-
candidates.push(path2.join(pkgRoot, "dist", `${srcPrefix}${ext}`));
|
|
2548
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
2549
|
+
typesType = getNonNullableType(typesType);
|
|
2550
|
+
const inputSymbol = typesType.getProperty("input");
|
|
2551
|
+
if (!inputSymbol) {
|
|
2552
|
+
return null;
|
|
2075
2553
|
}
|
|
2554
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
2076
2555
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2556
|
+
};
|
|
2557
|
+
|
|
2558
|
+
// src/schema/adapters/zod.ts
|
|
2559
|
+
var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
|
|
2560
|
+
var zodAdapter = {
|
|
2561
|
+
id: "zod",
|
|
2562
|
+
packages: ["zod"],
|
|
2563
|
+
matches(type, checker) {
|
|
2564
|
+
const typeName = checker.typeToString(type);
|
|
2565
|
+
return ZOD_TYPE_PATTERN.test(typeName);
|
|
2566
|
+
},
|
|
2567
|
+
extractOutputType(type, checker) {
|
|
2568
|
+
const outputSymbol = type.getProperty("_output");
|
|
2569
|
+
if (outputSymbol) {
|
|
2570
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
2080
2571
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
async function extractStandardSchemas(compiledJsPath, options = {}) {
|
|
2085
|
-
const { timeout = 1e4, target = "draft-2020-12", libraryOptions } = options;
|
|
2086
|
-
const result = {
|
|
2087
|
-
schemas: new Map,
|
|
2088
|
-
errors: []
|
|
2089
|
-
};
|
|
2090
|
-
if (!fs.existsSync(compiledJsPath)) {
|
|
2091
|
-
result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
|
|
2092
|
-
return result;
|
|
2093
|
-
}
|
|
2094
|
-
const optionsJson = JSON.stringify({ target, libraryOptions });
|
|
2095
|
-
return new Promise((resolve) => {
|
|
2096
|
-
const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, optionsJson], {
|
|
2097
|
-
timeout,
|
|
2098
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
2099
|
-
});
|
|
2100
|
-
let stdout = "";
|
|
2101
|
-
let stderr = "";
|
|
2102
|
-
child.stdout.on("data", (data) => {
|
|
2103
|
-
stdout += data.toString();
|
|
2104
|
-
});
|
|
2105
|
-
child.stderr.on("data", (data) => {
|
|
2106
|
-
stderr += data.toString();
|
|
2107
|
-
});
|
|
2108
|
-
child.on("close", (code) => {
|
|
2109
|
-
if (code !== 0) {
|
|
2110
|
-
result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
|
|
2111
|
-
resolve(result);
|
|
2112
|
-
return;
|
|
2113
|
-
}
|
|
2114
|
-
try {
|
|
2115
|
-
const parsed = JSON.parse(stdout);
|
|
2116
|
-
if (!parsed.success) {
|
|
2117
|
-
result.errors.push(`Extraction failed: ${parsed.error}`);
|
|
2118
|
-
resolve(result);
|
|
2119
|
-
return;
|
|
2120
|
-
}
|
|
2121
|
-
for (const item of parsed.results) {
|
|
2122
|
-
result.schemas.set(item.exportName, {
|
|
2123
|
-
exportName: item.exportName,
|
|
2124
|
-
vendor: item.vendor,
|
|
2125
|
-
outputSchema: item.outputSchema,
|
|
2126
|
-
inputSchema: item.inputSchema
|
|
2127
|
-
});
|
|
2128
|
-
}
|
|
2129
|
-
} catch (e) {
|
|
2130
|
-
result.errors.push(`Failed to parse extraction output: ${e}`);
|
|
2131
|
-
}
|
|
2132
|
-
resolve(result);
|
|
2133
|
-
});
|
|
2134
|
-
child.on("error", (err) => {
|
|
2135
|
-
result.errors.push(`Subprocess error: ${err.message}`);
|
|
2136
|
-
resolve(result);
|
|
2137
|
-
});
|
|
2138
|
-
});
|
|
2139
|
-
}
|
|
2140
|
-
async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
|
|
2141
|
-
const { preferDirectTs, ...extractOptions } = options;
|
|
2142
|
-
const isTypeScript = /\.tsx?$/.test(entryFile);
|
|
2143
|
-
if (!preferDirectTs) {
|
|
2144
|
-
const compiledPath = resolveCompiledPath(entryFile, baseDir);
|
|
2145
|
-
if (compiledPath) {
|
|
2146
|
-
const result = await extractStandardSchemas(compiledPath, extractOptions);
|
|
2147
|
-
return {
|
|
2148
|
-
...result,
|
|
2149
|
-
info: { method: "compiled", path: compiledPath }
|
|
2150
|
-
};
|
|
2572
|
+
const typeSymbol = type.getProperty("_type");
|
|
2573
|
+
if (typeSymbol) {
|
|
2574
|
+
return checker.getTypeOfSymbol(typeSymbol);
|
|
2151
2575
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
return
|
|
2158
|
-
...result,
|
|
2159
|
-
info: { method: "direct-ts", runtime: runtime2.name, path: entryFile }
|
|
2160
|
-
};
|
|
2576
|
+
return null;
|
|
2577
|
+
},
|
|
2578
|
+
extractInputType(type, checker) {
|
|
2579
|
+
const inputSymbol = type.getProperty("_input");
|
|
2580
|
+
if (inputSymbol) {
|
|
2581
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
2161
2582
|
}
|
|
2583
|
+
return null;
|
|
2162
2584
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2585
|
+
};
|
|
2586
|
+
|
|
2587
|
+
// src/schema/adapters/index.ts
|
|
2588
|
+
registerAdapter(zodAdapter);
|
|
2589
|
+
registerAdapter(valibotAdapter);
|
|
2590
|
+
registerAdapter(arktypeAdapter);
|
|
2591
|
+
registerAdapter(typeboxAdapter);
|
|
2592
|
+
|
|
2593
|
+
// src/serializers/variables.ts
|
|
2594
|
+
function serializeVariable(node, statement, ctx) {
|
|
2595
|
+
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
|
|
2596
|
+
const name = symbol?.getName() ?? node.name.getText();
|
|
2597
|
+
if (!name)
|
|
2598
|
+
return null;
|
|
2599
|
+
const deprecated = isSymbolDeprecated(symbol);
|
|
2600
|
+
const declSourceFile = node.getSourceFile();
|
|
2601
|
+
const { description, tags, examples } = getJSDocComment(statement);
|
|
2602
|
+
const source = getSourceLocation(node, declSourceFile);
|
|
2603
|
+
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
2604
|
+
const schemaExtraction = extractSchemaType(type, ctx.typeChecker);
|
|
2605
|
+
const typeToSerialize = schemaExtraction?.outputType ?? type;
|
|
2606
|
+
registerReferencedTypes(typeToSerialize, ctx);
|
|
2607
|
+
const schema = buildSchema(typeToSerialize, ctx.typeChecker, ctx);
|
|
2608
|
+
const flags = schemaExtraction ? {
|
|
2609
|
+
schemaLibrary: schemaExtraction.adapter.id,
|
|
2610
|
+
...schemaExtraction.inputType && schemaExtraction.inputType !== schemaExtraction.outputType ? { hasTransform: true } : {}
|
|
2611
|
+
} : undefined;
|
|
2165
2612
|
return {
|
|
2166
|
-
|
|
2167
|
-
|
|
2613
|
+
id: name,
|
|
2614
|
+
name,
|
|
2615
|
+
kind: "variable",
|
|
2616
|
+
description,
|
|
2617
|
+
tags,
|
|
2618
|
+
source,
|
|
2619
|
+
schema,
|
|
2620
|
+
...flags ? { flags } : {},
|
|
2621
|
+
...deprecated ? { deprecated: true } : {},
|
|
2622
|
+
...examples.length > 0 ? { examples } : {}
|
|
2168
2623
|
};
|
|
2169
2624
|
}
|
|
2170
2625
|
|
|
@@ -2381,6 +2836,18 @@ function normalizeStandardType(schema, options) {
|
|
|
2381
2836
|
result[keyword] = schema[keyword];
|
|
2382
2837
|
}
|
|
2383
2838
|
}
|
|
2839
|
+
for (const key of Object.keys(schema)) {
|
|
2840
|
+
if (key.startsWith("x-ts-") && schema[key] !== undefined) {
|
|
2841
|
+
const value = schema[key];
|
|
2842
|
+
if (isSchemaLike(value)) {
|
|
2843
|
+
result[key] = normalizeSchemaInternal(value, options);
|
|
2844
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
2845
|
+
result[key] = normalizeGenericObject(value, options);
|
|
2846
|
+
} else {
|
|
2847
|
+
result[key] = value;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2384
2851
|
return result;
|
|
2385
2852
|
}
|
|
2386
2853
|
function normalizeRef(schema, options) {
|
|
@@ -2388,6 +2855,11 @@ function normalizeRef(schema, options) {
|
|
|
2388
2855
|
if (schema.typeArguments && schema.typeArguments.length > 0) {
|
|
2389
2856
|
result["x-ts-type-arguments"] = schema.typeArguments.map((arg) => normalizeSchemaInternal(arg, options));
|
|
2390
2857
|
}
|
|
2858
|
+
for (const key of Object.keys(schema)) {
|
|
2859
|
+
if (key.startsWith("x-ts-") && schema[key] !== undefined) {
|
|
2860
|
+
result[key] = schema[key];
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2391
2863
|
return result;
|
|
2392
2864
|
}
|
|
2393
2865
|
function normalizeCombinator(keyword, schemas, originalSchema, options) {
|
|
@@ -2589,23 +3061,7 @@ function isOptionalMember(member) {
|
|
|
2589
3061
|
import * as fs2 from "node:fs";
|
|
2590
3062
|
import * as path3 from "node:path";
|
|
2591
3063
|
import { SCHEMA_URL, SCHEMA_VERSION } from "@openpkg-ts/spec";
|
|
2592
|
-
import
|
|
2593
|
-
|
|
2594
|
-
// src/serializers/context.ts
|
|
2595
|
-
function createContext(program, sourceFile, options = {}) {
|
|
2596
|
-
return {
|
|
2597
|
-
typeChecker: program.getTypeChecker(),
|
|
2598
|
-
program,
|
|
2599
|
-
sourceFile,
|
|
2600
|
-
maxTypeDepth: options.maxTypeDepth ?? 4,
|
|
2601
|
-
maxExternalTypeDepth: options.maxExternalTypeDepth ?? 2,
|
|
2602
|
-
currentDepth: 0,
|
|
2603
|
-
resolveExternalTypes: options.resolveExternalTypes ?? true,
|
|
2604
|
-
typeRegistry: new TypeRegistry,
|
|
2605
|
-
exportedIds: new Set,
|
|
2606
|
-
visitedTypes: new Set
|
|
2607
|
-
};
|
|
2608
|
-
}
|
|
3064
|
+
import ts11 from "typescript";
|
|
2609
3065
|
|
|
2610
3066
|
// src/builder/schema-merger.ts
|
|
2611
3067
|
function mergeRuntimeSchemas(staticExports, runtimeSchemas) {
|
|
@@ -2907,11 +3363,11 @@ function collectAllRefsWithContext(obj, refs, state) {
|
|
|
2907
3363
|
function findTypeDefinition(typeName, program, sourceFile) {
|
|
2908
3364
|
const checker = program.getTypeChecker();
|
|
2909
3365
|
const findInNode = (node) => {
|
|
2910
|
-
if ((
|
|
3366
|
+
if ((ts11.isInterfaceDeclaration(node) || ts11.isTypeAliasDeclaration(node) || ts11.isClassDeclaration(node) || ts11.isEnumDeclaration(node)) && node.name?.text === typeName) {
|
|
2911
3367
|
const sf = node.getSourceFile();
|
|
2912
3368
|
return sf.fileName;
|
|
2913
3369
|
}
|
|
2914
|
-
return
|
|
3370
|
+
return ts11.forEachChild(node, findInNode);
|
|
2915
3371
|
};
|
|
2916
3372
|
const entryResult = findInNode(sourceFile);
|
|
2917
3373
|
if (entryResult)
|
|
@@ -2923,7 +3379,7 @@ function findTypeDefinition(typeName, program, sourceFile) {
|
|
|
2923
3379
|
return result;
|
|
2924
3380
|
}
|
|
2925
3381
|
}
|
|
2926
|
-
const symbol = checker.resolveName(typeName, sourceFile,
|
|
3382
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts11.SymbolFlags.Type, false);
|
|
2927
3383
|
if (symbol?.declarations?.[0]) {
|
|
2928
3384
|
return symbol.declarations[0].getSourceFile().fileName;
|
|
2929
3385
|
}
|
|
@@ -2940,7 +3396,7 @@ function isExternalType2(definedIn, baseDir) {
|
|
|
2940
3396
|
}
|
|
2941
3397
|
function hasInternalTag(typeName, program, sourceFile) {
|
|
2942
3398
|
const checker = program.getTypeChecker();
|
|
2943
|
-
const symbol = checker.resolveName(typeName, sourceFile,
|
|
3399
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts11.SymbolFlags.Type, false);
|
|
2944
3400
|
if (!symbol)
|
|
2945
3401
|
return false;
|
|
2946
3402
|
const jsTags = symbol.getJsDocTags();
|
|
@@ -2989,36 +3445,36 @@ function collectForgottenExports(exports, types, program, sourceFile, exportedId
|
|
|
2989
3445
|
}
|
|
2990
3446
|
function resolveExportTarget(symbol, checker) {
|
|
2991
3447
|
let targetSymbol = symbol;
|
|
2992
|
-
if (symbol.flags &
|
|
3448
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
2993
3449
|
const aliasTarget = checker.getAliasedSymbol(symbol);
|
|
2994
3450
|
if (aliasTarget && aliasTarget !== symbol) {
|
|
2995
3451
|
targetSymbol = aliasTarget;
|
|
2996
3452
|
}
|
|
2997
3453
|
}
|
|
2998
3454
|
const declarations = targetSymbol.declarations ?? [];
|
|
2999
|
-
const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !==
|
|
3455
|
+
const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts11.SyntaxKind.ExportSpecifier) || declarations[0];
|
|
3000
3456
|
return { declaration, targetSymbol };
|
|
3001
3457
|
}
|
|
3002
3458
|
function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportName, ctx) {
|
|
3003
3459
|
let result = null;
|
|
3004
|
-
if (
|
|
3460
|
+
if (ts11.isFunctionDeclaration(declaration)) {
|
|
3005
3461
|
result = serializeFunctionExport(declaration, ctx);
|
|
3006
|
-
} else if (
|
|
3462
|
+
} else if (ts11.isClassDeclaration(declaration)) {
|
|
3007
3463
|
result = serializeClass(declaration, ctx);
|
|
3008
|
-
} else if (
|
|
3464
|
+
} else if (ts11.isInterfaceDeclaration(declaration)) {
|
|
3009
3465
|
result = serializeInterface(declaration, ctx);
|
|
3010
|
-
} else if (
|
|
3466
|
+
} else if (ts11.isTypeAliasDeclaration(declaration)) {
|
|
3011
3467
|
result = serializeTypeAlias(declaration, ctx);
|
|
3012
|
-
} else if (
|
|
3468
|
+
} else if (ts11.isEnumDeclaration(declaration)) {
|
|
3013
3469
|
result = serializeEnum(declaration, ctx);
|
|
3014
|
-
} else if (
|
|
3470
|
+
} else if (ts11.isVariableDeclaration(declaration)) {
|
|
3015
3471
|
const varStatement = declaration.parent?.parent;
|
|
3016
|
-
if (varStatement &&
|
|
3472
|
+
if (varStatement && ts11.isVariableStatement(varStatement)) {
|
|
3017
3473
|
result = serializeVariable(declaration, varStatement, ctx);
|
|
3018
3474
|
}
|
|
3019
|
-
} else if (
|
|
3475
|
+
} else if (ts11.isNamespaceExport(declaration) || ts11.isModuleDeclaration(declaration)) {
|
|
3020
3476
|
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
3021
|
-
} else if (
|
|
3477
|
+
} else if (ts11.isSourceFile(declaration)) {
|
|
3022
3478
|
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
3023
3479
|
}
|
|
3024
3480
|
if (result) {
|
|
@@ -3031,7 +3487,7 @@ function serializeNamespaceExport(symbol, exportName, ctx) {
|
|
|
3031
3487
|
const members = [];
|
|
3032
3488
|
const checker = ctx.program.getTypeChecker();
|
|
3033
3489
|
let targetSymbol = symbol;
|
|
3034
|
-
if (symbol.flags &
|
|
3490
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
3035
3491
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
3036
3492
|
if (aliased && aliased !== symbol) {
|
|
3037
3493
|
targetSymbol = aliased;
|
|
@@ -3058,30 +3514,31 @@ function serializeNamespaceExport(symbol, exportName, ctx) {
|
|
|
3058
3514
|
function serializeNamespaceMember(symbol, memberName, ctx) {
|
|
3059
3515
|
const checker = ctx.program.getTypeChecker();
|
|
3060
3516
|
let targetSymbol = symbol;
|
|
3061
|
-
if (symbol.flags &
|
|
3517
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
3062
3518
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
3063
3519
|
if (aliased && aliased !== symbol) {
|
|
3064
3520
|
targetSymbol = aliased;
|
|
3065
3521
|
}
|
|
3066
3522
|
}
|
|
3067
3523
|
const declarations = targetSymbol.declarations ?? [];
|
|
3068
|
-
const declaration = targetSymbol.valueDeclaration || declarations.find((d) => d.kind !==
|
|
3524
|
+
const declaration = targetSymbol.valueDeclaration || declarations.find((d) => d.kind !== ts11.SyntaxKind.ExportSpecifier) || declarations[0];
|
|
3069
3525
|
if (!declaration)
|
|
3070
3526
|
return null;
|
|
3527
|
+
const type = checker.getTypeAtLocation(declaration);
|
|
3528
|
+
const callSignatures = type.getCallSignatures();
|
|
3529
|
+
const deprecated = isSymbolDeprecated(targetSymbol);
|
|
3071
3530
|
let kind = "variable";
|
|
3072
|
-
if (
|
|
3531
|
+
if (ts11.isFunctionDeclaration(declaration) || ts11.isFunctionExpression(declaration)) {
|
|
3073
3532
|
kind = "function";
|
|
3074
|
-
} else if (
|
|
3533
|
+
} else if (ts11.isClassDeclaration(declaration)) {
|
|
3075
3534
|
kind = "class";
|
|
3076
|
-
} else if (
|
|
3535
|
+
} else if (ts11.isInterfaceDeclaration(declaration)) {
|
|
3077
3536
|
kind = "interface";
|
|
3078
|
-
} else if (
|
|
3537
|
+
} else if (ts11.isTypeAliasDeclaration(declaration)) {
|
|
3079
3538
|
kind = "type";
|
|
3080
|
-
} else if (
|
|
3539
|
+
} else if (ts11.isEnumDeclaration(declaration)) {
|
|
3081
3540
|
kind = "enum";
|
|
3082
|
-
} else if (
|
|
3083
|
-
const type = checker.getTypeAtLocation(declaration);
|
|
3084
|
-
const callSignatures = type.getCallSignatures();
|
|
3541
|
+
} else if (ts11.isVariableDeclaration(declaration)) {
|
|
3085
3542
|
if (callSignatures.length > 0) {
|
|
3086
3543
|
kind = "function";
|
|
3087
3544
|
}
|
|
@@ -3089,10 +3546,38 @@ function serializeNamespaceMember(symbol, memberName, ctx) {
|
|
|
3089
3546
|
const docComment = targetSymbol.getDocumentationComment(checker);
|
|
3090
3547
|
const description = docComment.map((c) => c.text).join(`
|
|
3091
3548
|
`) || undefined;
|
|
3549
|
+
let signatures;
|
|
3550
|
+
if (kind === "function" && callSignatures.length > 0) {
|
|
3551
|
+
signatures = callSignatures.map((sig, index) => {
|
|
3552
|
+
const params = extractParameters(sig, ctx);
|
|
3553
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
3554
|
+
registerReferencedTypes(returnType, ctx);
|
|
3555
|
+
const returnSchema = buildSchema(returnType, ctx.typeChecker, ctx);
|
|
3556
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
3557
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
|
|
3558
|
+
return {
|
|
3559
|
+
parameters: params,
|
|
3560
|
+
returns: { schema: returnSchema },
|
|
3561
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
3562
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
3563
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
3564
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
3565
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
3566
|
+
};
|
|
3567
|
+
});
|
|
3568
|
+
}
|
|
3569
|
+
let schema;
|
|
3570
|
+
if (kind !== "function") {
|
|
3571
|
+
registerReferencedTypes(type, ctx);
|
|
3572
|
+
schema = buildSchema(type, ctx.typeChecker, ctx);
|
|
3573
|
+
}
|
|
3092
3574
|
return {
|
|
3093
3575
|
name: memberName,
|
|
3094
3576
|
kind,
|
|
3095
|
-
...description ? { description } : {}
|
|
3577
|
+
...description ? { description } : {},
|
|
3578
|
+
...signatures ? { signatures } : {},
|
|
3579
|
+
...schema ? { schema } : {},
|
|
3580
|
+
...deprecated ? { flags: { deprecated: true } } : {}
|
|
3096
3581
|
};
|
|
3097
3582
|
}
|
|
3098
3583
|
function getJSDocFromExportSymbol(symbol) {
|
|
@@ -3100,11 +3585,11 @@ function getJSDocFromExportSymbol(symbol) {
|
|
|
3100
3585
|
const examples = [];
|
|
3101
3586
|
const decl = symbol.declarations?.[0];
|
|
3102
3587
|
if (decl) {
|
|
3103
|
-
const exportDecl =
|
|
3104
|
-
if (exportDecl &&
|
|
3105
|
-
const jsDocs =
|
|
3588
|
+
const exportDecl = ts11.isNamespaceExport(decl) ? decl.parent : decl;
|
|
3589
|
+
if (exportDecl && ts11.isExportDeclaration(exportDecl)) {
|
|
3590
|
+
const jsDocs = ts11.getJSDocCommentsAndTags(exportDecl);
|
|
3106
3591
|
for (const doc of jsDocs) {
|
|
3107
|
-
if (
|
|
3592
|
+
if (ts11.isJSDoc(doc) && doc.comment) {
|
|
3108
3593
|
const commentText = typeof doc.comment === "string" ? doc.comment : doc.comment.map((c) => ("text" in c) ? c.text : "").join("");
|
|
3109
3594
|
if (commentText) {
|
|
3110
3595
|
return {
|
|
@@ -3189,4 +3674,4 @@ async function getPackageMeta(entryFile, baseDir) {
|
|
|
3189
3674
|
} catch {}
|
|
3190
3675
|
return { name: path3.basename(searchDir) };
|
|
3191
3676
|
}
|
|
3192
|
-
export { BUILTIN_TYPE_SCHEMAS, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, isTypeReference, getNonNullableType, registerAdapter, findAdapter, isSchemaType, extractSchemaType, arktypeAdapter, typeboxAdapter, valibotAdapter, zodAdapter, serializeVariable,
|
|
3677
|
+
export { BUILTIN_TYPE_SCHEMAS, ARRAY_PROTOTYPE_METHODS, isPrimitiveName, getTypeOrigin, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, isStandardJSONSchema, detectTsRuntime, extractStandardSchemasFromTs, resolveCompiledPath, extractStandardSchemas, extractStandardSchemasFromProject, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, isTypeReference, getNonNullableType, registerAdapter, findAdapter, isSchemaType, extractSchemaType, arktypeAdapter, typeboxAdapter, valibotAdapter, zodAdapter, serializeVariable, normalizeSchema, normalizeExport, normalizeType, normalizeMembers, extract };
|