@openpkg-ts/sdk 0.1.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/index.js ADDED
@@ -0,0 +1,2140 @@
1
+ // src/analysis/run-analysis.ts
2
+ import * as fs2 from "node:fs";
3
+ import * as path4 from "node:path";
4
+
5
+ // src/ts-module.ts
6
+ import * as tsNamespace from "typescript";
7
+ var resolvedTypeScriptModule = (() => {
8
+ const candidate = tsNamespace;
9
+ if (candidate.ScriptTarget === undefined && typeof candidate.default !== "undefined") {
10
+ return candidate.default;
11
+ }
12
+ return candidate;
13
+ })();
14
+ var ts = resolvedTypeScriptModule;
15
+
16
+ // src/analysis/context.ts
17
+ import * as path2 from "node:path";
18
+
19
+ // src/options.ts
20
+ var DEFAULT_OPTIONS = {
21
+ includePrivate: false,
22
+ followImports: true
23
+ };
24
+ function normalizeOpenPkgOptions(options = {}) {
25
+ return {
26
+ ...DEFAULT_OPTIONS,
27
+ ...options
28
+ };
29
+ }
30
+
31
+ // src/analysis/program.ts
32
+ import * as path from "node:path";
33
+ var DEFAULT_COMPILER_OPTIONS = {
34
+ target: ts.ScriptTarget.Latest,
35
+ module: ts.ModuleKind.CommonJS,
36
+ lib: ["lib.es2021.d.ts"],
37
+ allowJs: true,
38
+ declaration: true,
39
+ moduleResolution: ts.ModuleResolutionKind.NodeJs
40
+ };
41
+ function createProgram({
42
+ entryFile,
43
+ baseDir = path.dirname(entryFile),
44
+ content
45
+ }) {
46
+ const configPath = ts.findConfigFile(baseDir, ts.sys.fileExists, "tsconfig.json");
47
+ let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
48
+ if (configPath) {
49
+ const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
50
+ const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath));
51
+ compilerOptions = { ...compilerOptions, ...parsedConfig.options };
52
+ }
53
+ const compilerHost = ts.createCompilerHost(compilerOptions, true);
54
+ let inMemorySource;
55
+ if (content !== undefined) {
56
+ inMemorySource = ts.createSourceFile(entryFile, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
57
+ const originalGetSourceFile = compilerHost.getSourceFile.bind(compilerHost);
58
+ compilerHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
59
+ if (fileName === entryFile) {
60
+ return inMemorySource;
61
+ }
62
+ return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
63
+ };
64
+ }
65
+ const program = ts.createProgram([entryFile], compilerOptions, compilerHost);
66
+ const sourceFile = inMemorySource ?? program.getSourceFile(entryFile);
67
+ return {
68
+ program,
69
+ compilerHost,
70
+ compilerOptions,
71
+ sourceFile,
72
+ configPath
73
+ };
74
+ }
75
+
76
+ // src/analysis/context.ts
77
+ function createAnalysisContext({
78
+ entryFile,
79
+ packageDir,
80
+ content,
81
+ options
82
+ }) {
83
+ const baseDir = packageDir ?? path2.dirname(entryFile);
84
+ const normalizedOptions = normalizeOpenPkgOptions(options);
85
+ const programResult = createProgram({ entryFile, baseDir, content });
86
+ if (!programResult.sourceFile) {
87
+ throw new Error(`Could not load ${entryFile}`);
88
+ }
89
+ return {
90
+ entryFile,
91
+ baseDir,
92
+ program: programResult.program,
93
+ checker: programResult.program.getTypeChecker(),
94
+ sourceFile: programResult.sourceFile,
95
+ compilerOptions: programResult.compilerOptions,
96
+ compilerHost: programResult.compilerHost,
97
+ options: normalizedOptions,
98
+ configPath: programResult.configPath
99
+ };
100
+ }
101
+
102
+ // src/analysis/spec-builder.ts
103
+ import * as fs from "node:fs";
104
+ import * as path3 from "node:path";
105
+ import { SCHEMA_URL } from "@openpkg-ts/spec";
106
+
107
+ // src/utils/type-utils.ts
108
+ function collectReferencedTypes(type, typeChecker, referencedTypes, visitedTypes = new Set) {
109
+ if (visitedTypes.has(type))
110
+ return;
111
+ visitedTypes.add(type);
112
+ const symbol = type.getSymbol();
113
+ if (symbol) {
114
+ const symbolName = symbol.getName();
115
+ if (!symbolName.startsWith("__") && !isBuiltInType(symbolName)) {
116
+ referencedTypes.add(symbolName);
117
+ }
118
+ }
119
+ if (type.isIntersection()) {
120
+ for (const intersectionType of type.types) {
121
+ collectReferencedTypes(intersectionType, typeChecker, referencedTypes, visitedTypes);
122
+ }
123
+ }
124
+ if (type.isUnion()) {
125
+ for (const unionType of type.types) {
126
+ collectReferencedTypes(unionType, typeChecker, referencedTypes, visitedTypes);
127
+ }
128
+ }
129
+ if (type.flags & ts.TypeFlags.Object) {
130
+ const objectType = type;
131
+ if (objectType.objectFlags & ts.ObjectFlags.Reference) {
132
+ const typeRef = objectType;
133
+ if (typeRef.typeArguments) {
134
+ for (const typeArg of typeRef.typeArguments) {
135
+ collectReferencedTypes(typeArg, typeChecker, referencedTypes, visitedTypes);
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ function collectReferencedTypesFromNode(node, typeChecker, referencedTypes) {
142
+ if (ts.isTypeReferenceNode(node)) {
143
+ const typeNameText = node.typeName.getText();
144
+ const symbol = typeChecker.getSymbolAtLocation(node.typeName);
145
+ const name = symbol?.getName() ?? typeNameText;
146
+ if (!isBuiltInType(name)) {
147
+ referencedTypes.add(name);
148
+ }
149
+ node.typeArguments?.forEach((arg) => collectReferencedTypesFromNode(arg, typeChecker, referencedTypes));
150
+ return;
151
+ }
152
+ if (ts.isExpressionWithTypeArguments(node)) {
153
+ const expressionText = node.expression.getText();
154
+ const symbol = typeChecker.getSymbolAtLocation(node.expression);
155
+ const name = symbol?.getName() ?? expressionText;
156
+ if (!isBuiltInType(name)) {
157
+ referencedTypes.add(name);
158
+ }
159
+ node.typeArguments?.forEach((arg) => collectReferencedTypesFromNode(arg, typeChecker, referencedTypes));
160
+ return;
161
+ }
162
+ if (ts.isUnionTypeNode(node) || ts.isIntersectionTypeNode(node)) {
163
+ node.types.forEach((typeNode) => collectReferencedTypesFromNode(typeNode, typeChecker, referencedTypes));
164
+ return;
165
+ }
166
+ if (ts.isArrayTypeNode(node)) {
167
+ collectReferencedTypesFromNode(node.elementType, typeChecker, referencedTypes);
168
+ return;
169
+ }
170
+ if (ts.isParenthesizedTypeNode(node)) {
171
+ collectReferencedTypesFromNode(node.type, typeChecker, referencedTypes);
172
+ return;
173
+ }
174
+ if (ts.isTypeLiteralNode(node)) {
175
+ node.members.forEach((member) => {
176
+ if (ts.isPropertySignature(member) && member.type) {
177
+ collectReferencedTypesFromNode(member.type, typeChecker, referencedTypes);
178
+ }
179
+ if (ts.isMethodSignature(member)) {
180
+ member.typeParameters?.forEach((param) => {
181
+ param.constraint && collectReferencedTypesFromNode(param.constraint, typeChecker, referencedTypes);
182
+ });
183
+ member.parameters.forEach((param) => {
184
+ if (param.type) {
185
+ collectReferencedTypesFromNode(param.type, typeChecker, referencedTypes);
186
+ }
187
+ });
188
+ if (member.type) {
189
+ collectReferencedTypesFromNode(member.type, typeChecker, referencedTypes);
190
+ }
191
+ }
192
+ if (ts.isCallSignatureDeclaration(member) && member.type) {
193
+ collectReferencedTypesFromNode(member.type, typeChecker, referencedTypes);
194
+ }
195
+ if (ts.isIndexSignatureDeclaration(member) && member.type) {
196
+ collectReferencedTypesFromNode(member.type, typeChecker, referencedTypes);
197
+ }
198
+ });
199
+ return;
200
+ }
201
+ if (ts.isTypeOperatorNode(node)) {
202
+ collectReferencedTypesFromNode(node.type, typeChecker, referencedTypes);
203
+ return;
204
+ }
205
+ if (ts.isIndexedAccessTypeNode(node)) {
206
+ collectReferencedTypesFromNode(node.objectType, typeChecker, referencedTypes);
207
+ collectReferencedTypesFromNode(node.indexType, typeChecker, referencedTypes);
208
+ return;
209
+ }
210
+ if (ts.isLiteralTypeNode(node)) {
211
+ return;
212
+ }
213
+ node.forEachChild((child) => {
214
+ if (ts.isTypeNode(child)) {
215
+ collectReferencedTypesFromNode(child, typeChecker, referencedTypes);
216
+ }
217
+ });
218
+ }
219
+ function isBuiltInType(name) {
220
+ const builtIns = [
221
+ "string",
222
+ "number",
223
+ "boolean",
224
+ "bigint",
225
+ "symbol",
226
+ "undefined",
227
+ "null",
228
+ "any",
229
+ "unknown",
230
+ "never",
231
+ "void",
232
+ "object",
233
+ "Array",
234
+ "Promise",
235
+ "Map",
236
+ "Set",
237
+ "WeakMap",
238
+ "WeakSet",
239
+ "Date",
240
+ "RegExp",
241
+ "Error",
242
+ "Function",
243
+ "Object",
244
+ "String",
245
+ "Number",
246
+ "Boolean",
247
+ "BigInt",
248
+ "Symbol",
249
+ "Uint8Array",
250
+ "Int8Array",
251
+ "Uint16Array",
252
+ "Int16Array",
253
+ "Uint32Array",
254
+ "Int32Array",
255
+ "Float32Array",
256
+ "Float64Array",
257
+ "BigInt64Array",
258
+ "BigUint64Array",
259
+ "Uint8ClampedArray",
260
+ "ArrayBuffer",
261
+ "ArrayBufferLike",
262
+ "DataView",
263
+ "Uint8ArrayConstructor",
264
+ "ArrayBufferConstructor",
265
+ "JSON",
266
+ "Math",
267
+ "Reflect",
268
+ "Proxy",
269
+ "Intl",
270
+ "globalThis",
271
+ "__type"
272
+ ];
273
+ return builtIns.includes(name);
274
+ }
275
+
276
+ // src/utils/parameter-utils.ts
277
+ var BUILTIN_TYPE_SCHEMAS = {
278
+ Date: { type: "string", format: "date-time" },
279
+ RegExp: { type: "object", description: "RegExp" },
280
+ Error: { type: "object" },
281
+ Promise: { type: "object" },
282
+ Map: { type: "object" },
283
+ Set: { type: "object" },
284
+ WeakMap: { type: "object" },
285
+ WeakSet: { type: "object" },
286
+ Function: { type: "object" },
287
+ ArrayBuffer: { type: "string", format: "binary" },
288
+ ArrayBufferLike: { type: "string", format: "binary" },
289
+ DataView: { type: "string", format: "binary" },
290
+ Uint8Array: { type: "string", format: "byte" },
291
+ Uint16Array: { type: "string", format: "byte" },
292
+ Uint32Array: { type: "string", format: "byte" },
293
+ Int8Array: { type: "string", format: "byte" },
294
+ Int16Array: { type: "string", format: "byte" },
295
+ Int32Array: { type: "string", format: "byte" },
296
+ Float32Array: { type: "string", format: "byte" },
297
+ Float64Array: { type: "string", format: "byte" },
298
+ BigInt64Array: { type: "string", format: "byte" },
299
+ BigUint64Array: { type: "string", format: "byte" }
300
+ };
301
+ function isObjectLiteralType(type) {
302
+ if (!(type.getFlags() & ts.TypeFlags.Object)) {
303
+ return false;
304
+ }
305
+ const objectFlags = type.objectFlags;
306
+ return (objectFlags & ts.ObjectFlags.ObjectLiteral) !== 0;
307
+ }
308
+ function isPureRefSchema(value) {
309
+ return Object.keys(value).length === 1 && "$ref" in value;
310
+ }
311
+ function withDescription(schema, description) {
312
+ if (isPureRefSchema(schema)) {
313
+ return {
314
+ allOf: [schema],
315
+ description
316
+ };
317
+ }
318
+ return {
319
+ ...schema,
320
+ description
321
+ };
322
+ }
323
+ function propertiesToSchema(properties, description) {
324
+ const schema = {
325
+ type: "object",
326
+ properties: {}
327
+ };
328
+ const required = [];
329
+ for (const prop of properties) {
330
+ const propType = prop.type;
331
+ let propSchema;
332
+ if (typeof propType === "string") {
333
+ if (["string", "number", "boolean", "bigint", "null"].includes(propType)) {
334
+ propSchema = { type: propType === "bigint" ? "string" : propType };
335
+ } else {
336
+ propSchema = { type: propType };
337
+ }
338
+ } else if (propType && typeof propType === "object") {
339
+ propSchema = propType;
340
+ } else {
341
+ propSchema = { type: "any" };
342
+ }
343
+ if (prop.description && typeof propSchema === "object") {
344
+ propSchema = withDescription(propSchema, prop.description);
345
+ }
346
+ schema.properties[prop.name] = propSchema;
347
+ if (!prop.optional) {
348
+ required.push(prop.name);
349
+ }
350
+ }
351
+ if (required.length > 0) {
352
+ schema.required = required;
353
+ }
354
+ if (description) {
355
+ return withDescription(schema, description);
356
+ }
357
+ return schema;
358
+ }
359
+ function buildSchemaFromTypeNode(node, typeChecker, typeRefs, referencedTypes, functionDoc, parentParamName) {
360
+ if (ts.isParenthesizedTypeNode(node)) {
361
+ return buildSchemaFromTypeNode(node.type, typeChecker, typeRefs, referencedTypes, functionDoc ?? null, parentParamName);
362
+ }
363
+ if (ts.isIntersectionTypeNode(node)) {
364
+ const schemas = node.types.map((type) => buildSchemaFromTypeNode(type, typeChecker, typeRefs, referencedTypes, functionDoc, parentParamName));
365
+ return { allOf: schemas };
366
+ }
367
+ if (ts.isUnionTypeNode(node)) {
368
+ const schemas = node.types.map((type) => buildSchemaFromTypeNode(type, typeChecker, typeRefs, referencedTypes, functionDoc, parentParamName));
369
+ return { anyOf: schemas };
370
+ }
371
+ if (ts.isArrayTypeNode(node)) {
372
+ return {
373
+ type: "array",
374
+ items: buildSchemaFromTypeNode(node.elementType, typeChecker, typeRefs, referencedTypes, functionDoc, parentParamName)
375
+ };
376
+ }
377
+ if (ts.isTypeLiteralNode(node)) {
378
+ const properties = {};
379
+ const required = [];
380
+ for (const member of node.members) {
381
+ if (!ts.isPropertySignature(member) || !member.name) {
382
+ continue;
383
+ }
384
+ const propName = member.name.getText();
385
+ let schema2 = "any";
386
+ if (member.type) {
387
+ const memberType = typeChecker.getTypeFromTypeNode(member.type);
388
+ const formatted = formatTypeReference(memberType, typeChecker, typeRefs, referencedTypes);
389
+ if (typeof formatted === "string") {
390
+ if (formatted === "any") {
391
+ schema2 = buildSchemaFromTypeNode(member.type, typeChecker, typeRefs, referencedTypes, functionDoc, parentParamName);
392
+ } else {
393
+ schema2 = { type: formatted };
394
+ }
395
+ } else {
396
+ schema2 = formatted;
397
+ }
398
+ } else {
399
+ schema2 = { type: "any" };
400
+ }
401
+ const description = getDocDescriptionForProperty(functionDoc, parentParamName, propName);
402
+ if (typeof schema2 === "object" && description) {
403
+ schema2 = withDescription(schema2, description);
404
+ }
405
+ properties[propName] = schema2;
406
+ if (!member.questionToken) {
407
+ required.push(propName);
408
+ }
409
+ }
410
+ const schema = {
411
+ type: "object",
412
+ properties
413
+ };
414
+ if (required.length > 0) {
415
+ schema.required = required;
416
+ }
417
+ return schema;
418
+ }
419
+ if (ts.isTypeReferenceNode(node)) {
420
+ const typeName = node.typeName.getText();
421
+ if (typeName === "Array") {
422
+ return { type: "array" };
423
+ }
424
+ const builtInSchema = BUILTIN_TYPE_SCHEMAS[typeName];
425
+ if (builtInSchema) {
426
+ return { ...builtInSchema };
427
+ }
428
+ if (isBuiltInType(typeName)) {
429
+ return { type: "object" };
430
+ }
431
+ if (!typeRefs.has(typeName)) {
432
+ typeRefs.set(typeName, typeName);
433
+ }
434
+ referencedTypes?.add(typeName);
435
+ return { $ref: `#/types/${typeName}` };
436
+ }
437
+ if (ts.isLiteralTypeNode(node)) {
438
+ if (ts.isStringLiteral(node.literal)) {
439
+ return { enum: [node.literal.text] };
440
+ }
441
+ if (ts.isNumericLiteral(node.literal)) {
442
+ return { enum: [Number(node.literal.text)] };
443
+ }
444
+ }
445
+ if (ts.isIntersectionTypeNode(node)) {
446
+ const schemas = node.types.map((typeNode) => buildSchemaFromTypeNode(typeNode, typeChecker, typeRefs, referencedTypes, functionDoc, parentParamName));
447
+ if (schemas.some((schema) => ("$ref" in schema) && Object.keys(schema).length === 1)) {
448
+ const refs = schemas.filter((schema) => ("$ref" in schema) && Object.keys(schema).length === 1);
449
+ const nonRefs = schemas.filter((schema) => !(("$ref" in schema) && Object.keys(schema).length === 1));
450
+ if (refs.length === schemas.length) {
451
+ return refs[0];
452
+ }
453
+ if (nonRefs.length > 0) {
454
+ const merged = nonRefs.reduce((acc, schema) => ({ ...acc, ...schema }), {});
455
+ return merged;
456
+ }
457
+ }
458
+ return {
459
+ allOf: schemas
460
+ };
461
+ }
462
+ return { type: node.getText() };
463
+ }
464
+ function getDocDescriptionForProperty(functionDoc, parentParamName, propName) {
465
+ if (!functionDoc) {
466
+ return;
467
+ }
468
+ let match = functionDoc.params.find((p) => p.name === `${parentParamName}.${propName}`);
469
+ if (!match) {
470
+ match = functionDoc.params.find((p) => p.name.endsWith(`.${propName}`));
471
+ }
472
+ return match?.description;
473
+ }
474
+ function schemaIsAny(schema) {
475
+ if (typeof schema === "string") {
476
+ return schema === "any";
477
+ }
478
+ if ("type" in schema && schema.type === "any" && Object.keys(schema).length === 1) {
479
+ return true;
480
+ }
481
+ return false;
482
+ }
483
+ function schemasAreEqual(left, right) {
484
+ if (typeof left !== typeof right) {
485
+ return false;
486
+ }
487
+ if (typeof left === "string" && typeof right === "string") {
488
+ return left === right;
489
+ }
490
+ if (left == null || right == null) {
491
+ return left === right;
492
+ }
493
+ const normalize = (value) => {
494
+ if (Array.isArray(value)) {
495
+ return value.map((item) => normalize(item));
496
+ }
497
+ if (value && typeof value === "object") {
498
+ const sortedEntries = Object.entries(value).map(([key, val]) => [key, normalize(val)]).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
499
+ return Object.fromEntries(sortedEntries);
500
+ }
501
+ return value;
502
+ };
503
+ return JSON.stringify(normalize(left)) === JSON.stringify(normalize(right));
504
+ }
505
+ function formatTypeReference(type, typeChecker, typeRefs, referencedTypes, visitedAliases) {
506
+ const visited = visitedAliases ?? new Set;
507
+ const aliasSymbol = type.aliasSymbol;
508
+ let aliasName;
509
+ let aliasAdded = false;
510
+ if (aliasSymbol) {
511
+ aliasName = aliasSymbol.getName();
512
+ if (visited.has(aliasName)) {
513
+ return { $ref: `#/types/${aliasName}` };
514
+ }
515
+ if (typeRefs.has(aliasName)) {
516
+ return { $ref: `#/types/${aliasName}` };
517
+ }
518
+ if (referencedTypes && !isBuiltInType(aliasName)) {
519
+ referencedTypes.add(aliasName);
520
+ return { $ref: `#/types/${aliasName}` };
521
+ }
522
+ visited.add(aliasName);
523
+ aliasAdded = true;
524
+ }
525
+ try {
526
+ const typeString = typeChecker.typeToString(type);
527
+ const primitives = [
528
+ "string",
529
+ "number",
530
+ "boolean",
531
+ "bigint",
532
+ "symbol",
533
+ "any",
534
+ "unknown",
535
+ "void",
536
+ "undefined",
537
+ "null",
538
+ "never"
539
+ ];
540
+ if (primitives.includes(typeString)) {
541
+ if (typeString === "bigint") {
542
+ return { type: "string", format: "bigint" };
543
+ }
544
+ if (typeString === "undefined" || typeString === "null") {
545
+ return { type: "null" };
546
+ }
547
+ if (typeString === "void" || typeString === "never") {
548
+ return { type: "null" };
549
+ }
550
+ return { type: typeString };
551
+ }
552
+ if (type.isUnion()) {
553
+ const unionType = type;
554
+ const parts = unionType.types.map((t) => formatTypeReference(t, typeChecker, typeRefs, referencedTypes, visited));
555
+ return {
556
+ anyOf: parts
557
+ };
558
+ }
559
+ if (type.isIntersection()) {
560
+ const intersectionType = type;
561
+ const parts = intersectionType.types.map((t) => formatTypeReference(t, typeChecker, typeRefs, referencedTypes, visited));
562
+ const normalized = parts.flatMap((part) => {
563
+ if (typeof part === "string") {
564
+ return [{ type: part }];
565
+ }
566
+ if (part && typeof part === "object" && "allOf" in part) {
567
+ return Array.isArray(part.allOf) ? part.allOf : [part];
568
+ }
569
+ return [part];
570
+ });
571
+ if (normalized.length === 1) {
572
+ return normalized[0];
573
+ }
574
+ return {
575
+ allOf: normalized
576
+ };
577
+ }
578
+ const symbol = type.getSymbol();
579
+ if (symbol) {
580
+ const symbolName = symbol.getName();
581
+ if (symbolName.startsWith("__")) {
582
+ if (type.getFlags() & ts.TypeFlags.Object) {
583
+ const properties = type.getProperties();
584
+ if (properties.length > 0) {
585
+ const objSchema = {
586
+ type: "object",
587
+ properties: {}
588
+ };
589
+ const required = [];
590
+ for (const prop of properties) {
591
+ const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
592
+ const propName = prop.getName();
593
+ objSchema.properties[propName] = formatTypeReference(propType, typeChecker, typeRefs, referencedTypes, visited);
594
+ if (!(prop.flags & ts.SymbolFlags.Optional)) {
595
+ required.push(propName);
596
+ }
597
+ }
598
+ if (required.length > 0) {
599
+ objSchema.required = required;
600
+ }
601
+ return objSchema;
602
+ }
603
+ }
604
+ return { type: "object" };
605
+ }
606
+ if (typeRefs.has(symbolName)) {
607
+ return { $ref: `#/types/${symbolName}` };
608
+ }
609
+ if (symbolName === "Array") {
610
+ return { type: "array" };
611
+ }
612
+ const builtInSchema = BUILTIN_TYPE_SCHEMAS[symbolName];
613
+ if (builtInSchema) {
614
+ return { ...builtInSchema };
615
+ }
616
+ if (referencedTypes && !isBuiltInType(symbolName)) {
617
+ referencedTypes.add(symbolName);
618
+ return { $ref: `#/types/${symbolName}` };
619
+ }
620
+ if (isBuiltInType(symbolName)) {
621
+ return { type: "object" };
622
+ }
623
+ return { $ref: `#/types/${symbolName}` };
624
+ }
625
+ if (type.isLiteral()) {
626
+ if (typeString.startsWith('"') && typeString.endsWith('"')) {
627
+ const literalValue = typeString.slice(1, -1);
628
+ return { enum: [literalValue] };
629
+ }
630
+ return { enum: [Number(typeString)] };
631
+ }
632
+ const typePattern = /^(\w+)(\s*\|\s*undefined)?$/;
633
+ const match = typeString.match(typePattern);
634
+ if (match) {
635
+ const [, typeName, hasUndefined] = match;
636
+ if (typeRefs.has(typeName) || !isBuiltInType(typeName)) {
637
+ if (hasUndefined) {
638
+ return {
639
+ anyOf: [{ $ref: `#/types/${typeName}` }, { type: "null" }]
640
+ };
641
+ }
642
+ return { $ref: `#/types/${typeName}` };
643
+ }
644
+ }
645
+ return { type: typeString };
646
+ } finally {
647
+ if (aliasAdded && aliasName) {
648
+ visited.delete(aliasName);
649
+ }
650
+ }
651
+ }
652
+ function structureParameter(param, paramDecl, paramType, typeChecker, typeRefs, functionDoc, paramDoc, referencedTypes) {
653
+ const paramName = param.getName();
654
+ const fallbackName = paramName === "__0" || ts.isObjectBindingPattern(paramDecl.name) || ts.isArrayBindingPattern(paramDecl.name) ? "object" : paramName;
655
+ if (paramType.isIntersection()) {
656
+ const properties = [];
657
+ const intersectionType = paramType;
658
+ for (const subType of intersectionType.types) {
659
+ const symbol2 = subType.getSymbol();
660
+ const _typeString = typeChecker.typeToString(subType);
661
+ const isAnonymousObject = isObjectLiteralType(subType) || symbol2?.getName().startsWith("__");
662
+ if (isAnonymousObject) {
663
+ for (const prop of subType.getProperties()) {
664
+ const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
665
+ let description = "";
666
+ if (functionDoc) {
667
+ let docParam = functionDoc.params.find((p) => p.name === `${paramName}.${prop.getName()}`);
668
+ if (!docParam && paramName === "__0") {
669
+ docParam = functionDoc.params.find((p) => p.name.endsWith(`.${prop.getName()}`));
670
+ }
671
+ if (docParam) {
672
+ description = docParam.description;
673
+ }
674
+ }
675
+ properties.push({
676
+ name: prop.getName(),
677
+ type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
678
+ description,
679
+ optional: !!(prop.flags & ts.SymbolFlags.Optional)
680
+ });
681
+ }
682
+ } else if (symbol2) {
683
+ const _symbolName = symbol2.getName();
684
+ if (!isBuiltInType(_symbolName)) {
685
+ for (const prop of subType.getProperties()) {
686
+ const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
687
+ properties.push({
688
+ name: prop.getName(),
689
+ type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
690
+ description: "",
691
+ optional: !!(prop.flags & ts.SymbolFlags.Optional)
692
+ });
693
+ }
694
+ }
695
+ }
696
+ }
697
+ const actualName = fallbackName;
698
+ return {
699
+ name: actualName,
700
+ required: !typeChecker.isOptionalParameter(paramDecl),
701
+ description: paramDoc?.description || "",
702
+ schema: propertiesToSchema(properties)
703
+ };
704
+ }
705
+ if (paramType.isUnion()) {
706
+ const unionType = paramType;
707
+ const objectOptions = [];
708
+ let hasNonObjectTypes = false;
709
+ for (const subType of unionType.types) {
710
+ const symbol2 = subType.getSymbol();
711
+ if (isObjectLiteralType(subType) || symbol2?.getName().startsWith("__")) {
712
+ const properties = [];
713
+ for (const prop of subType.getProperties()) {
714
+ const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
715
+ properties.push({
716
+ name: prop.getName(),
717
+ type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
718
+ description: "",
719
+ optional: !!(prop.flags & ts.SymbolFlags.Optional)
720
+ });
721
+ }
722
+ if (properties.length > 0) {
723
+ objectOptions.push({ properties });
724
+ }
725
+ } else {
726
+ hasNonObjectTypes = true;
727
+ }
728
+ }
729
+ if (objectOptions.length > 0 && !hasNonObjectTypes) {
730
+ const readableName2 = fallbackName;
731
+ return {
732
+ name: readableName2,
733
+ required: !typeChecker.isOptionalParameter(paramDecl),
734
+ description: paramDoc?.description || "",
735
+ schema: {
736
+ oneOf: objectOptions.map((opt) => propertiesToSchema(opt.properties))
737
+ }
738
+ };
739
+ }
740
+ }
741
+ const symbol = paramType.getSymbol();
742
+ if ((symbol?.getName().startsWith("__") || isObjectLiteralType(paramType)) && paramType.getProperties().length > 0) {
743
+ const properties = [];
744
+ for (const prop of paramType.getProperties()) {
745
+ const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration);
746
+ properties.push({
747
+ name: prop.getName(),
748
+ type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes),
749
+ description: "",
750
+ optional: !!(prop.flags & ts.SymbolFlags.Optional)
751
+ });
752
+ }
753
+ const readableName2 = fallbackName;
754
+ return {
755
+ name: readableName2,
756
+ required: !typeChecker.isOptionalParameter(paramDecl),
757
+ description: paramDoc?.description || "",
758
+ schema: propertiesToSchema(properties)
759
+ };
760
+ }
761
+ if (paramType.flags & ts.TypeFlags.Any && paramDecl.type && paramDecl.name && ts.isObjectBindingPattern(paramDecl.name)) {
762
+ const actualName = fallbackName;
763
+ const schema2 = buildSchemaFromTypeNode(paramDecl.type, typeChecker, typeRefs, referencedTypes, functionDoc ?? null, param.getName());
764
+ return {
765
+ name: actualName,
766
+ required: !typeChecker.isOptionalParameter(paramDecl),
767
+ description: paramDoc?.description || "",
768
+ schema: schema2
769
+ };
770
+ }
771
+ const typeRef = formatTypeReference(paramType, typeChecker, typeRefs, referencedTypes);
772
+ let schema;
773
+ if (typeof typeRef === "string") {
774
+ if ([
775
+ "string",
776
+ "number",
777
+ "boolean",
778
+ "null",
779
+ "undefined",
780
+ "any",
781
+ "unknown",
782
+ "never",
783
+ "void"
784
+ ].includes(typeRef)) {
785
+ schema = { type: typeRef };
786
+ } else {
787
+ schema = { type: typeRef };
788
+ }
789
+ } else {
790
+ schema = typeRef;
791
+ }
792
+ if (paramDecl.type) {
793
+ const astSchema = buildSchemaFromTypeNode(paramDecl.type, typeChecker, typeRefs, referencedTypes, functionDoc ?? null, param.getName());
794
+ if (schemaIsAny(schema)) {
795
+ schema = astSchema;
796
+ } else if (!(("type" in schema) && schema.type === "any") && !(typeof schema === "object" && isPureRefSchema(schema)) && Object.keys(astSchema).length > 0 && !schemasAreEqual(schema, astSchema)) {
797
+ schema = {
798
+ allOf: [schema, astSchema]
799
+ };
800
+ }
801
+ }
802
+ const readableName = fallbackName;
803
+ return {
804
+ name: readableName,
805
+ required: !typeChecker.isOptionalParameter(paramDecl),
806
+ description: paramDoc?.description || "",
807
+ schema
808
+ };
809
+ }
810
+
811
+ // src/utils/tsdoc-utils.ts
812
+ function parseJSDocComment(symbol, _typeChecker, sourceFileOverride) {
813
+ const node = symbol.valueDeclaration || symbol.declarations?.[0];
814
+ if (!node)
815
+ return null;
816
+ const sourceFile = sourceFileOverride || node.getSourceFile();
817
+ const commentRanges = ts.getLeadingCommentRanges(sourceFile.text, node.pos);
818
+ if (!commentRanges || commentRanges.length === 0) {
819
+ return null;
820
+ }
821
+ const lastComment = commentRanges[commentRanges.length - 1];
822
+ const commentText = sourceFile.text.substring(lastComment.pos, lastComment.end);
823
+ return parseJSDocText(commentText);
824
+ }
825
+ function parseJSDocText(commentText) {
826
+ const tags = [];
827
+ const result = {
828
+ description: "",
829
+ params: [],
830
+ examples: []
831
+ };
832
+ const cleanedText = commentText.replace(/^\/\*\*\s*/, "").replace(/\s*\*\/$/, "").replace(/^\s*\* ?/gm, "");
833
+ const lines = cleanedText.split(/\n/);
834
+ let currentTag = "";
835
+ let currentContent = [];
836
+ const pushDescription = (line) => {
837
+ const processed = replaceInlineLinks(line, tags).trimEnd();
838
+ if (processed.trim()) {
839
+ result.description = result.description ? `${result.description}
840
+ ${processed}` : processed;
841
+ }
842
+ };
843
+ for (const line of lines) {
844
+ const tagMatch = line.match(/^@(\w+)(?:\s+(.*))?$/);
845
+ if (tagMatch) {
846
+ if (currentTag) {
847
+ processTag(result, tags, currentTag, currentContent.join(String.fromCharCode(10)));
848
+ }
849
+ currentTag = tagMatch[1];
850
+ currentContent = tagMatch[2] ? [tagMatch[2]] : [];
851
+ } else if (currentTag) {
852
+ currentContent.push(line);
853
+ } else {
854
+ if (line.trim()) {
855
+ pushDescription(line);
856
+ }
857
+ }
858
+ }
859
+ if (currentTag) {
860
+ processTag(result, tags, currentTag, currentContent.join(String.fromCharCode(10)));
861
+ }
862
+ if (result.examples && result.examples.length === 0) {
863
+ delete result.examples;
864
+ }
865
+ if (tags.length > 0) {
866
+ result.tags = tags;
867
+ }
868
+ return result;
869
+ }
870
+ function processTag(result, tags, tag, content) {
871
+ switch (tag) {
872
+ case "param":
873
+ case "parameter": {
874
+ const paramMatch = content.match(/^(?:\{([^}]+)\}\s+)?(\S+)(?:\s+-\s+)?(.*)$/);
875
+ if (paramMatch) {
876
+ const [, type, name, description] = paramMatch;
877
+ const processedDescription = replaceInlineLinks(description || "", tags);
878
+ result.params.push({
879
+ name: name || "",
880
+ description: processedDescription || "",
881
+ type
882
+ });
883
+ }
884
+ break;
885
+ }
886
+ case "returns":
887
+ case "return": {
888
+ result.returns = replaceInlineLinks(content, tags);
889
+ break;
890
+ }
891
+ case "example": {
892
+ const example = replaceInlineLinks(content.trim(), tags).trim();
893
+ if (example) {
894
+ if (!result.examples) {
895
+ result.examples = [];
896
+ }
897
+ result.examples.push(example);
898
+ }
899
+ break;
900
+ }
901
+ case "see": {
902
+ const parts = content.split(",").map((part) => part.trim()).filter(Boolean);
903
+ for (const part of parts) {
904
+ const linkTargets = extractLinkTargets(part);
905
+ if (linkTargets.length > 0) {
906
+ for (const target of linkTargets) {
907
+ tags.push({ name: "link", text: target });
908
+ tags.push({ name: "see", text: target });
909
+ }
910
+ } else {
911
+ tags.push({ name: "see", text: part });
912
+ }
913
+ }
914
+ break;
915
+ }
916
+ case "link": {
917
+ const { target } = parseLinkBody(content.trim());
918
+ if (target) {
919
+ tags.push({ name: "link", text: target });
920
+ }
921
+ break;
922
+ }
923
+ default: {
924
+ replaceInlineLinks(content, tags);
925
+ }
926
+ }
927
+ }
928
+ function replaceInlineLinks(text, tags, tagName = "link") {
929
+ return text.replace(/\{@link\s+([^}]+)\}/g, (_match, body) => {
930
+ const { target, label } = parseLinkBody(body);
931
+ if (target) {
932
+ tags.push({ name: tagName, text: target });
933
+ }
934
+ return label || target || "";
935
+ });
936
+ }
937
+ function extractLinkTargets(text) {
938
+ const targets = [];
939
+ text.replace(/\{@link\s+([^}]+)\}/g, (_match, body) => {
940
+ const { target } = parseLinkBody(body);
941
+ if (target) {
942
+ targets.push(target);
943
+ }
944
+ return "";
945
+ });
946
+ return targets;
947
+ }
948
+ function parseLinkBody(raw) {
949
+ const trimmed = raw.trim();
950
+ if (!trimmed) {
951
+ return { target: "" };
952
+ }
953
+ const pipeIndex = trimmed.indexOf("|");
954
+ if (pipeIndex >= 0) {
955
+ const target2 = trimmed.slice(0, pipeIndex).trim();
956
+ const label2 = trimmed.slice(pipeIndex + 1).trim();
957
+ return { target: target2, label: label2 };
958
+ }
959
+ const parts = trimmed.split(/\s+/);
960
+ const target = parts.shift() ?? "";
961
+ const label = parts.join(" ").trim();
962
+ return { target, label: label || undefined };
963
+ }
964
+ function extractDestructuredParams(parsedDoc, paramName) {
965
+ const destructuredParams = new Map;
966
+ const paramPrefix = `${paramName}.`;
967
+ for (const param of parsedDoc.params) {
968
+ if (param.name.startsWith(paramPrefix)) {
969
+ const propertyName = param.name.substring(paramPrefix.length);
970
+ destructuredParams.set(propertyName, param.description);
971
+ } else if (param.name.includes(".") && paramName === "__0") {
972
+ const [_prefix, propertyName] = param.name.split(".", 2);
973
+ if (propertyName) {
974
+ destructuredParams.set(propertyName, param.description);
975
+ }
976
+ }
977
+ }
978
+ return destructuredParams;
979
+ }
980
+ function getParameterDocumentation(param, paramDecl, typeChecker) {
981
+ const result = {
982
+ description: ""
983
+ };
984
+ const funcNode = paramDecl.parent;
985
+ if (ts.isFunctionDeclaration(funcNode) || ts.isFunctionExpression(funcNode)) {
986
+ const funcSymbol = typeChecker.getSymbolAtLocation(funcNode.name || funcNode);
987
+ if (funcSymbol) {
988
+ const parsedDoc = parseJSDocComment(funcSymbol, typeChecker);
989
+ if (parsedDoc) {
990
+ const paramName = param.getName();
991
+ const paramDoc = parsedDoc.params.find((p) => p.name === paramName || p.name.split(".")[0] === paramName);
992
+ if (paramDoc) {
993
+ result.description = paramDoc.description;
994
+ }
995
+ const destructuredProps = extractDestructuredParams(parsedDoc, paramName);
996
+ if (destructuredProps.size > 0) {
997
+ result.destructuredProperties = Array.from(destructuredProps.entries()).map(([name, description]) => ({
998
+ name,
999
+ description
1000
+ }));
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+ return result;
1006
+ }
1007
+
1008
+ // src/analysis/ast-utils.ts
1009
+ function getJSDocComment(symbol, typeChecker) {
1010
+ const comments = symbol.getDocumentationComment(typeChecker);
1011
+ return ts.displayPartsToString(comments);
1012
+ }
1013
+ function getSourceLocation(node) {
1014
+ const sourceFile = node.getSourceFile();
1015
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
1016
+ return {
1017
+ file: sourceFile.fileName,
1018
+ line: line + 1
1019
+ };
1020
+ }
1021
+
1022
+ // src/analysis/serializers/classes.ts
1023
+ function serializeClass(declaration, symbol, context) {
1024
+ const { checker, typeRegistry } = context;
1025
+ const typeRefs = typeRegistry.getTypeRefs();
1026
+ const referencedTypes = typeRegistry.getReferencedTypes();
1027
+ const members = serializeClassMembers(declaration, checker, typeRefs, referencedTypes);
1028
+ const parsedDoc = parseJSDocComment(symbol, context.checker);
1029
+ const description = parsedDoc?.description ?? getJSDocComment(symbol, context.checker);
1030
+ const exportEntry = {
1031
+ id: symbol.getName(),
1032
+ name: symbol.getName(),
1033
+ kind: "class",
1034
+ description,
1035
+ source: getSourceLocation(declaration),
1036
+ members: members.length > 0 ? members : undefined,
1037
+ tags: parsedDoc?.tags
1038
+ };
1039
+ const typeDefinition = {
1040
+ id: symbol.getName(),
1041
+ name: symbol.getName(),
1042
+ kind: "class",
1043
+ description,
1044
+ source: getSourceLocation(declaration),
1045
+ members: members.length > 0 ? members : undefined,
1046
+ tags: parsedDoc?.tags
1047
+ };
1048
+ return {
1049
+ exportEntry,
1050
+ typeDefinition
1051
+ };
1052
+ }
1053
+ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes) {
1054
+ const members = [];
1055
+ for (const member of declaration.members) {
1056
+ if (!member.name && !ts.isConstructorDeclaration(member)) {
1057
+ continue;
1058
+ }
1059
+ if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) {
1060
+ const memberName = member.name?.getText();
1061
+ if (!memberName)
1062
+ continue;
1063
+ const memberSymbol = member.name ? checker.getSymbolAtLocation(member.name) : undefined;
1064
+ const memberType = memberSymbol ? checker.getTypeOfSymbolAtLocation(memberSymbol, member) : member.type ? checker.getTypeFromTypeNode(member.type) : checker.getTypeAtLocation(member);
1065
+ collectReferencedTypes(memberType, checker, referencedTypes);
1066
+ const schema = formatTypeReference(memberType, checker, typeRefs, referencedTypes);
1067
+ const flags = {};
1068
+ const isOptionalSymbol = memberSymbol != null && (memberSymbol.flags & ts.SymbolFlags.Optional) !== 0;
1069
+ if (member.questionToken || isOptionalSymbol) {
1070
+ flags.optional = true;
1071
+ }
1072
+ if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
1073
+ flags.readonly = true;
1074
+ }
1075
+ if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
1076
+ flags.static = true;
1077
+ }
1078
+ members.push({
1079
+ id: memberName,
1080
+ name: memberName,
1081
+ kind: "property",
1082
+ visibility: getMemberVisibility(member.modifiers),
1083
+ schema,
1084
+ description: memberSymbol ? getJSDocComment(memberSymbol, checker) : undefined,
1085
+ flags: Object.keys(flags).length > 0 ? flags : undefined
1086
+ });
1087
+ continue;
1088
+ }
1089
+ if (ts.isMethodDeclaration(member)) {
1090
+ const memberName = member.name?.getText() ?? "method";
1091
+ const memberSymbol = member.name ? checker.getSymbolAtLocation(member.name) : undefined;
1092
+ const methodDoc = memberSymbol ? parseJSDocComment(memberSymbol, checker) : null;
1093
+ const signature = checker.getSignatureFromDeclaration(member);
1094
+ const signatures = signature ? [
1095
+ serializeSignature(signature, checker, typeRefs, referencedTypes, methodDoc, memberSymbol)
1096
+ ] : undefined;
1097
+ members.push({
1098
+ id: memberName,
1099
+ name: memberName,
1100
+ kind: "method",
1101
+ visibility: getMemberVisibility(member.modifiers),
1102
+ signatures,
1103
+ description: memberSymbol ? getJSDocComment(memberSymbol, checker) : undefined,
1104
+ flags: getMethodFlags(member)
1105
+ });
1106
+ continue;
1107
+ }
1108
+ if (ts.isConstructorDeclaration(member)) {
1109
+ const ctorSymbol = checker.getSymbolAtLocation(member);
1110
+ const ctorDoc = ctorSymbol ? parseJSDocComment(ctorSymbol, checker) : null;
1111
+ const signature = checker.getSignatureFromDeclaration(member);
1112
+ const signatures = signature ? [serializeSignature(signature, checker, typeRefs, referencedTypes, ctorDoc, ctorSymbol)] : undefined;
1113
+ members.push({
1114
+ id: "constructor",
1115
+ name: "constructor",
1116
+ kind: "constructor",
1117
+ visibility: getMemberVisibility(member.modifiers),
1118
+ signatures,
1119
+ description: ctorSymbol ? getJSDocComment(ctorSymbol, checker) : undefined
1120
+ });
1121
+ continue;
1122
+ }
1123
+ if (ts.isGetAccessorDeclaration(member) || ts.isSetAccessorDeclaration(member)) {
1124
+ const memberName = member.name?.getText();
1125
+ if (!memberName)
1126
+ continue;
1127
+ const memberSymbol = checker.getSymbolAtLocation(member.name);
1128
+ const accessorType = ts.isGetAccessorDeclaration(member) ? checker.getTypeAtLocation(member) : member.parameters.length > 0 ? checker.getTypeAtLocation(member.parameters[0]) : checker.getTypeAtLocation(member);
1129
+ collectReferencedTypes(accessorType, checker, referencedTypes);
1130
+ const schema = formatTypeReference(accessorType, checker, typeRefs, referencedTypes);
1131
+ members.push({
1132
+ id: memberName,
1133
+ name: memberName,
1134
+ kind: "accessor",
1135
+ visibility: getMemberVisibility(member.modifiers),
1136
+ schema,
1137
+ description: memberSymbol ? getJSDocComment(memberSymbol, checker) : undefined
1138
+ });
1139
+ }
1140
+ }
1141
+ return members;
1142
+ }
1143
+ function serializeSignature(signature, checker, typeRefs, referencedTypes, doc, symbol) {
1144
+ return {
1145
+ parameters: signature.getParameters().map((param) => {
1146
+ const paramDecl = param.valueDeclaration;
1147
+ const paramType = paramDecl?.type != null ? checker.getTypeFromTypeNode(paramDecl.type) : checker.getTypeAtLocation(paramDecl);
1148
+ collectReferencedTypes(paramType, checker, referencedTypes);
1149
+ const paramDoc = paramDecl ? getParameterDocumentation(param, paramDecl, checker) : undefined;
1150
+ return structureParameter(param, paramDecl, paramType, checker, typeRefs, doc, paramDoc, referencedTypes);
1151
+ }),
1152
+ returns: {
1153
+ schema: formatTypeReference(signature.getReturnType(), checker, typeRefs, referencedTypes),
1154
+ description: doc?.returns || ""
1155
+ },
1156
+ description: doc?.description || (symbol ? getJSDocComment(symbol, checker) : undefined)
1157
+ };
1158
+ }
1159
+ function getMemberVisibility(modifiers) {
1160
+ if (!modifiers)
1161
+ return;
1162
+ if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword)) {
1163
+ return "private";
1164
+ }
1165
+ if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.ProtectedKeyword)) {
1166
+ return "protected";
1167
+ }
1168
+ if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.PublicKeyword)) {
1169
+ return "public";
1170
+ }
1171
+ return;
1172
+ }
1173
+ function getMethodFlags(member) {
1174
+ const flags = {};
1175
+ if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
1176
+ flags.static = true;
1177
+ }
1178
+ if (member.asteriskToken) {
1179
+ flags.generator = true;
1180
+ }
1181
+ if (member.questionToken) {
1182
+ flags.optional = true;
1183
+ }
1184
+ return Object.keys(flags).length > 0 ? flags : undefined;
1185
+ }
1186
+
1187
+ // src/analysis/serializers/enums.ts
1188
+ function serializeEnum(declaration, symbol, context) {
1189
+ const parsedDoc = parseJSDocComment(symbol, context.checker);
1190
+ const description = parsedDoc?.description ?? getJSDocComment(symbol, context.checker);
1191
+ const exportEntry = {
1192
+ id: symbol.getName(),
1193
+ name: symbol.getName(),
1194
+ kind: "enum",
1195
+ description,
1196
+ source: getSourceLocation(declaration),
1197
+ tags: parsedDoc?.tags
1198
+ };
1199
+ const typeDefinition = {
1200
+ id: symbol.getName(),
1201
+ name: symbol.getName(),
1202
+ kind: "enum",
1203
+ members: getEnumMembers(declaration),
1204
+ description,
1205
+ source: getSourceLocation(declaration),
1206
+ tags: parsedDoc?.tags
1207
+ };
1208
+ return {
1209
+ exportEntry,
1210
+ typeDefinition
1211
+ };
1212
+ }
1213
+ function getEnumMembers(enumDecl) {
1214
+ return enumDecl.members.map((member) => ({
1215
+ id: member.name?.getText() || "",
1216
+ name: member.name?.getText() || "",
1217
+ value: member.initializer ? member.initializer.getText() : undefined,
1218
+ description: ""
1219
+ }));
1220
+ }
1221
+
1222
+ // src/analysis/serializers/functions.ts
1223
+ function serializeCallSignatures(signatures, symbol, context, parsedDoc) {
1224
+ if (signatures.length === 0) {
1225
+ return [];
1226
+ }
1227
+ const { checker, typeRegistry } = context;
1228
+ const typeRefs = typeRegistry.getTypeRefs();
1229
+ const referencedTypes = typeRegistry.getReferencedTypes();
1230
+ const functionDoc = parsedDoc ?? (symbol ? parseJSDocComment(symbol, checker) : null);
1231
+ return signatures.map((signature) => {
1232
+ const parameters = signature.getParameters().map((param) => {
1233
+ const paramDecl = param.declarations?.find(ts.isParameter);
1234
+ const paramType = paramDecl ? paramDecl.type != null ? checker.getTypeFromTypeNode(paramDecl.type) : checker.getTypeAtLocation(paramDecl) : checker.getTypeOfSymbolAtLocation(param, symbol?.declarations?.[0] ?? signature.declaration ?? param.declarations?.[0] ?? param.valueDeclaration);
1235
+ collectReferencedTypes(paramType, checker, referencedTypes);
1236
+ if (paramDecl?.type) {
1237
+ collectReferencedTypesFromNode(paramDecl.type, checker, referencedTypes);
1238
+ }
1239
+ if (paramDecl && ts.isParameter(paramDecl)) {
1240
+ const paramDoc = getParameterDocumentation(param, paramDecl, checker);
1241
+ return structureParameter(param, paramDecl, paramType, checker, typeRefs, functionDoc, paramDoc, referencedTypes);
1242
+ }
1243
+ return {
1244
+ name: param.getName(),
1245
+ required: !(param.flags & ts.SymbolFlags.Optional),
1246
+ description: "",
1247
+ schema: formatTypeReference(paramType, checker, typeRefs, referencedTypes)
1248
+ };
1249
+ });
1250
+ const returnType = signature.getReturnType();
1251
+ if (returnType) {
1252
+ collectReferencedTypes(returnType, checker, referencedTypes);
1253
+ }
1254
+ return {
1255
+ parameters,
1256
+ returns: {
1257
+ schema: returnType ? formatTypeReference(returnType, checker, typeRefs, referencedTypes) : { type: "void" },
1258
+ description: functionDoc?.returns || ""
1259
+ },
1260
+ description: functionDoc?.description || undefined
1261
+ };
1262
+ });
1263
+ }
1264
+ function serializeFunctionExport(declaration, symbol, context) {
1265
+ const { checker } = context;
1266
+ const signature = checker.getSignatureFromDeclaration(declaration);
1267
+ const funcSymbol = checker.getSymbolAtLocation(declaration.name || declaration);
1268
+ const parsedDoc = parseJSDocComment(symbol, checker);
1269
+ const description = parsedDoc?.description ?? getJSDocComment(symbol, checker);
1270
+ return {
1271
+ id: symbol.getName(),
1272
+ name: symbol.getName(),
1273
+ kind: "function",
1274
+ signatures: signature ? serializeCallSignatures([signature], funcSymbol ?? symbol, context, parsedDoc) : [],
1275
+ description,
1276
+ source: getSourceLocation(declaration),
1277
+ examples: parsedDoc?.examples,
1278
+ tags: parsedDoc?.tags
1279
+ };
1280
+ }
1281
+
1282
+ // src/analysis/serializers/interfaces.ts
1283
+ function serializeInterface(declaration, symbol, context) {
1284
+ const parsedDoc = parseJSDocComment(symbol, context.checker);
1285
+ const description = parsedDoc?.description ?? getJSDocComment(symbol, context.checker);
1286
+ const exportEntry = {
1287
+ id: symbol.getName(),
1288
+ name: symbol.getName(),
1289
+ kind: "interface",
1290
+ description,
1291
+ source: getSourceLocation(declaration),
1292
+ tags: parsedDoc?.tags
1293
+ };
1294
+ const schema = interfaceToSchema(declaration, context.checker, context.typeRegistry.getTypeRefs(), context.typeRegistry.getReferencedTypes());
1295
+ const typeDefinition = {
1296
+ id: symbol.getName(),
1297
+ name: symbol.getName(),
1298
+ kind: "interface",
1299
+ schema,
1300
+ description,
1301
+ source: getSourceLocation(declaration),
1302
+ tags: parsedDoc?.tags
1303
+ };
1304
+ return {
1305
+ exportEntry,
1306
+ typeDefinition
1307
+ };
1308
+ }
1309
+ function interfaceToSchema(iface, typeChecker, typeRefs, referencedTypes) {
1310
+ const schema = {
1311
+ type: "object",
1312
+ properties: {}
1313
+ };
1314
+ const required = [];
1315
+ for (const prop of iface.members.filter(ts.isPropertySignature)) {
1316
+ const propName = prop.name?.getText() || "";
1317
+ if (prop.type) {
1318
+ const propType = typeChecker.getTypeAtLocation(prop.type);
1319
+ collectReferencedTypes(propType, typeChecker, referencedTypes);
1320
+ }
1321
+ schema.properties[propName] = prop.type ? formatTypeReference(typeChecker.getTypeAtLocation(prop.type), typeChecker, typeRefs, referencedTypes) : { type: "any" };
1322
+ if (!prop.questionToken) {
1323
+ required.push(propName);
1324
+ }
1325
+ }
1326
+ if (required.length > 0) {
1327
+ schema.required = required;
1328
+ }
1329
+ return schema;
1330
+ }
1331
+
1332
+ // src/analysis/serializers/type-aliases.ts
1333
+ function serializeTypeAlias(declaration, symbol, context) {
1334
+ const { checker, typeRegistry } = context;
1335
+ const typeRefs = typeRegistry.getTypeRefs();
1336
+ const referencedTypes = typeRegistry.getReferencedTypes();
1337
+ const parsedDoc = parseJSDocComment(symbol, checker);
1338
+ const description = parsedDoc?.description ?? getJSDocComment(symbol, checker);
1339
+ const exportEntry = {
1340
+ id: symbol.getName(),
1341
+ name: symbol.getName(),
1342
+ kind: "type",
1343
+ type: typeToRef(declaration.type, checker, typeRefs, referencedTypes),
1344
+ description,
1345
+ source: getSourceLocation(declaration),
1346
+ tags: parsedDoc?.tags
1347
+ };
1348
+ const aliasType = checker.getTypeAtLocation(declaration.type);
1349
+ const aliasName = symbol.getName();
1350
+ const existingRef = typeRefs.get(aliasName);
1351
+ if (existingRef) {
1352
+ typeRefs.delete(aliasName);
1353
+ }
1354
+ const aliasSchema = formatTypeReference(aliasType, checker, typeRefs, undefined);
1355
+ if (existingRef) {
1356
+ typeRefs.set(aliasName, existingRef);
1357
+ }
1358
+ const typeDefinition = {
1359
+ id: symbol.getName(),
1360
+ name: symbol.getName(),
1361
+ kind: "type",
1362
+ description,
1363
+ source: getSourceLocation(declaration),
1364
+ tags: parsedDoc?.tags
1365
+ };
1366
+ if (typeof aliasSchema === "string") {
1367
+ typeDefinition.type = aliasSchema;
1368
+ } else if (aliasSchema && Object.keys(aliasSchema).length > 0) {
1369
+ typeDefinition.schema = aliasSchema;
1370
+ } else {
1371
+ typeDefinition.type = declaration.type.getText();
1372
+ }
1373
+ return {
1374
+ exportEntry,
1375
+ typeDefinition
1376
+ };
1377
+ }
1378
+ function typeToRef(node, typeChecker, typeRefs, referencedTypes) {
1379
+ const type = typeChecker.getTypeAtLocation(node);
1380
+ collectReferencedTypes(type, typeChecker, referencedTypes);
1381
+ return formatTypeReference(type, typeChecker, typeRefs, referencedTypes);
1382
+ }
1383
+
1384
+ // src/analysis/serializers/variables.ts
1385
+ function serializeVariable(declaration, symbol, context) {
1386
+ const { checker, typeRegistry } = context;
1387
+ const variableType = checker.getTypeAtLocation(declaration.name ?? declaration);
1388
+ const callSignatures = variableType.getCallSignatures();
1389
+ const parsedDoc = parseJSDocComment(symbol, checker);
1390
+ const description = parsedDoc?.description ?? getJSDocComment(symbol, checker);
1391
+ if (callSignatures.length > 0) {
1392
+ return {
1393
+ id: symbol.getName(),
1394
+ name: symbol.getName(),
1395
+ kind: "function",
1396
+ signatures: serializeCallSignatures(callSignatures, symbol, context, parsedDoc),
1397
+ description,
1398
+ source: getSourceLocation(declaration.initializer ?? declaration),
1399
+ examples: parsedDoc?.examples,
1400
+ tags: parsedDoc?.tags
1401
+ };
1402
+ }
1403
+ const typeRefs = typeRegistry.getTypeRefs();
1404
+ const referencedTypes = typeRegistry.getReferencedTypes();
1405
+ return {
1406
+ id: symbol.getName(),
1407
+ name: symbol.getName(),
1408
+ kind: "variable",
1409
+ type: typeToRef2(declaration, checker, typeRefs, referencedTypes),
1410
+ description,
1411
+ source: getSourceLocation(declaration),
1412
+ tags: parsedDoc?.tags
1413
+ };
1414
+ }
1415
+ function typeToRef2(node, typeChecker, typeRefs, referencedTypes) {
1416
+ const type = typeChecker.getTypeAtLocation(node);
1417
+ collectReferencedTypes(type, typeChecker, referencedTypes);
1418
+ return formatTypeReference(type, typeChecker, typeRefs, referencedTypes);
1419
+ }
1420
+
1421
+ // src/analysis/type-registry.ts
1422
+ class TypeRegistry {
1423
+ typeRefs = new Map;
1424
+ typeDefinitions = new Map;
1425
+ referencedTypes = new Set;
1426
+ registerExportedType(name, id = name) {
1427
+ if (!this.typeRefs.has(name)) {
1428
+ this.typeRefs.set(name, id);
1429
+ }
1430
+ }
1431
+ hasType(name) {
1432
+ return this.typeDefinitions.has(name);
1433
+ }
1434
+ registerTypeDefinition(definition) {
1435
+ if (this.typeDefinitions.has(definition.name)) {
1436
+ return false;
1437
+ }
1438
+ this.typeDefinitions.set(definition.name, definition);
1439
+ if (!this.typeRefs.has(definition.name)) {
1440
+ this.typeRefs.set(definition.name, definition.id);
1441
+ }
1442
+ return true;
1443
+ }
1444
+ getTypeRefs() {
1445
+ return this.typeRefs;
1446
+ }
1447
+ getTypeDefinitions() {
1448
+ return Array.from(this.typeDefinitions.values());
1449
+ }
1450
+ getReferencedTypes() {
1451
+ return this.referencedTypes;
1452
+ }
1453
+ isKnownType(name) {
1454
+ if (this.typeDefinitions.has(name)) {
1455
+ return true;
1456
+ }
1457
+ const ref = this.typeRefs.get(name);
1458
+ if (ref === undefined) {
1459
+ return false;
1460
+ }
1461
+ if (ref !== name) {
1462
+ return this.typeDefinitions.has(ref);
1463
+ }
1464
+ return false;
1465
+ }
1466
+ }
1467
+
1468
+ // src/analysis/spec-builder.ts
1469
+ function buildOpenPkgSpec(context, resolveExternalTypes) {
1470
+ const { baseDir, checker: typeChecker, sourceFile, program } = context;
1471
+ const packageJsonPath = path3.join(baseDir, "package.json");
1472
+ const packageJson = fs.existsSync(packageJsonPath) ? JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) : {};
1473
+ const spec = {
1474
+ $schema: SCHEMA_URL,
1475
+ openpkg: "0.1.0",
1476
+ meta: {
1477
+ name: packageJson.name || "unknown",
1478
+ version: packageJson.version || "1.0.0",
1479
+ description: packageJson.description || "",
1480
+ license: packageJson.license || "",
1481
+ repository: packageJson.repository?.url || packageJson.repository || "",
1482
+ ecosystem: "js/ts"
1483
+ },
1484
+ exports: [],
1485
+ types: []
1486
+ };
1487
+ const typeRegistry = new TypeRegistry;
1488
+ const serializerContext = {
1489
+ checker: typeChecker,
1490
+ typeRegistry
1491
+ };
1492
+ const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
1493
+ if (!moduleSymbol) {
1494
+ return spec;
1495
+ }
1496
+ const exportedSymbols = typeChecker.getExportsOfModule(moduleSymbol);
1497
+ for (const symbol of exportedSymbols) {
1498
+ const { declaration, targetSymbol } = resolveExportTarget(symbol, typeChecker);
1499
+ if (!declaration)
1500
+ continue;
1501
+ const exportName = symbol.getName();
1502
+ if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration) || ts.isTypeAliasDeclaration(declaration) || ts.isEnumDeclaration(declaration)) {
1503
+ typeRegistry.registerExportedType(exportName, targetSymbol.getName());
1504
+ }
1505
+ }
1506
+ for (const symbol of exportedSymbols) {
1507
+ const { declaration, targetSymbol } = resolveExportTarget(symbol, typeChecker);
1508
+ if (!declaration)
1509
+ continue;
1510
+ const exportName = symbol.getName();
1511
+ if (ts.isFunctionDeclaration(declaration)) {
1512
+ const exportEntry = serializeFunctionExport(declaration, targetSymbol, serializerContext);
1513
+ spec.exports.push(withExportName(exportEntry, exportName));
1514
+ } else if (ts.isClassDeclaration(declaration)) {
1515
+ const { exportEntry, typeDefinition } = serializeClass(declaration, targetSymbol, serializerContext);
1516
+ spec.exports.push(withExportName(exportEntry, exportName));
1517
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1518
+ spec.types?.push(typeDefinition);
1519
+ }
1520
+ } else if (ts.isInterfaceDeclaration(declaration)) {
1521
+ const { exportEntry, typeDefinition } = serializeInterface(declaration, targetSymbol, serializerContext);
1522
+ spec.exports.push(withExportName(exportEntry, exportName));
1523
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1524
+ spec.types?.push(typeDefinition);
1525
+ }
1526
+ } else if (ts.isTypeAliasDeclaration(declaration)) {
1527
+ const { exportEntry, typeDefinition } = serializeTypeAlias(declaration, targetSymbol, serializerContext);
1528
+ spec.exports.push(withExportName(exportEntry, exportName));
1529
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1530
+ spec.types?.push(typeDefinition);
1531
+ }
1532
+ } else if (ts.isEnumDeclaration(declaration)) {
1533
+ const { exportEntry, typeDefinition } = serializeEnum(declaration, targetSymbol, serializerContext);
1534
+ spec.exports.push(withExportName(exportEntry, exportName));
1535
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1536
+ spec.types?.push(typeDefinition);
1537
+ }
1538
+ } else if (ts.isVariableDeclaration(declaration)) {
1539
+ const exportEntry = serializeVariable(declaration, targetSymbol, serializerContext);
1540
+ spec.exports.push(withExportName(exportEntry, exportName));
1541
+ }
1542
+ }
1543
+ for (const typeName of typeRegistry.getReferencedTypes()) {
1544
+ if (typeRegistry.isKnownType(typeName)) {
1545
+ continue;
1546
+ }
1547
+ const allSourceFiles = program.getSourceFiles();
1548
+ for (const file of allSourceFiles) {
1549
+ if (!resolveExternalTypes && (file.fileName.includes("node_modules") || file.fileName.endsWith(".d.ts") && !file.fileName.startsWith(baseDir))) {
1550
+ continue;
1551
+ }
1552
+ const fileSymbol = typeChecker.getSymbolAtLocation(file);
1553
+ if (!fileSymbol) {
1554
+ continue;
1555
+ }
1556
+ const exports = typeChecker.getExportsOfModule(fileSymbol);
1557
+ for (const exportSymbol of exports) {
1558
+ if (exportSymbol.getName() !== typeName || typeRegistry.isKnownType(typeName)) {
1559
+ continue;
1560
+ }
1561
+ const { declaration, targetSymbol } = resolveExportTarget(exportSymbol, typeChecker);
1562
+ if (!declaration)
1563
+ continue;
1564
+ if (ts.isClassDeclaration(declaration)) {
1565
+ const { typeDefinition } = serializeClass(declaration, targetSymbol, serializerContext);
1566
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1567
+ spec.types?.push(typeDefinition);
1568
+ }
1569
+ } else if (ts.isInterfaceDeclaration(declaration)) {
1570
+ const { typeDefinition } = serializeInterface(declaration, targetSymbol, serializerContext);
1571
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1572
+ spec.types?.push(typeDefinition);
1573
+ }
1574
+ } else if (ts.isTypeAliasDeclaration(declaration)) {
1575
+ const { typeDefinition } = serializeTypeAlias(declaration, targetSymbol, serializerContext);
1576
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1577
+ spec.types?.push(typeDefinition);
1578
+ }
1579
+ } else if (ts.isEnumDeclaration(declaration)) {
1580
+ const { typeDefinition } = serializeEnum(declaration, targetSymbol, serializerContext);
1581
+ if (typeDefinition && typeRegistry.registerTypeDefinition(typeDefinition)) {
1582
+ spec.types?.push(typeDefinition);
1583
+ }
1584
+ }
1585
+ }
1586
+ }
1587
+ }
1588
+ return spec;
1589
+ }
1590
+ function resolveExportTarget(symbol, checker) {
1591
+ let targetSymbol = symbol;
1592
+ if (symbol.flags & ts.SymbolFlags.Alias) {
1593
+ const aliasTarget = checker.getImmediateAliasedSymbol(symbol);
1594
+ if (aliasTarget) {
1595
+ targetSymbol = aliasTarget;
1596
+ }
1597
+ }
1598
+ const declarations = targetSymbol.declarations ?? [];
1599
+ const declaration = targetSymbol.valueDeclaration || declarations.find((decl) => decl.kind !== ts.SyntaxKind.ExportSpecifier) || declarations[0];
1600
+ return {
1601
+ declaration,
1602
+ targetSymbol
1603
+ };
1604
+ }
1605
+ function withExportName(entry, exportName) {
1606
+ if (entry.name === exportName) {
1607
+ return entry;
1608
+ }
1609
+ return {
1610
+ ...entry,
1611
+ id: exportName,
1612
+ name: exportName
1613
+ };
1614
+ }
1615
+
1616
+ // src/analysis/run-analysis.ts
1617
+ function findNearestPackageJson(startDir) {
1618
+ let current = startDir;
1619
+ while (true) {
1620
+ const candidate = path4.join(current, "package.json");
1621
+ if (fs2.existsSync(candidate)) {
1622
+ return candidate;
1623
+ }
1624
+ const parent = path4.dirname(current);
1625
+ if (parent === current) {
1626
+ return;
1627
+ }
1628
+ current = parent;
1629
+ }
1630
+ }
1631
+ function hasNodeModulesDirectory(directories) {
1632
+ for (const dir of directories) {
1633
+ let current = dir;
1634
+ while (true) {
1635
+ const candidate = path4.join(current, "node_modules");
1636
+ if (fs2.existsSync(candidate)) {
1637
+ return true;
1638
+ }
1639
+ const parent = path4.dirname(current);
1640
+ if (parent === current) {
1641
+ break;
1642
+ }
1643
+ current = parent;
1644
+ }
1645
+ }
1646
+ return false;
1647
+ }
1648
+ function runAnalysis(input) {
1649
+ const context = createAnalysisContext(input);
1650
+ const { baseDir, options } = context;
1651
+ const packageJsonPath = findNearestPackageJson(baseDir);
1652
+ const searchDirs = new Set([baseDir]);
1653
+ if (packageJsonPath) {
1654
+ searchDirs.add(path4.dirname(packageJsonPath));
1655
+ }
1656
+ const hasNodeModules = hasNodeModulesDirectory(searchDirs);
1657
+ const resolveExternalTypes = options.resolveExternalTypes !== undefined ? options.resolveExternalTypes : hasNodeModules;
1658
+ const diagnostics = ts.getPreEmitDiagnostics(context.program);
1659
+ const spec = buildOpenPkgSpec(context, resolveExternalTypes);
1660
+ return {
1661
+ spec,
1662
+ metadata: {
1663
+ baseDir,
1664
+ configPath: context.configPath,
1665
+ packageJsonPath,
1666
+ hasNodeModules,
1667
+ resolveExternalTypes
1668
+ },
1669
+ diagnostics
1670
+ };
1671
+ }
1672
+
1673
+ // src/extractor.ts
1674
+ async function extractPackageSpec(entryFile, packageDir, content, options) {
1675
+ const result = runAnalysis({
1676
+ entryFile,
1677
+ packageDir,
1678
+ content,
1679
+ options
1680
+ });
1681
+ return result.spec;
1682
+ }
1683
+ // src/openpkg.ts
1684
+ import * as fsSync from "node:fs";
1685
+ import * as fs3 from "node:fs/promises";
1686
+ import * as path5 from "node:path";
1687
+
1688
+ // src/filtering/apply-filters.ts
1689
+ var TYPE_REF_PREFIX = "#/types/";
1690
+ var toLowerKey = (value) => value.trim().toLowerCase();
1691
+ var buildLookupMap = (values) => {
1692
+ const map = new Map;
1693
+ if (!values) {
1694
+ return map;
1695
+ }
1696
+ for (const value of values) {
1697
+ const key = toLowerKey(value);
1698
+ if (!map.has(key)) {
1699
+ map.set(key, value);
1700
+ }
1701
+ }
1702
+ return map;
1703
+ };
1704
+ var matches = (candidate, lookup) => {
1705
+ if (!candidate) {
1706
+ return;
1707
+ }
1708
+ const keys = [candidate.id, candidate.name];
1709
+ for (const key of keys) {
1710
+ if (!key) {
1711
+ continue;
1712
+ }
1713
+ const normalized = toLowerKey(key);
1714
+ if (lookup.has(normalized)) {
1715
+ return normalized;
1716
+ }
1717
+ }
1718
+ return;
1719
+ };
1720
+ var collectTypeRefs = (value, refs, seen = new Set) => {
1721
+ if (value === null || value === undefined) {
1722
+ return;
1723
+ }
1724
+ if (typeof value !== "object") {
1725
+ return;
1726
+ }
1727
+ if (seen.has(value)) {
1728
+ return;
1729
+ }
1730
+ seen.add(value);
1731
+ if (Array.isArray(value)) {
1732
+ for (const item of value) {
1733
+ collectTypeRefs(item, refs, seen);
1734
+ }
1735
+ return;
1736
+ }
1737
+ const record = value;
1738
+ for (const [key, nested] of Object.entries(record)) {
1739
+ if (key === "$ref" && typeof nested === "string" && nested.startsWith(TYPE_REF_PREFIX)) {
1740
+ const typeId = nested.slice(TYPE_REF_PREFIX.length);
1741
+ if (typeId) {
1742
+ refs.add(typeId);
1743
+ }
1744
+ }
1745
+ collectTypeRefs(nested, refs, seen);
1746
+ }
1747
+ };
1748
+ var applyFilters = (spec, options) => {
1749
+ const includeLookup = buildLookupMap(options.include);
1750
+ const excludeLookup = buildLookupMap(options.exclude);
1751
+ if (includeLookup.size === 0 && excludeLookup.size === 0) {
1752
+ return { spec, diagnostics: [], changed: false };
1753
+ }
1754
+ const includeMatches = new Set;
1755
+ const diagnostics = [];
1756
+ const exportsList = spec.exports ?? [];
1757
+ const typesList = spec.types ?? [];
1758
+ const keptExports = [];
1759
+ for (const entry of exportsList) {
1760
+ const includeMatch = includeLookup.size === 0 ? undefined : matches(entry, includeLookup);
1761
+ const excludeMatch = matches(entry, excludeLookup);
1762
+ const allowedByInclude = includeLookup.size === 0 || Boolean(includeMatch);
1763
+ const allowedByExclude = !excludeMatch;
1764
+ if (includeMatch) {
1765
+ includeMatches.add(includeMatch);
1766
+ }
1767
+ if (allowedByInclude && allowedByExclude) {
1768
+ keptExports.push(entry);
1769
+ }
1770
+ }
1771
+ const typeMap = new Map(typesList.map((typeEntry) => [typeEntry.id, typeEntry]));
1772
+ const requestedTypeIds = new Set;
1773
+ const excludedTypeIds = new Set;
1774
+ for (const typeEntry of typesList) {
1775
+ const includeMatch = includeLookup.size === 0 ? undefined : matches(typeEntry, includeLookup);
1776
+ if (includeMatch) {
1777
+ includeMatches.add(includeMatch);
1778
+ requestedTypeIds.add(typeEntry.id);
1779
+ }
1780
+ const excludeMatch = matches(typeEntry, excludeLookup);
1781
+ if (excludeMatch) {
1782
+ excludedTypeIds.add(typeEntry.id);
1783
+ }
1784
+ }
1785
+ const referencedTypeIds = new Set;
1786
+ for (const entry of keptExports) {
1787
+ collectTypeRefs(entry, referencedTypeIds);
1788
+ }
1789
+ for (const requestedId of requestedTypeIds) {
1790
+ referencedTypeIds.add(requestedId);
1791
+ }
1792
+ const processedTypeIds = new Set;
1793
+ const finalTypeIds = new Set;
1794
+ const excludedButReferenced = new Set;
1795
+ const queue = Array.from(referencedTypeIds);
1796
+ while (queue.length > 0) {
1797
+ const currentId = queue.pop();
1798
+ if (!currentId || processedTypeIds.has(currentId)) {
1799
+ continue;
1800
+ }
1801
+ processedTypeIds.add(currentId);
1802
+ if (excludedTypeIds.has(currentId)) {
1803
+ excludedButReferenced.add(currentId);
1804
+ continue;
1805
+ }
1806
+ if (!typeMap.has(currentId)) {
1807
+ continue;
1808
+ }
1809
+ finalTypeIds.add(currentId);
1810
+ const typeEntry = typeMap.get(currentId);
1811
+ if (typeEntry) {
1812
+ const nestedRefs = new Set;
1813
+ collectTypeRefs(typeEntry, nestedRefs);
1814
+ for (const ref of nestedRefs) {
1815
+ if (!processedTypeIds.has(ref)) {
1816
+ queue.push(ref);
1817
+ }
1818
+ }
1819
+ }
1820
+ }
1821
+ if (includeLookup.size > 0 && keptExports.length === 0 && finalTypeIds.size === 0) {
1822
+ diagnostics.push({
1823
+ message: "Include filters did not match any exports or types.",
1824
+ severity: "warning"
1825
+ });
1826
+ }
1827
+ if (excludedButReferenced.size > 0) {
1828
+ const labels = Array.from(excludedButReferenced).map((id) => {
1829
+ const entry = typeMap.get(id);
1830
+ return entry?.name ?? id;
1831
+ });
1832
+ diagnostics.push({
1833
+ message: `Excluded types are still referenced: ${labels.join(", ")}`,
1834
+ severity: "warning",
1835
+ target: "type"
1836
+ });
1837
+ }
1838
+ const unmatchedIncludes = Array.from(includeLookup.keys()).filter((key) => !includeMatches.has(key));
1839
+ if (unmatchedIncludes.length > 0) {
1840
+ const labels = unmatchedIncludes.map((key) => includeLookup.get(key) ?? key);
1841
+ diagnostics.push({
1842
+ message: `Include filters with no matches: ${labels.join(", ")}`,
1843
+ severity: "warning"
1844
+ });
1845
+ }
1846
+ const filteredTypes = typesList.filter((typeEntry) => finalTypeIds.has(typeEntry.id));
1847
+ const filteredSpec = {
1848
+ ...spec,
1849
+ exports: keptExports,
1850
+ types: filteredTypes.length > 0 ? filteredTypes : spec.types ? [] : undefined
1851
+ };
1852
+ const changed = keptExports.length !== exportsList.length || filteredTypes.length !== typesList.length;
1853
+ return {
1854
+ spec: filteredSpec,
1855
+ diagnostics,
1856
+ changed
1857
+ };
1858
+ };
1859
+
1860
+ // src/openpkg.ts
1861
+ class OpenPkg {
1862
+ options;
1863
+ constructor(options = {}) {
1864
+ this.options = normalizeOpenPkgOptions(options);
1865
+ }
1866
+ async analyze(code, fileName = "temp.ts", analyzeOptions = {}) {
1867
+ const resolvedFileName = path5.resolve(fileName);
1868
+ const tempDir = path5.dirname(resolvedFileName);
1869
+ const spec = await extractPackageSpec(resolvedFileName, tempDir, code, this.options);
1870
+ return this.applySpecFilters(spec, analyzeOptions.filters).spec;
1871
+ }
1872
+ async analyzeFile(filePath, analyzeOptions = {}) {
1873
+ const resolvedPath = path5.resolve(filePath);
1874
+ const content = await fs3.readFile(resolvedPath, "utf-8");
1875
+ const packageDir = resolvePackageDir(resolvedPath);
1876
+ const spec = await extractPackageSpec(resolvedPath, packageDir, content, this.options);
1877
+ return this.applySpecFilters(spec, analyzeOptions.filters).spec;
1878
+ }
1879
+ async analyzeProject(entryPath, analyzeOptions = {}) {
1880
+ return this.analyzeFile(entryPath, analyzeOptions);
1881
+ }
1882
+ async analyzeWithDiagnostics(code, fileName, analyzeOptions = {}) {
1883
+ const resolvedFileName = path5.resolve(fileName ?? "temp.ts");
1884
+ const packageDir = resolvePackageDir(resolvedFileName);
1885
+ const analysis = runAnalysis({
1886
+ entryFile: resolvedFileName,
1887
+ packageDir,
1888
+ content: code,
1889
+ options: this.options
1890
+ });
1891
+ const filterOutcome = this.applySpecFilters(analysis.spec, analyzeOptions.filters);
1892
+ return {
1893
+ spec: filterOutcome.spec,
1894
+ diagnostics: [
1895
+ ...analysis.diagnostics.map((diagnostic) => this.normalizeDiagnostic(diagnostic)),
1896
+ ...filterOutcome.diagnostics
1897
+ ],
1898
+ metadata: this.normalizeMetadata(analysis.metadata)
1899
+ };
1900
+ }
1901
+ async analyzeFileWithDiagnostics(filePath, analyzeOptions = {}) {
1902
+ const resolvedPath = path5.resolve(filePath);
1903
+ const content = await fs3.readFile(resolvedPath, "utf-8");
1904
+ const packageDir = resolvePackageDir(resolvedPath);
1905
+ const analysis = runAnalysis({
1906
+ entryFile: resolvedPath,
1907
+ packageDir,
1908
+ content,
1909
+ options: this.options
1910
+ });
1911
+ const filterOutcome = this.applySpecFilters(analysis.spec, analyzeOptions.filters);
1912
+ return {
1913
+ spec: filterOutcome.spec,
1914
+ diagnostics: [
1915
+ ...analysis.diagnostics.map((diagnostic) => this.normalizeDiagnostic(diagnostic)),
1916
+ ...filterOutcome.diagnostics
1917
+ ],
1918
+ metadata: this.normalizeMetadata(analysis.metadata)
1919
+ };
1920
+ }
1921
+ normalizeDiagnostic(tsDiagnostic) {
1922
+ const message = ts.flattenDiagnosticMessageText(tsDiagnostic.messageText, `
1923
+ `);
1924
+ let location;
1925
+ if (tsDiagnostic.file && typeof tsDiagnostic.start === "number") {
1926
+ const { line, character } = tsDiagnostic.file.getLineAndCharacterOfPosition(tsDiagnostic.start);
1927
+ location = {
1928
+ file: tsDiagnostic.file.fileName,
1929
+ line: line + 1,
1930
+ column: character + 1
1931
+ };
1932
+ }
1933
+ const severity = this.mapSeverity(tsDiagnostic.category);
1934
+ return {
1935
+ message,
1936
+ severity,
1937
+ location
1938
+ };
1939
+ }
1940
+ mapSeverity(category) {
1941
+ switch (category) {
1942
+ case ts.DiagnosticCategory.Message:
1943
+ case ts.DiagnosticCategory.Suggestion:
1944
+ return "info";
1945
+ case ts.DiagnosticCategory.Warning:
1946
+ return "warning";
1947
+ default:
1948
+ return "error";
1949
+ }
1950
+ }
1951
+ normalizeMetadata(metadata) {
1952
+ return {
1953
+ baseDir: metadata.baseDir,
1954
+ configPath: metadata.configPath,
1955
+ packageJsonPath: metadata.packageJsonPath,
1956
+ hasNodeModules: metadata.hasNodeModules,
1957
+ resolveExternalTypes: metadata.resolveExternalTypes
1958
+ };
1959
+ }
1960
+ applySpecFilters(spec, filters) {
1961
+ if (!filters || !filters.include?.length && !filters.exclude?.length) {
1962
+ return { spec, diagnostics: [] };
1963
+ }
1964
+ const result = applyFilters(spec, filters);
1965
+ return {
1966
+ spec: result.spec,
1967
+ diagnostics: result.diagnostics.map((diagnostic) => ({
1968
+ message: diagnostic.message,
1969
+ severity: diagnostic.severity
1970
+ }))
1971
+ };
1972
+ }
1973
+ }
1974
+ async function analyze(code, options = {}) {
1975
+ return new OpenPkg().analyze(code, "temp.ts", options);
1976
+ }
1977
+ async function analyzeFile(filePath, options = {}) {
1978
+ return new OpenPkg().analyzeFile(filePath, options);
1979
+ }
1980
+ function resolvePackageDir(entryFile) {
1981
+ const fallbackDir = path5.dirname(entryFile);
1982
+ let currentDir = fallbackDir;
1983
+ while (true) {
1984
+ const candidate = path5.join(currentDir, "package.json");
1985
+ if (fsSync.existsSync(candidate)) {
1986
+ return currentDir;
1987
+ }
1988
+ const parentDir = path5.dirname(currentDir);
1989
+ if (parentDir === currentDir) {
1990
+ return fallbackDir;
1991
+ }
1992
+ currentDir = parentDir;
1993
+ }
1994
+ }
1995
+ // src/types/openpkg.ts
1996
+ import { z } from "zod";
1997
+ var schemaSchema = z.lazy(() => z.union([
1998
+ z.object({
1999
+ type: z.enum(["string", "number", "boolean", "integer", "null", "array", "object"])
2000
+ }),
2001
+ z.object({
2002
+ $ref: z.string()
2003
+ }),
2004
+ z.object({
2005
+ type: z.literal("array"),
2006
+ items: schemaSchema.optional(),
2007
+ description: z.string().optional()
2008
+ }),
2009
+ z.object({
2010
+ type: z.literal("object"),
2011
+ properties: z.record(z.string(), schemaSchema).optional(),
2012
+ required: z.array(z.string()).optional(),
2013
+ description: z.string().optional(),
2014
+ additionalProperties: z.union([z.boolean(), schemaSchema]).optional()
2015
+ }),
2016
+ z.object({
2017
+ oneOf: z.array(schemaSchema),
2018
+ description: z.string().optional()
2019
+ }),
2020
+ z.object({
2021
+ anyOf: z.array(schemaSchema),
2022
+ description: z.string().optional()
2023
+ }),
2024
+ z.object({
2025
+ allOf: z.array(schemaSchema),
2026
+ description: z.string().optional()
2027
+ }),
2028
+ z.object({
2029
+ enum: z.array(z.union([z.string(), z.number(), z.null()])),
2030
+ description: z.string().optional()
2031
+ })
2032
+ ]));
2033
+ var parameterSchema = z.object({
2034
+ name: z.string(),
2035
+ in: z.literal("query").optional(),
2036
+ required: z.boolean().optional(),
2037
+ description: z.string().optional(),
2038
+ schema: schemaSchema
2039
+ });
2040
+ var returnTypeSchema = z.object({
2041
+ schema: schemaSchema,
2042
+ description: z.string().optional()
2043
+ });
2044
+ var classMemberSchema = z.object({
2045
+ id: z.string(),
2046
+ name: z.string(),
2047
+ kind: z.enum(["method", "property", "constructor", "accessor"]),
2048
+ visibility: z.enum(["public", "private", "protected"]).optional(),
2049
+ signatures: z.array(z.object({
2050
+ parameters: z.array(parameterSchema).optional(),
2051
+ returns: returnTypeSchema.optional(),
2052
+ description: z.string().optional()
2053
+ })).optional(),
2054
+ schema: schemaSchema.optional(),
2055
+ description: z.string().optional(),
2056
+ examples: z.array(z.string()).optional(),
2057
+ flags: z.record(z.string(), z.boolean()).optional()
2058
+ });
2059
+ var enumMemberSchema = z.object({
2060
+ id: z.string(),
2061
+ name: z.string(),
2062
+ value: z.union([z.string(), z.number()]).optional(),
2063
+ description: z.string().optional()
2064
+ });
2065
+ var memberSchema = z.union([classMemberSchema, enumMemberSchema]);
2066
+ var openPkgSchema = z.object({
2067
+ $schema: z.string().optional(),
2068
+ openpkg: z.literal("0.1.0"),
2069
+ meta: z.object({
2070
+ name: z.string(),
2071
+ version: z.string(),
2072
+ description: z.string().optional(),
2073
+ license: z.string().optional(),
2074
+ repository: z.string().optional(),
2075
+ ecosystem: z.string().default("js/ts")
2076
+ }),
2077
+ exports: z.array(z.object({
2078
+ id: z.string(),
2079
+ name: z.string(),
2080
+ kind: z.enum([
2081
+ "function",
2082
+ "class",
2083
+ "variable",
2084
+ "interface",
2085
+ "type",
2086
+ "enum",
2087
+ "module",
2088
+ "namespace",
2089
+ "reference"
2090
+ ]),
2091
+ signatures: z.array(z.object({
2092
+ parameters: z.array(parameterSchema).optional(),
2093
+ returns: returnTypeSchema.optional(),
2094
+ description: z.string().optional()
2095
+ })).optional(),
2096
+ members: z.array(memberSchema).optional(),
2097
+ type: z.union([z.string(), schemaSchema]).optional(),
2098
+ schema: schemaSchema.optional(),
2099
+ description: z.string().optional(),
2100
+ examples: z.array(z.string()).optional(),
2101
+ source: z.object({
2102
+ file: z.string().optional(),
2103
+ line: z.number().optional(),
2104
+ url: z.string().optional()
2105
+ }).optional(),
2106
+ flags: z.record(z.string(), z.unknown()).optional(),
2107
+ tags: z.array(z.object({
2108
+ name: z.string(),
2109
+ text: z.string()
2110
+ })).optional()
2111
+ })),
2112
+ types: z.array(z.object({
2113
+ id: z.string(),
2114
+ name: z.string(),
2115
+ kind: z.enum(["class", "interface", "type", "enum"]),
2116
+ description: z.string().optional(),
2117
+ schema: schemaSchema.optional(),
2118
+ type: z.union([z.string(), schemaSchema]).optional(),
2119
+ members: z.array(memberSchema).optional(),
2120
+ source: z.object({
2121
+ file: z.string().optional(),
2122
+ line: z.number().optional(),
2123
+ url: z.string().optional()
2124
+ }).optional(),
2125
+ tags: z.array(z.object({
2126
+ name: z.string(),
2127
+ text: z.string()
2128
+ })).optional(),
2129
+ rawComments: z.string().optional()
2130
+ })).optional(),
2131
+ examples: z.array(z.object({})).optional(),
2132
+ extensions: z.record(z.string(), z.unknown()).optional()
2133
+ });
2134
+ export {
2135
+ openPkgSchema,
2136
+ extractPackageSpec,
2137
+ analyzeFile,
2138
+ analyze,
2139
+ OpenPkg
2140
+ };