@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.
@@ -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 typeString = checker.typeToString(type);
174
- return { type: "boolean", enum: [typeString === "true"] };
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: type.types.map((t) => buildSchema(t, checker, ctx))
272
+ allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx))
199
273
  }));
200
274
  }
201
- return { allOf: type.types.map((t) => buildSchema(t, checker, ctx)) };
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
- type: "array",
223
- prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
224
- minItems: elementTypes.length,
225
- maxItems: elementTypes.length
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
- $ref: `#/types/${name}`,
246
- typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
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
- return {
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
- return { $ref: `#/types/${name}` };
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/types/parameters.ts
859
- import ts5 from "typescript";
860
- function extractParameters(signature, ctx) {
861
- const { typeChecker: checker } = ctx;
862
- const result = [];
863
- const signatureDecl = signature.getDeclaration();
864
- const jsdocTags = signatureDecl ? ts5.getJSDocTags(signatureDecl) : [];
865
- for (const param of signature.getParameters()) {
866
- const decl = param.valueDeclaration;
867
- const type = checker.getTypeOfSymbolAtLocation(param, decl ?? param.valueDeclaration);
868
- if (decl && ts5.isObjectBindingPattern(decl.name)) {
869
- const expandedParams = expandBindingPattern(decl, type, jsdocTags, ctx);
870
- result.push(...expandedParams);
871
- } else {
872
- registerReferencedTypes(type, ctx);
873
- result.push({
874
- name: param.getName(),
875
- schema: buildSchema(type, checker, ctx),
876
- required: !(param.flags & 16777216)
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
- function inferParamAlias(jsdocTags) {
930
- const prefixes = [];
931
- for (const tag of jsdocTags) {
932
- if (tag.tagName.text !== "param")
933
- continue;
934
- const tagText = typeof tag.comment === "string" ? tag.comment : ts5.getTextOfJSDocComment(tag.comment) ?? "";
935
- const paramTag = tag;
936
- const paramName = paramTag.name?.getText() ?? "";
937
- if (paramName.includes(".")) {
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 extractDefaultValue(initializer) {
957
- if (ts5.isStringLiteral(initializer)) {
958
- return initializer.text;
1125
+ function detectTsRuntime() {
1126
+ if (cachedRuntime !== undefined) {
1127
+ return cachedRuntime;
959
1128
  }
960
- if (ts5.isNumericLiteral(initializer)) {
961
- return Number(initializer.text);
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 (initializer.kind === ts5.SyntaxKind.TrueKeyword) {
964
- return true;
1138
+ if (commandExists("bun")) {
1139
+ cachedRuntime = {
1140
+ cmd: "bun",
1141
+ args: ["run"],
1142
+ name: "bun"
1143
+ };
1144
+ return cachedRuntime;
965
1145
  }
966
- if (initializer.kind === ts5.SyntaxKind.FalseKeyword) {
967
- return false;
1146
+ if (commandExists("tsx")) {
1147
+ cachedRuntime = {
1148
+ cmd: "tsx",
1149
+ args: [],
1150
+ name: "tsx"
1151
+ };
1152
+ return cachedRuntime;
968
1153
  }
969
- if (initializer.kind === ts5.SyntaxKind.NullKeyword) {
970
- return null;
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
- return initializer.getText();
1162
+ cachedRuntime = null;
1163
+ return null;
973
1164
  }
974
- function registerReferencedTypes(type, ctx, depth = 0) {
975
- if (depth > ctx.maxTypeDepth)
976
- return;
977
- if (ctx.visitedTypes.has(type))
978
- return;
979
- 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);
980
- if (!isPrimitive) {
981
- ctx.visitedTypes.add(type);
982
- }
983
- const { typeChecker: checker, typeRegistry } = ctx;
984
- typeRegistry.registerType(type, ctx);
985
- const typeArgs = type.typeArguments;
986
- if (typeArgs) {
987
- for (const arg of typeArgs) {
988
- registerReferencedTypes(arg, ctx, depth + 1);
989
- }
990
- }
991
- if (type.isUnion()) {
992
- for (const t of type.types) {
993
- registerReferencedTypes(t, ctx, depth + 1);
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
- if (type.isIntersection()) {
997
- for (const t of type.types) {
998
- registerReferencedTypes(t, ctx, depth + 1);
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 (type.flags & ts5.TypeFlags.Object) {
1002
- const props = type.getProperties();
1003
- for (const prop of props.slice(0, 20)) {
1004
- const propType = checker.getTypeOfSymbol(prop);
1005
- registerReferencedTypes(propType, ctx, depth + 1);
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 (ts6.isPropertyDeclaration(member)) {
1866
+ if (ts7.isPropertyDeclaration(member)) {
1031
1867
  const propMember = serializeProperty(member, ctx);
1032
1868
  if (propMember)
1033
1869
  members.push(propMember);
1034
- } else if (ts6.isMethodDeclaration(member)) {
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 (ts6.isConstructorDeclaration(member)) {
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 (ts6.isGetAccessorDeclaration(member) || ts6.isSetAccessorDeclaration(member)) {
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 (ts6.isConstructorDeclaration(member))
1926
+ if (ts7.isConstructorDeclaration(member))
1080
1927
  return "constructor";
1081
1928
  if (!member.name)
1082
1929
  return;
1083
- if (ts6.isIdentifier(member.name))
1930
+ if (ts7.isIdentifier(member.name))
1084
1931
  return member.name.text;
1085
- if (ts6.isPrivateIdentifier(member.name))
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 = ts6.canHaveModifiers(member) ? ts6.getModifiers(member) : undefined;
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 === ts6.SyntaxKind.PrivateKeyword)
1941
+ if (mod.kind === ts7.SyntaxKind.PrivateKeyword)
1095
1942
  return "private";
1096
- if (mod.kind === ts6.SyntaxKind.ProtectedKeyword)
1943
+ if (mod.kind === ts7.SyntaxKind.ProtectedKeyword)
1097
1944
  return "protected";
1098
- if (mod.kind === ts6.SyntaxKind.PublicKeyword)
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 = ts6.canHaveModifiers(member) ? ts6.getModifiers(member) : undefined;
1105
- return modifiers?.some((m) => m.kind === ts6.SyntaxKind.StaticKeyword) ?? false;
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 = ts6.canHaveModifiers(member) ? ts6.getModifiers(member) : undefined;
1109
- return modifiers?.some((m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword) ?? false;
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 = ts6.getModifiers(node);
1164
- if (modifiers?.some((m) => m.kind === ts6.SyntaxKind.AsyncKeyword)) {
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 = ts6.isGetAccessorDeclaration(node) ? "getter" : "setter";
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 === ts6.SyntaxKind.ExtendsKeyword) {
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 === ts6.SyntaxKind.ImplementsKeyword) {
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 returnType = ctx.typeChecker.getReturnTypeOfSignature(sig);
1304
- registerReferencedTypes(returnType, ctx);
2186
+ const sigDoc = getJSDocForSignature(sig);
2187
+ const sigTypeParams = extractTypeParametersFromSignature(sig, ctx.typeChecker);
1305
2188
  return {
1306
2189
  parameters: params,
1307
- returns: {
1308
- schema: buildSchema(returnType, ctx.typeChecker, ctx)
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 ts7 from "typescript";
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 (ts7.isPropertySignature(member)) {
2228
+ if (ts9.isPropertySignature(member)) {
1343
2229
  const propMember = serializePropertySignature(member, ctx);
1344
2230
  if (propMember)
1345
2231
  members.push(propMember);
1346
- } else if (ts7.isMethodSignature(member)) {
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 (ts7.isCallSignatureDeclaration(member)) {
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 (ts7.isIndexSignatureDeclaration(member)) {
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 === ts7.SyntaxKind.ReadonlyKeyword)) {
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
- let typesType = checker.getTypeOfSymbol(typesSymbol);
1636
- typesType = getNonNullableType(typesType);
1637
- const inputSymbol = typesType.getProperty("input");
1638
- if (!inputSymbol) {
1639
- return null;
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/schema/adapters/zod.ts
1646
- var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
1647
- var zodAdapter = {
1648
- id: "zod",
1649
- packages: ["zod"],
1650
- matches(type, checker) {
1651
- const typeName = checker.typeToString(type);
1652
- return ZOD_TYPE_PATTERN.test(typeName);
1653
- },
1654
- extractOutputType(type, checker) {
1655
- const outputSymbol = type.getProperty("_output");
1656
- if (outputSymbol) {
1657
- return checker.getTypeOfSymbol(outputSymbol);
1658
- }
1659
- const typeSymbol = type.getProperty("_type");
1660
- if (typeSymbol) {
1661
- return checker.getTypeOfSymbol(typeSymbol);
1662
- }
1663
- return null;
1664
- },
1665
- extractInputType(type, checker) {
1666
- const inputSymbol = type.getProperty("_input");
1667
- if (inputSymbol) {
1668
- return checker.getTypeOfSymbol(inputSymbol);
1669
- }
1670
- return null;
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
- // src/schema/adapters/index.ts
1675
- registerAdapter(zodAdapter);
1676
- registerAdapter(valibotAdapter);
1677
- registerAdapter(arktypeAdapter);
1678
- registerAdapter(typeboxAdapter);
1679
-
1680
- // src/serializers/variables.ts
1681
- function serializeVariable(node, statement, ctx) {
1682
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
1683
- const name = symbol?.getName() ?? node.name.getText();
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(statement);
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
- const schemaExtraction = extractSchemaType(type, ctx.typeChecker);
1692
- const typeToSerialize = schemaExtraction?.outputType ?? type;
1693
- registerReferencedTypes(typeToSerialize, ctx);
1694
- const schema = buildSchema(typeToSerialize, ctx.typeChecker, ctx);
1695
- const flags = schemaExtraction ? {
1696
- schemaLibrary: schemaExtraction.adapter.id,
1697
- ...schemaExtraction.inputType && schemaExtraction.inputType !== schemaExtraction.outputType ? { hasTransform: true } : {}
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: "variable",
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/standard-schema.ts
1714
- import { spawn, spawnSync } from "node:child_process";
1715
- import * as fs from "node:fs";
1716
- import * as os from "node:os";
1717
- import * as path2 from "node:path";
1718
- function isStandardJSONSchema(obj) {
1719
- if (typeof obj !== "object" || obj === null)
1720
- return false;
1721
- const std = obj["~standard"];
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
- extract();
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
- function sanitizeTypeBoxSchema(schema: unknown): unknown {
1892
- return JSON.parse(JSON.stringify(schema));
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
- async function extract() {
1896
- const [,, modulePath, optionsJson] = process.argv;
1897
- const { target, libraryOptions } = JSON.parse(optionsJson || '{}');
1898
-
1899
- try {
1900
- const absPath = path.resolve(modulePath);
1901
- const mod = await import(pathToFileURL(absPath).href);
1902
- const results: Array<{exportName: string; vendor: string; outputSchema: unknown; inputSchema?: unknown}> = [];
1903
-
1904
- // Build exports map
1905
- const exports: Record<string, unknown> = {};
1906
- for (const [name, value] of Object.entries(mod)) {
1907
- if (name === 'default' && typeof value === 'object' && value !== null) {
1908
- Object.assign(exports, value);
1909
- } else if (name !== 'default') {
1910
- exports[name] = value;
1911
- }
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
- // Check each export
1915
- for (const [name, value] of Object.entries(exports)) {
1916
- if (name.startsWith('_')) continue;
1917
- if (typeof value !== 'object' || value === null) continue;
1918
-
1919
- const v = value as Record<string, unknown>;
1920
- const std = v['~standard'] as Record<string, unknown> | undefined;
1921
-
1922
- // Standard JSON Schema
1923
- if (std && typeof std === 'object' && std.version === 1 && typeof std.vendor === 'string') {
1924
- const jsonSchema = std.jsonSchema as Record<string, unknown> | undefined;
1925
- if (jsonSchema && typeof jsonSchema.output === 'function') {
1926
- try {
1927
- const options = { target: target || 'draft-2020-12', ...(libraryOptions && { libraryOptions }) };
1928
- const outputSchema = (jsonSchema.output as Function)(options);
1929
- const inputSchema = typeof jsonSchema.input === 'function' ? (jsonSchema.input as Function)(options) : undefined;
1930
- results.push({ exportName: name, vendor: std.vendor as string, outputSchema, inputSchema });
1931
- } catch {}
1932
- continue;
1933
- }
1934
- }
1935
-
1936
- // TypeBox
1937
- if (isTypeBoxSchema(value)) {
1938
- try {
1939
- results.push({ exportName: name, vendor: 'typebox', outputSchema: sanitizeTypeBoxSchema(value) });
1940
- } catch {}
1941
- }
2483
+ const args = checker.getTypeArguments(type);
2484
+ if (args.length < 1) {
2485
+ return null;
1942
2486
  }
1943
-
1944
- console.log(JSON.stringify({ success: true, results }));
1945
- } catch (e) {
1946
- console.log(JSON.stringify({ success: false, error: (e as Error).message }));
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
- extract();
1951
- `;
1952
- async function extractStandardSchemasFromTs(tsFilePath, options = {}) {
1953
- const { timeout = 1e4, target = "draft-2020-12", libraryOptions } = options;
1954
- const result = {
1955
- schemas: new Map,
1956
- errors: []
1957
- };
1958
- const runtime = detectTsRuntime();
1959
- if (!runtime) {
1960
- result.errors.push("No TypeScript runtime available. Install bun, tsx, or ts-node, or use Node 22+.");
1961
- return result;
1962
- }
1963
- if (!fs.existsSync(tsFilePath)) {
1964
- result.errors.push(`TypeScript file not found: ${tsFilePath}`);
1965
- return result;
1966
- }
1967
- const tempDir = os.tmpdir();
1968
- const workerPath = path2.join(tempDir, `openpkg-extract-worker-${Date.now()}.ts`);
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
- function readTsconfigOutDir(baseDir) {
2033
- const tsconfigPath = path2.join(baseDir, "tsconfig.json");
2034
- try {
2035
- if (!fs.existsSync(tsconfigPath)) {
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
- const content = fs.readFileSync(tsconfigPath, "utf-8");
2039
- const stripped = content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
2040
- const tsconfig = JSON.parse(stripped);
2041
- if (tsconfig.compilerOptions?.outDir) {
2042
- return tsconfig.compilerOptions.outDir.replace(/^\.\//, "");
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
- const commonOutDirs = ["dist", "build", "lib", "out"];
2060
- for (const outDir of commonOutDirs) {
2061
- if (outDir === tsconfigOutDir)
2062
- continue;
2063
- for (const ext of extensions) {
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
- for (const ext of extensions) {
2068
- candidates.push(path2.join(baseDir, `${withoutExt}${ext}`));
2069
- }
2070
- const workspaceMatch = baseDir.match(/^(.+\/packages\/[^/]+)$/);
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
- for (const candidate of candidates) {
2078
- if (fs.existsSync(candidate)) {
2079
- return candidate;
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
- return null;
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
- if (isTypeScript) {
2154
- const runtime2 = detectTsRuntime();
2155
- if (runtime2) {
2156
- const result = await extractStandardSchemasFromTs(entryFile, extractOptions);
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
- const runtime = detectTsRuntime();
2164
- const hint = isTypeScript && !runtime ? " Install bun, tsx, or ts-node for direct TS execution." : "";
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
- schemas: new Map,
2167
- errors: [`Could not find compiled JS for ${entryFile}.${hint}`]
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 ts8 from "typescript";
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 forgottenExports = collectForgottenExports(exports, types, program, sourceFile, exportedIds, projectBaseDir);
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
- collectAllRefsWithContext(obj[i], refs, {
2868
- ...state,
2869
- path: [...state.path, `[${i}]`]
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(".") || undefined
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
- newLocation = "return";
3367
+ state.location = "return";
2891
3368
  else if (key === "parameters" || key === "params")
2892
- newLocation = "parameter";
3369
+ state.location = "parameter";
2893
3370
  else if (key === "properties" || key === "members")
2894
- newLocation = "property";
3371
+ state.location = "property";
2895
3372
  else if (key === "extends" || key === "implements")
2896
- newLocation = "extends";
3373
+ state.location = "extends";
2897
3374
  else if (key === "typeParameters" || key === "typeParams")
2898
- newLocation = "type-parameter";
2899
- collectAllRefsWithContext(value, refs, {
2900
- ...state,
2901
- location: newLocation,
2902
- path: [...state.path, key]
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 ((ts8.isInterfaceDeclaration(node) || ts8.isTypeAliasDeclaration(node) || ts8.isClassDeclaration(node) || ts8.isEnumDeclaration(node)) && node.name?.text === typeName) {
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 ts8.forEachChild(node, findInNode);
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, ts8.SymbolFlags.Type, false);
3410
+ const symbol = checker.resolveName(typeName, sourceFile, ts11.SymbolFlags.Type, false);
2927
3411
  if (symbol?.declarations?.[0]) {
2928
- return symbol.declarations[0].getSourceFile().fileName;
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, ts8.SymbolFlags.Type, false);
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
- return jsTags.some((tag) => tag.name === "internal");
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 & ts8.SymbolFlags.Alias) {
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 !== ts8.SyntaxKind.ExportSpecifier) || declarations[0];
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 (ts8.isFunctionDeclaration(declaration)) {
3499
+ if (ts11.isFunctionDeclaration(declaration)) {
3005
3500
  result = serializeFunctionExport(declaration, ctx);
3006
- } else if (ts8.isClassDeclaration(declaration)) {
3501
+ } else if (ts11.isClassDeclaration(declaration)) {
3007
3502
  result = serializeClass(declaration, ctx);
3008
- } else if (ts8.isInterfaceDeclaration(declaration)) {
3503
+ } else if (ts11.isInterfaceDeclaration(declaration)) {
3009
3504
  result = serializeInterface(declaration, ctx);
3010
- } else if (ts8.isTypeAliasDeclaration(declaration)) {
3505
+ } else if (ts11.isTypeAliasDeclaration(declaration)) {
3011
3506
  result = serializeTypeAlias(declaration, ctx);
3012
- } else if (ts8.isEnumDeclaration(declaration)) {
3507
+ } else if (ts11.isEnumDeclaration(declaration)) {
3013
3508
  result = serializeEnum(declaration, ctx);
3014
- } else if (ts8.isVariableDeclaration(declaration)) {
3509
+ } else if (ts11.isVariableDeclaration(declaration)) {
3015
3510
  const varStatement = declaration.parent?.parent;
3016
- if (varStatement && ts8.isVariableStatement(varStatement)) {
3511
+ if (varStatement && ts11.isVariableStatement(varStatement)) {
3017
3512
  result = serializeVariable(declaration, varStatement, ctx);
3018
3513
  }
3019
- } else if (ts8.isNamespaceExport(declaration) || ts8.isModuleDeclaration(declaration)) {
3514
+ } else if (ts11.isNamespaceExport(declaration) || ts11.isModuleDeclaration(declaration)) {
3020
3515
  result = serializeNamespaceExport(exportSymbol, exportName, ctx);
3021
- } else if (ts8.isSourceFile(declaration)) {
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 & ts8.SymbolFlags.Alias) {
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 & ts8.SymbolFlags.Alias) {
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 !== ts8.SyntaxKind.ExportSpecifier) || declarations[0];
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 (ts8.isFunctionDeclaration(declaration) || ts8.isFunctionExpression(declaration)) {
3570
+ if (ts11.isFunctionDeclaration(declaration) || ts11.isFunctionExpression(declaration)) {
3073
3571
  kind = "function";
3074
- } else if (ts8.isClassDeclaration(declaration)) {
3572
+ } else if (ts11.isClassDeclaration(declaration)) {
3075
3573
  kind = "class";
3076
- } else if (ts8.isInterfaceDeclaration(declaration)) {
3574
+ } else if (ts11.isInterfaceDeclaration(declaration)) {
3077
3575
  kind = "interface";
3078
- } else if (ts8.isTypeAliasDeclaration(declaration)) {
3576
+ } else if (ts11.isTypeAliasDeclaration(declaration)) {
3079
3577
  kind = "type";
3080
- } else if (ts8.isEnumDeclaration(declaration)) {
3578
+ } else if (ts11.isEnumDeclaration(declaration)) {
3081
3579
  kind = "enum";
3082
- } else if (ts8.isVariableDeclaration(declaration)) {
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 = ts8.isNamespaceExport(decl) ? decl.parent : decl;
3104
- if (exportDecl && ts8.isExportDeclaration(exportDecl)) {
3105
- const jsDocs = ts8.getJSDocCommentsAndTags(exportDecl);
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 (ts8.isJSDoc(doc) && doc.comment) {
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, isStandardJSONSchema, detectTsRuntime, extractStandardSchemasFromTs, resolveCompiledPath, extractStandardSchemas, extractStandardSchemasFromProject, normalizeSchema, normalizeExport, normalizeType, normalizeMembers, extract };
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 };