@openpkg-ts/extract 0.23.2 → 0.24.1
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-2fes3xf8.js} +1471 -947
- 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,160 +1089,762 @@ 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);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
if (type.isIntersection()) {
|
|
1689
|
+
for (const t of type.types) {
|
|
1690
|
+
registerReferencedTypes(t, ctx, depth + 1);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
if (type.flags & ts5.TypeFlags.Object) {
|
|
1694
|
+
const props = type.getProperties();
|
|
1695
|
+
for (const prop of props.slice(0, 20)) {
|
|
1696
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
1697
|
+
registerReferencedTypes(propType, ctx, depth + 1);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// src/serializers/classes.ts
|
|
1703
|
+
import ts7 from "typescript";
|
|
1704
|
+
|
|
1705
|
+
// src/serializers/context.ts
|
|
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 inheritedNames = new Set;
|
|
1726
|
+
const typeToWalk = isStatic ? classType.getSymbol()?.valueDeclaration && checker.getTypeOfSymbolAtLocation(classType.getSymbol(), classType.getSymbol().valueDeclaration) : classType;
|
|
1727
|
+
if (!typeToWalk)
|
|
1728
|
+
return inherited;
|
|
1729
|
+
walkBaseTypes(typeToWalk, ownMemberNames, inherited, inheritedNames, visited, ctx, isStatic);
|
|
1730
|
+
return inherited;
|
|
1731
|
+
}
|
|
1732
|
+
function walkBaseTypes(type, ownMemberNames, inherited, inheritedNames, visited, ctx, isStatic) {
|
|
1733
|
+
if (visited.has(type))
|
|
1734
|
+
return;
|
|
1735
|
+
visited.add(type);
|
|
1736
|
+
const { typeChecker: checker } = ctx;
|
|
1737
|
+
const baseTypes = type.getBaseTypes?.() ?? [];
|
|
1738
|
+
for (const baseType of baseTypes) {
|
|
1739
|
+
const baseSymbol = baseType.getSymbol();
|
|
1740
|
+
const baseName = baseSymbol?.getName() ?? "unknown";
|
|
1741
|
+
const properties = isStatic ? getStaticMembers(baseType, checker) : baseType.getProperties();
|
|
1742
|
+
for (const prop of properties) {
|
|
1743
|
+
const propName = prop.getName();
|
|
1744
|
+
if (ownMemberNames.has(propName))
|
|
1745
|
+
continue;
|
|
1746
|
+
if (inheritedNames.has(propName))
|
|
1747
|
+
continue;
|
|
1748
|
+
if (propName.startsWith("#") || propName.startsWith("__"))
|
|
1749
|
+
continue;
|
|
1750
|
+
const member = serializeInheritedMember(prop, baseName, ctx, isStatic);
|
|
1751
|
+
if (member) {
|
|
1752
|
+
inherited.push(member);
|
|
1753
|
+
inheritedNames.add(propName);
|
|
1754
|
+
}
|
|
994
1755
|
}
|
|
1756
|
+
walkBaseTypes(baseType, ownMemberNames, inherited, inheritedNames, visited, ctx, isStatic);
|
|
995
1757
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1758
|
+
}
|
|
1759
|
+
function getStaticMembers(classType, checker) {
|
|
1760
|
+
const symbol = classType.getSymbol();
|
|
1761
|
+
if (!symbol)
|
|
1762
|
+
return [];
|
|
1763
|
+
const decl = symbol.valueDeclaration;
|
|
1764
|
+
if (!decl)
|
|
1765
|
+
return [];
|
|
1766
|
+
const constructorType = checker.getTypeOfSymbolAtLocation(symbol, decl);
|
|
1767
|
+
return constructorType.getProperties().filter((prop) => {
|
|
1768
|
+
const name = prop.getName();
|
|
1769
|
+
return name !== "prototype" && name !== "constructor" && !name.startsWith("__");
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
function serializeInheritedMember(symbol, inheritedFrom, ctx, isStatic) {
|
|
1773
|
+
const { typeChecker: checker } = ctx;
|
|
1774
|
+
const name = symbol.getName();
|
|
1775
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
1776
|
+
const decl = declarations[0];
|
|
1777
|
+
if (!decl)
|
|
1778
|
+
return null;
|
|
1779
|
+
const type = checker.getTypeOfSymbol(symbol);
|
|
1780
|
+
registerReferencedTypes(type, ctx);
|
|
1781
|
+
let visibility;
|
|
1782
|
+
if (decl && ts6.canHaveModifiers(decl)) {
|
|
1783
|
+
const modifiers = ts6.getModifiers(decl);
|
|
1784
|
+
if (modifiers) {
|
|
1785
|
+
for (const mod of modifiers) {
|
|
1786
|
+
if (mod.kind === ts6.SyntaxKind.PrivateKeyword)
|
|
1787
|
+
visibility = "private";
|
|
1788
|
+
else if (mod.kind === ts6.SyntaxKind.ProtectedKeyword)
|
|
1789
|
+
visibility = "protected";
|
|
1790
|
+
else if (mod.kind === ts6.SyntaxKind.PublicKeyword)
|
|
1791
|
+
visibility = "public";
|
|
1792
|
+
}
|
|
999
1793
|
}
|
|
1000
1794
|
}
|
|
1001
|
-
if (
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1795
|
+
if (visibility === "private")
|
|
1796
|
+
return null;
|
|
1797
|
+
const { description, tags } = getJSDocComment(decl);
|
|
1798
|
+
let kind = "property";
|
|
1799
|
+
const callSigs = type.getCallSignatures();
|
|
1800
|
+
if (callSigs.length > 0) {
|
|
1801
|
+
kind = "method";
|
|
1802
|
+
} else if (ts6.isGetAccessorDeclaration(decl)) {
|
|
1803
|
+
kind = "getter";
|
|
1804
|
+
} else if (ts6.isSetAccessorDeclaration(decl)) {
|
|
1805
|
+
kind = "setter";
|
|
1806
|
+
}
|
|
1807
|
+
const flags = {};
|
|
1808
|
+
if (isStatic)
|
|
1809
|
+
flags.static = true;
|
|
1810
|
+
if (decl && ts6.canHaveModifiers(decl)) {
|
|
1811
|
+
const modifiers = ts6.getModifiers(decl);
|
|
1812
|
+
if (modifiers?.some((m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword)) {
|
|
1813
|
+
flags.readonly = true;
|
|
1006
1814
|
}
|
|
1007
1815
|
}
|
|
1816
|
+
let signatures;
|
|
1817
|
+
if (kind === "method" && callSigs.length > 0) {
|
|
1818
|
+
signatures = callSigs.map((sig, index) => {
|
|
1819
|
+
const params = extractParameters(sig, ctx);
|
|
1820
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1821
|
+
registerReferencedTypes(returnType, ctx);
|
|
1822
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
1823
|
+
return {
|
|
1824
|
+
parameters: params.length > 0 ? params : undefined,
|
|
1825
|
+
returns: {
|
|
1826
|
+
schema: buildSchema(returnType, checker, ctx)
|
|
1827
|
+
},
|
|
1828
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
1829
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
1830
|
+
...callSigs.length > 1 ? { overloadIndex: index } : {}
|
|
1831
|
+
};
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
return {
|
|
1835
|
+
name,
|
|
1836
|
+
kind,
|
|
1837
|
+
inheritedFrom,
|
|
1838
|
+
description,
|
|
1839
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
1840
|
+
visibility,
|
|
1841
|
+
schema: kind !== "method" ? buildSchema(type, checker, ctx) : undefined,
|
|
1842
|
+
signatures,
|
|
1843
|
+
flags: Object.keys(flags).length > 0 ? flags : undefined
|
|
1844
|
+
};
|
|
1008
1845
|
}
|
|
1009
1846
|
|
|
1010
1847
|
// src/serializers/classes.ts
|
|
1011
|
-
import ts6 from "typescript";
|
|
1012
1848
|
function serializeClass(node, ctx) {
|
|
1013
1849
|
const { typeChecker: checker } = ctx;
|
|
1014
1850
|
const symbol = checker.getSymbolAtLocation(node.name ?? node);
|
|
@@ -1027,11 +1863,11 @@ function serializeClass(node, ctx) {
|
|
|
1027
1863
|
const memberName = getMemberName(member);
|
|
1028
1864
|
if (memberName?.startsWith("#"))
|
|
1029
1865
|
continue;
|
|
1030
|
-
if (
|
|
1866
|
+
if (ts7.isPropertyDeclaration(member)) {
|
|
1031
1867
|
const propMember = serializeProperty(member, ctx);
|
|
1032
1868
|
if (propMember)
|
|
1033
1869
|
members.push(propMember);
|
|
1034
|
-
} else if (
|
|
1870
|
+
} else if (ts7.isMethodDeclaration(member)) {
|
|
1035
1871
|
const methodMember = serializeMethod(member, ctx);
|
|
1036
1872
|
if (methodMember?.name) {
|
|
1037
1873
|
if (!methodsByName.has(methodMember.name)) {
|
|
@@ -1046,17 +1882,28 @@ function serializeClass(node, ctx) {
|
|
|
1046
1882
|
}
|
|
1047
1883
|
}
|
|
1048
1884
|
}
|
|
1049
|
-
} else if (
|
|
1885
|
+
} else if (ts7.isConstructorDeclaration(member)) {
|
|
1050
1886
|
const ctorSig = serializeConstructor(member, ctx);
|
|
1051
1887
|
if (ctorSig)
|
|
1052
1888
|
signatures.push(ctorSig);
|
|
1053
|
-
} else if (
|
|
1889
|
+
} else if (ts7.isGetAccessorDeclaration(member) || ts7.isSetAccessorDeclaration(member)) {
|
|
1054
1890
|
const accessorMember = serializeAccessor(member, ctx);
|
|
1055
1891
|
if (accessorMember)
|
|
1056
1892
|
members.push(accessorMember);
|
|
1057
1893
|
}
|
|
1058
1894
|
}
|
|
1059
1895
|
members.push(...methodsByName.values());
|
|
1896
|
+
const ownMemberNames = new Set;
|
|
1897
|
+
for (const member of node.members) {
|
|
1898
|
+
const memberName = getMemberName(member);
|
|
1899
|
+
if (memberName)
|
|
1900
|
+
ownMemberNames.add(memberName);
|
|
1901
|
+
}
|
|
1902
|
+
const classType = checker.getTypeAtLocation(node);
|
|
1903
|
+
const inheritedInstance = getInheritedMembers(classType, ownMemberNames, ctx, false);
|
|
1904
|
+
members.push(...inheritedInstance);
|
|
1905
|
+
const inheritedStatic = getInheritedMembers(classType, ownMemberNames, ctx, true);
|
|
1906
|
+
members.push(...inheritedStatic);
|
|
1060
1907
|
const extendsClause = getExtendsClause(node, checker);
|
|
1061
1908
|
const implementsClause = getImplementsClause(node, checker);
|
|
1062
1909
|
return {
|
|
@@ -1076,37 +1923,37 @@ function serializeClass(node, ctx) {
|
|
|
1076
1923
|
};
|
|
1077
1924
|
}
|
|
1078
1925
|
function getMemberName(member) {
|
|
1079
|
-
if (
|
|
1926
|
+
if (ts7.isConstructorDeclaration(member))
|
|
1080
1927
|
return "constructor";
|
|
1081
1928
|
if (!member.name)
|
|
1082
1929
|
return;
|
|
1083
|
-
if (
|
|
1930
|
+
if (ts7.isIdentifier(member.name))
|
|
1084
1931
|
return member.name.text;
|
|
1085
|
-
if (
|
|
1932
|
+
if (ts7.isPrivateIdentifier(member.name))
|
|
1086
1933
|
return member.name.text;
|
|
1087
1934
|
return member.name.getText();
|
|
1088
1935
|
}
|
|
1089
1936
|
function getVisibility(member) {
|
|
1090
|
-
const modifiers =
|
|
1937
|
+
const modifiers = ts7.canHaveModifiers(member) ? ts7.getModifiers(member) : undefined;
|
|
1091
1938
|
if (!modifiers)
|
|
1092
1939
|
return;
|
|
1093
1940
|
for (const mod of modifiers) {
|
|
1094
|
-
if (mod.kind ===
|
|
1941
|
+
if (mod.kind === ts7.SyntaxKind.PrivateKeyword)
|
|
1095
1942
|
return "private";
|
|
1096
|
-
if (mod.kind ===
|
|
1943
|
+
if (mod.kind === ts7.SyntaxKind.ProtectedKeyword)
|
|
1097
1944
|
return "protected";
|
|
1098
|
-
if (mod.kind ===
|
|
1945
|
+
if (mod.kind === ts7.SyntaxKind.PublicKeyword)
|
|
1099
1946
|
return "public";
|
|
1100
1947
|
}
|
|
1101
1948
|
return;
|
|
1102
1949
|
}
|
|
1103
1950
|
function isStatic(member) {
|
|
1104
|
-
const modifiers =
|
|
1105
|
-
return modifiers?.some((m) => m.kind ===
|
|
1951
|
+
const modifiers = ts7.canHaveModifiers(member) ? ts7.getModifiers(member) : undefined;
|
|
1952
|
+
return modifiers?.some((m) => m.kind === ts7.SyntaxKind.StaticKeyword) ?? false;
|
|
1106
1953
|
}
|
|
1107
1954
|
function isReadonly(member) {
|
|
1108
|
-
const modifiers =
|
|
1109
|
-
return modifiers?.some((m) => m.kind ===
|
|
1955
|
+
const modifiers = ts7.canHaveModifiers(member) ? ts7.getModifiers(member) : undefined;
|
|
1956
|
+
return modifiers?.some((m) => m.kind === ts7.SyntaxKind.ReadonlyKeyword) ?? false;
|
|
1110
1957
|
}
|
|
1111
1958
|
function serializeProperty(node, ctx) {
|
|
1112
1959
|
const { typeChecker: checker } = ctx;
|
|
@@ -1144,15 +1991,22 @@ function serializeMethod(node, ctx) {
|
|
|
1144
1991
|
const visibility = getVisibility(node);
|
|
1145
1992
|
const type = checker.getTypeAtLocation(node);
|
|
1146
1993
|
const callSignatures = type.getCallSignatures();
|
|
1147
|
-
const signatures = callSignatures.map((sig) => {
|
|
1994
|
+
const signatures = callSignatures.map((sig, index) => {
|
|
1148
1995
|
const params = extractParameters(sig, ctx);
|
|
1149
1996
|
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1150
1997
|
registerReferencedTypes(returnType, ctx);
|
|
1998
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
1999
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, checker);
|
|
1151
2000
|
return {
|
|
1152
2001
|
parameters: params.length > 0 ? params : undefined,
|
|
1153
2002
|
returns: {
|
|
1154
2003
|
schema: buildSchema(returnType, checker, ctx)
|
|
1155
|
-
}
|
|
2004
|
+
},
|
|
2005
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
2006
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
2007
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
2008
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
2009
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
1156
2010
|
};
|
|
1157
2011
|
});
|
|
1158
2012
|
const flags = {};
|
|
@@ -1160,8 +2014,8 @@ function serializeMethod(node, ctx) {
|
|
|
1160
2014
|
flags.static = true;
|
|
1161
2015
|
if (node.asteriskToken)
|
|
1162
2016
|
flags.generator = true;
|
|
1163
|
-
const modifiers =
|
|
1164
|
-
if (modifiers?.some((m) => m.kind ===
|
|
2017
|
+
const modifiers = ts7.getModifiers(node);
|
|
2018
|
+
if (modifiers?.some((m) => m.kind === ts7.SyntaxKind.AsyncKeyword)) {
|
|
1165
2019
|
flags.async = true;
|
|
1166
2020
|
}
|
|
1167
2021
|
return {
|
|
@@ -1196,7 +2050,7 @@ function serializeAccessor(node, ctx) {
|
|
|
1196
2050
|
const type = checker.getTypeAtLocation(node);
|
|
1197
2051
|
const schema = buildSchema(type, checker, ctx);
|
|
1198
2052
|
registerReferencedTypes(type, ctx);
|
|
1199
|
-
const kind =
|
|
2053
|
+
const kind = ts7.isGetAccessorDeclaration(node) ? "getter" : "setter";
|
|
1200
2054
|
const flags = {};
|
|
1201
2055
|
if (isStatic(node))
|
|
1202
2056
|
flags.static = true;
|
|
@@ -1214,7 +2068,7 @@ function getExtendsClause(node, checker) {
|
|
|
1214
2068
|
if (!node.heritageClauses)
|
|
1215
2069
|
return;
|
|
1216
2070
|
for (const clause of node.heritageClauses) {
|
|
1217
|
-
if (clause.token ===
|
|
2071
|
+
if (clause.token === ts7.SyntaxKind.ExtendsKeyword) {
|
|
1218
2072
|
const expr = clause.types[0];
|
|
1219
2073
|
if (expr) {
|
|
1220
2074
|
const type = checker.getTypeAtLocation(expr);
|
|
@@ -1229,7 +2083,7 @@ function getImplementsClause(node, checker) {
|
|
|
1229
2083
|
if (!node.heritageClauses)
|
|
1230
2084
|
return;
|
|
1231
2085
|
for (const clause of node.heritageClauses) {
|
|
1232
|
-
if (clause.token ===
|
|
2086
|
+
if (clause.token === ts7.SyntaxKind.ImplementsKeyword) {
|
|
1233
2087
|
return clause.types.map((expr) => {
|
|
1234
2088
|
const type = checker.getTypeAtLocation(expr);
|
|
1235
2089
|
const symbol = type.getSymbol();
|
|
@@ -1286,6 +2140,35 @@ function serializeEnum(node, ctx) {
|
|
|
1286
2140
|
}
|
|
1287
2141
|
|
|
1288
2142
|
// src/serializers/functions.ts
|
|
2143
|
+
import ts8 from "typescript";
|
|
2144
|
+
function buildReturnSchema(sig, ctx) {
|
|
2145
|
+
const returnType = ctx.typeChecker.getReturnTypeOfSignature(sig);
|
|
2146
|
+
registerReferencedTypes(returnType, ctx);
|
|
2147
|
+
const schema = buildSchema(returnType, ctx.typeChecker, ctx);
|
|
2148
|
+
const declaration = sig.getDeclaration();
|
|
2149
|
+
if (declaration && ts8.isFunctionLike(declaration) && declaration.type) {
|
|
2150
|
+
const returnTypeNode = declaration.type;
|
|
2151
|
+
if (ts8.isTypePredicateNode(returnTypeNode)) {
|
|
2152
|
+
const parameterName = ts8.isIdentifier(returnTypeNode.parameterName) ? returnTypeNode.parameterName.text : returnTypeNode.parameterName.getText();
|
|
2153
|
+
let predicateTypeSchema = { type: "unknown" };
|
|
2154
|
+
if (returnTypeNode.type) {
|
|
2155
|
+
const predicateType = ctx.typeChecker.getTypeAtLocation(returnTypeNode.type);
|
|
2156
|
+
predicateTypeSchema = buildSchema(predicateType, ctx.typeChecker, ctx);
|
|
2157
|
+
registerReferencedTypes(predicateType, ctx);
|
|
2158
|
+
}
|
|
2159
|
+
const baseSchema = typeof schema === "string" ? { type: schema } : schema;
|
|
2160
|
+
const schemaWithPredicate = {
|
|
2161
|
+
...baseSchema,
|
|
2162
|
+
"x-ts-type-predicate": {
|
|
2163
|
+
parameterName,
|
|
2164
|
+
type: predicateTypeSchema
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
return { schema: schemaWithPredicate };
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return { schema };
|
|
2171
|
+
}
|
|
1289
2172
|
function serializeFunctionExport(node, ctx) {
|
|
1290
2173
|
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
1291
2174
|
const name = symbol?.getName() ?? node.name?.getText();
|
|
@@ -1298,15 +2181,18 @@ function serializeFunctionExport(node, ctx) {
|
|
|
1298
2181
|
const typeParameters = extractTypeParameters(node, ctx.typeChecker);
|
|
1299
2182
|
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
1300
2183
|
const callSignatures = type.getCallSignatures();
|
|
1301
|
-
const signatures = callSignatures.map((sig) => {
|
|
2184
|
+
const signatures = callSignatures.map((sig, index) => {
|
|
1302
2185
|
const params = extractParameters(sig, ctx);
|
|
1303
|
-
const
|
|
1304
|
-
|
|
2186
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
2187
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
|
|
1305
2188
|
return {
|
|
1306
2189
|
parameters: params,
|
|
1307
|
-
returns:
|
|
1308
|
-
|
|
1309
|
-
}
|
|
2190
|
+
returns: buildReturnSchema(sig, ctx),
|
|
2191
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
2192
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
2193
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
2194
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
2195
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
1310
2196
|
};
|
|
1311
2197
|
});
|
|
1312
2198
|
return {
|
|
@@ -1324,7 +2210,7 @@ function serializeFunctionExport(node, ctx) {
|
|
|
1324
2210
|
}
|
|
1325
2211
|
|
|
1326
2212
|
// src/serializers/interfaces.ts
|
|
1327
|
-
import
|
|
2213
|
+
import ts9 from "typescript";
|
|
1328
2214
|
function serializeInterface(node, ctx) {
|
|
1329
2215
|
const { typeChecker: checker } = ctx;
|
|
1330
2216
|
const symbol = checker.getSymbolAtLocation(node.name ?? node);
|
|
@@ -1339,22 +2225,22 @@ function serializeInterface(node, ctx) {
|
|
|
1339
2225
|
const members = [];
|
|
1340
2226
|
const methodsByName = new Map;
|
|
1341
2227
|
for (const member of node.members) {
|
|
1342
|
-
if (
|
|
2228
|
+
if (ts9.isPropertySignature(member)) {
|
|
1343
2229
|
const propMember = serializePropertySignature(member, ctx);
|
|
1344
2230
|
if (propMember)
|
|
1345
2231
|
members.push(propMember);
|
|
1346
|
-
} else if (
|
|
2232
|
+
} else if (ts9.isMethodSignature(member)) {
|
|
1347
2233
|
const methodMember = serializeMethodSignature(member, ctx);
|
|
1348
2234
|
if (methodMember?.name) {
|
|
1349
2235
|
if (!methodsByName.has(methodMember.name)) {
|
|
1350
2236
|
methodsByName.set(methodMember.name, methodMember);
|
|
1351
2237
|
}
|
|
1352
2238
|
}
|
|
1353
|
-
} else if (
|
|
2239
|
+
} else if (ts9.isCallSignatureDeclaration(member)) {
|
|
1354
2240
|
const callMember = serializeCallSignature(member, ctx);
|
|
1355
2241
|
if (callMember)
|
|
1356
2242
|
members.push(callMember);
|
|
1357
|
-
} else if (
|
|
2243
|
+
} else if (ts9.isIndexSignatureDeclaration(member)) {
|
|
1358
2244
|
const indexMember = serializeIndexSignature(member, ctx);
|
|
1359
2245
|
if (indexMember)
|
|
1360
2246
|
members.push(indexMember);
|
|
@@ -1386,7 +2272,7 @@ function serializePropertySignature(node, ctx) {
|
|
|
1386
2272
|
const flags = {};
|
|
1387
2273
|
if (node.questionToken)
|
|
1388
2274
|
flags.optional = true;
|
|
1389
|
-
if (node.modifiers?.some((m) => m.kind ===
|
|
2275
|
+
if (node.modifiers?.some((m) => m.kind === ts9.SyntaxKind.ReadonlyKeyword)) {
|
|
1390
2276
|
flags.readonly = true;
|
|
1391
2277
|
}
|
|
1392
2278
|
return {
|
|
@@ -1404,15 +2290,22 @@ function serializeMethodSignature(node, ctx) {
|
|
|
1404
2290
|
const { description, tags } = getJSDocComment(node);
|
|
1405
2291
|
const type = checker.getTypeAtLocation(node);
|
|
1406
2292
|
const callSignatures = type.getCallSignatures();
|
|
1407
|
-
const signatures = callSignatures.map((sig) => {
|
|
2293
|
+
const signatures = callSignatures.map((sig, index) => {
|
|
1408
2294
|
const params = extractParameters(sig, ctx);
|
|
1409
2295
|
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1410
2296
|
registerReferencedTypes(returnType, ctx);
|
|
2297
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
2298
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, checker);
|
|
1411
2299
|
return {
|
|
1412
2300
|
parameters: params.length > 0 ? params : undefined,
|
|
1413
2301
|
returns: {
|
|
1414
2302
|
schema: buildSchema(returnType, checker, ctx)
|
|
1415
|
-
}
|
|
2303
|
+
},
|
|
2304
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
2305
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
2306
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
2307
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
2308
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
1416
2309
|
};
|
|
1417
2310
|
});
|
|
1418
2311
|
const flags = {};
|
|
@@ -1436,735 +2329,299 @@ function serializeCallSignature(node, ctx) {
|
|
|
1436
2329
|
const params = extractParameters(sig, ctx);
|
|
1437
2330
|
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
1438
2331
|
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);
|
|
1463
|
-
return {
|
|
1464
|
-
name: `[${keyTypeName}]`,
|
|
1465
|
-
kind: "index-signature",
|
|
1466
|
-
description,
|
|
1467
|
-
tags: tags.length > 0 ? tags : undefined,
|
|
1468
|
-
schema: {
|
|
1469
|
-
type: "object",
|
|
1470
|
-
additionalProperties: valueSchema
|
|
1471
|
-
}
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
function getInterfaceExtends(node, checker) {
|
|
1475
|
-
if (!node.heritageClauses)
|
|
1476
|
-
return;
|
|
1477
|
-
for (const clause of node.heritageClauses) {
|
|
1478
|
-
if (clause.token === ts7.SyntaxKind.ExtendsKeyword && clause.types.length > 0) {
|
|
1479
|
-
const names = clause.types.map((expr) => {
|
|
1480
|
-
const type = checker.getTypeAtLocation(expr);
|
|
1481
|
-
return type.getSymbol()?.getName() ?? expr.expression.getText();
|
|
1482
|
-
});
|
|
1483
|
-
return names.join(" & ");
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
return;
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// src/serializers/type-aliases.ts
|
|
1490
|
-
function serializeTypeAlias(node, ctx) {
|
|
1491
|
-
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
1492
|
-
const name = symbol?.getName() ?? node.name?.getText();
|
|
1493
|
-
if (!name)
|
|
1494
|
-
return null;
|
|
1495
|
-
const deprecated = isSymbolDeprecated(symbol);
|
|
1496
|
-
const declSourceFile = node.getSourceFile();
|
|
1497
|
-
const { description, tags, examples } = getJSDocComment(node);
|
|
1498
|
-
const source = getSourceLocation(node, declSourceFile);
|
|
1499
|
-
const typeParameters = extractTypeParameters(node, ctx.typeChecker);
|
|
1500
|
-
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
1501
|
-
registerReferencedTypes(type, ctx);
|
|
1502
|
-
const schema = buildSchema(type, ctx.typeChecker, ctx);
|
|
1503
|
-
return {
|
|
1504
|
-
id: name,
|
|
1505
|
-
name,
|
|
1506
|
-
kind: "type",
|
|
1507
|
-
description,
|
|
1508
|
-
tags,
|
|
1509
|
-
source,
|
|
1510
|
-
typeParameters,
|
|
1511
|
-
schema,
|
|
1512
|
-
...deprecated ? { deprecated: true } : {},
|
|
1513
|
-
...examples.length > 0 ? { examples } : {}
|
|
1514
|
-
};
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
// src/schema/registry.ts
|
|
1518
|
-
function isTypeReference(type) {
|
|
1519
|
-
return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
|
|
1520
|
-
}
|
|
1521
|
-
function getNonNullableType(type) {
|
|
1522
|
-
if (type.isUnion()) {
|
|
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;
|
|
2332
|
+
return {
|
|
2333
|
+
name: "()",
|
|
2334
|
+
kind: "call-signature",
|
|
2335
|
+
description,
|
|
2336
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
2337
|
+
signatures: [
|
|
2338
|
+
{
|
|
2339
|
+
parameters: params.length > 0 ? params : undefined,
|
|
2340
|
+
returns: {
|
|
2341
|
+
schema: buildSchema(returnType, checker, ctx)
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
]
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
function serializeIndexSignature(node, ctx) {
|
|
2348
|
+
const { typeChecker: checker } = ctx;
|
|
2349
|
+
const { description, tags } = getJSDocComment(node);
|
|
2350
|
+
const valueType = node.type ? checker.getTypeAtLocation(node.type) : checker.getAnyType();
|
|
2351
|
+
const valueSchema = buildSchema(valueType, checker, ctx);
|
|
2352
|
+
registerReferencedTypes(valueType, ctx);
|
|
2353
|
+
const keyParam = node.parameters[0];
|
|
2354
|
+
const keyType = keyParam?.type ? checker.getTypeAtLocation(keyParam.type) : checker.getStringType();
|
|
2355
|
+
const keyTypeName = checker.typeToString(keyType);
|
|
2356
|
+
return {
|
|
2357
|
+
name: `[${keyTypeName}]`,
|
|
2358
|
+
kind: "index-signature",
|
|
2359
|
+
description,
|
|
2360
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
2361
|
+
schema: {
|
|
2362
|
+
type: "object",
|
|
2363
|
+
additionalProperties: valueSchema
|
|
1634
2364
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
function getInterfaceExtends(node, checker) {
|
|
2368
|
+
if (!node.heritageClauses)
|
|
2369
|
+
return;
|
|
2370
|
+
for (const clause of node.heritageClauses) {
|
|
2371
|
+
if (clause.token === ts9.SyntaxKind.ExtendsKeyword && clause.types.length > 0) {
|
|
2372
|
+
const names = clause.types.map((expr) => {
|
|
2373
|
+
const type = checker.getTypeAtLocation(expr);
|
|
2374
|
+
return type.getSymbol()?.getName() ?? expr.expression.getText();
|
|
2375
|
+
});
|
|
2376
|
+
return names.join(" & ");
|
|
1640
2377
|
}
|
|
1641
|
-
return checker.getTypeOfSymbol(inputSymbol);
|
|
1642
2378
|
}
|
|
1643
|
-
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
1644
2381
|
|
|
1645
|
-
// src/
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
const
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
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;
|
|
2382
|
+
// src/serializers/type-aliases.ts
|
|
2383
|
+
import ts10 from "typescript";
|
|
2384
|
+
function buildIntersectionSchemaFromNode(node, ctx) {
|
|
2385
|
+
const types = node.types;
|
|
2386
|
+
const schemas = [];
|
|
2387
|
+
for (const typeNode of types) {
|
|
2388
|
+
const type = ctx.typeChecker.getTypeAtLocation(typeNode);
|
|
2389
|
+
registerReferencedTypes(type, ctx);
|
|
2390
|
+
schemas.push(buildSchema(type, ctx.typeChecker, ctx));
|
|
2391
|
+
}
|
|
2392
|
+
if (schemas.length === 0) {
|
|
2393
|
+
return { type: "never" };
|
|
1671
2394
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
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();
|
|
2395
|
+
if (schemas.length === 1) {
|
|
2396
|
+
return schemas[0];
|
|
2397
|
+
}
|
|
2398
|
+
return { allOf: schemas };
|
|
2399
|
+
}
|
|
2400
|
+
function serializeTypeAlias(node, ctx) {
|
|
2401
|
+
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
|
|
2402
|
+
const name = symbol?.getName() ?? node.name?.getText();
|
|
1684
2403
|
if (!name)
|
|
1685
2404
|
return null;
|
|
1686
2405
|
const deprecated = isSymbolDeprecated(symbol);
|
|
1687
2406
|
const declSourceFile = node.getSourceFile();
|
|
1688
|
-
const { description, tags, examples } = getJSDocComment(
|
|
2407
|
+
const { description, tags, examples } = getJSDocComment(node);
|
|
1689
2408
|
const source = getSourceLocation(node, declSourceFile);
|
|
2409
|
+
const typeParameters = extractTypeParameters(node, ctx.typeChecker);
|
|
1690
2410
|
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
} : undefined;
|
|
2411
|
+
registerReferencedTypes(type, ctx);
|
|
2412
|
+
let schema;
|
|
2413
|
+
if (ts10.isIntersectionTypeNode(node.type)) {
|
|
2414
|
+
schema = buildIntersectionSchemaFromNode(node.type, ctx);
|
|
2415
|
+
} else {
|
|
2416
|
+
schema = buildSchema(type, ctx.typeChecker, ctx);
|
|
2417
|
+
}
|
|
1699
2418
|
return {
|
|
1700
2419
|
id: name,
|
|
1701
2420
|
name,
|
|
1702
|
-
kind: "
|
|
2421
|
+
kind: "type",
|
|
1703
2422
|
description,
|
|
1704
2423
|
tags,
|
|
1705
2424
|
source,
|
|
2425
|
+
typeParameters,
|
|
1706
2426
|
schema,
|
|
1707
|
-
...flags ? { flags } : {},
|
|
1708
2427
|
...deprecated ? { deprecated: true } : {},
|
|
1709
2428
|
...examples.length > 0 ? { examples } : {}
|
|
1710
2429
|
};
|
|
1711
2430
|
}
|
|
1712
2431
|
|
|
1713
|
-
// src/schema/
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
if (typeof std !== "object" || std === null)
|
|
1723
|
-
return false;
|
|
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;
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
function detectTsRuntime() {
|
|
1747
|
-
if (cachedRuntime !== undefined) {
|
|
1748
|
-
return cachedRuntime;
|
|
1749
|
-
}
|
|
1750
|
-
const nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
|
|
1751
|
-
if (nodeVersion >= 22) {
|
|
1752
|
-
cachedRuntime = {
|
|
1753
|
-
cmd: "node",
|
|
1754
|
-
args: ["--experimental-strip-types", "--no-warnings"],
|
|
1755
|
-
name: "node (native)"
|
|
1756
|
-
};
|
|
1757
|
-
return cachedRuntime;
|
|
1758
|
-
}
|
|
1759
|
-
if (commandExists("bun")) {
|
|
1760
|
-
cachedRuntime = {
|
|
1761
|
-
cmd: "bun",
|
|
1762
|
-
args: ["run"],
|
|
1763
|
-
name: "bun"
|
|
1764
|
-
};
|
|
1765
|
-
return cachedRuntime;
|
|
1766
|
-
}
|
|
1767
|
-
if (commandExists("tsx")) {
|
|
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"
|
|
1780
|
-
};
|
|
1781
|
-
return cachedRuntime;
|
|
1782
|
-
}
|
|
1783
|
-
cachedRuntime = null;
|
|
1784
|
-
return null;
|
|
1785
|
-
}
|
|
1786
|
-
var WORKER_SCRIPT = `
|
|
1787
|
-
const path = require('path');
|
|
1788
|
-
const { pathToFileURL } = require('url');
|
|
1789
|
-
|
|
1790
|
-
// TypeBox detection: schemas have Symbol.for('TypeBox.Kind') and are JSON Schema
|
|
1791
|
-
const TYPEBOX_KIND = Symbol.for('TypeBox.Kind');
|
|
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));
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
async function extract() {
|
|
1807
|
-
// With node -e, argv is: [node, arg1, arg2, ...]
|
|
1808
|
-
// (the -e script is NOT in argv)
|
|
1809
|
-
const [modulePath, optionsJson] = process.argv.slice(1);
|
|
1810
|
-
const { target, libraryOptions } = JSON.parse(optionsJson || '{}');
|
|
1811
|
-
|
|
1812
|
-
try {
|
|
1813
|
-
// Import the module using dynamic import (works with ESM and CJS)
|
|
1814
|
-
const absPath = path.resolve(modulePath);
|
|
1815
|
-
const mod = await import(pathToFileURL(absPath).href);
|
|
1816
|
-
const results = [];
|
|
1817
|
-
|
|
1818
|
-
// Build exports map - handle both ESM and CJS (where exports are in mod.default)
|
|
1819
|
-
const exports = {};
|
|
1820
|
-
for (const [name, value] of Object.entries(mod)) {
|
|
1821
|
-
if (name === 'default' && typeof value === 'object' && value !== null) {
|
|
1822
|
-
// CJS module: spread default exports
|
|
1823
|
-
Object.assign(exports, value);
|
|
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
|
|
1864
|
-
}
|
|
1865
|
-
continue;
|
|
1866
|
-
}
|
|
2432
|
+
// src/schema/registry.ts
|
|
2433
|
+
function isTypeReference(type) {
|
|
2434
|
+
return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
|
|
2435
|
+
}
|
|
2436
|
+
function getNonNullableType(type) {
|
|
2437
|
+
if (type.isUnion()) {
|
|
2438
|
+
const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
|
|
2439
|
+
if (nonNullable.length === 1) {
|
|
2440
|
+
return nonNullable[0];
|
|
1867
2441
|
}
|
|
1868
|
-
|
|
1869
|
-
console.log(JSON.stringify({ success: true, results }));
|
|
1870
|
-
} catch (e) {
|
|
1871
|
-
console.log(JSON.stringify({ success: false, error: e.message }));
|
|
1872
2442
|
}
|
|
2443
|
+
return type;
|
|
1873
2444
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
var TS_WORKER_SCRIPT = `
|
|
1878
|
-
import * as path from 'path';
|
|
1879
|
-
import { pathToFileURL } from 'url';
|
|
1880
|
-
|
|
1881
|
-
// TypeBox detection
|
|
1882
|
-
const TYPEBOX_KIND = Symbol.for('TypeBox.Kind');
|
|
1883
|
-
|
|
1884
|
-
function isTypeBoxSchema(obj: unknown): boolean {
|
|
1885
|
-
if (!obj || typeof obj !== 'object') return false;
|
|
1886
|
-
const o = obj as Record<string | symbol, unknown>;
|
|
1887
|
-
if (!o[TYPEBOX_KIND]) return false;
|
|
1888
|
-
return typeof o.type === 'string' || 'anyOf' in o || 'oneOf' in o || 'allOf' in o;
|
|
2445
|
+
var adapters = [];
|
|
2446
|
+
function registerAdapter(adapter) {
|
|
2447
|
+
adapters.push(adapter);
|
|
1889
2448
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
2449
|
+
function findAdapter(type, checker) {
|
|
2450
|
+
return adapters.find((a) => a.matches(type, checker));
|
|
2451
|
+
}
|
|
2452
|
+
function isSchemaType(type, checker) {
|
|
2453
|
+
return adapters.some((a) => a.matches(type, checker));
|
|
2454
|
+
}
|
|
2455
|
+
function extractSchemaType(type, checker) {
|
|
2456
|
+
const adapter = findAdapter(type, checker);
|
|
2457
|
+
if (!adapter)
|
|
2458
|
+
return null;
|
|
2459
|
+
const outputType = adapter.extractOutputType(type, checker);
|
|
2460
|
+
if (!outputType)
|
|
2461
|
+
return null;
|
|
2462
|
+
const inputType = adapter.extractInputType?.(type, checker) ?? undefined;
|
|
2463
|
+
return {
|
|
2464
|
+
adapter,
|
|
2465
|
+
outputType,
|
|
2466
|
+
inputType
|
|
2467
|
+
};
|
|
1893
2468
|
}
|
|
1894
2469
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
if (name === 'default' && typeof value === 'object' && value !== null) {
|
|
1908
|
-
Object.assign(exports, value);
|
|
1909
|
-
} else if (name !== 'default') {
|
|
1910
|
-
exports[name] = value;
|
|
1911
|
-
}
|
|
2470
|
+
// src/schema/adapters/arktype.ts
|
|
2471
|
+
var ARKTYPE_TYPE_PATTERN = /^Type</;
|
|
2472
|
+
var arktypeAdapter = {
|
|
2473
|
+
id: "arktype",
|
|
2474
|
+
packages: ["arktype"],
|
|
2475
|
+
matches(type, checker) {
|
|
2476
|
+
const typeName = checker.typeToString(type);
|
|
2477
|
+
return ARKTYPE_TYPE_PATTERN.test(typeName);
|
|
2478
|
+
},
|
|
2479
|
+
extractOutputType(type, checker) {
|
|
2480
|
+
if (!isTypeReference(type)) {
|
|
2481
|
+
return null;
|
|
1912
2482
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
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
|
-
}
|
|
2483
|
+
const args = checker.getTypeArguments(type);
|
|
2484
|
+
if (args.length < 1) {
|
|
2485
|
+
return null;
|
|
1942
2486
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2487
|
+
return args[0];
|
|
2488
|
+
},
|
|
2489
|
+
extractInputType(type, checker) {
|
|
2490
|
+
if (!isTypeReference(type)) {
|
|
2491
|
+
return null;
|
|
2492
|
+
}
|
|
2493
|
+
const args = checker.getTypeArguments(type);
|
|
2494
|
+
if (args.length < 2) {
|
|
2495
|
+
return null;
|
|
2496
|
+
}
|
|
2497
|
+
return args[1];
|
|
1947
2498
|
}
|
|
1948
|
-
}
|
|
2499
|
+
};
|
|
1949
2500
|
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
return
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
try {
|
|
1970
|
-
fs.writeFileSync(workerPath, TS_WORKER_SCRIPT);
|
|
1971
|
-
const optionsJson = JSON.stringify({ target, libraryOptions });
|
|
1972
|
-
const args = [...runtime.args, workerPath, tsFilePath, optionsJson];
|
|
1973
|
-
return await new Promise((resolve) => {
|
|
1974
|
-
const child = spawn(runtime.cmd, args, {
|
|
1975
|
-
timeout,
|
|
1976
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1977
|
-
cwd: path2.dirname(tsFilePath)
|
|
1978
|
-
});
|
|
1979
|
-
let stdout = "";
|
|
1980
|
-
let stderr = "";
|
|
1981
|
-
child.stdout.on("data", (data) => {
|
|
1982
|
-
stdout += data.toString();
|
|
1983
|
-
});
|
|
1984
|
-
child.stderr.on("data", (data) => {
|
|
1985
|
-
stderr += data.toString();
|
|
1986
|
-
});
|
|
1987
|
-
child.on("close", (code) => {
|
|
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;
|
|
2501
|
+
// src/schema/adapters/typebox.ts
|
|
2502
|
+
var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
|
|
2503
|
+
var typeboxAdapter = {
|
|
2504
|
+
id: "typebox",
|
|
2505
|
+
packages: ["@sinclair/typebox"],
|
|
2506
|
+
matches(type, checker) {
|
|
2507
|
+
const typeName = checker.typeToString(type);
|
|
2508
|
+
if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
|
|
2509
|
+
return false;
|
|
2510
|
+
}
|
|
2511
|
+
const typeProperty = type.getProperty("type");
|
|
2512
|
+
return typeProperty !== undefined;
|
|
2513
|
+
},
|
|
2514
|
+
extractOutputType(type, checker) {
|
|
2515
|
+
const staticSymbol = type.getProperty("static");
|
|
2516
|
+
if (staticSymbol) {
|
|
2517
|
+
return checker.getTypeOfSymbol(staticSymbol);
|
|
2518
|
+
}
|
|
2519
|
+
return null;
|
|
2030
2520
|
}
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2521
|
+
};
|
|
2522
|
+
|
|
2523
|
+
// src/schema/adapters/valibot.ts
|
|
2524
|
+
var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
|
|
2525
|
+
var valibotAdapter = {
|
|
2526
|
+
id: "valibot",
|
|
2527
|
+
packages: ["valibot"],
|
|
2528
|
+
matches(type, checker) {
|
|
2529
|
+
const typeName = checker.typeToString(type);
|
|
2530
|
+
return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
|
|
2531
|
+
},
|
|
2532
|
+
extractOutputType(type, checker) {
|
|
2533
|
+
const typesSymbol = type.getProperty("~types");
|
|
2534
|
+
if (!typesSymbol) {
|
|
2036
2535
|
return null;
|
|
2037
2536
|
}
|
|
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}`));
|
|
2537
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
2538
|
+
typesType = getNonNullableType(typesType);
|
|
2539
|
+
const outputSymbol = typesType.getProperty("output");
|
|
2540
|
+
if (!outputSymbol) {
|
|
2541
|
+
return null;
|
|
2057
2542
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
candidates.push(path2.join(baseDir, outDir, `${srcPrefix}${ext}`));
|
|
2543
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
2544
|
+
},
|
|
2545
|
+
extractInputType(type, checker) {
|
|
2546
|
+
const typesSymbol = type.getProperty("~types");
|
|
2547
|
+
if (!typesSymbol) {
|
|
2548
|
+
return null;
|
|
2065
2549
|
}
|
|
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}`));
|
|
2550
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
2551
|
+
typesType = getNonNullableType(typesType);
|
|
2552
|
+
const inputSymbol = typesType.getProperty("input");
|
|
2553
|
+
if (!inputSymbol) {
|
|
2554
|
+
return null;
|
|
2075
2555
|
}
|
|
2556
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
2076
2557
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2558
|
+
};
|
|
2559
|
+
|
|
2560
|
+
// src/schema/adapters/zod.ts
|
|
2561
|
+
var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
|
|
2562
|
+
var zodAdapter = {
|
|
2563
|
+
id: "zod",
|
|
2564
|
+
packages: ["zod"],
|
|
2565
|
+
matches(type, checker) {
|
|
2566
|
+
const typeName = checker.typeToString(type);
|
|
2567
|
+
return ZOD_TYPE_PATTERN.test(typeName);
|
|
2568
|
+
},
|
|
2569
|
+
extractOutputType(type, checker) {
|
|
2570
|
+
const outputSymbol = type.getProperty("_output");
|
|
2571
|
+
if (outputSymbol) {
|
|
2572
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
2080
2573
|
}
|
|
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
|
-
};
|
|
2574
|
+
const typeSymbol = type.getProperty("_type");
|
|
2575
|
+
if (typeSymbol) {
|
|
2576
|
+
return checker.getTypeOfSymbol(typeSymbol);
|
|
2151
2577
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
return
|
|
2158
|
-
...result,
|
|
2159
|
-
info: { method: "direct-ts", runtime: runtime2.name, path: entryFile }
|
|
2160
|
-
};
|
|
2578
|
+
return null;
|
|
2579
|
+
},
|
|
2580
|
+
extractInputType(type, checker) {
|
|
2581
|
+
const inputSymbol = type.getProperty("_input");
|
|
2582
|
+
if (inputSymbol) {
|
|
2583
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
2161
2584
|
}
|
|
2585
|
+
return null;
|
|
2162
2586
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2587
|
+
};
|
|
2588
|
+
|
|
2589
|
+
// src/schema/adapters/index.ts
|
|
2590
|
+
registerAdapter(zodAdapter);
|
|
2591
|
+
registerAdapter(valibotAdapter);
|
|
2592
|
+
registerAdapter(arktypeAdapter);
|
|
2593
|
+
registerAdapter(typeboxAdapter);
|
|
2594
|
+
|
|
2595
|
+
// src/serializers/variables.ts
|
|
2596
|
+
function serializeVariable(node, statement, ctx) {
|
|
2597
|
+
const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
|
|
2598
|
+
const name = symbol?.getName() ?? node.name.getText();
|
|
2599
|
+
if (!name)
|
|
2600
|
+
return null;
|
|
2601
|
+
const deprecated = isSymbolDeprecated(symbol);
|
|
2602
|
+
const declSourceFile = node.getSourceFile();
|
|
2603
|
+
const { description, tags, examples } = getJSDocComment(statement);
|
|
2604
|
+
const source = getSourceLocation(node, declSourceFile);
|
|
2605
|
+
const type = ctx.typeChecker.getTypeAtLocation(node);
|
|
2606
|
+
const schemaExtraction = extractSchemaType(type, ctx.typeChecker);
|
|
2607
|
+
const typeToSerialize = schemaExtraction?.outputType ?? type;
|
|
2608
|
+
registerReferencedTypes(typeToSerialize, ctx);
|
|
2609
|
+
const schema = buildSchema(typeToSerialize, ctx.typeChecker, ctx);
|
|
2610
|
+
const flags = schemaExtraction ? {
|
|
2611
|
+
schemaLibrary: schemaExtraction.adapter.id,
|
|
2612
|
+
...schemaExtraction.inputType && schemaExtraction.inputType !== schemaExtraction.outputType ? { hasTransform: true } : {}
|
|
2613
|
+
} : undefined;
|
|
2165
2614
|
return {
|
|
2166
|
-
|
|
2167
|
-
|
|
2615
|
+
id: name,
|
|
2616
|
+
name,
|
|
2617
|
+
kind: "variable",
|
|
2618
|
+
description,
|
|
2619
|
+
tags,
|
|
2620
|
+
source,
|
|
2621
|
+
schema,
|
|
2622
|
+
...flags ? { flags } : {},
|
|
2623
|
+
...deprecated ? { deprecated: true } : {},
|
|
2624
|
+
...examples.length > 0 ? { examples } : {}
|
|
2168
2625
|
};
|
|
2169
2626
|
}
|
|
2170
2627
|
|
|
@@ -2381,6 +2838,18 @@ function normalizeStandardType(schema, options) {
|
|
|
2381
2838
|
result[keyword] = schema[keyword];
|
|
2382
2839
|
}
|
|
2383
2840
|
}
|
|
2841
|
+
for (const key of Object.keys(schema)) {
|
|
2842
|
+
if (key.startsWith("x-ts-") && schema[key] !== undefined) {
|
|
2843
|
+
const value = schema[key];
|
|
2844
|
+
if (isSchemaLike(value)) {
|
|
2845
|
+
result[key] = normalizeSchemaInternal(value, options);
|
|
2846
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
2847
|
+
result[key] = normalizeGenericObject(value, options);
|
|
2848
|
+
} else {
|
|
2849
|
+
result[key] = value;
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2384
2853
|
return result;
|
|
2385
2854
|
}
|
|
2386
2855
|
function normalizeRef(schema, options) {
|
|
@@ -2388,6 +2857,11 @@ function normalizeRef(schema, options) {
|
|
|
2388
2857
|
if (schema.typeArguments && schema.typeArguments.length > 0) {
|
|
2389
2858
|
result["x-ts-type-arguments"] = schema.typeArguments.map((arg) => normalizeSchemaInternal(arg, options));
|
|
2390
2859
|
}
|
|
2860
|
+
for (const key of Object.keys(schema)) {
|
|
2861
|
+
if (key.startsWith("x-ts-") && schema[key] !== undefined) {
|
|
2862
|
+
result[key] = schema[key];
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2391
2865
|
return result;
|
|
2392
2866
|
}
|
|
2393
2867
|
function normalizeCombinator(keyword, schemas, originalSchema, options) {
|
|
@@ -2589,23 +3063,7 @@ function isOptionalMember(member) {
|
|
|
2589
3063
|
import * as fs2 from "node:fs";
|
|
2590
3064
|
import * as path3 from "node:path";
|
|
2591
3065
|
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
|
-
}
|
|
3066
|
+
import ts11 from "typescript";
|
|
2609
3067
|
|
|
2610
3068
|
// src/builder/schema-merger.ts
|
|
2611
3069
|
function mergeRuntimeSchemas(staticExports, runtimeSchemas) {
|
|
@@ -2636,6 +3094,24 @@ function mergeRuntimeSchemas(staticExports, runtimeSchemas) {
|
|
|
2636
3094
|
}
|
|
2637
3095
|
|
|
2638
3096
|
// src/builder/spec-builder.ts
|
|
3097
|
+
var typeDefinitionCache = null;
|
|
3098
|
+
function getTypeDefinitionCache() {
|
|
3099
|
+
if (!typeDefinitionCache) {
|
|
3100
|
+
typeDefinitionCache = new Map;
|
|
3101
|
+
}
|
|
3102
|
+
return typeDefinitionCache;
|
|
3103
|
+
}
|
|
3104
|
+
var internalTagCache = null;
|
|
3105
|
+
function getInternalTagCache() {
|
|
3106
|
+
if (!internalTagCache) {
|
|
3107
|
+
internalTagCache = new Map;
|
|
3108
|
+
}
|
|
3109
|
+
return internalTagCache;
|
|
3110
|
+
}
|
|
3111
|
+
function clearTypeDefinitionCache() {
|
|
3112
|
+
typeDefinitionCache = null;
|
|
3113
|
+
internalTagCache = null;
|
|
3114
|
+
}
|
|
2639
3115
|
var BUILTIN_TYPES2 = new Set([
|
|
2640
3116
|
"Array",
|
|
2641
3117
|
"ArrayBuffer",
|
|
@@ -2720,6 +3196,7 @@ function shouldSkipDanglingRef(name) {
|
|
|
2720
3196
|
return false;
|
|
2721
3197
|
}
|
|
2722
3198
|
async function extract(options) {
|
|
3199
|
+
clearTypeDefinitionCache();
|
|
2723
3200
|
const {
|
|
2724
3201
|
entryFile,
|
|
2725
3202
|
baseDir,
|
|
@@ -2781,7 +3258,8 @@ async function extract(options) {
|
|
|
2781
3258
|
const meta = await getPackageMeta(entryFile, baseDir);
|
|
2782
3259
|
const types = ctx.typeRegistry.getAll();
|
|
2783
3260
|
const projectBaseDir = baseDir ?? path3.dirname(entryFile);
|
|
2784
|
-
const
|
|
3261
|
+
const definedTypes = new Set(types.map((t) => t.id));
|
|
3262
|
+
const forgottenExports = collectForgottenExports(exports, types, program, sourceFile, exportedIds, projectBaseDir, definedTypes);
|
|
2785
3263
|
for (const forgotten of forgottenExports) {
|
|
2786
3264
|
const refSummary = forgotten.referencedBy.slice(0, 3).map((r) => `${r.exportName} (${r.location})`).join(", ");
|
|
2787
3265
|
const moreRefs = forgotten.referencedBy.length > 3 ? ` +${forgotten.referencedBy.length - 3} more` : "";
|
|
@@ -2864,10 +3342,9 @@ function collectAllRefsWithContext(obj, refs, state) {
|
|
|
2864
3342
|
return;
|
|
2865
3343
|
if (Array.isArray(obj)) {
|
|
2866
3344
|
for (let i = 0;i < obj.length; i++) {
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
});
|
|
3345
|
+
state.path.push(`[${i}]`);
|
|
3346
|
+
collectAllRefsWithContext(obj[i], refs, state);
|
|
3347
|
+
state.path.pop();
|
|
2871
3348
|
}
|
|
2872
3349
|
return;
|
|
2873
3350
|
}
|
|
@@ -2880,53 +3357,63 @@ function collectAllRefsWithContext(obj, refs, state) {
|
|
|
2880
3357
|
typeName,
|
|
2881
3358
|
exportName: state.exportName,
|
|
2882
3359
|
location: state.location,
|
|
2883
|
-
path: state.path.join(".")
|
|
3360
|
+
path: state.path.length > 0 ? state.path.join(".") : undefined
|
|
2884
3361
|
});
|
|
2885
3362
|
refs.set(typeName, existing);
|
|
2886
3363
|
}
|
|
3364
|
+
const prevLocation = state.location;
|
|
2887
3365
|
for (const [key, value] of Object.entries(record)) {
|
|
2888
|
-
let newLocation = state.location;
|
|
2889
3366
|
if (key === "returnType" || key === "returns")
|
|
2890
|
-
|
|
3367
|
+
state.location = "return";
|
|
2891
3368
|
else if (key === "parameters" || key === "params")
|
|
2892
|
-
|
|
3369
|
+
state.location = "parameter";
|
|
2893
3370
|
else if (key === "properties" || key === "members")
|
|
2894
|
-
|
|
3371
|
+
state.location = "property";
|
|
2895
3372
|
else if (key === "extends" || key === "implements")
|
|
2896
|
-
|
|
3373
|
+
state.location = "extends";
|
|
2897
3374
|
else if (key === "typeParameters" || key === "typeParams")
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
});
|
|
3375
|
+
state.location = "type-parameter";
|
|
3376
|
+
state.path.push(key);
|
|
3377
|
+
collectAllRefsWithContext(value, refs, state);
|
|
3378
|
+
state.path.pop();
|
|
3379
|
+
state.location = prevLocation;
|
|
2904
3380
|
}
|
|
2905
3381
|
}
|
|
2906
3382
|
}
|
|
2907
3383
|
function findTypeDefinition(typeName, program, sourceFile) {
|
|
3384
|
+
const cache = getTypeDefinitionCache();
|
|
3385
|
+
if (cache.has(typeName)) {
|
|
3386
|
+
return cache.get(typeName);
|
|
3387
|
+
}
|
|
2908
3388
|
const checker = program.getTypeChecker();
|
|
2909
3389
|
const findInNode = (node) => {
|
|
2910
|
-
if ((
|
|
3390
|
+
if ((ts11.isInterfaceDeclaration(node) || ts11.isTypeAliasDeclaration(node) || ts11.isClassDeclaration(node) || ts11.isEnumDeclaration(node)) && node.name?.text === typeName) {
|
|
2911
3391
|
const sf = node.getSourceFile();
|
|
2912
3392
|
return sf.fileName;
|
|
2913
3393
|
}
|
|
2914
|
-
return
|
|
3394
|
+
return ts11.forEachChild(node, findInNode);
|
|
2915
3395
|
};
|
|
2916
3396
|
const entryResult = findInNode(sourceFile);
|
|
2917
|
-
if (entryResult)
|
|
3397
|
+
if (entryResult) {
|
|
3398
|
+
cache.set(typeName, entryResult);
|
|
2918
3399
|
return entryResult;
|
|
3400
|
+
}
|
|
2919
3401
|
for (const sf of program.getSourceFiles()) {
|
|
2920
3402
|
if (sf.isDeclarationFile && !sf.fileName.includes("node_modules")) {
|
|
2921
3403
|
const result = findInNode(sf);
|
|
2922
|
-
if (result)
|
|
3404
|
+
if (result) {
|
|
3405
|
+
cache.set(typeName, result);
|
|
2923
3406
|
return result;
|
|
3407
|
+
}
|
|
2924
3408
|
}
|
|
2925
3409
|
}
|
|
2926
|
-
const symbol = checker.resolveName(typeName, sourceFile,
|
|
3410
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts11.SymbolFlags.Type, false);
|
|
2927
3411
|
if (symbol?.declarations?.[0]) {
|
|
2928
|
-
|
|
3412
|
+
const result = symbol.declarations[0].getSourceFile().fileName;
|
|
3413
|
+
cache.set(typeName, result);
|
|
3414
|
+
return result;
|
|
2929
3415
|
}
|
|
3416
|
+
cache.set(typeName, undefined);
|
|
2930
3417
|
return;
|
|
2931
3418
|
}
|
|
2932
3419
|
function isExternalType2(definedIn, baseDir) {
|
|
@@ -2939,15 +3426,23 @@ function isExternalType2(definedIn, baseDir) {
|
|
|
2939
3426
|
return !normalizedDefined.startsWith(normalizedBase);
|
|
2940
3427
|
}
|
|
2941
3428
|
function hasInternalTag(typeName, program, sourceFile) {
|
|
3429
|
+
const cache = getInternalTagCache();
|
|
3430
|
+
const cached = cache.get(typeName);
|
|
3431
|
+
if (cached !== undefined) {
|
|
3432
|
+
return cached;
|
|
3433
|
+
}
|
|
2942
3434
|
const checker = program.getTypeChecker();
|
|
2943
|
-
const symbol = checker.resolveName(typeName, sourceFile,
|
|
2944
|
-
if (!symbol)
|
|
3435
|
+
const symbol = checker.resolveName(typeName, sourceFile, ts11.SymbolFlags.Type, false);
|
|
3436
|
+
if (!symbol) {
|
|
3437
|
+
cache.set(typeName, false);
|
|
2945
3438
|
return false;
|
|
3439
|
+
}
|
|
2946
3440
|
const jsTags = symbol.getJsDocTags();
|
|
2947
|
-
|
|
3441
|
+
const isInternal = jsTags.some((tag) => tag.name === "internal");
|
|
3442
|
+
cache.set(typeName, isInternal);
|
|
3443
|
+
return isInternal;
|
|
2948
3444
|
}
|
|
2949
|
-
function collectForgottenExports(exports, types, program, sourceFile, exportedIds, baseDir) {
|
|
2950
|
-
const definedTypes = new Set(types.map((t) => t.id));
|
|
3445
|
+
function collectForgottenExports(exports, types, program, sourceFile, exportedIds, baseDir, definedTypes) {
|
|
2951
3446
|
const referencedTypes = new Map;
|
|
2952
3447
|
for (const exp of exports) {
|
|
2953
3448
|
collectAllRefsWithContext(exp, referencedTypes, {
|
|
@@ -2989,36 +3484,36 @@ function collectForgottenExports(exports, types, program, sourceFile, exportedId
|
|
|
2989
3484
|
}
|
|
2990
3485
|
function resolveExportTarget(symbol, checker) {
|
|
2991
3486
|
let targetSymbol = symbol;
|
|
2992
|
-
if (symbol.flags &
|
|
3487
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
2993
3488
|
const aliasTarget = checker.getAliasedSymbol(symbol);
|
|
2994
3489
|
if (aliasTarget && aliasTarget !== symbol) {
|
|
2995
3490
|
targetSymbol = aliasTarget;
|
|
2996
3491
|
}
|
|
2997
3492
|
}
|
|
2998
3493
|
const declarations = targetSymbol.declarations ?? [];
|
|
2999
|
-
const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !==
|
|
3494
|
+
const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts11.SyntaxKind.ExportSpecifier) || declarations[0];
|
|
3000
3495
|
return { declaration, targetSymbol };
|
|
3001
3496
|
}
|
|
3002
3497
|
function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportName, ctx) {
|
|
3003
3498
|
let result = null;
|
|
3004
|
-
if (
|
|
3499
|
+
if (ts11.isFunctionDeclaration(declaration)) {
|
|
3005
3500
|
result = serializeFunctionExport(declaration, ctx);
|
|
3006
|
-
} else if (
|
|
3501
|
+
} else if (ts11.isClassDeclaration(declaration)) {
|
|
3007
3502
|
result = serializeClass(declaration, ctx);
|
|
3008
|
-
} else if (
|
|
3503
|
+
} else if (ts11.isInterfaceDeclaration(declaration)) {
|
|
3009
3504
|
result = serializeInterface(declaration, ctx);
|
|
3010
|
-
} else if (
|
|
3505
|
+
} else if (ts11.isTypeAliasDeclaration(declaration)) {
|
|
3011
3506
|
result = serializeTypeAlias(declaration, ctx);
|
|
3012
|
-
} else if (
|
|
3507
|
+
} else if (ts11.isEnumDeclaration(declaration)) {
|
|
3013
3508
|
result = serializeEnum(declaration, ctx);
|
|
3014
|
-
} else if (
|
|
3509
|
+
} else if (ts11.isVariableDeclaration(declaration)) {
|
|
3015
3510
|
const varStatement = declaration.parent?.parent;
|
|
3016
|
-
if (varStatement &&
|
|
3511
|
+
if (varStatement && ts11.isVariableStatement(varStatement)) {
|
|
3017
3512
|
result = serializeVariable(declaration, varStatement, ctx);
|
|
3018
3513
|
}
|
|
3019
|
-
} else if (
|
|
3514
|
+
} else if (ts11.isNamespaceExport(declaration) || ts11.isModuleDeclaration(declaration)) {
|
|
3020
3515
|
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
3021
|
-
} else if (
|
|
3516
|
+
} else if (ts11.isSourceFile(declaration)) {
|
|
3022
3517
|
result = serializeNamespaceExport(exportSymbol, exportName, ctx);
|
|
3023
3518
|
}
|
|
3024
3519
|
if (result) {
|
|
@@ -3031,7 +3526,7 @@ function serializeNamespaceExport(symbol, exportName, ctx) {
|
|
|
3031
3526
|
const members = [];
|
|
3032
3527
|
const checker = ctx.program.getTypeChecker();
|
|
3033
3528
|
let targetSymbol = symbol;
|
|
3034
|
-
if (symbol.flags &
|
|
3529
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
3035
3530
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
3036
3531
|
if (aliased && aliased !== symbol) {
|
|
3037
3532
|
targetSymbol = aliased;
|
|
@@ -3058,30 +3553,31 @@ function serializeNamespaceExport(symbol, exportName, ctx) {
|
|
|
3058
3553
|
function serializeNamespaceMember(symbol, memberName, ctx) {
|
|
3059
3554
|
const checker = ctx.program.getTypeChecker();
|
|
3060
3555
|
let targetSymbol = symbol;
|
|
3061
|
-
if (symbol.flags &
|
|
3556
|
+
if (symbol.flags & ts11.SymbolFlags.Alias) {
|
|
3062
3557
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
3063
3558
|
if (aliased && aliased !== symbol) {
|
|
3064
3559
|
targetSymbol = aliased;
|
|
3065
3560
|
}
|
|
3066
3561
|
}
|
|
3067
3562
|
const declarations = targetSymbol.declarations ?? [];
|
|
3068
|
-
const declaration = targetSymbol.valueDeclaration || declarations.find((d) => d.kind !==
|
|
3563
|
+
const declaration = targetSymbol.valueDeclaration || declarations.find((d) => d.kind !== ts11.SyntaxKind.ExportSpecifier) || declarations[0];
|
|
3069
3564
|
if (!declaration)
|
|
3070
3565
|
return null;
|
|
3566
|
+
const type = checker.getTypeAtLocation(declaration);
|
|
3567
|
+
const callSignatures = type.getCallSignatures();
|
|
3568
|
+
const deprecated = isSymbolDeprecated(targetSymbol);
|
|
3071
3569
|
let kind = "variable";
|
|
3072
|
-
if (
|
|
3570
|
+
if (ts11.isFunctionDeclaration(declaration) || ts11.isFunctionExpression(declaration)) {
|
|
3073
3571
|
kind = "function";
|
|
3074
|
-
} else if (
|
|
3572
|
+
} else if (ts11.isClassDeclaration(declaration)) {
|
|
3075
3573
|
kind = "class";
|
|
3076
|
-
} else if (
|
|
3574
|
+
} else if (ts11.isInterfaceDeclaration(declaration)) {
|
|
3077
3575
|
kind = "interface";
|
|
3078
|
-
} else if (
|
|
3576
|
+
} else if (ts11.isTypeAliasDeclaration(declaration)) {
|
|
3079
3577
|
kind = "type";
|
|
3080
|
-
} else if (
|
|
3578
|
+
} else if (ts11.isEnumDeclaration(declaration)) {
|
|
3081
3579
|
kind = "enum";
|
|
3082
|
-
} else if (
|
|
3083
|
-
const type = checker.getTypeAtLocation(declaration);
|
|
3084
|
-
const callSignatures = type.getCallSignatures();
|
|
3580
|
+
} else if (ts11.isVariableDeclaration(declaration)) {
|
|
3085
3581
|
if (callSignatures.length > 0) {
|
|
3086
3582
|
kind = "function";
|
|
3087
3583
|
}
|
|
@@ -3089,10 +3585,38 @@ function serializeNamespaceMember(symbol, memberName, ctx) {
|
|
|
3089
3585
|
const docComment = targetSymbol.getDocumentationComment(checker);
|
|
3090
3586
|
const description = docComment.map((c) => c.text).join(`
|
|
3091
3587
|
`) || undefined;
|
|
3588
|
+
let signatures;
|
|
3589
|
+
if (kind === "function" && callSignatures.length > 0) {
|
|
3590
|
+
signatures = callSignatures.map((sig, index) => {
|
|
3591
|
+
const params = extractParameters(sig, ctx);
|
|
3592
|
+
const returnType = checker.getReturnTypeOfSignature(sig);
|
|
3593
|
+
registerReferencedTypes(returnType, ctx);
|
|
3594
|
+
const returnSchema = buildSchema(returnType, ctx.typeChecker, ctx);
|
|
3595
|
+
const sigDoc = getJSDocForSignature(sig);
|
|
3596
|
+
const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
|
|
3597
|
+
return {
|
|
3598
|
+
parameters: params,
|
|
3599
|
+
returns: { schema: returnSchema },
|
|
3600
|
+
...sigDoc.description ? { description: sigDoc.description } : {},
|
|
3601
|
+
...sigDoc.tags.length > 0 ? { tags: sigDoc.tags } : {},
|
|
3602
|
+
...sigDoc.examples.length > 0 ? { examples: sigDoc.examples } : {},
|
|
3603
|
+
...sigTypeParams ? { typeParameters: sigTypeParams } : {},
|
|
3604
|
+
...callSignatures.length > 1 ? { overloadIndex: index } : {}
|
|
3605
|
+
};
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3608
|
+
let schema;
|
|
3609
|
+
if (kind !== "function") {
|
|
3610
|
+
registerReferencedTypes(type, ctx);
|
|
3611
|
+
schema = buildSchema(type, ctx.typeChecker, ctx);
|
|
3612
|
+
}
|
|
3092
3613
|
return {
|
|
3093
3614
|
name: memberName,
|
|
3094
3615
|
kind,
|
|
3095
|
-
...description ? { description } : {}
|
|
3616
|
+
...description ? { description } : {},
|
|
3617
|
+
...signatures ? { signatures } : {},
|
|
3618
|
+
...schema ? { schema } : {},
|
|
3619
|
+
...deprecated ? { flags: { deprecated: true } } : {}
|
|
3096
3620
|
};
|
|
3097
3621
|
}
|
|
3098
3622
|
function getJSDocFromExportSymbol(symbol) {
|
|
@@ -3100,11 +3624,11 @@ function getJSDocFromExportSymbol(symbol) {
|
|
|
3100
3624
|
const examples = [];
|
|
3101
3625
|
const decl = symbol.declarations?.[0];
|
|
3102
3626
|
if (decl) {
|
|
3103
|
-
const exportDecl =
|
|
3104
|
-
if (exportDecl &&
|
|
3105
|
-
const jsDocs =
|
|
3627
|
+
const exportDecl = ts11.isNamespaceExport(decl) ? decl.parent : decl;
|
|
3628
|
+
if (exportDecl && ts11.isExportDeclaration(exportDecl)) {
|
|
3629
|
+
const jsDocs = ts11.getJSDocCommentsAndTags(exportDecl);
|
|
3106
3630
|
for (const doc of jsDocs) {
|
|
3107
|
-
if (
|
|
3631
|
+
if (ts11.isJSDoc(doc) && doc.comment) {
|
|
3108
3632
|
const commentText = typeof doc.comment === "string" ? doc.comment : doc.comment.map((c) => ("text" in c) ? c.text : "").join("");
|
|
3109
3633
|
if (commentText) {
|
|
3110
3634
|
return {
|
|
@@ -3189,4 +3713,4 @@ async function getPackageMeta(entryFile, baseDir) {
|
|
|
3189
3713
|
} catch {}
|
|
3190
3714
|
return { name: path3.basename(searchDir) };
|
|
3191
3715
|
}
|
|
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,
|
|
3716
|
+
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 };
|