@openpkg-ts/sdk 0.35.1 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.d.ts CHANGED
@@ -400,8 +400,12 @@ import { OpenPkg as OpenPkg8, SpecExport as SpecExport4, SpecMember as SpecMembe
400
400
  interface FormatSchemaOptions {
401
401
  /** Include package attribution for external types */
402
402
  includePackage?: boolean;
403
- /** Collapse unions with more than N members (default: no collapse) */
403
+ /** Collapse unions with more than N members (default: 5) */
404
404
  collapseUnionThreshold?: number;
405
+ /** Max recursion depth before returning "..." (default: 3) */
406
+ maxDepth?: number;
407
+ /** @internal current recursion depth */
408
+ _depth?: number;
405
409
  }
406
410
  /**
407
411
  * Format a schema to a human-readable type string.
@@ -675,4 +679,47 @@ declare class QueryBuilder {
675
679
  * Create a query builder for the given spec
676
680
  */
677
681
  declare function query(spec: OpenPkg9): QueryBuilder;
678
- export { toSearchIndexJSON, toSearchIndex2 as toSearchIndex, toPagefindRecords2 as toPagefindRecords, toAlgoliaRecords2 as toAlgoliaRecords, sortByName, resolveTypeRef, query, isProperty, isMethod, hasDeprecatedTag, groupByVisibility, getProperties, getMethods, getMemberBadges, getDeprecationMessage, formatTypeParameters, formatSchema, formatReturnType, formatParameters, formatMappedType, formatConditionalType, formatBadges, findMissingParamDocs, buildSignatureString, analyzeSpec, SpecMappedType, SpecDiagnostics, SpecConditionalType, SearchRecord, SearchOptions, SearchIndex, QueryBuilder, PagefindRecord, LoadOptions, FormatSchemaOptions, DocsInstance, DiagnosticItem, AlgoliaRecord };
682
+ import { OpenPkg as OpenPkg10, SpecExample, SpecExport as SpecExport6, SpecSchema as SpecSchema2, SpecSignatureParameter } from "@openpkg-ts/spec";
683
+ interface CodeExample {
684
+ /** Unique identifier */
685
+ id: string;
686
+ /** Display label for chip */
687
+ label: string;
688
+ /** Code content */
689
+ code: string;
690
+ /** Language for highlighting (e.g. 'ts', 'bash') */
691
+ language?: string;
692
+ }
693
+ interface Language {
694
+ /** Language identifier (e.g. "typescript", "python") */
695
+ id: string;
696
+ /** Display label (e.g. "TypeScript", "Python") */
697
+ label: string;
698
+ }
699
+ interface APIParameterSchema {
700
+ /** Type name */
701
+ type?: string;
702
+ /** Formatted type string */
703
+ typeString?: string;
704
+ /** Description */
705
+ description?: string;
706
+ /** Nested properties for object types */
707
+ properties?: Record<string, APIParameterSchema>;
708
+ /** Required property names */
709
+ required?: string[];
710
+ }
711
+ declare function getLangForHighlight(lang: string): string;
712
+ declare function getLanguageLabel(lang: string): string;
713
+ declare function specSchemaToAPISchema(schema: SpecSchema2 | undefined): APIParameterSchema | undefined;
714
+ declare function specParamToAPIParam(param: SpecSignatureParameter): {
715
+ name: string;
716
+ type: string;
717
+ required: boolean;
718
+ description?: string;
719
+ children?: APIParameterSchema;
720
+ };
721
+ /** Convert spec examples to CodeExample[] (Shape B: id/label/code/language) */
722
+ declare function specExamplesToCodeExamples(examples: (string | SpecExample)[] | undefined, defaultLang?: string): CodeExample[];
723
+ declare function getLanguagesFromExamples(examples: (string | SpecExample)[] | undefined): Language[];
724
+ declare function buildImportStatement(exp: SpecExport6, spec: OpenPkg10): string;
725
+ export { toSearchIndexJSON, toSearchIndex2 as toSearchIndex, toPagefindRecords2 as toPagefindRecords, toAlgoliaRecords2 as toAlgoliaRecords, specSchemaToAPISchema, specParamToAPIParam, specExamplesToCodeExamples, sortByName, resolveTypeRef, query, isProperty, isMethod, hasDeprecatedTag, groupByVisibility, getProperties, getMethods, getMemberBadges, getLanguagesFromExamples, getLanguageLabel, getLangForHighlight, getDeprecationMessage, formatTypeParameters, formatSchema, formatReturnType, formatParameters, formatMappedType, formatConditionalType, formatBadges, findMissingParamDocs, buildSignatureString, buildImportStatement, analyzeSpec, SpecMappedType, SpecDiagnostics, SpecConditionalType, SearchRecord, SearchOptions, SearchIndex, QueryBuilder, PagefindRecord, LoadOptions, Language, FormatSchemaOptions, DocsInstance, DiagnosticItem, CodeExample, AlgoliaRecord, APIParameterSchema };
package/dist/browser.js CHANGED
@@ -25,12 +25,121 @@ import {
25
25
  toPagefindRecords,
26
26
  toSearchIndex,
27
27
  toSearchIndexJSON
28
- } from "./shared/chunk-4fgxg5jj.js";
28
+ } from "./shared/chunk-hnajr1tb.js";
29
+ // src/core/spec-converters.ts
30
+ function getLangForHighlight(lang) {
31
+ const langMap = {
32
+ typescript: "ts",
33
+ javascript: "js",
34
+ ts: "ts",
35
+ js: "js",
36
+ tsx: "tsx",
37
+ jsx: "jsx",
38
+ bash: "bash",
39
+ shell: "bash",
40
+ json: "json",
41
+ python: "python",
42
+ go: "go",
43
+ rust: "rust"
44
+ };
45
+ return langMap[lang.toLowerCase()] || lang;
46
+ }
47
+ function getLanguageLabel(lang) {
48
+ const labels = {
49
+ typescript: "TypeScript",
50
+ javascript: "JavaScript",
51
+ ts: "TypeScript",
52
+ js: "JavaScript",
53
+ bash: "Bash",
54
+ json: "JSON",
55
+ python: "Python",
56
+ go: "Go",
57
+ rust: "Rust"
58
+ };
59
+ return labels[lang.toLowerCase()] || lang;
60
+ }
61
+ function specSchemaToAPISchema(schema) {
62
+ if (!schema || typeof schema !== "object")
63
+ return;
64
+ const s = schema;
65
+ const result = {};
66
+ result.type = formatSchema(schema);
67
+ result.typeString = result.type;
68
+ if (typeof s.description === "string") {
69
+ result.description = s.description;
70
+ }
71
+ if (s.type === "object" && s.properties && typeof s.properties === "object") {
72
+ result.properties = {};
73
+ for (const [key, value] of Object.entries(s.properties)) {
74
+ const nested = specSchemaToAPISchema(value);
75
+ if (nested)
76
+ result.properties[key] = nested;
77
+ }
78
+ if (Array.isArray(s.required)) {
79
+ result.required = s.required;
80
+ }
81
+ }
82
+ return result;
83
+ }
84
+ function specParamToAPIParam(param) {
85
+ const type = formatSchema(param.schema);
86
+ const children = specSchemaToAPISchema(param.schema);
87
+ const hasNestedProperties = children?.properties && Object.keys(children.properties).length > 0;
88
+ return {
89
+ name: param.name ?? "unknown",
90
+ type,
91
+ required: param.required !== false,
92
+ description: param.description,
93
+ children: hasNestedProperties ? children : undefined
94
+ };
95
+ }
96
+ function specExamplesToCodeExamples(examples, defaultLang = "typescript") {
97
+ if (!examples?.length)
98
+ return [];
99
+ return examples.map((ex, i) => {
100
+ const lang = typeof ex === "string" ? defaultLang : ex.language || defaultLang;
101
+ const code = typeof ex === "string" ? ex : ex.code;
102
+ const label = typeof ex === "string" ? getLanguageLabel(lang) : ex.title || getLanguageLabel(lang);
103
+ return {
104
+ id: `example-${i}`,
105
+ label,
106
+ code,
107
+ language: getLangForHighlight(lang)
108
+ };
109
+ });
110
+ }
111
+ function getLanguagesFromExamples(examples) {
112
+ if (!examples?.length)
113
+ return [];
114
+ const seen = new Set;
115
+ const result = [];
116
+ for (const ex of examples) {
117
+ const lang = typeof ex === "string" ? "typescript" : ex.language || "typescript";
118
+ if (!seen.has(lang)) {
119
+ seen.add(lang);
120
+ result.push({ id: lang, label: getLanguageLabel(lang) });
121
+ }
122
+ }
123
+ return result;
124
+ }
125
+ function buildImportStatement(exp, spec) {
126
+ const packageName = spec.meta?.name || "package";
127
+ const presentation = spec.extensions?.presentation?.[exp.id];
128
+ const importPath = presentation?.importPath || packageName;
129
+ const alias = presentation?.alias || exp.name;
130
+ if (exp.kind === "type" || exp.kind === "interface") {
131
+ return `import type { ${alias} } from '${importPath}'`;
132
+ }
133
+ return `import { ${alias} } from '${importPath}'`;
134
+ }
29
135
  export {
30
136
  toSearchIndexJSON,
31
137
  toSearchIndex,
32
138
  toPagefindRecords,
33
139
  toAlgoliaRecords,
140
+ specSchemaToAPISchema,
141
+ specParamToAPIParam,
142
+ specExamplesToCodeExamples,
34
143
  sortByName,
35
144
  resolveTypeRef,
36
145
  query,
@@ -41,6 +150,9 @@ export {
41
150
  getProperties,
42
151
  getMethods,
43
152
  getMemberBadges,
153
+ getLanguagesFromExamples,
154
+ getLanguageLabel,
155
+ getLangForHighlight,
44
156
  getDeprecationMessage,
45
157
  formatTypeParameters,
46
158
  formatSchema,
@@ -51,6 +163,7 @@ export {
51
163
  formatBadges,
52
164
  findMissingParamDocs,
53
165
  buildSignatureString,
166
+ buildImportStatement,
54
167
  analyzeSpec,
55
168
  QueryBuilder
56
169
  };
package/dist/index.d.ts CHANGED
@@ -745,8 +745,12 @@ import { OpenPkg as OpenPkg9, SpecExport as SpecExport4, SpecMember as SpecMembe
745
745
  interface FormatSchemaOptions {
746
746
  /** Include package attribution for external types */
747
747
  includePackage?: boolean;
748
- /** Collapse unions with more than N members (default: no collapse) */
748
+ /** Collapse unions with more than N members (default: 5) */
749
749
  collapseUnionThreshold?: number;
750
+ /** Max recursion depth before returning "..." (default: 3) */
751
+ maxDepth?: number;
752
+ /** @internal current recursion depth */
753
+ _depth?: number;
750
754
  }
751
755
  /**
752
756
  * Format a schema to a human-readable type string.
@@ -1169,8 +1173,10 @@ interface SerializerContext {
1169
1173
  resolveExternalTypes: boolean;
1170
1174
  typeRegistry: TypeRegistry;
1171
1175
  exportedIds: Set<string>;
1172
- /** Track visited types to prevent infinite recursion */
1176
+ /** Stack-style recursion guard for buildSchemaInternal (add before recurse, delete after) */
1173
1177
  visitedTypes: Set<ts.Type>;
1178
+ /** Permanent "already processed" set for registerReferencedTypes */
1179
+ registeredTypes: Set<ts.Type>;
1174
1180
  /** Flag to indicate we're processing tuple elements - skip Array prototype methods */
1175
1181
  inTupleElement?: boolean;
1176
1182
  /** Include private/protected class members (default: false) */
@@ -1627,7 +1633,7 @@ import ts14 from "typescript";
1627
1633
  declare function extractParameters(signature: ts14.Signature, ctx: SerializerContext): SpecSignatureParameter[];
1628
1634
  /**
1629
1635
  * Recursively register types referenced by a ts.Type.
1630
- * Uses ctx.visitedTypes to prevent infinite recursion on circular types.
1636
+ * Uses ctx.registeredTypes to prevent re-processing already-registered types.
1631
1637
  */
1632
1638
  declare function registerReferencedTypes(type: ts14.Type, ctx: SerializerContext, depth?: number): void;
1633
1639
  import { SpecSchema as SpecSchema2 } from "@openpkg-ts/spec";
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  toPagefindRecords,
30
30
  toSearchIndex,
31
31
  toSearchIndexJSON
32
- } from "./shared/chunk-4fgxg5jj.js";
32
+ } from "./shared/chunk-hnajr1tb.js";
33
33
 
34
34
  // src/primitives/diff.ts
35
35
  import {
@@ -1746,7 +1746,9 @@ function parseExamplesFromTags(tags) {
1746
1746
  function stripParamSeparator(text) {
1747
1747
  if (!text)
1748
1748
  return;
1749
- const stripped = text.replace(/^-\s*/, "").trim();
1749
+ let stripped = text.replace(/^-\s*/, "").trim();
1750
+ const parts = stripped.split(/\n\s*\n/);
1751
+ stripped = parts[0].trim();
1750
1752
  return stripped || undefined;
1751
1753
  }
1752
1754
  function stripTypeParamSeparator(text) {
@@ -2227,40 +2229,13 @@ function buildSchema(type, checker, ctx) {
2227
2229
  const schema = buildSchemaInternal(type, checker, ctx);
2228
2230
  return ensureNonEmptySchema(schema, type, checker);
2229
2231
  }
2230
- function buildSchemaInternal(type, checker, ctx) {
2231
- if (isAtMaxDepth(ctx)) {
2232
- return { type: checker.typeToString(type) };
2233
- }
2234
- if (ctx?.visitedTypes.has(type)) {
2235
- const callSignatures = type.getCallSignatures();
2236
- if (callSignatures.length > 0) {
2237
- return buildFunctionSchema(callSignatures, checker, ctx);
2238
- }
2239
- const symbol2 = type.getSymbol() || type.aliasSymbol;
2240
- if (symbol2 && !isAnonymous(type)) {
2241
- const name = symbol2.getName();
2242
- const schema = { $ref: `#/types/${name}` };
2243
- const typeRef2 = type;
2244
- if (typeRef2.target) {
2245
- const typeArgs2 = checker.getTypeArguments(typeRef2);
2246
- if (typeArgs2 && typeArgs2.length > 0) {
2247
- ctx.visitedTypes.delete(type);
2248
- schema.typeArguments = typeArgs2.map((t) => buildSchema(t, checker, ctx));
2249
- ctx.visitedTypes.add(type);
2250
- }
2251
- }
2252
- return schema;
2253
- }
2254
- if (type.flags & ts3.TypeFlags.Object) {
2255
- const properties = type.getProperties();
2256
- if (properties.length > 0) {
2257
- return buildObjectSchema(properties, checker, ctx, type);
2258
- }
2232
+ function buildMaxDepthSchema(type, checker) {
2233
+ const symbol = type.getSymbol() || type.aliasSymbol;
2234
+ if (symbol && !isAnonymous(type)) {
2235
+ const name = symbol.getName();
2236
+ if (!name.startsWith("__") && !isPrimitiveName(name)) {
2237
+ return { $ref: `#/types/${name}` };
2259
2238
  }
2260
- return { type: checker.typeToString(type) };
2261
- }
2262
- if (ctx && type.flags & ts3.TypeFlags.Object) {
2263
- ctx.visitedTypes.add(type);
2264
2239
  }
2265
2240
  if (type.flags & ts3.TypeFlags.String)
2266
2241
  return { type: "string" };
@@ -2274,223 +2249,281 @@ function buildSchemaInternal(type, checker, ctx) {
2274
2249
  return { type: "null" };
2275
2250
  if (type.flags & ts3.TypeFlags.Void)
2276
2251
  return { type: "void" };
2277
- if (type.flags & ts3.TypeFlags.Any)
2278
- return { type: "any" };
2279
- if (type.flags & ts3.TypeFlags.Unknown)
2280
- return { type: "unknown" };
2281
- if (type.flags & ts3.TypeFlags.Never)
2282
- return { type: "never" };
2283
- if (type.flags & ts3.TypeFlags.BigInt)
2284
- return { type: "bigint" };
2285
- if (type.flags & ts3.TypeFlags.ESSymbol)
2286
- return { type: "symbol" };
2287
- if (type.isThisType?.()) {
2288
- const constraint = type.getConstraint?.();
2289
- const symbol2 = constraint?.getSymbol() ?? type.getSymbol();
2290
- if (symbol2 && !isAnonymous(type)) {
2291
- return {
2292
- $ref: `#/types/${symbol2.getName()}`,
2293
- "x-ts-type": "this"
2294
- };
2295
- }
2296
- }
2297
- if (type.flags & ts3.TypeFlags.StringLiteral) {
2298
- const literal = type.value;
2299
- return { type: "string", enum: [literal] };
2252
+ if (type.isUnion()) {
2253
+ const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker));
2254
+ return { anyOf: schemas };
2300
2255
  }
2301
- if (type.flags & ts3.TypeFlags.NumberLiteral) {
2302
- const literal = type.value;
2303
- return { type: "number", enum: [literal] };
2256
+ if (type.isIntersection()) {
2257
+ const schemas = type.types.map((t) => buildMaxDepthSchema(t, checker));
2258
+ return { allOf: schemas };
2304
2259
  }
2305
- if (type.flags & ts3.TypeFlags.BooleanLiteral) {
2306
- const typeString2 = checker.typeToString(type);
2307
- return { type: "boolean", enum: [typeString2 === "true"] };
2260
+ return { type: checker.typeToString(type) };
2261
+ }
2262
+ function buildSchemaInternal(type, checker, ctx) {
2263
+ if (isAtMaxDepth(ctx)) {
2264
+ return buildMaxDepthSchema(type, checker);
2308
2265
  }
2309
- if (type.isUnion()) {
2310
- const types = type.types;
2311
- const allStringLiterals = types.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
2312
- if (allStringLiterals) {
2313
- const enumValues = types.map((t) => t.value);
2314
- return { type: "string", enum: enumValues };
2315
- }
2316
- const allNumberLiterals = types.every((t) => t.flags & ts3.TypeFlags.NumberLiteral);
2317
- if (allNumberLiterals) {
2318
- const enumValues = types.map((t) => t.value);
2319
- return { type: "number", enum: enumValues };
2320
- }
2321
- if (ctx) {
2322
- return withDepth(ctx, () => ({
2323
- anyOf: types.map((t) => buildSchema(t, checker, ctx))
2324
- }));
2325
- }
2326
- return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
2327
- }
2328
- const isIntersectionType = type.isIntersection() || !!(type.flags & ts3.TypeFlags.Intersection);
2329
- if (isIntersectionType && "types" in type) {
2330
- const intersectionType = type;
2331
- const filteredTypes = intersectionType.types.filter((t) => !(t.flags & ts3.TypeFlags.Never));
2332
- if (filteredTypes.length === 0) {
2333
- return { type: "never" };
2334
- }
2335
- if (filteredTypes.length === 1) {
2336
- return buildSchema(filteredTypes[0], checker, ctx);
2266
+ if (ctx?.visitedTypes.has(type)) {
2267
+ const callSignatures = type.getCallSignatures();
2268
+ if (callSignatures.length > 0) {
2269
+ return buildFunctionSchema(callSignatures, checker, ctx);
2337
2270
  }
2338
- if (ctx) {
2339
- return withDepth(ctx, () => ({
2340
- allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx))
2341
- }));
2271
+ const symbol = type.getSymbol() || type.aliasSymbol;
2272
+ if (symbol && !isAnonymous(type)) {
2273
+ return { $ref: `#/types/${symbol.getName()}` };
2342
2274
  }
2343
- return { allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx)) };
2275
+ return { type: checker.typeToString(type) };
2344
2276
  }
2345
- const typeString = checker.typeToString(type);
2346
- if (typeString === "never[]" || typeString === "[]") {
2347
- return { type: "array", prefixedItems: [], minItems: 0, maxItems: 0 };
2277
+ const addedToVisited = !!(ctx && type.flags & ts3.TypeFlags.Object);
2278
+ if (addedToVisited) {
2279
+ ctx.visitedTypes.add(type);
2348
2280
  }
2349
- const symbol = type.getSymbol() || type.aliasSymbol;
2350
- if (symbol?.getName() === "Array" && isBuiltinSymbol(symbol)) {
2351
- const typeRef2 = type;
2352
- const typeArgs2 = typeRef2.target ? checker.getTypeArguments(typeRef2) : undefined;
2353
- const elementType = typeArgs2?.[0];
2354
- if (elementType) {
2355
- return { type: "array", items: buildSchema(elementType, checker, ctx) };
2356
- }
2357
- return { type: "array", items: { "x-ts-type": "unknown" } };
2358
- }
2359
- if (checker.isArrayType(type)) {
2360
- const arrayTypeRef = type;
2361
- const arrayTypeArgs = checker.getTypeArguments(arrayTypeRef);
2362
- const elementType = arrayTypeArgs?.[0];
2363
- if (elementType) {
2281
+ try {
2282
+ if (type.flags & ts3.TypeFlags.String)
2283
+ return { type: "string" };
2284
+ if (type.flags & ts3.TypeFlags.Number)
2285
+ return { type: "number" };
2286
+ if (type.flags & ts3.TypeFlags.Boolean)
2287
+ return { type: "boolean" };
2288
+ if (type.flags & ts3.TypeFlags.Undefined)
2289
+ return { type: "undefined" };
2290
+ if (type.flags & ts3.TypeFlags.Null)
2291
+ return { type: "null" };
2292
+ if (type.flags & ts3.TypeFlags.Void)
2293
+ return { type: "void" };
2294
+ if (type.flags & ts3.TypeFlags.Any)
2295
+ return { type: "any" };
2296
+ if (type.flags & ts3.TypeFlags.Unknown)
2297
+ return { type: "unknown" };
2298
+ if (type.flags & ts3.TypeFlags.Never)
2299
+ return { type: "never" };
2300
+ if (type.flags & ts3.TypeFlags.BigInt)
2301
+ return { type: "bigint" };
2302
+ if (type.flags & ts3.TypeFlags.ESSymbol)
2303
+ return { type: "symbol" };
2304
+ if (type.isThisType?.()) {
2305
+ const constraint = type.getConstraint?.();
2306
+ const symbol2 = constraint?.getSymbol() ?? type.getSymbol();
2307
+ if (symbol2 && !isAnonymous(type)) {
2308
+ return {
2309
+ $ref: `#/types/${symbol2.getName()}`,
2310
+ "x-ts-type": "this"
2311
+ };
2312
+ }
2313
+ }
2314
+ if (type.flags & ts3.TypeFlags.StringLiteral) {
2315
+ const literal = type.value;
2316
+ return { type: "string", enum: [literal] };
2317
+ }
2318
+ if (type.flags & ts3.TypeFlags.NumberLiteral) {
2319
+ const literal = type.value;
2320
+ return { type: "number", enum: [literal] };
2321
+ }
2322
+ if (type.flags & ts3.TypeFlags.BooleanLiteral) {
2323
+ const typeString2 = checker.typeToString(type);
2324
+ return { type: "boolean", enum: [typeString2 === "true"] };
2325
+ }
2326
+ if (type.aliasSymbol && !type.aliasTypeArguments?.length) {
2327
+ const aliasName = type.aliasSymbol.getName();
2328
+ if (!aliasName.startsWith("__") && !isPrimitiveName(aliasName)) {
2329
+ const packageOrigin = getTypeOrigin(type, checker);
2330
+ const schema = { $ref: `#/types/${aliasName}` };
2331
+ if (packageOrigin) {
2332
+ setSchemaExtension(schema, "x-ts-package", packageOrigin);
2333
+ }
2334
+ return schema;
2335
+ }
2336
+ }
2337
+ if (type.isUnion()) {
2338
+ const types = type.types;
2339
+ const allStringLiterals = types.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
2340
+ if (allStringLiterals) {
2341
+ const enumValues = types.map((t) => t.value);
2342
+ return { type: "string", enum: enumValues };
2343
+ }
2344
+ const allNumberLiterals = types.every((t) => t.flags & ts3.TypeFlags.NumberLiteral);
2345
+ if (allNumberLiterals) {
2346
+ const enumValues = types.map((t) => t.value);
2347
+ return { type: "number", enum: enumValues };
2348
+ }
2349
+ if (ctx) {
2350
+ return withDepth(ctx, () => ({
2351
+ anyOf: types.map((t) => buildSchema(t, checker, ctx))
2352
+ }));
2353
+ }
2354
+ return { anyOf: types.map((t) => buildSchema(t, checker, ctx)) };
2355
+ }
2356
+ const isIntersectionType = type.isIntersection() || !!(type.flags & ts3.TypeFlags.Intersection);
2357
+ if (isIntersectionType && "types" in type) {
2358
+ const intersectionType = type;
2359
+ const filteredTypes = intersectionType.types.filter((t) => !(t.flags & ts3.TypeFlags.Never));
2360
+ if (filteredTypes.length === 0) {
2361
+ return { type: "never" };
2362
+ }
2363
+ if (filteredTypes.length === 1) {
2364
+ return buildSchema(filteredTypes[0], checker, ctx);
2365
+ }
2364
2366
  if (ctx) {
2365
2367
  return withDepth(ctx, () => ({
2366
- type: "array",
2367
- items: buildSchema(elementType, checker, ctx)
2368
+ allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx))
2368
2369
  }));
2369
2370
  }
2370
- return { type: "array", items: buildSchema(elementType, checker, ctx) };
2371
+ return { allOf: filteredTypes.map((t) => buildSchema(t, checker, ctx)) };
2371
2372
  }
2372
- return { type: "array" };
2373
- }
2374
- if (checker.isTupleType(type)) {
2375
- const tupleTypeRef = type;
2376
- const elementTypes = checker.getTypeArguments(tupleTypeRef) ?? [];
2377
- if (ctx) {
2378
- return withDepth(ctx, () => {
2379
- const prevInTupleElement = ctx.inTupleElement;
2380
- ctx.inTupleElement = true;
2381
- try {
2382
- return {
2373
+ const typeString = checker.typeToString(type);
2374
+ if (typeString === "never[]" || typeString === "[]") {
2375
+ return { type: "array", prefixedItems: [], minItems: 0, maxItems: 0 };
2376
+ }
2377
+ const symbol = type.getSymbol() || type.aliasSymbol;
2378
+ if (symbol?.getName() === "Array" && isBuiltinSymbol(symbol)) {
2379
+ const typeRef2 = type;
2380
+ const typeArgs2 = typeRef2.target ? checker.getTypeArguments(typeRef2) : undefined;
2381
+ const elementType = typeArgs2?.[0];
2382
+ if (elementType) {
2383
+ return { type: "array", items: buildSchema(elementType, checker, ctx) };
2384
+ }
2385
+ return { type: "array", items: { "x-ts-type": "unknown" } };
2386
+ }
2387
+ if (checker.isArrayType(type)) {
2388
+ const arrayTypeRef = type;
2389
+ const arrayTypeArgs = checker.getTypeArguments(arrayTypeRef);
2390
+ const elementType = arrayTypeArgs?.[0];
2391
+ if (elementType) {
2392
+ if (ctx) {
2393
+ return withDepth(ctx, () => ({
2383
2394
  type: "array",
2384
- prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2385
- minItems: elementTypes.length,
2386
- maxItems: elementTypes.length
2387
- };
2388
- } finally {
2389
- ctx.inTupleElement = prevInTupleElement;
2395
+ items: buildSchema(elementType, checker, ctx)
2396
+ }));
2390
2397
  }
2391
- });
2392
- }
2393
- return {
2394
- type: "array",
2395
- prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2396
- minItems: elementTypes.length,
2397
- maxItems: elementTypes.length
2398
- };
2399
- }
2400
- const typeRef = type;
2401
- const typeArgs = typeRef.target ? checker.getTypeArguments(typeRef) : undefined;
2402
- if (typeRef.target && typeArgs && typeArgs.length > 0) {
2403
- const symbol2 = typeRef.target.getSymbol();
2404
- const name = symbol2?.getName();
2405
- if (name && BUILTIN_TYPES.has(name)) {
2406
- return { $ref: `#/types/${name}` };
2398
+ return { type: "array", items: buildSchema(elementType, checker, ctx) };
2399
+ }
2400
+ return { type: "array" };
2407
2401
  }
2408
- if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
2409
- const packageOrigin = getTypeOrigin(typeRef.target, checker);
2402
+ if (checker.isTupleType(type)) {
2403
+ const tupleTypeRef = type;
2404
+ const elementTypes = checker.getTypeArguments(tupleTypeRef) ?? [];
2410
2405
  if (ctx) {
2411
2406
  return withDepth(ctx, () => {
2412
- const schema2 = {
2413
- $ref: `#/types/${name}`,
2414
- typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
2415
- };
2416
- if (packageOrigin) {
2417
- setSchemaExtension(schema2, "x-ts-package", packageOrigin);
2407
+ const prevInTupleElement = ctx.inTupleElement;
2408
+ ctx.inTupleElement = true;
2409
+ try {
2410
+ return {
2411
+ type: "array",
2412
+ prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2413
+ minItems: elementTypes.length,
2414
+ maxItems: elementTypes.length
2415
+ };
2416
+ } finally {
2417
+ ctx.inTupleElement = prevInTupleElement;
2418
2418
  }
2419
- return schema2;
2420
2419
  });
2421
2420
  }
2422
- const schema = {
2423
- $ref: `#/types/${name}`,
2424
- typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
2421
+ return {
2422
+ type: "array",
2423
+ prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
2424
+ minItems: elementTypes.length,
2425
+ maxItems: elementTypes.length
2425
2426
  };
2426
- if (packageOrigin) {
2427
- setSchemaExtension(schema, "x-ts-package", packageOrigin);
2428
- }
2429
- return schema;
2430
2427
  }
2431
- }
2432
- const aliasTypeArgs = type.aliasTypeArguments;
2433
- const aliasSymbol = type.aliasSymbol;
2434
- if (aliasSymbol && aliasTypeArgs && aliasTypeArgs.length > 0) {
2435
- const name = aliasSymbol.getName();
2436
- if (BUILTIN_TYPES.has(name)) {
2437
- return { $ref: `#/types/${name}` };
2438
- }
2439
- if (isBuiltinGeneric(name) || !name.startsWith("__")) {
2440
- const packageOrigin = getTypeOrigin(type, checker);
2441
- if (ctx) {
2442
- return withDepth(ctx, () => {
2443
- const schema2 = {
2444
- $ref: `#/types/${name}`,
2445
- typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
2446
- };
2447
- if (packageOrigin) {
2448
- setSchemaExtension(schema2, "x-ts-package", packageOrigin);
2449
- }
2450
- return schema2;
2451
- });
2428
+ const typeRef = type;
2429
+ const typeArgs = typeRef.target ? checker.getTypeArguments(typeRef) : undefined;
2430
+ if (typeRef.target && typeArgs && typeArgs.length > 0) {
2431
+ const symbol2 = typeRef.target.getSymbol();
2432
+ const name = symbol2?.getName();
2433
+ if (name && BUILTIN_TYPES.has(name)) {
2434
+ return { $ref: `#/types/${name}` };
2452
2435
  }
2453
- const schema = {
2454
- $ref: `#/types/${name}`,
2455
- typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
2456
- };
2457
- if (packageOrigin) {
2458
- setSchemaExtension(schema, "x-ts-package", packageOrigin);
2436
+ if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
2437
+ const packageOrigin = getTypeOrigin(typeRef.target, checker);
2438
+ if (ctx) {
2439
+ return withDepth(ctx, () => {
2440
+ const schema2 = {
2441
+ $ref: `#/types/${name}`,
2442
+ typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
2443
+ };
2444
+ if (packageOrigin) {
2445
+ setSchemaExtension(schema2, "x-ts-package", packageOrigin);
2446
+ }
2447
+ return schema2;
2448
+ });
2449
+ }
2450
+ const schema = {
2451
+ $ref: `#/types/${name}`,
2452
+ typeArguments: typeArgs.map((t) => buildSchema(t, checker, ctx))
2453
+ };
2454
+ if (packageOrigin) {
2455
+ setSchemaExtension(schema, "x-ts-package", packageOrigin);
2456
+ }
2457
+ return schema;
2459
2458
  }
2460
- return schema;
2461
2459
  }
2462
- }
2463
- if (type.flags & ts3.TypeFlags.Object) {
2464
- const callSignatures = type.getCallSignatures();
2465
- if (callSignatures.length > 0) {
2466
- return buildFunctionSchema(callSignatures, checker, ctx);
2460
+ const aliasTypeArgs = type.aliasTypeArguments;
2461
+ const aliasSymbol = type.aliasSymbol;
2462
+ if (aliasSymbol && aliasTypeArgs && aliasTypeArgs.length > 0) {
2463
+ const name = aliasSymbol.getName();
2464
+ if (BUILTIN_TYPES.has(name)) {
2465
+ return { $ref: `#/types/${name}` };
2466
+ }
2467
+ if (isBuiltinGeneric(name) || !name.startsWith("__")) {
2468
+ const packageOrigin = getTypeOrigin(type, checker);
2469
+ if (ctx) {
2470
+ return withDepth(ctx, () => {
2471
+ const schema2 = {
2472
+ $ref: `#/types/${name}`,
2473
+ typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
2474
+ };
2475
+ if (packageOrigin) {
2476
+ setSchemaExtension(schema2, "x-ts-package", packageOrigin);
2477
+ }
2478
+ return schema2;
2479
+ });
2480
+ }
2481
+ const schema = {
2482
+ $ref: `#/types/${name}`,
2483
+ typeArguments: aliasTypeArgs.map((t) => buildSchema(t, checker, ctx))
2484
+ };
2485
+ if (packageOrigin) {
2486
+ setSchemaExtension(schema, "x-ts-package", packageOrigin);
2487
+ }
2488
+ return schema;
2489
+ }
2467
2490
  }
2468
- }
2469
- if (symbol && !isAnonymous(type)) {
2470
- const name = symbol.getName();
2471
- if (isPrimitiveName(name)) {
2472
- return { type: name };
2491
+ if (type.flags & ts3.TypeFlags.Object) {
2492
+ const callSignatures = type.getCallSignatures();
2493
+ if (callSignatures.length > 0) {
2494
+ return buildFunctionSchema(callSignatures, checker, ctx);
2495
+ }
2473
2496
  }
2474
- if (BUILTIN_TYPES.has(name)) {
2475
- return { $ref: `#/types/${name}` };
2497
+ if (symbol && !isAnonymous(type)) {
2498
+ const name = symbol.getName();
2499
+ if (isPrimitiveName(name)) {
2500
+ return { type: name };
2501
+ }
2502
+ if (BUILTIN_TYPES.has(name)) {
2503
+ return { $ref: `#/types/${name}` };
2504
+ }
2505
+ if (!name.startsWith("__")) {
2506
+ const packageOrigin = getTypeOrigin(type, checker);
2507
+ const schema = { $ref: `#/types/${name}` };
2508
+ if (packageOrigin) {
2509
+ setSchemaExtension(schema, "x-ts-package", packageOrigin);
2510
+ }
2511
+ return schema;
2512
+ }
2476
2513
  }
2477
- if (!name.startsWith("__")) {
2478
- const packageOrigin = getTypeOrigin(type, checker);
2479
- const schema = { $ref: `#/types/${name}` };
2480
- if (packageOrigin) {
2481
- setSchemaExtension(schema, "x-ts-package", packageOrigin);
2514
+ if (type.flags & ts3.TypeFlags.Object) {
2515
+ const objectType = type;
2516
+ const properties = type.getProperties();
2517
+ if (properties.length > 0 || objectType.objectFlags & ts3.ObjectFlags.Anonymous) {
2518
+ return buildObjectSchema(properties, checker, ctx, type);
2482
2519
  }
2483
- return schema;
2484
2520
  }
2485
- }
2486
- if (type.flags & ts3.TypeFlags.Object) {
2487
- const objectType = type;
2488
- const properties = type.getProperties();
2489
- if (properties.length > 0 || objectType.objectFlags & ts3.ObjectFlags.Anonymous) {
2490
- return buildObjectSchema(properties, checker, ctx, type);
2521
+ return { type: checker.typeToString(type) };
2522
+ } finally {
2523
+ if (addedToVisited) {
2524
+ ctx.visitedTypes.delete(type);
2491
2525
  }
2492
2526
  }
2493
- return { type: checker.typeToString(type) };
2494
2527
  }
2495
2528
  function buildFunctionSchema(callSignatures, checker, ctx) {
2496
2529
  const buildSignatures = () => {
@@ -2801,11 +2834,11 @@ function extractDefaultValue(initializer) {
2801
2834
  function registerReferencedTypes(type, ctx, depth = 0) {
2802
2835
  if (depth > ctx.maxTypeDepth)
2803
2836
  return;
2804
- if (ctx.visitedTypes.has(type))
2837
+ if (ctx.registeredTypes.has(type))
2805
2838
  return;
2806
2839
  const isPrimitive = type.flags & (ts4.TypeFlags.String | ts4.TypeFlags.Number | ts4.TypeFlags.Boolean | ts4.TypeFlags.Void | ts4.TypeFlags.Undefined | ts4.TypeFlags.Null | ts4.TypeFlags.Any | ts4.TypeFlags.Unknown | ts4.TypeFlags.Never | ts4.TypeFlags.StringLiteral | ts4.TypeFlags.NumberLiteral | ts4.TypeFlags.BooleanLiteral);
2807
2840
  if (!isPrimitive) {
2808
- ctx.visitedTypes.add(type);
2841
+ ctx.registeredTypes.add(type);
2809
2842
  }
2810
2843
  const { typeChecker: checker, typeRegistry } = ctx;
2811
2844
  typeRegistry.registerType(type, ctx);
@@ -3049,13 +3082,14 @@ function createContext(program, sourceFile, options = {}) {
3049
3082
  typeChecker: program.getTypeChecker(),
3050
3083
  program,
3051
3084
  sourceFile,
3052
- maxTypeDepth: options.maxTypeDepth ?? 4,
3085
+ maxTypeDepth: options.maxTypeDepth ?? 5,
3053
3086
  maxExternalTypeDepth: options.maxExternalTypeDepth ?? 2,
3054
3087
  currentDepth: 0,
3055
3088
  resolveExternalTypes: options.resolveExternalTypes ?? true,
3056
3089
  typeRegistry: new TypeRegistry,
3057
3090
  exportedIds: new Set,
3058
3091
  visitedTypes: new Set,
3092
+ registeredTypes: new Set,
3059
3093
  includePrivate: options.includePrivate ?? false,
3060
3094
  maxProperties: options.maxProperties ?? 20,
3061
3095
  onTruncation: options.onTruncation
@@ -5896,6 +5930,41 @@ async function extract(options) {
5896
5930
  }
5897
5931
  const verification = buildVerificationSummary(exportedSymbols.length, exports.length, exportTracker);
5898
5932
  const meta = await getPackageMeta(entryFile, baseDir);
5933
+ {
5934
+ const symFlags = ts16.SymbolFlags.Type | ts16.SymbolFlags.Interface | ts16.SymbolFlags.Class;
5935
+ const maxPasses = 5;
5936
+ for (let pass = 0;pass < maxPasses; pass++) {
5937
+ const allRefs = new Map;
5938
+ for (const exp of exports) {
5939
+ collectAllRefsWithContext(exp, allRefs, {
5940
+ exportName: exp.id || exp.name,
5941
+ location: "property",
5942
+ path: []
5943
+ });
5944
+ }
5945
+ for (const t of ctx.typeRegistry.getAll()) {
5946
+ collectAllRefsWithContext(t, allRefs, {
5947
+ exportName: t.id,
5948
+ location: "property",
5949
+ path: []
5950
+ });
5951
+ }
5952
+ let added = 0;
5953
+ for (const [typeName] of allRefs) {
5954
+ if (ctx.typeRegistry.has(typeName))
5955
+ continue;
5956
+ if (BUILTIN_TYPES2.has(typeName))
5957
+ continue;
5958
+ const tsType = findTypeInProgram(typeName, typeChecker, program, sourceFile, symFlags);
5959
+ if (tsType) {
5960
+ ctx.typeRegistry.registerType(tsType, ctx);
5961
+ added++;
5962
+ }
5963
+ }
5964
+ if (added === 0)
5965
+ break;
5966
+ }
5967
+ }
5899
5968
  const types = ctx.typeRegistry.getAll();
5900
5969
  const projectBaseDir = baseDir ?? path8.dirname(entryFile);
5901
5970
  const definedTypes = new Set(types.map((t) => t.id));
@@ -6219,20 +6288,42 @@ function createEmptySpec(entryFile, includeSchema, isDtsSource) {
6219
6288
  }
6220
6289
  };
6221
6290
  }
6291
+ function findTypeInProgram(name, checker, program, sourceFile, symFlags) {
6292
+ const localSym = checker.resolveName(name, sourceFile, symFlags, false);
6293
+ if (localSym)
6294
+ return checker.getDeclaredTypeOfSymbol(localSym);
6295
+ const entryDir = path8.dirname(sourceFile.fileName);
6296
+ for (const sf of program.getSourceFiles()) {
6297
+ const fn = sf.fileName;
6298
+ if (fn.includes("/typescript/lib/lib.") || fn.includes("\\typescript\\lib\\lib."))
6299
+ continue;
6300
+ if (fn.includes("/@types/node/") || fn.includes("\\@types\\node\\"))
6301
+ continue;
6302
+ if (fn.startsWith(entryDir))
6303
+ continue;
6304
+ const sym = checker.resolveName(name, sf, symFlags, false);
6305
+ if (sym)
6306
+ return checker.getDeclaredTypeOfSymbol(sym);
6307
+ }
6308
+ return;
6309
+ }
6222
6310
  async function getPackageMeta(entryFile, baseDir) {
6223
- const searchDir = baseDir ?? path8.dirname(entryFile);
6224
- const pkgPath = path8.join(searchDir, "package.json");
6225
- try {
6226
- if (fs7.existsSync(pkgPath)) {
6227
- const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
6228
- return {
6229
- name: pkg.name ?? path8.basename(searchDir),
6230
- version: pkg.version,
6231
- description: pkg.description
6232
- };
6233
- }
6234
- } catch {}
6235
- return { name: path8.basename(searchDir) };
6311
+ let dir = baseDir ?? path8.dirname(entryFile);
6312
+ while (dir !== path8.dirname(dir)) {
6313
+ const pkgPath = path8.join(dir, "package.json");
6314
+ try {
6315
+ if (fs7.existsSync(pkgPath)) {
6316
+ const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
6317
+ return {
6318
+ name: pkg.name ?? path8.basename(dir),
6319
+ version: pkg.version,
6320
+ description: pkg.description
6321
+ };
6322
+ }
6323
+ } catch {}
6324
+ dir = path8.dirname(dir);
6325
+ }
6326
+ return { name: path8.basename(baseDir ?? path8.dirname(entryFile)) };
6236
6327
  }
6237
6328
 
6238
6329
  // src/primitives/spec.ts
@@ -115,6 +115,11 @@ function formatSchema(schema, options) {
115
115
  return "unknown";
116
116
  if (typeof schema === "string")
117
117
  return schema;
118
+ const depth = options?._depth ?? 0;
119
+ const maxDepth = options?.maxDepth ?? 3;
120
+ if (depth >= maxDepth)
121
+ return "...";
122
+ const nextOpts = { ...options, _depth: depth + 1 };
118
123
  const withPackage = (typeStr) => {
119
124
  if (options?.includePackage && typeof schema === "object" && "x-ts-package" in schema) {
120
125
  const pkg = schema["x-ts-package"];
@@ -135,41 +140,41 @@ function formatSchema(schema, options) {
135
140
  }
136
141
  if ("x-ts-type-predicate" in schema) {
137
142
  const pred = schema["x-ts-type-predicate"];
138
- return `${pred.parameterName} is ${formatSchema(pred.type, options)}`;
143
+ return `${pred.parameterName} is ${formatSchema(pred.type, nextOpts)}`;
139
144
  }
140
145
  if ("$ref" in schema && typeof schema.$ref === "string") {
141
146
  const baseName = schema.$ref.replace("#/types/", "");
142
147
  if ("x-ts-type-arguments" in schema && Array.isArray(schema["x-ts-type-arguments"])) {
143
- const args = schema["x-ts-type-arguments"].map((s) => formatSchema(s, options)).join(", ");
148
+ const args = schema["x-ts-type-arguments"].map((s) => formatSchema(s, nextOpts)).join(", ");
144
149
  return withPackage(`${baseName}<${args}>`);
145
150
  }
146
151
  return withPackage(baseName);
147
152
  }
148
153
  if ("anyOf" in schema && Array.isArray(schema.anyOf)) {
149
- const threshold = options?.collapseUnionThreshold;
154
+ const threshold = options?.collapseUnionThreshold ?? 5;
150
155
  const members = schema.anyOf;
151
- if (threshold && members.length > threshold) {
156
+ if (members.length > threshold) {
152
157
  const shown = members.slice(0, 3);
153
158
  const remaining = members.length - 3;
154
- const shownStr = shown.map((s) => formatSchema(s, options)).join(" | ");
155
- return `${shownStr} | ... (${remaining} more)`;
159
+ const shownStr = shown.map((s) => formatSchema(s, nextOpts)).join(" | ");
160
+ return `${shownStr} | ... and ${remaining} more`;
156
161
  }
157
- return members.map((s) => formatSchema(s, options)).join(" | ");
162
+ return members.map((s) => formatSchema(s, nextOpts)).join(" | ");
158
163
  }
159
164
  if ("allOf" in schema && Array.isArray(schema.allOf)) {
160
- return schema.allOf.map((s) => formatSchema(s, options)).join(" & ");
165
+ return schema.allOf.map((s) => formatSchema(s, nextOpts)).join(" & ");
161
166
  }
162
167
  if ("type" in schema && schema.type === "array") {
163
- const items = "items" in schema ? formatSchema(schema.items, options) : "unknown";
168
+ const items = "items" in schema ? formatSchema(schema.items, nextOpts) : "unknown";
164
169
  return `${items}[]`;
165
170
  }
166
171
  if ("type" in schema && schema.type === "tuple" && "items" in schema) {
167
- const items = schema.items.map((s) => formatSchema(s, options)).join(", ");
172
+ const items = schema.items.map((s) => formatSchema(s, nextOpts)).join(", ");
168
173
  return `[${items}]`;
169
174
  }
170
175
  if ("type" in schema && schema.type === "object") {
171
176
  if ("properties" in schema && schema.properties) {
172
- const props = Object.entries(schema.properties).map(([k, v]) => `${k}: ${formatSchema(v, options)}`).join("; ");
177
+ const props = Object.entries(schema.properties).map(([k, v]) => `${k}: ${formatSchema(v, nextOpts)}`).join("; ");
173
178
  return `{ ${props} }`;
174
179
  }
175
180
  return "object";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/sdk",
3
- "version": "0.35.1",
3
+ "version": "0.36.0",
4
4
  "description": "TypeScript API extraction SDK - programmatic primitives for OpenPkg specs",
5
5
  "keywords": [
6
6
  "openpkg",