@deplens/mcp 0.1.7 → 0.1.8

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.
@@ -1,58 +1,62 @@
1
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"
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
+ import { analyzePackageSource } from "./parse-source.mjs";
10
+ import { downloadVersion } from "./version-resolver.mjs";
9
11
 
10
12
  function getPackageName(target) {
11
- if (!target) return target
13
+ if (!target) return target;
12
14
  if (target.startsWith("@")) {
13
- const parts = target.split("/")
14
- return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : target
15
+ const parts = target.split("/");
16
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : target;
15
17
  }
16
- return target.split("/")[0]
18
+ return target.split("/")[0];
17
19
  }
18
20
 
19
21
  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
22
+ if (!target) return null;
23
+ const base = getPackageName(target);
24
+ if (!base) return null;
25
+ if (target === base) return null;
26
+ const prefix = `${base}/`;
27
+ return target.startsWith(prefix) ? target.slice(prefix.length) : null;
26
28
  }
27
29
 
28
30
  async function findWorkspaceRoot(startDir) {
29
- if (!startDir) return null
30
- let dir = path.resolve(startDir)
31
+ if (!startDir) return null;
32
+ let dir = path.resolve(startDir);
31
33
  while (true) {
32
- const pkgPath = path.join(dir, "package.json")
34
+ const pkgPath = path.join(dir, "package.json");
33
35
  if (fs.existsSync(pkgPath)) {
34
36
  try {
35
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
36
- if (pkg?.workspaces) return { dir, pkg }
37
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
38
+ if (pkg?.workspaces) return { dir, pkg };
37
39
  } catch (e) {}
38
40
  }
39
- const parent = path.dirname(dir)
40
- if (parent === dir) break
41
- dir = parent
41
+ const parent = path.dirname(dir);
42
+ if (parent === dir) break;
43
+ dir = parent;
42
44
  }
43
- return null
45
+ return null;
44
46
  }
45
47
 
46
48
  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)
49
+ const patterns = Array.isArray(workspaces)
50
+ ? workspaces
51
+ : workspaces?.packages;
52
+ if (!Array.isArray(patterns) || patterns.length === 0) return [];
53
+ const dirs = [];
54
+ const target = getPackageName(targetPackage);
51
55
 
52
56
  for (const pattern of patterns) {
53
- if (!pattern) continue
54
- const normalized = String(pattern).replace(/\\/g, "/").replace(/\/?$/, "/")
55
- const globPattern = `${normalized}package.json`
57
+ if (!pattern) continue;
58
+ const normalized = String(pattern).replace(/\\/g, "/").replace(/\/?$/, "/");
59
+ const globPattern = `${normalized}package.json`;
56
60
  const matches =
57
61
  typeof Bun !== "undefined" && Bun.Glob
58
62
  ? await Array.fromAsync(
@@ -69,257 +73,373 @@ async function listWorkspacePackageDirs(rootDir, workspaces, targetPackage) {
69
73
  onlyFiles: true,
70
74
  dot: false,
71
75
  followSymbolicLinks: false,
72
- })
76
+ });
73
77
  for (const match of matches) {
74
- const pkgPath = path.join(rootDir, match)
78
+ const pkgPath = path.join(rootDir, match);
75
79
  try {
76
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
80
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
77
81
  const depTables = [
78
82
  pkg?.dependencies,
79
83
  pkg?.devDependencies,
80
84
  pkg?.peerDependencies,
81
85
  pkg?.optionalDependencies,
82
- ]
86
+ ];
83
87
  const hasTarget =
84
88
  Boolean(target) &&
85
- depTables.some((deps) => deps && Object.prototype.hasOwnProperty.call(deps, target))
89
+ depTables.some(
90
+ (deps) =>
91
+ deps && Object.prototype.hasOwnProperty.call(deps, target),
92
+ );
86
93
  if (hasTarget) {
87
- dirs.push(path.dirname(pkgPath))
94
+ dirs.push(path.dirname(pkgPath));
88
95
  }
89
96
  } catch (e) {}
90
97
  }
91
98
  }
92
99
 
93
- return dirs
100
+ return dirs;
94
101
  }
95
102
 
96
103
  async function resolveTargetModule(target, cwd, resolveFrom) {
97
- const baseDir = resolveFrom || cwd
98
- if (!baseDir) return { resolved: null, resolveCwd: baseDir, resolver: null }
104
+ const baseDir = resolveFrom || cwd;
105
+ if (!baseDir) return { resolved: null, resolveCwd: baseDir, resolver: null };
99
106
 
100
107
  const tryResolve = async (dir) => {
101
- if (!dir) return null
108
+ if (!dir) return null;
102
109
  if (typeof Bun !== "undefined" && Bun.resolve) {
103
110
  try {
104
- const resolved = await Bun.resolve(target, dir)
105
- return { resolved, resolver: "bun", resolveCwd: dir }
111
+ const resolved = await Bun.resolve(target, dir);
112
+ return { resolved, resolver: "bun", resolveCwd: dir };
106
113
  } catch (e) {}
107
114
  }
108
115
  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 }
116
+ const parentUrl = pathToFileURL(path.join(dir, "noop.js")).href;
117
+ const resolvedUrl = await importMetaResolve(target, parentUrl);
118
+ const resolvedPath = resolvedUrl.startsWith("file://")
119
+ ? fileURLToPath(resolvedUrl)
120
+ : resolvedUrl;
121
+ return {
122
+ resolved: resolvedPath,
123
+ resolver: "import-meta-resolve",
124
+ resolveCwd: dir,
125
+ };
113
126
  } catch (e) {}
114
127
  try {
115
- const req = createRequire(path.join(dir, "noop.js"))
116
- const resolved = req.resolve(target)
117
- return { resolved, resolver: "require", resolveCwd: dir }
128
+ const req = createRequire(path.join(dir, "noop.js"));
129
+ const resolved = req.resolve(target);
130
+ return { resolved, resolver: "require", resolveCwd: dir };
118
131
  } catch (e) {
119
- return null
132
+ return null;
120
133
  }
121
- }
134
+ };
122
135
 
123
- const direct = await tryResolve(baseDir)
124
- if (direct) return direct
136
+ const direct = await tryResolve(baseDir);
137
+ if (direct) return direct;
125
138
 
126
- const workspace = await findWorkspaceRoot(baseDir)
139
+ const workspace = await findWorkspaceRoot(baseDir);
127
140
  if (workspace?.pkg?.workspaces) {
128
- const dirs = await listWorkspacePackageDirs(workspace.dir, workspace.pkg.workspaces, target)
141
+ const dirs = await listWorkspacePackageDirs(
142
+ workspace.dir,
143
+ workspace.pkg.workspaces,
144
+ target,
145
+ );
129
146
  for (const dir of dirs) {
130
- const resolved = await tryResolve(dir)
131
- if (resolved) return resolved
147
+ const resolved = await tryResolve(dir);
148
+ if (resolved) return resolved;
132
149
  }
133
150
  }
134
151
 
135
- return { resolved: null, resolveCwd: baseDir, resolver: null }
152
+ return { resolved: null, resolveCwd: baseDir, resolver: null };
136
153
  }
137
154
 
138
155
  function findPackageJsonFromPath(startPath) {
139
- if (!startPath) return null
140
- let dir = path.dirname(startPath)
156
+ if (!startPath) return null;
157
+ let dir = path.dirname(startPath);
141
158
  for (let i = 0; i < 10; i++) {
142
- const candidate = path.join(dir, "package.json")
143
- if (fs.existsSync(candidate)) return candidate
144
- const parent = path.dirname(dir)
145
- if (parent === dir) break
146
- dir = parent
159
+ const candidate = path.join(dir, "package.json");
160
+ if (fs.existsSync(candidate)) return candidate;
161
+ const parent = path.dirname(dir);
162
+ if (parent === dir) break;
163
+ dir = parent;
147
164
  }
148
- return null
165
+ return null;
149
166
  }
150
167
 
151
168
  function findPackageJsonInNodeModules(startDir, basePkg) {
152
- if (!startDir || !basePkg) return null
153
- const segments = basePkg.split("/")
154
- let dir = path.resolve(startDir)
169
+ if (!startDir || !basePkg) return null;
170
+ const segments = basePkg.split("/");
171
+ let dir = path.resolve(startDir);
155
172
  while (true) {
156
- const candidate = path.join(dir, "node_modules", ...segments, "package.json")
157
- if (fs.existsSync(candidate)) return candidate
158
- const parent = path.dirname(dir)
159
- if (parent === dir) break
160
- dir = parent
173
+ const candidate = path.join(
174
+ dir,
175
+ "node_modules",
176
+ ...segments,
177
+ "package.json",
178
+ );
179
+ if (fs.existsSync(candidate)) return candidate;
180
+ const parent = path.dirname(dir);
181
+ if (parent === dir) break;
182
+ dir = parent;
161
183
  }
162
- return null
184
+ return null;
163
185
  }
164
186
 
165
187
  function resolvePackageInfo(basePkg, require, resolveFrom, resolvedPath) {
166
- let pkgPath
167
- let pkgDir
188
+ let pkgPath;
189
+ let pkgDir;
168
190
  try {
169
- pkgPath = require.resolve(`${basePkg}/package.json`)
170
- pkgDir = path.dirname(pkgPath)
191
+ pkgPath = require.resolve(`${basePkg}/package.json`);
192
+ pkgDir = path.dirname(pkgPath);
171
193
  } catch (e) {
172
194
  try {
173
- const mainPath = require.resolve(basePkg)
174
- let dir = path.dirname(mainPath)
195
+ const mainPath = require.resolve(basePkg);
196
+ let dir = path.dirname(mainPath);
175
197
  for (let i = 0; i < 5; i++) {
176
- const candidate = path.join(dir, "package.json")
198
+ const candidate = path.join(dir, "package.json");
177
199
  if (fs.existsSync(candidate)) {
178
- pkgPath = candidate
179
- pkgDir = dir
180
- break
200
+ pkgPath = candidate;
201
+ pkgDir = dir;
202
+ break;
181
203
  }
182
- dir = path.dirname(dir)
204
+ dir = path.dirname(dir);
183
205
  }
184
206
  } catch (err) {}
185
207
  }
186
208
 
187
209
  if (!pkgPath && resolveFrom && basePkg) {
188
- const fallback = findPackageJsonInNodeModules(resolveFrom, basePkg)
210
+ const fallback = findPackageJsonInNodeModules(resolveFrom, basePkg);
189
211
  if (fallback) {
190
- pkgPath = fallback
191
- pkgDir = path.dirname(fallback)
212
+ pkgPath = fallback;
213
+ pkgDir = path.dirname(fallback);
192
214
  }
193
215
  }
194
216
 
195
217
  if (!pkgPath && resolvedPath) {
196
- const fallback = findPackageJsonFromPath(resolvedPath)
218
+ const fallback = findPackageJsonFromPath(resolvedPath);
197
219
  if (fallback) {
198
- pkgPath = fallback
199
- pkgDir = path.dirname(fallback)
220
+ pkgPath = fallback;
221
+ pkgDir = path.dirname(fallback);
200
222
  }
201
223
  }
202
224
 
203
225
  if (resolveFrom && basePkg) {
204
- const rootCandidate = findPackageJsonInNodeModules(resolveFrom, basePkg)
226
+ const rootCandidate = findPackageJsonInNodeModules(resolveFrom, basePkg);
205
227
  if (rootCandidate && rootCandidate !== pkgPath) {
206
- pkgPath = rootCandidate
207
- pkgDir = path.dirname(rootCandidate)
228
+ pkgPath = rootCandidate;
229
+ pkgDir = path.dirname(rootCandidate);
208
230
  }
209
231
  }
210
232
 
211
- if (!pkgPath || !fs.existsSync(pkgPath)) return null
212
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"))
213
- return { pkg, pkgPath, pkgDir }
233
+ if (!pkgPath || !fs.existsSync(pkgPath)) return null;
234
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
235
+ return { pkg, pkgPath, pkgDir };
214
236
  }
215
237
 
216
238
  function resolveTypesFromExportEntry(entry) {
217
- if (!entry) return null
218
- if (typeof entry === "string") return entry
239
+ if (!entry) return null;
240
+ if (typeof entry === "string") return entry;
219
241
  if (typeof entry === "object") {
220
- if (typeof entry.types === "string") return entry.types
242
+ // First try explicit types field
243
+ if (typeof entry.types === "string") return entry.types;
244
+
245
+ // Then try import/require/default but look for their types too
221
246
  for (const key of ["import", "require", "default"]) {
222
- if (typeof entry[key] === "string") return entry[key]
247
+ const value = entry[key];
248
+ if (typeof value === "string") {
249
+ // Try to find corresponding .d.ts
250
+ return value;
251
+ } else if (
252
+ value &&
253
+ typeof value === "object" &&
254
+ typeof value.types === "string"
255
+ ) {
256
+ return value.types;
257
+ }
223
258
  }
224
259
  }
225
- return null
260
+ return null;
226
261
  }
227
262
 
228
263
  function coerceTypesPath(typesPath) {
229
- if (!typesPath) return typesPath
230
- if (typesPath.endsWith(".d.ts") || typesPath.endsWith(".d.cts") || typesPath.endsWith(".d.mts")) {
231
- return typesPath
264
+ if (!typesPath) return typesPath;
265
+ if (
266
+ typesPath.endsWith(".d.ts") ||
267
+ typesPath.endsWith(".d.cts") ||
268
+ typesPath.endsWith(".d.mts")
269
+ ) {
270
+ return typesPath;
232
271
  }
233
- if (typesPath.endsWith(".mjs")) return typesPath.replace(/\.mjs$/, ".d.mts")
234
- if (typesPath.endsWith(".cjs")) return typesPath.replace(/\.cjs$/, ".d.cts")
235
- if (typesPath.endsWith(".js")) return typesPath.replace(/\.js$/, ".d.ts")
236
- return typesPath
272
+ if (typesPath.endsWith(".mjs")) return typesPath.replace(/\.mjs$/, ".d.mts");
273
+ if (typesPath.endsWith(".cjs")) return typesPath.replace(/\.cjs$/, ".d.cts");
274
+ if (typesPath.endsWith(".js")) return typesPath.replace(/\.js$/, ".d.ts");
275
+ return typesPath;
237
276
  }
238
277
 
239
- function resolveTypesFile(pkg, pkgDir, subpath) {
240
- if (!pkg || !pkgDir) return { typesFile: null, dtsPath: null, source: null }
241
- let typesFile = null
242
- let source = null
278
+ function tryResolveTypesPackage(basePkgName, require) {
279
+ if (!basePkgName || basePkgName.startsWith("@types/")) return null;
280
+
281
+ // Convert package name to @types format
282
+ // react -> @types/react
283
+ // @foo/bar -> @types/foo__bar
284
+ let typesPackageName;
285
+ if (basePkgName.startsWith("@")) {
286
+ const [scope, name] = basePkgName.slice(1).split("/");
287
+ typesPackageName = `@types/${scope}__${name}`;
288
+ } else {
289
+ typesPackageName = `@types/${basePkgName}`;
290
+ }
291
+
292
+ try {
293
+ // Try to resolve the @types package
294
+ const typesPkgJsonPath = require.resolve(
295
+ `${typesPackageName}/package.json`,
296
+ );
297
+ if (typesPkgJsonPath && fs.existsSync(typesPkgJsonPath)) {
298
+ const typesPkg = JSON.parse(fs.readFileSync(typesPkgJsonPath, "utf-8"));
299
+ const typesPkgDir = path.dirname(typesPkgJsonPath);
300
+
301
+ // Find the types file in @types package
302
+ const typesEntry = typesPkg.types || typesPkg.typings || "index.d.ts";
303
+ const typesFullPath = path.resolve(typesPkgDir, typesEntry);
304
+
305
+ if (fs.existsSync(typesFullPath)) {
306
+ return {
307
+ typesFile: typesEntry,
308
+ dtsPath: typesFullPath,
309
+ source: "@types",
310
+ pkgDir: typesPkgDir,
311
+ };
312
+ }
313
+ }
314
+ } catch (e) {
315
+ // @types package not found or not resolvable
316
+ }
317
+
318
+ return null;
319
+ }
320
+
321
+ function resolveTypesFile(pkg, pkgDir, subpath, basePkgName, require) {
322
+ if (!pkg || !pkgDir) return { typesFile: null, dtsPath: null, source: null };
323
+ let typesFile = null;
324
+ let source = null;
243
325
 
244
326
  if (pkg.exports) {
245
- let entry = null
327
+ let entry = null;
246
328
  if (subpath) {
247
- const key = subpath.startsWith(".") ? subpath : `./${subpath}`
329
+ const key = subpath.startsWith(".") ? subpath : `./${subpath}`;
248
330
  if (typeof pkg.exports === "object") {
249
- entry = pkg.exports[key]
331
+ entry = pkg.exports[key];
250
332
  }
251
333
  } else if (typeof pkg.exports === "string") {
252
- entry = pkg.exports
334
+ entry = pkg.exports;
253
335
  } else if (typeof pkg.exports === "object") {
254
- entry = pkg.exports["."] ?? pkg.exports["./"]
336
+ entry = pkg.exports["."] ?? pkg.exports["./"];
255
337
  }
256
338
 
257
- const typesFromExport = resolveTypesFromExportEntry(entry)
339
+ const typesFromExport = resolveTypesFromExportEntry(entry);
258
340
  if (typesFromExport) {
259
- typesFile = coerceTypesPath(typesFromExport)
260
- source = "exports"
341
+ typesFile = coerceTypesPath(typesFromExport);
342
+ source = "exports";
261
343
  }
262
344
  }
263
345
 
264
346
  if (!typesFile) {
265
- typesFile = pkg.types || pkg.typings || null
266
- if (typesFile) source = "package"
347
+ typesFile = pkg.types || pkg.typings || null;
348
+ if (typesFile) source = "package";
267
349
  }
268
350
 
269
351
  if (!typesFile) {
270
352
  const candidates = [
353
+ "index.d.ts",
354
+ "index.d.cts",
355
+ "index.d.mts",
271
356
  "dist/index.d.ts",
272
357
  "dist/index.d.cts",
273
358
  "dist/index.d.mts",
274
359
  "lib/index.d.ts",
275
360
  "lib/index.d.cts",
276
361
  "lib/index.d.mts",
277
- "index.d.ts",
278
- "index.d.cts",
279
- "index.d.mts",
280
362
  "types/index.d.ts",
281
- ]
363
+ "types/index.d.cts",
364
+ "types/index.d.mts",
365
+ "src/index.d.ts",
366
+ "dist/types/index.d.ts",
367
+ "build/index.d.ts",
368
+ ];
282
369
  for (const candidate of candidates) {
283
- const candidatePath = path.resolve(pkgDir, candidate)
370
+ const candidatePath = path.resolve(pkgDir, candidate);
284
371
  if (fs.existsSync(candidatePath)) {
285
- typesFile = candidate
286
- source = "fallback"
287
- break
372
+ typesFile = candidate;
373
+ source = "fallback";
374
+ break;
375
+ }
376
+ }
377
+ }
378
+
379
+ // Last resort: search for any .d.ts file in package root or common directories
380
+ if (!typesFile) {
381
+ const searchDirs = [".", "dist", "lib", "types", "build"];
382
+ for (const dir of searchDirs) {
383
+ const searchPath = path.resolve(pkgDir, dir);
384
+ if (!fs.existsSync(searchPath)) continue;
385
+
386
+ try {
387
+ const files = fs.readdirSync(searchPath);
388
+ const dtsFile = files.find(
389
+ (f) =>
390
+ f.endsWith(".d.ts") || f.endsWith(".d.mts") || f.endsWith(".d.cts"),
391
+ );
392
+ if (dtsFile) {
393
+ typesFile = path.join(dir === "." ? "" : dir, dtsFile);
394
+ source = "search";
395
+ break;
396
+ }
397
+ } catch (e) {
398
+ // Skip directories we can't read
288
399
  }
289
400
  }
290
401
  }
291
402
 
292
403
  if (!typesFile) {
293
- return { typesFile: null, dtsPath: null, source: null }
404
+ // Try @types/* package before giving up
405
+ if (basePkgName && require) {
406
+ const typesPackageResult = tryResolveTypesPackage(basePkgName, require);
407
+ if (typesPackageResult) {
408
+ return typesPackageResult;
409
+ }
410
+ }
411
+ return { typesFile: null, dtsPath: null, source: null };
294
412
  }
295
413
 
296
- const resolved = path.isAbsolute(typesFile) ? typesFile : path.resolve(pkgDir, typesFile)
297
- let dtsPath = resolved
414
+ const resolved = path.isAbsolute(typesFile)
415
+ ? typesFile
416
+ : path.resolve(pkgDir, typesFile);
417
+ let dtsPath = resolved;
298
418
  if (!fs.existsSync(dtsPath)) {
299
- const mapped = coerceTypesPath(dtsPath)
419
+ const mapped = coerceTypesPath(dtsPath);
300
420
  if (mapped !== dtsPath && fs.existsSync(mapped)) {
301
- dtsPath = mapped
421
+ dtsPath = mapped;
302
422
  } else {
303
- const replacements = [".d.ts", ".d.cts", ".d.mts"]
423
+ const replacements = [".d.ts", ".d.cts", ".d.mts"];
304
424
  for (const ext of replacements) {
305
- if (dtsPath.endsWith(ext)) continue
306
- const candidate = `${dtsPath}${ext}`
425
+ if (dtsPath.endsWith(ext)) continue;
426
+ const candidate = `${dtsPath}${ext}`;
307
427
  if (fs.existsSync(candidate)) {
308
- dtsPath = candidate
309
- break
428
+ dtsPath = candidate;
429
+ break;
310
430
  }
311
431
  }
312
432
  if (!fs.existsSync(dtsPath) && dtsPath.endsWith(".d.ts")) {
313
- const ctsPath = dtsPath.replace(".d.ts", ".d.cts")
314
- const mtsPath = dtsPath.replace(".d.ts", ".d.mts")
315
- if (fs.existsSync(ctsPath)) dtsPath = ctsPath
316
- else if (fs.existsSync(mtsPath)) dtsPath = mtsPath
433
+ const ctsPath = dtsPath.replace(".d.ts", ".d.cts");
434
+ const mtsPath = dtsPath.replace(".d.ts", ".d.mts");
435
+ if (fs.existsSync(ctsPath)) dtsPath = ctsPath;
436
+ else if (fs.existsSync(mtsPath)) dtsPath = mtsPath;
317
437
  }
318
438
  }
319
439
  }
320
440
 
321
441
  if (!fs.existsSync(dtsPath)) {
322
- const altDir = path.dirname(dtsPath)
442
+ const altDir = path.dirname(dtsPath);
323
443
  const altCandidates = [
324
444
  "types.d.ts",
325
445
  "types.d.mts",
@@ -327,185 +447,216 @@ function resolveTypesFile(pkg, pkgDir, subpath) {
327
447
  "index.d.ts",
328
448
  "index.d.mts",
329
449
  "index.d.cts",
330
- ]
450
+ ];
331
451
  for (const candidate of altCandidates) {
332
- const altPath = path.join(altDir, candidate)
452
+ const altPath = path.join(altDir, candidate);
333
453
  if (fs.existsSync(altPath)) {
334
- dtsPath = altPath
335
- typesFile = path.relative(pkgDir, altPath)
454
+ dtsPath = altPath;
455
+ typesFile = path.relative(pkgDir, altPath);
336
456
  if (!source || source === "exports" || source === "package") {
337
- source = "fallback"
457
+ source = "fallback";
338
458
  }
339
- break
459
+ break;
340
460
  }
341
461
  }
342
462
  }
343
463
 
344
- return { typesFile, dtsPath, source }
464
+ // Final fallback: If declared types file doesn't exist, try @types/* package
465
+ if (!fs.existsSync(dtsPath) && basePkgName && require) {
466
+ const typesPackageResult = tryResolveTypesPackage(basePkgName, require);
467
+ if (typesPackageResult) {
468
+ return typesPackageResult;
469
+ }
470
+ }
471
+
472
+ return { typesFile, dtsPath, source };
345
473
  }
346
474
 
347
475
  function normalizeCjsExports(exportsValue) {
348
476
  if (exportsValue === null || exportsValue === undefined) {
349
- return { default: exportsValue }
477
+ return { default: exportsValue };
350
478
  }
351
479
  if (typeof exportsValue === "object" || typeof exportsValue === "function") {
352
- return { ...exportsValue, default: exportsValue }
480
+ return { ...exportsValue, default: exportsValue };
353
481
  }
354
- return { default: exportsValue }
482
+ return { default: exportsValue };
355
483
  }
356
484
 
357
485
  function detectModuleFormat(resolvedPath, pkg) {
358
- const ext = path.extname(resolvedPath)
359
- if (ext === ".cjs" || ext === ".cts") return "cjs"
360
- if (ext === ".mjs" || ext === ".mts") return "esm"
361
- if (pkg?.type === "module") return "esm"
362
- return "cjs"
486
+ const ext = path.extname(resolvedPath);
487
+ if (ext === ".cjs" || ext === ".cts") return "cjs";
488
+ if (ext === ".mjs" || ext === ".mts") return "esm";
489
+ if (pkg?.type === "module") return "esm";
490
+ return "cjs";
363
491
  }
364
492
 
365
493
  function isProbablyClass(fn) {
366
- if (typeof fn !== "function") return false
367
- const source = Function.prototype.toString.call(fn)
368
- if (source.startsWith("class ")) return true
494
+ if (typeof fn !== "function") return false;
495
+ const source = Function.prototype.toString.call(fn);
496
+ if (source.startsWith("class ")) return true;
369
497
  if (fn.prototype) {
370
- const protoProps = Object.getOwnPropertyNames(fn.prototype).filter((p) => p !== "constructor")
371
- if (protoProps.length > 0) return true
498
+ const protoProps = Object.getOwnPropertyNames(fn.prototype).filter(
499
+ (p) => p !== "constructor",
500
+ );
501
+ if (protoProps.length > 0) return true;
372
502
  }
373
- return false
503
+ return false;
374
504
  }
375
505
 
376
506
  async function loadModuleExports(resolvedPath, require, pkg) {
377
- const format = detectModuleFormat(resolvedPath, pkg)
507
+ const format = detectModuleFormat(resolvedPath, pkg);
378
508
  if (format === "cjs") {
379
- const mod = require(resolvedPath)
380
- return { module: normalizeCjsExports(mod), format }
509
+ const mod = require(resolvedPath);
510
+ return { module: normalizeCjsExports(mod), format };
381
511
  }
382
- const mod = await import(pathToFileURL(resolvedPath).href)
383
- return { module: mod, format }
512
+ const mod = await import(pathToFileURL(resolvedPath).href);
513
+ return { module: mod, format };
384
514
  }
385
515
 
386
516
  function buildSymbolMatcher(symbols, fallbackFilter) {
387
- const patterns = []
517
+ const patterns = [];
388
518
  const addPattern = (value) => {
389
- if (!value) return
519
+ if (!value) return;
390
520
  if (value.startsWith("/") && value.endsWith("/") && value.length > 2) {
391
521
  try {
392
- patterns.push({ type: "regex", value: new RegExp(value.slice(1, -1), "i") })
393
- return
522
+ patterns.push({
523
+ type: "regex",
524
+ value: new RegExp(value.slice(1, -1), "i"),
525
+ });
526
+ return;
394
527
  } catch (e) {
395
- patterns.push({ type: "substring", value: value.toLowerCase() })
396
- return
528
+ patterns.push({ type: "substring", value: value.toLowerCase() });
529
+ return;
397
530
  }
398
531
  }
399
532
  if (value.includes("*")) {
400
- const escaped = value.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")
401
- patterns.push({ type: "regex", value: new RegExp(`^${escaped}$`, "i") })
402
- return
533
+ const escaped = value
534
+ .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
535
+ .replace(/\*/g, ".*");
536
+ patterns.push({ type: "regex", value: new RegExp(`^${escaped}$`, "i") });
537
+ return;
403
538
  }
404
- patterns.push({ type: "substring", value: value.toLowerCase() })
405
- }
539
+ patterns.push({ type: "substring", value: value.toLowerCase() });
540
+ };
406
541
 
407
542
  if (Array.isArray(symbols)) {
408
- symbols.forEach(addPattern)
543
+ symbols.forEach(addPattern);
409
544
  } else if (typeof symbols === "string") {
410
- addPattern(symbols)
545
+ addPattern(symbols);
411
546
  } else if (fallbackFilter) {
412
- addPattern(fallbackFilter)
547
+ addPattern(fallbackFilter);
413
548
  }
414
549
 
415
- if (patterns.length === 0) return null
550
+ if (patterns.length === 0) return null;
416
551
  return (name) => {
417
- const lower = name.toLowerCase()
552
+ const lower = name.toLowerCase();
418
553
  return patterns.some((pattern) => {
419
- if (pattern.type === "regex") return pattern.value.test(name)
420
- return lower.includes(pattern.value)
421
- })
422
- }
554
+ if (pattern.type === "regex") return pattern.value.test(name);
555
+ return lower.includes(pattern.value);
556
+ });
557
+ };
423
558
  }
424
559
 
425
560
  function truncateSummary(text, mode, maxLen, truncateMode) {
426
- if (!text) return ""
427
- const normalized = text.replace(/\s+/g, " ").trim()
428
- if (truncateMode === "none") return normalized
429
- const limit = typeof maxLen === "number" ? maxLen : mode === "compact" ? 240 : 1200
430
- if (normalized.length <= limit) return normalized
561
+ if (!text) return "";
562
+ const normalized = text.replace(/\s+/g, " ").trim();
563
+ if (truncateMode === "none") return normalized;
564
+ const limit =
565
+ typeof maxLen === "number" ? maxLen : mode === "compact" ? 240 : 1200;
566
+ if (normalized.length <= limit) return normalized;
431
567
  if (truncateMode === "sentence") {
432
- const slice = normalized.slice(0, limit)
433
- const lastPeriod = Math.max(slice.lastIndexOf("."), slice.lastIndexOf("!"), slice.lastIndexOf("?"))
568
+ const slice = normalized.slice(0, limit);
569
+ const lastPeriod = Math.max(
570
+ slice.lastIndexOf("."),
571
+ slice.lastIndexOf("!"),
572
+ slice.lastIndexOf("?"),
573
+ );
434
574
  if (lastPeriod > 40) {
435
- return `${slice.slice(0, lastPeriod + 1)}`
575
+ return `${slice.slice(0, lastPeriod + 1)}`;
436
576
  }
437
577
  }
438
578
  if (truncateMode === "word") {
439
- const slice = normalized.slice(0, limit)
440
- const lastSpace = slice.lastIndexOf(" ")
579
+ const slice = normalized.slice(0, limit);
580
+ const lastSpace = slice.lastIndexOf(" ");
441
581
  if (lastSpace > 40) {
442
- return `${slice.slice(0, lastSpace)}...`
582
+ return `${slice.slice(0, lastSpace)}...`;
443
583
  }
444
584
  }
445
- return `${normalized.slice(0, Math.max(0, limit - 3))}...`
585
+ return `${normalized.slice(0, Math.max(0, limit - 3))}...`;
446
586
  }
447
587
 
448
588
  function formatJsdocEntry(name, doc, options) {
449
- const mode = options.mode || "compact"
450
- const truncateMode = options.truncate || "word"
451
- const maxLen = options.maxLen
452
- const sections = options.sections && options.sections.length > 0 ? options.sections : ["summary", "tags"]
453
- const includeTags = options.tags?.include || null
454
- const excludeTags = options.tags?.exclude || null
455
-
456
- const summary = truncateSummary(doc.summary || "", mode, maxLen, truncateMode)
457
- const tags = doc.tags || {}
458
- const tagLines = []
589
+ const mode = options.mode || "compact";
590
+ const truncateMode = options.truncate || "word";
591
+ const maxLen = options.maxLen;
592
+ const sections =
593
+ options.sections && options.sections.length > 0
594
+ ? options.sections
595
+ : ["summary", "tags"];
596
+ const includeTags = options.tags?.include || null;
597
+ const excludeTags = options.tags?.exclude || null;
598
+
599
+ const summary = truncateSummary(
600
+ doc.summary || "",
601
+ mode,
602
+ maxLen,
603
+ truncateMode,
604
+ );
605
+ const tags = doc.tags || {};
606
+ const tagLines = [];
459
607
 
460
608
  const addTag = (tagName, values) => {
461
609
  if (!values || values.length === 0) {
462
- tagLines.push(`@${tagName}`)
463
- return
610
+ tagLines.push(`@${tagName}`);
611
+ return;
464
612
  }
465
613
  for (const value of values) {
466
- tagLines.push(value ? `@${tagName} ${value}` : `@${tagName}`)
614
+ tagLines.push(value ? `@${tagName} ${value}` : `@${tagName}`);
467
615
  }
468
- }
616
+ };
469
617
 
470
- const wantParams = sections.includes("params")
471
- const wantReturns = sections.includes("returns")
472
- const wantTags = sections.includes("tags")
618
+ const wantParams = sections.includes("params");
619
+ const wantReturns = sections.includes("returns");
620
+ const wantTags = sections.includes("tags");
473
621
 
474
622
  if (wantParams) {
475
- if (tags.param) addTag("param", tags.param)
623
+ if (tags.param) addTag("param", tags.param);
476
624
  }
477
625
  if (wantReturns) {
478
- if (tags.returns) addTag("returns", tags.returns)
479
- if (tags.return) addTag("return", tags.return)
626
+ if (tags.returns) addTag("returns", tags.returns);
627
+ if (tags.return) addTag("return", tags.return);
480
628
  }
481
629
  if (wantTags) {
482
630
  for (const [tagName, values] of Object.entries(tags)) {
483
- if (tagName === "param" || tagName === "returns" || tagName === "return") continue
484
- if (includeTags && !includeTags.includes(tagName)) continue
485
- if (excludeTags && excludeTags.includes(tagName)) continue
631
+ if (tagName === "param" || tagName === "returns" || tagName === "return")
632
+ continue;
633
+ if (includeTags && !includeTags.includes(tagName)) continue;
634
+ if (excludeTags && excludeTags.includes(tagName)) continue;
486
635
  if (mode === "compact" && !includeTags && !excludeTags) {
487
- if (!["deprecated", "since", "experimental"].includes(tagName)) continue
636
+ if (!["deprecated", "since", "experimental"].includes(tagName))
637
+ continue;
488
638
  }
489
- addTag(tagName, values)
639
+ addTag(tagName, values);
490
640
  }
491
641
  }
492
642
 
493
- const parts = []
643
+ const parts = [];
494
644
  if (sections.includes("summary") && summary) {
495
- parts.push(summary)
645
+ parts.push(summary);
496
646
  }
497
647
  if (tagLines.length > 0) {
498
- parts.push(tagLines.join("; "))
648
+ parts.push(tagLines.join("; "));
499
649
  }
500
650
 
501
- return `${name}: ${parts.join(" | ")}`.trim()
651
+ return `${name}: ${parts.join(" | ")}`.trim();
502
652
  }
503
653
 
504
654
  function filterTypeInfo(typeInfo, filter, kindFilter) {
505
- if (!typeInfo) return null
506
- const includeName = (name) => !filter || name.toLowerCase().includes(filter)
507
- const allow = (kind) => !kindFilter || kindFilter.length === 0 || kindFilter.includes(kind)
508
- const includeExtras = !kindFilter || kindFilter.length === 0
655
+ if (!typeInfo) return null;
656
+ const includeName = (name) => !filter || name.toLowerCase().includes(filter);
657
+ const allow = (kind) =>
658
+ !kindFilter || kindFilter.length === 0 || kindFilter.includes(kind);
659
+ const includeExtras = !kindFilter || kindFilter.length === 0;
509
660
 
510
661
  const filtered = {
511
662
  functions: {},
@@ -516,270 +667,431 @@ function filterTypeInfo(typeInfo, filter, kindFilter) {
516
667
  namespaces: {},
517
668
  defaults: [],
518
669
  jsdoc: {},
519
- }
670
+ };
520
671
 
521
672
  if (allow("function")) {
522
673
  for (const [name, info] of Object.entries(typeInfo.functions)) {
523
- if (includeName(name)) filtered.functions[name] = info
674
+ if (includeName(name)) filtered.functions[name] = info;
524
675
  }
525
676
  }
526
677
 
527
678
  if (allow("interface")) {
528
679
  for (const [name, props] of Object.entries(typeInfo.interfaces)) {
529
- if (includeName(name)) filtered.interfaces[name] = props
680
+ if (includeName(name)) filtered.interfaces[name] = props;
530
681
  }
531
682
  }
532
683
 
533
684
  if (allow("type")) {
534
685
  for (const [name, def] of Object.entries(typeInfo.types)) {
535
- if (includeName(name)) filtered.types[name] = def
686
+ if (includeName(name)) filtered.types[name] = def;
536
687
  }
537
688
  }
538
689
 
539
690
  if (allow("class")) {
540
691
  for (const [name, info] of Object.entries(typeInfo.classes)) {
541
- if (includeName(name)) filtered.classes[name] = info
692
+ if (includeName(name)) filtered.classes[name] = info;
542
693
  }
543
694
  }
544
695
 
545
696
  if (includeExtras) {
546
697
  for (const [name, members] of Object.entries(typeInfo.enums || {})) {
547
- if (includeName(name)) filtered.enums[name] = members
698
+ if (includeName(name)) filtered.enums[name] = members;
548
699
  }
549
700
  for (const [name, value] of Object.entries(typeInfo.namespaces || {})) {
550
- if (includeName(name)) filtered.namespaces[name] = value
701
+ if (includeName(name)) filtered.namespaces[name] = value;
551
702
  }
552
- filtered.defaults = (typeInfo.defaults || []).filter((value) => includeName("default") || includeName(value))
703
+ filtered.defaults = (typeInfo.defaults || []).filter(
704
+ (value) => includeName("default") || includeName(value),
705
+ );
553
706
  }
554
707
 
555
708
  if (typeInfo.jsdoc) {
556
709
  for (const [name, doc] of Object.entries(typeInfo.jsdoc)) {
557
- if (includeName(name)) filtered.jsdoc[name] = doc
710
+ if (includeName(name)) filtered.jsdoc[name] = doc;
558
711
  }
559
712
  }
560
713
 
561
- return filtered
714
+ return filtered;
562
715
  }
563
716
 
564
717
  // Helper function to inspect object properties recursively
565
718
  function inspectObject(obj, currentDepth = 0, maxDepth = 1, indent = " ") {
566
719
  if (currentDepth >= maxDepth || obj === null || obj === undefined) {
567
- return []
720
+ return [];
568
721
  }
569
722
 
570
- const lines = []
723
+ const lines = [];
571
724
  try {
572
- const descriptors = Object.getOwnPropertyDescriptors(obj)
573
- const keys = Object.keys(descriptors).slice(0, 10) // Limit to first 10 properties
725
+ const descriptors = Object.getOwnPropertyDescriptors(obj);
726
+ const keys = Object.keys(descriptors).slice(0, 10); // Limit to first 10 properties
574
727
  for (const key of keys) {
575
728
  try {
576
- const descriptor = descriptors[key]
729
+ const descriptor = descriptors[key];
577
730
  if (descriptor.get || descriptor.set) {
578
- lines.push(`${indent.repeat(currentDepth + 1)}${key}: <getter>`)
579
- continue
731
+ lines.push(`${indent.repeat(currentDepth + 1)}${key}: <getter>`);
732
+ continue;
580
733
  }
581
- const value = descriptor.value
582
- const type = typeof value
583
- const prefix = indent.repeat(currentDepth + 1)
734
+ const value = descriptor.value;
735
+ const type = typeof value;
736
+ const prefix = indent.repeat(currentDepth + 1);
584
737
 
585
738
  if (type === "function") {
586
- const paramCount = value.length
587
- lines.push(`${prefix}${key}(${paramCount} param${paramCount !== 1 ? "s" : ""})`)
739
+ const paramCount = value.length;
740
+ lines.push(
741
+ `${prefix}${key}(${paramCount} param${paramCount !== 1 ? "s" : ""})`,
742
+ );
588
743
  } else if (type === "object" && value !== null) {
589
- lines.push(`${prefix}${key}: {object}`)
744
+ lines.push(`${prefix}${key}: {object}`);
590
745
  if (currentDepth + 1 < maxDepth) {
591
- lines.push(...inspectObject(value, currentDepth + 1, maxDepth, indent))
746
+ lines.push(
747
+ ...inspectObject(value, currentDepth + 1, maxDepth, indent),
748
+ );
592
749
  }
593
750
  } else {
594
- const valStr = type === "string" ? `"${String(value).substring(0, 30)}"` : String(value).substring(0, 30)
595
- lines.push(`${prefix}${key}: ${valStr}`)
751
+ const valStr =
752
+ type === "string"
753
+ ? `"${String(value).substring(0, 30)}"`
754
+ : String(value).substring(0, 30);
755
+ lines.push(`${prefix}${key}: ${valStr}`);
596
756
  }
597
757
  } catch (e) {
598
758
  // Skip properties that throw on access
599
759
  }
600
760
  }
601
- const totalKeys = Object.keys(descriptors).length
761
+ const totalKeys = Object.keys(descriptors).length;
602
762
  if (totalKeys > 10) {
603
- lines.push(`${indent.repeat(currentDepth + 1)}... and ${totalKeys - 10} more`)
763
+ lines.push(
764
+ `${indent.repeat(currentDepth + 1)}... and ${totalKeys - 10} more`,
765
+ );
604
766
  }
605
767
  } catch (e) {
606
768
  // Skip if object is not enumerable
607
769
  }
608
- return lines
770
+ return lines;
609
771
  }
610
772
 
611
773
  export async function runInspect(options) {
612
- const collect = !options?.write && !options?.writeError
613
- const output = collect ? [] : null
614
- const write = options?.write
615
- const writeError = options?.writeError ?? options?.write
774
+ const collect = !options?.write && !options?.writeError;
775
+ const output = collect ? [] : null;
776
+ const write = options?.write;
777
+ const writeError = options?.writeError ?? options?.write;
778
+ const format =
779
+ options?.format === "object"
780
+ ? "object"
781
+ : options?.format === "json"
782
+ ? "json"
783
+ : "text";
616
784
 
617
785
  const log = (line = "") => {
618
- if (collect) output.push(String(line))
619
- else if (write) write(String(line))
620
- }
786
+ if (format === "json" || format === "object") return;
787
+ if (collect) output.push(String(line));
788
+ else if (write) write(String(line));
789
+ };
621
790
  const logErr = (line = "") => {
622
- if (collect) output.push(String(line))
623
- else if (writeError) writeError(String(line))
624
- }
625
-
626
- const target = options?.target
627
- const filter = options?.filter ? options.filter.toLowerCase() : null
628
- const showTypes = Boolean(options?.showTypes)
629
- const jsdocModeRaw = options?.jsdoc ? String(options.jsdoc).toLowerCase() : null
630
- const jsdocQuery = options?.jsdocQuery || null
631
- const jsdocOutputRaw = options?.jsdocOutput ? String(options.jsdocOutput).toLowerCase() : null
632
- const jsdocOutput = jsdocOutputRaw || (jsdocQuery ? "section" : "off")
633
- const wantJsdoc = jsdocOutput !== "off"
791
+ if (format === "json" || format === "object") return;
792
+ if (collect) output.push(String(line));
793
+ else if (writeError) writeError(String(line));
794
+ };
795
+
796
+ const target = options?.target;
797
+ const filterRaw = options?.filter || null;
798
+ const filter = filterRaw ? filterRaw.toLowerCase() : null;
799
+ const showTypes = Boolean(options?.showTypes);
800
+ const remote = Boolean(options?.remote);
801
+ const remoteVersion = options?.remoteVersion
802
+ ? String(options.remoteVersion)
803
+ : null;
804
+ const jsdocModeRaw = options?.jsdoc
805
+ ? String(options.jsdoc).toLowerCase()
806
+ : null;
807
+ const jsdocQuery = options?.jsdocQuery || null;
808
+ const jsdocOutputRaw = options?.jsdocOutput
809
+ ? String(options.jsdocOutput).toLowerCase()
810
+ : null;
811
+ const jsdocOutput = jsdocOutputRaw || (jsdocQuery ? "section" : "off");
812
+ const wantJsdoc = jsdocOutput !== "off";
634
813
  const jsdocMode =
635
- showTypes || wantJsdoc ? (jsdocQuery?.mode || jsdocModeRaw || "compact") : "off"
814
+ showTypes || wantJsdoc
815
+ ? jsdocQuery?.mode || jsdocModeRaw || "compact"
816
+ : "off";
636
817
  const kindFilter = Array.isArray(options?.kind)
637
818
  ? options.kind.map((k) => String(k).trim().toLowerCase())
638
- : null
639
- let depth = typeof options?.depth === "number" ? options.depth : 1
640
- if (isNaN(depth) || depth < 0 || depth > 5) depth = 1
819
+ : null;
820
+ let depth = typeof options?.depth === "number" ? options.depth : 1;
821
+ if (isNaN(depth) || depth < 0 || depth > 5) depth = 1;
822
+ const analyzeSource = Boolean(options?.analyzeSource);
823
+ const sourceMaxFiles =
824
+ typeof options?.sourceMaxFiles === "number" ? options.sourceMaxFiles : 5;
825
+ const sourceIncludeBody = Boolean(options?.sourceIncludeBody);
826
+
827
+ const jsonOutput =
828
+ format === "json" || format === "object"
829
+ ? {
830
+ schemaVersion: 1,
831
+ package: null,
832
+ version: null,
833
+ description: null,
834
+ exports: null,
835
+ types: null,
836
+ docs: null,
837
+ sections: null,
838
+ examples: null,
839
+ resolution: null,
840
+ meta: {
841
+ target: target || null,
842
+ showTypes,
843
+ format,
844
+ remote,
845
+ remoteVersion,
846
+ },
847
+ warnings: [],
848
+ }
849
+ : null;
850
+
851
+ const warn = (message) => {
852
+ if (jsonOutput) {
853
+ jsonOutput.warnings.push(message);
854
+ return;
855
+ }
856
+ logErr(`⚠️ ${message}`);
857
+ };
641
858
 
642
859
  if (!target) {
643
860
  logErr(
644
861
  "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]",
645
- )
646
- return collect ? output.join("\n") : ""
862
+ );
863
+ if (format === "object") return jsonOutput;
864
+ if (format === "json") return JSON.stringify(jsonOutput, null, 2);
865
+ return collect ? output.join("\n") : "";
647
866
  }
648
867
 
649
- const baseCwd = options?.cwd
650
- const resolveFrom = options?.resolveFrom
868
+ const baseCwd = options?.cwd;
869
+ let resolveFrom = options?.resolveFrom
651
870
  ? path.resolve(baseCwd || process.cwd(), options.resolveFrom)
652
- : baseCwd
871
+ : baseCwd;
872
+
873
+ if (remote) {
874
+ const basePkgName = getPackageName(target);
875
+ if (basePkgName) {
876
+ const spec = remoteVersion || "latest";
877
+ log(`\n🌐 Remote: downloading ${basePkgName}@${spec}...`);
878
+ try {
879
+ const downloaded = downloadVersion(basePkgName, spec, {
880
+ timeout: 120000,
881
+ });
882
+ resolveFrom = downloaded.path;
883
+ log(` CachePath: ${downloaded.path}`);
884
+ log(` Cached: ${downloaded.cached ? "yes" : "no"}`);
885
+ } catch (e) {
886
+ const message = e instanceof Error ? e.message : String(e);
887
+ logErr(`\n❌ Remote download failed: ${message}`);
888
+ }
889
+ }
890
+ }
891
+
653
892
  if (baseCwd) {
654
893
  try {
655
- process.chdir(baseCwd)
894
+ process.chdir(baseCwd);
656
895
  } catch (e) {}
657
896
  }
658
897
 
659
- const resolution = await resolveTargetModule(target, baseCwd, resolveFrom)
660
- const resolveCwd = resolution.resolveCwd || baseCwd
661
- const require = createRequire(resolveCwd ? path.join(resolveCwd, "noop.js") : import.meta.url)
662
- const resolvedPath = resolution.resolved
898
+ const resolution = await resolveTargetModule(target, baseCwd, resolveFrom);
899
+ const resolveCwd = resolution.resolveCwd || baseCwd;
900
+ const require = createRequire(
901
+ resolveCwd ? path.join(resolveCwd, "noop.js") : import.meta.url,
902
+ );
903
+ const resolvedPath = resolution.resolved;
663
904
  const entrypointPath =
664
905
  typeof resolvedPath === "string" && resolvedPath.startsWith("file://")
665
906
  ? fileURLToPath(resolvedPath)
666
- : resolvedPath
667
- const entrypointExists = entrypointPath ? fs.existsSync(entrypointPath) : false
907
+ : resolvedPath;
908
+ const entrypointExists = entrypointPath
909
+ ? fs.existsSync(entrypointPath)
910
+ : false;
911
+
912
+ if (jsonOutput) {
913
+ jsonOutput.resolution = {
914
+ target,
915
+ resolveFrom: resolveFrom || null,
916
+ resolveCwd: resolveCwd || null,
917
+ resolved: resolution.resolved || null,
918
+ entrypointPath: entrypointPath || null,
919
+ entrypointExists,
920
+ };
921
+ }
668
922
 
669
- const flags = []
670
- if (filter) flags.push(`Filtro: "${filter}"`)
671
- if (kindFilter) flags.push(`Kind: ${kindFilter.join(",")}`)
672
- if (showTypes || wantJsdoc) flags.push("Type Analysis")
673
- if (jsdocOutput !== "off") flags.push(`JSDoc: ${jsdocMode}`)
674
- if (depth > 1) flags.push(`Depth: ${depth}`)
923
+ const flags = [];
924
+ if (filterRaw) flags.push(`Filtro: "${filterRaw}"`);
925
+ if (kindFilter) flags.push(`Kind: ${kindFilter.join(",")}`);
926
+ if (showTypes || wantJsdoc) flags.push("Type Analysis");
927
+ if (remote) flags.push(`Remote${remoteVersion ? `@${remoteVersion}` : ""}`);
928
+ if (jsdocOutput !== "off") flags.push(`JSDoc: ${jsdocMode}`);
929
+ if (depth > 1) flags.push(`Depth: ${depth}`);
675
930
 
676
- const flagsStr = flags.length > 0 ? ` (${flags.join(" | ")})` : ""
677
- log(`🔍 Target: ${target}${flagsStr}`)
931
+ const flagsStr = flags.length > 0 ? ` (${flags.join(" | ")})` : "";
932
+ log(`🔍 Target: ${target}${flagsStr}`);
678
933
 
679
934
  try {
680
935
  if (!resolution.resolved) {
681
- logErr(`\n❌ Erro: Não foi possível resolver '${target}'`)
682
- logErr(`ResolveFrom: ${resolveFrom || baseCwd || "unknown"}`)
683
- logErr(`Certifique-se que '${target}' está instalado e é um caminho válido.`)
684
- return collect ? output.join("\n") : ""
936
+ const errorMsg = `Não foi possível resolver '${target}'`;
937
+ warn(errorMsg);
938
+ logErr(`\n❌ Erro: ${errorMsg}`);
939
+ logErr(`ResolveFrom: ${resolveFrom || baseCwd || "unknown"}`);
940
+ logErr(
941
+ `Certifique-se que '${target}' está instalado e é um caminho válido.`,
942
+ );
943
+ if (format === "object") return jsonOutput;
944
+ if (format === "json") return JSON.stringify(jsonOutput, null, 2);
945
+ return collect ? output.join("\n") : "";
685
946
  }
686
947
 
687
- const basePkg = getPackageName(target)
688
- const subpath = getPackageSubpath(target)
948
+ const basePkg = getPackageName(target);
949
+ const subpath = getPackageSubpath(target);
689
950
  const pkgInfo = basePkg
690
- ? resolvePackageInfo(basePkg, require, resolveFrom || baseCwd || process.cwd(), entrypointPath)
691
- : null
692
- const pkg = pkgInfo?.pkg
693
- const pkgDir = pkgInfo?.pkgDir
694
-
695
- log(`\n🧭 Resolution:`)
696
- log(` ResolveFrom: ${resolveFrom || baseCwd || "unknown"}`)
697
- log(` Entrypoint: ${resolution.resolved}`)
951
+ ? resolvePackageInfo(
952
+ basePkg,
953
+ require,
954
+ resolveFrom || baseCwd || process.cwd(),
955
+ entrypointPath,
956
+ )
957
+ : null;
958
+ const pkg = pkgInfo?.pkg;
959
+ const pkgDir = pkgInfo?.pkgDir;
960
+
961
+ log(`\n🧭 Resolution:`);
962
+ log(` ResolveFrom: ${resolveFrom || baseCwd || "unknown"}`);
963
+ log(` Entrypoint: ${resolution.resolved}`);
698
964
  if (resolution.resolver) {
699
- log(` Resolver: ${resolution.resolver}`)
965
+ log(` Resolver: ${resolution.resolver}`);
700
966
  }
701
967
  if (pkgDir) {
702
- log(` PackageRoot: ${pkgDir}`)
968
+ log(` PackageRoot: ${pkgDir}`);
703
969
  }
704
970
 
705
- let dtsPath
706
- let typesFile
707
- let typesSource
971
+ let dtsPath;
972
+ let typesFile;
973
+ let typesSource;
708
974
 
709
975
  if (pkg) {
710
- log(`\n📄 Package Info:`)
711
- log(` Name: ${pkg.name || basePkg}`)
712
- log(` Version: ${pkg.version || "Unknown"}`)
976
+ log(`\n📄 Package Info:`);
977
+ log(` Name: ${pkg.name || basePkg}`);
978
+ log(` Version: ${pkg.version || "Unknown"}`);
713
979
  if (pkg.description) {
714
- log(` Description: ${pkg.description}`)
980
+ log(` Description: ${pkg.description}`);
715
981
  }
716
982
  if (pkg.license) {
717
- log(` License: ${pkg.license}`)
983
+ log(` License: ${pkg.license}`);
718
984
  }
719
985
 
720
- const typesResolution = resolveTypesFile(pkg, pkgDir, subpath)
721
- typesFile = typesResolution.typesFile
722
- dtsPath = typesResolution.dtsPath
723
- typesSource = typesResolution.source
986
+ if (jsonOutput) {
987
+ jsonOutput.package = pkg.name || basePkg;
988
+ jsonOutput.version = pkg.version || null;
989
+ jsonOutput.description = pkg.description || null;
990
+ }
991
+
992
+ const typesResolution = resolveTypesFile(
993
+ pkg,
994
+ pkgDir,
995
+ subpath,
996
+ basePkg,
997
+ require,
998
+ );
999
+ typesFile = typesResolution.typesFile;
1000
+ dtsPath = typesResolution.dtsPath;
1001
+ typesSource = typesResolution.source;
1002
+
1003
+ // Update pkgDir if we're using @types package
1004
+ if (typesResolution.pkgDir && typesSource === "@types") {
1005
+ // Keep original pkgDir for runtime, but use types pkgDir for dtsPath
1006
+ // dtsPath is already set correctly from typesResolution
1007
+ }
724
1008
 
725
1009
  if (typesFile) {
726
- const sourceLabel = typesSource ? ` (${typesSource})` : ""
727
- const existsLabel = dtsPath && fs.existsSync(dtsPath) ? "" : " (missing)"
728
- log(` Types: ${typesFile}${sourceLabel}${existsLabel}`)
1010
+ const sourceLabel = typesSource ? ` (${typesSource})` : "";
1011
+ const existsLabel =
1012
+ dtsPath && fs.existsSync(dtsPath) ? "" : " (missing)";
1013
+ log(` Types: ${typesFile}${sourceLabel}${existsLabel}`);
729
1014
  } else {
730
- log(` Types: Not found`)
1015
+ log(` Types: Not found`);
731
1016
  }
732
1017
 
733
1018
  // === Mostrar subpath exports ===
734
1019
  if (pkg.exports && typeof pkg.exports === "object") {
735
- const exportEntries = Object.entries(pkg.exports)
1020
+ const exportEntries = Object.entries(pkg.exports);
736
1021
  if (exportEntries.length > 0) {
737
- log(`\n🚪 Subpath Exports (${exportEntries.length} available):`)
1022
+ log(`\n🚪 Subpath Exports (${exportEntries.length} available):`);
738
1023
  for (const [pathKey, value] of exportEntries.slice(0, 10)) {
739
1024
  if (typeof value === "string") {
740
- log(` ${pathKey} → ${value}`)
1025
+ log(` ${pathKey} → ${value}`);
741
1026
  } else if (value && typeof value === "object") {
742
- const targets = Object.keys(value).join(", ")
743
- log(` ${pathKey} → { ${targets} }`)
1027
+ const targets = Object.keys(value).join(", ");
1028
+ log(` ${pathKey} → { ${targets} }`);
744
1029
  }
745
1030
  }
746
1031
  if (exportEntries.length > 10) {
747
- log(` ... and ${exportEntries.length - 10} more`)
1032
+ log(` ... and ${exportEntries.length - 10} more`);
748
1033
  }
749
1034
  }
750
1035
  }
751
1036
  }
752
1037
 
753
- let typeInfoRaw = null
1038
+ let typeInfoRaw = null;
754
1039
  if ((showTypes || wantJsdoc) && dtsPath && fs.existsSync(dtsPath)) {
755
- typeInfoRaw = parseDtsFile(dtsPath, null)
1040
+ typeInfoRaw = parseDtsFile(dtsPath, null);
756
1041
  }
757
1042
 
758
- let moduleNamespace = {}
759
- let moduleDescriptors = {}
760
- let allExports = []
761
- const runtimeAvailable = Boolean(entrypointExists)
1043
+ let moduleNamespace = {};
1044
+ let moduleDescriptors = {};
1045
+ let allExports = [];
1046
+ const runtimeAvailable = Boolean(entrypointExists);
762
1047
  if (!runtimeAvailable) {
763
- log(`\n⚠️ Entrypoint not found on disk; runtime exports skipped.`)
1048
+ log(`\n⚠️ Entrypoint not found on disk; runtime exports skipped.`);
764
1049
  } else {
765
- const { module: loadedNamespace } = await loadModuleExports(entrypointPath, require, pkg)
766
- moduleNamespace = loadedNamespace
767
- moduleDescriptors = Object.getOwnPropertyDescriptors(moduleNamespace)
768
- allExports = Object.keys(moduleDescriptors)
1050
+ const { module: loadedNamespace } = await loadModuleExports(
1051
+ entrypointPath,
1052
+ require,
1053
+ pkg,
1054
+ );
1055
+ moduleNamespace = loadedNamespace;
1056
+ moduleDescriptors = Object.getOwnPropertyDescriptors(moduleNamespace);
1057
+ allExports = Object.keys(moduleDescriptors);
769
1058
  }
770
1059
 
771
- // Lógica de Filtro
772
- let finalList = allExports
773
- if (filter) {
774
- finalList = allExports.filter((key) => key.toLowerCase().includes(filter))
1060
+ // Lógica de Filtro (case-insensitive, supports regex)
1061
+ let finalList = allExports;
1062
+ if (filterRaw) {
1063
+ // Check if it's a regex pattern
1064
+ const isRegex =
1065
+ filterRaw.startsWith("/") &&
1066
+ filterRaw.endsWith("/") &&
1067
+ filterRaw.length > 2;
1068
+ if (isRegex) {
1069
+ try {
1070
+ const regexPattern = filterRaw.slice(1, -1);
1071
+ const regex = new RegExp(regexPattern, "i"); // case-insensitive
1072
+ finalList = allExports.filter((key) => regex.test(key));
1073
+ } catch (e) {
1074
+ // Fallback to substring match if regex is invalid
1075
+ finalList = allExports.filter((key) =>
1076
+ key.toLowerCase().includes(filter),
1077
+ );
1078
+ }
1079
+ } else {
1080
+ // Simple substring match (case-insensitive)
1081
+ finalList = allExports.filter((key) =>
1082
+ key.toLowerCase().includes(filter),
1083
+ );
1084
+ }
775
1085
  }
776
1086
 
777
1087
  // Se a lista for muito grande e não tiver filtro, avisa e corta
778
- const LIMIT = 100
779
- if (!filter && finalList.length > LIMIT) {
780
- log(`\n⚠️ Módulo exporta ${finalList.length} itens. Mostrando os primeiros ${LIMIT}...`)
781
- log(`DICA: Use o parâmetro 'filter' para encontrar o que procura.`)
782
- finalList = finalList.slice(0, LIMIT)
1088
+ const LIMIT = 100;
1089
+ if (!filterRaw && finalList.length > LIMIT) {
1090
+ log(
1091
+ `\n⚠️ Módulo exporta ${finalList.length} itens. Mostrando os primeiros ${LIMIT}...`,
1092
+ );
1093
+ log(`DICA: Use o parâmetro 'filter' para encontrar o que procura.`);
1094
+ finalList = finalList.slice(0, LIMIT);
783
1095
  }
784
1096
 
785
1097
  // === MELHORIA 2: Categorizar exports por tipo ===
@@ -789,45 +1101,49 @@ export async function runInspect(options) {
789
1101
  objects: [],
790
1102
  primitives: [],
791
1103
  constants: [],
792
- }
1104
+ };
793
1105
 
794
1106
  if (runtimeAvailable) {
795
1107
  for (const key of finalList) {
796
- const descriptor = moduleDescriptors[key]
797
- if (!descriptor) continue
1108
+ const descriptor = moduleDescriptors[key];
1109
+ if (!descriptor) continue;
798
1110
  if (descriptor.get || descriptor.set) {
799
- categorized.objects.push(key)
800
- continue
1111
+ categorized.objects.push(key);
1112
+ continue;
801
1113
  }
802
- const value = descriptor.value
803
- const type = typeof value
1114
+ const value = descriptor.value;
1115
+ const type = typeof value;
804
1116
 
805
1117
  if (type === "function") {
806
1118
  // Distinguir class vs function
807
1119
  if (isProbablyClass(value)) {
808
- categorized.classes.push(key)
1120
+ categorized.classes.push(key);
809
1121
  } else {
810
- categorized.functions.push(key)
1122
+ categorized.functions.push(key);
811
1123
  }
812
1124
  } else if (type === "object" && value !== null) {
813
- categorized.objects.push(key)
814
- } else if (type === "string" || type === "number" || type === "boolean") {
815
- categorized.constants.push(key)
1125
+ categorized.objects.push(key);
1126
+ } else if (
1127
+ type === "string" ||
1128
+ type === "number" ||
1129
+ type === "boolean"
1130
+ ) {
1131
+ categorized.constants.push(key);
816
1132
  } else {
817
- categorized.primitives.push(key)
1133
+ categorized.primitives.push(key);
818
1134
  }
819
1135
  }
820
1136
 
821
1137
  // Prefer class from type info when available
822
1138
  if (typeInfoRaw && Object.keys(typeInfoRaw.classes).length > 0) {
823
- const classNames = new Set(Object.keys(typeInfoRaw.classes))
1139
+ const classNames = new Set(Object.keys(typeInfoRaw.classes));
824
1140
  categorized.functions = categorized.functions.filter((name) => {
825
1141
  if (classNames.has(name)) {
826
- categorized.classes.push(name)
827
- return false
1142
+ categorized.classes.push(name);
1143
+ return false;
828
1144
  }
829
- return true
830
- })
1145
+ return true;
1146
+ });
831
1147
  }
832
1148
 
833
1149
  // Apply kind filter if specified
@@ -837,70 +1153,89 @@ export async function runInspect(options) {
837
1153
  class: "classes",
838
1154
  object: "objects",
839
1155
  constant: "constants",
840
- }
1156
+ };
841
1157
 
842
1158
  // Keep only the requested kinds
843
1159
  for (const [key, value] of Object.entries(categorized)) {
844
1160
  const shouldKeep = Object.entries(kindMap).some(
845
1161
  ([kind, catKey]) => kindFilter.includes(kind) && catKey === key,
846
- )
1162
+ );
847
1163
  if (!shouldKeep) {
848
- categorized[key] = []
1164
+ categorized[key] = [];
849
1165
  }
850
1166
  }
851
1167
 
852
1168
  // Update finalList to only include filtered kinds
853
- finalList = [...categorized.functions, ...categorized.classes, ...categorized.objects, ...categorized.constants]
1169
+ finalList = [
1170
+ ...categorized.functions,
1171
+ ...categorized.classes,
1172
+ ...categorized.objects,
1173
+ ...categorized.constants,
1174
+ ];
854
1175
  }
855
1176
  }
856
1177
 
1178
+ if (jsonOutput) {
1179
+ jsonOutput.exports = {
1180
+ total: finalList.length,
1181
+ functions: categorized.functions,
1182
+ classes: categorized.classes,
1183
+ objects: categorized.objects,
1184
+ constants: categorized.constants,
1185
+ };
1186
+ }
1187
+
857
1188
  // Mostrar exports categorizados
858
1189
  if (jsdocOutput !== "only") {
859
1190
  if (!runtimeAvailable) {
860
- log(`\nℹ️ Runtime exports unavailable. Use --types to inspect type exports.`)
1191
+ log(
1192
+ `\nℹ️ Runtime exports unavailable. Use --types to inspect type exports.`,
1193
+ );
861
1194
  }
862
- log(`\n🔑 Exports Encontrados (${finalList.length} total):`)
1195
+ log(`\n🔑 Exports Encontrados (${finalList.length} total):`);
863
1196
 
864
1197
  if (categorized.functions.length > 0) {
865
- log(`\n 📘 Functions (${categorized.functions.length}):`)
866
- log(` ${categorized.functions.join(", ")}`)
1198
+ log(`\n 📘 Functions (${categorized.functions.length}):`);
1199
+ log(` ${categorized.functions.join(", ")}`);
867
1200
  }
868
1201
 
869
1202
  if (categorized.classes.length > 0) {
870
- log(`\n 🏛️ Classes (${categorized.classes.length}):`)
871
- log(` ${categorized.classes.join(", ")}`)
1203
+ log(`\n 🏛️ Classes (${categorized.classes.length}):`);
1204
+ log(` ${categorized.classes.join(", ")}`);
872
1205
  }
873
1206
 
874
1207
  if (categorized.objects.length > 0) {
875
- log(`\n 📦 Objects/Namespaces (${categorized.objects.length}):`)
876
- log(` ${categorized.objects.join(", ")}`)
1208
+ log(`\n 📦 Objects/Namespaces (${categorized.objects.length}):`);
1209
+ log(` ${categorized.objects.join(", ")}`);
877
1210
 
878
1211
  // If depth > 0, show object contents
879
1212
  if (depth > 0 && categorized.objects.length <= 10) {
880
- log(`\n 📦 Object Contents (depth: ${depth}):`)
1213
+ log(`\n 📦 Object Contents (depth: ${depth}):`);
881
1214
  for (const objName of categorized.objects) {
882
- log(`\n ${objName}:`)
883
- const descriptor = moduleDescriptors[objName]
1215
+ log(`\n ${objName}:`);
1216
+ const descriptor = moduleDescriptors[objName];
884
1217
  if (!descriptor || descriptor.get || descriptor.set) {
885
- log(` ${objName}: <getter>`)
886
- continue
1218
+ log(` ${objName}: <getter>`);
1219
+ continue;
887
1220
  }
888
- const objValue = descriptor.value
889
- const lines = inspectObject(objValue, 0, depth, " ")
890
- lines.forEach((line) => log(` ${line}`))
1221
+ const objValue = descriptor.value;
1222
+ const lines = inspectObject(objValue, 0, depth, " ");
1223
+ lines.forEach((line) => log(` ${line}`));
891
1224
  }
892
1225
  } else if (depth > 0 && categorized.objects.length > 10) {
893
- log(`\n ℹ️ Too many objects to show contents. Use 'filter' to narrow down.`)
1226
+ log(
1227
+ `\n ℹ️ Too many objects to show contents. Use 'filter' to narrow down.`,
1228
+ );
894
1229
  }
895
1230
  }
896
1231
 
897
1232
  if (categorized.constants.length > 0) {
898
- log(`\n 🔢 Constants (${categorized.constants.length}):`)
899
- log(` ${categorized.constants.join(", ")}`)
1233
+ log(`\n 🔢 Constants (${categorized.constants.length}):`);
1234
+ log(` ${categorized.constants.join(", ")}`);
900
1235
  }
901
1236
 
902
1237
  if (finalList.length === 0) {
903
- log("Nenhum export corresponde ao filtro.")
1238
+ log("Nenhum export corresponde ao filtro.");
904
1239
  }
905
1240
  }
906
1241
 
@@ -908,28 +1243,47 @@ export async function runInspect(options) {
908
1243
  if (jsdocOutput !== "only") {
909
1244
  if (!runtimeAvailable) {
910
1245
  // Skip runtime-only signature/default export hints when entrypoint is missing
911
- } else if (!showTypes && categorized.functions.length > 0 && categorized.functions.length <= 15) {
912
- log(`\n✍️ Function Signatures:`)
1246
+ } else if (
1247
+ !showTypes &&
1248
+ categorized.functions.length > 0 &&
1249
+ categorized.functions.length <= 15
1250
+ ) {
1251
+ log(`\n✍️ Function Signatures:`);
913
1252
  for (const fname of categorized.functions) {
914
- const descriptor = moduleDescriptors[fname]
915
- const fn = descriptor?.value
1253
+ const descriptor = moduleDescriptors[fname];
1254
+ const fn = descriptor?.value;
916
1255
  if (typeof fn === "function") {
917
- const paramCount = fn.length
918
- const params = paramCount === 0 ? "" : paramCount === 1 ? "1 param" : `${paramCount} params`
919
- log(` ${fname}(${params})`)
1256
+ const paramCount = fn.length;
1257
+ const params =
1258
+ paramCount === 0
1259
+ ? ""
1260
+ : paramCount === 1
1261
+ ? "1 param"
1262
+ : `${paramCount} params`;
1263
+ log(` ${fname}(${params})`);
920
1264
  }
921
1265
  }
922
1266
  }
923
1267
 
924
1268
  // Default export handling
925
1269
  if (runtimeAvailable) {
926
- const defaultDescriptor = moduleDescriptors.default
927
- if (defaultDescriptor && (!filter || "default".includes(filter))) {
928
- const defaultValue = defaultDescriptor.get || defaultDescriptor.set ? undefined : defaultDescriptor.value
929
- const defaultType = typeof defaultValue
930
- log(`\n📦 Default Export: ${defaultType}`)
931
- if (defaultType === "function" && defaultValue && defaultValue.length !== undefined) {
932
- log(` Parameters: ${defaultValue.length}`)
1270
+ const defaultDescriptor = moduleDescriptors.default;
1271
+ if (
1272
+ defaultDescriptor &&
1273
+ (!filterRaw || "default".toLowerCase().includes(filter))
1274
+ ) {
1275
+ const defaultValue =
1276
+ defaultDescriptor.get || defaultDescriptor.set
1277
+ ? undefined
1278
+ : defaultDescriptor.value;
1279
+ const defaultType = typeof defaultValue;
1280
+ log(`\n📦 Default Export: ${defaultType}`);
1281
+ if (
1282
+ defaultType === "function" &&
1283
+ defaultValue &&
1284
+ defaultValue.length !== undefined
1285
+ ) {
1286
+ log(` Parameters: ${defaultValue.length}`);
933
1287
  }
934
1288
  }
935
1289
  }
@@ -939,19 +1293,23 @@ export async function runInspect(options) {
939
1293
  if (showTypes || wantJsdoc) {
940
1294
  if (dtsPath && fs.existsSync(dtsPath)) {
941
1295
  if (jsdocOutput !== "only") {
942
- log(`\n🔬 Type Definitions Analysis:`)
943
- log(` Source: ${path.basename(dtsPath)}`)
1296
+ log(`\n🔬 Type Definitions Analysis:`);
1297
+ log(` Source: ${path.basename(dtsPath)}`);
944
1298
  }
945
1299
 
946
- const typeInfo = filterTypeInfo(typeInfoRaw, filter, kindFilter)
1300
+ const typeInfo = filterTypeInfo(typeInfoRaw, filter, kindFilter);
1301
+
1302
+ if (jsonOutput) {
1303
+ jsonOutput.types = typeInfo;
1304
+ }
947
1305
 
948
1306
  if (typeInfo) {
949
1307
  if (jsdocOutput !== "only") {
950
1308
  // Show function signatures with full type info
951
1309
  if (Object.keys(typeInfo.functions).length > 0) {
952
- log(`\n 📘 Function Type Signatures:`)
1310
+ log(`\n 📘 Function Type Signatures:`);
953
1311
  for (const [name, info] of Object.entries(typeInfo.functions)) {
954
- log(` ${name}(${info.params}): ${info.returnType}`)
1312
+ log(` ${name}(${info.params}): ${info.returnType}`);
955
1313
  if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
956
1314
  const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
957
1315
  mode: jsdocMode,
@@ -959,22 +1317,22 @@ export async function runInspect(options) {
959
1317
  maxLen: jsdocQuery?.maxLen,
960
1318
  sections: jsdocQuery?.sections,
961
1319
  tags: jsdocQuery?.tags,
962
- })
963
- log(` ↳ ${entry}`)
1320
+ });
1321
+ log(` ↳ ${entry}`);
964
1322
  }
965
1323
  }
966
1324
  }
967
1325
 
968
1326
  // Show interfaces
969
1327
  if (Object.keys(typeInfo.interfaces).length > 0) {
970
- log(`\n 📋 Interfaces:`)
1328
+ log(`\n 📋 Interfaces:`);
971
1329
  for (const [name, props] of Object.entries(typeInfo.interfaces)) {
972
- log(` interface ${name} {`)
973
- props.forEach((prop) => log(` ${prop}`))
1330
+ log(` interface ${name} {`);
1331
+ props.forEach((prop) => log(` ${prop}`));
974
1332
  if (props.length === 5) {
975
- log(` ... (truncated)`)
1333
+ log(` ... (truncated)`);
976
1334
  }
977
- log(` }`)
1335
+ log(` }`);
978
1336
  if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
979
1337
  const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
980
1338
  mode: jsdocMode,
@@ -982,18 +1340,21 @@ export async function runInspect(options) {
982
1340
  maxLen: jsdocQuery?.maxLen,
983
1341
  sections: jsdocQuery?.sections,
984
1342
  tags: jsdocQuery?.tags,
985
- })
986
- log(` ↳ ${entry}`)
1343
+ });
1344
+ log(` ↳ ${entry}`);
987
1345
  }
988
1346
  }
989
1347
  }
990
1348
 
991
1349
  // Show type aliases
992
1350
  if (Object.keys(typeInfo.types).length > 0) {
993
- log(`\n 📝 Type Aliases:`)
1351
+ log(`\n 📝 Type Aliases:`);
994
1352
  for (const [name, definition] of Object.entries(typeInfo.types)) {
995
- const shortDef = definition.length > 80 ? definition.substring(0, 80) + "..." : definition
996
- log(` type ${name} = ${shortDef}`)
1353
+ const shortDef =
1354
+ definition.length > 80
1355
+ ? definition.substring(0, 80) + "..."
1356
+ : definition;
1357
+ log(` type ${name} = ${shortDef}`);
997
1358
  if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
998
1359
  const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
999
1360
  mode: jsdocMode,
@@ -1001,18 +1362,22 @@ export async function runInspect(options) {
1001
1362
  maxLen: jsdocQuery?.maxLen,
1002
1363
  sections: jsdocQuery?.sections,
1003
1364
  tags: jsdocQuery?.tags,
1004
- })
1005
- log(` ↳ ${entry}`)
1365
+ });
1366
+ log(` ↳ ${entry}`);
1006
1367
  }
1007
1368
  }
1008
1369
  }
1009
1370
 
1010
1371
  // Show class inheritance
1011
1372
  if (Object.keys(typeInfo.classes).length > 0) {
1012
- log(`\n 🏛️ Class Definitions:`)
1013
- for (const [name, extendsClass] of Object.entries(typeInfo.classes)) {
1014
- const inheritance = extendsClass ? ` extends ${extendsClass}` : ""
1015
- log(` class ${name}${inheritance}`)
1373
+ log(`\n 🏛️ Class Definitions:`);
1374
+ for (const [name, extendsClass] of Object.entries(
1375
+ typeInfo.classes,
1376
+ )) {
1377
+ const inheritance = extendsClass
1378
+ ? ` extends ${extendsClass}`
1379
+ : "";
1380
+ log(` class ${name}${inheritance}`);
1016
1381
  if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
1017
1382
  const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
1018
1383
  mode: jsdocMode,
@@ -1020,17 +1385,18 @@ export async function runInspect(options) {
1020
1385
  maxLen: jsdocQuery?.maxLen,
1021
1386
  sections: jsdocQuery?.sections,
1022
1387
  tags: jsdocQuery?.tags,
1023
- })
1024
- log(` ↳ ${entry}`)
1388
+ });
1389
+ log(` ↳ ${entry}`);
1025
1390
  }
1026
1391
  }
1027
1392
  }
1028
1393
 
1029
1394
  if (Object.keys(typeInfo.enums).length > 0) {
1030
- log(`\n 🧾 Enums:`)
1395
+ log(`\n 🧾 Enums:`);
1031
1396
  for (const [name, members] of Object.entries(typeInfo.enums)) {
1032
- const preview = members.length > 0 ? ` = [${members.join(", ")}]` : ""
1033
- log(` enum ${name}${preview}`)
1397
+ const preview =
1398
+ members.length > 0 ? ` = [${members.join(", ")}]` : "";
1399
+ log(` enum ${name}${preview}`);
1034
1400
  if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
1035
1401
  const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
1036
1402
  mode: jsdocMode,
@@ -1038,16 +1404,16 @@ export async function runInspect(options) {
1038
1404
  maxLen: jsdocQuery?.maxLen,
1039
1405
  sections: jsdocQuery?.sections,
1040
1406
  tags: jsdocQuery?.tags,
1041
- })
1042
- log(` ↳ ${entry}`)
1407
+ });
1408
+ log(` ↳ ${entry}`);
1043
1409
  }
1044
1410
  }
1045
1411
  }
1046
1412
 
1047
1413
  if (Object.keys(typeInfo.namespaces).length > 0) {
1048
- log(`\n 📦 Namespaces:`)
1414
+ log(`\n 📦 Namespaces:`);
1049
1415
  for (const name of Object.keys(typeInfo.namespaces)) {
1050
- log(` namespace ${name}`)
1416
+ log(` namespace ${name}`);
1051
1417
  if (jsdocOutput === "inline" && typeInfo.jsdoc?.[name]) {
1052
1418
  const entry = formatJsdocEntry(name, typeInfo.jsdoc[name], {
1053
1419
  mode: jsdocMode,
@@ -1055,26 +1421,36 @@ export async function runInspect(options) {
1055
1421
  maxLen: jsdocQuery?.maxLen,
1056
1422
  sections: jsdocQuery?.sections,
1057
1423
  tags: jsdocQuery?.tags,
1058
- })
1059
- log(` ↳ ${entry}`)
1424
+ });
1425
+ log(` ↳ ${entry}`);
1060
1426
  }
1061
1427
  }
1062
1428
  }
1063
1429
 
1064
1430
  if (typeInfo.defaults.length > 0) {
1065
- log(`\n 📦 Default Exports:`)
1066
- typeInfo.defaults.slice(0, 5).forEach((value) => log(` default = ${value}`))
1431
+ log(`\n 📦 Default Exports:`);
1432
+ typeInfo.defaults
1433
+ .slice(0, 5)
1434
+ .forEach((value) => log(` default = ${value}`));
1067
1435
  }
1068
1436
  }
1069
1437
 
1070
- if (wantJsdoc && jsdocMode !== "off" && typeInfo.jsdoc && Object.keys(typeInfo.jsdoc).length > 0) {
1071
- const symbolMatcher = buildSymbolMatcher(jsdocQuery?.symbols, filter)
1438
+ if (
1439
+ wantJsdoc &&
1440
+ jsdocMode !== "off" &&
1441
+ typeInfo.jsdoc &&
1442
+ Object.keys(typeInfo.jsdoc).length > 0
1443
+ ) {
1444
+ const symbolMatcher = buildSymbolMatcher(
1445
+ jsdocQuery?.symbols,
1446
+ filter,
1447
+ );
1072
1448
  const entries = Object.entries(typeInfo.jsdoc)
1073
1449
  .filter(([name]) => (symbolMatcher ? symbolMatcher(name) : true))
1074
- .slice(0, 50)
1450
+ .slice(0, 50);
1075
1451
 
1076
1452
  if (entries.length > 0) {
1077
- log(`\n 📚 JSDoc:`)
1453
+ log(`\n 📚 JSDoc:`);
1078
1454
  for (const [name, doc] of entries) {
1079
1455
  const entry = formatJsdocEntry(name, doc, {
1080
1456
  mode: jsdocMode,
@@ -1082,8 +1458,8 @@ export async function runInspect(options) {
1082
1458
  maxLen: jsdocQuery?.maxLen,
1083
1459
  sections: jsdocQuery?.sections,
1084
1460
  tags: jsdocQuery?.tags,
1085
- })
1086
- log(` ${entry}`)
1461
+ });
1462
+ log(` ${entry}`);
1087
1463
  }
1088
1464
  }
1089
1465
  }
@@ -1096,19 +1472,25 @@ export async function runInspect(options) {
1096
1472
  ...Object.keys(typeInfo.classes),
1097
1473
  ...Object.keys(typeInfo.enums),
1098
1474
  ...Object.keys(typeInfo.namespaces),
1099
- ])
1475
+ ]);
1100
1476
  if (runtimeAvailable) {
1101
- const runtimeNames = new Set(allExports)
1102
- const runtimeOnly = [...runtimeNames].filter((name) => !typeExportNames.has(name))
1103
- const typesOnly = [...typeExportNames].filter((name) => !runtimeNames.has(name))
1477
+ const runtimeNames = new Set(allExports);
1478
+ const runtimeOnly = [...runtimeNames].filter(
1479
+ (name) => !typeExportNames.has(name),
1480
+ );
1481
+ const typesOnly = [...typeExportNames].filter(
1482
+ (name) => !runtimeNames.has(name),
1483
+ );
1104
1484
 
1105
1485
  if (runtimeOnly.length > 0 || typesOnly.length > 0) {
1106
- log(`\n ⚖️ Runtime/Types Mismatch:`)
1486
+ log(`\n ⚖️ Runtime/Types Mismatch:`);
1107
1487
  if (runtimeOnly.length > 0) {
1108
- log(` Runtime only: ${runtimeOnly.slice(0, 10).join(", ")}`)
1488
+ log(
1489
+ ` Runtime only: ${runtimeOnly.slice(0, 10).join(", ")}`,
1490
+ );
1109
1491
  }
1110
1492
  if (typesOnly.length > 0) {
1111
- log(` Types only: ${typesOnly.slice(0, 10).join(", ")}`)
1493
+ log(` Types only: ${typesOnly.slice(0, 10).join(", ")}`);
1112
1494
  }
1113
1495
  }
1114
1496
  }
@@ -1122,24 +1504,110 @@ export async function runInspect(options) {
1122
1504
  Object.keys(typeInfo.namespaces).length === 0 &&
1123
1505
  typeInfo.defaults.length === 0
1124
1506
  ) {
1125
- log(` ⚠️ No type definitions found for filtered exports`)
1507
+ log(` ⚠️ No type definitions found for filtered exports`);
1126
1508
  }
1127
1509
  }
1128
1510
  } else {
1129
1511
  if (jsdocOutput !== "only") {
1130
- log(` ⚠️ Could not parse type definitions`)
1512
+ log(` ⚠️ Could not parse type definitions`);
1131
1513
  }
1132
1514
  }
1133
1515
  } else {
1134
1516
  if (jsdocOutput !== "only") {
1135
- log(`\n⚠️ Type definitions not available for this package`)
1517
+ log(`\n⚠️ Type definitions not available for this package`);
1518
+ }
1519
+ }
1520
+ }
1521
+
1522
+ // Source code analysis
1523
+ if (analyzeSource && pkgDir) {
1524
+ log(`\n📝 Source Code Analysis:`);
1525
+ try {
1526
+ const sourceAnalysis = analyzePackageSource(pkgDir, {
1527
+ filter: filterRaw,
1528
+ maxFiles: sourceMaxFiles,
1529
+ includeBody: sourceIncludeBody,
1530
+ maxBodyLines: 10,
1531
+ });
1532
+
1533
+ if (sourceAnalysis.error) {
1534
+ log(` ⚠️ ${sourceAnalysis.error}`);
1535
+ } else {
1536
+ log(` Files analyzed: ${sourceAnalysis.summary.totalFiles}`);
1537
+ log(` Functions found: ${sourceAnalysis.summary.totalFunctions}`);
1538
+ log(` Avg complexity: ${sourceAnalysis.summary.avgComplexity}`);
1539
+
1540
+ if (sourceAnalysis.summary.highComplexityFunctions.length > 0) {
1541
+ log(`\n ⚠️ High Complexity Functions (≥10):`);
1542
+ for (const fn of sourceAnalysis.summary.highComplexityFunctions.slice(
1543
+ 0,
1544
+ 5,
1545
+ )) {
1546
+ log(` ${fn.name} (${fn.complexity}) - ${fn.file}`);
1547
+ }
1548
+ }
1549
+
1550
+ for (const file of sourceAnalysis.files) {
1551
+ const fnCount = Object.keys(file.functions).length;
1552
+ if (fnCount === 0) continue;
1553
+
1554
+ log(`\n 📄 ${file.path} (${fnCount} functions):`);
1555
+
1556
+ for (const [name, info] of Object.entries(file.functions)) {
1557
+ const asyncTag = info.async ? "async " : "";
1558
+ const exportTag = info.exported ? "export " : "";
1559
+ const params = info.params
1560
+ .map(
1561
+ (p) =>
1562
+ `${p.name}${p.optional ? "?" : ""}: ${p.type}${p.default ? ` = ${p.default}` : ""}`,
1563
+ )
1564
+ .join(", ");
1565
+
1566
+ log(
1567
+ ` ${exportTag}${asyncTag}${name}(${params}): ${info.returnType}`,
1568
+ );
1569
+ log(
1570
+ ` ├─ Complexity: ${info.complexity} | Lines: ${info.lines}`,
1571
+ );
1572
+
1573
+ if (info.dependencies.length > 0) {
1574
+ log(
1575
+ ` ├─ Uses: ${info.dependencies.slice(0, 5).join(", ")}${info.dependencies.length > 5 ? "..." : ""}`,
1576
+ );
1577
+ }
1578
+
1579
+ if (info.patterns.length > 0) {
1580
+ log(` └─ Patterns: ${info.patterns.join(", ")}`);
1581
+ }
1582
+
1583
+ if (info.body && sourceIncludeBody) {
1584
+ log(` 📋 Body:`);
1585
+ info.body
1586
+ .split("\n")
1587
+ .slice(0, 8)
1588
+ .forEach((line) => {
1589
+ log(` ${line}`);
1590
+ });
1591
+ }
1592
+ }
1593
+ }
1136
1594
  }
1595
+ } catch (sourceErr) {
1596
+ log(` ❌ Source analysis error: ${sourceErr.message}`);
1137
1597
  }
1138
1598
  }
1139
1599
  } catch (e) {
1140
- logErr(`\n❌ Erro: ${e.message}`)
1141
- logErr(`Certifique-se que '${target}' está instalado e é um caminho válido.`)
1600
+ const message = e instanceof Error ? e.message : String(e);
1601
+ if (jsonOutput) {
1602
+ warn(message);
1603
+ }
1604
+ logErr(`\n❌ Erro: ${message}`);
1605
+ logErr(
1606
+ `Certifique-se que '${target}' está instalado e é um caminho válido.`,
1607
+ );
1142
1608
  }
1143
1609
 
1144
- return collect ? output.join("\n") : ""
1610
+ if (format === "object") return jsonOutput;
1611
+ if (format === "json") return JSON.stringify(jsonOutput, null, 2);
1612
+ return collect ? output.join("\n") : "";
1145
1613
  }