@doccov/sdk 0.30.4 → 0.30.7

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.
@@ -82,6 +82,11 @@ interface ExportRegistry {
82
82
  types: Set<string>;
83
83
  /** Combined set of all names (for backward compatibility) */
84
84
  all: Set<string>;
85
+ /** Pre-computed candidate lists for fuzzy matching (performance optimization) */
86
+ callableNames: string[];
87
+ typeNames: string[];
88
+ allExportNames: string[];
89
+ allNames: string[];
85
90
  }
86
91
  /**
87
92
  * Extended drift with category and fixability metadata.
@@ -36,7 +36,7 @@ import {
36
36
  renderSparkline,
37
37
  saveReport,
38
38
  saveSnapshot
39
- } from "../shared/chunk-fdjehkbt.js";
39
+ } from "../shared/chunk-ehne5cde.js";
40
40
  import"../shared/chunk-r4wa72ae.js";
41
41
  export {
42
42
  saveSnapshot,
package/dist/index.d.ts CHANGED
@@ -87,6 +87,11 @@ interface ExportRegistry {
87
87
  types: Set<string>;
88
88
  /** Combined set of all names (for backward compatibility) */
89
89
  all: Set<string>;
90
+ /** Pre-computed candidate lists for fuzzy matching (performance optimization) */
91
+ callableNames: string[];
92
+ typeNames: string[];
93
+ allExportNames: string[];
94
+ allNames: string[];
90
95
  }
91
96
  /**
92
97
  * Extended drift with category and fixability metadata.
@@ -2632,7 +2637,7 @@ interface SpecSummary {
2632
2637
  * ```
2633
2638
  */
2634
2639
  declare function extractSpecSummary(openpkg: OpenPkg8, doccov: DocCovSpec4): SpecSummary;
2635
- import { OpenPkg as OpenPkg_wnxvpqssax } from "@openpkg-ts/spec";
2640
+ import { OpenPkg as OpenPkg_lwtdjapvef } from "@openpkg-ts/spec";
2636
2641
  /**
2637
2642
  * Build Plan types for AI-powered repository scanning.
2638
2643
  */
@@ -2729,7 +2734,7 @@ interface BuildPlanExecutionResult {
2729
2734
  /** Whether all required steps succeeded */
2730
2735
  success: boolean;
2731
2736
  /** Generated OpenPkg spec (if successful) */
2732
- spec?: OpenPkg_wnxvpqssax;
2737
+ spec?: OpenPkg_lwtdjapvef;
2733
2738
  /** Results for each step */
2734
2739
  stepResults: BuildPlanStepResult[];
2735
2740
  /** Total execution time in milliseconds */
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ import {
66
66
  saveSnapshot,
67
67
  serializeJSDoc,
68
68
  ts
69
- } from "./shared/chunk-fdjehkbt.js";
69
+ } from "./shared/chunk-ehne5cde.js";
70
70
  import {
71
71
  mergeFilters,
72
72
  parseListFlag
@@ -109,7 +109,7 @@ function findRepoRoot(startDir) {
109
109
  }
110
110
  return startDir;
111
111
  }
112
- var DEFAULT_MAX_TYPE_DEPTH = 20;
112
+ var DEFAULT_MAX_TYPE_DEPTH = 4;
113
113
  var DEFAULT_OPTIONS = {
114
114
  includePrivate: false,
115
115
  followImports: true,
@@ -226,13 +226,7 @@ async function runAnalysis(input) {
226
226
  const packageJsonPath = findNearestPackageJson(baseDir);
227
227
  const hasNodeModules = canResolveExternalModules(program, baseDir);
228
228
  const resolveExternalTypes = options.resolveExternalTypes !== undefined ? options.resolveExternalTypes : hasNodeModules;
229
- const diagnostics = ts.getPreEmitDiagnostics(context.program).filter((d) => {
230
- if (d.code === 5053)
231
- return false;
232
- const msg = ts.flattenDiagnosticMessageText(d.messageText, `
233
- `);
234
- return !/allowJs/i.test(msg);
235
- });
229
+ const diagnostics = [];
236
230
  const specDiagnostics = [];
237
231
  if (!hasNodeModules && hasExternalImports(context.sourceFile)) {
238
232
  specDiagnostics.push({
@@ -1247,10 +1247,21 @@ function findClosestMatch(source, candidates) {
1247
1247
  if (candidates.length === 0) {
1248
1248
  return;
1249
1249
  }
1250
+ const sourceLen = source.length;
1251
+ const sourceLower = source.toLowerCase();
1252
+ const sourceFirst = sourceLower[0];
1253
+ const filtered = candidates.filter((c) => {
1254
+ if (Math.abs(c.length - sourceLen) > 3)
1255
+ return false;
1256
+ if (c[0]?.toLowerCase() !== sourceFirst)
1257
+ return false;
1258
+ return true;
1259
+ });
1260
+ const toCheck = filtered.length > 0 ? filtered : candidates.slice(0, 50);
1250
1261
  const sourceWords = splitCamelCase(source);
1251
1262
  let bestMatch;
1252
1263
  let bestScore = 0;
1253
- for (const candidate of candidates) {
1264
+ for (const candidate of toCheck) {
1254
1265
  if (candidate === source)
1255
1266
  continue;
1256
1267
  const candidateWords = splitCamelCase(candidate);
@@ -1273,6 +1284,10 @@ function findClosestMatch(source, candidates) {
1273
1284
  if (matchingWords < 2)
1274
1285
  continue;
1275
1286
  const wordScore = matchingWords / Math.max(sourceWords.length, candidateWords.length);
1287
+ if (!suffixMatch && wordScore < 0.3)
1288
+ continue;
1289
+ if (suffixMatch && wordScore < 0.2)
1290
+ continue;
1276
1291
  const editDistance = levenshtein(source.toLowerCase(), candidate.toLowerCase());
1277
1292
  const maxLen = Math.max(source.length, candidate.length);
1278
1293
  const levScore = 1 - editDistance / maxLen;
@@ -1394,11 +1409,14 @@ function isIdentifierReference(node) {
1394
1409
  return false;
1395
1410
  return true;
1396
1411
  }
1397
- function detectExampleDrift(entry, registry) {
1398
- if (!registry || !entry.examples?.length)
1412
+ function stripCodeBlockMarkers(example) {
1413
+ return example.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
1414
+ }
1415
+ function detectAllExampleIssues(entry, registry) {
1416
+ if (!entry.examples?.length)
1399
1417
  return [];
1400
1418
  const drifts = [];
1401
- for (const example of entry.examples) {
1419
+ for (let i = 0;i < entry.examples.length; i++) {
1402
1420
  let visit = function(node) {
1403
1421
  if (ts2.isIdentifier(node)) {
1404
1422
  const text = node.text;
@@ -1418,12 +1436,28 @@ function detectExampleDrift(entry, registry) {
1418
1436
  }
1419
1437
  ts2.forEachChild(node, visit);
1420
1438
  };
1439
+ const example = entry.examples[i];
1421
1440
  if (typeof example !== "string")
1422
1441
  continue;
1423
- const codeContent = example.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
1442
+ const codeContent = stripCodeBlockMarkers(example);
1424
1443
  if (!codeContent)
1425
1444
  continue;
1426
- const sourceFile = ts2.createSourceFile("example.ts", codeContent, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TS);
1445
+ const sourceFile = ts2.createSourceFile(`example-${i}.ts`, codeContent, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TS);
1446
+ const parseDiagnostics = sourceFile.parseDiagnostics;
1447
+ if (parseDiagnostics && parseDiagnostics.length > 0) {
1448
+ const firstError = parseDiagnostics[0];
1449
+ const message = ts2.flattenDiagnosticMessageText(firstError.messageText, `
1450
+ `);
1451
+ drifts.push({
1452
+ type: "example-syntax-error",
1453
+ target: `example[${i}]`,
1454
+ issue: `@example contains invalid syntax: ${message}`,
1455
+ suggestion: "Check for missing brackets, semicolons, or typos."
1456
+ });
1457
+ continue;
1458
+ }
1459
+ if (!registry)
1460
+ continue;
1427
1461
  const localDeclarations = new Set;
1428
1462
  const referencedIdentifiers = new Map;
1429
1463
  visit(sourceFile);
@@ -1434,14 +1468,11 @@ function detectExampleDrift(entry, registry) {
1434
1468
  if (!registry.all.has(identifier)) {
1435
1469
  let candidates;
1436
1470
  if (context === "call") {
1437
- candidates = Array.from(registry.exports.values()).filter((e) => e.isCallable).map((e) => e.name);
1471
+ candidates = registry.callableNames;
1438
1472
  } else if (context === "type") {
1439
- candidates = [
1440
- ...Array.from(registry.types),
1441
- ...Array.from(registry.exports.values()).filter((e) => ["class", "interface", "type", "enum"].includes(e.kind)).map((e) => e.name)
1442
- ];
1473
+ candidates = registry.typeNames;
1443
1474
  } else {
1444
- candidates = Array.from(registry.exports.keys());
1475
+ candidates = registry.allExportNames;
1445
1476
  }
1446
1477
  const suggestion = findClosestMatch(identifier, candidates);
1447
1478
  const isPascal = /^[A-Z]/.test(identifier);
@@ -1459,34 +1490,6 @@ function detectExampleDrift(entry, registry) {
1459
1490
  }
1460
1491
  return drifts;
1461
1492
  }
1462
- function detectExampleSyntaxErrors(entry) {
1463
- if (!entry.examples || entry.examples.length === 0) {
1464
- return [];
1465
- }
1466
- const drifts = [];
1467
- for (let i = 0;i < entry.examples.length; i++) {
1468
- const example = entry.examples[i];
1469
- if (typeof example !== "string")
1470
- continue;
1471
- const codeContent = example.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
1472
- if (!codeContent)
1473
- continue;
1474
- const sourceFile = ts2.createSourceFile(`example-${i}.ts`, codeContent, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TS);
1475
- const parseDiagnostics = sourceFile.parseDiagnostics;
1476
- if (parseDiagnostics && parseDiagnostics.length > 0) {
1477
- const firstError = parseDiagnostics[0];
1478
- const message = ts2.flattenDiagnosticMessageText(firstError.messageText, `
1479
- `);
1480
- drifts.push({
1481
- type: "example-syntax-error",
1482
- target: `example[${i}]`,
1483
- issue: `@example contains invalid syntax: ${message}`,
1484
- suggestion: "Check for missing brackets, semicolons, or typos."
1485
- });
1486
- }
1487
- }
1488
- return drifts;
1489
- }
1490
1493
  function detectExampleRuntimeErrors(entry, runtimeResults) {
1491
1494
  if (!entry.examples || entry.examples.length === 0 || runtimeResults.size === 0) {
1492
1495
  return [];
@@ -1912,7 +1915,7 @@ function detectBrokenLinks(entry, registry) {
1912
1915
  continue;
1913
1916
  }
1914
1917
  if (!registry.all.has(rootName) && !registry.all.has(target)) {
1915
- const suggestion = findClosestMatch(rootName, Array.from(registry.all));
1918
+ const suggestion = findClosestMatch(rootName, registry.allNames);
1916
1919
  drifts.push({
1917
1920
  type: "broken-link",
1918
1921
  target,
@@ -2104,7 +2107,15 @@ function buildExportRegistry(spec) {
2104
2107
  if (type.id)
2105
2108
  all.add(type.id);
2106
2109
  }
2107
- return { exports, types, all };
2110
+ const callableNames = Array.from(exports.values()).filter((e) => e.isCallable).map((e) => e.name);
2111
+ const typeKinds = new Set(["class", "interface", "type", "enum"]);
2112
+ const typeNames = [
2113
+ ...Array.from(types),
2114
+ ...Array.from(exports.values()).filter((e) => typeKinds.has(e.kind)).map((e) => e.name)
2115
+ ];
2116
+ const allExportNames = Array.from(exports.keys());
2117
+ const allNames = Array.from(all);
2118
+ return { exports, types, all, callableNames, typeNames, allExportNames, allNames };
2108
2119
  }
2109
2120
  function computeDrift(spec) {
2110
2121
  const registry = buildExportRegistry(spec);
@@ -2116,20 +2127,23 @@ function computeDrift(spec) {
2116
2127
  return { exports };
2117
2128
  }
2118
2129
  function computeExportDrift(entry, registry) {
2119
- return [
2120
- ...detectParamDrift(entry),
2121
- ...detectOptionalityDrift(entry),
2122
- ...detectParamTypeDrift(entry),
2123
- ...detectReturnTypeDrift(entry),
2124
- ...detectGenericConstraintDrift(entry),
2125
- ...detectDeprecatedDrift(entry),
2126
- ...detectVisibilityDrift(entry),
2127
- ...detectExampleDrift(entry, registry),
2128
- ...detectBrokenLinks(entry, registry),
2129
- ...detectExampleSyntaxErrors(entry),
2130
- ...detectAsyncMismatch(entry),
2131
- ...detectPropertyTypeDrift(entry)
2132
- ];
2130
+ const hasDescription = Boolean(entry.description);
2131
+ const hasTags = (entry.tags?.length ?? 0) > 0;
2132
+ const hasExamples = (entry.examples?.length ?? 0) > 0;
2133
+ if (!hasDescription && !hasTags && !hasExamples) {
2134
+ return [];
2135
+ }
2136
+ const drifts = [];
2137
+ if (hasTags) {
2138
+ drifts.push(...detectParamDrift(entry), ...detectOptionalityDrift(entry), ...detectParamTypeDrift(entry), ...detectReturnTypeDrift(entry), ...detectGenericConstraintDrift(entry), ...detectDeprecatedDrift(entry), ...detectVisibilityDrift(entry), ...detectAsyncMismatch(entry), ...detectPropertyTypeDrift(entry));
2139
+ }
2140
+ if (hasExamples) {
2141
+ drifts.push(...detectAllExampleIssues(entry, registry));
2142
+ }
2143
+ if (hasDescription || hasTags) {
2144
+ drifts.push(...detectBrokenLinks(entry, registry));
2145
+ }
2146
+ return drifts;
2133
2147
  }
2134
2148
 
2135
2149
  // src/analysis/health.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/sdk",
3
- "version": "0.30.4",
3
+ "version": "0.30.7",
4
4
  "description": "DocCov SDK - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",
@@ -48,7 +48,7 @@
48
48
  ],
49
49
  "dependencies": {
50
50
  "@doccov/spec": "^0.27.0",
51
- "@openpkg-ts/extract": "^0.23.1",
51
+ "@openpkg-ts/extract": "0.23.2",
52
52
  "@openpkg-ts/spec": "^0.23.0",
53
53
  "@vercel/sandbox": "^1.0.3",
54
54
  "mdast": "^3.0.0",