@agentskit/doc-bridge 1.7.44 → 1.7.45
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/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +6 -4
- package/action.yml +1 -1
- package/dist/cli/program.js +378 -189
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +5 -3
- package/dist/config/index.js.map +1 -1
- package/dist/index-BUL0q7s8.d.ts +660 -0
- package/dist/index.d.ts +637 -2724
- package/dist/index.js +328 -149
- package/dist/index.js.map +1 -1
- package/docs/RELEASE.md +22 -8
- package/docs/agent-corpus/INDEX.md +2 -2
- package/docs/agent-corpus/chat.md +2 -2
- package/docs/agent-corpus/cli.md +2 -2
- package/docs/agent-corpus/conformance.md +2 -2
- package/docs/agent-corpus/doc-bridge.md +1 -1
- package/docs/agent-corpus/doctor.md +2 -2
- package/docs/agent-corpus/gates.md +2 -2
- package/docs/agent-corpus/mcp.md +2 -2
- package/docs/agent-corpus/memory.md +2 -2
- package/docs/agent-corpus/query.md +2 -2
- package/docs/knowledge-engine-runbook.md +13 -1
- package/docs/spec/benchmark-v1.md +6 -0
- package/docs/spec/config-v1.md +45 -0
- package/docs/validation-cycle-plan.md +19 -0
- package/docs/verification-harness.md +4 -0
- package/mcpb/manifest.json +1 -1
- package/package.json +68 -70
- package/scripts/check-ecosystem-upstream.mjs +3 -2
- package/scripts/report-visual-check.mjs +20 -3
- package/scripts/verification-harness.mjs +0 -1
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/cli/demo.ts +2 -2
- package/src/cli/program.ts +15 -5
- package/src/config/load-config.ts +7 -1
- package/src/config/schema.ts +4 -2
- package/src/conformance/documentation-standard-v1.ts +14 -8
- package/src/discovery/documentation.ts +44 -18
- package/src/discovery/repository.ts +77 -28
- package/src/doctor/run-doctor.ts +2 -15
- package/src/federation/llms.ts +72 -20
- package/src/fixes/proposals.ts +4 -3
- package/src/index-builder/human-adapters/fumadocs.ts +1 -1
- package/src/index-builder/watch-index.ts +1 -1
- package/src/lib/bounded-text.ts +15 -10
- package/src/reconciliation/reconcile.ts +47 -5
- package/src/report/html.ts +21 -15
- package/src/rules/engine.ts +15 -2
- package/src/safety/repository.ts +1 -1
- package/src/schemas/knowledge.ts +5 -2
- package/src/validate.ts +7 -1
- package/src/version.ts +1 -1
- package/dist/index-C2PCQSrB.d.ts +0 -2251
package/dist/index.js
CHANGED
|
@@ -198,17 +198,19 @@ var RuleIdSchema = z.enum([
|
|
|
198
198
|
var RuleSeveritySchema = z.enum(["off", "info", "warn", "error"]);
|
|
199
199
|
var RulesConfigSchema = z.object({
|
|
200
200
|
mode: z.enum(["default", "recommended", "strict"]).optional(),
|
|
201
|
-
severity: z.
|
|
201
|
+
severity: z.partialRecord(RuleIdSchema, RuleSeveritySchema).optional(),
|
|
202
202
|
ignore: z.array(RuleIdSchema).max(128).optional(),
|
|
203
203
|
criticalEntities: z.array(z.string().min(1).max(256)).max(128).optional(),
|
|
204
204
|
criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
205
|
-
warningThresholds: z.
|
|
205
|
+
warningThresholds: z.partialRecord(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
|
|
206
206
|
}).strict();
|
|
207
207
|
var ReconciliationConfigSchema = z.object({
|
|
208
208
|
/** Semantic comparison level. Raw discovery always keeps file-level relations. */
|
|
209
209
|
scope: z.enum(["file", "module", "package"]).optional(),
|
|
210
210
|
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
211
211
|
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
|
|
212
|
+
/** Limit missing-declaration findings to relations whose endpoints are internal project entities. */
|
|
213
|
+
requiredRelationTargets: z.enum(["all", "internal"]).optional(),
|
|
212
214
|
includeOrphanedDocuments: z.boolean().optional()
|
|
213
215
|
}).strict();
|
|
214
216
|
var AnalysisConfigSchema = z.object({
|
|
@@ -754,7 +756,7 @@ var parseConfig = (input) => {
|
|
|
754
756
|
throw new Error(
|
|
755
757
|
`Invalid doc-bridge config:
|
|
756
758
|
${result.error.issues.map(
|
|
757
|
-
(issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`
|
|
759
|
+
(issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.code === "invalid_type" && issue.message.endsWith("received undefined") ? "Required" : issue.code === "invalid_value" && "values" in issue ? "Invalid enum value" : issue.message}`
|
|
758
760
|
).join("\n")}`
|
|
759
761
|
);
|
|
760
762
|
};
|
|
@@ -1177,7 +1179,7 @@ var EntitySchema = z5.object({
|
|
|
1177
1179
|
aliases: z5.array(boundedString(256)).max(32).optional(),
|
|
1178
1180
|
provenance: ProvenanceSchema,
|
|
1179
1181
|
evidence: z5.array(EvidenceSchema).max(64),
|
|
1180
|
-
metadata: z5.record(z5.unknown()).optional()
|
|
1182
|
+
metadata: z5.record(z5.string(), z5.unknown()).optional()
|
|
1181
1183
|
}).strict();
|
|
1182
1184
|
var RelationSchema = z5.object({
|
|
1183
1185
|
id: boundedString(256),
|
|
@@ -1187,7 +1189,7 @@ var RelationSchema = z5.object({
|
|
|
1187
1189
|
discriminator: boundedString(256).optional(),
|
|
1188
1190
|
provenance: ProvenanceSchema,
|
|
1189
1191
|
evidence: z5.array(EvidenceSchema).max(64),
|
|
1190
|
-
metadata: z5.record(z5.unknown()).optional()
|
|
1192
|
+
metadata: z5.record(z5.string(), z5.unknown()).optional()
|
|
1191
1193
|
}).strict();
|
|
1192
1194
|
var DiscoverySnapshotV1Schema = z5.object({
|
|
1193
1195
|
type: z5.literal("discovery-snapshot"),
|
|
@@ -1233,11 +1235,14 @@ var ReconciliationReportV1Schema = z5.object({
|
|
|
1233
1235
|
diagnosticCount: z5.number().int().nonnegative(),
|
|
1234
1236
|
scope: z5.enum(["file", "module", "package"]).optional(),
|
|
1235
1237
|
requiredRelationKinds: z5.array(boundedString(128)).max(128).optional(),
|
|
1238
|
+
requiredRelationTargets: z5.enum(["all", "internal"]).optional(),
|
|
1236
1239
|
diagnosticsByCode: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
|
|
1237
1240
|
diagnosticsByStatus: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
|
|
1238
1241
|
documentation: z5.object({
|
|
1239
1242
|
documentCount: z5.number().int().nonnegative(),
|
|
1240
1243
|
documentedDocumentCount: z5.number().int().nonnegative(),
|
|
1244
|
+
documentClassificationCounts: z5.record(z5.string().max(128), z5.number().int().nonnegative()),
|
|
1245
|
+
documentedDocumentClassificationCounts: z5.record(z5.string().max(128), z5.number().int().nonnegative()),
|
|
1241
1246
|
packageCount: z5.number().int().nonnegative(),
|
|
1242
1247
|
packageStatus: z5.object({
|
|
1243
1248
|
fresh: z5.number().int().nonnegative(),
|
|
@@ -1348,9 +1353,14 @@ var FixProposalV1Schema = z5.object({
|
|
|
1348
1353
|
});
|
|
1349
1354
|
|
|
1350
1355
|
// src/validate.ts
|
|
1356
|
+
var zodMessage = (issue) => {
|
|
1357
|
+
if (issue.code === "invalid_type" && issue.message.endsWith("received undefined")) return "Required";
|
|
1358
|
+
if (issue.code === "invalid_value" && "values" in issue) return "Invalid enum value";
|
|
1359
|
+
return issue.message;
|
|
1360
|
+
};
|
|
1351
1361
|
var zodIssues = (error) => error.issues.map((issue) => ({
|
|
1352
1362
|
path: issue.path.join(".") || "(root)",
|
|
1353
|
-
message: issue
|
|
1363
|
+
message: zodMessage(issue)
|
|
1354
1364
|
}));
|
|
1355
1365
|
var safeParseAgentHandoff = (input) => {
|
|
1356
1366
|
const legacy = AgentHandoffLegacySchema.safeParse(input);
|
|
@@ -1390,7 +1400,7 @@ ${zodIssues(result.error).map((i) => ` - ${i.path}: ${i.message}`).join("\n")}`
|
|
|
1390
1400
|
|
|
1391
1401
|
// src/index-builder/build-index.ts
|
|
1392
1402
|
import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
1393
|
-
import { dirname as
|
|
1403
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
1394
1404
|
|
|
1395
1405
|
// src/lib/paths.ts
|
|
1396
1406
|
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
@@ -1474,22 +1484,27 @@ var defaultChecksForTarget = (root, opts) => {
|
|
|
1474
1484
|
import { minimatch } from "minimatch";
|
|
1475
1485
|
|
|
1476
1486
|
// src/lib/bounded-text.ts
|
|
1477
|
-
import { readFileSync as readFileSync3
|
|
1487
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync3 } from "fs";
|
|
1478
1488
|
var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
1479
1489
|
var MAX_CORPUS_BYTES = 64 * 1024 * 1024;
|
|
1480
1490
|
var readBoundedText = (path, budget, limits) => {
|
|
1481
1491
|
const maxFileBytes = limits?.maxFileBytes ?? MAX_DOCUMENT_BYTES;
|
|
1482
1492
|
const maxCorpusBytes = limits?.maxCorpusBytes ?? MAX_CORPUS_BYTES;
|
|
1483
|
-
const
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
throw new Error(`Documentation
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1493
|
+
const fd = openSync(path, "r");
|
|
1494
|
+
try {
|
|
1495
|
+
const stat = fstatSync(fd);
|
|
1496
|
+
if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
|
|
1497
|
+
if (stat.size > maxFileBytes) {
|
|
1498
|
+
throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
|
|
1499
|
+
}
|
|
1500
|
+
if (budget.used + stat.size > maxCorpusBytes) {
|
|
1501
|
+
throw new Error(`Documentation corpus exceeds the ${maxCorpusBytes} byte read budget.`);
|
|
1502
|
+
}
|
|
1503
|
+
budget.used += stat.size;
|
|
1504
|
+
return readFileSync3(fd, "utf8");
|
|
1505
|
+
} finally {
|
|
1506
|
+
closeSync(fd);
|
|
1490
1507
|
}
|
|
1491
|
-
budget.used += stat.size;
|
|
1492
|
-
return readFileSync3(path, "utf8");
|
|
1493
1508
|
};
|
|
1494
1509
|
|
|
1495
1510
|
// src/lib/markdown.ts
|
|
@@ -1913,7 +1928,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1913
1928
|
};
|
|
1914
1929
|
|
|
1915
1930
|
// src/version.ts
|
|
1916
|
-
var PACKAGE_VERSION = "1.7.
|
|
1931
|
+
var PACKAGE_VERSION = "1.7.45";
|
|
1917
1932
|
|
|
1918
1933
|
// src/index-builder/capabilities.ts
|
|
1919
1934
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -2344,7 +2359,7 @@ var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(roo
|
|
|
2344
2359
|
|
|
2345
2360
|
// src/index-builder/plugins/nx.ts
|
|
2346
2361
|
import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
|
|
2347
|
-
import { basename as basename2, dirname as
|
|
2362
|
+
import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
|
|
2348
2363
|
var NX_SCAN_SKIP = /* @__PURE__ */ new Set([
|
|
2349
2364
|
"node_modules",
|
|
2350
2365
|
".git",
|
|
@@ -2408,7 +2423,7 @@ var discoverNxProjects = (root, config) => {
|
|
|
2408
2423
|
for (const manifest of manifests) {
|
|
2409
2424
|
const json = readJsonRecord(manifest);
|
|
2410
2425
|
if (!json) continue;
|
|
2411
|
-
const manifestDir =
|
|
2426
|
+
const manifestDir = dirname2(manifest);
|
|
2412
2427
|
const manifestName = basename2(manifest);
|
|
2413
2428
|
const isProjectJson = manifestName === "project.json";
|
|
2414
2429
|
if (!isProjectJson && manifestName !== "package.json") continue;
|
|
@@ -2456,7 +2471,7 @@ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
|
2456
2471
|
import { basename as basename3, join as join6 } from "path";
|
|
2457
2472
|
|
|
2458
2473
|
// src/lib/glob-expand.ts
|
|
2459
|
-
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync
|
|
2474
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync } from "fs";
|
|
2460
2475
|
import { join as join5 } from "path";
|
|
2461
2476
|
var expandWorkspaceGlobs = (root, patterns) => {
|
|
2462
2477
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -2473,7 +2488,7 @@ var expandWorkspaceGlobs = (root, patterns) => {
|
|
|
2473
2488
|
for (const name of readdirSync2(base)) {
|
|
2474
2489
|
const abs = join5(base, name);
|
|
2475
2490
|
try {
|
|
2476
|
-
if (
|
|
2491
|
+
if (statSync(abs).isDirectory()) dirs.add(toPosix(abs));
|
|
2477
2492
|
} catch {
|
|
2478
2493
|
}
|
|
2479
2494
|
}
|
|
@@ -2583,7 +2598,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2583
2598
|
lookup
|
|
2584
2599
|
};
|
|
2585
2600
|
if (write) {
|
|
2586
|
-
mkdirSync(
|
|
2601
|
+
mkdirSync(dirname3(indexPath), { recursive: true });
|
|
2587
2602
|
writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}
|
|
2588
2603
|
`, "utf8");
|
|
2589
2604
|
}
|
|
@@ -2606,7 +2621,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2606
2621
|
const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
|
|
2607
2622
|
capabilitiesPath = join7(root, capabilitiesOut);
|
|
2608
2623
|
if (write) {
|
|
2609
|
-
mkdirSync(
|
|
2624
|
+
mkdirSync(dirname3(capabilitiesPath), { recursive: true });
|
|
2610
2625
|
writeFileSync(
|
|
2611
2626
|
capabilitiesPath,
|
|
2612
2627
|
renderCapabilitiesJson(config, index, {
|
|
@@ -2628,14 +2643,14 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2628
2643
|
// src/discovery/repository.ts
|
|
2629
2644
|
import { execFileSync } from "child_process";
|
|
2630
2645
|
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
2631
|
-
import { basename as basename4, dirname as
|
|
2646
|
+
import { basename as basename4, dirname as dirname4, extname, join as join8, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
|
|
2632
2647
|
import * as ts from "typescript";
|
|
2633
2648
|
|
|
2634
2649
|
// src/safety/repository.ts
|
|
2635
|
-
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync6, statSync as
|
|
2650
|
+
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync6, statSync as statSync2 } from "fs";
|
|
2636
2651
|
import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
|
|
2637
2652
|
import { minimatch as minimatch4 } from "minimatch";
|
|
2638
|
-
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.turbo/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
2653
|
+
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.next/**", "**/out/**", "**/.turbo/**", "**/.svelte-kit/**", "**/.mcpb-build/**", "**/.mcpb-output/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
2639
2654
|
var containedPath = (root, candidate) => {
|
|
2640
2655
|
const projectRoot = realpathSync6.native(resolve6(root));
|
|
2641
2656
|
const unresolved = resolve6(projectRoot, candidate);
|
|
@@ -2695,7 +2710,7 @@ var safeWalkFiles = (root, options = {}) => {
|
|
|
2695
2710
|
reason = `Repository scan exceeded the ${options.maxFiles ?? 1e4} file limit.`;
|
|
2696
2711
|
return;
|
|
2697
2712
|
}
|
|
2698
|
-
bytes +=
|
|
2713
|
+
bytes += statSync2(absolute).size;
|
|
2699
2714
|
if (options.maxBytes !== void 0 && bytes > options.maxBytes) {
|
|
2700
2715
|
reason = `Repository scan exceeded the ${options.maxBytes} byte limit.`;
|
|
2701
2716
|
return;
|
|
@@ -2752,6 +2767,7 @@ var firstLineContaining = (text, pattern) => {
|
|
|
2752
2767
|
};
|
|
2753
2768
|
var documentClassification = (path) => {
|
|
2754
2769
|
if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
|
|
2770
|
+
if (/(^|\/)docs-archive(?:\/|$)/.test(path)) return "archive";
|
|
2755
2771
|
if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
|
|
2756
2772
|
if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
|
|
2757
2773
|
return "unclassified";
|
|
@@ -2830,7 +2846,7 @@ var readCompilerOptions = (root) => {
|
|
|
2830
2846
|
if (!configPath) return { options: {} };
|
|
2831
2847
|
const parsed = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
2832
2848
|
if (parsed.error) return { options: {}, error: ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") };
|
|
2833
|
-
const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys,
|
|
2849
|
+
const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname4(configPath));
|
|
2834
2850
|
if (config.errors.length) {
|
|
2835
2851
|
return {
|
|
2836
2852
|
options: config.options,
|
|
@@ -2899,20 +2915,56 @@ var exportedNames = (sourceFile) => {
|
|
|
2899
2915
|
};
|
|
2900
2916
|
var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
2901
2917
|
const references = [];
|
|
2918
|
+
const dynamicEvidence = [];
|
|
2902
2919
|
let hasDynamic = false;
|
|
2903
2920
|
let hasLiteralDynamic = false;
|
|
2904
2921
|
let hasRuntimeWiring = false;
|
|
2905
2922
|
let hasUnresolvedRuntimeWiring = false;
|
|
2906
2923
|
const importedBindings = /* @__PURE__ */ new Map();
|
|
2907
2924
|
const staticStringBindings = /* @__PURE__ */ new Map();
|
|
2925
|
+
const localBindings = /* @__PURE__ */ new Set();
|
|
2926
|
+
const resolveStaticString = (expression) => {
|
|
2927
|
+
if (ts.isStringLiteralLike(expression)) return expression.text;
|
|
2928
|
+
if (ts.isIdentifier(expression)) return staticStringBindings.get(expression.text);
|
|
2929
|
+
if (ts.isParenthesizedExpression(expression)) return resolveStaticString(expression.expression);
|
|
2930
|
+
if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
2931
|
+
const left = resolveStaticString(expression.left);
|
|
2932
|
+
const right = resolveStaticString(expression.right);
|
|
2933
|
+
return left !== void 0 && right !== void 0 ? left + right : void 0;
|
|
2934
|
+
}
|
|
2935
|
+
return void 0;
|
|
2936
|
+
};
|
|
2908
2937
|
const collectStaticStringBindings = (node) => {
|
|
2909
|
-
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.
|
|
2938
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isVariableDeclarationList(node.parent) && (node.parent.flags & ts.NodeFlags.Const) !== 0) {
|
|
2939
|
+
const value = resolveStaticString(node.initializer);
|
|
2910
2940
|
const previous = staticStringBindings.get(node.name.text);
|
|
2911
|
-
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous ===
|
|
2941
|
+
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === value ? value : void 0);
|
|
2912
2942
|
}
|
|
2943
|
+
if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node)) && ts.isIdentifier(node.name)) localBindings.add(node.name.text);
|
|
2944
|
+
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)) && node.name) localBindings.add(node.name.text);
|
|
2913
2945
|
ts.forEachChild(node, collectStaticStringBindings);
|
|
2914
2946
|
};
|
|
2915
2947
|
collectStaticStringBindings(sourceFile);
|
|
2948
|
+
const addImportedBindingReference = (expression, node) => {
|
|
2949
|
+
if (ts.isIdentifier(expression)) {
|
|
2950
|
+
const specifier = importedBindings.get(expression.text);
|
|
2951
|
+
if (specifier) {
|
|
2952
|
+
addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
2953
|
+
return true;
|
|
2954
|
+
}
|
|
2955
|
+
return false;
|
|
2956
|
+
}
|
|
2957
|
+
if (ts.isPropertyAccessExpression(expression)) return addImportedBindingReference(expression.expression, node);
|
|
2958
|
+
if (ts.isCallExpression(expression)) return addImportedBindingReference(expression.expression, node);
|
|
2959
|
+
return false;
|
|
2960
|
+
};
|
|
2961
|
+
const isKnownLocal = (expression) => {
|
|
2962
|
+
if (ts.isIdentifier(expression)) return localBindings.has(expression.text) || importedBindings.has(expression.text);
|
|
2963
|
+
if (expression.kind === ts.SyntaxKind.ThisKeyword) return true;
|
|
2964
|
+
if (ts.isPropertyAccessExpression(expression)) return isKnownLocal(expression.expression);
|
|
2965
|
+
if (ts.isCallExpression(expression)) return isKnownLocal(expression.expression);
|
|
2966
|
+
return ts.isStringLiteralLike(expression);
|
|
2967
|
+
};
|
|
2916
2968
|
const addReference = (specifier, kind, node, detection) => {
|
|
2917
2969
|
references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
|
|
2918
2970
|
};
|
|
@@ -2932,32 +2984,35 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
|
2932
2984
|
importedBindings.set(node.name.text, node.moduleReference.expression.text);
|
|
2933
2985
|
} else if (ts.isCallExpression(node)) {
|
|
2934
2986
|
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
2935
|
-
|
|
2987
|
+
const specifier = node.arguments[0] ? resolveStaticString(node.arguments[0]) : void 0;
|
|
2988
|
+
if (specifier !== void 0) {
|
|
2936
2989
|
hasLiteralDynamic = true;
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2990
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
2991
|
+
addReference({ text: specifier }, "imports", node, "dynamic-literal");
|
|
2992
|
+
} else {
|
|
2993
|
+
hasDynamic = true;
|
|
2994
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
2995
|
+
}
|
|
2942
2996
|
} else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
2943
2997
|
const argument = node.arguments[0];
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2998
|
+
const specifier = argument ? resolveStaticString(argument) : void 0;
|
|
2999
|
+
if (specifier !== void 0) {
|
|
3000
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3001
|
+
addReference({ text: specifier }, "imports", node);
|
|
3002
|
+
} else {
|
|
3003
|
+
hasDynamic = true;
|
|
3004
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3005
|
+
}
|
|
2947
3006
|
} else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
|
|
2948
3007
|
hasRuntimeWiring = true;
|
|
2949
|
-
|
|
2950
|
-
let hasPotentialTargetArgument = false;
|
|
3008
|
+
let hasUnresolvedTarget = false;
|
|
2951
3009
|
for (const argument of node.arguments) {
|
|
2952
|
-
if (ts.
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
if (specifier) addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
2956
|
-
} else if (ts.isPropertyAccessExpression(argument) || ts.isCallExpression(argument)) {
|
|
2957
|
-
hasPotentialTargetArgument = true;
|
|
2958
|
-
}
|
|
3010
|
+
if (ts.isStringLiteralLike(argument)) continue;
|
|
3011
|
+
if (addImportedBindingReference(argument, node)) continue;
|
|
3012
|
+
if (!isKnownLocal(argument)) hasUnresolvedTarget = true;
|
|
2959
3013
|
}
|
|
2960
|
-
|
|
3014
|
+
const receiver = node.expression.expression;
|
|
3015
|
+
if (hasUnresolvedTarget && !isKnownLocal(receiver)) hasUnresolvedRuntimeWiring = true;
|
|
2961
3016
|
}
|
|
2962
3017
|
}
|
|
2963
3018
|
ts.forEachChild(node, visit);
|
|
@@ -2966,6 +3021,7 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
|
2966
3021
|
return {
|
|
2967
3022
|
references,
|
|
2968
3023
|
exports: exportedNames(sourceFile),
|
|
3024
|
+
dynamicEvidence,
|
|
2969
3025
|
hasDynamic,
|
|
2970
3026
|
hasLiteralDynamic,
|
|
2971
3027
|
hasRuntimeWiring,
|
|
@@ -2973,7 +3029,7 @@ var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
|
2973
3029
|
};
|
|
2974
3030
|
};
|
|
2975
3031
|
var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
|
|
2976
|
-
const base = resolve7(
|
|
3032
|
+
const base = resolve7(dirname4(containingFile), specifier);
|
|
2977
3033
|
const extension = extname(base);
|
|
2978
3034
|
const extensionlessBase = extension ? base.slice(0, -extension.length) : base;
|
|
2979
3035
|
const candidates = [
|
|
@@ -3042,11 +3098,11 @@ var artifact = (root, config, files, entities, relations, coverage) => {
|
|
|
3042
3098
|
sourceRevision: revision.value,
|
|
3043
3099
|
sourceRevisionKind: revision.kind,
|
|
3044
3100
|
configurationHash: sha256NormalizedV1(config ?? {}),
|
|
3045
|
-
pipelineVersion: "1.
|
|
3046
|
-
analyzerVersions: { repository: "1.
|
|
3101
|
+
pipelineVersion: "1.1.8",
|
|
3102
|
+
analyzerVersions: { repository: "1.1.1", "js-ts": "1.3.4" },
|
|
3047
3103
|
entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3048
3104
|
relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3049
|
-
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.
|
|
3105
|
+
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.1.1", "js-ts": "1.3.4" }[entry.analyzer] ?? "1.0.0") }))
|
|
3050
3106
|
};
|
|
3051
3107
|
return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
3052
3108
|
};
|
|
@@ -3140,6 +3196,7 @@ var discoverRepository = (opts = {}) => {
|
|
|
3140
3196
|
const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
|
|
3141
3197
|
let observedLiteralDynamic = false;
|
|
3142
3198
|
let observedUnresolvedDynamic = false;
|
|
3199
|
+
const observedDynamicEvidence = [];
|
|
3143
3200
|
let observedRuntimeWiring = false;
|
|
3144
3201
|
let observedUnresolvedRuntimeWiring = false;
|
|
3145
3202
|
for (const module of modules.values()) {
|
|
@@ -3149,6 +3206,7 @@ var discoverRepository = (opts = {}) => {
|
|
|
3149
3206
|
const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
|
|
3150
3207
|
observedLiteralDynamic ||= references.hasLiteralDynamic;
|
|
3151
3208
|
observedUnresolvedDynamic ||= references.hasDynamic;
|
|
3209
|
+
observedDynamicEvidence.push(...references.dynamicEvidence);
|
|
3152
3210
|
observedRuntimeWiring ||= references.hasRuntimeWiring;
|
|
3153
3211
|
observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
|
|
3154
3212
|
for (const reference of references.references) {
|
|
@@ -3160,10 +3218,10 @@ var discoverRepository = (opts = {}) => {
|
|
|
3160
3218
|
}
|
|
3161
3219
|
addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence], ...reference.detection ? { metadata: { detection: reference.detection } } : {} });
|
|
3162
3220
|
}
|
|
3163
|
-
if (references.hasLiteralDynamic || references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: references.hasDynamic ? "not-analyzed" : "complete", reason: references.hasDynamic ? "A non-literal dynamic import was found; the target is unresolved." : "Literal dynamic imports were resolved.", evidence: [
|
|
3221
|
+
if (references.hasLiteralDynamic || references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: references.hasDynamic ? "not-analyzed" : "complete", reason: references.hasDynamic ? "A non-literal dynamic import was found; the target is unresolved." : "Literal dynamic imports were resolved.", evidence: [...references.dynamicEvidence.slice(0, 32)] });
|
|
3164
3222
|
if (references.hasUnresolvedRuntimeWiring) coverage.push({ analyzer: "js-ts", scope: `runtime-wiring:${module.path}`, status: "not-analyzed", reason: "A runtime registration/wiring call was found without a statically imported target.", evidence: [lineEvidence("code", root, module.absPath)] });
|
|
3165
3223
|
}
|
|
3166
|
-
if (dynamicCoverageIndex >= 0) coverage[dynamicCoverageIndex] = observedUnresolvedDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "partial", reason: "Literal dynamic imports are resolved; non-literal import expressions and require calls remain unresolved." } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved." } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
|
|
3224
|
+
if (dynamicCoverageIndex >= 0) coverage[dynamicCoverageIndex] = observedUnresolvedDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "partial", reason: "Literal dynamic imports are resolved; non-literal import expressions and require calls remain unresolved. Evidence lists representative dynamic loading sites.", evidence: [...observedDynamicEvidence.slice(0, 32)] } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved.", evidence: [...observedDynamicEvidence.slice(0, 32)] } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
|
|
3167
3225
|
const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
|
|
3168
3226
|
if (runtimeCoverageIndex >= 0) coverage[runtimeCoverageIndex] = observedUnresolvedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "partial", reason: "Some configured runtime-wiring calls remain unresolved after static binding analysis." } : observedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "complete", reason: "All observed configured runtime-wiring calls resolved to static bindings." } : { analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." };
|
|
3169
3227
|
return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
|
|
@@ -3279,6 +3337,29 @@ var scalar = (value) => {
|
|
|
3279
3337
|
}
|
|
3280
3338
|
return trimmed;
|
|
3281
3339
|
};
|
|
3340
|
+
var isFieldName = (value) => {
|
|
3341
|
+
if (!/^[A-Za-z]/.test(value)) return false;
|
|
3342
|
+
for (const character of value.slice(1)) {
|
|
3343
|
+
if (!/[A-Za-z0-9_-]/.test(character)) return false;
|
|
3344
|
+
}
|
|
3345
|
+
return true;
|
|
3346
|
+
};
|
|
3347
|
+
var parseIndentedField = (raw, indentation) => {
|
|
3348
|
+
const prefix = " ".repeat(indentation);
|
|
3349
|
+
if (!raw.startsWith(prefix) || raw[indentation] === " ") return void 0;
|
|
3350
|
+
const body = raw.slice(indentation);
|
|
3351
|
+
const separator = body.indexOf(":");
|
|
3352
|
+
if (separator <= 0) return void 0;
|
|
3353
|
+
const key = body.slice(0, separator).trim();
|
|
3354
|
+
return isFieldName(key) ? { key, value: body.slice(separator + 1).trim() } : void 0;
|
|
3355
|
+
};
|
|
3356
|
+
var parseListItem = (raw, indentation) => {
|
|
3357
|
+
const prefix = `${" ".repeat(indentation)}-`;
|
|
3358
|
+
if (!raw.startsWith(prefix)) return void 0;
|
|
3359
|
+
const rest = raw.slice(prefix.length);
|
|
3360
|
+
if (rest && !/\s/.test(rest[0] ?? "")) return void 0;
|
|
3361
|
+
return rest.trim();
|
|
3362
|
+
};
|
|
3282
3363
|
var conventionalPackageReference = (path, agentRoot) => {
|
|
3283
3364
|
const prefix = `${agentRoot.replace(/\/$/, "")}/`;
|
|
3284
3365
|
if (!path.startsWith(prefix)) return void 0;
|
|
@@ -3388,11 +3469,10 @@ var parseBlock = (input, options = {}) => {
|
|
|
3388
3469
|
section = void 0;
|
|
3389
3470
|
continue;
|
|
3390
3471
|
}
|
|
3391
|
-
|
|
3472
|
+
const sectionField = parseIndentedField(raw, 2);
|
|
3473
|
+
if (sectionField) {
|
|
3392
3474
|
finishRelation();
|
|
3393
|
-
const
|
|
3394
|
-
const key = match?.[1];
|
|
3395
|
-
const value = match?.[2] ?? "";
|
|
3475
|
+
const { key, value } = sectionField;
|
|
3396
3476
|
if (key !== "covers" && key !== "relations") {
|
|
3397
3477
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown docbridge field: ${key ?? "(missing)"}.`, line);
|
|
3398
3478
|
section = void 0;
|
|
@@ -3409,35 +3489,36 @@ var parseBlock = (input, options = {}) => {
|
|
|
3409
3489
|
}
|
|
3410
3490
|
continue;
|
|
3411
3491
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3492
|
+
const listValue = parseListItem(raw, 4);
|
|
3493
|
+
if (section === "covers" && listValue !== void 0) {
|
|
3494
|
+
const value = scalar(listValue);
|
|
3414
3495
|
if (!value) addDiagnostic(diagnostics, input, "DOCBRIDGE_REFERENCE_MISSING", "covers entries must not be empty.", line);
|
|
3415
3496
|
else covers.push({ value, line });
|
|
3416
3497
|
continue;
|
|
3417
3498
|
}
|
|
3418
|
-
if (section === "relations" &&
|
|
3499
|
+
if (section === "relations" && listValue !== void 0) {
|
|
3419
3500
|
finishRelation();
|
|
3420
|
-
const firstField =
|
|
3501
|
+
const firstField = parseIndentedField(` ${listValue}`, 4);
|
|
3421
3502
|
current = { startLine: line, endLine: line, fields: /* @__PURE__ */ new Set() };
|
|
3422
|
-
if (firstField?.
|
|
3423
|
-
current.fields.add(firstField
|
|
3424
|
-
current[firstField
|
|
3425
|
-
} else if (
|
|
3503
|
+
if (firstField?.key) {
|
|
3504
|
+
current.fields.add(firstField.key);
|
|
3505
|
+
current[firstField.key] = scalar(firstField.value);
|
|
3506
|
+
} else if (listValue) {
|
|
3426
3507
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_INVALID", "Relation entries must be field mappings.", line);
|
|
3427
3508
|
}
|
|
3428
3509
|
continue;
|
|
3429
3510
|
}
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
const key = field
|
|
3511
|
+
const field = parseIndentedField(raw, 6);
|
|
3512
|
+
if (section === "relations" && current && field) {
|
|
3513
|
+
const { key, value } = field;
|
|
3433
3514
|
current.endLine = line;
|
|
3434
|
-
if (!
|
|
3435
|
-
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key
|
|
3515
|
+
if (!["from", "to", "kind", "detection"].includes(key)) {
|
|
3516
|
+
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key}.`, line);
|
|
3436
3517
|
} else if (current.fields.has(key)) {
|
|
3437
3518
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_DUPLICATE", `Duplicate relation field: ${key}.`, line);
|
|
3438
3519
|
} else {
|
|
3439
3520
|
current.fields.add(key);
|
|
3440
|
-
current[key] = scalar(
|
|
3521
|
+
current[key] = scalar(value);
|
|
3441
3522
|
}
|
|
3442
3523
|
continue;
|
|
3443
3524
|
}
|
|
@@ -3527,11 +3608,13 @@ var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
|
|
|
3527
3608
|
|
|
3528
3609
|
// src/reconciliation/reconcile.ts
|
|
3529
3610
|
var ignoredDocumentationRelations = /* @__PURE__ */ new Set(["covers"]);
|
|
3611
|
+
var isInternalEntity = (id) => !id.startsWith("external:") && !id.startsWith("unresolved:");
|
|
3530
3612
|
var metadataDetection = (relation) => {
|
|
3531
3613
|
const detection = relation.metadata?.detection;
|
|
3532
3614
|
return typeof detection === "string" ? detection : void 0;
|
|
3533
3615
|
};
|
|
3534
|
-
var
|
|
3616
|
+
var normalizeDetection = (value) => value === "dynamic-literal" ? "dynamic" : value;
|
|
3617
|
+
var relationDetection = (relation) => normalizeDetection(relation.discriminator ?? metadataDetection(relation) ?? "static");
|
|
3535
3618
|
var entityResolver = (snapshots) => {
|
|
3536
3619
|
const references = /* @__PURE__ */ new Map();
|
|
3537
3620
|
const entities = snapshots.flatMap((snapshot) => snapshot.entities).sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -3558,6 +3641,19 @@ var diagnosticCounts = (values) => {
|
|
|
3558
3641
|
for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
3559
3642
|
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
3560
3643
|
};
|
|
3644
|
+
var documentClass = (entity) => {
|
|
3645
|
+
const classification = entity.metadata?.classification;
|
|
3646
|
+
return typeof classification === "string" && classification.length > 0 ? classification : "unclassified";
|
|
3647
|
+
};
|
|
3648
|
+
var countDocumentClasses = (documents, selectedIds) => {
|
|
3649
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3650
|
+
for (const document of documents) {
|
|
3651
|
+
if (selectedIds && !selectedIds.has(document.id)) continue;
|
|
3652
|
+
const classification = documentClass(document);
|
|
3653
|
+
counts.set(classification, (counts.get(classification) ?? 0) + 1);
|
|
3654
|
+
}
|
|
3655
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
3656
|
+
};
|
|
3561
3657
|
var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
|
|
3562
3658
|
var coverageAvailable = (snapshot, relation) => {
|
|
3563
3659
|
const status = snapshot.coverage.find(
|
|
@@ -3610,7 +3706,6 @@ var aggregatedRelations = (relations, scope, entities, packageByModule) => {
|
|
|
3610
3706
|
const parts = key.split("\0");
|
|
3611
3707
|
const from = parts[0] ?? first.from;
|
|
3612
3708
|
const to = parts[1] ?? first.to;
|
|
3613
|
-
const kind = parts[2] ?? first.kind;
|
|
3614
3709
|
const detection = parts[3] ?? relationDetection(first);
|
|
3615
3710
|
const mergedEvidence = mergeEvidence(...group);
|
|
3616
3711
|
return {
|
|
@@ -3692,9 +3787,18 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
3692
3787
|
group.push(relation);
|
|
3693
3788
|
declaredByBase.set(base2, group);
|
|
3694
3789
|
}
|
|
3790
|
+
const observedDetectionsByBase = /* @__PURE__ */ new Map();
|
|
3791
|
+
for (const relation of observedRelations) {
|
|
3792
|
+
const base2 = relationBase(relation, resolveEntity2);
|
|
3793
|
+
const detections = observedDetectionsByBase.get(base2) ?? /* @__PURE__ */ new Set();
|
|
3794
|
+
detections.add(relationDetection(relation));
|
|
3795
|
+
observedDetectionsByBase.set(base2, detections);
|
|
3796
|
+
}
|
|
3695
3797
|
for (const group of declaredByBase.values()) {
|
|
3696
3798
|
const detections = new Set(group.map(relationDetection));
|
|
3697
3799
|
if (detections.size < 2) continue;
|
|
3800
|
+
const observedDetections = observedDetectionsByBase.get(relationBase(group[0], resolveEntity2));
|
|
3801
|
+
if (observedDetections && [...detections].every((detection) => observedDetections.has(detection))) continue;
|
|
3698
3802
|
diagnostics.push(reportDiagnostic(
|
|
3699
3803
|
"CONFLICTING_DECLARATIONS",
|
|
3700
3804
|
"conflict",
|
|
@@ -3721,7 +3825,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
3721
3825
|
void 0,
|
|
3722
3826
|
[relation.id, match.id]
|
|
3723
3827
|
));
|
|
3724
|
-
} else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind))) {
|
|
3828
|
+
} else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind)) && (options.requiredRelationTargets !== "internal" || relation.from !== relation.to && isInternalEntity(relation.from) && isInternalEntity(relation.to))) {
|
|
3725
3829
|
diagnostics.push(reportDiagnostic(
|
|
3726
3830
|
"RELATION_UNDOCUMENTED",
|
|
3727
3831
|
"undocumented",
|
|
@@ -3784,9 +3888,13 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
3784
3888
|
else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
|
|
3785
3889
|
else packageStatus.fresh += 1;
|
|
3786
3890
|
}
|
|
3891
|
+
const documents = observed.entities.filter((entity) => entity.kind === "document");
|
|
3892
|
+
const documentedDocumentIds = new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from));
|
|
3787
3893
|
const documentation = {
|
|
3788
|
-
documentCount:
|
|
3789
|
-
documentedDocumentCount:
|
|
3894
|
+
documentCount: documents.length,
|
|
3895
|
+
documentedDocumentCount: documentedDocumentIds.size,
|
|
3896
|
+
documentClassificationCounts: countDocumentClasses(documents),
|
|
3897
|
+
documentedDocumentClassificationCounts: countDocumentClasses(documents, documentedDocumentIds),
|
|
3790
3898
|
packageCount: packageEntities.length,
|
|
3791
3899
|
packageStatus
|
|
3792
3900
|
};
|
|
@@ -3809,6 +3917,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
3809
3917
|
diagnosticCount: sortedDiagnostics.length,
|
|
3810
3918
|
...options.scope === void 0 ? {} : { scope: options.scope },
|
|
3811
3919
|
...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
|
|
3920
|
+
...options.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: options.requiredRelationTargets },
|
|
3812
3921
|
diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
|
|
3813
3922
|
diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
|
|
3814
3923
|
documentation
|
|
@@ -3932,7 +4041,11 @@ var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
|
|
|
3932
4041
|
pipelineVersion: snapshot.pipelineVersion,
|
|
3933
4042
|
analyzerVersions: snapshot.analyzerVersions,
|
|
3934
4043
|
diagnosticCount: report.diagnostics.length,
|
|
4044
|
+
actionableCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length,
|
|
4045
|
+
confirmedCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length,
|
|
3935
4046
|
requiredRelationKinds: report.summary.requiredRelationKinds,
|
|
4047
|
+
requiredRelationTargets: report.summary.requiredRelationTargets,
|
|
4048
|
+
documentation: report.summary.documentation,
|
|
3936
4049
|
diagnosticSummary: {
|
|
3937
4050
|
errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
|
|
3938
4051
|
warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
|
|
@@ -4090,8 +4203,8 @@ styles += String.raw`
|
|
|
4090
4203
|
:root{--ink:#182b2b;--muted:#536664;--paper:#efe9df;--panel:#f8f5ee;--line:#c8c8b9;--green:#176b51;--blue:#155a68;--amber:#93530b;--red:#9b302b;--shadow:none}
|
|
4091
4204
|
*{box-sizing:border-box}body{background:var(--paper);font-family:"Avenir Next","Segoe UI",ui-sans-serif,system-ui,sans-serif;font-size:14px;line-height:1.5}.shell{max-width:1720px;padding:30px clamp(20px,4vw,64px) 56px}.masthead{align-items:flex-start;border-bottom:2px solid var(--ink);padding:4px 0 28px}.masthead>div:first-child{max-width:840px}.eyebrow{font-size:10px;letter-spacing:.16em}.masthead h1{font-family:"Iowan Old Style",Georgia,serif;font-size:clamp(3rem,7vw,7.2rem);letter-spacing:-.075em;line-height:.9;margin:16px 0 20px}.lede{font-size:17px;line-height:1.45;max-width:650px}.run-meta{padding-top:3px;min-width:210px}.read-only{border:0;border-radius:0;padding:0;background:none;color:var(--green);font-size:10px;letter-spacing:.1em;text-transform:uppercase}.run-meta strong{color:var(--ink);font-size:12px;overflow-wrap:anywhere;margin-top:16px}.summary{grid-template-columns:repeat(5,minmax(0,1fr));gap:0;margin:0;padding:18px 0 22px;border-bottom:1px solid var(--line)}.metric{border:0;border-left:1px solid var(--line);border-radius:0;background:none;box-shadow:none;padding:0 18px}.metric:first-child{border-left:0;padding-left:0}.metric b{font-family:"Iowan Old Style",Georgia,serif;font-size:38px;letter-spacing:-.05em}.metric span{font-size:11px;letter-spacing:.07em;text-transform:uppercase}.lens-bar{align-items:flex-end;margin:42px 0 6px}.tabs{gap:0}.tab,.level{border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--muted);padding:8px 14px;min-height:38px;font-weight:700}.tab:first-child,.level:first-child{padding-left:0}.tab:hover,.level:hover{background:none;color:var(--ink);border-color:var(--line)}.tab[aria-selected=true],.level[aria-pressed=true]{background:none;color:var(--ink);border-color:var(--green)}.lens-bar>.subtle{max-width:430px;text-align:right}.filters{border-block:1px solid var(--line);padding:14px 0;margin:12px 0 24px}.filters input,.filters select,.filters button{border:1px solid var(--line);border-radius:3px;background:var(--panel);min-height:38px}.filters button{font-weight:700}.insights{grid-template-columns:repeat(4,minmax(0,1fr));gap:0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin:18px 0 30px}.insight{border:0;border-left:1px solid var(--line);border-radius:0;background:none;box-shadow:none;padding:14px 16px 16px}.insight:first-child{border-left:0;padding-left:0}.insight h3{font-size:12px;letter-spacing:.02em}.insight p{font-size:13px}.tag{border-radius:2px;padding:2px 5px;font-size:9px}.workspace{grid-template-columns:minmax(0,1fr) 350px;gap:24px}.panel{border:1px solid var(--line);border-radius:3px;background:var(--panel);box-shadow:none;padding:24px}.panel h2{font-family:"Iowan Old Style",Georgia,serif;font-size:30px;letter-spacing:-.04em}.map-wrap{border:1px solid #aeb9ae;border-radius:2px;background-color:#e8eee7;background-image:linear-gradient(rgba(24,43,43,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(24,43,43,.07) 1px,transparent 1px);background-size:24px 24px}.map-wrap::-webkit-scrollbar{height:10px}.map-wrap::-webkit-scrollbar-thumb{background:var(--line)}#graph{height:620px}.graph-node rect{fill:#f8f5ee;stroke:#274744;stroke-width:1.4;rx:2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2.5}.graph-node.selected rect{fill:#dceee1;stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fff0d4;stroke:var(--amber)}.edge{stroke:#69857d;stroke-width:1.5;opacity:.8}.edge.alert{stroke:var(--amber);stroke-width:2.5}.edge-label{fill:var(--ink);font-size:10px;font-weight:700}.node-label{font-size:12px;font-weight:800}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.side{top:20px}.side .panel-head{border-bottom:1px solid var(--line);padding-bottom:14px}.detail-title{font-family:"Iowan Old Style",Georgia,serif;font-size:28px}.detail-grid{gap:0}.detail-grid>div{border-left:1px solid var(--line);padding-left:10px}.detail-grid>div:nth-child(odd){border-left:0;padding-left:0}.detail-grid b{font-family:"Iowan Old Style",Georgia,serif;font-size:26px}.run{margin-top:24px}.run-summary{border-bottom:1px solid var(--line);padding-bottom:12px}.chip{border:0;border-left:1px solid var(--line);border-radius:0;padding:2px 10px}.chip:first-child{border-left:0;padding-left:0}.finding-groups{grid-template-columns:repeat(2,minmax(0,1fr));gap:0}.finding-group{border:0;border-top:1px solid var(--line);border-radius:0;background:none;padding:16px 14px 16px 0}.finding-group:nth-child(even){padding-left:14px;border-left:1px solid var(--line)}.finding-group h3{font-size:13px;letter-spacing:.02em}.finding-detail{border-top:2px solid var(--ink)}.finding{padding:16px 0}.metadata{gap:24px;margin-top:26px}.metadata div{border-top:1px solid var(--line);padding-top:9px}.map-wrap,.panel,.finding-group,.insight{transition:background-color .16s ease,border-color .16s ease}@media(max-width:900px){.masthead{display:block}.run-meta{padding-top:24px;text-align:left}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:18px}.metric:nth-child(4){border-left:0;padding-left:0}.workspace{grid-template-columns:1fr}.side{position:static}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}@media(max-width:560px){.shell{padding:20px 16px 40px}.masthead h1{font-size:clamp(3rem,16vw,5rem)}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.metric:nth-child(odd){border-left:0;padding-left:0}.metric:nth-child(even){border-left:1px solid var(--line);padding-left:12px}.insights{grid-template-columns:1fr}.insight,.insight:first-child{border-left:0;border-top:1px solid var(--line);padding-left:0}.insight:first-child{border-top:0}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.filters label{width:100%}.filters input,.filters select{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){.map-wrap,.panel,.finding-group,.insight{transition:none}}@media(prefers-color-scheme:dark){:root{--ink:#e8eee7;--muted:#b0beb3;--paper:#131c1a;--panel:#1b2824;--line:#42534b;--green:#72d09d;--blue:#86cfe1;--amber:#f0bc6b;--red:#ff9186}.map-wrap{background-color:#17231e;background-image:linear-gradient(rgba(232,238,231,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(232,238,231,.07) 1px,transparent 1px)}.graph-node rect{fill:#1c2b25;stroke:#90aa99}.graph-node.selected rect{fill:#294936}.graph-node.issue rect{fill:#40321e}.filters input,.filters select,.filters button{background:#202f29;color:var(--ink)}}
|
|
4092
4205
|
`;
|
|
4093
|
-
styles += String.raw`.graph-node .node-label,.graph-node text.node-label{fill:var(--ink)}.graph-node .node-kind{fill:var(--muted)}.edge-label{fill:var(--ink)}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.map-wrap #graph{min-width:0!important;width:100%!important}.panel-head>.tabs{margin-top:12px}}@media(max-width:700px){.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.filters label{width:100%}.filters input,.filters select{width:100%}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}`;
|
|
4094
|
-
styles += String.raw`.
|
|
4206
|
+
styles += String.raw`.graph-node .node-label,.graph-node text.node-label{fill:var(--ink)}.graph-node .node-kind{fill:var(--muted)}.edge-label{fill:var(--ink)}.panel-head>div:first-child{min-width:0;flex:1 1 0}.panel-head>.tabs:not(.report-tabs){flex-wrap:nowrap;max-width:100%;overflow-x:auto}.panel-head>.tabs:not(.report-tabs) .level{flex:0 0 auto}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.map-wrap #graph{min-width:0!important;width:100%!important}.panel-head>.tabs{margin-top:12px}}@media(max-width:700px){.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.lens-bar{display:block}.lens-bar>.subtle{text-align:left;margin-top:10px}.filters label{width:100%}.filters input,.filters select{width:100%}.finding-groups{grid-template-columns:1fr}.finding-group:nth-child(even){padding-left:0;border-left:0}}`;
|
|
4207
|
+
styles += String.raw`.report-tabs{display:flex;gap:0;align-items:flex-end;border-bottom:1px solid var(--line);margin:24px 0 0}.report-tabs .tab{font-size:13px}.breadcrumbs{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0 0 14px;color:var(--muted);font-size:12px}.breadcrumbs button{border:0;background:none;color:var(--blue);padding:3px 0;font-weight:700}.breadcrumbs button:hover{text-decoration:underline}.breadcrumbs .current{color:var(--ink);font-weight:700}.scope-note{margin:0 0 12px}.report-dashboard{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(280px,1fr);gap:24px}.report-table{width:100%;border-collapse:collapse;font-size:12px}.report-table th,.report-table td{border-top:1px solid var(--line);padding:10px 8px;text-align:left;vertical-align:top}.report-table th{color:var(--muted);font-size:10px;letter-spacing:.08em;text-transform:uppercase}.report-table td:first-child{font-weight:750}.bar-list{display:grid;gap:10px}.bar-item{display:grid;grid-template-columns:minmax(120px,1fr) 2fr auto;gap:10px;align-items:center;font-size:12px}.bar-item i{height:8px;background:var(--line);display:block;overflow:hidden}.bar-item i b{display:block;height:100%;background:var(--green)}.map-wrap{touch-action:none;cursor:grab}.map-wrap.dragging{cursor:grabbing}@media(max-width:700px){.report-tabs{overflow:auto}.report-tabs .tab{white-space:nowrap}.report-dashboard{grid-template-columns:1fr}.bar-item{grid-template-columns:minmax(100px,1fr) 1fr auto}}`;
|
|
4095
4208
|
styles += String.raw`body[data-report-view="architecture"] #insights,body[data-report-view="architecture"] .filters,body[data-report-view="architecture"] .run,body[data-report-view="architecture"] .metadata{display:none}body[data-report-view="insights"] .workspace,body[data-report-view="insights"] .filters,body[data-report-view="insights"] .run,body[data-report-view="insights"] .metadata{display:none}body[data-report-view="findings"] .workspace,body[data-report-view="findings"] #insights,body[data-report-view="findings"] #report-dashboard,body[data-report-view="findings"] #coverage-title,body[data-report-view="findings"] #coverage-title~*{display:none}body[data-report-view="findings"] .run[aria-labelledby="coverage-title"]{display:none}body[data-report-view="coverage"] .workspace,body[data-report-view="coverage"] #insights,body[data-report-view="coverage"] #report-dashboard,body[data-report-view="coverage"] .filters,body[data-report-view="coverage"] .run[aria-labelledby="run-title"],body[data-report-view="coverage"] .metadata{display:none}body[data-report-view="insights"] #report-dashboard{display:block}body[data-report-view="map"] #report-dashboard{display:none}`;
|
|
4096
4209
|
styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
|
|
4097
4210
|
styles += String.raw`:root{--ink:#17251f;--muted:#586b63;--paper:#f2f5f1;--panel:#fbfcf9;--line:#c9d5cc;--green:#13734a;--blue:#155f78;--amber:#995d08;--red:#a52e2b}html,body{max-width:100%;overflow-x:hidden}body{font-size:15px;line-height:1.55}.shell{width:100%;max-width:1600px;margin:0 auto;padding:28px clamp(16px,3vw,48px) 56px}.masthead{gap:32px;padding:0 0 24px;align-items:flex-start}.masthead h1{font-size:clamp(2.8rem,5.5vw,5.4rem);line-height:.94;margin:12px 0 16px;max-width:100%;overflow-wrap:anywhere}.lede{max-width:60ch;font-size:16px}.run-meta{min-width:0;max-width:300px}.summary{margin:0;padding:18px 0 20px;grid-template-columns:repeat(5,minmax(0,1fr));gap:0}.metric{min-width:0;padding:0 16px;border-inline-start:1px solid var(--line)}.metric:first-child{padding-inline-start:0;border-inline-start:0}.metric b{font-size:clamp(1.8rem,3vw,3rem);max-width:100%;overflow-wrap:anywhere}.metric span{font-size:10px;letter-spacing:.06em;line-height:1.25}.lens-bar{margin:24px 0 4px;align-items:flex-end}.lens-bar>.subtle{max-width:52ch}.report-tabs{max-width:100%;overflow-x:auto}.tab,.level{min-height:40px;padding:8px 12px}.filters{max-width:100%;align-items:center}.filters label{min-width:0}.filters input,.filters select{max-width:100%;min-width:0}.workspace{min-width:0;grid-template-columns:minmax(0,1fr) minmax(280px,340px);gap:20px}.panel{min-width:0}.map-wrap{min-width:0;max-width:100%;width:100%;overflow:hidden;contain:layout paint}#graph{display:block;width:100%!important;min-width:0!important;max-width:100%;height:clamp(480px,60vh,680px)!important}.side{min-width:0}.detail-title,.path,.list,.finding,.finding-group,.report-table{min-width:0;overflow-wrap:anywhere}.report-dashboard{min-width:0}.report-table{table-layout:fixed}.report-table th,.report-table td{overflow-wrap:anywhere}.bar-item{min-width:0}.bar-item span{min-width:0;overflow-wrap:anywhere}.tab:focus-visible,.level:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid var(--blue);outline-offset:2px}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:16px}.summary .metric:nth-child(4){border-inline-start:0;padding-inline-start:0}}@media(max-width:700px){.shell{padding:12px 16px 32px}.masthead{padding-bottom:14px}.masthead h1{font-size:clamp(2.2rem,11vw,3.4rem);margin:6px 0 10px}.lede{font-size:14px;line-height:1.35}.run-meta{display:none}.summary{padding:10px 0 12px;grid-template-columns:repeat(2,minmax(0,1fr));row-gap:8px}.summary .metric:nth-child(even){border-inline-start:1px solid var(--line);padding-inline-start:12px}.summary .metric:nth-child(odd){border-inline-start:0;padding-inline-start:0}.metric{padding-inline:10px}.metric b{font-size:1.65rem}.lens-bar{display:block;margin-top:14px}.lens-bar>.subtle{display:none}.filters{display:grid;grid-template-columns:1fr;gap:10px}.filters label{display:grid;grid-template-columns:1fr;gap:5px}.filters input,.filters select,.filters button{width:100%;min-height:44px}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.finding-groups{grid-template-columns:1fr}.report-dashboard{grid-template-columns:1fr}#graph{height:520px!important}.lens-bar + #lens-caption{margin:8px 0}.lens-bar + #lens-caption + #insights{margin-top:8px}}@media(prefers-color-scheme:dark){:root{--ink:#edf5ef;--muted:#b7c6ba;--paper:#111815;--panel:#1a2821;--line:#405248;--green:#73d19d;--blue:#8bd1e6;--amber:#f0bd70;--red:#ff9389}}`;
|
|
@@ -4101,7 +4214,8 @@ styles += String.raw`#lens-caption + p.subtle{display:none}`;
|
|
|
4101
4214
|
var script = String.raw`
|
|
4102
4215
|
const data=${"${DATA}"};
|
|
4103
4216
|
data.entities=data.entities||[];data.relations=data.relations||[];data.diagnostics=data.diagnostics||[];
|
|
4104
|
-
|
|
4217
|
+
globalThis.__DOC_BRIDGE_DATA__=data;
|
|
4218
|
+
const policySuffix=data.requiredRelationKinds===undefined?"Policy: all observed relation kinds require declarations.":data.requiredRelationKinds.length?"Policy: declarations required for "+data.requiredRelationKinds.join(", ")+(data.requiredRelationTargets==="internal"?" between internal entities":"")+".":"Policy: missing relation declarations disabled; stale, conflicting, and unresolved declarations remain checked.";
|
|
4105
4219
|
const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
|
|
4106
4220
|
new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
|
|
4107
4221
|
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
|
|
@@ -4112,7 +4226,7 @@ const groupById=new Map(Object.entries(data.view?.groups||{}));
|
|
|
4112
4226
|
const packageChunkNameById=new Map([...groupById.values()].flatMap((group)=>group.members).filter((id)=>data.entities.find((entity)=>entity.id===id)?.kind==="package").sort().map((id,index)=>[id,data.view?.levelChunks?.packages?.[index]]));
|
|
4113
4227
|
const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
|
|
4114
4228
|
const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
|
|
4115
|
-
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingIndex.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=data.view?.diagnosticGroup?.[finding.id]||[];scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
|
|
4229
|
+
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingIndex.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=data.view?.diagnosticGroup?.[finding.id]||[];if(finding.status!=="confirmed")scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
|
|
4116
4230
|
const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
|
|
4117
4231
|
const isDetailChunk=(name)=>name.startsWith("chunks/details-");
|
|
4118
4232
|
const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
|
|
@@ -4129,10 +4243,10 @@ const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",
|
|
|
4129
4243
|
const ensureReportChrome=()=>{document.querySelectorAll(".tab[data-lens]").forEach((tab)=>{tab.textContent=viewLabels[tab.dataset.lens]||tab.dataset.lens;tab.closest(".tabs")?.classList.add("report-tabs")});let breadcrumbs=document.querySelector("#breadcrumbs");if(!breadcrumbs){breadcrumbs=document.createElement("nav");breadcrumbs.id="breadcrumbs";breadcrumbs.className="breadcrumbs";breadcrumbs.setAttribute("aria-label","Report location");document.querySelector(".workspace")?.before(breadcrumbs)}return breadcrumbs};
|
|
4130
4244
|
const label=(entity)=>entity?.name||entity?.id||"Unknown";
|
|
4131
4245
|
const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
|
|
4132
|
-
const renderBreadcrumbs=()=>{const breadcrumbs=ensureReportChrome(),items=[{label:"Repository",selected:"",level:"overview"}];if(state.selected){const group=groupById.get(state.selected),entity=byId.get(state.selected);if(group)items.push({label:group.name,selected:group.id,level:"package"});else if(entity){const packageId=packageOf(entity.id),packageEntity=byId.get(packageId),groupId=groupFor(packageId);if(
|
|
4246
|
+
const renderBreadcrumbs=()=>{const breadcrumbs=ensureReportChrome(),items=[{label:"Repository",selected:"",level:"overview"}];if(state.selected){const group=groupById.get(state.selected),entity=byId.get(state.selected);if(group)items.push({label:group.name,selected:group.id,level:"package"});else if(entity){const packageId=packageOf(entity.id),packageEntity=byId.get(packageId),groupId=groupFor(packageId),group=groupById.get(groupId);if(group&&groupId!==packageId&&group.name!==label(packageEntity))items.push({label:group.name,selected:groupId,level:"package"});if(packageEntity&&packageEntity.id!==entity.id)items.push({label:label(packageEntity),selected:packageEntity.id,level:"module"});items.push({label:label(entity),selected:entity.id,level:state.level})}}breadcrumbs.innerHTML=items.map((item,index)=>{const separator="<span aria-hidden=\"true\">"+(index?" / ":"")+"</span>";if(index===items.length-1)return separator+"<span class=\"current\">"+esc(item.label)+"</span>";return separator+"<button type=\"button\" data-breadcrumb-selected=\""+esc(item.selected)+"\" data-breadcrumb-level=\""+esc(item.level)+"\">"+esc(item.label)+"</button>"}).join("")};
|
|
4133
4247
|
const degreeMap=(relations)=>{const degrees=new Map();relations.forEach((relation)=>{degrees.set(relation.from,(degrees.get(relation.from)||0)+1);degrees.set(relation.to,(degrees.get(relation.to)||0)+1)});return degrees};
|
|
4134
4248
|
const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
|
|
4135
|
-
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(groupFindingIndex.get(nodeId)||[]),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
4249
|
+
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(groupFindingIndex.get(nodeId)||[]),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed").forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).filter((finding)=>finding.status!=="confirmed").forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
4136
4250
|
const findingInLens=(finding)=>state.lens==="architecture"||(state.lens==="drift"&&finding.status!=="confirmed")||(state.lens==="risks"&&(finding.severity==="error"||finding.severity==="warn"))||(state.lens==="evidence"&&finding.evidence.length>0);
|
|
4137
4251
|
const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
|
|
4138
4252
|
const relationInLens=(id)=>relationFindings(id).some(findingInLens);
|
|
@@ -4142,8 +4256,8 @@ const detailChunkName=()=>{const entity=state.selected?byId.get(state.selected):
|
|
|
4142
4256
|
const nodesFor=()=>{if(state.level==="overview")return data.view?.overview?.nodes||[];const packages=packageNodes(),selectedEntity=state.selected?byId.get(state.selected):null,groupScope=state.selected&&groupById.has(state.selected)?state.selected:null,packageScope=selectedEntity?.kind==="package"?selectedEntity.id:state.selected&&!groupScope?packageOf(state.selected):null;if(state.level==="package"&&!state.selected){const packageIds=new Set(packages.map((node)=>node.id));return packages.concat(state.lens==="architecture"?[]:externalFor(packageIds))}if((state.level==="module"||state.level==="file")&&!state.selected)return[];let nodes=data.entities.filter((entity)=>{if(state.level==="package")return entity.kind==="package"&&(!groupScope||groupFor(entity.id)===groupScope)&&(!packageScope||entity.id===packageScope);if(state.level==="module")return entity.kind==="module"&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope);return Boolean(entity.path)&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope)});if(state.level==="module"&&selectedEntity?.kind==="module"){const neighborhood=new Set([selectedEntity.id]);(relationsByEntity.get(selectedEntity.id)||[]).forEach((relation)=>{if(!["imports","re-exports"].includes(relation.kind))return;if(relation.from===selectedEntity.id)neighborhood.add(relation.to);if(relation.to===selectedEntity.id)neighborhood.add(relation.from)});nodes=data.entities.filter((entity)=>entity.kind==="module"&&neighborhood.has(entity.id))}const scopedIds=new Set(nodes.map((node)=>node.id)),external=state.lens==="architecture"?[]:externalFor(scopedIds),degrees=degreeMap(data.relations);return nodes.concat(external).sort((a,b)=>(degrees.get(b.id)||0)-(degrees.get(a.id)||0)||a.id.localeCompare(b.id)).slice(0,state.level==="file"?80:60)};
|
|
4143
4257
|
const graphModelUncached=()=>{if(state.level==="overview"){const overviewNodes=data.view?.overview?.nodes||[],overviewNodeIds=new Set(overviewNodes.map((node)=>node.id)),base=(data.view?.overview?.edges||[]).filter((edge)=>overviewNodeIds.has(edge.from)&&overviewNodeIds.has(edge.to)),edges=state.lens==="architecture"?base:base.filter((edge)=>edge.relationIds.some(relationInLens)),visible=new Set(edges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?overviewNodes:overviewNodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:edges.map((edge)=>({...edge,health:edge.relationIds.some((id)=>relationHealth(relationById.get(id)))?"error":""}))}}const nodes=nodesFor(),ids=new Set(nodes.map((node)=>node.id)),aggregate=state.level==="package"&&(!state.selected||state.selected.startsWith("group:")),edges=new Map();data.relations.forEach((relation)=>{const essential=state.level==="package"?relation.kind==="depends-on":relation.kind==="imports"||relation.kind==="re-exports";if(relation.kind==="contains"|| (state.lens==="architecture"&&!essential)||(state.lens!=="architecture"&&!relationInLens(relation.id)))return;const from=aggregate?packageOf(relation.from):relation.from,to=aggregate?packageOf(relation.to):relation.to;if(from===to||!ids.has(from)||!ids.has(to))return;const key=from+"→"+to,current=edges.get(key)||{from,to,count:0,kinds:new Set(),relationIds:new Set(),health:""};current.count++;current.kinds.add(relation.kind);current.relationIds.add(relation.id);current.health=current.health==="error"||relationHealth(relation)==="error"?"error":current.health||relationHealth(relation);edges.set(key,current)});const visibleEdges=[...edges.values()].sort((a,b)=>b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to));const visible=new Set(visibleEdges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?nodes:nodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:visibleEdges}};
|
|
4144
4258
|
const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
|
|
4145
|
-
const renderInsights=()=>{const summary=data.diagnosticSummary||{},undocumented=summary.undocumented??data.diagnostics.filter((finding)=>finding.status==="undocumented").length,drift=summary.drift??data.diagnostics.filter((finding)=>finding.status==="stale-or-unverified"||finding.status==="conflict").length,unsupported=(data.coverage||[]).filter((entry)=>entry.status==="not-analyzed"||entry.status==="partial").length,model=graphModel(),degrees=degreeMap(model.edges),values=[...degrees.values()].sort((a,b)=>a-b),median=values.length?values[Math.floor(values.length/2)]:0,hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length,isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector("#insights").innerHTML=[["Documentation drift",undocumented+drift,"Relations or docs needing comparison.",""],["Unanalyzed scope",unsupported,"Coverage gaps are explicit.",""],["Connectivity hotspots",hot,"Heuristic: unusually connected nodes.","heuristic"],["Disconnected nodes",isolated,"Heuristic: no visible edge at this level.","heuristic"]].map(([title,count,copy,tag])=>"<article class=\"insight\"><h3>"+esc(title)+" <span class=\"tag "+tag+"\">"+(tag?"heuristic":"signal")+"</span></h3><p><strong>"+count+"</strong> · "+esc(copy)+"</p></article>").join("")};
|
|
4146
|
-
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}if(!
|
|
4259
|
+
const renderInsights=()=>{const summary=data.diagnosticSummary||{},documentation=data.documentation||{},classificationCounts=documentation.documentClassificationCounts||{},documentedCounts=documentation.documentedDocumentClassificationCounts||{},agentDocs=classificationCounts.agent??0,documentedAgentDocs=documentedCounts.agent??0,undocumented=summary.undocumented??data.diagnostics.filter((finding)=>finding.status==="undocumented").length,drift=summary.drift??data.diagnostics.filter((finding)=>finding.status==="stale-or-unverified"||finding.status==="conflict").length,unsupported=(data.coverage||[]).filter((entry)=>entry.status==="not-analyzed"||entry.status==="partial").length,model=graphModel(),degrees=degreeMap(model.edges),values=[...degrees.values()].sort((a,b)=>a-b),median=values.length?values[Math.floor(values.length/2)]:0,hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length,isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector("#insights").innerHTML=[["Documentation drift",undocumented+drift,"Relations or docs needing comparison.",""],["Agent documentation",documentedAgentDocs+"/"+agentDocs,"Configured agent-corpus documents linked to knowledge.",""],["Unanalyzed scope",unsupported,"Coverage gaps are explicit.",""],["Connectivity hotspots",hot,"Heuristic: unusually connected nodes.","heuristic"],["Disconnected nodes",isolated,"Heuristic: no visible edge at this level.","heuristic"]].map(([title,count,copy,tag])=>"<article class=\"insight\"><h3>"+esc(title)+" <span class=\"tag "+tag+"\">"+(tag?"heuristic":"signal")+"</span></h3><p><strong>"+count+"</strong> · "+esc(copy)+"</p></article>").join("")};
|
|
4260
|
+
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}const actionable=data.diagnostics.filter((finding)=>finding.status!=="confirmed");if(!actionable.length){dashboard.innerHTML="<p class=\"empty\">No actionable findings. Confirmed checks are available in Evidence.</p>";return}const degrees=degreeMap(data.relations),groups=[...groupById.entries()].filter(([,group])=>group.kind!=="external"),rows=groups.map(([id,group])=>{const members=groupEntityIds.get(id)||new Set(),modules=group.moduleCount||[...members].filter((entityId)=>byId.get(entityId)?.kind==="module").length;return{name:group.name,packages:group.members.length,modules,findings:groupFindingCounts.get(id)||0}}).sort((a,b)=>b.findings-a.findings||a.name.localeCompare(b.name)),topPackages=[...data.entities].filter((entity)=>entity.kind==="package").map((entity)=>({name:label(entity),degree:degrees.get(entity.id)||0})).sort((a,b)=>b.degree-a.degree||a.name.localeCompare(b.name)).slice(0,10),max=Math.max(1,...topPackages.map((row)=>row.degree));dashboard.innerHTML="<section><div class=\"eyebrow\">Attention by scope</div><h2>Where the map needs a closer look</h2><table class=\"report-table\"><thead><tr><th>Scope</th><th>Packages</th><th>Modules</th><th>Findings</th></tr></thead><tbody>"+rows.map((row)=>"<tr><td>"+esc(row.name)+"</td><td>"+row.packages+"</td><td>"+row.modules+"</td><td><strong>"+row.findings+"</strong></td></tr>").join("")+"</tbody></table></section><section><div class=\"eyebrow\">Connectivity</div><h2>Most connected packages</h2><div class=\"bar-list\">"+topPackages.map((row)=>"<div class=\"bar-item\"><span>"+esc(row.name)+"</span><i><b style=\"width:"+Math.round(row.degree/max*100)+"%\"></b></i><strong>"+row.degree+"</strong></div>").join("")+"</div></section>"};
|
|
4147
4261
|
const mapState={scale:1,dragging:false,x:0,y:0};
|
|
4148
4262
|
const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;const dense=svg.closest(".map-wrap")?.classList.contains("dense");svg.setAttribute("preserveAspectRatio",dense?"xMinYMin meet":"xMidYMid meet");svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
|
|
4149
4263
|
const routeGraphEdges=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.querySelectorAll("line.edge").forEach((line,index)=>{const x1=Number(line.getAttribute("x1")),y1=Number(line.getAttribute("y1")),x2=Number(line.getAttribute("x2")),y2=Number(line.getAttribute("y2")),midX=(x1+x2)/2+((index%5)-2)*14,path=document.createElementNS("http://www.w3.org/2000/svg","path");path.setAttribute("class",line.getAttribute("class")||"edge");path.setAttribute("fill","none");path.setAttribute("marker-end",line.getAttribute("marker-end")||"url(#arrow)");path.setAttribute("d","M "+x1+" "+y1+" H "+midX+" V "+y2+" H "+x2);const label=line.parentElement?.querySelector("text.edge-label");if(label){label.setAttribute("x",String(midX+3));label.setAttribute("y",String((y1+y2)/2))}line.replaceWith(path)})};
|
|
@@ -4152,16 +4266,16 @@ let nodeClickTimer;
|
|
|
4152
4266
|
document.addEventListener("dblclick",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);enterNode(node.dataset.node)},true);
|
|
4153
4267
|
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);nodeClickTimer=setTimeout(async()=>{state.selected=node.dataset.node;render();const chunk=detailChunkName();if(chunk&&!hasChunk(chunk)){await loadChunk(chunk);render()}},380)},true);
|
|
4154
4268
|
document.addEventListener("click",(event)=>{const crumb=event.target.closest?.("[data-breadcrumb-level]");if(!crumb)return;event.preventDefault();event.stopImmediatePropagation();state.level=crumb.dataset.breadcrumbLevel;state.selected=crumb.dataset.breadcrumbSelected||null;mapState.scale=1;mapState.x=0;mapState.y=0;render()},true);
|
|
4155
|
-
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
4269
|
+
const diagnosticsFor=(id)=>(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed");
|
|
4156
4270
|
const renderDetails=()=>{const panel=document.querySelector("#details"),entity=state.selected?byId.get(state.selected):null,group=state.selected?groupById.get(state.selected):null;if(group){const members=group.members.map((id)=>byId.get(id)).filter(Boolean),relations=new Set(),ids=groupEntityIds.get(state.selected)||new Set();ids.forEach((id)=>(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation)));const findings=nodeIssues(state.selected),evidence=members.flatMap((member)=>member.evidence||[]).slice(0,8),unit=group.kind==="external"?"dependencies":"packages";panel.innerHTML="<div class=\"eyebrow\">Selected group</div><h2 class=\"detail-title\">"+esc(group.name)+"</h2><span class=\"tag\">"+esc(group.kind)+"</span><p class=\"path\">"+esc(group.path||"Derived from stable package identity and repository structure")+"</p><div class=\"detail-grid\"><div><b>"+members.length+"</b><span>"+unit+"</span></div><div><b>"+relations.size+"</b><span>relations</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Members</h3><ul class=\"list\">"+(members.length?members.slice(0,8).map((member)=>"<li>"+esc(label(member))+"</li>").join(""):"<li>No members recorded.</li>")+(members.length>8?"<li class=\"subtle\">+"+(members.length-8)+" more — choose Package level to inspect all.</li>":"")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"");return}if(!entity){panel.innerHTML="<p class=\"subtle\">Select a node in the map to inspect its evidence, connectivity, and findings.</p>";return}const relations=relationsByEntity.get(entity.id)||[],incoming=relations.filter((relation)=>relation.to===entity.id),outgoing=relations.filter((relation)=>relation.from===entity.id),findings=diagnosticsFor(entity.id),evidence=[...(entity.evidence||[]),...incoming.flatMap((relation)=>relation.evidence||[]),...outgoing.flatMap((relation)=>relation.evidence||[])].slice(0,8);panel.innerHTML="<div class=\"eyebrow\">Selected entity</div><h2 class=\"detail-title\">"+esc(label(entity))+"</h2><span class=\"tag\">"+esc(entity.kind)+"</span><p class=\"path\">"+esc(entity.path||entity.id)+"</p><div class=\"detail-grid\"><div><b>"+incoming.length+"</b><span>incoming</span></div><div><b>"+outgoing.length+"</b><span>outgoing</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Evidence</h3><ul class=\"list\">"+(evidence.length?evidence.map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join(""):"<li>No evidence recorded.</li>")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"")};
|
|
4157
4271
|
const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph"),dense=model.edges.length>64,edges=dense?[...model.edges].sort((a,b)=>(b.health?1:0)-(a.health?1:0)||b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to)).slice(0,64):model.edges;if(!model.nodes.length){svg.innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>";return}const ids=new Set(model.nodes.map((node)=>node.id)),incoming=new Map(model.nodes.map((node)=>[node.id,0])),outgoing=new Map(model.nodes.map((node)=>[node.id,[]]));edges.forEach((edge)=>{if(!ids.has(edge.from)||!ids.has(edge.to))return;incoming.set(edge.to,(incoming.get(edge.to)||0)+1);outgoing.get(edge.from).push(edge.to)});const rank=new Map(),work=[];model.nodes.filter((node)=>(incoming.get(node.id)||0)===0).sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id)).forEach((node)=>{rank.set(node.id,0);work.push(node)});for(let index=0;index<work.length;index++){const node=work[index];for(const next of outgoing.get(node.id)||[]){const nextRank=Math.max(rank.get(next)||0,(rank.get(node.id)||0)+1);rank.set(next,nextRank);if(!work.some((item)=>item.id===next))work.push(byId.get(next)||{id:next,name:next})}}model.nodes.forEach((node)=>{if(!rank.has(node.id))rank.set(node.id,0)});const columns=new Map();model.nodes.forEach((node)=>{const items=columns.get(rank.get(node.id))||[];items.push(node);columns.set(rank.get(node.id),items)});for(const items of columns.values())items.sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id));if(dense){columns.clear();model.nodes.forEach((node,index)=>{const column=Math.floor(index/8),items=columns.get(column)||[];items.push(node);columns.set(column,items)})}const maxRows=Math.max(1,...[...columns.values()].map((items)=>items.length)),cellWidth=190,cellHeight=82,pad=28,columnCount=Math.max(1,...columns.keys())+1,width=Math.max(620,columnCount*cellWidth+pad*2,Math.min(1000,svg.clientWidth*1.05)),height=Math.max(520,maxRows*cellHeight+pad*2);svg.setAttribute("viewBox","0 0 "+width+" "+height);svg.style.width=dense?Math.max(1000,width)+"px":"100%";svg.style.minWidth="0";svg.style.height="100%";document.querySelector(".map-wrap")?.classList.toggle("dense",dense);const positions=new Map();for(const [column,items] of columns)items.forEach((node,row)=>positions.set(node.id,{x:pad+column*cellWidth+78,y:pad+row*cellHeight+27}));const defs="<defs><marker id=\"arrow\" markerWidth=\"8\" markerHeight=\"8\" refX=\"7\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L0,6 L7,3 z\" fill=\"#789087\"/></marker></defs>",edgeMarkup=edges.map((edge)=>{const from=positions.get(edge.from),to=positions.get(edge.to);if(!from||!to)return "";const midX=(from.x+to.x)/2,midY=(from.y+to.y)/2;return "<g><line class=\"edge "+(edge.health?"alert":"")+"\" x1=\""+from.x+"\" y1=\""+from.y+"\" x2=\""+to.x+"\" y2=\""+to.y+"\" marker-end=\"url(#arrow)\"/><text class=\"edge-label\" x=\""+midX+"\" y=\""+midY+"\">"+esc(edge.count>1?edge.count+"×":"")+"</text></g>"}).join(""),nodes=model.nodes.map((node)=>{const pos=positions.get(node.id),issues=nodeIssues(node.id),degree=degreeMap(edges).get(node.id)||0,selected=state.selected===node.id?" selected":"",issue=issues.length?" issue":"",nodeLabel=label(node),nodeKind=node.kind==="domain"?(node.memberCount||0)+" packages":node.kind;return "<g class=\"graph-node"+selected+issue+"\" transform=\"translate("+(pos.x-78)+","+(pos.y-27)+")\"><title>"+esc(nodeLabel)+" · "+esc(nodeKind)+"</title><rect width=\"156\" height=\"54\" rx=\"8\" role=\"button\" tabindex=\"0\" aria-label=\""+esc(nodeLabel+" "+nodeKind)+"\" data-node=\""+esc(node.id)+"\"></rect><text class=\"node-label\" x=\"10\" y=\"19\">"+esc(short(nodeLabel,22))+"</text><text class=\"node-kind\" x=\"10\" y=\"34\">"+esc(short(nodeKind,22))+"</text><text class=\"node-count\" x=\"144\" y=\"19\" text-anchor=\"end\">"+(degree||"")+"</text></g>"}).join("");svg.innerHTML=defs+edgeMarkup+nodes;routeGraphEdges();applyMapTransform();document.querySelector("#map-note").textContent=(state.level==="overview"?"Bounded domain view. ":state.level+" view. ")+"Directional edges are aggregated from canonical relations; "+model.edges.length+" visible connection groups"+(dense?" (showing "+edges.length+" prioritized of "+model.edges.length+").":".");};
|
|
4158
4272
|
const findingMatches=(finding)=>{const query=state.query.toLowerCase();return(!query||[finding.id,finding.code,finding.message,...finding.entityIds,...finding.relationIds].join(" ").toLowerCase().includes(query))&&(!state.status||finding.status===state.status)&&(!state.severity||finding.severity===state.severity)};
|
|
4159
4273
|
const findingScope=(finding)=>data.view?.diagnosticGroup?.[finding.id]?.[0]||(()=>{const ids=[...(finding.entityIds||[])];(finding.relationIds||[]).forEach((id)=>{const relation=relationById.get(id);if(relation)ids.push(relation.from,relation.to)});return ids.map(groupFor).sort()[0]||"repository"})();
|
|
4160
4274
|
const findingGroups=(findings)=>{const groups=new Map();findings.forEach((finding)=>{const scope=findingScope(finding),key=[scope,finding.code,finding.status,finding.severity].join("|"),group=groups.get(key)||{key,scope,code:finding.code,status:finding.status,severity:finding.severity,findings:[]};group.findings.push(finding);groups.set(key,group)});return[...groups.values()].sort((left,right)=>right.findings.length-left.findings.length||left.code.localeCompare(right.code)||left.key.localeCompare(right.key))};
|
|
4161
4275
|
const renderFinding=(finding)=>"<article class=\"finding\" id=\""+esc("diagnostic-"+finding.id.replace(/[^A-Za-z0-9_-]+/g,"-"))+"\"><div class=\"finding-head\"><h3>"+esc(finding.code)+"</h3><span><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> <span class=\"tag\">"+esc(finding.status)+"</span></span></div><p>"+esc(finding.message)+"</p>"+(finding.evidence.length?"<ul>"+finding.evidence.slice(0,4).map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join("")+"</ul>":"")+(finding.remediation?"<p><strong>Next check:</strong> "+esc(finding.remediation)+"</p>":"")+"</article>";
|
|
4162
|
-
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==="risks")findings=findings.filter((finding)=>finding.severity==="error"||finding.severity==="warn");if(state.lens==="evidence")findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==="drift")findings=findings.filter((finding)=>finding.status!=="confirmed");const groups=findingGroups(findings),active=groups.find((group)=>group.key===state.findingGroup);if(!active){state.findingGroup=null;state.findingPage=0}syncHash();const selected=active||null,pageSize=40,pageCount=selected?Math.ceil(selected.findings.length/pageSize):0,page=Math.max(0,Math.min(state.findingPage,Math.max(0,pageCount-1))),start=page*pageSize;document.querySelector("#finding-count").textContent=findings.length+"
|
|
4276
|
+
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==="risks")findings=findings.filter((finding)=>finding.severity==="error"||finding.severity==="warn");if(state.lens==="evidence")findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==="drift")findings=findings.filter((finding)=>finding.status!=="confirmed");const itemLabel=state.lens==="evidence"?"evidence checks":"findings",groups=findingGroups(findings),active=groups.find((group)=>group.key===state.findingGroup);if(!active){state.findingGroup=null;state.findingPage=0}syncHash();const selected=active||null,pageSize=40,pageCount=selected?Math.ceil(selected.findings.length/pageSize):0,page=Math.max(0,Math.min(state.findingPage,Math.max(0,pageCount-1))),start=page*pageSize;document.querySelector("#finding-count").textContent=findings.length+" "+itemLabel+" · "+groups.length+" groups";document.querySelector("#findings").innerHTML=findings.length?"<div class=\"finding-groups\">"+groups.map((group)=>"<article class=\"finding-group\"><div class=\"finding-head\"><h3>"+esc(group.code)+"</h3><span><span class=\"tag "+esc(group.severity)+"\">"+esc(group.severity)+"</span> <span class=\"tag\">"+esc(group.status)+"</span></span></div><p><strong>"+group.findings.length+"</strong> "+itemLabel+" · "+esc(groupById.get(group.scope)?.name||group.scope)+"</p><p>"+esc(group.findings[0].message)+"</p><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(group.key)+"\" aria-expanded=\""+String(selected?.key===group.key)+"\">Inspect group</button></article>").join("")+"</div>"+(selected?"<div class=\"finding-detail\"><div class=\"finding-head\"><h3>"+esc(selected.code)+" · "+esc(groupById.get(selected.scope)?.name||selected.scope)+"</h3><span class=\"subtle\">"+selected.findings.length+" "+itemLabel+"</span></div>"+selected.findings.slice(start,start+pageSize).map(renderFinding).join("")+"<div class=\"finding-actions\"><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(selected.key)+"\" data-finding-page=\""+(page-1)+"\" "+(page===0?"disabled":"")+">Previous</button><span class=\"subtle\">Showing "+(start+1)+"–"+Math.min(start+pageSize,selected.findings.length)+" of "+selected.findings.length+" · page "+(page+1)+"/"+pageCount+"</span><button class=\"tab\" type=\"button\" data-finding-group=\""+esc(selected.key)+"\" data-finding-page=\""+(page+1)+"\" "+(page+1>=pageCount?"disabled":"")+">Next</button></div></div>":""):"<p class=\"empty\">No "+itemLabel+" match the current lens and filters.</p>"};
|
|
4163
4277
|
const renderCoverage=()=>{document.querySelector("#coverage-list").innerHTML=(data.coverage||[]).map((entry)=>{const width=entry.status==="complete"?100:entry.status==="partial"?55:12;return"<div class=\"coverage-row\"><div><b>"+esc(entry.analyzer)+"</b><br><span class=\"subtle\">"+esc(entry.scope)+"</span></div><div class=\"bar\"><i class=\""+(entry.status==="complete"?"":entry.status==="partial"?"partial":"none")+"\" style=\"width:"+width+"%\"></i></div><div>"+esc(entry.status)+"</div></div>"}).join("")||"<p class=\"empty\">No coverage metadata.</p>"};
|
|
4164
|
-
const deferFindings=()=>{if(findingsLoaded())return;const
|
|
4278
|
+
const deferFindings=()=>{if(findingsLoaded())return;const actionable=data.actionableCount??data.diagnosticCount??0,confirmed=data.confirmedCount??0;document.querySelector("#finding-count").textContent=actionable+" actionable findings · "+confirmed+" confirmed checks available on demand";document.querySelector("#findings").innerHTML="<div class=\"empty\"><p>Findings stay out of the first paint so large repositories remain responsive. Confirmed checks are evidence, not issues.</p><button id=\"load-findings\" class=\"tab\" type=\"button\">Load findings</button></div>"};
|
|
4165
4279
|
const scopePrompt=()=>{if((state.level==="module"||state.level==="file")&&!state.selected){document.querySelector("#map-note").textContent="Select an app or package to inspect this level.";document.querySelector("#graph").innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>"}};
|
|
4166
4280
|
const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
|
|
4167
4281
|
const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
|
|
@@ -4197,11 +4311,12 @@ var render = (input, options) => {
|
|
|
4197
4311
|
const data = reportData(snapshot, report, includeSnippets, options.privacy);
|
|
4198
4312
|
const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
|
|
4199
4313
|
const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1e3 ? "Large snapshots are rendered from compact canonical data with progressive graph levels." : "The viewer starts with a grouped topology and expands into canonical entities on demand.";
|
|
4200
|
-
const
|
|
4314
|
+
const actionableCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length;
|
|
4315
|
+
const confirmedCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length;
|
|
4201
4316
|
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
4202
|
-
const policyNote = report.summary.requiredRelationKinds === void 0 ? "Missing relation declarations are checked for every observed relation kind." : report.summary.requiredRelationKinds.length ? `Missing relation declarations are checked for: ${report.summary.requiredRelationKinds.join(", ")}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
|
|
4317
|
+
const policyNote = report.summary.requiredRelationKinds === void 0 ? "Missing relation declarations are checked for every observed relation kind." : report.summary.requiredRelationKinds.length ? `Missing relation declarations are checked for: ${report.summary.requiredRelationKinds.join(", ")}${report.summary.requiredRelationTargets === "internal" ? " between internal entities" : ""}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
|
|
4203
4318
|
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
4204
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(data.project.name)}</title><style>${styles}</style></head><body>${options.dataScripts ?? ""}<main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(data.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(data.revision)}</strong><span>${escapeHtml(data.revisionKind)} \xB7 pipeline ${escapeHtml(data.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${
|
|
4319
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(data.project.name)}</title><style>${styles}</style></head><body>${options.dataScripts ?? ""}<main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(data.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(data.revision)}</strong><span>${escapeHtml(data.revisionKind)} \xB7 pipeline ${escapeHtml(data.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${actionableCount ? "warn" : ""}"><b>${actionableCount}</b><span>actionable findings</span></div><div class="metric"><b>${confirmedCount}</b><span>confirmed checks</span></div><div class="metric ${unsupportedCount ? "bad" : ""}"><b>${unsupportedCount}</b><span>partial / unanalyzed scopes</span></div></section><div class="lens-bar"><nav class="tabs" aria-label="Report lenses"><button class="tab" data-lens="architecture" aria-selected="true">Architecture</button><button class="tab" data-lens="drift" aria-selected="false">Documentation drift</button><button class="tab" data-lens="risks" aria-selected="false">Risks & hotspots</button><button class="tab" data-lens="evidence" aria-selected="false">Evidence</button></nav><span class="subtle">${escapeHtml(largeNote)}</span></div><p id="lens-caption" class="subtle">The repository topology at the selected level.</p><p class="subtle">${escapeHtml(policyNote)}</p><section id="insights" class="insights" aria-label="Attention signals"></section><section class="filters" aria-label="Finding filters"><label>Search <input id="search" type="search" placeholder="Press / to search findings"></label><label>Status <select id="status"><option value="">Any status</option>${statusOptions.map((value) => `<option>${escapeHtml(value)}</option>`).join("")}</select></label><label>Severity <select id="severity"><option value="">Any severity</option><option>error</option><option>warn</option><option>info</option></select></label><button id="reset" type="button">Reset filters</button></section><div class="workspace"><section class="panel" aria-labelledby="architecture-title"><div class="panel-head"><div><h2 id="architecture-title">Architecture map</h2><p id="map-note" class="subtle">Grouped package/external view.</p></div><div class="tabs" aria-label="Graph level"><button class="level" data-level="overview" aria-pressed="true">Overview</button><button class="level" data-level="package" aria-pressed="false">Package</button><button class="level" data-level="module" aria-pressed="false">Module</button><button class="level" data-level="file" aria-pressed="false">File</button></div></div><div class="map-wrap"><svg id="graph" viewBox="0 0 1000 560" role="img" aria-label="Interactive architecture graph"></svg></div><p class="subtle">Node number = visible connection degree. Amber nodes/edges have actionable findings. Click or focus a node to inspect its evidence, connectivity, and findings. Grouped edges preserve canonical relation direction and count.</p></section><aside class="panel side" aria-labelledby="details-title"><div class="panel-head"><div><div class="eyebrow">Evidence trail</div><h2 id="details-title">Details</h2></div><button id="clear-selection" class="tab" type="button">Clear</button></div><div id="details"><p class="subtle">Select a node in the map to inspect its evidence, connectivity, and findings.</p></div></aside></div><section class="panel run" aria-labelledby="run-title"><div class="panel-head"><div><div class="eyebrow">Jest-like diagnostics</div><h2 id="run-title">Run report</h2></div><span id="finding-count" class="subtle">${actionableCount} actionable findings \xB7 ${confirmedCount} confirmed checks</span></div><div class="run-summary"><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "error").length}</b> errors</span><span class="chip"><b>${report.diagnostics.filter((item) => item.severity === "warn").length}</b> warnings</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "undocumented").length}</b> undocumented</span><span class="chip"><b>${report.diagnostics.filter((item) => item.status === "stale-or-unverified" || item.status === "conflict").length}</b> drift/conflict</span><span class="chip"><b>${confirmedCount}</b> confirmed checks</span></div><div id="findings"></div></section><section class="panel run" aria-labelledby="coverage-title"><div class="panel-head"><div><div class="eyebrow">Analyzer boundaries</div><h2 id="coverage-title">Coverage & unsupported areas</h2></div><span class="subtle">Explicit limits are part of the evidence</span></div><div id="coverage-list"></div></section><section class="metadata" aria-label="Run metadata"><div><b>Snapshot</b>${escapeHtml(data.snapshotHash)}</div><div><b>Report</b>${escapeHtml(data.reportHash)}</div><div><b>Configuration</b>${escapeHtml(data.configurationHash)}</div><div><b>Analyzers</b>${escapeHtml(Object.entries(data.analyzerVersions).map(([name, version]) => `${name} ${version}`).join(", "))}</div><div><b>Source revision</b>${escapeHtml(data.revision)} (${escapeHtml(data.revisionKind)})</div><div><b>Mode</b>${options.privacy === "anonymized" ? "Anonymized read-only browser viewer; evidence paths and project identity are redacted." : "Read-only browser viewer; approvals and fixes remain outside this artifact."}</div></section></main><script>${scriptBody}</script></body></html>`;
|
|
4205
4320
|
};
|
|
4206
4321
|
var parseReportInput = (input) => {
|
|
4207
4322
|
if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
|
|
@@ -4314,7 +4429,7 @@ var renderOfflineReportArtifact = (input, options = {}) => {
|
|
|
4314
4429
|
diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
|
|
4315
4430
|
};
|
|
4316
4431
|
const files = {
|
|
4317
|
-
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, diagnosticSummary: data.diagnosticSummary, requiredRelationKinds: data.requiredRelationKinds, entities: packageEntities, relations: packageRelations, view: overviewView }),
|
|
4432
|
+
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, actionableCount: data.actionableCount, confirmedCount: data.confirmedCount, diagnosticSummary: data.diagnosticSummary, documentation: data.documentation, requiredRelationKinds: data.requiredRelationKinds, entities: packageEntities, relations: packageRelations, view: overviewView }),
|
|
4318
4433
|
...levelFiles,
|
|
4319
4434
|
...detailFiles,
|
|
4320
4435
|
"chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
|
|
@@ -4327,8 +4442,8 @@ var renderOfflineReportArtifact = (input, options = {}) => {
|
|
|
4327
4442
|
};
|
|
4328
4443
|
|
|
4329
4444
|
// src/fixes/proposals.ts
|
|
4330
|
-
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync7, renameSync,
|
|
4331
|
-
import { basename as basename5, dirname as
|
|
4445
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync10, realpathSync as realpathSync7, renameSync, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
4446
|
+
import { basename as basename5, dirname as dirname5, extname as extname2, join as join10, relative as relative6, resolve as resolve9, sep as sep7 } from "path";
|
|
4332
4447
|
var hash2 = (value) => sha256NormalizedV1(value);
|
|
4333
4448
|
var artifactMetadata = (root, options) => ({
|
|
4334
4449
|
schemaVersion: 1,
|
|
@@ -4384,12 +4499,12 @@ var createMarkdownLinkFixProposal = (root, options) => {
|
|
|
4384
4499
|
const target = match[3];
|
|
4385
4500
|
if (!target || match[1] === "!" || /^(?:[a-z]+:|\/|#)/i.test(target)) continue;
|
|
4386
4501
|
const targetPath = target.split("#")[0]?.split("?")[0];
|
|
4387
|
-
if (!targetPath || existsSync11(resolve9(projectRoot,
|
|
4502
|
+
if (!targetPath || existsSync11(resolve9(projectRoot, dirname5(path), targetPath))) continue;
|
|
4388
4503
|
const targetStem = basename5(targetPath, extname2(targetPath)).toLowerCase();
|
|
4389
4504
|
const labelStem = (match[2] ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
4390
4505
|
const candidates = paths.filter((candidate) => basename5(candidate, extname2(candidate)).toLowerCase() === targetStem || basename5(candidate, extname2(candidate)).toLowerCase().replace(/[^a-z0-9]+/g, "") === labelStem);
|
|
4391
4506
|
if (candidates.length !== 1) continue;
|
|
4392
|
-
let replacement = relative6(
|
|
4507
|
+
let replacement = relative6(dirname5(path), candidates[0]).split(sep7).join("/");
|
|
4393
4508
|
if (target.startsWith("./") && !replacement.startsWith(".")) replacement = `./${replacement}`;
|
|
4394
4509
|
next = next.replace(match[0], match[0].replace(target, replacement));
|
|
4395
4510
|
}
|
|
@@ -4401,8 +4516,13 @@ var createArtifactNormalizationProposal = (root, artifactPath, options) => {
|
|
|
4401
4516
|
const projectRoot = realpathSync7.native(resolve9(root));
|
|
4402
4517
|
const path = artifactPath.split(sep7).join("/");
|
|
4403
4518
|
const absolute = containedPath(projectRoot, path);
|
|
4404
|
-
if (!absolute
|
|
4405
|
-
|
|
4519
|
+
if (!absolute) return void 0;
|
|
4520
|
+
let before;
|
|
4521
|
+
try {
|
|
4522
|
+
before = readFileSync10(absolute, "utf8");
|
|
4523
|
+
} catch {
|
|
4524
|
+
return void 0;
|
|
4525
|
+
}
|
|
4406
4526
|
let after;
|
|
4407
4527
|
try {
|
|
4408
4528
|
after = `${JSON.stringify(sortJson(JSON.parse(before)), null, 2)}
|
|
@@ -4792,8 +4912,20 @@ var evaluateRules = (report, options = {}) => {
|
|
|
4792
4912
|
const sortedFindings = [...findings].sort((a, b) => a.id.localeCompare(b.id));
|
|
4793
4913
|
return { mode: resolved.mode, findings: sortedFindings, exitCode: sortedFindings.some((finding) => finding.severity === "error") ? 1 : 0 };
|
|
4794
4914
|
};
|
|
4795
|
-
var parseRuleId = (value) =>
|
|
4796
|
-
|
|
4915
|
+
var parseRuleId = (value) => {
|
|
4916
|
+
try {
|
|
4917
|
+
return RuleIdSchema.parse(value);
|
|
4918
|
+
} catch {
|
|
4919
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
4920
|
+
}
|
|
4921
|
+
};
|
|
4922
|
+
var parseRuleSeverity = (value) => {
|
|
4923
|
+
try {
|
|
4924
|
+
return RuleSeveritySchema.parse(value);
|
|
4925
|
+
} catch {
|
|
4926
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
4927
|
+
}
|
|
4928
|
+
};
|
|
4797
4929
|
|
|
4798
4930
|
// src/federation/ecosystem-llms.ts
|
|
4799
4931
|
function formatEcosystemLlmsBlock(options) {
|
|
@@ -4825,7 +4957,7 @@ function formatEcosystemLlmsSection(options) {
|
|
|
4825
4957
|
import { readFileSync as readFileSync14 } from "fs";
|
|
4826
4958
|
|
|
4827
4959
|
// src/conformance/documentation-standard-v1.ts
|
|
4828
|
-
import { existsSync as existsSync13,
|
|
4960
|
+
import { closeSync as closeSync2, existsSync as existsSync13, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync12, realpathSync as realpathSync8 } from "fs";
|
|
4829
4961
|
import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve11, sep as sep8 } from "path";
|
|
4830
4962
|
|
|
4831
4963
|
// src/conformance/ecosystem-contract.ts
|
|
@@ -5030,11 +5162,10 @@ var fileEvidence = (root, path, options) => {
|
|
|
5030
5162
|
evidence: { path, detail: "Path escapes the project root." }
|
|
5031
5163
|
};
|
|
5032
5164
|
}
|
|
5033
|
-
|
|
5034
|
-
return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
|
|
5035
|
-
}
|
|
5165
|
+
let fd;
|
|
5036
5166
|
try {
|
|
5037
|
-
|
|
5167
|
+
fd = openSync2(abs, "r");
|
|
5168
|
+
const stat = fstatSync2(fd);
|
|
5038
5169
|
if (!stat.isFile()) {
|
|
5039
5170
|
return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
|
|
5040
5171
|
}
|
|
@@ -5055,7 +5186,7 @@ var fileEvidence = (root, path, options) => {
|
|
|
5055
5186
|
evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
|
|
5056
5187
|
};
|
|
5057
5188
|
}
|
|
5058
|
-
const content = readFileSync12(
|
|
5189
|
+
const content = readFileSync12(fd, "utf8");
|
|
5059
5190
|
return {
|
|
5060
5191
|
exists: content.trim().length > 0,
|
|
5061
5192
|
content,
|
|
@@ -5064,8 +5195,15 @@ var fileEvidence = (root, path, options) => {
|
|
|
5064
5195
|
detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
|
|
5065
5196
|
}
|
|
5066
5197
|
};
|
|
5067
|
-
} catch {
|
|
5068
|
-
|
|
5198
|
+
} catch (error) {
|
|
5199
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
5200
|
+
return {
|
|
5201
|
+
exists: false,
|
|
5202
|
+
content: "",
|
|
5203
|
+
evidence: { path, detail: code === "ENOENT" ? "File does not exist." : "File is not readable text." }
|
|
5204
|
+
};
|
|
5205
|
+
} finally {
|
|
5206
|
+
if (fd !== void 0) closeSync2(fd);
|
|
5069
5207
|
}
|
|
5070
5208
|
};
|
|
5071
5209
|
var resultWithException = (draft, options) => {
|
|
@@ -6330,7 +6468,7 @@ var startMcpStdioServer = (ctx) => {
|
|
|
6330
6468
|
// src/mcp/install.ts
|
|
6331
6469
|
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
|
|
6332
6470
|
import { homedir } from "os";
|
|
6333
|
-
import { dirname as
|
|
6471
|
+
import { dirname as dirname6, join as join15, resolve as resolve14 } from "path";
|
|
6334
6472
|
var SERVER_NAME = "ak-docs";
|
|
6335
6473
|
var mcpServerEntry = (root) => ({
|
|
6336
6474
|
command: "npx",
|
|
@@ -6347,7 +6485,7 @@ var readJson2 = (path) => {
|
|
|
6347
6485
|
}
|
|
6348
6486
|
};
|
|
6349
6487
|
var writeJson = (path, value) => {
|
|
6350
|
-
mkdirSync5(
|
|
6488
|
+
mkdirSync5(dirname6(path), { recursive: true });
|
|
6351
6489
|
writeFileSync6(path, `${JSON.stringify(value, null, 2)}
|
|
6352
6490
|
`, "utf8");
|
|
6353
6491
|
};
|
|
@@ -6427,16 +6565,6 @@ var gradeForScore = (score) => {
|
|
|
6427
6565
|
if (score >= 40) return "D";
|
|
6428
6566
|
return "F";
|
|
6429
6567
|
};
|
|
6430
|
-
var agentDocPaths = (index) => {
|
|
6431
|
-
const paths = /* @__PURE__ */ new Set();
|
|
6432
|
-
for (const owner of Object.values(index.lookup?.ownership ?? {})) {
|
|
6433
|
-
if (owner.agentDoc) paths.add(owner.agentDoc);
|
|
6434
|
-
}
|
|
6435
|
-
for (const handoff of Object.values(index.handoffs ?? {})) {
|
|
6436
|
-
if (handoff.startHere) paths.add(handoff.startHere);
|
|
6437
|
-
}
|
|
6438
|
-
return paths;
|
|
6439
|
-
};
|
|
6440
6568
|
var computeScore = (coverage) => {
|
|
6441
6569
|
let score = 0;
|
|
6442
6570
|
if (coverage.freshness.hasIndex) score += 15;
|
|
@@ -6524,7 +6652,7 @@ var runDoctor = (root, config) => {
|
|
|
6524
6652
|
let index;
|
|
6525
6653
|
let hasIndex = true;
|
|
6526
6654
|
let freshnessOk = false;
|
|
6527
|
-
let freshnessMessage
|
|
6655
|
+
let freshnessMessage;
|
|
6528
6656
|
try {
|
|
6529
6657
|
index = loadDocBridgeIndex(root, config);
|
|
6530
6658
|
const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
|
|
@@ -6544,8 +6672,6 @@ var runDoctor = (root, config) => {
|
|
|
6544
6672
|
const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
|
|
6545
6673
|
const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
|
|
6546
6674
|
const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
|
|
6547
|
-
const expectedAgentDocs = agentDocPaths(index);
|
|
6548
|
-
const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
|
|
6549
6675
|
const corpusDocs = scanAgentCorpus(root, config).filter(
|
|
6550
6676
|
(doc) => doc.path !== config.corpus.agent.index
|
|
6551
6677
|
);
|
|
@@ -6624,7 +6750,7 @@ var formatDoctorText = (report) => {
|
|
|
6624
6750
|
|
|
6625
6751
|
// src/index-builder/watch-index.ts
|
|
6626
6752
|
import { existsSync as existsSync17, watch } from "fs";
|
|
6627
|
-
import { dirname as
|
|
6753
|
+
import { dirname as dirname7, resolve as resolve15 } from "path";
|
|
6628
6754
|
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
6629
6755
|
var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
|
|
6630
6756
|
var collectWatchRoots = (root, config, configPath) => {
|
|
@@ -6638,7 +6764,7 @@ var collectWatchRoots = (root, config, configPath) => {
|
|
|
6638
6764
|
if (typeof value === "string" && value.length) roots.add(resolve15(root, value));
|
|
6639
6765
|
}
|
|
6640
6766
|
}
|
|
6641
|
-
if (configPath) roots.add(
|
|
6767
|
+
if (configPath) roots.add(dirname7(resolve15(configPath)));
|
|
6642
6768
|
return [...roots].filter((dir) => existsSync17(dir));
|
|
6643
6769
|
};
|
|
6644
6770
|
var watchDocBridgeIndex = (opts) => {
|
|
@@ -6708,7 +6834,7 @@ var watchDocBridgeIndex = (opts) => {
|
|
|
6708
6834
|
// src/memory/github-pr.ts
|
|
6709
6835
|
import { execFileSync as execFileSync2, spawnSync } from "child_process";
|
|
6710
6836
|
import { existsSync as existsSync18, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
6711
|
-
import { join as
|
|
6837
|
+
import { join as join16 } from "path";
|
|
6712
6838
|
var run = (cmd, args, cwd) => {
|
|
6713
6839
|
const result = spawnSync(cmd, [...args], { cwd, encoding: "utf8" });
|
|
6714
6840
|
const out = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
@@ -6716,10 +6842,10 @@ var run = (cmd, args, cwd) => {
|
|
|
6716
6842
|
};
|
|
6717
6843
|
var hasGh = () => run("gh", ["--version"], process.cwd()).ok;
|
|
6718
6844
|
var slug = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
6719
|
-
var defaultPromotionDraftPath = (root) =>
|
|
6845
|
+
var defaultPromotionDraftPath = (root) => join16(root, ".doc-bridge", "drafts", `memory-promotion-${slug()}.md`);
|
|
6720
6846
|
var writePromotionDraft = (root, draft, path) => {
|
|
6721
6847
|
const draftPath = path ?? defaultPromotionDraftPath(root);
|
|
6722
|
-
mkdirSync6(
|
|
6848
|
+
mkdirSync6(join16(root, ".doc-bridge", "drafts"), { recursive: true });
|
|
6723
6849
|
writeFileSync7(draftPath, `${draft.body}
|
|
6724
6850
|
`, "utf8");
|
|
6725
6851
|
return draftPath;
|
|
@@ -6755,7 +6881,7 @@ var promoteMemoryToGithubPr = (root, draft, options = {}) => {
|
|
|
6755
6881
|
message: `Wrote draft to ${relDraft}. Run the printed git/gh commands to open a draft PR.`
|
|
6756
6882
|
};
|
|
6757
6883
|
}
|
|
6758
|
-
if (!existsSync18(
|
|
6884
|
+
if (!existsSync18(join16(root, ".git"))) {
|
|
6759
6885
|
return {
|
|
6760
6886
|
ok: false,
|
|
6761
6887
|
dryRun: false,
|
|
@@ -7039,9 +7165,20 @@ var defaultFetchText = async (url) => {
|
|
|
7039
7165
|
if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
7040
7166
|
return res.text();
|
|
7041
7167
|
};
|
|
7168
|
+
var httpUrl = (value) => {
|
|
7169
|
+
try {
|
|
7170
|
+
const parsed = new URL(value);
|
|
7171
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
7172
|
+
if (parsed.username || parsed.password) return void 0;
|
|
7173
|
+
return parsed.href;
|
|
7174
|
+
} catch {
|
|
7175
|
+
return void 0;
|
|
7176
|
+
}
|
|
7177
|
+
};
|
|
7042
7178
|
var sourceText = async (root, source, fetchText) => {
|
|
7043
7179
|
try {
|
|
7044
|
-
|
|
7180
|
+
const remote = httpUrl(source);
|
|
7181
|
+
if (remote) return await fetchText(remote);
|
|
7045
7182
|
const path = resolve16(root, source);
|
|
7046
7183
|
if (!existsSync19(path)) return null;
|
|
7047
7184
|
return readFileSync18(path, "utf8");
|
|
@@ -7054,22 +7191,57 @@ var sameOrigin = (base, target) => {
|
|
|
7054
7191
|
return new URL(base).origin === new URL(target).origin;
|
|
7055
7192
|
};
|
|
7056
7193
|
var parseLlmsTxtLinks = (raw) => {
|
|
7057
|
-
const links = [
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7194
|
+
const links = [];
|
|
7195
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
7196
|
+
let cursor = 0;
|
|
7197
|
+
while (cursor < line.length) {
|
|
7198
|
+
const open = line.indexOf("[", cursor);
|
|
7199
|
+
if (open < 0) break;
|
|
7200
|
+
const titleEnd = line.indexOf("]", open + 1);
|
|
7201
|
+
const urlStart = titleEnd < 0 ? -1 : line.indexOf("(", titleEnd + 1);
|
|
7202
|
+
const urlEnd = urlStart < 0 ? -1 : line.indexOf(")", urlStart + 1);
|
|
7203
|
+
if (titleEnd < 0 || urlStart !== titleEnd + 1 || urlEnd < 0) {
|
|
7204
|
+
cursor = open + 1;
|
|
7205
|
+
continue;
|
|
7206
|
+
}
|
|
7207
|
+
const title = line.slice(open + 1, titleEnd).trim();
|
|
7208
|
+
const url = line.slice(urlStart + 1, urlEnd).trim();
|
|
7209
|
+
const description = line.slice(urlEnd + 1).trim().replace(/^:\s*/, "");
|
|
7210
|
+
if (url) links.push({ title: title || url, url, ...description ? { description } : {} });
|
|
7211
|
+
cursor = urlEnd + 1;
|
|
7212
|
+
}
|
|
7213
|
+
for (const token of line.split(/\s+/)) {
|
|
7214
|
+
let end = token.length;
|
|
7215
|
+
while (end > 0 && "),.;:".includes(token[end - 1] ?? "")) end -= 1;
|
|
7216
|
+
const url = token.slice(0, end);
|
|
7217
|
+
if (!/^https?:\/\//i.test(url) || links.some((link) => link.url === url)) continue;
|
|
7218
|
+
links.push({ title: slugFromPath(url), url });
|
|
7219
|
+
}
|
|
7065
7220
|
}
|
|
7066
7221
|
return links;
|
|
7067
7222
|
};
|
|
7223
|
+
var firstMatchingLine = (section, predicate) => section.split(/\r?\n/).find((line) => predicate(line.trim()))?.trim();
|
|
7224
|
+
var sectionTitle = (section, sourceUrl) => {
|
|
7225
|
+
const titleLine = firstMatchingLine(section, (line) => line.startsWith("title:"));
|
|
7226
|
+
if (titleLine) return titleLine.slice("title:".length).trim();
|
|
7227
|
+
const urlLine = firstMatchingLine(section, (line) => /^https?:\/\//i.test(line));
|
|
7228
|
+
if (urlLine) {
|
|
7229
|
+
try {
|
|
7230
|
+
return new URL(urlLine).pathname.split("/").filter(Boolean).at(-1) ?? slugFromPath(sourceUrl);
|
|
7231
|
+
} catch {
|
|
7232
|
+
return slugFromPath(sourceUrl);
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
7235
|
+
const heading = firstMatchingLine(section, (line) => line.startsWith("#"));
|
|
7236
|
+
if (heading) return heading.replace(/^#+\s*/, "").trim();
|
|
7237
|
+
return slugFromPath(sourceUrl);
|
|
7238
|
+
};
|
|
7068
7239
|
var chunksFromMarkdown = (property, raw, sourceUrl) => {
|
|
7069
|
-
const
|
|
7240
|
+
const frontmatterEnd = raw.startsWith("---\n") ? raw.indexOf("\n---", 4) : -1;
|
|
7241
|
+
const searchable = raw.includes("\n==== ") ? raw : frontmatterEnd >= 0 ? raw.slice(frontmatterEnd + "\n---".length).replace(/^\n/, "") : raw;
|
|
7070
7242
|
const sections = searchable.includes("\n==== ") ? searchable.split(/\n====\s+/).filter((section) => section.trim()) : searchable.split(/\n(?=##?\s+)/);
|
|
7071
7243
|
return sections.map((section, index) => {
|
|
7072
|
-
const title =
|
|
7244
|
+
const title = sectionTitle(section, sourceUrl);
|
|
7073
7245
|
const id = slugFromPath(title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")) || `${index}`;
|
|
7074
7246
|
return {
|
|
7075
7247
|
chunkKey: `${property}:federated:${id}`,
|
|
@@ -7097,8 +7269,15 @@ var loadFederatedChunks = async (root, config, options = {}) => {
|
|
|
7097
7269
|
chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
|
|
7098
7270
|
const links = parseLlmsTxtLinks(llms);
|
|
7099
7271
|
for (const link of links) {
|
|
7100
|
-
const
|
|
7101
|
-
|
|
7272
|
+
const linkUrl = httpUrl(link.url);
|
|
7273
|
+
const baseUrl = source.rawBaseUrl ? httpUrl(source.rawBaseUrl) : void 0;
|
|
7274
|
+
const url = linkUrl ?? (baseUrl ? new URL(link.url, baseUrl).href : link.url);
|
|
7275
|
+
let pathname = url;
|
|
7276
|
+
try {
|
|
7277
|
+
pathname = new URL(url).pathname;
|
|
7278
|
+
} catch {
|
|
7279
|
+
}
|
|
7280
|
+
if (!/\.(md|txt)$/i.test(pathname)) continue;
|
|
7102
7281
|
if (!sameOrigin(source.llmsTxt, url)) continue;
|
|
7103
7282
|
const raw = await sourceText(root, url, fetchText);
|
|
7104
7283
|
if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
|
|
@@ -7130,10 +7309,10 @@ var retrieveHybridChunks = async (root, config, index, query, options = {}) => {
|
|
|
7130
7309
|
|
|
7131
7310
|
// src/intelligence/rag.ts
|
|
7132
7311
|
import { readFileSync as readFileSync19 } from "fs";
|
|
7133
|
-
import { join as
|
|
7312
|
+
import { join as join18 } from "path";
|
|
7134
7313
|
|
|
7135
7314
|
// src/intelligence/adapter.ts
|
|
7136
|
-
import { join as
|
|
7315
|
+
import { join as join17 } from "path";
|
|
7137
7316
|
|
|
7138
7317
|
// src/intelligence/peers.ts
|
|
7139
7318
|
var PeerMissingError = class extends Error {
|
|
@@ -7260,7 +7439,7 @@ var resolveIntelligenceRuntime = async (config) => {
|
|
|
7260
7439
|
}
|
|
7261
7440
|
return { adapter, embed, provider, ...model ? { model } : {} };
|
|
7262
7441
|
};
|
|
7263
|
-
var defaultVectorStorePath = (root) =>
|
|
7442
|
+
var defaultVectorStorePath = (root) => join17(root, ".doc-bridge", "vectors");
|
|
7264
7443
|
|
|
7265
7444
|
// src/intelligence/rag.ts
|
|
7266
7445
|
var loadDocuments = (root, index, sources) => {
|
|
@@ -7268,7 +7447,7 @@ var loadDocuments = (root, index, sources) => {
|
|
|
7268
7447
|
const docs = [];
|
|
7269
7448
|
if (includeAgent) {
|
|
7270
7449
|
for (const entry of index.knowledge) {
|
|
7271
|
-
const abs =
|
|
7450
|
+
const abs = join18(root, entry.path);
|
|
7272
7451
|
let content = "";
|
|
7273
7452
|
try {
|
|
7274
7453
|
content = readFileSync19(abs, "utf8");
|
|
@@ -7289,7 +7468,7 @@ var createDocBridgeRag = async (root, config, index) => {
|
|
|
7289
7468
|
const { embed } = await resolveIntelligenceRuntime(config);
|
|
7290
7469
|
const ragMod = await importPeer("@agentskit/rag");
|
|
7291
7470
|
const memoryMod = await importPeer("@agentskit/memory");
|
|
7292
|
-
const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ?
|
|
7471
|
+
const storePath = typeof config.intelligence?.retriever?.options?.storePath === "string" ? join18(root, config.intelligence.retriever.options.storePath) : defaultVectorStorePath(root);
|
|
7293
7472
|
const store = memoryMod.fileVectorMemory({ path: storePath });
|
|
7294
7473
|
const rag = ragMod.createRAG({
|
|
7295
7474
|
embed,
|