@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.
package/dist/analysis/index.d.ts
CHANGED
|
@@ -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.
|
package/dist/analysis/index.js
CHANGED
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
|
|
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?:
|
|
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-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
1398
|
-
|
|
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 (
|
|
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
|
|
1442
|
+
const codeContent = stripCodeBlockMarkers(example);
|
|
1424
1443
|
if (!codeContent)
|
|
1425
1444
|
continue;
|
|
1426
|
-
const sourceFile = ts2.createSourceFile(
|
|
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 =
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
...
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
...
|
|
2131
|
-
|
|
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.
|
|
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": "
|
|
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",
|