@openpkg-ts/extract 0.12.0 → 0.14.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.
@@ -203,17 +203,133 @@ class TypeRegistry {
203
203
  if (external) {
204
204
  kind = "external";
205
205
  }
206
- const typeString = checker.typeToString(type);
207
206
  const source = decl ? buildExternalSource(decl) : undefined;
207
+ const schema = this.buildShallowSchema(type, checker);
208
+ let members;
209
+ if (decl && (ts.isClassDeclaration(decl) || ts.isInterfaceDeclaration(decl))) {
210
+ members = this.extractShallowMembers(decl, checker);
211
+ }
208
212
  return {
209
213
  id: name,
210
214
  name,
211
215
  kind,
212
- type: typeString !== name ? typeString : undefined,
216
+ schema,
217
+ members: members?.length ? members : undefined,
213
218
  ...external ? { external: true } : {},
214
219
  ...source ? { source } : {}
215
220
  };
216
221
  }
222
+ buildShallowSchema(type, checker) {
223
+ if (type.isUnion()) {
224
+ const allEnumLiterals = type.types.every((t) => t.flags & (ts.TypeFlags.EnumLiteral | ts.TypeFlags.NumberLiteral));
225
+ if (allEnumLiterals) {
226
+ const values = type.types.map((t) => {
227
+ if (t.flags & ts.TypeFlags.NumberLiteral) {
228
+ return t.value;
229
+ }
230
+ return checker.typeToString(t);
231
+ }).filter((v) => v !== undefined);
232
+ if (values.every((v) => typeof v === "number")) {
233
+ return { type: "number", enum: values };
234
+ }
235
+ return { type: "string", enum: values };
236
+ }
237
+ return {
238
+ anyOf: type.types.map((t) => {
239
+ if (t.flags & ts.TypeFlags.EnumLiteral) {
240
+ const value = t.value;
241
+ if (typeof value === "number") {
242
+ return { type: "number", enum: [value] };
243
+ }
244
+ return { type: checker.typeToString(t) };
245
+ }
246
+ const sym = t.getSymbol() || t.aliasSymbol;
247
+ if (sym && sym.flags & ts.SymbolFlags.EnumMember) {
248
+ return { type: checker.typeToString(t) };
249
+ }
250
+ if (sym && !sym.getName().startsWith("__")) {
251
+ return { $ref: `#/types/${sym.getName()}` };
252
+ }
253
+ if (t.flags & ts.TypeFlags.StringLiteral) {
254
+ return { type: "string", enum: [t.value] };
255
+ }
256
+ if (t.flags & ts.TypeFlags.NumberLiteral) {
257
+ return { type: "number", enum: [t.value] };
258
+ }
259
+ return { type: checker.typeToString(t) };
260
+ })
261
+ };
262
+ }
263
+ if (type.isIntersection()) {
264
+ return {
265
+ allOf: type.types.map((t) => {
266
+ const sym = t.getSymbol() || t.aliasSymbol;
267
+ if (sym && !sym.getName().startsWith("__")) {
268
+ return { $ref: `#/types/${sym.getName()}` };
269
+ }
270
+ return { type: checker.typeToString(t) };
271
+ })
272
+ };
273
+ }
274
+ const props = type.getProperties();
275
+ if (props.length > 0) {
276
+ const properties = {};
277
+ const required = [];
278
+ for (const prop of props.slice(0, 30)) {
279
+ const propName = prop.getName();
280
+ if (propName.startsWith("_"))
281
+ continue;
282
+ const propType = checker.getTypeOfSymbol(prop);
283
+ const callSigs = propType.getCallSignatures();
284
+ if (callSigs.length > 0) {
285
+ properties[propName] = { type: "function" };
286
+ } else {
287
+ const propSym = propType.getSymbol() || propType.aliasSymbol;
288
+ const symName = propSym?.getName();
289
+ if (propSym && symName && !symName.startsWith("__") && symName !== propName) {
290
+ properties[propName] = { $ref: `#/types/${symName}` };
291
+ } else {
292
+ properties[propName] = { type: checker.typeToString(propType) };
293
+ }
294
+ }
295
+ if (!(prop.flags & ts.SymbolFlags.Optional)) {
296
+ required.push(propName);
297
+ }
298
+ }
299
+ return {
300
+ type: "object",
301
+ properties,
302
+ ...required.length ? { required } : {}
303
+ };
304
+ }
305
+ return { type: checker.typeToString(type) };
306
+ }
307
+ extractShallowMembers(decl, checker) {
308
+ const members = [];
309
+ for (const member of decl.members) {
310
+ if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) {
311
+ const name = member.name?.getText();
312
+ if (!name || name.startsWith("#") || name.startsWith("_"))
313
+ continue;
314
+ const type = checker.getTypeAtLocation(member);
315
+ const sym = type.getSymbol() || type.aliasSymbol;
316
+ members.push({
317
+ name,
318
+ kind: "property",
319
+ schema: sym && !sym.getName().startsWith("__") ? { $ref: `#/types/${sym.getName()}` } : { type: checker.typeToString(type) }
320
+ });
321
+ } else if (ts.isMethodDeclaration(member) || ts.isMethodSignature(member)) {
322
+ const name = member.name?.getText();
323
+ if (!name || name.startsWith("#") || name.startsWith("_"))
324
+ continue;
325
+ members.push({
326
+ name,
327
+ kind: "method"
328
+ });
329
+ }
330
+ }
331
+ return members;
332
+ }
217
333
  registerFromSymbol(symbol, checker) {
218
334
  const name = symbol.getName();
219
335
  if (this.has(name))
@@ -289,6 +405,44 @@ function getParamDescription(propertyName, jsdocTags, inferredAlias) {
289
405
  }
290
406
  return;
291
407
  }
408
+ function extractTypeParameters(node, checker) {
409
+ if (!node.typeParameters || node.typeParameters.length === 0) {
410
+ return;
411
+ }
412
+ return node.typeParameters.map((tp) => {
413
+ const name = tp.name.text;
414
+ let constraint;
415
+ if (tp.constraint) {
416
+ const constraintType = checker.getTypeAtLocation(tp.constraint);
417
+ constraint = checker.typeToString(constraintType);
418
+ }
419
+ let defaultType;
420
+ if (tp.default) {
421
+ const defType = checker.getTypeAtLocation(tp.default);
422
+ defaultType = checker.typeToString(defType);
423
+ }
424
+ return {
425
+ name,
426
+ ...constraint ? { constraint } : {},
427
+ ...defaultType ? { default: defaultType } : {}
428
+ };
429
+ });
430
+ }
431
+ function isSymbolDeprecated(symbol) {
432
+ if (!symbol) {
433
+ return false;
434
+ }
435
+ const jsDocTags = symbol.getJsDocTags();
436
+ if (jsDocTags.some((tag) => tag.name.toLowerCase() === "deprecated")) {
437
+ return true;
438
+ }
439
+ for (const declaration of symbol.getDeclarations() ?? []) {
440
+ if (ts2.getJSDocDeprecatedTag(declaration)) {
441
+ return true;
442
+ }
443
+ }
444
+ return false;
445
+ }
292
446
 
293
447
  // src/compiler/program.ts
294
448
  import * as path2 from "node:path";
@@ -339,59 +493,32 @@ function createProgram({
339
493
  };
340
494
  }
341
495
 
342
- // src/serializers/classes.ts
343
- function serializeClass(node, ctx) {
344
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
345
- const name = symbol?.getName() ?? node.name?.getText();
346
- if (!name)
347
- return null;
348
- const declSourceFile = node.getSourceFile();
349
- const { description, tags, examples } = getJSDocComment(node);
350
- const source = getSourceLocation(node, declSourceFile);
351
- return {
352
- id: name,
353
- name,
354
- kind: "class",
355
- description,
356
- tags,
357
- source,
358
- members: [],
359
- ...examples.length > 0 ? { examples } : {}
360
- };
361
- }
362
-
363
- // src/serializers/enums.ts
364
- function serializeEnum(node, ctx) {
365
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
366
- const name = symbol?.getName() ?? node.name?.getText();
367
- if (!name)
368
- return null;
369
- const declSourceFile = node.getSourceFile();
370
- const { description, tags, examples } = getJSDocComment(node);
371
- const source = getSourceLocation(node, declSourceFile);
372
- const members = node.members.map((member) => {
373
- const memberSymbol = ctx.typeChecker.getSymbolAtLocation(member.name);
374
- const memberName = memberSymbol?.getName() ?? member.name.getText();
375
- return {
376
- id: memberName,
377
- name: memberName,
378
- kind: "enum-member"
379
- };
380
- });
381
- return {
382
- id: name,
383
- name,
384
- kind: "enum",
385
- description,
386
- tags,
387
- source,
388
- members,
389
- ...examples.length > 0 ? { examples } : {}
390
- };
391
- }
392
-
393
496
  // src/types/schema-builder.ts
394
497
  import ts4 from "typescript";
498
+ var BUILTIN_TYPE_SCHEMAS = {
499
+ Date: { type: "string", format: "date-time" },
500
+ RegExp: { type: "object", description: "RegExp" },
501
+ Error: { type: "object" },
502
+ Promise: { type: "object" },
503
+ Map: { type: "object" },
504
+ Set: { type: "object" },
505
+ WeakMap: { type: "object" },
506
+ WeakSet: { type: "object" },
507
+ Function: { type: "object" },
508
+ ArrayBuffer: { type: "string", format: "binary" },
509
+ ArrayBufferLike: { type: "string", format: "binary" },
510
+ DataView: { type: "string", format: "binary" },
511
+ Uint8Array: { type: "string", format: "byte" },
512
+ Uint16Array: { type: "string", format: "byte" },
513
+ Uint32Array: { type: "string", format: "byte" },
514
+ Int8Array: { type: "string", format: "byte" },
515
+ Int16Array: { type: "string", format: "byte" },
516
+ Int32Array: { type: "string", format: "byte" },
517
+ Float32Array: { type: "string", format: "byte" },
518
+ Float64Array: { type: "string", format: "byte" },
519
+ BigInt64Array: { type: "string", format: "byte" },
520
+ BigUint64Array: { type: "string", format: "byte" }
521
+ };
395
522
  var PRIMITIVES2 = new Set([
396
523
  "string",
397
524
  "number",
@@ -487,10 +614,10 @@ function buildSchema(type, checker, ctx, _depth = 0) {
487
614
  if (isAtMaxDepth(ctx)) {
488
615
  return { type: checker.typeToString(type) };
489
616
  }
490
- if (ctx && ctx.visitedTypes.has(type)) {
617
+ if (ctx?.visitedTypes.has(type)) {
491
618
  const symbol2 = type.getSymbol() || type.aliasSymbol;
492
619
  if (symbol2) {
493
- return { $ref: symbol2.getName() };
620
+ return { $ref: `#/types/${symbol2.getName()}` };
494
621
  }
495
622
  return { type: checker.typeToString(type) };
496
623
  }
@@ -574,15 +701,15 @@ function buildSchema(type, checker, ctx, _depth = 0) {
574
701
  const elementTypes = typeRef2.typeArguments ?? [];
575
702
  if (ctx) {
576
703
  return withDepth(ctx, () => ({
577
- type: "tuple",
578
- items: elementTypes.map((t) => buildSchema(t, checker, ctx)),
704
+ type: "array",
705
+ prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
579
706
  minItems: elementTypes.length,
580
707
  maxItems: elementTypes.length
581
708
  }));
582
709
  }
583
710
  return {
584
- type: "tuple",
585
- items: elementTypes.map((t) => buildSchema(t, checker, ctx)),
711
+ type: "array",
712
+ prefixedItems: elementTypes.map((t) => buildSchema(t, checker, ctx)),
586
713
  minItems: elementTypes.length,
587
714
  maxItems: elementTypes.length
588
715
  };
@@ -591,15 +718,18 @@ function buildSchema(type, checker, ctx, _depth = 0) {
591
718
  if (typeRef.target && typeRef.typeArguments && typeRef.typeArguments.length > 0) {
592
719
  const symbol2 = typeRef.target.getSymbol();
593
720
  const name = symbol2?.getName();
721
+ if (name && BUILTIN_TYPES.has(name)) {
722
+ return { $ref: `#/types/${name}` };
723
+ }
594
724
  if (name && (isBuiltinGeneric(name) || !isAnonymous(typeRef.target))) {
595
725
  if (ctx) {
596
726
  return withDepth(ctx, () => ({
597
- $ref: name,
727
+ $ref: `#/types/${name}`,
598
728
  typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
599
729
  }));
600
730
  }
601
731
  return {
602
- $ref: name,
732
+ $ref: `#/types/${name}`,
603
733
  typeArguments: typeRef.typeArguments.map((t) => buildSchema(t, checker, ctx))
604
734
  };
605
735
  }
@@ -611,10 +741,10 @@ function buildSchema(type, checker, ctx, _depth = 0) {
611
741
  return { type: name };
612
742
  }
613
743
  if (BUILTIN_TYPES.has(name)) {
614
- return { $ref: name };
744
+ return { $ref: `#/types/${name}` };
615
745
  }
616
746
  if (!name.startsWith("__")) {
617
- return { $ref: name };
747
+ return { $ref: `#/types/${name}` };
618
748
  }
619
749
  }
620
750
  if (type.flags & ts4.TypeFlags.Object) {
@@ -681,6 +811,110 @@ function buildObjectSchema(properties, checker, ctx) {
681
811
  }
682
812
  return buildProps();
683
813
  }
814
+ function isPureRefSchema(schema) {
815
+ return typeof schema === "object" && Object.keys(schema).length === 1 && "$ref" in schema;
816
+ }
817
+ function withDescription(schema, description) {
818
+ if (isPureRefSchema(schema)) {
819
+ return {
820
+ allOf: [schema],
821
+ description
822
+ };
823
+ }
824
+ return { ...schema, description };
825
+ }
826
+ function schemaIsAny(schema) {
827
+ if (typeof schema === "string") {
828
+ return schema === "any";
829
+ }
830
+ if ("type" in schema && schema.type === "any" && Object.keys(schema).length === 1) {
831
+ return true;
832
+ }
833
+ return false;
834
+ }
835
+ function schemasAreEqual(left, right) {
836
+ if (typeof left !== typeof right) {
837
+ return false;
838
+ }
839
+ if (typeof left === "string" && typeof right === "string") {
840
+ return left === right;
841
+ }
842
+ if (left == null || right == null) {
843
+ return left === right;
844
+ }
845
+ const normalize = (value) => {
846
+ if (Array.isArray(value)) {
847
+ return value.map((item) => normalize(item));
848
+ }
849
+ if (value && typeof value === "object") {
850
+ const sortedEntries = Object.entries(value).map(([key, val]) => [key, normalize(val)]).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
851
+ return Object.fromEntries(sortedEntries);
852
+ }
853
+ return value;
854
+ };
855
+ return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
856
+ }
857
+ function deduplicateSchemas(schemas) {
858
+ const result = [];
859
+ for (const schema of schemas) {
860
+ const isDuplicate = result.some((existing) => schemasAreEqual(existing, schema));
861
+ if (!isDuplicate) {
862
+ result.push(schema);
863
+ }
864
+ }
865
+ return result;
866
+ }
867
+ function findDiscriminatorProperty(unionTypes, checker) {
868
+ const memberProps = [];
869
+ for (const t of unionTypes) {
870
+ if (t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined)) {
871
+ continue;
872
+ }
873
+ const props = t.getProperties();
874
+ if (!props || props.length === 0) {
875
+ return;
876
+ }
877
+ const propValues = new Map;
878
+ for (const prop of props) {
879
+ const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
880
+ if (!declaration)
881
+ continue;
882
+ try {
883
+ const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
884
+ if (propType.isStringLiteral()) {
885
+ propValues.set(prop.getName(), propType.value);
886
+ } else if (propType.isNumberLiteral()) {
887
+ propValues.set(prop.getName(), propType.value);
888
+ }
889
+ } catch {}
890
+ }
891
+ memberProps.push(propValues);
892
+ }
893
+ if (memberProps.length < 2) {
894
+ return;
895
+ }
896
+ const firstMember = memberProps[0];
897
+ for (const [propName, firstValue] of firstMember) {
898
+ const values = new Set([firstValue]);
899
+ let isDiscriminator = true;
900
+ for (let i = 1;i < memberProps.length; i++) {
901
+ const value = memberProps[i].get(propName);
902
+ if (value === undefined) {
903
+ isDiscriminator = false;
904
+ break;
905
+ }
906
+ if (values.has(value)) {
907
+ isDiscriminator = false;
908
+ break;
909
+ }
910
+ values.add(value);
911
+ }
912
+ if (isDiscriminator) {
913
+ return propName;
914
+ }
915
+ }
916
+ return;
917
+ }
684
918
 
685
919
  // src/types/parameters.ts
686
920
  import ts5 from "typescript";
@@ -801,7 +1035,10 @@ function extractDefaultValue(initializer) {
801
1035
  function registerReferencedTypes(type, ctx) {
802
1036
  if (ctx.visitedTypes.has(type))
803
1037
  return;
804
- ctx.visitedTypes.add(type);
1038
+ const isPrimitive = type.flags & (ts5.TypeFlags.String | ts5.TypeFlags.Number | ts5.TypeFlags.Boolean | ts5.TypeFlags.Void | ts5.TypeFlags.Undefined | ts5.TypeFlags.Null | ts5.TypeFlags.Any | ts5.TypeFlags.Unknown | ts5.TypeFlags.Never | ts5.TypeFlags.StringLiteral | ts5.TypeFlags.NumberLiteral | ts5.TypeFlags.BooleanLiteral);
1039
+ if (!isPrimitive) {
1040
+ ctx.visitedTypes.add(type);
1041
+ }
805
1042
  const { typeChecker: checker, typeRegistry, exportedIds } = ctx;
806
1043
  typeRegistry.registerType(type, checker, exportedIds);
807
1044
  const typeArgs = type.typeArguments;
@@ -822,6 +1059,280 @@ function registerReferencedTypes(type, ctx) {
822
1059
  }
823
1060
  }
824
1061
 
1062
+ // src/serializers/classes.ts
1063
+ import ts6 from "typescript";
1064
+ function serializeClass(node, ctx) {
1065
+ const { typeChecker: checker } = ctx;
1066
+ const symbol = checker.getSymbolAtLocation(node.name ?? node);
1067
+ const name = symbol?.getName() ?? node.name?.getText();
1068
+ if (!name)
1069
+ return null;
1070
+ const declSourceFile = node.getSourceFile();
1071
+ const { description, tags, examples } = getJSDocComment(node);
1072
+ const source = getSourceLocation(node, declSourceFile);
1073
+ const typeParameters = extractTypeParameters(node, checker);
1074
+ const members = [];
1075
+ const signatures = [];
1076
+ const methodsByName = new Map;
1077
+ for (const member of node.members) {
1078
+ const memberName = getMemberName(member);
1079
+ if (memberName?.startsWith("#"))
1080
+ continue;
1081
+ if (ts6.isPropertyDeclaration(member)) {
1082
+ const propMember = serializeProperty(member, ctx);
1083
+ if (propMember)
1084
+ members.push(propMember);
1085
+ } else if (ts6.isMethodDeclaration(member)) {
1086
+ const methodMember = serializeMethod(member, ctx);
1087
+ if (methodMember?.name) {
1088
+ if (!methodsByName.has(methodMember.name)) {
1089
+ methodsByName.set(methodMember.name, methodMember);
1090
+ } else {
1091
+ const existing = methodsByName.get(methodMember.name);
1092
+ if (!existing.description && methodMember.description) {
1093
+ existing.description = methodMember.description;
1094
+ }
1095
+ if (!existing.tags && methodMember.tags) {
1096
+ existing.tags = methodMember.tags;
1097
+ }
1098
+ }
1099
+ }
1100
+ } else if (ts6.isConstructorDeclaration(member)) {
1101
+ const ctorSig = serializeConstructor(member, ctx);
1102
+ if (ctorSig)
1103
+ signatures.push(ctorSig);
1104
+ } else if (ts6.isGetAccessorDeclaration(member) || ts6.isSetAccessorDeclaration(member)) {
1105
+ const accessorMember = serializeAccessor(member, ctx);
1106
+ if (accessorMember)
1107
+ members.push(accessorMember);
1108
+ }
1109
+ }
1110
+ members.push(...methodsByName.values());
1111
+ const extendsClause = getExtendsClause(node, checker);
1112
+ const implementsClause = getImplementsClause(node, checker);
1113
+ return {
1114
+ id: name,
1115
+ name,
1116
+ kind: "class",
1117
+ description,
1118
+ tags,
1119
+ source,
1120
+ typeParameters,
1121
+ members: members.length > 0 ? members : undefined,
1122
+ signatures: signatures.length > 0 ? signatures : undefined,
1123
+ extends: extendsClause,
1124
+ implements: implementsClause?.length ? implementsClause : undefined,
1125
+ ...examples.length > 0 ? { examples } : {}
1126
+ };
1127
+ }
1128
+ function getMemberName(member) {
1129
+ if (ts6.isConstructorDeclaration(member))
1130
+ return "constructor";
1131
+ if (!member.name)
1132
+ return;
1133
+ if (ts6.isIdentifier(member.name))
1134
+ return member.name.text;
1135
+ if (ts6.isPrivateIdentifier(member.name))
1136
+ return member.name.text;
1137
+ return member.name.getText();
1138
+ }
1139
+ function getVisibility(member) {
1140
+ const modifiers = ts6.getModifiers(member);
1141
+ if (!modifiers)
1142
+ return;
1143
+ for (const mod of modifiers) {
1144
+ if (mod.kind === ts6.SyntaxKind.PrivateKeyword)
1145
+ return "private";
1146
+ if (mod.kind === ts6.SyntaxKind.ProtectedKeyword)
1147
+ return "protected";
1148
+ if (mod.kind === ts6.SyntaxKind.PublicKeyword)
1149
+ return "public";
1150
+ }
1151
+ return;
1152
+ }
1153
+ function isStatic(member) {
1154
+ const modifiers = ts6.getModifiers(member);
1155
+ return modifiers?.some((m) => m.kind === ts6.SyntaxKind.StaticKeyword) ?? false;
1156
+ }
1157
+ function isReadonly(member) {
1158
+ const modifiers = ts6.getModifiers(member);
1159
+ return modifiers?.some((m) => m.kind === ts6.SyntaxKind.ReadonlyKeyword) ?? false;
1160
+ }
1161
+ function serializeProperty(node, ctx) {
1162
+ const { typeChecker: checker } = ctx;
1163
+ const name = getMemberName(node);
1164
+ if (!name)
1165
+ return null;
1166
+ const { description, tags } = getJSDocComment(node);
1167
+ const visibility = getVisibility(node);
1168
+ const type = checker.getTypeAtLocation(node);
1169
+ const schema = buildSchema(type, checker, ctx);
1170
+ registerReferencedTypes(type, ctx);
1171
+ const flags = {};
1172
+ if (isStatic(node))
1173
+ flags.static = true;
1174
+ if (isReadonly(node))
1175
+ flags.readonly = true;
1176
+ if (node.questionToken)
1177
+ flags.optional = true;
1178
+ return {
1179
+ name,
1180
+ kind: "property",
1181
+ description,
1182
+ tags: tags.length > 0 ? tags : undefined,
1183
+ visibility,
1184
+ schema,
1185
+ flags: Object.keys(flags).length > 0 ? flags : undefined
1186
+ };
1187
+ }
1188
+ function serializeMethod(node, ctx) {
1189
+ const { typeChecker: checker } = ctx;
1190
+ const name = getMemberName(node);
1191
+ if (!name)
1192
+ return null;
1193
+ const { description, tags } = getJSDocComment(node);
1194
+ const visibility = getVisibility(node);
1195
+ const type = checker.getTypeAtLocation(node);
1196
+ const callSignatures = type.getCallSignatures();
1197
+ const signatures = callSignatures.map((sig) => {
1198
+ const params = extractParameters(sig, ctx);
1199
+ const returnType = checker.getReturnTypeOfSignature(sig);
1200
+ registerReferencedTypes(returnType, ctx);
1201
+ return {
1202
+ parameters: params.length > 0 ? params : undefined,
1203
+ returns: {
1204
+ schema: buildSchema(returnType, checker, ctx)
1205
+ }
1206
+ };
1207
+ });
1208
+ const flags = {};
1209
+ if (isStatic(node))
1210
+ flags.static = true;
1211
+ if (node.asteriskToken)
1212
+ flags.generator = true;
1213
+ const modifiers = ts6.getModifiers(node);
1214
+ if (modifiers?.some((m) => m.kind === ts6.SyntaxKind.AsyncKeyword)) {
1215
+ flags.async = true;
1216
+ }
1217
+ return {
1218
+ name,
1219
+ kind: "method",
1220
+ description,
1221
+ tags: tags.length > 0 ? tags : undefined,
1222
+ visibility,
1223
+ signatures: signatures.length > 0 ? signatures : undefined,
1224
+ flags: Object.keys(flags).length > 0 ? flags : undefined
1225
+ };
1226
+ }
1227
+ function serializeConstructor(node, ctx) {
1228
+ const { typeChecker: checker } = ctx;
1229
+ const { description } = getJSDocComment(node);
1230
+ const sig = checker.getSignatureFromDeclaration(node);
1231
+ if (!sig)
1232
+ return null;
1233
+ const params = extractParameters(sig, ctx);
1234
+ return {
1235
+ description,
1236
+ parameters: params.length > 0 ? params : undefined
1237
+ };
1238
+ }
1239
+ function serializeAccessor(node, ctx) {
1240
+ const { typeChecker: checker } = ctx;
1241
+ const name = getMemberName(node);
1242
+ if (!name)
1243
+ return null;
1244
+ const { description, tags } = getJSDocComment(node);
1245
+ const visibility = getVisibility(node);
1246
+ const type = checker.getTypeAtLocation(node);
1247
+ const schema = buildSchema(type, checker, ctx);
1248
+ registerReferencedTypes(type, ctx);
1249
+ const kind = ts6.isGetAccessorDeclaration(node) ? "getter" : "setter";
1250
+ const flags = {};
1251
+ if (isStatic(node))
1252
+ flags.static = true;
1253
+ return {
1254
+ name,
1255
+ kind,
1256
+ description,
1257
+ tags: tags.length > 0 ? tags : undefined,
1258
+ visibility,
1259
+ schema,
1260
+ flags: Object.keys(flags).length > 0 ? flags : undefined
1261
+ };
1262
+ }
1263
+ function getExtendsClause(node, checker) {
1264
+ if (!node.heritageClauses)
1265
+ return;
1266
+ for (const clause of node.heritageClauses) {
1267
+ if (clause.token === ts6.SyntaxKind.ExtendsKeyword) {
1268
+ const expr = clause.types[0];
1269
+ if (expr) {
1270
+ const type = checker.getTypeAtLocation(expr);
1271
+ const symbol = type.getSymbol();
1272
+ return symbol?.getName() ?? expr.expression.getText();
1273
+ }
1274
+ }
1275
+ }
1276
+ return;
1277
+ }
1278
+ function getImplementsClause(node, checker) {
1279
+ if (!node.heritageClauses)
1280
+ return;
1281
+ for (const clause of node.heritageClauses) {
1282
+ if (clause.token === ts6.SyntaxKind.ImplementsKeyword) {
1283
+ return clause.types.map((expr) => {
1284
+ const type = checker.getTypeAtLocation(expr);
1285
+ const symbol = type.getSymbol();
1286
+ return symbol?.getName() ?? expr.expression.getText();
1287
+ });
1288
+ }
1289
+ }
1290
+ return;
1291
+ }
1292
+
1293
+ // src/serializers/enums.ts
1294
+ function serializeEnum(node, ctx) {
1295
+ const { typeChecker: checker } = ctx;
1296
+ const symbol = checker.getSymbolAtLocation(node.name ?? node);
1297
+ const name = symbol?.getName() ?? node.name?.getText();
1298
+ if (!name)
1299
+ return null;
1300
+ const declSourceFile = node.getSourceFile();
1301
+ const { description, tags, examples } = getJSDocComment(node);
1302
+ const source = getSourceLocation(node, declSourceFile);
1303
+ const members = node.members.map((member) => {
1304
+ const memberSymbol = checker.getSymbolAtLocation(member.name);
1305
+ const memberName = memberSymbol?.getName() ?? member.name.getText();
1306
+ const constantValue = checker.getConstantValue(member);
1307
+ let schema;
1308
+ if (typeof constantValue === "string") {
1309
+ schema = { type: "string", enum: [constantValue] };
1310
+ } else if (typeof constantValue === "number") {
1311
+ schema = { type: "number", enum: [constantValue] };
1312
+ } else if (member.initializer) {
1313
+ schema = { type: member.initializer.getText() };
1314
+ }
1315
+ const { description: memberDesc } = getJSDocComment(member);
1316
+ return {
1317
+ id: memberName,
1318
+ name: memberName,
1319
+ kind: "enum-member",
1320
+ ...schema ? { schema } : {},
1321
+ ...memberDesc ? { description: memberDesc } : {}
1322
+ };
1323
+ });
1324
+ return {
1325
+ id: name,
1326
+ name,
1327
+ kind: "enum",
1328
+ description,
1329
+ tags,
1330
+ source,
1331
+ members,
1332
+ ...examples.length > 0 ? { examples } : {}
1333
+ };
1334
+ }
1335
+
825
1336
  // src/serializers/functions.ts
826
1337
  function serializeFunctionExport(node, ctx) {
827
1338
  const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
@@ -831,6 +1342,7 @@ function serializeFunctionExport(node, ctx) {
831
1342
  const declSourceFile = node.getSourceFile();
832
1343
  const { description, tags, examples } = getJSDocComment(node);
833
1344
  const source = getSourceLocation(node, declSourceFile);
1345
+ const typeParameters = extractTypeParameters(node, ctx.typeChecker);
834
1346
  const type = ctx.typeChecker.getTypeAtLocation(node);
835
1347
  const callSignatures = type.getCallSignatures();
836
1348
  const signatures = callSignatures.map((sig) => {
@@ -851,20 +1363,50 @@ function serializeFunctionExport(node, ctx) {
851
1363
  description,
852
1364
  tags,
853
1365
  source,
1366
+ typeParameters,
854
1367
  signatures,
855
1368
  ...examples.length > 0 ? { examples } : {}
856
1369
  };
857
1370
  }
858
1371
 
859
1372
  // src/serializers/interfaces.ts
1373
+ import ts7 from "typescript";
860
1374
  function serializeInterface(node, ctx) {
861
- const symbol = ctx.typeChecker.getSymbolAtLocation(node.name ?? node);
1375
+ const { typeChecker: checker } = ctx;
1376
+ const symbol = checker.getSymbolAtLocation(node.name ?? node);
862
1377
  const name = symbol?.getName() ?? node.name?.getText();
863
1378
  if (!name)
864
1379
  return null;
865
1380
  const declSourceFile = node.getSourceFile();
866
1381
  const { description, tags, examples } = getJSDocComment(node);
867
1382
  const source = getSourceLocation(node, declSourceFile);
1383
+ const typeParameters = extractTypeParameters(node, checker);
1384
+ const members = [];
1385
+ const methodsByName = new Map;
1386
+ for (const member of node.members) {
1387
+ if (ts7.isPropertySignature(member)) {
1388
+ const propMember = serializePropertySignature(member, ctx);
1389
+ if (propMember)
1390
+ members.push(propMember);
1391
+ } else if (ts7.isMethodSignature(member)) {
1392
+ const methodMember = serializeMethodSignature(member, ctx);
1393
+ if (methodMember?.name) {
1394
+ if (!methodsByName.has(methodMember.name)) {
1395
+ methodsByName.set(methodMember.name, methodMember);
1396
+ }
1397
+ }
1398
+ } else if (ts7.isCallSignatureDeclaration(member)) {
1399
+ const callMember = serializeCallSignature(member, ctx);
1400
+ if (callMember)
1401
+ members.push(callMember);
1402
+ } else if (ts7.isIndexSignatureDeclaration(member)) {
1403
+ const indexMember = serializeIndexSignature(member, ctx);
1404
+ if (indexMember)
1405
+ members.push(indexMember);
1406
+ }
1407
+ }
1408
+ members.push(...methodsByName.values());
1409
+ const extendsClause = getInterfaceExtends(node, checker);
868
1410
  return {
869
1411
  id: name,
870
1412
  name,
@@ -872,10 +1414,121 @@ function serializeInterface(node, ctx) {
872
1414
  description,
873
1415
  tags,
874
1416
  source,
875
- members: [],
1417
+ typeParameters,
1418
+ members: members.length > 0 ? members : undefined,
1419
+ extends: extendsClause,
876
1420
  ...examples.length > 0 ? { examples } : {}
877
1421
  };
878
1422
  }
1423
+ function serializePropertySignature(node, ctx) {
1424
+ const { typeChecker: checker } = ctx;
1425
+ const name = node.name.getText();
1426
+ const { description, tags } = getJSDocComment(node);
1427
+ const type = checker.getTypeAtLocation(node);
1428
+ const schema = buildSchema(type, checker, ctx);
1429
+ registerReferencedTypes(type, ctx);
1430
+ const flags = {};
1431
+ if (node.questionToken)
1432
+ flags.optional = true;
1433
+ if (node.modifiers?.some((m) => m.kind === ts7.SyntaxKind.ReadonlyKeyword)) {
1434
+ flags.readonly = true;
1435
+ }
1436
+ return {
1437
+ name,
1438
+ kind: "property",
1439
+ description,
1440
+ tags: tags.length > 0 ? tags : undefined,
1441
+ schema,
1442
+ flags: Object.keys(flags).length > 0 ? flags : undefined
1443
+ };
1444
+ }
1445
+ function serializeMethodSignature(node, ctx) {
1446
+ const { typeChecker: checker } = ctx;
1447
+ const name = node.name.getText();
1448
+ const { description, tags } = getJSDocComment(node);
1449
+ const type = checker.getTypeAtLocation(node);
1450
+ const callSignatures = type.getCallSignatures();
1451
+ const signatures = callSignatures.map((sig) => {
1452
+ const params = extractParameters(sig, ctx);
1453
+ const returnType = checker.getReturnTypeOfSignature(sig);
1454
+ registerReferencedTypes(returnType, ctx);
1455
+ return {
1456
+ parameters: params.length > 0 ? params : undefined,
1457
+ returns: {
1458
+ schema: buildSchema(returnType, checker, ctx)
1459
+ }
1460
+ };
1461
+ });
1462
+ const flags = {};
1463
+ if (node.questionToken)
1464
+ flags.optional = true;
1465
+ return {
1466
+ name,
1467
+ kind: "method",
1468
+ description,
1469
+ tags: tags.length > 0 ? tags : undefined,
1470
+ signatures: signatures.length > 0 ? signatures : undefined,
1471
+ flags: Object.keys(flags).length > 0 ? flags : undefined
1472
+ };
1473
+ }
1474
+ function serializeCallSignature(node, ctx) {
1475
+ const { typeChecker: checker } = ctx;
1476
+ const { description, tags } = getJSDocComment(node);
1477
+ const sig = checker.getSignatureFromDeclaration(node);
1478
+ if (!sig)
1479
+ return null;
1480
+ const params = extractParameters(sig, ctx);
1481
+ const returnType = checker.getReturnTypeOfSignature(sig);
1482
+ registerReferencedTypes(returnType, ctx);
1483
+ return {
1484
+ name: "()",
1485
+ kind: "call-signature",
1486
+ description,
1487
+ tags: tags.length > 0 ? tags : undefined,
1488
+ signatures: [
1489
+ {
1490
+ parameters: params.length > 0 ? params : undefined,
1491
+ returns: {
1492
+ schema: buildSchema(returnType, checker, ctx)
1493
+ }
1494
+ }
1495
+ ]
1496
+ };
1497
+ }
1498
+ function serializeIndexSignature(node, ctx) {
1499
+ const { typeChecker: checker } = ctx;
1500
+ const { description, tags } = getJSDocComment(node);
1501
+ const valueType = node.type ? checker.getTypeAtLocation(node.type) : checker.getAnyType();
1502
+ const valueSchema = buildSchema(valueType, checker, ctx);
1503
+ registerReferencedTypes(valueType, ctx);
1504
+ const keyParam = node.parameters[0];
1505
+ const keyType = keyParam?.type ? checker.getTypeAtLocation(keyParam.type) : checker.getStringType();
1506
+ const keyTypeName = checker.typeToString(keyType);
1507
+ return {
1508
+ name: `[${keyTypeName}]`,
1509
+ kind: "index-signature",
1510
+ description,
1511
+ tags: tags.length > 0 ? tags : undefined,
1512
+ schema: {
1513
+ type: "object",
1514
+ additionalProperties: valueSchema
1515
+ }
1516
+ };
1517
+ }
1518
+ function getInterfaceExtends(node, checker) {
1519
+ if (!node.heritageClauses)
1520
+ return;
1521
+ for (const clause of node.heritageClauses) {
1522
+ if (clause.token === ts7.SyntaxKind.ExtendsKeyword && clause.types.length > 0) {
1523
+ const names = clause.types.map((expr) => {
1524
+ const type = checker.getTypeAtLocation(expr);
1525
+ return type.getSymbol()?.getName() ?? expr.expression.getText();
1526
+ });
1527
+ return names.length === 1 ? names[0] : names;
1528
+ }
1529
+ }
1530
+ return;
1531
+ }
879
1532
 
880
1533
  // src/serializers/type-aliases.ts
881
1534
  function serializeTypeAlias(node, ctx) {
@@ -886,7 +1539,9 @@ function serializeTypeAlias(node, ctx) {
886
1539
  const declSourceFile = node.getSourceFile();
887
1540
  const { description, tags, examples } = getJSDocComment(node);
888
1541
  const source = getSourceLocation(node, declSourceFile);
1542
+ const typeParameters = extractTypeParameters(node, ctx.typeChecker);
889
1543
  const type = ctx.typeChecker.getTypeAtLocation(node);
1544
+ const schema = buildSchema(type, ctx.typeChecker, ctx);
890
1545
  registerReferencedTypes(type, ctx);
891
1546
  return {
892
1547
  id: name,
@@ -895,7 +1550,8 @@ function serializeTypeAlias(node, ctx) {
895
1550
  description,
896
1551
  tags,
897
1552
  source,
898
- schema: buildSchema(type, ctx.typeChecker, ctx),
1553
+ typeParameters,
1554
+ schema,
899
1555
  ...examples.length > 0 ? { examples } : {}
900
1556
  };
901
1557
  }
@@ -910,6 +1566,7 @@ function serializeVariable(node, statement, ctx) {
910
1566
  const { description, tags, examples } = getJSDocComment(statement);
911
1567
  const source = getSourceLocation(node, declSourceFile);
912
1568
  const type = ctx.typeChecker.getTypeAtLocation(node);
1569
+ const schema = buildSchema(type, ctx.typeChecker, ctx);
913
1570
  registerReferencedTypes(type, ctx);
914
1571
  return {
915
1572
  id: name,
@@ -918,7 +1575,7 @@ function serializeVariable(node, statement, ctx) {
918
1575
  description,
919
1576
  tags,
920
1577
  source,
921
- schema: buildSchema(type, ctx.typeChecker, ctx),
1578
+ schema,
922
1579
  ...examples.length > 0 ? { examples } : {}
923
1580
  };
924
1581
  }
@@ -926,8 +1583,8 @@ function serializeVariable(node, statement, ctx) {
926
1583
  // src/builder/spec-builder.ts
927
1584
  import * as fs2 from "node:fs";
928
1585
  import * as path3 from "node:path";
929
- import { SCHEMA_VERSION } from "@openpkg-ts/spec";
930
- import ts6 from "typescript";
1586
+ import { SCHEMA_URL, SCHEMA_VERSION } from "@openpkg-ts/spec";
1587
+ import ts8 from "typescript";
931
1588
 
932
1589
  // src/serializers/context.ts
933
1590
  function createContext(program, sourceFile, options = {}) {
@@ -946,15 +1603,52 @@ function createContext(program, sourceFile, options = {}) {
946
1603
  }
947
1604
 
948
1605
  // src/builder/spec-builder.ts
1606
+ var BUILTIN_TYPES2 = new Set([
1607
+ "Array",
1608
+ "Promise",
1609
+ "Map",
1610
+ "Set",
1611
+ "Record",
1612
+ "Partial",
1613
+ "Required",
1614
+ "Pick",
1615
+ "Omit",
1616
+ "Exclude",
1617
+ "Extract",
1618
+ "NonNullable",
1619
+ "Parameters",
1620
+ "ReturnType",
1621
+ "Readonly",
1622
+ "ReadonlyArray",
1623
+ "Awaited",
1624
+ "Date",
1625
+ "RegExp",
1626
+ "Error",
1627
+ "Function",
1628
+ "Object",
1629
+ "String",
1630
+ "Number",
1631
+ "Boolean",
1632
+ "Symbol",
1633
+ "BigInt"
1634
+ ]);
949
1635
  async function extract(options) {
950
- const { entryFile, baseDir, content, maxTypeDepth, maxExternalTypeDepth, resolveExternalTypes } = options;
1636
+ const {
1637
+ entryFile,
1638
+ baseDir,
1639
+ content,
1640
+ maxTypeDepth,
1641
+ maxExternalTypeDepth,
1642
+ resolveExternalTypes,
1643
+ includeSchema
1644
+ } = options;
951
1645
  const diagnostics = [];
952
1646
  const exports = [];
953
1647
  const result = createProgram({ entryFile, baseDir, content });
954
1648
  const { program, sourceFile } = result;
955
1649
  if (!sourceFile) {
956
1650
  return {
957
- spec: createEmptySpec(entryFile),
1651
+ spec: createEmptySpec(entryFile, includeSchema),
958
1652
  diagnostics: [{ message: `Could not load source file: ${entryFile}`, severity: "error" }]
959
1653
  };
960
1654
  }
@@ -962,7 +1656,7 @@ async function extract(options) {
962
1656
  const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
963
1657
  if (!moduleSymbol) {
964
1658
  return {
965
- spec: createEmptySpec(entryFile),
1659
+ spec: createEmptySpec(entryFile, includeSchema),
966
1660
  diagnostics: [{ message: "Could not get module symbol", severity: "warning" }]
967
1661
  };
968
1662
  }
@@ -971,7 +1665,11 @@ async function extract(options) {
971
1665
  for (const symbol of exportedSymbols) {
972
1666
  exportedIds.add(symbol.getName());
973
1667
  }
974
- const ctx = createContext(program, sourceFile, { maxTypeDepth, maxExternalTypeDepth, resolveExternalTypes });
1668
+ const ctx = createContext(program, sourceFile, {
1669
+ maxTypeDepth,
1670
+ maxExternalTypeDepth,
1671
+ resolveExternalTypes
1672
+ });
975
1673
  ctx.exportedIds = exportedIds;
976
1674
  for (const symbol of exportedSymbols) {
977
1675
  try {
@@ -990,11 +1688,31 @@ async function extract(options) {
990
1688
  }
991
1689
  }
992
1690
  const meta = await getPackageMeta(entryFile, baseDir);
1691
+ const types = ctx.typeRegistry.getAll();
1692
+ const danglingRefs = collectDanglingRefs(exports, types);
1693
+ for (const ref of danglingRefs) {
1694
+ diagnostics.push({
1695
+ message: `Type '${ref}' is referenced but not defined in types[].`,
1696
+ severity: "warning",
1697
+ code: "DANGLING_REF",
1698
+ suggestion: "The type may be from an external package. Check import paths."
1699
+ });
1700
+ }
1701
+ const externalTypes = types.filter((t) => t.kind === "external");
1702
+ if (externalTypes.length > 0) {
1703
+ diagnostics.push({
1704
+ message: `${externalTypes.length} external type(s) could not be fully resolved: ${externalTypes.slice(0, 5).map((t) => t.id).join(", ")}${externalTypes.length > 5 ? "..." : ""}`,
1705
+ severity: "warning",
1706
+ code: "EXTERNAL_TYPE_STUBS",
1707
+ suggestion: "Types are from external packages. Full resolution requires type declarations."
1708
+ });
1709
+ }
993
1710
  const spec = {
1711
+ ...includeSchema ? { $schema: SCHEMA_URL } : {},
994
1712
  openpkg: SCHEMA_VERSION,
995
1713
  meta,
996
1714
  exports,
997
- types: ctx.typeRegistry.getAll(),
1715
+ types,
998
1716
  generation: {
999
1717
  generator: "@openpkg-ts/extract",
1000
1718
  timestamp: new Date().toISOString()
@@ -1002,46 +1720,72 @@ async function extract(options) {
1002
1720
  };
1003
1721
  return { spec, diagnostics };
1004
1722
  }
1723
+ function collectAllRefs(obj, refs) {
1724
+ if (obj === null || obj === undefined)
1725
+ return;
1726
+ if (Array.isArray(obj)) {
1727
+ for (const item of obj) {
1728
+ collectAllRefs(item, refs);
1729
+ }
1730
+ return;
1731
+ }
1732
+ if (typeof obj === "object") {
1733
+ const record = obj;
1734
+ if (typeof record.$ref === "string" && record.$ref.startsWith("#/types/")) {
1735
+ refs.add(record.$ref.slice("#/types/".length));
1736
+ }
1737
+ for (const value of Object.values(record)) {
1738
+ collectAllRefs(value, refs);
1739
+ }
1740
+ }
1741
+ }
1742
+ function collectDanglingRefs(exports, types) {
1743
+ const definedTypes = new Set(types.map((t) => t.id));
1744
+ const referencedTypes = new Set;
1745
+ collectAllRefs(exports, referencedTypes);
1746
+ collectAllRefs(types, referencedTypes);
1747
+ return Array.from(referencedTypes).filter((ref) => !definedTypes.has(ref) && !BUILTIN_TYPES2.has(ref));
1748
+ }
1005
1749
  function resolveExportTarget(symbol, checker) {
1006
1750
  let targetSymbol = symbol;
1007
- if (symbol.flags & ts6.SymbolFlags.Alias) {
1751
+ if (symbol.flags & ts8.SymbolFlags.Alias) {
1008
1752
  const aliasTarget = checker.getAliasedSymbol(symbol);
1009
1753
  if (aliasTarget && aliasTarget !== symbol) {
1010
1754
  targetSymbol = aliasTarget;
1011
1755
  }
1012
1756
  }
1013
1757
  const declarations = targetSymbol.declarations ?? [];
1014
- const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts6.SyntaxKind.ExportSpecifier) || declarations[0];
1758
+ const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts8.SyntaxKind.ExportSpecifier) || declarations[0];
1015
1759
  return { declaration, targetSymbol };
1016
1760
  }
1017
- function serializeDeclaration(declaration, exportSymbol, targetSymbol, exportName, ctx) {
1761
+ function serializeDeclaration(declaration, exportSymbol, _targetSymbol, exportName, ctx) {
1018
1762
  let result = null;
1019
- if (ts6.isFunctionDeclaration(declaration)) {
1763
+ if (ts8.isFunctionDeclaration(declaration)) {
1020
1764
  result = serializeFunctionExport(declaration, ctx);
1021
- } else if (ts6.isClassDeclaration(declaration)) {
1765
+ } else if (ts8.isClassDeclaration(declaration)) {
1022
1766
  result = serializeClass(declaration, ctx);
1023
- } else if (ts6.isInterfaceDeclaration(declaration)) {
1767
+ } else if (ts8.isInterfaceDeclaration(declaration)) {
1024
1768
  result = serializeInterface(declaration, ctx);
1025
- } else if (ts6.isTypeAliasDeclaration(declaration)) {
1769
+ } else if (ts8.isTypeAliasDeclaration(declaration)) {
1026
1770
  result = serializeTypeAlias(declaration, ctx);
1027
- } else if (ts6.isEnumDeclaration(declaration)) {
1771
+ } else if (ts8.isEnumDeclaration(declaration)) {
1028
1772
  result = serializeEnum(declaration, ctx);
1029
- } else if (ts6.isVariableDeclaration(declaration)) {
1773
+ } else if (ts8.isVariableDeclaration(declaration)) {
1030
1774
  const varStatement = declaration.parent?.parent;
1031
- if (varStatement && ts6.isVariableStatement(varStatement)) {
1775
+ if (varStatement && ts8.isVariableStatement(varStatement)) {
1032
1776
  result = serializeVariable(declaration, varStatement, ctx);
1033
1777
  }
1034
- } else if (ts6.isNamespaceExport(declaration) || ts6.isModuleDeclaration(declaration)) {
1035
- result = serializeNamespaceExport(exportSymbol, exportName, ctx);
1036
- } else if (ts6.isSourceFile(declaration)) {
1037
- result = serializeNamespaceExport(exportSymbol, exportName, ctx);
1778
+ } else if (ts8.isNamespaceExport(declaration) || ts8.isModuleDeclaration(declaration)) {
1779
+ result = serializeNamespaceExport(exportSymbol, exportName);
1780
+ } else if (ts8.isSourceFile(declaration)) {
1781
+ result = serializeNamespaceExport(exportSymbol, exportName);
1038
1782
  }
1039
1783
  if (result) {
1040
1784
  result = withExportName(result, exportName);
1041
1785
  }
1042
1786
  return result;
1043
1787
  }
1044
- function serializeNamespaceExport(symbol, exportName, ctx) {
1788
+ function serializeNamespaceExport(symbol, exportName) {
1045
1789
  const { description, tags, examples } = getJSDocFromExportSymbol(symbol);
1046
1790
  return {
1047
1791
  id: exportName,
@@ -1057,11 +1801,11 @@ function getJSDocFromExportSymbol(symbol) {
1057
1801
  const examples = [];
1058
1802
  const decl = symbol.declarations?.[0];
1059
1803
  if (decl) {
1060
- const exportDecl = ts6.isNamespaceExport(decl) ? decl.parent : decl;
1061
- if (exportDecl && ts6.isExportDeclaration(exportDecl)) {
1062
- const jsDocs = ts6.getJSDocCommentsAndTags(exportDecl);
1804
+ const exportDecl = ts8.isNamespaceExport(decl) ? decl.parent : decl;
1805
+ if (exportDecl && ts8.isExportDeclaration(exportDecl)) {
1806
+ const jsDocs = ts8.getJSDocCommentsAndTags(exportDecl);
1063
1807
  for (const doc of jsDocs) {
1064
- if (ts6.isJSDoc(doc) && doc.comment) {
1808
+ if (ts8.isJSDoc(doc) && doc.comment) {
1065
1809
  const commentText = typeof doc.comment === "string" ? doc.comment : doc.comment.map((c) => ("text" in c) ? c.text : "").join("");
1066
1810
  if (commentText) {
1067
1811
  return {
@@ -1119,8 +1863,9 @@ function withExportName(entry, exportName) {
1119
1863
  name: entry.name
1120
1864
  };
1121
1865
  }
1122
- function createEmptySpec(entryFile) {
1866
+ function createEmptySpec(entryFile, includeSchema) {
1123
1867
  return {
1868
+ ...includeSchema ? { $schema: SCHEMA_URL } : {},
1124
1869
  openpkg: SCHEMA_VERSION,
1125
1870
  meta: { name: path3.basename(entryFile, path3.extname(entryFile)) },
1126
1871
  exports: [],
@@ -1145,4 +1890,4 @@ async function getPackageMeta(entryFile, baseDir) {
1145
1890
  } catch {}
1146
1891
  return { name: path3.basename(searchDir) };
1147
1892
  }
1148
- export { TypeRegistry, getJSDocComment, getSourceLocation, createProgram, serializeClass, serializeEnum, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, extractParameters, registerReferencedTypes, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, extract };
1893
+ export { TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, BUILTIN_TYPE_SCHEMAS, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, extract };