@deplens/mcp 0.1.2 → 0.1.3

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.
@@ -0,0 +1,1041 @@
1
+ // inspect.mjs
2
+ import { createRequire } from "module"
3
+ import fs from "fs"
4
+ import path from "path"
5
+ import { fileURLToPath, pathToFileURL } from "url"
6
+ import fg from "fast-glob"
7
+ import { resolve as importMetaResolve } from "import-meta-resolve"
8
+ import { parseDtsFile } from "./parse-dts.mjs"
9
+
10
+ function getPackageName(target) {
11
+ if (!target) return target
12
+ if (target.startsWith("@")) {
13
+ const parts = target.split("/")
14
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : target
15
+ }
16
+ return target.split("/")[0]
17
+ }
18
+
19
+ function getPackageSubpath(target) {
20
+ if (!target) return null
21
+ const base = getPackageName(target)
22
+ if (!base) return null
23
+ if (target === base) return null
24
+ const prefix = `${base}/`
25
+ return target.startsWith(prefix) ? target.slice(prefix.length) : null
26
+ }
27
+
28
+ async function findWorkspaceRoot(startDir) {
29
+ if (!startDir) return null
30
+ let dir = path.resolve(startDir)
31
+ while (true) {
32
+ const pkgPath = path.join(dir, "package.json")
33
+ if (fs.existsSync(pkgPath)) {
34
+ try {
35
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
36
+ if (pkg?.workspaces) return { dir, pkg }
37
+ } catch (e) {}
38
+ }
39
+ const parent = path.dirname(dir)
40
+ if (parent === dir) break
41
+ dir = parent
42
+ }
43
+ return null
44
+ }
45
+
46
+ async function listWorkspacePackageDirs(rootDir, workspaces, targetPackage) {
47
+ const patterns = Array.isArray(workspaces) ? workspaces : workspaces?.packages
48
+ if (!Array.isArray(patterns) || patterns.length === 0) return []
49
+ const dirs = []
50
+ const target = getPackageName(targetPackage)
51
+
52
+ for (const pattern of patterns) {
53
+ if (!pattern) continue
54
+ const normalized = String(pattern).replace(/\\/g, "/").replace(/\/?$/, "/")
55
+ const globPattern = `${normalized}package.json`
56
+ const matches =
57
+ typeof Bun !== "undefined" && Bun.Glob
58
+ ? await Array.fromAsync(
59
+ new Bun.Glob(globPattern).scan({
60
+ cwd: rootDir,
61
+ absolute: false,
62
+ onlyFiles: true,
63
+ followSymlinks: false,
64
+ dot: false,
65
+ }),
66
+ )
67
+ : await fg(globPattern, {
68
+ cwd: rootDir,
69
+ onlyFiles: true,
70
+ dot: false,
71
+ followSymbolicLinks: false,
72
+ })
73
+ for (const match of matches) {
74
+ const pkgPath = path.join(rootDir, match)
75
+ try {
76
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
77
+ const depTables = [
78
+ pkg?.dependencies,
79
+ pkg?.devDependencies,
80
+ pkg?.peerDependencies,
81
+ pkg?.optionalDependencies,
82
+ ]
83
+ const hasTarget =
84
+ Boolean(target) &&
85
+ depTables.some((deps) => deps && Object.prototype.hasOwnProperty.call(deps, target))
86
+ if (hasTarget) {
87
+ dirs.push(path.dirname(pkgPath))
88
+ }
89
+ } catch (e) {}
90
+ }
91
+ }
92
+
93
+ return dirs
94
+ }
95
+
96
+ async function resolveTargetModule(target, cwd, resolveFrom) {
97
+ const baseDir = resolveFrom || cwd
98
+ if (!baseDir) return { resolved: null, resolveCwd: baseDir, resolver: null }
99
+
100
+ const tryResolve = async (dir) => {
101
+ if (!dir) return null
102
+ if (typeof Bun !== "undefined" && Bun.resolve) {
103
+ try {
104
+ const resolved = await Bun.resolve(target, dir)
105
+ return { resolved, resolver: "bun", resolveCwd: dir }
106
+ } catch (e) {}
107
+ }
108
+ try {
109
+ const parentUrl = pathToFileURL(path.join(dir, "noop.js")).href
110
+ const resolvedUrl = await importMetaResolve(target, parentUrl)
111
+ const resolvedPath = resolvedUrl.startsWith("file://") ? fileURLToPath(resolvedUrl) : resolvedUrl
112
+ return { resolved: resolvedPath, resolver: "import-meta-resolve", resolveCwd: dir }
113
+ } catch (e) {}
114
+ try {
115
+ const req = createRequire(path.join(dir, "noop.js"))
116
+ const resolved = req.resolve(target)
117
+ return { resolved, resolver: "require", resolveCwd: dir }
118
+ } catch (e) {
119
+ return null
120
+ }
121
+ }
122
+
123
+ const direct = await tryResolve(baseDir)
124
+ if (direct) return direct
125
+
126
+ const workspace = await findWorkspaceRoot(baseDir)
127
+ if (workspace?.pkg?.workspaces) {
128
+ const dirs = await listWorkspacePackageDirs(workspace.dir, workspace.pkg.workspaces, target)
129
+ for (const dir of dirs) {
130
+ const resolved = await tryResolve(dir)
131
+ if (resolved) return resolved
132
+ }
133
+ }
134
+
135
+ return { resolved: null, resolveCwd: baseDir, resolver: null }
136
+ }
137
+
138
+ function resolvePackageInfo(basePkg, require) {
139
+ let pkgPath
140
+ let pkgDir
141
+ try {
142
+ pkgPath = require.resolve(`${basePkg}/package.json`)
143
+ pkgDir = path.dirname(pkgPath)
144
+ } catch (e) {
145
+ try {
146
+ const mainPath = require.resolve(basePkg)
147
+ let dir = path.dirname(mainPath)
148
+ for (let i = 0; i < 5; i++) {
149
+ const candidate = path.join(dir, "package.json")
150
+ if (fs.existsSync(candidate)) {
151
+ pkgPath = candidate
152
+ pkgDir = dir
153
+ break
154
+ }
155
+ dir = path.dirname(dir)
156
+ }
157
+ } catch (err) {}
158
+ }
159
+
160
+ if (!pkgPath || !fs.existsSync(pkgPath)) return null
161
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
162
+ return { pkg, pkgPath, pkgDir }
163
+ }
164
+
165
+ function resolveTypesFromExportEntry(entry) {
166
+ if (!entry) return null
167
+ if (typeof entry === "string") return entry
168
+ if (typeof entry === "object") {
169
+ if (typeof entry.types === "string") return entry.types
170
+ for (const key of ["import", "require", "default"]) {
171
+ if (typeof entry[key] === "string") return entry[key]
172
+ }
173
+ }
174
+ return null
175
+ }
176
+
177
+ function coerceTypesPath(typesPath) {
178
+ if (!typesPath) return typesPath
179
+ if (typesPath.endsWith(".d.ts") || typesPath.endsWith(".d.cts") || typesPath.endsWith(".d.mts")) {
180
+ return typesPath
181
+ }
182
+ if (typesPath.endsWith(".mjs")) return typesPath.replace(/\.mjs$/, ".d.mts")
183
+ if (typesPath.endsWith(".cjs")) return typesPath.replace(/\.cjs$/, ".d.cts")
184
+ if (typesPath.endsWith(".js")) return typesPath.replace(/\.js$/, ".d.ts")
185
+ return typesPath
186
+ }
187
+
188
+ function resolveTypesFile(pkg, pkgDir, subpath) {
189
+ if (!pkg || !pkgDir) return { typesFile: null, dtsPath: null, source: null }
190
+ let typesFile = null
191
+ let source = null
192
+
193
+ if (pkg.exports) {
194
+ let entry = null
195
+ if (subpath) {
196
+ const key = subpath.startsWith(".") ? subpath : `./${subpath}`
197
+ if (typeof pkg.exports === "object") {
198
+ entry = pkg.exports[key]
199
+ }
200
+ } else if (typeof pkg.exports === "string") {
201
+ entry = pkg.exports
202
+ } else if (typeof pkg.exports === "object") {
203
+ entry = pkg.exports["."] ?? pkg.exports["./"]
204
+ }
205
+
206
+ const typesFromExport = resolveTypesFromExportEntry(entry)
207
+ if (typesFromExport) {
208
+ typesFile = coerceTypesPath(typesFromExport)
209
+ source = "exports"
210
+ }
211
+ }
212
+
213
+ if (!typesFile) {
214
+ typesFile = pkg.types || pkg.typings || null
215
+ if (typesFile) source = "package"
216
+ }
217
+
218
+ if (!typesFile) {
219
+ const candidates = [
220
+ "dist/index.d.ts",
221
+ "dist/index.d.cts",
222
+ "dist/index.d.mts",
223
+ "lib/index.d.ts",
224
+ "lib/index.d.cts",
225
+ "lib/index.d.mts",
226
+ "index.d.ts",
227
+ "index.d.cts",
228
+ "index.d.mts",
229
+ "types/index.d.ts",
230
+ ]
231
+ for (const candidate of candidates) {
232
+ const candidatePath = path.resolve(pkgDir, candidate)
233
+ if (fs.existsSync(candidatePath)) {
234
+ typesFile = candidate
235
+ source = "fallback"
236
+ break
237
+ }
238
+ }
239
+ }
240
+
241
+ if (!typesFile) {
242
+ return { typesFile: null, dtsPath: null, source: null }
243
+ }
244
+
245
+ const resolved = path.isAbsolute(typesFile) ? typesFile : path.resolve(pkgDir, typesFile)
246
+ let dtsPath = resolved
247
+ if (!fs.existsSync(dtsPath)) {
248
+ const mapped = coerceTypesPath(dtsPath)
249
+ if (mapped !== dtsPath && fs.existsSync(mapped)) {
250
+ dtsPath = mapped
251
+ } else {
252
+ const replacements = [".d.ts", ".d.cts", ".d.mts"]
253
+ for (const ext of replacements) {
254
+ if (dtsPath.endsWith(ext)) continue
255
+ const candidate = `${dtsPath}${ext}`
256
+ if (fs.existsSync(candidate)) {
257
+ dtsPath = candidate
258
+ break
259
+ }
260
+ }
261
+ if (!fs.existsSync(dtsPath) && dtsPath.endsWith(".d.ts")) {
262
+ const ctsPath = dtsPath.replace(".d.ts", ".d.cts")
263
+ const mtsPath = dtsPath.replace(".d.ts", ".d.mts")
264
+ if (fs.existsSync(ctsPath)) dtsPath = ctsPath
265
+ else if (fs.existsSync(mtsPath)) dtsPath = mtsPath
266
+ }
267
+ }
268
+ }
269
+
270
+ return { typesFile, dtsPath, source }
271
+ }
272
+
273
+ function normalizeCjsExports(exportsValue) {
274
+ if (exportsValue === null || exportsValue === undefined) {
275
+ return { default: exportsValue }
276
+ }
277
+ if (typeof exportsValue === "object" || typeof exportsValue === "function") {
278
+ return { ...exportsValue, default: exportsValue }
279
+ }
280
+ return { default: exportsValue }
281
+ }
282
+
283
+ function detectModuleFormat(resolvedPath, pkg) {
284
+ const ext = path.extname(resolvedPath)
285
+ if (ext === ".cjs" || ext === ".cts") return "cjs"
286
+ if (ext === ".mjs" || ext === ".mts") return "esm"
287
+ if (pkg?.type === "module") return "esm"
288
+ return "cjs"
289
+ }
290
+
291
+ function isProbablyClass(fn) {
292
+ if (typeof fn !== "function") return false
293
+ const source = Function.prototype.toString.call(fn)
294
+ if (source.startsWith("class ")) return true
295
+ if (fn.prototype) {
296
+ const protoProps = Object.getOwnPropertyNames(fn.prototype).filter((p) => p !== "constructor")
297
+ if (protoProps.length > 0) return true
298
+ }
299
+ return false
300
+ }
301
+
302
+ async function loadModuleExports(resolvedPath, require, pkg) {
303
+ const format = detectModuleFormat(resolvedPath, pkg)
304
+ if (format === "cjs") {
305
+ const mod = require(resolvedPath)
306
+ return { module: normalizeCjsExports(mod), format }
307
+ }
308
+ const mod = await import(pathToFileURL(resolvedPath).href)
309
+ return { module: mod, format }
310
+ }
311
+
312
+ function buildSymbolMatcher(symbols, fallbackFilter) {
313
+ const patterns = []
314
+ const addPattern = (value) => {
315
+ if (!value) return
316
+ if (value.startsWith("/") && value.endsWith("/") && value.length > 2) {
317
+ try {
318
+ patterns.push({ type: "regex", value: new RegExp(value.slice(1, -1), "i") })
319
+ return
320
+ } catch (e) {
321
+ patterns.push({ type: "substring", value: value.toLowerCase() })
322
+ return
323
+ }
324
+ }
325
+ if (value.includes("*")) {
326
+ const escaped = value.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")
327
+ patterns.push({ type: "regex", value: new RegExp(`^${escaped}$`, "i") })
328
+ return
329
+ }
330
+ patterns.push({ type: "substring", value: value.toLowerCase() })
331
+ }
332
+
333
+ if (Array.isArray(symbols)) {
334
+ symbols.forEach(addPattern)
335
+ } else if (typeof symbols === "string") {
336
+ addPattern(symbols)
337
+ } else if (fallbackFilter) {
338
+ addPattern(fallbackFilter)
339
+ }
340
+
341
+ if (patterns.length === 0) return null
342
+ return (name) => {
343
+ const lower = name.toLowerCase()
344
+ return patterns.some((pattern) => {
345
+ if (pattern.type === "regex") return pattern.value.test(name)
346
+ return lower.includes(pattern.value)
347
+ })
348
+ }
349
+ }
350
+
351
+ function truncateSummary(text, mode, maxLen, truncateMode) {
352
+ if (!text) return ""
353
+ const normalized = text.replace(/\s+/g, " ").trim()
354
+ if (truncateMode === "none") return normalized
355
+ const limit = typeof maxLen === "number" ? maxLen : mode === "compact" ? 240 : 1200
356
+ if (normalized.length <= limit) return normalized
357
+ if (truncateMode === "sentence") {
358
+ const slice = normalized.slice(0, limit)
359
+ const lastPeriod = Math.max(slice.lastIndexOf("."), slice.lastIndexOf("!"), slice.lastIndexOf("?"))
360
+ if (lastPeriod > 40) {
361
+ return `${slice.slice(0, lastPeriod + 1)}`
362
+ }
363
+ }
364
+ if (truncateMode === "word") {
365
+ const slice = normalized.slice(0, limit)
366
+ const lastSpace = slice.lastIndexOf(" ")
367
+ if (lastSpace > 40) {
368
+ return `${slice.slice(0, lastSpace)}...`
369
+ }
370
+ }
371
+ return `${normalized.slice(0, Math.max(0, limit - 3))}...`
372
+ }
373
+
374
+ function formatJsdocEntry(name, doc, options) {
375
+ const mode = options.mode || "compact"
376
+ const truncateMode = options.truncate || "word"
377
+ const maxLen = options.maxLen
378
+ const sections = options.sections && options.sections.length > 0 ? options.sections : ["summary", "tags"]
379
+ const includeTags = options.tags?.include || null
380
+ const excludeTags = options.tags?.exclude || null
381
+
382
+ const summary = truncateSummary(doc.summary || "", mode, maxLen, truncateMode)
383
+ const tags = doc.tags || {}
384
+ const tagLines = []
385
+
386
+ const addTag = (tagName, values) => {
387
+ if (!values || values.length === 0) {
388
+ tagLines.push(`@${tagName}`)
389
+ return
390
+ }
391
+ for (const value of values) {
392
+ tagLines.push(value ? `@${tagName} ${value}` : `@${tagName}`)
393
+ }
394
+ }
395
+
396
+ const wantParams = sections.includes("params")
397
+ const wantReturns = sections.includes("returns")
398
+ const wantTags = sections.includes("tags")
399
+
400
+ if (wantParams) {
401
+ if (tags.param) addTag("param", tags.param)
402
+ }
403
+ if (wantReturns) {
404
+ if (tags.returns) addTag("returns", tags.returns)
405
+ if (tags.return) addTag("return", tags.return)
406
+ }
407
+ if (wantTags) {
408
+ for (const [tagName, values] of Object.entries(tags)) {
409
+ if (tagName === "param" || tagName === "returns" || tagName === "return") continue
410
+ if (includeTags && !includeTags.includes(tagName)) continue
411
+ if (excludeTags && excludeTags.includes(tagName)) continue
412
+ if (mode === "compact" && !includeTags && !excludeTags) {
413
+ if (!["deprecated", "since", "experimental"].includes(tagName)) continue
414
+ }
415
+ addTag(tagName, values)
416
+ }
417
+ }
418
+
419
+ const parts = []
420
+ if (sections.includes("summary") && summary) {
421
+ parts.push(summary)
422
+ }
423
+ if (tagLines.length > 0) {
424
+ parts.push(tagLines.join("; "))
425
+ }
426
+
427
+ return `${name}: ${parts.join(" | ")}`.trim()
428
+ }
429
+
430
+ function filterTypeInfo(typeInfo, filter, kindFilter) {
431
+ if (!typeInfo) return null
432
+ const includeName = (name) => !filter || name.toLowerCase().includes(filter)
433
+ const allow = (kind) => !kindFilter || kindFilter.length === 0 || kindFilter.includes(kind)
434
+ const includeExtras = !kindFilter || kindFilter.length === 0
435
+
436
+ const filtered = {
437
+ functions: {},
438
+ interfaces: {},
439
+ types: {},
440
+ classes: {},
441
+ enums: {},
442
+ namespaces: {},
443
+ defaults: [],
444
+ jsdoc: {},
445
+ }
446
+
447
+ if (allow("function")) {
448
+ for (const [name, info] of Object.entries(typeInfo.functions)) {
449
+ if (includeName(name)) filtered.functions[name] = info
450
+ }
451
+ }
452
+
453
+ if (allow("interface")) {
454
+ for (const [name, props] of Object.entries(typeInfo.interfaces)) {
455
+ if (includeName(name)) filtered.interfaces[name] = props
456
+ }
457
+ }
458
+
459
+ if (allow("type")) {
460
+ for (const [name, def] of Object.entries(typeInfo.types)) {
461
+ if (includeName(name)) filtered.types[name] = def
462
+ }
463
+ }
464
+
465
+ if (allow("class")) {
466
+ for (const [name, info] of Object.entries(typeInfo.classes)) {
467
+ if (includeName(name)) filtered.classes[name] = info
468
+ }
469
+ }
470
+
471
+ if (includeExtras) {
472
+ for (const [name, members] of Object.entries(typeInfo.enums || {})) {
473
+ if (includeName(name)) filtered.enums[name] = members
474
+ }
475
+ for (const [name, value] of Object.entries(typeInfo.namespaces || {})) {
476
+ if (includeName(name)) filtered.namespaces[name] = value
477
+ }
478
+ filtered.defaults = (typeInfo.defaults || []).filter((value) => includeName("default") || includeName(value))
479
+ }
480
+
481
+ if (typeInfo.jsdoc) {
482
+ for (const [name, doc] of Object.entries(typeInfo.jsdoc)) {
483
+ if (includeName(name)) filtered.jsdoc[name] = doc
484
+ }
485
+ }
486
+
487
+ return filtered
488
+ }
489
+
490
+ // Helper function to inspect object properties recursively
491
+ function inspectObject(obj, currentDepth = 0, maxDepth = 1, indent = " ") {
492
+ if (currentDepth >= maxDepth || obj === null || obj === undefined) {
493
+ return []
494
+ }
495
+
496
+ const lines = []
497
+ try {
498
+ const descriptors = Object.getOwnPropertyDescriptors(obj)
499
+ const keys = Object.keys(descriptors).slice(0, 10) // Limit to first 10 properties
500
+ for (const key of keys) {
501
+ try {
502
+ const descriptor = descriptors[key]
503
+ if (descriptor.get || descriptor.set) {
504
+ lines.push(`${indent.repeat(currentDepth + 1)}${key}: <getter>`)
505
+ continue
506
+ }
507
+ const value = descriptor.value
508
+ const type = typeof value
509
+ const prefix = indent.repeat(currentDepth + 1)
510
+
511
+ if (type === "function") {
512
+ const paramCount = value.length
513
+ lines.push(`${prefix}${key}(${paramCount} param${paramCount !== 1 ? "s" : ""})`)
514
+ } else if (type === "object" && value !== null) {
515
+ lines.push(`${prefix}${key}: {object}`)
516
+ if (currentDepth + 1 < maxDepth) {
517
+ lines.push(...inspectObject(value, currentDepth + 1, maxDepth, indent))
518
+ }
519
+ } else {
520
+ const valStr = type === "string" ? `"${String(value).substring(0, 30)}"` : String(value).substring(0, 30)
521
+ lines.push(`${prefix}${key}: ${valStr}`)
522
+ }
523
+ } catch (e) {
524
+ // Skip properties that throw on access
525
+ }
526
+ }
527
+ const totalKeys = Object.keys(descriptors).length
528
+ if (totalKeys > 10) {
529
+ lines.push(`${indent.repeat(currentDepth + 1)}... and ${totalKeys - 10} more`)
530
+ }
531
+ } catch (e) {
532
+ // Skip if object is not enumerable
533
+ }
534
+ return lines
535
+ }
536
+
537
+ export async function runInspect(options) {
538
+ const collect = !options?.write && !options?.writeError
539
+ const output = collect ? [] : null
540
+ const write = options?.write
541
+ const writeError = options?.writeError ?? options?.write
542
+
543
+ const log = (line = "") => {
544
+ if (collect) output.push(String(line))
545
+ else if (write) write(String(line))
546
+ }
547
+ const logErr = (line = "") => {
548
+ if (collect) output.push(String(line))
549
+ else if (writeError) writeError(String(line))
550
+ }
551
+
552
+ const target = options?.target
553
+ const filter = options?.filter ? options.filter.toLowerCase() : null
554
+ const showTypes = Boolean(options?.showTypes)
555
+ const jsdocModeRaw = options?.jsdoc ? String(options.jsdoc).toLowerCase() : null
556
+ const jsdocQuery = options?.jsdocQuery || null
557
+ const jsdocOutputRaw = options?.jsdocOutput ? String(options.jsdocOutput).toLowerCase() : null
558
+ const jsdocOutput = jsdocOutputRaw || (jsdocQuery ? "section" : "off")
559
+ const wantJsdoc = jsdocOutput !== "off"
560
+ const jsdocMode =
561
+ showTypes || wantJsdoc ? (jsdocQuery?.mode || jsdocModeRaw || "compact") : "off"
562
+ const kindFilter = Array.isArray(options?.kind)
563
+ ? options.kind.map((k) => String(k).trim().toLowerCase())
564
+ : null
565
+ let depth = typeof options?.depth === "number" ? options.depth : 1
566
+ if (isNaN(depth) || depth < 0 || depth > 5) depth = 1
567
+
568
+ if (!target) {
569
+ logErr(
570
+ "Uso: node inspect.mjs <pacote> [filtro] [--filter VALUE] [--types] [--jsdoc off|compact|full] [--jsdoc-output off|section|inline|only] [--jsdoc-symbol NAME|glob|/re/] [--jsdoc-sections summary,params,returns,tags] [--jsdoc-tags t1,t2] [--jsdoc-tags-exclude t1,t2] [--jsdoc-truncate none|sentence|word] [--jsdoc-max-len N] [--kind function,class,...] [--depth N] [--resolve-from DIR]",
571
+ )
572
+ return collect ? output.join("\n") : ""
573
+ }
574
+
575
+ const baseCwd = options?.cwd
576
+ const resolveFrom = options?.resolveFrom
577
+ ? path.resolve(baseCwd || process.cwd(), options.resolveFrom)
578
+ : baseCwd
579
+ if (baseCwd) {
580
+ try {
581
+ process.chdir(baseCwd)
582
+ } catch (e) {}
583
+ }
584
+
585
+ const resolution = await resolveTargetModule(target, baseCwd, resolveFrom)
586
+ const resolveCwd = resolution.resolveCwd || baseCwd
587
+ const require = createRequire(resolveCwd ? path.join(resolveCwd, "noop.js") : import.meta.url)
588
+
589
+ const flags = []
590
+ if (filter) flags.push(`Filtro: "${filter}"`)
591
+ if (kindFilter) flags.push(`Kind: ${kindFilter.join(",")}`)
592
+ if (showTypes || wantJsdoc) flags.push("Type Analysis")
593
+ if (jsdocOutput !== "off") flags.push(`JSDoc: ${jsdocMode}`)
594
+ if (depth > 1) flags.push(`Depth: ${depth}`)
595
+
596
+ const flagsStr = flags.length > 0 ? ` (${flags.join(" | ")})` : ""
597
+ log(`🔍 Target: ${target}${flagsStr}`)
598
+
599
+ try {
600
+ if (!resolution.resolved) {
601
+ logErr(`\n❌ Erro: Não foi possível resolver '${target}'`)
602
+ logErr(`ResolveFrom: ${resolveFrom || baseCwd || "unknown"}`)
603
+ logErr(`Certifique-se que '${target}' está instalado e é um caminho válido.`)
604
+ return collect ? output.join("\n") : ""
605
+ }
606
+
607
+ const basePkg = getPackageName(target)
608
+ const subpath = getPackageSubpath(target)
609
+ const pkgInfo = basePkg ? resolvePackageInfo(basePkg, require) : null
610
+ const pkg = pkgInfo?.pkg
611
+ const pkgDir = pkgInfo?.pkgDir
612
+
613
+ log(`\n🧭 Resolution:`)
614
+ log(` ResolveFrom: ${resolveFrom || baseCwd || "unknown"}`)
615
+ log(` Entrypoint: ${resolution.resolved}`)
616
+ if (resolution.resolver) {
617
+ log(` Resolver: ${resolution.resolver}`)
618
+ }
619
+ if (pkgDir) {
620
+ log(` PackageRoot: ${pkgDir}`)
621
+ }
622
+
623
+ let dtsPath
624
+ let typesFile
625
+ let typesSource
626
+
627
+ if (pkg) {
628
+ log(`\n📄 Package Info:`)
629
+ log(` Name: ${pkg.name || basePkg}`)
630
+ log(` Version: ${pkg.version || "Unknown"}`)
631
+ if (pkg.description) {
632
+ log(` Description: ${pkg.description}`)
633
+ }
634
+ if (pkg.license) {
635
+ log(` License: ${pkg.license}`)
636
+ }
637
+
638
+ const typesResolution = resolveTypesFile(pkg, pkgDir, subpath)
639
+ typesFile = typesResolution.typesFile
640
+ dtsPath = typesResolution.dtsPath
641
+ typesSource = typesResolution.source
642
+
643
+ if (typesFile) {
644
+ const sourceLabel = typesSource ? ` (${typesSource})` : ""
645
+ const existsLabel = dtsPath && fs.existsSync(dtsPath) ? "" : " (missing)"
646
+ log(` Types: ${typesFile}${sourceLabel}${existsLabel}`)
647
+ } else {
648
+ log(` Types: Not found`)
649
+ }
650
+
651
+ // === Mostrar subpath exports ===
652
+ if (pkg.exports && typeof pkg.exports === "object") {
653
+ const exportEntries = Object.entries(pkg.exports)
654
+ if (exportEntries.length > 0) {
655
+ log(`\n🚪 Subpath Exports (${exportEntries.length} available):`)
656
+ for (const [pathKey, value] of exportEntries.slice(0, 10)) {
657
+ if (typeof value === "string") {
658
+ log(` ${pathKey} → ${value}`)
659
+ } else if (value && typeof value === "object") {
660
+ const targets = Object.keys(value).join(", ")
661
+ log(` ${pathKey} → { ${targets} }`)
662
+ }
663
+ }
664
+ if (exportEntries.length > 10) {
665
+ log(` ... and ${exportEntries.length - 10} more`)
666
+ }
667
+ }
668
+ }
669
+ }
670
+
671
+ let typeInfoRaw = null
672
+ if ((showTypes || wantJsdoc) && dtsPath && fs.existsSync(dtsPath)) {
673
+ typeInfoRaw = parseDtsFile(dtsPath, null)
674
+ }
675
+
676
+ const { module: moduleNamespace } = await loadModuleExports(resolution.resolved, require, pkg)
677
+ const moduleDescriptors = Object.getOwnPropertyDescriptors(moduleNamespace)
678
+ const allExports = Object.keys(moduleDescriptors)
679
+
680
+ // Lógica de Filtro
681
+ let finalList = allExports
682
+ if (filter) {
683
+ finalList = allExports.filter((key) => key.toLowerCase().includes(filter))
684
+ }
685
+
686
+ // Se a lista for muito grande e não tiver filtro, avisa e corta
687
+ const LIMIT = 100
688
+ if (!filter && finalList.length > LIMIT) {
689
+ log(`\n⚠️ Módulo exporta ${finalList.length} itens. Mostrando os primeiros ${LIMIT}...`)
690
+ log(`DICA: Use o parâmetro 'filter' para encontrar o que procura.`)
691
+ finalList = finalList.slice(0, LIMIT)
692
+ }
693
+
694
+ // === MELHORIA 2: Categorizar exports por tipo ===
695
+ const categorized = {
696
+ functions: [],
697
+ classes: [],
698
+ objects: [],
699
+ primitives: [],
700
+ constants: [],
701
+ }
702
+
703
+ for (const key of finalList) {
704
+ const descriptor = moduleDescriptors[key]
705
+ if (!descriptor) continue
706
+ if (descriptor.get || descriptor.set) {
707
+ categorized.objects.push(key)
708
+ continue
709
+ }
710
+ const value = descriptor.value
711
+ const type = typeof value
712
+
713
+ if (type === "function") {
714
+ // Distinguir class vs function
715
+ if (isProbablyClass(value)) {
716
+ categorized.classes.push(key)
717
+ } else {
718
+ categorized.functions.push(key)
719
+ }
720
+ } else if (type === "object" && value !== null) {
721
+ categorized.objects.push(key)
722
+ } else if (type === "string" || type === "number" || type === "boolean") {
723
+ categorized.constants.push(key)
724
+ } else {
725
+ categorized.primitives.push(key)
726
+ }
727
+ }
728
+
729
+ // Prefer class from type info when available
730
+ if (typeInfoRaw && Object.keys(typeInfoRaw.classes).length > 0) {
731
+ const classNames = new Set(Object.keys(typeInfoRaw.classes))
732
+ categorized.functions = categorized.functions.filter((name) => {
733
+ if (classNames.has(name)) {
734
+ categorized.classes.push(name)
735
+ return false
736
+ }
737
+ return true
738
+ })
739
+ }
740
+
741
+ // Apply kind filter if specified
742
+ if (kindFilter && kindFilter.length > 0) {
743
+ const kindMap = {
744
+ function: "functions",
745
+ class: "classes",
746
+ object: "objects",
747
+ constant: "constants",
748
+ }
749
+
750
+ // Keep only the requested kinds
751
+ for (const [key, value] of Object.entries(categorized)) {
752
+ const shouldKeep = Object.entries(kindMap).some(([kind, catKey]) => kindFilter.includes(kind) && catKey === key)
753
+ if (!shouldKeep) {
754
+ categorized[key] = []
755
+ }
756
+ }
757
+
758
+ // Update finalList to only include filtered kinds
759
+ finalList = [...categorized.functions, ...categorized.classes, ...categorized.objects, ...categorized.constants]
760
+ }
761
+
762
+ // Mostrar exports categorizados
763
+ if (jsdocOutput !== "only") {
764
+ log(`\n🔑 Exports Encontrados (${finalList.length} total):`)
765
+
766
+ if (categorized.functions.length > 0) {
767
+ log(`\n 📘 Functions (${categorized.functions.length}):`)
768
+ log(` ${categorized.functions.join(", ")}`)
769
+ }
770
+
771
+ if (categorized.classes.length > 0) {
772
+ log(`\n 🏛️ Classes (${categorized.classes.length}):`)
773
+ log(` ${categorized.classes.join(", ")}`)
774
+ }
775
+
776
+ if (categorized.objects.length > 0) {
777
+ log(`\n 📦 Objects/Namespaces (${categorized.objects.length}):`)
778
+ log(` ${categorized.objects.join(", ")}`)
779
+
780
+ // If depth > 0, show object contents
781
+ if (depth > 0 && categorized.objects.length <= 10) {
782
+ log(`\n 📦 Object Contents (depth: ${depth}):`)
783
+ for (const objName of categorized.objects) {
784
+ log(`\n ${objName}:`)
785
+ const descriptor = moduleDescriptors[objName]
786
+ if (!descriptor || descriptor.get || descriptor.set) {
787
+ log(` ${objName}: <getter>`)
788
+ continue
789
+ }
790
+ const objValue = descriptor.value
791
+ const lines = inspectObject(objValue, 0, depth, " ")
792
+ lines.forEach((line) => log(` ${line}`))
793
+ }
794
+ } else if (depth > 0 && categorized.objects.length > 10) {
795
+ log(`\n ℹ️ Too many objects to show contents. Use 'filter' to narrow down.`)
796
+ }
797
+ }
798
+
799
+ if (categorized.constants.length > 0) {
800
+ log(`\n 🔢 Constants (${categorized.constants.length}):`)
801
+ log(` ${categorized.constants.join(", ")}`)
802
+ }
803
+
804
+ if (finalList.length === 0) {
805
+ log("Nenhum export corresponde ao filtro.")
806
+ }
807
+ }
808
+
809
+ // === MELHORIA 5: Mostrar assinaturas de funções ===
810
+ if (jsdocOutput !== "only") {
811
+ if (!showTypes && categorized.functions.length > 0 && categorized.functions.length <= 15) {
812
+ log(`\n✍️ Function Signatures:`)
813
+ for (const fname of categorized.functions) {
814
+ const descriptor = moduleDescriptors[fname]
815
+ const fn = descriptor?.value
816
+ if (typeof fn === "function") {
817
+ const paramCount = fn.length
818
+ const params = paramCount === 0 ? "" : paramCount === 1 ? "1 param" : `${paramCount} params`
819
+ log(` ${fname}(${params})`)
820
+ }
821
+ }
822
+ }
823
+
824
+ // Default export handling
825
+ const defaultDescriptor = moduleDescriptors.default
826
+ if (defaultDescriptor && (!filter || "default".includes(filter))) {
827
+ const defaultValue = defaultDescriptor.get || defaultDescriptor.set ? undefined : defaultDescriptor.value
828
+ const defaultType = typeof defaultValue
829
+ log(`\n📦 Default Export: ${defaultType}`)
830
+ if (defaultType === "function" && defaultValue && defaultValue.length !== undefined) {
831
+ log(` Parameters: ${defaultValue.length}`)
832
+ }
833
+ }
834
+ }
835
+
836
+ // === NEW: Parse .d.ts file if --types flag is present ===
837
+ if (showTypes || wantJsdoc) {
838
+ if (dtsPath && fs.existsSync(dtsPath)) {
839
+ if (jsdocOutput !== "only") {
840
+ log(`\n🔬 Type Definitions Analysis:`)
841
+ log(` Source: ${path.basename(dtsPath)}`)
842
+ }
843
+
844
+ const typeInfo = filterTypeInfo(typeInfoRaw, filter, kindFilter)
845
+
846
+ if (typeInfo) {
847
+ if (jsdocOutput !== "only") {
848
+ // Show function signatures with full type info
849
+ if (Object.keys(typeInfo.functions).length > 0) {
850
+ log(`\n 📘 Function Type Signatures:`)
851
+ for (const [name, info] of Object.entries(typeInfo.functions)) {
852
+ log(` ${name}(${info.params}): ${info.returnType}`)
853
+ if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
854
+ const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
855
+ mode: jsdocMode,
856
+ truncate: jsdocQuery?.truncate,
857
+ maxLen: jsdocQuery?.maxLen,
858
+ sections: jsdocQuery?.sections,
859
+ tags: jsdocQuery?.tags,
860
+ })
861
+ log(` ↳ ${entry}`)
862
+ }
863
+ }
864
+ }
865
+
866
+ // Show interfaces
867
+ if (Object.keys(typeInfo.interfaces).length > 0) {
868
+ log(`\n 📋 Interfaces:`)
869
+ for (const [name, props] of Object.entries(typeInfo.interfaces)) {
870
+ log(` interface ${name} {`)
871
+ props.forEach((prop) => log(` ${prop}`))
872
+ if (props.length === 5) {
873
+ log(` ... (truncated)`)
874
+ }
875
+ log(` }`)
876
+ if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
877
+ const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
878
+ mode: jsdocMode,
879
+ truncate: jsdocQuery?.truncate,
880
+ maxLen: jsdocQuery?.maxLen,
881
+ sections: jsdocQuery?.sections,
882
+ tags: jsdocQuery?.tags,
883
+ })
884
+ log(` ↳ ${entry}`)
885
+ }
886
+ }
887
+ }
888
+
889
+ // Show type aliases
890
+ if (Object.keys(typeInfo.types).length > 0) {
891
+ log(`\n 📝 Type Aliases:`)
892
+ for (const [name, definition] of Object.entries(typeInfo.types)) {
893
+ const shortDef = definition.length > 80 ? definition.substring(0, 80) + "..." : definition
894
+ log(` type ${name} = ${shortDef}`)
895
+ if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
896
+ const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
897
+ mode: jsdocMode,
898
+ truncate: jsdocQuery?.truncate,
899
+ maxLen: jsdocQuery?.maxLen,
900
+ sections: jsdocQuery?.sections,
901
+ tags: jsdocQuery?.tags,
902
+ })
903
+ log(` ↳ ${entry}`)
904
+ }
905
+ }
906
+ }
907
+
908
+ // Show class inheritance
909
+ if (Object.keys(typeInfo.classes).length > 0) {
910
+ log(`\n 🏛️ Class Definitions:`)
911
+ for (const [name, extendsClass] of Object.entries(typeInfo.classes)) {
912
+ const inheritance = extendsClass ? ` extends ${extendsClass}` : ""
913
+ log(` class ${name}${inheritance}`)
914
+ if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
915
+ const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
916
+ mode: jsdocMode,
917
+ truncate: jsdocQuery?.truncate,
918
+ maxLen: jsdocQuery?.maxLen,
919
+ sections: jsdocQuery?.sections,
920
+ tags: jsdocQuery?.tags,
921
+ })
922
+ log(` ↳ ${entry}`)
923
+ }
924
+ }
925
+ }
926
+
927
+ if (Object.keys(typeInfo.enums).length > 0) {
928
+ log(`\n 🧾 Enums:`)
929
+ for (const [name, members] of Object.entries(typeInfo.enums)) {
930
+ const preview = members.length > 0 ? ` = [${members.join(", ")}]` : ""
931
+ log(` enum ${name}${preview}`)
932
+ if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
933
+ const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
934
+ mode: jsdocMode,
935
+ truncate: jsdocQuery?.truncate,
936
+ maxLen: jsdocQuery?.maxLen,
937
+ sections: jsdocQuery?.sections,
938
+ tags: jsdocQuery?.tags,
939
+ })
940
+ log(` ↳ ${entry}`)
941
+ }
942
+ }
943
+ }
944
+
945
+ if (Object.keys(typeInfo.namespaces).length > 0) {
946
+ log(`\n 📦 Namespaces:`)
947
+ for (const name of Object.keys(typeInfo.namespaces)) {
948
+ log(` namespace ${name}`)
949
+ if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
950
+ const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
951
+ mode: jsdocMode,
952
+ truncate: jsdocQuery?.truncate,
953
+ maxLen: jsdocQuery?.maxLen,
954
+ sections: jsdocQuery?.sections,
955
+ tags: jsdocQuery?.tags,
956
+ })
957
+ log(` ↳ ${entry}`)
958
+ }
959
+ }
960
+ }
961
+
962
+ if (typeInfo.defaults.length > 0) {
963
+ log(`\n 📦 Default Exports:`)
964
+ typeInfo.defaults.slice(0, 5).forEach((value) => log(` default = ${value}`))
965
+ }
966
+ }
967
+
968
+ if (wantJsdoc && jsdocMode !== "off" && typeInfo.jsdoc && Object.keys(typeInfo.jsdoc).length > 0) {
969
+ const symbolMatcher = buildSymbolMatcher(jsdocQuery?.symbols, filter)
970
+ const entries = Object.entries(typeInfo.jsdoc)
971
+ .filter(([name]) => (symbolMatcher ? symbolMatcher(name) : true))
972
+ .slice(0, 50)
973
+
974
+ if (entries.length > 0) {
975
+ log(`\n 📚 JSDoc:`)
976
+ for (const [name, doc] of entries) {
977
+ const entry = formatJsdocEntry(name, doc, {
978
+ mode: jsdocMode,
979
+ truncate: jsdocQuery?.truncate,
980
+ maxLen: jsdocQuery?.maxLen,
981
+ sections: jsdocQuery?.sections,
982
+ tags: jsdocQuery?.tags,
983
+ })
984
+ log(` ${entry}`)
985
+ }
986
+ }
987
+ }
988
+
989
+ if (jsdocOutput !== "only") {
990
+ const typeExportNames = new Set([
991
+ ...Object.keys(typeInfo.functions),
992
+ ...Object.keys(typeInfo.interfaces),
993
+ ...Object.keys(typeInfo.types),
994
+ ...Object.keys(typeInfo.classes),
995
+ ...Object.keys(typeInfo.enums),
996
+ ...Object.keys(typeInfo.namespaces),
997
+ ])
998
+ const runtimeNames = new Set(allExports)
999
+ const runtimeOnly = [...runtimeNames].filter((name) => !typeExportNames.has(name))
1000
+ const typesOnly = [...typeExportNames].filter((name) => !runtimeNames.has(name))
1001
+
1002
+ if (runtimeOnly.length > 0 || typesOnly.length > 0) {
1003
+ log(`\n ⚖️ Runtime/Types Mismatch:`)
1004
+ if (runtimeOnly.length > 0) {
1005
+ log(` Runtime only: ${runtimeOnly.slice(0, 10).join(", ")}`)
1006
+ }
1007
+ if (typesOnly.length > 0) {
1008
+ log(` Types only: ${typesOnly.slice(0, 10).join(", ")}`)
1009
+ }
1010
+ }
1011
+
1012
+ if (
1013
+ Object.keys(typeInfo.functions).length === 0 &&
1014
+ Object.keys(typeInfo.interfaces).length === 0 &&
1015
+ Object.keys(typeInfo.types).length === 0 &&
1016
+ Object.keys(typeInfo.classes).length === 0 &&
1017
+ Object.keys(typeInfo.enums).length === 0 &&
1018
+ Object.keys(typeInfo.namespaces).length === 0 &&
1019
+ typeInfo.defaults.length === 0
1020
+ ) {
1021
+ log(` ⚠️ No type definitions found for filtered exports`)
1022
+ }
1023
+ }
1024
+ } else {
1025
+ if (jsdocOutput !== "only") {
1026
+ log(` ⚠️ Could not parse type definitions`)
1027
+ }
1028
+ }
1029
+ } else {
1030
+ if (jsdocOutput !== "only") {
1031
+ log(`\n⚠️ Type definitions not available for this package`)
1032
+ }
1033
+ }
1034
+ }
1035
+ } catch (e) {
1036
+ logErr(`\n❌ Erro: ${e.message}`)
1037
+ logErr(`Certifique-se que '${target}' está instalado e é um caminho válido.`)
1038
+ }
1039
+
1040
+ return collect ? output.join("\n") : ""
1041
+ }