@openpkg-ts/sdk 0.54.8 → 0.54.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1830,6 +1830,18 @@ function declKey(symbol, checker) {
1830
1830
  return;
1831
1831
  return `${decl.getSourceFile().fileName}#${decl.getStart()}`;
1832
1832
  }
1833
+ function fileLabel(fileName) {
1834
+ const base = path3.basename(fileName).replace(/(\.d)?\.[cm]?[tj]sx?$/, "");
1835
+ return base === "index" ? path3.basename(path3.dirname(fileName)) || base : base;
1836
+ }
1837
+ function namespaceLabel(decl) {
1838
+ const names = [];
1839
+ for (let node = decl?.parent;node; node = node.parent) {
1840
+ if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name))
1841
+ names.unshift(node.name.text);
1842
+ }
1843
+ return names.length > 0 ? names.join(".") : undefined;
1844
+ }
1833
1845
  function resolveTypeId(symbol, ctx) {
1834
1846
  const cached = ctx.typeIds.get(symbol);
1835
1847
  if (cached)
@@ -1853,16 +1865,49 @@ function resolveTypeId(symbol, ctx) {
1853
1865
  const owner = ctx.idOwner.get(name);
1854
1866
  if (!owner || owner === key)
1855
1867
  return claim(name);
1856
- const file = symbol.declarations?.[0]?.getSourceFile().fileName ?? "";
1857
- const scoped = `${packageLabel(file, ctx.workspacePackages)}.${name}`;
1858
- const scopedOwner = ctx.idOwner.get(scoped);
1859
- if (!scopedOwner || scopedOwner === key)
1860
- return claim(scoped);
1868
+ const decl = symbol.declarations?.[0];
1869
+ const file = decl?.getSourceFile().fileName ?? "";
1870
+ const pkg = packageLabel(file, ctx.workspacePackages);
1871
+ const ownerFile = owner.includes("#") ? owner.slice(0, owner.lastIndexOf("#")) : "";
1872
+ const scopes = pkg === packageLabel(ownerFile, ctx.workspacePackages) ? [] : [pkg];
1873
+ const namespace = namespaceLabel(decl);
1874
+ if (namespace)
1875
+ scopes.push(namespace);
1876
+ if (file)
1877
+ scopes.push(fileLabel(file));
1878
+ for (const scope of scopes) {
1879
+ const scoped = `${scope}.${name}`;
1880
+ const scopedOwner = ctx.idOwner.get(scoped);
1881
+ if (!scopedOwner || scopedOwner === key)
1882
+ return claim(scoped);
1883
+ }
1861
1884
  let n = 2;
1862
1885
  while (ctx.idOwner.has(`${name}_${n}`))
1863
1886
  n++;
1864
1887
  return claim(`${name}_${n}`);
1865
1888
  }
1889
+ var EXPORTABLE_TYPE_FLAGS = ts.SymbolFlags.Interface | ts.SymbolFlags.TypeAlias | ts.SymbolFlags.Class | ts.SymbolFlags.RegularEnum | ts.SymbolFlags.ConstEnum;
1890
+ function claimExportedTypeIds(exportedSymbols, ctx) {
1891
+ const renamed = [];
1892
+ for (const exported of exportedSymbols) {
1893
+ let target = exported;
1894
+ if (exported.flags & ts.SymbolFlags.Alias) {
1895
+ try {
1896
+ target = ctx.typeChecker.getAliasedSymbol(exported);
1897
+ } catch {
1898
+ continue;
1899
+ }
1900
+ }
1901
+ if (!(target.flags & EXPORTABLE_TYPE_FLAGS))
1902
+ continue;
1903
+ if (target.getName() === exported.getName())
1904
+ resolveTypeId(target, ctx);
1905
+ else
1906
+ renamed.push(target);
1907
+ }
1908
+ for (const target of renamed)
1909
+ resolveTypeId(target, ctx);
1910
+ }
1866
1911
  function typeRefId(type, ctx) {
1867
1912
  const symbol = type.aliasSymbol ?? type.getSymbol();
1868
1913
  if (!symbol)
@@ -2218,6 +2263,21 @@ function getExportKind(declaration, type) {
2218
2263
  return "function";
2219
2264
  return "variable";
2220
2265
  }
2266
+ function getExtendsExpressions(node) {
2267
+ return node.heritageClauses?.find((clause) => clause.token === ts2.SyntaxKind.ExtendsKeyword)?.types ?? [];
2268
+ }
2269
+ function getExtendsText(node, checker) {
2270
+ const names = getExtendsExpressions(node).map((expr) => {
2271
+ const name = checker.getTypeAtLocation(expr).getSymbol()?.getName();
2272
+ return name && !name.startsWith("__") ? name : expr.expression.getText();
2273
+ });
2274
+ return names.length > 0 ? names.join(" & ") : undefined;
2275
+ }
2276
+ function propertyNameText(name) {
2277
+ if (ts2.isComputedPropertyName(name))
2278
+ return name.getText();
2279
+ return name.text;
2280
+ }
2221
2281
 
2222
2282
  // src/compiler/program.ts
2223
2283
  import * as fs4 from "node:fs";
@@ -2558,8 +2618,39 @@ function createProgram(options) {
2558
2618
  import ts10 from "typescript";
2559
2619
 
2560
2620
  // src/types/parameters.ts
2621
+ import ts7 from "typescript";
2622
+
2623
+ // src/ast/registry.ts
2561
2624
  import ts6 from "typescript";
2562
2625
 
2626
+ // src/serializers/expansion-budget.ts
2627
+ var MAX_BUDGET_OPS = 1e4;
2628
+ var MAX_SCHEMA_OPS = 200000;
2629
+ function withExpansionBudget(ctx, owner, fn) {
2630
+ const outer = {
2631
+ budget: ctx.budget,
2632
+ currentDepth: ctx.currentDepth,
2633
+ visitedTypes: ctx.visitedTypes,
2634
+ inTupleElement: ctx.inTupleElement,
2635
+ aliasBody: ctx.aliasBody
2636
+ };
2637
+ const budget = { owner, ops: 0, exceeded: false };
2638
+ Object.assign(ctx, {
2639
+ budget,
2640
+ currentDepth: 0,
2641
+ visitedTypes: new Set,
2642
+ inTupleElement: undefined,
2643
+ aliasBody: undefined
2644
+ });
2645
+ try {
2646
+ return fn();
2647
+ } finally {
2648
+ if (budget.exceeded)
2649
+ ctx.exhaustedBudgets.push(owner);
2650
+ Object.assign(ctx, outer);
2651
+ }
2652
+ }
2653
+
2563
2654
  // src/types/schema-builder.ts
2564
2655
  import ts5 from "typescript";
2565
2656
 
@@ -2617,6 +2708,10 @@ function resolveFromModuleSpecifier(decl, symbol, checker, program) {
2617
2708
  if (ts4.isExportSpecifier(decl)) {
2618
2709
  const exportDecl = decl.parent?.parent;
2619
2710
  if (exportDecl && ts4.isExportDeclaration(exportDecl)) {
2711
+ if (!exportDecl.moduleSpecifier) {
2712
+ const local = checker.getExportSpecifierLocalTargetSymbol(decl);
2713
+ return local && local !== symbol ? local : undefined;
2714
+ }
2620
2715
  moduleSpecifier = exportDecl.moduleSpecifier;
2621
2716
  importedName = (decl.propertyName ?? decl.name).text;
2622
2717
  }
@@ -2789,7 +2884,8 @@ function resolvedSymbol(symbol, checker) {
2789
2884
  return;
2790
2885
  if (symbol.flags & ts5.SymbolFlags.Alias) {
2791
2886
  try {
2792
- return checker.getAliasedSymbol(symbol);
2887
+ const target = checker.getAliasedSymbol(symbol);
2888
+ return checker.isUnknownSymbol(target) ? symbol : target;
2793
2889
  } catch {
2794
2890
  return symbol;
2795
2891
  }
@@ -2824,10 +2920,11 @@ function buildSchemaFromTypeNode(node, checker, ctx) {
2824
2920
  if (node.kind === ts5.SyntaxKind.UnknownKeyword) {
2825
2921
  return { type: "unknown" };
2826
2922
  }
2827
- if (ts5.isTypeReferenceNode(node)) {
2828
- const raw = checker.getSymbolAtLocation(ts5.isQualifiedName(node.typeName) ? node.typeName.right : node.typeName);
2923
+ const nameNode = ts5.isTypeReferenceNode(node) ? node.typeName : ts5.isExpressionWithTypeArguments(node) && (ts5.isIdentifier(node.expression) || ts5.isPropertyAccessExpression(node.expression)) ? node.expression : undefined;
2924
+ if (nameNode) {
2925
+ const raw = checker.getSymbolAtLocation(ts5.isQualifiedName(nameNode) ? nameNode.right : ts5.isPropertyAccessExpression(nameNode) ? nameNode.name : nameNode);
2829
2926
  const symbol = resolvedSymbol(raw, checker);
2830
- const name = symbol?.getName() ?? node.typeName.getText();
2927
+ const name = symbol?.getName() ?? nameNode.getText();
2831
2928
  const args = node.typeArguments?.map((arg) => {
2832
2929
  if (typeNodeDefersExpansion(arg, checker, ctx?.program)) {
2833
2930
  return buildSchemaFromTypeNode(arg, checker, ctx);
@@ -2843,6 +2940,9 @@ function buildSchemaFromTypeNode(node, checker, ctx) {
2843
2940
  }
2844
2941
  return { ...schema, typeArguments: args };
2845
2942
  };
2943
+ if (symbol && symbol.flags & ts5.SymbolFlags.TypeParameter) {
2944
+ return { "x-ts-type": name };
2945
+ }
2846
2946
  if (name && isBuiltinGeneric(name) && (!symbol || isBuiltinSymbol(symbol))) {
2847
2947
  return withArgs({ ...builtinSchema(name) });
2848
2948
  }
@@ -2850,20 +2950,42 @@ function buildSchemaFromTypeNode(node, checker, ctx) {
2850
2950
  let refId = name;
2851
2951
  if (symbol && ctx) {
2852
2952
  try {
2853
- refId = namedRefId(checker.getDeclaredTypeOfSymbol(symbol), name, ctx);
2953
+ refId = typeRefId(checker.getDeclaredTypeOfSymbol(symbol), ctx) || (symbol.flags & ts5.SymbolFlags.TypeAlias ? resolveTypeId(symbol, ctx) : name);
2854
2954
  } catch {
2855
2955
  refId = name;
2856
2956
  }
2857
2957
  }
2958
+ if (ctx && symbol && symbol.flags & ts5.SymbolFlags.Alias && !ctx.typeRegistry.has(refId)) {
2959
+ ctx.typeRegistry.add({ id: refId, name, kind: "type", schema: { "x-ts-type": name } });
2960
+ }
2858
2961
  return withArgs({ $ref: `#/types/${refId}` });
2859
2962
  }
2860
- return { "x-ts-type": scrubImportQualifiers(node.getText()) };
2963
+ return {
2964
+ "x-ts-type": scrubImportQualifiers(node.getText().replace(/\s+/g, " "))
2965
+ };
2861
2966
  }
2862
2967
  const t = checker.getTypeFromTypeNode(node);
2863
2968
  if (!(t.flags & ts5.TypeFlags.Any)) {
2864
2969
  return buildSchema(t, checker, ctx);
2865
2970
  }
2866
- return { "x-ts-type": scrubImportQualifiers(node.getText()) };
2971
+ return { "x-ts-type": scrubImportQualifiers(node.getText().replace(/\s+/g, " ")) };
2972
+ }
2973
+ function openHeritageArms(declarations, checker, ctx) {
2974
+ return declarations.filter((decl) => ts5.isClassLike(decl) || ts5.isInterfaceDeclaration(decl)).flatMap((decl) => [...getExtendsExpressions(decl)]).flatMap((expr) => {
2975
+ const base = checker.getTypeAtLocation(expr);
2976
+ if (!(base.flags & ts5.TypeFlags.Any))
2977
+ return [];
2978
+ const registered = ctx?.typeRegistry.registerType(base, ctx);
2979
+ const written = checker.getSymbolAtLocation(expr.expression);
2980
+ const unresolvedImport = !!written && !!(written.flags & ts5.SymbolFlags.Alias) && resolvedSymbol(written, checker) === written;
2981
+ if (!registered && unresolvedImport) {
2982
+ return [{ "x-ts-type": scrubImportQualifiers(expr.getText()) }];
2983
+ }
2984
+ return [buildSchemaFromTypeNode(expr, checker, ctx)];
2985
+ });
2986
+ }
2987
+ function withOpenHeritage(schema, arms) {
2988
+ return arms.length > 0 ? { allOf: [schema, ...arms] } : schema;
2867
2989
  }
2868
2990
  function stripUndefinedFromType(type, checker) {
2869
2991
  if (!type.isUnion())
@@ -3047,7 +3169,20 @@ function typeNodeDefersExpansion(node, checker, program) {
3047
3169
  const symbol = resolveAliasSymbol(raw, checker, undefined, program);
3048
3170
  return shouldDeferAlias(symbol);
3049
3171
  }
3050
- function cheapTypeText(type, _checker, typeNode) {
3172
+ function isStaleTypeParameterNode(type, checker, typeNode) {
3173
+ if (type.flags & ts5.TypeFlags.TypeParameter)
3174
+ return false;
3175
+ if (!ts5.isTypeReferenceNode(typeNode) || !ts5.isIdentifier(typeNode.typeName))
3176
+ return false;
3177
+ const symbol = checker.getSymbolAtLocation(typeNode.typeName);
3178
+ return !!symbol && (symbol.flags & ts5.SymbolFlags.TypeParameter) !== 0;
3179
+ }
3180
+ function cheapTypeText(type, checker, typeNode) {
3181
+ if (typeNode && isStaleTypeParameterNode(type, checker, typeNode)) {
3182
+ const argument = type.aliasSymbol?.getName() ?? type.getSymbol()?.getName();
3183
+ if (argument && !argument.startsWith("__") && !isBuiltinGeneric(argument))
3184
+ return argument;
3185
+ }
3051
3186
  if (typeNode) {
3052
3187
  try {
3053
3188
  const text = scrubImportQualifiers(typeNode.getText().replace(/\s+/g, " ").trim());
@@ -3258,7 +3393,12 @@ function isUtilityOverTypeParameter(type) {
3258
3393
  const args = type.aliasTypeArguments;
3259
3394
  if (!args || args.length === 0)
3260
3395
  return false;
3261
- return args.some((t) => containsUnresolvedTypeParameter(t));
3396
+ if (!args.some((t) => containsUnresolvedTypeParameter(t)))
3397
+ return false;
3398
+ if (args[0].flags & ts5.TypeFlags.TypeParameter || !(type.flags & ts5.TypeFlags.Object)) {
3399
+ return true;
3400
+ }
3401
+ return type.getProperties().length === 0;
3262
3402
  }
3263
3403
  function writtenUtilityText(type, checker, typeNode) {
3264
3404
  let node = typeNode;
@@ -3338,10 +3478,18 @@ function buildSchema(type, checker, ctx, typeNode) {
3338
3478
  const schema = buildSchemaInternal(type, checker, ctx, typeNode);
3339
3479
  return ensureNonEmptySchema(schema, type, checker);
3340
3480
  }
3341
- function buildMaxDepthSchema(type, checker, typeNode) {
3481
+ function buildAliasBodySchema(type, checker, ctx) {
3482
+ ctx.aliasBody = type;
3483
+ try {
3484
+ return buildSchema(type, checker, ctx);
3485
+ } finally {
3486
+ ctx.aliasBody = undefined;
3487
+ }
3488
+ }
3489
+ function buildMaxDepthSchema(type, checker, typeNode, ctx) {
3342
3490
  if (type.flags & ts5.TypeFlags.Any) {
3343
3491
  if (typeNode)
3344
- return buildSchemaFromTypeNode(typeNode, checker);
3492
+ return buildSchemaFromTypeNode(typeNode, checker, ctx);
3345
3493
  return { "x-ts-type": checker.typeToString(type) };
3346
3494
  }
3347
3495
  if (type.flags & ts5.TypeFlags.TypeParameter && !isFluentThisType(type)) {
@@ -3354,7 +3502,7 @@ function buildMaxDepthSchema(type, checker, typeNode) {
3354
3502
  return builtinSchema(name);
3355
3503
  }
3356
3504
  if (!name.startsWith("__") && !isPrimitiveName(name)) {
3357
- return { $ref: `#/types/${name}` };
3505
+ return { $ref: `#/types/${ctx ? resolveTypeId(symbol, ctx) : name}` };
3358
3506
  }
3359
3507
  }
3360
3508
  if (type.flags & ts5.TypeFlags.String)
@@ -3377,22 +3525,53 @@ function buildMaxDepthSchema(type, checker, typeNode) {
3377
3525
  };
3378
3526
  }
3379
3527
  if (type.isUnion()) {
3380
- const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker));
3528
+ const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker, undefined, ctx));
3381
3529
  return { anyOf: schemas };
3382
3530
  }
3383
3531
  if (type.isIntersection()) {
3384
- const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker));
3532
+ const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker, undefined, ctx));
3385
3533
  return { allOf: schemas };
3386
3534
  }
3387
3535
  return { type: checker.typeToString(type) };
3388
3536
  }
3537
+ function writtenTypeArguments(typeNode, symbol, checker) {
3538
+ if (!typeNode || !symbol || !ts5.isTypeReferenceNode(typeNode) || !typeNode.typeArguments) {
3539
+ return;
3540
+ }
3541
+ const name = ts5.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right : typeNode.typeName;
3542
+ const written = resolvedSymbol(checker.getSymbolAtLocation(name), checker);
3543
+ return written === symbol ? typeNode.typeArguments : undefined;
3544
+ }
3545
+ function genericAliasRef(type, checker, ctx, typeNode) {
3546
+ const aliasTypeArgs = type.aliasTypeArguments;
3547
+ const name = type.aliasSymbol?.getName();
3548
+ if (!name || !aliasTypeArgs?.length)
3549
+ return;
3550
+ if (name.startsWith("__") || BUILTIN_TYPES.has(name) || isBuiltinGeneric(name))
3551
+ return;
3552
+ const argNodes = writtenTypeArguments(typeNode, type.aliasSymbol, checker);
3553
+ const build = () => {
3554
+ const schema = {
3555
+ $ref: `#/types/${namedRefId(type, name, ctx)}`,
3556
+ typeArguments: aliasTypeArgs.map((t, i) => buildSchema(t, checker, ctx, argNodes?.[i]))
3557
+ };
3558
+ const packageOrigin = getTypeOrigin(type, checker);
3559
+ if (packageOrigin) {
3560
+ setSchemaExtension(schema, "x-ts-package", packageOrigin);
3561
+ }
3562
+ return schema;
3563
+ };
3564
+ return ctx ? withDepth(ctx, build) : build();
3565
+ }
3389
3566
  function buildSchemaInternal(type, checker, ctx, typeNode) {
3390
3567
  if (isAtMaxDepth(ctx)) {
3391
- return buildMaxDepthSchema(type, checker, typeNode);
3568
+ return buildMaxDepthSchema(type, checker, typeNode, ctx);
3392
3569
  }
3393
3570
  if (ctx) {
3394
3571
  ctx.schemaOps += 1;
3395
- if (ctx.schemaOps > ctx.maxSchemaOps) {
3572
+ ctx.budget.ops += 1;
3573
+ if (ctx.budget.ops > ctx.maxBudgetOps || ctx.schemaOps > ctx.maxSchemaOps) {
3574
+ ctx.budget.exceeded = true;
3396
3575
  ctx.budgetExceeded = true;
3397
3576
  return { "x-ts-type": cheapTypeText(type, checker, typeNode) };
3398
3577
  }
@@ -3486,6 +3665,14 @@ function buildSchemaInternal(type, checker, ctx, typeNode) {
3486
3665
  return schema;
3487
3666
  }
3488
3667
  }
3668
+ if (type.isUnion() || type.isIntersection()) {
3669
+ const ownBody = ctx?.aliasBody === type;
3670
+ if (ownBody && ctx)
3671
+ ctx.aliasBody = undefined;
3672
+ const aliasRef = ownBody ? undefined : genericAliasRef(type, checker, ctx, typeNode);
3673
+ if (aliasRef)
3674
+ return aliasRef;
3675
+ }
3489
3676
  if (type.flags & ts5.TypeFlags.TemplateLiteral) {
3490
3677
  return {
3491
3678
  type: "string",
@@ -3610,11 +3797,12 @@ function buildSchemaInternal(type, checker, ctx, typeNode) {
3610
3797
  }
3611
3798
  if (name && !isAnonymous(typeRef.target)) {
3612
3799
  const packageOrigin = getTypeOrigin(typeRef.target, checker);
3800
+ const argNodes = writtenTypeArguments(typeNode, symbol2, checker);
3613
3801
  if (ctx) {
3614
3802
  return withDepth(ctx, () => {
3615
3803
  const schema2 = {
3616
3804
  $ref: `#/types/${namedRefId(typeRef.target, name, ctx)}`,
3617
- typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
3805
+ typeArguments: typeArgs.map((t, i) => buildSchema(t, checker, ctx, argNodes?.[i]))
3618
3806
  };
3619
3807
  if (packageOrigin) {
3620
3808
  setSchemaExtension(schema2, "x-ts-package", packageOrigin);
@@ -3653,29 +3841,9 @@ function buildSchemaInternal(type, checker, ctx, typeNode) {
3653
3841
  });
3654
3842
  return ctx ? withDepth(ctx, build) : build();
3655
3843
  }
3656
- if (!name.startsWith("__")) {
3657
- const packageOrigin = getTypeOrigin(type, checker);
3658
- if (ctx) {
3659
- return withDepth(ctx, () => {
3660
- const schema2 = {
3661
- $ref: `#/types/${namedRefId(type, name, ctx)}`,
3662
- typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
3663
- };
3664
- if (packageOrigin) {
3665
- setSchemaExtension(schema2, "x-ts-package", packageOrigin);
3666
- }
3667
- return schema2;
3668
- });
3669
- }
3670
- const schema = {
3671
- $ref: `#/types/${name}`,
3672
- typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
3673
- };
3674
- if (packageOrigin) {
3675
- setSchemaExtension(schema, "x-ts-package", packageOrigin);
3676
- }
3677
- return schema;
3678
- }
3844
+ const aliasRef = genericAliasRef(type, checker, ctx, typeNode);
3845
+ if (aliasRef)
3846
+ return aliasRef;
3679
3847
  }
3680
3848
  if (type.flags & ts5.TypeFlags.Object) {
3681
3849
  const callSignatures = type.getCallSignatures();
@@ -3940,219 +4108,7 @@ function findDiscriminatorProperty(unionTypes, checker) {
3940
4108
  return;
3941
4109
  }
3942
4110
 
3943
- // src/types/parameters.ts
3944
- function extractParameters(signature, ctx) {
3945
- const { typeChecker: checker } = ctx;
3946
- const result = [];
3947
- const signatureDecl = signature.getDeclaration();
3948
- const jsdocTags = signatureDecl ? ts6.getJSDocTags(signatureDecl) : [];
3949
- for (const param of signature.getParameters()) {
3950
- const decl = param.valueDeclaration;
3951
- if (!decl)
3952
- continue;
3953
- const defer = typeNodeDefersExpansion(decl.type, checker, ctx.program);
3954
- const type = defer ? undefined : checker.getTypeOfSymbolAtLocation(param, decl);
3955
- if (decl && ts6.isObjectBindingPattern(decl.name)) {
3956
- const expandedParams = expandBindingPattern(decl, type ?? checker.getTypeOfSymbolAtLocation(param, decl), jsdocTags, ctx);
3957
- result.push(...expandedParams);
3958
- } else {
3959
- const isOptional = !!decl?.questionToken || !!decl?.initializer;
3960
- const isRest = !!decl.dotDotDotToken;
3961
- const paramName = param.getName();
3962
- const description = getParamDescription(paramName, jsdocTags);
3963
- const schema = defer ? buildSchemaFromTypeNode(decl.type, checker, ctx) : buildSchema(isOptional ? stripUndefinedFromType(type, checker) : type, checker, ctx, decl.type);
3964
- if (!defer && type) {
3965
- registerReferencedTypes(isOptional ? stripUndefinedFromType(type, checker) : type, ctx);
3966
- }
3967
- const paramResult = {
3968
- name: paramName,
3969
- schema,
3970
- required: !isOptional && !isRest,
3971
- ...isRest ? { rest: true } : {}
3972
- };
3973
- if (description) {
3974
- paramResult.description = description;
3975
- const inlineTags = parseInlineTags(description);
3976
- if (inlineTags)
3977
- paramResult.inlineTags = inlineTags;
3978
- }
3979
- if (decl.initializer) {
3980
- applyDefault(paramResult, decl.initializer);
3981
- }
3982
- result.push(paramResult);
3983
- }
3984
- }
3985
- return result;
3986
- }
3987
- function expandBindingPattern(paramDecl, paramType, jsdocTags, ctx) {
3988
- const { typeChecker: checker } = ctx;
3989
- const result = [];
3990
- const bindingPattern = paramDecl.name;
3991
- const allProperties = getEffectiveProperties(paramType, checker);
3992
- const inferredAlias = inferParamAlias(jsdocTags);
3993
- for (const element of bindingPattern.elements) {
3994
- if (!ts6.isBindingElement(element))
3995
- continue;
3996
- const propertyName = element.propertyName ? ts6.isIdentifier(element.propertyName) ? element.propertyName.text : element.propertyName.getText() : ts6.isIdentifier(element.name) ? element.name.text : element.name.getText();
3997
- const propSymbol = allProperties.get(propertyName);
3998
- if (!propSymbol)
3999
- continue;
4000
- const isOptional = !!(propSymbol.flags & ts6.SymbolFlags.Optional) || element.initializer !== undefined;
4001
- const propType = checker.getTypeOfSymbol(propSymbol);
4002
- const effectiveType = isOptional ? stripUndefinedFromType(propType, checker) : propType;
4003
- registerReferencedTypes(effectiveType, ctx);
4004
- const description = getParamDescription(propertyName, jsdocTags, inferredAlias);
4005
- const param = {
4006
- name: propertyName,
4007
- schema: buildSchema(effectiveType, checker, ctx, declaredTypeNode(propSymbol.valueDeclaration)),
4008
- required: !isOptional
4009
- };
4010
- if (description) {
4011
- param.description = description;
4012
- const inlineTags = parseInlineTags(description);
4013
- if (inlineTags)
4014
- param.inlineTags = inlineTags;
4015
- }
4016
- if (element.initializer) {
4017
- applyDefault(param, element.initializer);
4018
- }
4019
- result.push(param);
4020
- }
4021
- return result;
4022
- }
4023
- function getEffectiveProperties(type, _checker) {
4024
- const properties = new Map;
4025
- if (type.isIntersection()) {
4026
- for (const subType of type.types) {
4027
- for (const prop of subType.getProperties()) {
4028
- properties.set(prop.getName(), prop);
4029
- }
4030
- }
4031
- } else {
4032
- for (const prop of type.getProperties()) {
4033
- properties.set(prop.getName(), prop);
4034
- }
4035
- }
4036
- return properties;
4037
- }
4038
- function inferParamAlias(jsdocTags) {
4039
- const prefixes = [];
4040
- for (const tag of jsdocTags) {
4041
- if (tag.tagName.text !== "param")
4042
- continue;
4043
- const tagText = typeof tag.comment === "string" ? tag.comment : ts6.getTextOfJSDocComment(tag.comment) ?? "";
4044
- const paramTag = tag;
4045
- const paramName = paramTag.name?.getText() ?? "";
4046
- if (paramName.includes(".")) {
4047
- const prefix = paramName.split(".")[0];
4048
- if (prefix && !prefix.startsWith("__")) {
4049
- prefixes.push(prefix);
4050
- }
4051
- } else if (tagText.includes(".")) {
4052
- const match = tagText.match(/^(\w+)\./);
4053
- if (match && !match[1].startsWith("__")) {
4054
- prefixes.push(match[1]);
4055
- }
4056
- }
4057
- }
4058
- if (prefixes.length === 0)
4059
- return;
4060
- const counts = new Map;
4061
- for (const p of prefixes)
4062
- counts.set(p, (counts.get(p) ?? 0) + 1);
4063
- return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0];
4064
- }
4065
- function extractLiteralDefault(initializer) {
4066
- if (ts6.isStringLiteral(initializer)) {
4067
- return { literal: true, value: initializer.text };
4068
- }
4069
- if (ts6.isNumericLiteral(initializer)) {
4070
- return { literal: true, value: Number(initializer.text) };
4071
- }
4072
- if (ts6.isPrefixUnaryExpression(initializer) && initializer.operator === ts6.SyntaxKind.MinusToken && ts6.isNumericLiteral(initializer.operand)) {
4073
- return { literal: true, value: -Number(initializer.operand.text) };
4074
- }
4075
- if (initializer.kind === ts6.SyntaxKind.TrueKeyword) {
4076
- return { literal: true, value: true };
4077
- }
4078
- if (initializer.kind === ts6.SyntaxKind.FalseKeyword) {
4079
- return { literal: true, value: false };
4080
- }
4081
- if (initializer.kind === ts6.SyntaxKind.NullKeyword) {
4082
- return { literal: true, value: null };
4083
- }
4084
- return { literal: false, text: initializer.getText() };
4085
- }
4086
- function applyDefault(param, initializer) {
4087
- const extracted = extractLiteralDefault(initializer);
4088
- if (extracted.literal) {
4089
- param.default = extracted.value;
4090
- if (param.schema && typeof param.schema === "object" && !Array.isArray(param.schema)) {
4091
- param.schema.default = extracted.value;
4092
- }
4093
- } else {
4094
- param.default = extracted.text;
4095
- if (param.schema && typeof param.schema === "object" && !Array.isArray(param.schema)) {
4096
- param.schema["x-ts-default"] = extracted.text;
4097
- }
4098
- }
4099
- }
4100
- function registerReferencedTypes(type, ctx, depth = 0) {
4101
- if (depth > ctx.maxTypeDepth)
4102
- return;
4103
- if (ctx.registeredTypes.has(type))
4104
- return;
4105
- const isPrimitive = type.flags & (ts6.TypeFlags.String | ts6.TypeFlags.Number | ts6.TypeFlags.Boolean | ts6.TypeFlags.Void | ts6.TypeFlags.Undefined | ts6.TypeFlags.Null | ts6.TypeFlags.Any | ts6.TypeFlags.Unknown | ts6.TypeFlags.Never | ts6.TypeFlags.StringLiteral | ts6.TypeFlags.NumberLiteral | ts6.TypeFlags.BooleanLiteral);
4106
- if (!isPrimitive) {
4107
- ctx.registeredTypes.add(type);
4108
- }
4109
- const { typeChecker: checker, typeRegistry } = ctx;
4110
- typeRegistry.registerType(type, ctx);
4111
- const typeArgs = type.typeArguments;
4112
- if (typeArgs) {
4113
- for (const arg of typeArgs) {
4114
- registerReferencedTypes(arg, ctx, depth + 1);
4115
- }
4116
- }
4117
- if (type.isUnion()) {
4118
- for (const t of type.types) {
4119
- registerReferencedTypes(t, ctx, depth + 1);
4120
- }
4121
- }
4122
- if (type.isIntersection()) {
4123
- for (const t of type.types) {
4124
- registerReferencedTypes(t, ctx, depth + 1);
4125
- }
4126
- }
4127
- const typeSymbol = type.aliasSymbol ?? type.getSymbol();
4128
- if (typeSymbol && ctx.shouldExpandExternal && !typeSymbol.getName().startsWith("__") && !ctx.shouldExpandExternal(typeSymbol)) {
4129
- return;
4130
- }
4131
- if (isForeignPackage(typeSymbol, ctx.workspacePackages)) {
4132
- return;
4133
- }
4134
- if (isDeferredMappedOrConditional(type)) {
4135
- return;
4136
- }
4137
- if (type.flags & ts6.TypeFlags.Object) {
4138
- const props = type.getProperties();
4139
- const limit = ctx.maxProperties;
4140
- if (props.length > limit && ctx.onTruncation) {
4141
- const typeName = type.getSymbol()?.getName() ?? "anonymous";
4142
- ctx.onTruncation(typeName, props.length, limit);
4143
- }
4144
- for (const prop of props.slice(0, limit)) {
4145
- const propType = checker.getTypeOfSymbol(prop);
4146
- registerReferencedTypes(propType, ctx, depth + 1);
4147
- }
4148
- }
4149
- }
4150
-
4151
- // src/serializers/context.ts
4152
- import ts8 from "typescript";
4153
-
4154
4111
  // src/ast/registry.ts
4155
- import ts7 from "typescript";
4156
4112
  var BUILTINS = new Set([
4157
4113
  "Array",
4158
4114
  "ArrayBuffer",
@@ -4229,6 +4185,26 @@ function isExternalType(decl) {
4229
4185
  return false;
4230
4186
  return sourceFile.fileName.includes("node_modules");
4231
4187
  }
4188
+ function declaredForm(type, checker, symbol = type.aliasSymbol ?? type.getSymbol()) {
4189
+ if (!symbol)
4190
+ return type;
4191
+ const generic = ts6.SymbolFlags.TypeAlias | ts6.SymbolFlags.Interface | ts6.SymbolFlags.Class;
4192
+ if (!(symbol.flags & generic))
4193
+ return type;
4194
+ const target = type.target;
4195
+ const instantiated = type.aliasTypeArguments?.length || target && target !== type;
4196
+ return instantiated ? declaredTypeOf(symbol, type, checker) : type;
4197
+ }
4198
+ function declaredTypeOf(symbol, fallback, checker) {
4199
+ const declared = checker.getDeclaredTypeOfSymbol(symbol);
4200
+ return declared.flags & ts6.TypeFlags.Any ? fallback : declared;
4201
+ }
4202
+ function registeredForm(type, symbol, checker) {
4203
+ const constructorSide = symbol.flags & ts6.SymbolFlags.Class && type.objectFlags & ts6.ObjectFlags.Anonymous;
4204
+ if (constructorSide)
4205
+ return declaredTypeOf(symbol, type, checker);
4206
+ return declaredForm(type, checker, symbol);
4207
+ }
4232
4208
 
4233
4209
  class TypeRegistry {
4234
4210
  types = new Map;
@@ -4262,13 +4238,13 @@ class TypeRegistry {
4262
4238
  return;
4263
4239
  if (name.startsWith('"'))
4264
4240
  return;
4265
- if (symbol.flags & ts7.SymbolFlags.EnumMember)
4241
+ if (symbol.flags & ts6.SymbolFlags.EnumMember)
4266
4242
  return;
4267
- if (symbol.flags & ts7.SymbolFlags.TypeParameter)
4243
+ if (symbol.flags & ts6.SymbolFlags.TypeParameter)
4268
4244
  return;
4269
- if (symbol.flags & ts7.SymbolFlags.Method)
4245
+ if (symbol.flags & ts6.SymbolFlags.Method)
4270
4246
  return;
4271
- if (symbol.flags & ts7.SymbolFlags.Function)
4247
+ if (symbol.flags & ts6.SymbolFlags.Function)
4272
4248
  return;
4273
4249
  if (isGenericTypeParameter(name))
4274
4250
  return;
@@ -4301,7 +4277,7 @@ class TypeRegistry {
4301
4277
  return id;
4302
4278
  this.processing.add(id);
4303
4279
  try {
4304
- const specType = this.buildSpecType(type, symbol, id, ctx);
4280
+ const specType = withExpansionBudget(ctx, `type ${id}`, () => this.buildSpecType(registeredForm(type, symbol, ctx.typeChecker), symbol, id, ctx));
4305
4281
  if (specType) {
4306
4282
  this.add(specType);
4307
4283
  return specType.id;
@@ -4318,17 +4294,17 @@ class TypeRegistry {
4318
4294
  let kind = "type";
4319
4295
  const external = decl ? isExternalType(decl) : false;
4320
4296
  if (decl) {
4321
- if (ts7.isClassDeclaration(decl))
4297
+ if (ts6.isClassDeclaration(decl))
4322
4298
  kind = "class";
4323
- else if (ts7.isInterfaceDeclaration(decl))
4299
+ else if (ts6.isInterfaceDeclaration(decl))
4324
4300
  kind = "interface";
4325
- else if (ts7.isEnumDeclaration(decl))
4301
+ else if (ts6.isEnumDeclaration(decl))
4326
4302
  kind = "enum";
4327
4303
  }
4328
4304
  if (external) {
4329
4305
  kind = "external";
4330
4306
  }
4331
- let schema = buildSchema(type, checker, ctx);
4307
+ let schema = buildAliasBodySchema(type, checker, ctx);
4332
4308
  if (this.isSelfRef(schema, id)) {
4333
4309
  schema = this.resolveSelRefSchema(type, checker, ctx);
4334
4310
  }
@@ -4338,8 +4314,8 @@ class TypeRegistry {
4338
4314
  schema = enumSchema;
4339
4315
  }
4340
4316
  }
4341
- if (kind === "type" && decl && ts7.isTypeAliasDeclaration(decl) && shouldEmitAliasTypeText(decl.type) && typeof schema === "object" && schema !== null && !("x-ts-type" in schema)) {
4342
- const text = renderTypeText(type, checker, decl, ts7.TypeFormatFlags.InTypeAlias);
4317
+ if (kind === "type" && decl && ts6.isTypeAliasDeclaration(decl) && shouldEmitAliasTypeText(decl.type) && typeof schema === "object" && schema !== null && !("x-ts-type" in schema)) {
4318
+ const text = renderTypeText(type, checker, decl, ts6.TypeFormatFlags.InTypeAlias);
4343
4319
  if (!PRIMITIVES.has(text) && text !== name) {
4344
4320
  schema["x-ts-type"] = text;
4345
4321
  const declared = writtenTypeText(declaredTypeNode(decl));
@@ -4348,8 +4324,11 @@ class TypeRegistry {
4348
4324
  }
4349
4325
  }
4350
4326
  }
4327
+ const heritage = (symbol.declarations ?? []).filter((d) => ts6.isClassDeclaration(d) || ts6.isInterfaceDeclaration(d));
4328
+ const extendsText = heritage.map((d) => getExtendsText(d, checker)).find(Boolean);
4329
+ schema = withOpenHeritage(schema, openHeritageArms(heritage, checker, ctx));
4351
4330
  let typeParameters;
4352
- if (decl && (ts7.isTypeAliasDeclaration(decl) || ts7.isInterfaceDeclaration(decl) || ts7.isClassDeclaration(decl))) {
4331
+ if (decl && (ts6.isTypeAliasDeclaration(decl) || ts6.isInterfaceDeclaration(decl) || ts6.isClassDeclaration(decl))) {
4353
4332
  typeParameters = extractTypeParameters(decl, checker);
4354
4333
  }
4355
4334
  return {
@@ -4358,6 +4337,7 @@ class TypeRegistry {
4358
4337
  kind,
4359
4338
  ...typeParameters && typeParameters.length > 0 ? { typeParameters } : {},
4360
4339
  schema,
4340
+ ...extendsText ? { extends: extendsText } : {},
4361
4341
  ...external ? { external: true } : {}
4362
4342
  };
4363
4343
  }
@@ -4370,14 +4350,14 @@ class TypeRegistry {
4370
4350
  resolveSelRefSchema(type, checker, ctx) {
4371
4351
  if (type.isUnion()) {
4372
4352
  const types = type.types;
4373
- const allStringLiterals = types.every((t) => t.flags & ts7.TypeFlags.StringLiteral);
4353
+ const allStringLiterals = types.every((t) => t.flags & ts6.TypeFlags.StringLiteral);
4374
4354
  if (allStringLiterals) {
4375
4355
  return {
4376
4356
  type: "string",
4377
4357
  enum: types.map((t) => t.value)
4378
4358
  };
4379
4359
  }
4380
- const allNumberLiterals = types.every((t) => t.flags & ts7.TypeFlags.NumberLiteral);
4360
+ const allNumberLiterals = types.every((t) => t.flags & ts6.TypeFlags.NumberLiteral);
4381
4361
  if (allNumberLiterals) {
4382
4362
  return {
4383
4363
  type: "number",
@@ -4395,126 +4375,343 @@ class TypeRegistry {
4395
4375
  return enumSchema;
4396
4376
  }
4397
4377
  }
4398
- const callSignatures = type.getCallSignatures();
4399
- if (callSignatures.length > 0 && type.getProperties().length === 0) {
4400
- return buildFunctionSchema(callSignatures, checker, ctx);
4378
+ const callSignatures = type.getCallSignatures();
4379
+ if (callSignatures.length > 0 && type.getProperties().length === 0) {
4380
+ return buildFunctionSchema(callSignatures, checker, ctx);
4381
+ }
4382
+ if (checker.isTupleType(type)) {
4383
+ const elementTypes = checker.getTypeArguments(type) ?? [];
4384
+ return {
4385
+ type: "array",
4386
+ prefixItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
4387
+ minItems: elementTypes.length,
4388
+ maxItems: elementTypes.length
4389
+ };
4390
+ }
4391
+ if (checker.isArrayType(type)) {
4392
+ const elementType = checker.getTypeArguments(type)?.[0];
4393
+ return elementType ? { type: "array", items: buildSchema(elementType, checker, ctx) } : { type: "array" };
4394
+ }
4395
+ if (type.flags & ts6.TypeFlags.Conditional) {
4396
+ return { "x-ts-type": renderTypeText(type, checker) };
4397
+ }
4398
+ const constraint = checker.getBaseConstraintOfType(type);
4399
+ const primitive = constraint && constraint.flags & ts6.TypeFlags.StringLike ? "string" : constraint && constraint.flags & ts6.TypeFlags.NumberLike ? "number" : constraint && constraint.flags & ts6.TypeFlags.BooleanLike ? "boolean" : undefined;
4400
+ if (primitive) {
4401
+ return { type: primitive, "x-ts-type": renderTypeText(type, checker) };
4402
+ }
4403
+ return this.buildObjectSchemaFromProperties(type, checker, ctx);
4404
+ }
4405
+ buildEnumSchema(symbol, checker) {
4406
+ const decl = symbol.declarations?.find(ts6.isEnumDeclaration);
4407
+ if (!decl)
4408
+ return;
4409
+ const members = [];
4410
+ for (const member of decl.members) {
4411
+ const memberSymbol = checker.getSymbolAtLocation(member.name);
4412
+ if (memberSymbol) {
4413
+ const constantValue = checker.getConstantValue(member);
4414
+ if (constantValue !== undefined) {
4415
+ members.push({
4416
+ name: memberSymbol.getName(),
4417
+ value: constantValue
4418
+ });
4419
+ }
4420
+ }
4421
+ }
4422
+ if (members.length === 0)
4423
+ return;
4424
+ return {
4425
+ type: typeof members[0].value === "string" ? "string" : "number",
4426
+ enum: members.map((m) => m.value),
4427
+ "x-enum-members": members
4428
+ };
4429
+ }
4430
+ buildObjectSchemaFromProperties(type, checker, ctx) {
4431
+ const properties = type.getProperties();
4432
+ const indexInfos = checker.getIndexInfosOfType(type);
4433
+ const stringIndex = indexInfos.find((i) => i.keyType.flags & ts6.TypeFlags.String);
4434
+ const numberIndex = indexInfos.find((i) => i.keyType.flags & ts6.TypeFlags.Number);
4435
+ if (properties.length === 0 && !stringIndex && !numberIndex) {
4436
+ return { type: checker.typeToString(type) };
4437
+ }
4438
+ const props = {};
4439
+ const required = [];
4440
+ const limit = ctx.maxProperties;
4441
+ const isArrayLike = checker.isArrayType(type) || checker.isTupleType(type) || type.symbol?.getName() === "Array" && isLibFile(type.symbol?.getDeclarations()?.[0]?.getSourceFile()?.fileName ?? "");
4442
+ const isStringLike = type.flags & ts6.TypeFlags.StringLike;
4443
+ const isNumberLike = type.flags & ts6.TypeFlags.NumberLike;
4444
+ const included = properties.filter((prop) => {
4445
+ const propName = prop.getName();
4446
+ if (propName.startsWith("__@"))
4447
+ return false;
4448
+ if (isArrayLike && ARRAY_PROTOTYPE_METHODS.has(propName))
4449
+ return false;
4450
+ if (isStringLike && STRING_PROTOTYPE_METHODS.has(propName))
4451
+ return false;
4452
+ if (isNumberLike && NUMBER_PROTOTYPE_METHODS.has(propName))
4453
+ return false;
4454
+ return true;
4455
+ });
4456
+ if (included.length > limit && ctx.onTruncation) {
4457
+ const typeName = type.getSymbol()?.getName() ?? "anonymous";
4458
+ ctx.onTruncation(typeName, included.length, limit);
4459
+ }
4460
+ for (const prop of included.slice(0, limit)) {
4461
+ const propName = prop.getName();
4462
+ const rawPropType = checker.getTypeOfSymbol(prop);
4463
+ const propType = prop.flags & ts6.SymbolFlags.Optional ? stripUndefinedFromType(rawPropType, checker) : rawPropType;
4464
+ this.registerType(propType, ctx);
4465
+ let propSchema = buildSchema(propType, checker, ctx, declaredTypeNode(prop.valueDeclaration ?? prop.getDeclarations()?.[0]));
4466
+ const docComment = prop.getDocumentationComment(checker);
4467
+ if (docComment.length > 0) {
4468
+ const description = docComment.map((c) => c.text).join(`
4469
+ `);
4470
+ if (description.trim()) {
4471
+ propSchema = withDescription(propSchema, description);
4472
+ }
4473
+ }
4474
+ const { deprecated, reason } = isSymbolDeprecated(prop);
4475
+ if (deprecated) {
4476
+ propSchema = withDeprecated(propSchema, reason);
4477
+ }
4478
+ propSchema = decoratePropertySchema(propSchema, prop, propType, checker);
4479
+ props[propName] = propSchema;
4480
+ if (!(prop.flags & ts6.SymbolFlags.Optional)) {
4481
+ required.push(propName);
4482
+ }
4483
+ }
4484
+ return {
4485
+ type: "object",
4486
+ properties: props,
4487
+ ...required.length > 0 ? { required } : {},
4488
+ ...stringIndex ? { additionalProperties: buildSchema(stringIndex.type, checker, ctx) } : {},
4489
+ ...numberIndex ? {
4490
+ patternProperties: { "^\\d+$": buildSchema(numberIndex.type, checker, ctx) },
4491
+ "x-ts-index-key": "number"
4492
+ } : {}
4493
+ };
4494
+ }
4495
+ }
4496
+
4497
+ // src/types/parameters.ts
4498
+ function extractParameters(signature, ctx) {
4499
+ const { typeChecker: checker } = ctx;
4500
+ const result = [];
4501
+ const signatureDecl = signature.getDeclaration();
4502
+ const jsdocTags = signatureDecl ? ts7.getJSDocTags(signatureDecl) : [];
4503
+ for (const param of signature.getParameters()) {
4504
+ const decl = param.valueDeclaration;
4505
+ if (!decl)
4506
+ continue;
4507
+ const defer = typeNodeDefersExpansion(decl.type, checker, ctx.program);
4508
+ const type = defer ? undefined : checker.getTypeOfSymbolAtLocation(param, decl);
4509
+ if (decl && ts7.isObjectBindingPattern(decl.name)) {
4510
+ const expandedParams = expandBindingPattern(decl, type ?? checker.getTypeOfSymbolAtLocation(param, decl), jsdocTags, ctx);
4511
+ result.push(...expandedParams);
4512
+ } else {
4513
+ const isOptional = !!decl?.questionToken || !!decl?.initializer;
4514
+ const isRest = !!decl.dotDotDotToken;
4515
+ const paramName = param.getName();
4516
+ const description = getParamDescription(paramName, jsdocTags);
4517
+ const schema = defer ? buildSchemaFromTypeNode(decl.type, checker, ctx) : buildSchema(isOptional ? stripUndefinedFromType(type, checker) : type, checker, ctx, decl.type);
4518
+ if (!defer && type) {
4519
+ registerReferencedTypes(isOptional ? stripUndefinedFromType(type, checker) : type, ctx);
4520
+ }
4521
+ const paramResult = {
4522
+ name: paramName,
4523
+ schema,
4524
+ required: !isOptional && !isRest,
4525
+ ...isRest ? { rest: true } : {}
4526
+ };
4527
+ if (description) {
4528
+ paramResult.description = description;
4529
+ const inlineTags = parseInlineTags(description);
4530
+ if (inlineTags)
4531
+ paramResult.inlineTags = inlineTags;
4532
+ }
4533
+ if (decl.initializer) {
4534
+ applyDefault(paramResult, decl.initializer);
4535
+ }
4536
+ result.push(paramResult);
4537
+ }
4538
+ }
4539
+ return result;
4540
+ }
4541
+ function expandBindingPattern(paramDecl, paramType, jsdocTags, ctx) {
4542
+ const { typeChecker: checker } = ctx;
4543
+ const result = [];
4544
+ const bindingPattern = paramDecl.name;
4545
+ const allProperties = getEffectiveProperties(paramType, checker);
4546
+ const inferredAlias = inferParamAlias(jsdocTags);
4547
+ for (const element of bindingPattern.elements) {
4548
+ if (!ts7.isBindingElement(element))
4549
+ continue;
4550
+ const propertyName = element.propertyName ? ts7.isIdentifier(element.propertyName) ? element.propertyName.text : element.propertyName.getText() : ts7.isIdentifier(element.name) ? element.name.text : element.name.getText();
4551
+ const propSymbol = allProperties.get(propertyName);
4552
+ if (!propSymbol)
4553
+ continue;
4554
+ const isOptional = !!(propSymbol.flags & ts7.SymbolFlags.Optional) || element.initializer !== undefined;
4555
+ const propType = checker.getTypeOfSymbol(propSymbol);
4556
+ const effectiveType = isOptional ? stripUndefinedFromType(propType, checker) : propType;
4557
+ registerReferencedTypes(effectiveType, ctx);
4558
+ const description = getParamDescription(propertyName, jsdocTags, inferredAlias);
4559
+ const param = {
4560
+ name: propertyName,
4561
+ schema: buildSchema(effectiveType, checker, ctx, declaredTypeNode(propSymbol.valueDeclaration)),
4562
+ required: !isOptional
4563
+ };
4564
+ if (description) {
4565
+ param.description = description;
4566
+ const inlineTags = parseInlineTags(description);
4567
+ if (inlineTags)
4568
+ param.inlineTags = inlineTags;
4569
+ }
4570
+ if (element.initializer) {
4571
+ applyDefault(param, element.initializer);
4572
+ }
4573
+ result.push(param);
4574
+ }
4575
+ return result;
4576
+ }
4577
+ function getEffectiveProperties(type, _checker) {
4578
+ const properties = new Map;
4579
+ if (type.isIntersection()) {
4580
+ for (const subType of type.types) {
4581
+ for (const prop of subType.getProperties()) {
4582
+ properties.set(prop.getName(), prop);
4583
+ }
4584
+ }
4585
+ } else {
4586
+ for (const prop of type.getProperties()) {
4587
+ properties.set(prop.getName(), prop);
4401
4588
  }
4402
- if (checker.isTupleType(type)) {
4403
- const elementTypes = checker.getTypeArguments(type) ?? [];
4404
- return {
4405
- type: "array",
4406
- prefixItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
4407
- minItems: elementTypes.length,
4408
- maxItems: elementTypes.length
4409
- };
4589
+ }
4590
+ return properties;
4591
+ }
4592
+ function inferParamAlias(jsdocTags) {
4593
+ const prefixes = [];
4594
+ for (const tag of jsdocTags) {
4595
+ if (tag.tagName.text !== "param")
4596
+ continue;
4597
+ const tagText = typeof tag.comment === "string" ? tag.comment : ts7.getTextOfJSDocComment(tag.comment) ?? "";
4598
+ const paramTag = tag;
4599
+ const paramName = paramTag.name?.getText() ?? "";
4600
+ if (paramName.includes(".")) {
4601
+ const prefix = paramName.split(".")[0];
4602
+ if (prefix && !prefix.startsWith("__")) {
4603
+ prefixes.push(prefix);
4604
+ }
4605
+ } else if (tagText.includes(".")) {
4606
+ const match = tagText.match(/^(\w+)\./);
4607
+ if (match && !match[1].startsWith("__")) {
4608
+ prefixes.push(match[1]);
4609
+ }
4410
4610
  }
4411
- if (checker.isArrayType(type)) {
4412
- const elementType = checker.getTypeArguments(type)?.[0];
4413
- return elementType ? { type: "array", items: buildSchema(elementType, checker, ctx) } : { type: "array" };
4611
+ }
4612
+ if (prefixes.length === 0)
4613
+ return;
4614
+ const counts = new Map;
4615
+ for (const p of prefixes)
4616
+ counts.set(p, (counts.get(p) ?? 0) + 1);
4617
+ return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0];
4618
+ }
4619
+ function extractLiteralDefault(initializer) {
4620
+ if (ts7.isStringLiteral(initializer)) {
4621
+ return { literal: true, value: initializer.text };
4622
+ }
4623
+ if (ts7.isNumericLiteral(initializer)) {
4624
+ return { literal: true, value: Number(initializer.text) };
4625
+ }
4626
+ if (ts7.isPrefixUnaryExpression(initializer) && initializer.operator === ts7.SyntaxKind.MinusToken && ts7.isNumericLiteral(initializer.operand)) {
4627
+ return { literal: true, value: -Number(initializer.operand.text) };
4628
+ }
4629
+ if (initializer.kind === ts7.SyntaxKind.TrueKeyword) {
4630
+ return { literal: true, value: true };
4631
+ }
4632
+ if (initializer.kind === ts7.SyntaxKind.FalseKeyword) {
4633
+ return { literal: true, value: false };
4634
+ }
4635
+ if (initializer.kind === ts7.SyntaxKind.NullKeyword) {
4636
+ return { literal: true, value: null };
4637
+ }
4638
+ return { literal: false, text: initializer.getText() };
4639
+ }
4640
+ function applyDefault(param, initializer) {
4641
+ const extracted = extractLiteralDefault(initializer);
4642
+ if (extracted.literal) {
4643
+ param.default = extracted.value;
4644
+ if (param.schema && typeof param.schema === "object" && !Array.isArray(param.schema)) {
4645
+ param.schema.default = extracted.value;
4414
4646
  }
4415
- if (type.flags & ts7.TypeFlags.Conditional) {
4416
- return { "x-ts-type": renderTypeText(type, checker) };
4647
+ } else {
4648
+ param.default = extracted.text;
4649
+ if (param.schema && typeof param.schema === "object" && !Array.isArray(param.schema)) {
4650
+ param.schema["x-ts-default"] = extracted.text;
4417
4651
  }
4418
- const constraint = checker.getBaseConstraintOfType(type);
4419
- const primitive = constraint && constraint.flags & ts7.TypeFlags.StringLike ? "string" : constraint && constraint.flags & ts7.TypeFlags.NumberLike ? "number" : constraint && constraint.flags & ts7.TypeFlags.BooleanLike ? "boolean" : undefined;
4420
- if (primitive) {
4421
- return { type: primitive, "x-ts-type": renderTypeText(type, checker) };
4652
+ }
4653
+ }
4654
+ function registerReferencedTypes(type, ctx, depth = 0) {
4655
+ if (depth > ctx.maxTypeDepth)
4656
+ return;
4657
+ if (ctx.registeredTypes.has(type))
4658
+ return;
4659
+ const isPrimitive = type.flags & (ts7.TypeFlags.String | ts7.TypeFlags.Number | ts7.TypeFlags.Boolean | ts7.TypeFlags.Void | ts7.TypeFlags.Undefined | ts7.TypeFlags.Null | ts7.TypeFlags.Any | ts7.TypeFlags.Unknown | ts7.TypeFlags.Never | ts7.TypeFlags.StringLiteral | ts7.TypeFlags.NumberLiteral | ts7.TypeFlags.BooleanLiteral);
4660
+ if (!isPrimitive) {
4661
+ ctx.registeredTypes.add(type);
4662
+ }
4663
+ const { typeChecker: checker, typeRegistry } = ctx;
4664
+ typeRegistry.registerType(type, ctx);
4665
+ const typeArgs = type.typeArguments;
4666
+ if (typeArgs) {
4667
+ for (const arg of typeArgs) {
4668
+ registerReferencedTypes(arg, ctx, depth + 1);
4422
4669
  }
4423
- return this.buildObjectSchemaFromProperties(type, checker, ctx);
4424
4670
  }
4425
- buildEnumSchema(symbol, checker) {
4426
- const decl = symbol.declarations?.find(ts7.isEnumDeclaration);
4427
- if (!decl)
4428
- return;
4429
- const members = [];
4430
- for (const member of decl.members) {
4431
- const memberSymbol = checker.getSymbolAtLocation(member.name);
4432
- if (memberSymbol) {
4433
- const constantValue = checker.getConstantValue(member);
4434
- if (constantValue !== undefined) {
4435
- members.push({
4436
- name: memberSymbol.getName(),
4437
- value: constantValue
4438
- });
4439
- }
4440
- }
4671
+ for (const arg of type.aliasTypeArguments ?? []) {
4672
+ registerReferencedTypes(arg, ctx, depth + 1);
4673
+ }
4674
+ const declared = declaredForm(type, checker);
4675
+ if (declared !== type) {
4676
+ registerReferencedTypes(declared, ctx, depth);
4677
+ return;
4678
+ }
4679
+ if (type.isUnion()) {
4680
+ for (const t of type.types) {
4681
+ registerReferencedTypes(t, ctx, depth + 1);
4441
4682
  }
4442
- if (members.length === 0)
4443
- return;
4444
- return {
4445
- type: typeof members[0].value === "string" ? "string" : "number",
4446
- enum: members.map((m) => m.value),
4447
- "x-enum-members": members
4448
- };
4449
4683
  }
4450
- buildObjectSchemaFromProperties(type, checker, ctx) {
4451
- const properties = type.getProperties();
4452
- const indexInfos = checker.getIndexInfosOfType(type);
4453
- const stringIndex = indexInfos.find((i) => i.keyType.flags & ts7.TypeFlags.String);
4454
- const numberIndex = indexInfos.find((i) => i.keyType.flags & ts7.TypeFlags.Number);
4455
- if (properties.length === 0 && !stringIndex && !numberIndex) {
4456
- return { type: checker.typeToString(type) };
4684
+ if (type.isIntersection()) {
4685
+ for (const t of type.types) {
4686
+ registerReferencedTypes(t, ctx, depth + 1);
4457
4687
  }
4458
- const props = {};
4459
- const required = [];
4688
+ }
4689
+ const typeSymbol = type.aliasSymbol ?? type.getSymbol();
4690
+ if (typeSymbol && ctx.shouldExpandExternal && !typeSymbol.getName().startsWith("__") && !ctx.shouldExpandExternal(typeSymbol)) {
4691
+ return;
4692
+ }
4693
+ if (isForeignPackage(typeSymbol, ctx.workspacePackages)) {
4694
+ return;
4695
+ }
4696
+ if (isDeferredMappedOrConditional(type)) {
4697
+ return;
4698
+ }
4699
+ if (type.flags & ts7.TypeFlags.Object) {
4700
+ const props = type.getProperties();
4460
4701
  const limit = ctx.maxProperties;
4461
- const isArrayLike = checker.isArrayType(type) || checker.isTupleType(type) || type.symbol?.getName() === "Array" && isLibFile(type.symbol?.getDeclarations()?.[0]?.getSourceFile()?.fileName ?? "");
4462
- const isStringLike = type.flags & ts7.TypeFlags.StringLike;
4463
- const isNumberLike = type.flags & ts7.TypeFlags.NumberLike;
4464
- const included = properties.filter((prop) => {
4465
- const propName = prop.getName();
4466
- if (propName.startsWith("__@"))
4467
- return false;
4468
- if (isArrayLike && ARRAY_PROTOTYPE_METHODS.has(propName))
4469
- return false;
4470
- if (isStringLike && STRING_PROTOTYPE_METHODS.has(propName))
4471
- return false;
4472
- if (isNumberLike && NUMBER_PROTOTYPE_METHODS.has(propName))
4473
- return false;
4474
- return true;
4475
- });
4476
- if (included.length > limit && ctx.onTruncation) {
4702
+ if (props.length > limit && ctx.onTruncation) {
4477
4703
  const typeName = type.getSymbol()?.getName() ?? "anonymous";
4478
- ctx.onTruncation(typeName, included.length, limit);
4704
+ ctx.onTruncation(typeName, props.length, limit);
4479
4705
  }
4480
- for (const prop of included.slice(0, limit)) {
4481
- const propName = prop.getName();
4482
- const rawPropType = checker.getTypeOfSymbol(prop);
4483
- const propType = prop.flags & ts7.SymbolFlags.Optional ? stripUndefinedFromType(rawPropType, checker) : rawPropType;
4484
- this.registerType(propType, ctx);
4485
- let propSchema = buildSchema(propType, checker, ctx, declaredTypeNode(prop.valueDeclaration ?? prop.getDeclarations()?.[0]));
4486
- const docComment = prop.getDocumentationComment(checker);
4487
- if (docComment.length > 0) {
4488
- const description = docComment.map((c) => c.text).join(`
4489
- `);
4490
- if (description.trim()) {
4491
- propSchema = withDescription(propSchema, description);
4492
- }
4493
- }
4494
- const { deprecated, reason } = isSymbolDeprecated(prop);
4495
- if (deprecated) {
4496
- propSchema = withDeprecated(propSchema, reason);
4497
- }
4498
- propSchema = decoratePropertySchema(propSchema, prop, propType, checker);
4499
- props[propName] = propSchema;
4500
- if (!(prop.flags & ts7.SymbolFlags.Optional)) {
4501
- required.push(propName);
4502
- }
4706
+ for (const prop of props.slice(0, limit)) {
4707
+ const propType = checker.getTypeOfSymbol(prop);
4708
+ registerReferencedTypes(propType, ctx, depth + 1);
4503
4709
  }
4504
- return {
4505
- type: "object",
4506
- properties: props,
4507
- ...required.length > 0 ? { required } : {},
4508
- ...stringIndex ? { additionalProperties: buildSchema(stringIndex.type, checker, ctx) } : {},
4509
- ...numberIndex ? {
4510
- patternProperties: { "^\\d+$": buildSchema(numberIndex.type, checker, ctx) },
4511
- "x-ts-index-key": "number"
4512
- } : {}
4513
- };
4514
4710
  }
4515
4711
  }
4516
4712
 
4517
4713
  // src/serializers/context.ts
4714
+ import ts8 from "typescript";
4518
4715
  function createContext(program, sourceFile, options = {}) {
4519
4716
  return {
4520
4717
  typeChecker: program.getTypeChecker(),
@@ -4534,9 +4731,12 @@ function createContext(program, sourceFile, options = {}) {
4534
4731
  declIds: new Map,
4535
4732
  idOwner: new Map,
4536
4733
  workspacePackages: options.workspacePackages ?? new Map,
4734
+ budget: { owner: "", ops: 0, exceeded: false },
4735
+ maxBudgetOps: MAX_BUDGET_OPS,
4537
4736
  schemaOps: 0,
4538
- maxSchemaOps: 20000,
4539
- budgetExceeded: false
4737
+ maxSchemaOps: MAX_SCHEMA_OPS,
4738
+ budgetExceeded: false,
4739
+ exhaustedBudgets: []
4540
4740
  };
4541
4741
  }
4542
4742
  function getInheritedMembers(classType, ownMemberNames, ctx, isStatic = false) {
@@ -4602,7 +4802,7 @@ function getStaticMembers(classType, checker) {
4602
4802
  });
4603
4803
  }
4604
4804
  function inheritedMethodSchema(symbol, type, ctx) {
4605
- if (!ctx.budgetExceeded) {
4805
+ if (!ctx.budget.exceeded) {
4606
4806
  return decoratePropertySchema({ "x-ts-function": true }, symbol, type, ctx.typeChecker);
4607
4807
  }
4608
4808
  return {
@@ -4620,7 +4820,7 @@ function serializeInheritedMember(symbol, inheritedFrom, ctx, isStatic) {
4620
4820
  const isOptional = !!(symbol.flags & ts8.SymbolFlags.Optional);
4621
4821
  const rawType = checker.getTypeOfSymbol(symbol);
4622
4822
  const type = isOptional ? stripUndefinedFromType(rawType, checker) : rawType;
4623
- const registerTypes = !ctx.budgetExceeded;
4823
+ const registerTypes = !ctx.budget.exceeded;
4624
4824
  if (registerTypes)
4625
4825
  registerReferencedTypes(type, ctx);
4626
4826
  let visibility;
@@ -4789,7 +4989,8 @@ function serializeClass(node, ctx) {
4789
4989
  members.push(...inheritedInstance);
4790
4990
  const inheritedStatic = getInheritedMembers(classType, ownMemberNames, ctx, true);
4791
4991
  members.push(...inheritedStatic);
4792
- const extendsClause = getExtendsClause(node, checker);
4992
+ const extendsClause = getExtendsText(node, checker);
4993
+ const openArms = openHeritageArms([node], checker, ctx);
4793
4994
  const implementsClause = getImplementsClause(node, checker);
4794
4995
  const classFlags = {};
4795
4996
  const classModifiers = ts10.getModifiers(node);
@@ -4807,6 +5008,7 @@ function serializeClass(node, ctx) {
4807
5008
  members: members.length > 0 ? members : undefined,
4808
5009
  signatures: signatures.length > 0 ? signatures : undefined,
4809
5010
  extends: extendsClause,
5011
+ ...openArms.length > 0 ? { schema: { allOf: openArms } } : {},
4810
5012
  implements: implementsClause?.length ? implementsClause : undefined,
4811
5013
  ...deprecated ? { deprecated: true, deprecationReason } : {},
4812
5014
  ...examples.length > 0 ? { examples } : {},
@@ -4819,11 +5021,7 @@ function getMemberName(member) {
4819
5021
  return "constructor";
4820
5022
  if (!member.name)
4821
5023
  return;
4822
- if (ts10.isIdentifier(member.name))
4823
- return member.name.text;
4824
- if (ts10.isPrivateIdentifier(member.name))
4825
- return member.name.text;
4826
- return member.name.getText();
5024
+ return propertyNameText(member.name);
4827
5025
  }
4828
5026
  function getVisibility(member) {
4829
5027
  const modifiers = ts10.canHaveModifiers(member) ? ts10.getModifiers(member) : undefined;
@@ -4934,8 +5132,18 @@ function serializeConstructor(node, ctx) {
4934
5132
  return null;
4935
5133
  return serializeConstructorSignature(node, sig, ctx);
4936
5134
  }
5135
+ function serializeConstructSignatures(sigs, ctx) {
5136
+ return sigs.map((sig, index) => {
5137
+ const typeParameters = extractTypeParametersFromSignature(sig, ctx.typeChecker);
5138
+ return {
5139
+ ...serializeConstructorSignature(sig.getDeclaration(), sig, ctx),
5140
+ ...typeParameters ? { typeParameters } : {},
5141
+ ...sigs.length > 1 ? { overloadIndex: index } : {}
5142
+ };
5143
+ });
5144
+ }
4937
5145
  function serializeConstructorSignature(node, sig, ctx) {
4938
- const { description, tags, examples, inlineTags } = getJSDocComment(node);
5146
+ const { description, tags, examples, inlineTags } = node ? getJSDocComment(node) : { description: undefined, tags: [], examples: [], inlineTags: undefined };
4939
5147
  const params = extractParameters(sig, ctx);
4940
5148
  return {
4941
5149
  description,
@@ -5015,21 +5223,6 @@ function serializeAccessor(node, ctx) {
5015
5223
  ...inlineTags ? { inlineTags } : {}
5016
5224
  };
5017
5225
  }
5018
- function getExtendsClause(node, checker) {
5019
- if (!node.heritageClauses)
5020
- return;
5021
- for (const clause of node.heritageClauses) {
5022
- if (clause.token === ts10.SyntaxKind.ExtendsKeyword) {
5023
- const expr = clause.types[0];
5024
- if (expr) {
5025
- const type = checker.getTypeAtLocation(expr);
5026
- const symbol = type.getSymbol();
5027
- return symbol?.getName() ?? expr.expression.getText();
5028
- }
5029
- }
5030
- }
5031
- return;
5032
- }
5033
5226
  function getImplementsClause(node, checker) {
5034
5227
  if (!node.heritageClauses)
5035
5228
  return;
@@ -5222,12 +5415,12 @@ function signaturesFromDeclaredType(typeNode, ctx) {
5222
5415
  const callSignatures = ctx.typeChecker.getTypeFromTypeNode(inner).getCallSignatures();
5223
5416
  return callSignatures.length > 0 ? signaturesFromChecker(callSignatures, ctx) : undefined;
5224
5417
  }
5225
- function serializeFunctionExport(node, ctx, nameOverride, declaredType) {
5418
+ function serializeFunctionExport(node, ctx, nameOverride, declaredType, jsdocNode) {
5226
5419
  const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
5227
5420
  const name = nameOverride ?? symbol?.getName() ?? node.name?.getText() ?? anonymousDefaultName(node);
5228
5421
  if (!name)
5229
5422
  return null;
5230
- const { description, tags, examples, source, deprecated, deprecationReason, inlineTags } = extractExportMetadata(node, symbol, ctx.typeChecker);
5423
+ const { description, tags, examples, source, deprecated, deprecationReason, inlineTags } = extractExportMetadata(node, symbol, ctx.typeChecker, jsdocNode);
5231
5424
  const declaredFn = declaredType && unwrapParens(declaredType);
5232
5425
  const typeParameters = extractTypeParameters(declaredFn && ts11.isFunctionTypeNode(declaredFn) ? declaredFn : node, ctx.typeChecker);
5233
5426
  const signatures = (declaredType && signaturesFromDeclaredType(declaredType, ctx)) ?? (anySignatureDefers(node, ctx) ? signaturesFromAst(functionSignatureDecls(node, ctx), ctx) : signaturesFromChecker(ctx.typeChecker.getTypeAtLocation(node).getCallSignatures(), ctx));
@@ -5266,7 +5459,8 @@ function serializeInterface(node, ctx) {
5266
5459
  const { description, tags, examples, source, deprecated, deprecationReason, inlineTags } = extractExportMetadata(node, symbol, checker);
5267
5460
  const typeParameters = extractTypeParameters(node, checker);
5268
5461
  const { members, callSignatureMember } = serializeTypeElements(node.members, ctx);
5269
- const extendsClause = getInterfaceExtends(node, checker);
5462
+ const extendsClause = getExtendsText(node, checker);
5463
+ const openArms = openHeritageArms([node], checker, ctx);
5270
5464
  const exportSignatures = callSignatureMember?.signatures && callSignatureMember.signatures.length > 0 ? callSignatureMember.signatures : undefined;
5271
5465
  return {
5272
5466
  id: name,
@@ -5279,6 +5473,7 @@ function serializeInterface(node, ctx) {
5279
5473
  members: members.length > 0 ? members : undefined,
5280
5474
  signatures: exportSignatures,
5281
5475
  extends: extendsClause,
5476
+ ...openArms.length > 0 ? { schema: { allOf: openArms } } : {},
5282
5477
  ...deprecated ? { deprecated: true, deprecationReason } : {},
5283
5478
  ...examples.length > 0 ? { examples } : {},
5284
5479
  ...inlineTags ? { inlineTags } : {}
@@ -5368,7 +5563,7 @@ function serializeMergedTypeSide(symbol, ctx) {
5368
5563
  const { description, tags } = getJSDocComment(typeDecl);
5369
5564
  return {
5370
5565
  members,
5371
- extends: interfaces.map((decl) => getInterfaceExtends(decl, checker)).find(Boolean),
5566
+ extends: interfaces.map((decl) => getExtendsText(decl, checker)).find(Boolean),
5372
5567
  typeParameters: extractTypeParameters(typeDecl, checker),
5373
5568
  description,
5374
5569
  tags
@@ -5376,7 +5571,7 @@ function serializeMergedTypeSide(symbol, ctx) {
5376
5571
  }
5377
5572
  function serializePropertySignature(node, ctx) {
5378
5573
  const { typeChecker: checker } = ctx;
5379
- const name = node.name.getText();
5574
+ const name = propertyNameText(node.name);
5380
5575
  const { description, tags, inlineTags } = getJSDocComment(node);
5381
5576
  const rawType = checker.getTypeAtLocation(node);
5382
5577
  const type = node.questionToken ? stripUndefinedFromType(rawType, checker) : rawType;
@@ -5406,7 +5601,7 @@ function serializePropertySignature(node, ctx) {
5406
5601
  }
5407
5602
  function serializeMethodSignature(node, ctx) {
5408
5603
  const { typeChecker: checker } = ctx;
5409
- const name = node.name.getText();
5604
+ const name = propertyNameText(node.name);
5410
5605
  const { description, tags, inlineTags } = getJSDocComment(node);
5411
5606
  const rawType = checker.getTypeAtLocation(node);
5412
5607
  const type = node.questionToken ? stripUndefinedFromType(rawType, checker) : rawType;
@@ -5474,20 +5669,6 @@ function serializeIndexSignature(node, ctx) {
5474
5669
  ...inlineTags ? { inlineTags } : {}
5475
5670
  };
5476
5671
  }
5477
- function getInterfaceExtends(node, checker) {
5478
- if (!node.heritageClauses)
5479
- return;
5480
- for (const clause of node.heritageClauses) {
5481
- if (clause.token === ts12.SyntaxKind.ExtendsKeyword && clause.types.length > 0) {
5482
- const names = clause.types.map((expr) => {
5483
- const type = checker.getTypeAtLocation(expr);
5484
- return type.getSymbol()?.getName() ?? expr.expression.getText();
5485
- });
5486
- return names.join(" & ");
5487
- }
5488
- }
5489
- return;
5490
- }
5491
5672
 
5492
5673
  // src/serializers/type-aliases.ts
5493
5674
  import ts13 from "typescript";
@@ -5532,7 +5713,7 @@ function serializeTypeAlias(node, ctx) {
5532
5713
  schema = buildObjectSchema(type.getProperties(), ctx.typeChecker, ctx, type);
5533
5714
  members = serializeResolvedMembers(type, node, ctx);
5534
5715
  } else {
5535
- schema = buildSchema(type, ctx.typeChecker, ctx);
5716
+ schema = buildAliasBodySchema(type, ctx.typeChecker, ctx);
5536
5717
  if (isObjectShapedAlias(type, ctx)) {
5537
5718
  members = serializeResolvedMembers(type, node, ctx);
5538
5719
  }
@@ -5847,12 +6028,17 @@ function serializeVariable(node, statement, ctx) {
5847
6028
  const name = symbol?.getName() ?? node.name.getText();
5848
6029
  if (!name)
5849
6030
  return null;
5850
- const { description, tags, examples, source, deprecated, deprecationReason, inlineTags } = extractExportMetadata(node, symbol, ctx.typeChecker, statement);
5851
- const type = ctx.typeChecker.getTypeAtLocation(node);
6031
+ return serializeValue(name, node, symbol, statement, ctx.typeChecker.getTypeAtLocation(node), ts15.isVariableDeclaration(node) ? node.type : undefined, ctx);
6032
+ }
6033
+ function serializeDefaultExpression(node, symbol, type, ctx) {
6034
+ return serializeValue(symbol.getName(), node.expression, symbol, node, type, undefined, ctx);
6035
+ }
6036
+ function serializeValue(name, node, symbol, jsdocNode, type, typeNode, ctx) {
6037
+ const { description, tags, examples, source, deprecated, deprecationReason, inlineTags } = extractExportMetadata(node, symbol, ctx.typeChecker, jsdocNode);
5852
6038
  const schemaExtraction = extractSchemaType(type, ctx.typeChecker);
5853
6039
  const typeToSerialize = schemaExtraction?.outputType ?? type;
5854
6040
  registerReferencedTypes(typeToSerialize, ctx);
5855
- const schema = buildSchema(typeToSerialize, ctx.typeChecker, ctx, ts15.isVariableDeclaration(node) ? node.type : undefined);
6041
+ const schema = buildSchema(typeToSerialize, ctx.typeChecker, ctx, typeNode);
5856
6042
  const flags = schemaExtraction ? {
5857
6043
  schemaLibrary: schemaExtraction.adapter.id,
5858
6044
  ...schemaExtraction.inputType && schemaExtraction.inputType !== schemaExtraction.outputType ? { hasTransform: true } : {}
@@ -6242,10 +6428,14 @@ function normalizeExport(exp, options = {}) {
6242
6428
  result.members = exp.members.map((member) => normalizeMember(member, options));
6243
6429
  }
6244
6430
  if (!vendorSchema && shouldGenerateMembersSchema(exp.kind) && exp.members && exp.members.length > 0) {
6245
- result.schema = normalizeMembers(exp.members, options);
6431
+ result.schema = withOpenArms(normalizeMembers(exp.members, options), result.schema);
6246
6432
  }
6247
6433
  return result;
6248
6434
  }
6435
+ function withOpenArms(membersSchema, provided) {
6436
+ const arms = provided?.allOf;
6437
+ return Array.isArray(arms) ? { allOf: [membersSchema, ...arms] } : membersSchema;
6438
+ }
6249
6439
  function normalizeType(type, options = {}) {
6250
6440
  const result = { ...type };
6251
6441
  if (type.schema) {
@@ -6255,7 +6445,7 @@ function normalizeType(type, options = {}) {
6255
6445
  result.members = type.members.map((member) => normalizeMember(member, options));
6256
6446
  }
6257
6447
  if (shouldGenerateMembersSchema(type.kind) && type.members && type.members.length > 0) {
6258
- result.schema = normalizeMembers(type.members, options);
6448
+ result.schema = withOpenArms(normalizeMembers(type.members, options), result.schema);
6259
6449
  }
6260
6450
  return result;
6261
6451
  }
@@ -6438,6 +6628,7 @@ async function getExport(options) {
6438
6628
  }
6439
6629
  const ctx = createContext(program, sourceFile, { maxTypeDepth });
6440
6630
  ctx.exportedIds = exportedIds;
6631
+ claimExportedTypeIds(exportedSymbols, ctx);
6441
6632
  try {
6442
6633
  const originalDecls = targetSymbol.declarations ?? [];
6443
6634
  const isNamespaceExportDecl = originalDecls.some((d) => ts16.isNamespaceExport(d) || ts16.isNamespaceImport(d));
@@ -7611,7 +7802,7 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
7611
7802
  const symbol = type.aliasSymbol ?? type.getSymbol();
7612
7803
  const named = !!symbol && !symbol.getName().startsWith("__");
7613
7804
  const allowed = !named || !symbol || symbolAllowed(symbol);
7614
- if (named && allowed && symbol && !ctx.exportedIds.has(symbol.getName())) {
7805
+ if (named && allowed && symbol && !isExportedType(symbol)) {
7615
7806
  ctx.typeRegistry.registerType(type, ctx);
7616
7807
  const name = symbol.getName();
7617
7808
  if (!symbolByName.has(name)) {
@@ -7632,6 +7823,11 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
7632
7823
  visit(t, depth + 1);
7633
7824
  }
7634
7825
  }
7826
+ const declared = declaredForm(type, checker);
7827
+ if (declared !== type) {
7828
+ visit(declared, depth);
7829
+ return;
7830
+ }
7635
7831
  if (!allowed || isDeferredMappedOrConditional(type) || !(type.flags & ts20.TypeFlags.Object || type.isClassOrInterface())) {
7636
7832
  return;
7637
7833
  }
@@ -7659,6 +7855,12 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
7659
7855
  }
7660
7856
  };
7661
7857
  const TYPE_SYMBOL_FLAGS = ts20.SymbolFlags.Interface | ts20.SymbolFlags.TypeAlias | ts20.SymbolFlags.Class | ts20.SymbolFlags.RegularEnum | ts20.SymbolFlags.ConstEnum;
7858
+ const isExportedType = (symbol) => {
7859
+ const name = symbol.getName();
7860
+ if (!ctx.exportedIds.has(name))
7861
+ return false;
7862
+ return !(symbol.flags & TYPE_SYMBOL_FLAGS) || resolveTypeId(symbol, ctx) === name;
7863
+ };
7662
7864
  const visitedSymbols = new Set;
7663
7865
  const symbolKind = (symbol) => {
7664
7866
  if (symbol.flags & ts20.SymbolFlags.Interface)
@@ -7673,6 +7875,7 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
7673
7875
  const resolved = resolveAliasSymbol(symbol, checker, undefined, ctx.program);
7674
7876
  return resolved;
7675
7877
  };
7878
+ const writtenWhenAny = (symbol, declared) => declared.flags & ts20.TypeFlags.Any ? symbol.declarations?.find(ts20.isTypeAliasDeclaration)?.type : undefined;
7676
7879
  const symbolByName = new Map;
7677
7880
  const registerTypeSymbol = (symbol) => {
7678
7881
  const name = symbol.getName();
@@ -7682,7 +7885,7 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
7682
7885
  return;
7683
7886
  if (!symbolAllowed(symbol))
7684
7887
  return;
7685
- if (ctx.exportedIds.has(name)) {
7888
+ if (isExportedType(symbol)) {
7686
7889
  walkDeclarations(symbol);
7687
7890
  return;
7688
7891
  }
@@ -7698,7 +7901,7 @@ function expandReachableTypes(exportedSymbols, ctx, opts) {
7698
7901
  id,
7699
7902
  name,
7700
7903
  kind: symbolKind(symbol),
7701
- schema: ensureNonEmptySchema(buildSchema(declared, checker, ctx), declared, checker)
7904
+ schema: withExpansionBudget(ctx, `type ${id}`, () => ensureNonEmptySchema(buildSchema(declared, checker, ctx, writtenWhenAny(symbol, declared)), declared, checker))
7702
7905
  });
7703
7906
  }
7704
7907
  symbolByName.set(name, symbol);
@@ -8080,6 +8283,7 @@ async function extract(options) {
8080
8283
  workspacePackages: result.workspacePackages ?? new Map
8081
8284
  });
8082
8285
  ctx.exportedIds = exportedIds;
8286
+ claimExportedTypeIds(exportedSymbols, ctx);
8083
8287
  const filteredSymbols = exportedSymbols.filter((s) => shouldIncludeExport(s.getName(), only, ignore));
8084
8288
  const total = filteredSymbols.length;
8085
8289
  for (let i = 0;i < filteredSymbols.length; i++) {
@@ -8147,10 +8351,15 @@ async function extract(options) {
8147
8351
  }
8148
8352
  continue;
8149
8353
  }
8150
- const exp = serializeDeclaration2(declaration, symbol, exportName, ctx, isTypeOnly);
8354
+ const exp = withExpansionBudget(ctx, exportName, () => serializeDeclaration2(declaration, symbol, exportName, ctx, isTypeOnly));
8151
8355
  if (exp) {
8152
- if (!exp.members && isValueDeclaration(declaration) && hasTypeSide(targetSymbol)) {
8153
- mergedTypeSides.push({ index: exports.length, symbol: targetSymbol });
8356
+ const typeSide = mergedTypeSideOf(exp, declaration, targetSymbol, ctx);
8357
+ if (typeSide) {
8358
+ mergedTypeSides.push({
8359
+ index: exports.length,
8360
+ symbol: typeSide,
8361
+ ontoClass: ts21.isClassDeclaration(declaration)
8362
+ });
8154
8363
  }
8155
8364
  exports.push(exp);
8156
8365
  tracker.status = "success";
@@ -8170,8 +8379,8 @@ async function extract(options) {
8170
8379
  });
8171
8380
  }
8172
8381
  }
8173
- for (const { index, symbol } of mergedTypeSides) {
8174
- exports[index] = withMergedTypeSide(exports[index], symbol, ctx);
8382
+ for (const { index, symbol, ontoClass } of mergedTypeSides) {
8383
+ exports[index] = withExpansionBudget(ctx, exports[index].name, () => withMergedTypeSide(exports[index], symbol, ctx, ontoClass));
8175
8384
  }
8176
8385
  const verification = buildVerificationSummary(exportedSymbols.length, exports.length, exportTracker);
8177
8386
  const meta = await getPackageMeta(entryFile, baseDir);
@@ -8182,7 +8391,7 @@ async function extract(options) {
8182
8391
  });
8183
8392
  if (ctx.budgetExceeded) {
8184
8393
  diagnostics.push({
8185
- message: "Stopped expanding some types after hitting the schema expansion budget",
8394
+ message: `Stopped expanding some types after hitting the schema expansion budget${exhaustedBudgetNote(ctx)}`,
8186
8395
  severity: "warning",
8187
8396
  code: "TYPE_EXPANSION_LIMIT"
8188
8397
  });
@@ -8354,21 +8563,18 @@ function serializeDeclaration2(declaration, exportSymbol, exportName, ctx, isTyp
8354
8563
  result = serializeVariable(declaration, varStatement, ctx);
8355
8564
  if (result?.kind === "variable") {
8356
8565
  const type = ctx.typeChecker.getTypeAtLocation(declaration);
8357
- if (type.getConstructSignatures().length > 0) {
8358
- result = { ...result, kind: "class" };
8359
- } else {
8360
- const callSigs = callSignaturesForVariable(declaration, ctx);
8361
- if (callSigs.length > 0) {
8362
- result = {
8363
- ...result,
8364
- kind: "function",
8365
- signatures: buildSignatures(callSigs, ctx.typeChecker, ctx)
8366
- };
8367
- }
8368
- }
8566
+ result = withCallableKind(result, type, callSignaturesForVariable(declaration, ctx), ctx);
8369
8567
  }
8370
8568
  }
8371
8569
  }
8570
+ } else if (ts21.isExportAssignment(declaration) && !declaration.isExportEquals) {
8571
+ const expression = skipOuterExpressions(declaration.expression);
8572
+ if (ts21.isArrowFunction(expression) || ts21.isFunctionExpression(expression)) {
8573
+ result = serializeFunctionExport(expression, ctx, exportName, undefined, declaration);
8574
+ } else {
8575
+ const type = ctx.typeChecker.getTypeOfSymbol(exportSymbol);
8576
+ result = withCallableKind(serializeDefaultExpression(declaration, exportSymbol, type, ctx), type, type.getCallSignatures(), ctx);
8577
+ }
8372
8578
  } else if (ts21.isNamespaceExport(declaration) || ts21.isModuleDeclaration(declaration) || ts21.isNamespaceImport(declaration) || ts21.isSourceFile(declaration)) {
8373
8579
  try {
8374
8580
  result = serializeNamespaceExport(exportSymbol, exportName, ctx);
@@ -8559,6 +8765,30 @@ function variableStatementOf(declaration) {
8559
8765
  const statement = node.parent?.parent;
8560
8766
  return statement && ts21.isVariableStatement(statement) ? statement : undefined;
8561
8767
  }
8768
+ function skipOuterExpressions(expression) {
8769
+ let node = expression;
8770
+ while (ts21.isParenthesizedExpression(node) || ts21.isAsExpression(node) || ts21.isSatisfiesExpression(node) || ts21.isTypeAssertionExpression(node) || ts21.isNonNullExpression(node)) {
8771
+ node = node.expression;
8772
+ }
8773
+ return node;
8774
+ }
8775
+ function withCallableKind(entry, type, callSigs, ctx) {
8776
+ const constructSigs = type.getConstructSignatures();
8777
+ if (constructSigs.length > 0) {
8778
+ return {
8779
+ ...entry,
8780
+ kind: "class",
8781
+ signatures: serializeConstructSignatures(constructSigs, ctx)
8782
+ };
8783
+ }
8784
+ if (callSigs.length === 0)
8785
+ return entry;
8786
+ return {
8787
+ ...entry,
8788
+ kind: "function",
8789
+ signatures: buildSignatures(callSigs, ctx.typeChecker, ctx)
8790
+ };
8791
+ }
8562
8792
  function callSignaturesForVariable(declaration, ctx) {
8563
8793
  const checker = ctx.typeChecker;
8564
8794
  if (ts21.isVariableDeclaration(declaration) && declaration.type && ts21.isTypeReferenceNode(declaration.type)) {
@@ -8609,18 +8839,45 @@ function isValueDeclaration(declaration) {
8609
8839
  function hasTypeSide(symbol) {
8610
8840
  return (symbol.flags & (ts21.SymbolFlags.Interface | ts21.SymbolFlags.TypeAlias)) !== 0;
8611
8841
  }
8612
- function withMergedTypeSide(entry, symbol, ctx) {
8842
+ function mergedTypeSideOf(exp, declaration, symbol, ctx) {
8843
+ if (ts21.isClassDeclaration(declaration)) {
8844
+ return symbol.flags & ts21.SymbolFlags.Interface ? symbol : undefined;
8845
+ }
8846
+ if (exp.members || !isValueDeclaration(declaration))
8847
+ return;
8848
+ if (hasTypeSide(symbol))
8849
+ return symbol;
8850
+ if (exp.kind !== "class")
8851
+ return;
8852
+ const [construct] = ctx.typeChecker.getTypeAtLocation(declaration).getConstructSignatures();
8853
+ const instance = construct?.getReturnType();
8854
+ const constructed = instance && (instance.aliasSymbol ?? instance.getSymbol());
8855
+ if (!constructed || !hasTypeSide(constructed))
8856
+ return;
8857
+ if (ctx.shouldExpandExternal && !ctx.shouldExpandExternal(constructed))
8858
+ return;
8859
+ return constructed;
8860
+ }
8861
+ function withMergedTypeSide(entry, symbol, ctx, ontoClass) {
8613
8862
  const typeSide = serializeMergedTypeSide(symbol, ctx);
8614
8863
  if (!typeSide)
8615
8864
  return entry;
8616
8865
  const { members, description, tags, ...instanceType } = typeSide;
8866
+ const own = new Set(entry.members?.map((m) => m.name));
8617
8867
  return {
8618
8868
  ...entry,
8619
- members,
8620
- ...entry.kind === "class" ? instanceType : {},
8869
+ members: [...entry.members ?? [], ...(members ?? []).filter((m) => !own.has(m.name))],
8870
+ ...entry.kind === "class" && !ontoClass ? instanceType : {},
8621
8871
  ...entry.description ? {} : { description, tags: [...entry.tags ?? [], ...tags ?? []] }
8622
8872
  };
8623
8873
  }
8874
+ function exhaustedBudgetNote(ctx) {
8875
+ const owners = [...new Set(ctx.exhaustedBudgets)];
8876
+ if (owners.length === 0)
8877
+ return "";
8878
+ const shown = owners.slice(0, 10).join(", ");
8879
+ return `: ${shown}${owners.length > 10 ? ` (+${owners.length - 10} more)` : ""}`;
8880
+ }
8624
8881
  function withExportName(entry, exportName) {
8625
8882
  if (entry.name === exportName) {
8626
8883
  return entry;
@@ -9192,6 +9449,7 @@ function getNodeName(node) {
9192
9449
  export {
9193
9450
  zodAdapter,
9194
9451
  writtenTypeText,
9452
+ withOpenHeritage,
9195
9453
  withDescription,
9196
9454
  withDeprecated,
9197
9455
  validateSpec2 as validateSpec,
@@ -9237,6 +9495,7 @@ export {
9237
9495
  query,
9238
9496
  pickEntry,
9239
9497
  parseGithubRepo,
9498
+ openHeritageArms,
9240
9499
  normalizeType,
9241
9500
  normalizeSchema,
9242
9501
  normalizeMembers,
@@ -9318,6 +9577,7 @@ export {
9318
9577
  buildSchema,
9319
9578
  buildObjectSchema,
9320
9579
  buildFunctionSchema,
9580
+ buildAliasBodySchema,
9321
9581
  assertSpec,
9322
9582
  asStandardSchema,
9323
9583
  arktypeAdapter,