@agentskit/doc-bridge 1.6.4 → 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 +249 -0
- package/CONTRIBUTING.md +6 -4
- package/action.yml +1 -1
- package/dist/cli/program.js +1139 -294
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +43 -5
- package/dist/config/index.js.map +1 -1
- package/dist/index-BUL0q7s8.d.ts +660 -0
- package/dist/index.d.ts +817 -2134
- package/dist/index.js +1154 -244
- package/dist/index.js.map +1 -1
- package/docs/PRD-enterprise-hardening.md +288 -0
- package/docs/RELEASE.md +22 -8
- package/docs/adr/0001-enterprise-verification-contract.md +35 -0
- 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 +30 -2
- package/docs/spec/analyzer-plugin-v1.md +24 -0
- package/docs/spec/benchmark-v1.md +36 -0
- package/docs/spec/config-v1.md +156 -0
- package/docs/validation-cycle-plan.md +255 -0
- package/docs/verification-harness.md +37 -4
- 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 +64 -12
- package/scripts/verification-harness.mjs +216 -14
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +31 -7
- package/src/cli/demo.ts +2 -2
- package/src/cli/program.ts +59 -16
- package/src/config/index.ts +2 -0
- package/src/config/load-config.ts +7 -1
- package/src/config/schema.ts +60 -2
- package/src/conformance/documentation-standard-v1.ts +14 -8
- package/src/discovery/documentation.ts +90 -23
- package/src/discovery/repository.ts +147 -19
- 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/index.ts +29 -0
- package/src/lib/bounded-text.ts +15 -10
- package/src/metrics/benchmark.ts +176 -0
- package/src/plugins/contract.ts +89 -0
- package/src/reconciliation/reconcile.ts +181 -5
- package/src/report/html.ts +318 -88
- package/src/rules/engine.ts +15 -2
- package/src/safety/repository.ts +1 -1
- package/src/schemas/knowledge.ts +21 -3
- package/src/validate.ts +7 -1
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +65 -9
- package/dist/index-DudNuwI5.d.ts +0 -2060
package/dist/cli/program.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/cli/program.ts
|
|
2
|
-
import { existsSync as existsSync21, mkdirSync as
|
|
3
|
-
import { dirname as
|
|
2
|
+
import { closeSync as closeSync3, existsSync as existsSync21, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync21, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
3
|
+
import { dirname as dirname9, relative as relative10, resolve as resolve18 } from "path";
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
5
|
|
|
6
6
|
// src/config/load-config.ts
|
|
@@ -467,15 +467,41 @@ var RuleIdSchema = z.enum([
|
|
|
467
467
|
var RuleSeveritySchema = z.enum(["off", "info", "warn", "error"]);
|
|
468
468
|
var RulesConfigSchema = z.object({
|
|
469
469
|
mode: z.enum(["default", "recommended", "strict"]).optional(),
|
|
470
|
-
severity: z.
|
|
470
|
+
severity: z.partialRecord(RuleIdSchema, RuleSeveritySchema).optional(),
|
|
471
471
|
ignore: z.array(RuleIdSchema).max(128).optional(),
|
|
472
472
|
criticalEntities: z.array(z.string().min(1).max(256)).max(128).optional(),
|
|
473
473
|
criticalPaths: z.array(z.string().min(1).max(512)).max(128).optional(),
|
|
474
|
-
warningThresholds: z.
|
|
474
|
+
warningThresholds: z.partialRecord(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
|
|
475
475
|
}).strict();
|
|
476
476
|
var ReconciliationConfigSchema = z.object({
|
|
477
|
+
/** Semantic comparison level. Raw discovery always keeps file-level relations. */
|
|
478
|
+
scope: z.enum(["file", "module", "package"]).optional(),
|
|
477
479
|
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
478
|
-
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional()
|
|
480
|
+
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
|
|
481
|
+
/** Limit missing-declaration findings to relations whose endpoints are internal project entities. */
|
|
482
|
+
requiredRelationTargets: z.enum(["all", "internal"]).optional(),
|
|
483
|
+
includeOrphanedDocuments: z.boolean().optional()
|
|
484
|
+
}).strict();
|
|
485
|
+
var AnalysisConfigSchema = z.object({
|
|
486
|
+
plugins: z.array(
|
|
487
|
+
z.object({
|
|
488
|
+
id: z.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
|
|
489
|
+
enabled: z.boolean().optional(),
|
|
490
|
+
order: z.number().int().nonnegative().optional(),
|
|
491
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
492
|
+
reason: z.string().min(1).max(1024).optional()
|
|
493
|
+
}).strict()
|
|
494
|
+
).max(128).optional(),
|
|
495
|
+
jsTs: z.object({
|
|
496
|
+
runtimeWiringMethods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).max(64).optional(),
|
|
497
|
+
runtimeWiringAdapters: z.array(
|
|
498
|
+
z.object({
|
|
499
|
+
id: z.string().min(1).max(128),
|
|
500
|
+
methods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).min(1).max(64)
|
|
501
|
+
}).strict()
|
|
502
|
+
).max(32).optional(),
|
|
503
|
+
includeTestRuntimeWiring: z.boolean().optional()
|
|
504
|
+
}).strict().optional()
|
|
479
505
|
}).strict();
|
|
480
506
|
var WorkflowConfigSchema = z.object({
|
|
481
507
|
stateDir: z.string().min(1).max(512).optional()
|
|
@@ -488,6 +514,10 @@ var RepositorySafetyConfigSchema = z.object({
|
|
|
488
514
|
maxMemoryMb: z.number().int().positive().max(1048576).optional(),
|
|
489
515
|
redactSecrets: z.boolean().optional()
|
|
490
516
|
}).strict();
|
|
517
|
+
var ReportConfigSchema = z.object({
|
|
518
|
+
/** Public report privacy mode. Private is the default; anonymized preserves topology without project identity. */
|
|
519
|
+
privacy: z.enum(["private", "anonymized"]).optional()
|
|
520
|
+
}).strict();
|
|
491
521
|
var SurfacesConfigSchema = z.object({
|
|
492
522
|
cli: z.object({
|
|
493
523
|
bin: z.string().min(1).max(64).optional(),
|
|
@@ -559,7 +589,12 @@ var IntelligenceConfigSchema = z.object({
|
|
|
559
589
|
enabled: z.boolean().optional(),
|
|
560
590
|
agentId: z.string().min(1).max(256).optional(),
|
|
561
591
|
agentRoot: z.string().min(1).max(512).optional(),
|
|
562
|
-
runnerModule: z.string().min(1).max(512).optional()
|
|
592
|
+
runnerModule: z.string().min(1).max(512).optional(),
|
|
593
|
+
deterministic: z.boolean().optional(),
|
|
594
|
+
timeoutMs: z.number().int().positive().max(6e5).optional(),
|
|
595
|
+
maxTokens: z.number().int().positive().max(1e6).optional(),
|
|
596
|
+
maxResponseBytes: z.number().int().positive().max(1e7).optional(),
|
|
597
|
+
maxConcurrency: z.number().int().positive().max(64).optional()
|
|
563
598
|
}).strict().optional()
|
|
564
599
|
}).strict();
|
|
565
600
|
var FederationConfigSchema = z.object({
|
|
@@ -651,9 +686,11 @@ var DocBridgeConfigV1Schema = z.object({
|
|
|
651
686
|
routing: RoutingConfigSchema.optional(),
|
|
652
687
|
gates: GatesConfigSchema.optional(),
|
|
653
688
|
reconciliation: ReconciliationConfigSchema.optional(),
|
|
689
|
+
analysis: AnalysisConfigSchema.optional(),
|
|
654
690
|
rules: RulesConfigSchema.optional(),
|
|
655
691
|
workflow: WorkflowConfigSchema.optional(),
|
|
656
692
|
safety: RepositorySafetyConfigSchema.optional(),
|
|
693
|
+
report: ReportConfigSchema.optional(),
|
|
657
694
|
surfaces: SurfacesConfigSchema.optional(),
|
|
658
695
|
intelligence: IntelligenceConfigSchema.optional(),
|
|
659
696
|
federation: FederationConfigSchema.optional(),
|
|
@@ -721,7 +758,7 @@ var parseConfig = (input) => {
|
|
|
721
758
|
throw new Error(
|
|
722
759
|
`Invalid doc-bridge config:
|
|
723
760
|
${result.error.issues.map(
|
|
724
|
-
(issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`
|
|
761
|
+
(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}`
|
|
725
762
|
).join("\n")}`
|
|
726
763
|
);
|
|
727
764
|
};
|
|
@@ -745,7 +782,7 @@ var projectRootFromConfigPath = (configFilePath, projectRootField) => {
|
|
|
745
782
|
};
|
|
746
783
|
|
|
747
784
|
// src/conformance/documentation-standard-v1.ts
|
|
748
|
-
import { existsSync as existsSync9,
|
|
785
|
+
import { closeSync as closeSync2, existsSync as existsSync9, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync8, realpathSync as realpathSync6 } from "fs";
|
|
749
786
|
import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve6, sep as sep5 } from "path";
|
|
750
787
|
|
|
751
788
|
// src/conformance/ecosystem-contract.ts
|
|
@@ -925,7 +962,7 @@ var parseCanonicalEcosystemContract = (manifestInput, claimsInput) => {
|
|
|
925
962
|
|
|
926
963
|
// src/index-builder/build-index.ts
|
|
927
964
|
import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
928
|
-
import { dirname as
|
|
965
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
929
966
|
|
|
930
967
|
// src/lib/paths.ts
|
|
931
968
|
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
@@ -1009,22 +1046,27 @@ var defaultChecksForTarget = (root, opts) => {
|
|
|
1009
1046
|
import { minimatch } from "minimatch";
|
|
1010
1047
|
|
|
1011
1048
|
// src/lib/bounded-text.ts
|
|
1012
|
-
import { readFileSync as readFileSync3
|
|
1049
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync3 } from "fs";
|
|
1013
1050
|
var MAX_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
1014
1051
|
var MAX_CORPUS_BYTES = 64 * 1024 * 1024;
|
|
1015
1052
|
var readBoundedText = (path, budget, limits) => {
|
|
1016
1053
|
const maxFileBytes = limits?.maxFileBytes ?? MAX_DOCUMENT_BYTES;
|
|
1017
1054
|
const maxCorpusBytes = limits?.maxCorpusBytes ?? MAX_CORPUS_BYTES;
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
throw new Error(`Documentation
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1055
|
+
const fd = openSync(path, "r");
|
|
1056
|
+
try {
|
|
1057
|
+
const stat = fstatSync(fd);
|
|
1058
|
+
if (!stat.isFile()) throw new Error(`Documentation path is not a regular file: ${path}`);
|
|
1059
|
+
if (stat.size > maxFileBytes) {
|
|
1060
|
+
throw new Error(`Documentation file exceeds the ${maxFileBytes} byte limit: ${path}`);
|
|
1061
|
+
}
|
|
1062
|
+
if (budget.used + stat.size > maxCorpusBytes) {
|
|
1063
|
+
throw new Error(`Documentation corpus exceeds the ${maxCorpusBytes} byte read budget.`);
|
|
1064
|
+
}
|
|
1065
|
+
budget.used += stat.size;
|
|
1066
|
+
return readFileSync3(fd, "utf8");
|
|
1067
|
+
} finally {
|
|
1068
|
+
closeSync(fd);
|
|
1025
1069
|
}
|
|
1026
|
-
budget.used += stat.size;
|
|
1027
|
-
return readFileSync3(path, "utf8");
|
|
1028
1070
|
};
|
|
1029
1071
|
|
|
1030
1072
|
// src/lib/markdown.ts
|
|
@@ -1448,7 +1490,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1448
1490
|
};
|
|
1449
1491
|
|
|
1450
1492
|
// src/version.ts
|
|
1451
|
-
var PACKAGE_VERSION = "1.
|
|
1493
|
+
var PACKAGE_VERSION = "1.7.45";
|
|
1452
1494
|
|
|
1453
1495
|
// src/index-builder/capabilities.ts
|
|
1454
1496
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1505,9 +1547,9 @@ var contentHashForArtifactV1 = (artifact2) => {
|
|
|
1505
1547
|
|
|
1506
1548
|
// src/index-builder/llms-txt.ts
|
|
1507
1549
|
var routePath = (path, pathPrefix) => {
|
|
1508
|
-
const
|
|
1550
|
+
const normalizedPath2 = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
|
|
1509
1551
|
const normalizedPrefix = pathPrefix?.replaceAll("\\", "/").replace(/\/$/, "");
|
|
1510
|
-
const relativePath3 = normalizedPrefix &&
|
|
1552
|
+
const relativePath3 = normalizedPrefix && normalizedPath2.startsWith(`${normalizedPrefix}/`) ? normalizedPath2.slice(normalizedPrefix.length + 1) : normalizedPath2;
|
|
1511
1553
|
return relativePath3.replace(/^\/+/, "");
|
|
1512
1554
|
};
|
|
1513
1555
|
var knowledgeUrl = (path, options = {}) => {
|
|
@@ -1879,7 +1921,7 @@ var scanHumanDocs = (root, config) => Object.fromEntries(scanHumanDocRecords(roo
|
|
|
1879
1921
|
|
|
1880
1922
|
// src/index-builder/plugins/nx.ts
|
|
1881
1923
|
import { existsSync as existsSync6, lstatSync as lstatSync2, readFileSync as readFileSync5, realpathSync as realpathSync5 } from "fs";
|
|
1882
|
-
import { basename as basename2, dirname as
|
|
1924
|
+
import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve5, sep as sep4 } from "path";
|
|
1883
1925
|
var NX_SCAN_SKIP = /* @__PURE__ */ new Set([
|
|
1884
1926
|
"node_modules",
|
|
1885
1927
|
".git",
|
|
@@ -1943,7 +1985,7 @@ var discoverNxProjects = (root, config) => {
|
|
|
1943
1985
|
for (const manifest of manifests) {
|
|
1944
1986
|
const json = readJsonRecord(manifest);
|
|
1945
1987
|
if (!json) continue;
|
|
1946
|
-
const manifestDir =
|
|
1988
|
+
const manifestDir = dirname2(manifest);
|
|
1947
1989
|
const manifestName = basename2(manifest);
|
|
1948
1990
|
const isProjectJson = manifestName === "project.json";
|
|
1949
1991
|
if (!isProjectJson && manifestName !== "package.json") continue;
|
|
@@ -1991,7 +2033,7 @@ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
|
1991
2033
|
import { basename as basename3, join as join6 } from "path";
|
|
1992
2034
|
|
|
1993
2035
|
// src/lib/glob-expand.ts
|
|
1994
|
-
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync
|
|
2036
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, statSync } from "fs";
|
|
1995
2037
|
import { join as join5 } from "path";
|
|
1996
2038
|
var expandWorkspaceGlobs = (root, patterns) => {
|
|
1997
2039
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -2008,7 +2050,7 @@ var expandWorkspaceGlobs = (root, patterns) => {
|
|
|
2008
2050
|
for (const name of readdirSync2(base)) {
|
|
2009
2051
|
const abs = join5(base, name);
|
|
2010
2052
|
try {
|
|
2011
|
-
if (
|
|
2053
|
+
if (statSync(abs).isDirectory()) dirs.add(toPosix(abs));
|
|
2012
2054
|
} catch {
|
|
2013
2055
|
}
|
|
2014
2056
|
}
|
|
@@ -2118,7 +2160,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2118
2160
|
lookup
|
|
2119
2161
|
};
|
|
2120
2162
|
if (write) {
|
|
2121
|
-
mkdirSync(
|
|
2163
|
+
mkdirSync(dirname3(indexPath), { recursive: true });
|
|
2122
2164
|
writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}
|
|
2123
2165
|
`, "utf8");
|
|
2124
2166
|
}
|
|
@@ -2141,7 +2183,7 @@ var buildDocBridgeIndex = (opts) => {
|
|
|
2141
2183
|
const capabilitiesOut = config.index?.capabilities?.outFile ?? ".doc-bridge/capabilities.json";
|
|
2142
2184
|
capabilitiesPath = join7(root, capabilitiesOut);
|
|
2143
2185
|
if (write) {
|
|
2144
|
-
mkdirSync(
|
|
2186
|
+
mkdirSync(dirname3(capabilitiesPath), { recursive: true });
|
|
2145
2187
|
writeFileSync(
|
|
2146
2188
|
capabilitiesPath,
|
|
2147
2189
|
renderCapabilitiesJson(config, index, {
|
|
@@ -2187,11 +2229,10 @@ var fileEvidence = (root, path, options) => {
|
|
|
2187
2229
|
evidence: { path, detail: "Path escapes the project root." }
|
|
2188
2230
|
};
|
|
2189
2231
|
}
|
|
2190
|
-
|
|
2191
|
-
return { exists: false, content: "", evidence: { path, detail: "File does not exist." } };
|
|
2192
|
-
}
|
|
2232
|
+
let fd;
|
|
2193
2233
|
try {
|
|
2194
|
-
|
|
2234
|
+
fd = openSync2(abs, "r");
|
|
2235
|
+
const stat = fstatSync2(fd);
|
|
2195
2236
|
if (!stat.isFile()) {
|
|
2196
2237
|
return { exists: false, content: "", evidence: { path, detail: "Path is not a regular file." } };
|
|
2197
2238
|
}
|
|
@@ -2212,7 +2253,7 @@ var fileEvidence = (root, path, options) => {
|
|
|
2212
2253
|
evidence: { path, detail: `Text evidence exceeds ${MAX_TEXT_EVIDENCE_BYTES} bytes.` }
|
|
2213
2254
|
};
|
|
2214
2255
|
}
|
|
2215
|
-
const content = readFileSync8(
|
|
2256
|
+
const content = readFileSync8(fd, "utf8");
|
|
2216
2257
|
return {
|
|
2217
2258
|
exists: content.trim().length > 0,
|
|
2218
2259
|
content,
|
|
@@ -2221,8 +2262,15 @@ var fileEvidence = (root, path, options) => {
|
|
|
2221
2262
|
detail: content.trim().length > 0 ? "File exists and is non-empty." : "File is empty."
|
|
2222
2263
|
}
|
|
2223
2264
|
};
|
|
2224
|
-
} catch {
|
|
2225
|
-
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
2267
|
+
return {
|
|
2268
|
+
exists: false,
|
|
2269
|
+
content: "",
|
|
2270
|
+
evidence: { path, detail: code === "ENOENT" ? "File does not exist." : "File is not readable text." }
|
|
2271
|
+
};
|
|
2272
|
+
} finally {
|
|
2273
|
+
if (fd !== void 0) closeSync2(fd);
|
|
2226
2274
|
}
|
|
2227
2275
|
};
|
|
2228
2276
|
var resultWithException = (draft, options) => {
|
|
@@ -2556,9 +2604,10 @@ var EvidenceSchema2 = z3.object({
|
|
|
2556
2604
|
});
|
|
2557
2605
|
}
|
|
2558
2606
|
});
|
|
2559
|
-
var CoverageStatusSchema = z3.enum(["complete", "partial", "not-analyzed"]);
|
|
2607
|
+
var CoverageStatusSchema = z3.enum(["complete", "partial", "not-analyzed", "not-applicable"]);
|
|
2560
2608
|
var CoverageSchema = z3.object({
|
|
2561
2609
|
analyzer: boundedString(128),
|
|
2610
|
+
analyzerVersion: boundedString(64).optional(),
|
|
2562
2611
|
scope: boundedString(512),
|
|
2563
2612
|
status: CoverageStatusSchema,
|
|
2564
2613
|
reason: z3.string().max(1024).optional(),
|
|
@@ -2587,7 +2636,7 @@ var EntitySchema = z3.object({
|
|
|
2587
2636
|
aliases: z3.array(boundedString(256)).max(32).optional(),
|
|
2588
2637
|
provenance: ProvenanceSchema,
|
|
2589
2638
|
evidence: z3.array(EvidenceSchema2).max(64),
|
|
2590
|
-
metadata: z3.record(z3.unknown()).optional()
|
|
2639
|
+
metadata: z3.record(z3.string(), z3.unknown()).optional()
|
|
2591
2640
|
}).strict();
|
|
2592
2641
|
var RelationSchema = z3.object({
|
|
2593
2642
|
id: boundedString(256),
|
|
@@ -2597,7 +2646,7 @@ var RelationSchema = z3.object({
|
|
|
2597
2646
|
discriminator: boundedString(256).optional(),
|
|
2598
2647
|
provenance: ProvenanceSchema,
|
|
2599
2648
|
evidence: z3.array(EvidenceSchema2).max(64),
|
|
2600
|
-
metadata: z3.record(z3.unknown()).optional()
|
|
2649
|
+
metadata: z3.record(z3.string(), z3.unknown()).optional()
|
|
2601
2650
|
}).strict();
|
|
2602
2651
|
var DiscoverySnapshotV1Schema = z3.object({
|
|
2603
2652
|
type: z3.literal("discovery-snapshot"),
|
|
@@ -2641,7 +2690,24 @@ var ReconciliationReportV1Schema = z3.object({
|
|
|
2641
2690
|
entityCount: z3.number().int().nonnegative(),
|
|
2642
2691
|
relationCount: z3.number().int().nonnegative(),
|
|
2643
2692
|
diagnosticCount: z3.number().int().nonnegative(),
|
|
2644
|
-
|
|
2693
|
+
scope: z3.enum(["file", "module", "package"]).optional(),
|
|
2694
|
+
requiredRelationKinds: z3.array(boundedString(128)).max(128).optional(),
|
|
2695
|
+
requiredRelationTargets: z3.enum(["all", "internal"]).optional(),
|
|
2696
|
+
diagnosticsByCode: z3.record(z3.string().max(128), z3.number().int().nonnegative()).optional(),
|
|
2697
|
+
diagnosticsByStatus: z3.record(z3.string().max(128), z3.number().int().nonnegative()).optional(),
|
|
2698
|
+
documentation: z3.object({
|
|
2699
|
+
documentCount: z3.number().int().nonnegative(),
|
|
2700
|
+
documentedDocumentCount: z3.number().int().nonnegative(),
|
|
2701
|
+
documentClassificationCounts: z3.record(z3.string().max(128), z3.number().int().nonnegative()),
|
|
2702
|
+
documentedDocumentClassificationCounts: z3.record(z3.string().max(128), z3.number().int().nonnegative()),
|
|
2703
|
+
packageCount: z3.number().int().nonnegative(),
|
|
2704
|
+
packageStatus: z3.object({
|
|
2705
|
+
fresh: z3.number().int().nonnegative(),
|
|
2706
|
+
stale: z3.number().int().nonnegative(),
|
|
2707
|
+
missing: z3.number().int().nonnegative(),
|
|
2708
|
+
unverified: z3.number().int().nonnegative()
|
|
2709
|
+
}).strict()
|
|
2710
|
+
}).strict().optional()
|
|
2645
2711
|
}).strict()
|
|
2646
2712
|
}).strict();
|
|
2647
2713
|
var WorkflowStateSchema = z3.enum([
|
|
@@ -2759,6 +2825,37 @@ var scalar = (value) => {
|
|
|
2759
2825
|
}
|
|
2760
2826
|
return trimmed;
|
|
2761
2827
|
};
|
|
2828
|
+
var isFieldName = (value) => {
|
|
2829
|
+
if (!/^[A-Za-z]/.test(value)) return false;
|
|
2830
|
+
for (const character of value.slice(1)) {
|
|
2831
|
+
if (!/[A-Za-z0-9_-]/.test(character)) return false;
|
|
2832
|
+
}
|
|
2833
|
+
return true;
|
|
2834
|
+
};
|
|
2835
|
+
var parseIndentedField = (raw, indentation) => {
|
|
2836
|
+
const prefix = " ".repeat(indentation);
|
|
2837
|
+
if (!raw.startsWith(prefix) || raw[indentation] === " ") return void 0;
|
|
2838
|
+
const body = raw.slice(indentation);
|
|
2839
|
+
const separator = body.indexOf(":");
|
|
2840
|
+
if (separator <= 0) return void 0;
|
|
2841
|
+
const key = body.slice(0, separator).trim();
|
|
2842
|
+
return isFieldName(key) ? { key, value: body.slice(separator + 1).trim() } : void 0;
|
|
2843
|
+
};
|
|
2844
|
+
var parseListItem = (raw, indentation) => {
|
|
2845
|
+
const prefix = `${" ".repeat(indentation)}-`;
|
|
2846
|
+
if (!raw.startsWith(prefix)) return void 0;
|
|
2847
|
+
const rest = raw.slice(prefix.length);
|
|
2848
|
+
if (rest && !/\s/.test(rest[0] ?? "")) return void 0;
|
|
2849
|
+
return rest.trim();
|
|
2850
|
+
};
|
|
2851
|
+
var conventionalPackageReference = (path, agentRoot) => {
|
|
2852
|
+
const prefix = `${agentRoot.replace(/\/$/, "")}/`;
|
|
2853
|
+
if (!path.startsWith(prefix)) return void 0;
|
|
2854
|
+
const relative11 = path.slice(prefix.length);
|
|
2855
|
+
const [scope, file] = relative11.split("/");
|
|
2856
|
+
if (scope !== "packages" && scope !== "apps" || !file) return void 0;
|
|
2857
|
+
return file.replace(/\.mdx?$/, "");
|
|
2858
|
+
};
|
|
2762
2859
|
var list = (value) => {
|
|
2763
2860
|
const trimmed = value.trim();
|
|
2764
2861
|
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
@@ -2775,7 +2872,15 @@ var addDiagnostic = (diagnostics, input, code, message, line, endLine = line) =>
|
|
|
2775
2872
|
diagnostics.push(diagnostic(input.path, code, message, line, endLine));
|
|
2776
2873
|
};
|
|
2777
2874
|
var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
|
|
2778
|
-
const
|
|
2875
|
+
const direct = entities.find((entity2) => entity2.id === reference || entity2.aliases?.includes(reference));
|
|
2876
|
+
if (direct) return direct;
|
|
2877
|
+
const packageReference = reference.replace(/^package:/, "");
|
|
2878
|
+
const packageCandidates = entities.filter((entity2) => {
|
|
2879
|
+
if (entity2.kind !== "package") return false;
|
|
2880
|
+
const pathName = entity2.path?.split("/").pop();
|
|
2881
|
+
return entity2.name === reference || entity2.path === reference || pathName === packageReference || entity2.name.endsWith(`/${packageReference}`);
|
|
2882
|
+
});
|
|
2883
|
+
const resolved = packageCandidates.length === 1 ? packageCandidates[0] : void 0;
|
|
2779
2884
|
if (resolved) return resolved;
|
|
2780
2885
|
const id = `unresolved:${reference}`;
|
|
2781
2886
|
const existing = unresolved.get(id);
|
|
@@ -2791,9 +2896,13 @@ var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
|
|
|
2791
2896
|
return entity;
|
|
2792
2897
|
};
|
|
2793
2898
|
var relationKey = (from, to, kind) => `${from}\0${to}\0${kind}`;
|
|
2794
|
-
var parseBlock = (input) => {
|
|
2899
|
+
var parseBlock = (input, options = {}) => {
|
|
2900
|
+
const conventionalPath = conventionalPackageReference(input.path, options.agentRoot ?? "docs/for-agents");
|
|
2795
2901
|
const frontmatter = findFrontmatter(input.content);
|
|
2796
2902
|
if (!frontmatter) {
|
|
2903
|
+
if (conventionalPath && !input.content.replace(/^\uFEFF/, "").startsWith("---")) {
|
|
2904
|
+
return { covers: [{ value: conventionalPath, line: 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
2905
|
+
}
|
|
2797
2906
|
if (input.content.replace(/^\uFEFF/, "").startsWith("---")) {
|
|
2798
2907
|
return {
|
|
2799
2908
|
covers: [],
|
|
@@ -2805,8 +2914,24 @@ var parseBlock = (input) => {
|
|
|
2805
2914
|
return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
|
|
2806
2915
|
}
|
|
2807
2916
|
const { lines, end } = frontmatter;
|
|
2808
|
-
|
|
2809
|
-
|
|
2917
|
+
let docbridgeLine = -1;
|
|
2918
|
+
let typeLine = -1;
|
|
2919
|
+
let packageLine = -1;
|
|
2920
|
+
let humanDocLine = -1;
|
|
2921
|
+
for (let index = 1; index < end; index += 1) {
|
|
2922
|
+
const line = lines[index] ?? "";
|
|
2923
|
+
if (docbridgeLine < 0 && /^docbridge\s*:/.test(line)) docbridgeLine = index;
|
|
2924
|
+
if (typeLine < 0 && /^type\s*:/.test(line)) typeLine = index;
|
|
2925
|
+
if (packageLine < 0 && /^package\s*:/.test(line)) packageLine = index;
|
|
2926
|
+
if (humanDocLine < 0 && /^humanDoc\s*:/.test(line)) humanDocLine = index;
|
|
2927
|
+
}
|
|
2928
|
+
if (docbridgeLine < 0) {
|
|
2929
|
+
const type = typeLine >= 0 ? scalar(lines[typeLine]?.slice("type:".length) ?? "") : "";
|
|
2930
|
+
const packageReference = packageLine >= 0 ? scalar(lines[packageLine]?.slice("package:".length) ?? "") : conventionalPath ?? "";
|
|
2931
|
+
if (type === "package" && packageReference) return { covers: [{ value: packageReference, line: packageLine >= 0 ? packageLine + 1 : typeLine + 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
2932
|
+
if (conventionalPath) return { covers: [{ value: packageReference, line: humanDocLine >= 0 ? humanDocLine + 1 : 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
2933
|
+
return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
|
|
2934
|
+
}
|
|
2810
2935
|
const diagnostics = [];
|
|
2811
2936
|
const covers = [];
|
|
2812
2937
|
const relations = [];
|
|
@@ -2832,11 +2957,10 @@ var parseBlock = (input) => {
|
|
|
2832
2957
|
section = void 0;
|
|
2833
2958
|
continue;
|
|
2834
2959
|
}
|
|
2835
|
-
|
|
2960
|
+
const sectionField = parseIndentedField(raw, 2);
|
|
2961
|
+
if (sectionField) {
|
|
2836
2962
|
finishRelation();
|
|
2837
|
-
const
|
|
2838
|
-
const key = match?.[1];
|
|
2839
|
-
const value = match?.[2] ?? "";
|
|
2963
|
+
const { key, value } = sectionField;
|
|
2840
2964
|
if (key !== "covers" && key !== "relations") {
|
|
2841
2965
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown docbridge field: ${key ?? "(missing)"}.`, line);
|
|
2842
2966
|
section = void 0;
|
|
@@ -2853,35 +2977,36 @@ var parseBlock = (input) => {
|
|
|
2853
2977
|
}
|
|
2854
2978
|
continue;
|
|
2855
2979
|
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2980
|
+
const listValue = parseListItem(raw, 4);
|
|
2981
|
+
if (section === "covers" && listValue !== void 0) {
|
|
2982
|
+
const value = scalar(listValue);
|
|
2858
2983
|
if (!value) addDiagnostic(diagnostics, input, "DOCBRIDGE_REFERENCE_MISSING", "covers entries must not be empty.", line);
|
|
2859
2984
|
else covers.push({ value, line });
|
|
2860
2985
|
continue;
|
|
2861
2986
|
}
|
|
2862
|
-
if (section === "relations" &&
|
|
2987
|
+
if (section === "relations" && listValue !== void 0) {
|
|
2863
2988
|
finishRelation();
|
|
2864
|
-
const firstField =
|
|
2989
|
+
const firstField = parseIndentedField(` ${listValue}`, 4);
|
|
2865
2990
|
current = { startLine: line, endLine: line, fields: /* @__PURE__ */ new Set() };
|
|
2866
|
-
if (firstField?.
|
|
2867
|
-
current.fields.add(firstField
|
|
2868
|
-
current[firstField
|
|
2869
|
-
} else if (
|
|
2991
|
+
if (firstField?.key) {
|
|
2992
|
+
current.fields.add(firstField.key);
|
|
2993
|
+
current[firstField.key] = scalar(firstField.value);
|
|
2994
|
+
} else if (listValue) {
|
|
2870
2995
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_RELATION_INVALID", "Relation entries must be field mappings.", line);
|
|
2871
2996
|
}
|
|
2872
2997
|
continue;
|
|
2873
2998
|
}
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
const key = field
|
|
2999
|
+
const field = parseIndentedField(raw, 6);
|
|
3000
|
+
if (section === "relations" && current && field) {
|
|
3001
|
+
const { key, value } = field;
|
|
2877
3002
|
current.endLine = line;
|
|
2878
|
-
if (!
|
|
2879
|
-
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key
|
|
3003
|
+
if (!["from", "to", "kind", "detection"].includes(key)) {
|
|
3004
|
+
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_UNKNOWN", `Unknown relation field: ${key}.`, line);
|
|
2880
3005
|
} else if (current.fields.has(key)) {
|
|
2881
3006
|
addDiagnostic(diagnostics, input, "DOCBRIDGE_FIELD_DUPLICATE", `Duplicate relation field: ${key}.`, line);
|
|
2882
3007
|
} else {
|
|
2883
3008
|
current.fields.add(key);
|
|
2884
|
-
current[key] = scalar(
|
|
3009
|
+
current[key] = scalar(value);
|
|
2885
3010
|
}
|
|
2886
3011
|
continue;
|
|
2887
3012
|
}
|
|
@@ -2891,7 +3016,7 @@ var parseBlock = (input) => {
|
|
|
2891
3016
|
return { covers, relations, diagnostics, hasDocbridge: true };
|
|
2892
3017
|
};
|
|
2893
3018
|
var parseDocumentationDeclarations = (input, options) => {
|
|
2894
|
-
const parsed = parseBlock(input);
|
|
3019
|
+
const parsed = parseBlock(input, options);
|
|
2895
3020
|
if (!parsed.hasDocbridge) return { hasDocbridge: false, entities: [], relations: [], diagnostics: [] };
|
|
2896
3021
|
const diagnostics = [...parsed.diagnostics];
|
|
2897
3022
|
const unresolved = /* @__PURE__ */ new Map();
|
|
@@ -2952,12 +3077,12 @@ var parseDocumentationDeclarations = (input, options) => {
|
|
|
2952
3077
|
diagnostics
|
|
2953
3078
|
};
|
|
2954
3079
|
};
|
|
2955
|
-
var applyDocumentationDeclarations = (snapshot, documents) => {
|
|
3080
|
+
var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
|
|
2956
3081
|
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
2957
3082
|
const relations = new Map(snapshot.relations.map((relation) => [relation.id, relation]));
|
|
2958
3083
|
const diagnostics = [];
|
|
2959
3084
|
for (const document of documents) {
|
|
2960
|
-
const result = parseDocumentationDeclarations(document, { snapshot });
|
|
3085
|
+
const result = parseDocumentationDeclarations(document, { snapshot, ...options });
|
|
2961
3086
|
diagnostics.push(...result.diagnostics);
|
|
2962
3087
|
for (const entity of result.entities) entities.set(entity.id, entity);
|
|
2963
3088
|
for (const relation of result.relations) relations.set(relation.id, relation);
|
|
@@ -2972,14 +3097,14 @@ var applyDocumentationDeclarations = (snapshot, documents) => {
|
|
|
2972
3097
|
// src/discovery/repository.ts
|
|
2973
3098
|
import { execFileSync } from "child_process";
|
|
2974
3099
|
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
2975
|
-
import { basename as basename4, dirname as
|
|
3100
|
+
import { basename as basename4, dirname as dirname4, extname, join as join8, relative as relative6, resolve as resolve8, sep as sep7 } from "path";
|
|
2976
3101
|
import * as ts from "typescript";
|
|
2977
3102
|
|
|
2978
3103
|
// src/safety/repository.ts
|
|
2979
|
-
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync7, statSync as
|
|
3104
|
+
import { lstatSync as lstatSync3, readdirSync as readdirSync3, realpathSync as realpathSync7, statSync as statSync2 } from "fs";
|
|
2980
3105
|
import { isAbsolute as isAbsolute5, relative as relative5, resolve as resolve7, sep as sep6 } from "path";
|
|
2981
3106
|
import { minimatch as minimatch4 } from "minimatch";
|
|
2982
|
-
var DEFAULT_SAFETY_EXCLUDES = ["**/.git/**", "**/node_modules/**", "**/dist/**", "**/build/**", "**/coverage/**", "**/.doc-bridge/**", "**/.turbo/**", "**/.env", "**/.env.*", "**/*secret*", "**/*credential*", "**/*.pem", "**/*.key"];
|
|
3107
|
+
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"];
|
|
2983
3108
|
var containedPath = (root, candidate) => {
|
|
2984
3109
|
const projectRoot = realpathSync7.native(resolve7(root));
|
|
2985
3110
|
const unresolved = resolve7(projectRoot, candidate);
|
|
@@ -3039,7 +3164,7 @@ var safeWalkFiles = (root, options = {}) => {
|
|
|
3039
3164
|
reason = `Repository scan exceeded the ${options.maxFiles ?? 1e4} file limit.`;
|
|
3040
3165
|
return;
|
|
3041
3166
|
}
|
|
3042
|
-
bytes +=
|
|
3167
|
+
bytes += statSync2(absolute).size;
|
|
3043
3168
|
if (options.maxBytes !== void 0 && bytes > options.maxBytes) {
|
|
3044
3169
|
reason = `Repository scan exceeded the ${options.maxBytes} byte limit.`;
|
|
3045
3170
|
return;
|
|
@@ -3064,6 +3189,8 @@ var SOURCE_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", "
|
|
|
3064
3189
|
var DOCUMENT_EXTENSIONS = [".md", ".mdx"];
|
|
3065
3190
|
var DEFAULT_MAX_FILES2 = 1e4;
|
|
3066
3191
|
var EMPTY_HASH = "0".repeat(64);
|
|
3192
|
+
var DEFAULT_RUNTIME_WIRING_METHODS = ["register", "use", "mount", "attach"];
|
|
3193
|
+
var TEST_MODULE_PATTERN = /(?:\.test|\.spec|__tests__)/;
|
|
3067
3194
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3068
3195
|
var readJson = (path) => {
|
|
3069
3196
|
try {
|
|
@@ -3092,6 +3219,13 @@ var firstLineContaining = (text, pattern) => {
|
|
|
3092
3219
|
const line = text.split(/\r?\n/).findIndex((value) => value.includes(pattern));
|
|
3093
3220
|
return line >= 0 ? line + 1 : void 0;
|
|
3094
3221
|
};
|
|
3222
|
+
var documentClassification = (path) => {
|
|
3223
|
+
if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
|
|
3224
|
+
if (/(^|\/)docs-archive(?:\/|$)/.test(path)) return "archive";
|
|
3225
|
+
if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
|
|
3226
|
+
if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
|
|
3227
|
+
return "unclassified";
|
|
3228
|
+
};
|
|
3095
3229
|
var packageName = (manifest, fallback) => typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : fallback || void 0;
|
|
3096
3230
|
var workspacePatterns = (root, rootManifest) => {
|
|
3097
3231
|
const fromPackageJson = rootManifest?.workspaces;
|
|
@@ -3166,7 +3300,7 @@ var readCompilerOptions = (root) => {
|
|
|
3166
3300
|
if (!configPath) return { options: {} };
|
|
3167
3301
|
const parsed = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
3168
3302
|
if (parsed.error) return { options: {}, error: ts.flattenDiagnosticMessageText(parsed.error.messageText, "\n") };
|
|
3169
|
-
const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys,
|
|
3303
|
+
const config = ts.parseJsonConfigFileContent(parsed.config, ts.sys, dirname4(configPath));
|
|
3170
3304
|
if (config.errors.length) {
|
|
3171
3305
|
return {
|
|
3172
3306
|
options: config.options,
|
|
@@ -3233,29 +3367,106 @@ var exportedNames = (sourceFile) => {
|
|
|
3233
3367
|
visit(sourceFile);
|
|
3234
3368
|
return [...names].sort();
|
|
3235
3369
|
};
|
|
3236
|
-
var moduleReferences = (root, path, sourceFile) => {
|
|
3370
|
+
var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
3237
3371
|
const references = [];
|
|
3372
|
+
const dynamicEvidence = [];
|
|
3238
3373
|
let hasDynamic = false;
|
|
3374
|
+
let hasLiteralDynamic = false;
|
|
3239
3375
|
let hasRuntimeWiring = false;
|
|
3240
|
-
|
|
3241
|
-
|
|
3376
|
+
let hasUnresolvedRuntimeWiring = false;
|
|
3377
|
+
const importedBindings = /* @__PURE__ */ new Map();
|
|
3378
|
+
const staticStringBindings = /* @__PURE__ */ new Map();
|
|
3379
|
+
const localBindings = /* @__PURE__ */ new Set();
|
|
3380
|
+
const resolveStaticString = (expression) => {
|
|
3381
|
+
if (ts.isStringLiteralLike(expression)) return expression.text;
|
|
3382
|
+
if (ts.isIdentifier(expression)) return staticStringBindings.get(expression.text);
|
|
3383
|
+
if (ts.isParenthesizedExpression(expression)) return resolveStaticString(expression.expression);
|
|
3384
|
+
if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
3385
|
+
const left = resolveStaticString(expression.left);
|
|
3386
|
+
const right = resolveStaticString(expression.right);
|
|
3387
|
+
return left !== void 0 && right !== void 0 ? left + right : void 0;
|
|
3388
|
+
}
|
|
3389
|
+
return void 0;
|
|
3390
|
+
};
|
|
3391
|
+
const collectStaticStringBindings = (node) => {
|
|
3392
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isVariableDeclarationList(node.parent) && (node.parent.flags & ts.NodeFlags.Const) !== 0) {
|
|
3393
|
+
const value = resolveStaticString(node.initializer);
|
|
3394
|
+
const previous = staticStringBindings.get(node.name.text);
|
|
3395
|
+
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === value ? value : void 0);
|
|
3396
|
+
}
|
|
3397
|
+
if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node)) && ts.isIdentifier(node.name)) localBindings.add(node.name.text);
|
|
3398
|
+
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)) && node.name) localBindings.add(node.name.text);
|
|
3399
|
+
ts.forEachChild(node, collectStaticStringBindings);
|
|
3400
|
+
};
|
|
3401
|
+
collectStaticStringBindings(sourceFile);
|
|
3402
|
+
const addImportedBindingReference = (expression, node) => {
|
|
3403
|
+
if (ts.isIdentifier(expression)) {
|
|
3404
|
+
const specifier = importedBindings.get(expression.text);
|
|
3405
|
+
if (specifier) {
|
|
3406
|
+
addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
3407
|
+
return true;
|
|
3408
|
+
}
|
|
3409
|
+
return false;
|
|
3410
|
+
}
|
|
3411
|
+
if (ts.isPropertyAccessExpression(expression)) return addImportedBindingReference(expression.expression, node);
|
|
3412
|
+
if (ts.isCallExpression(expression)) return addImportedBindingReference(expression.expression, node);
|
|
3413
|
+
return false;
|
|
3414
|
+
};
|
|
3415
|
+
const isKnownLocal = (expression) => {
|
|
3416
|
+
if (ts.isIdentifier(expression)) return localBindings.has(expression.text) || importedBindings.has(expression.text);
|
|
3417
|
+
if (expression.kind === ts.SyntaxKind.ThisKeyword) return true;
|
|
3418
|
+
if (ts.isPropertyAccessExpression(expression)) return isKnownLocal(expression.expression);
|
|
3419
|
+
if (ts.isCallExpression(expression)) return isKnownLocal(expression.expression);
|
|
3420
|
+
return ts.isStringLiteralLike(expression);
|
|
3421
|
+
};
|
|
3422
|
+
const addReference = (specifier, kind, node, detection) => {
|
|
3423
|
+
references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
|
|
3242
3424
|
};
|
|
3243
3425
|
const visit = (node) => {
|
|
3244
3426
|
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
3245
3427
|
addReference(node.moduleSpecifier, "imports", node);
|
|
3428
|
+
const clause = node.importClause;
|
|
3429
|
+
if (clause?.name) importedBindings.set(clause.name.text, node.moduleSpecifier.text);
|
|
3430
|
+
if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings)) importedBindings.set(clause.namedBindings.name.text, node.moduleSpecifier.text);
|
|
3431
|
+
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
3432
|
+
for (const element of clause.namedBindings.elements) importedBindings.set((element.name ?? element.propertyName)?.text ?? "", node.moduleSpecifier.text);
|
|
3433
|
+
}
|
|
3246
3434
|
} else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
3247
3435
|
addReference(node.moduleSpecifier, "re-exports", node);
|
|
3248
3436
|
} else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) {
|
|
3249
3437
|
addReference(node.moduleReference.expression, "imports", node);
|
|
3438
|
+
importedBindings.set(node.name.text, node.moduleReference.expression.text);
|
|
3250
3439
|
} else if (ts.isCallExpression(node)) {
|
|
3251
3440
|
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
3252
|
-
|
|
3441
|
+
const specifier = node.arguments[0] ? resolveStaticString(node.arguments[0]) : void 0;
|
|
3442
|
+
if (specifier !== void 0) {
|
|
3443
|
+
hasLiteralDynamic = true;
|
|
3444
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3445
|
+
addReference({ text: specifier }, "imports", node, "dynamic-literal");
|
|
3446
|
+
} else {
|
|
3447
|
+
hasDynamic = true;
|
|
3448
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3449
|
+
}
|
|
3253
3450
|
} else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
3254
3451
|
const argument = node.arguments[0];
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3452
|
+
const specifier = argument ? resolveStaticString(argument) : void 0;
|
|
3453
|
+
if (specifier !== void 0) {
|
|
3454
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3455
|
+
addReference({ text: specifier }, "imports", node);
|
|
3456
|
+
} else {
|
|
3457
|
+
hasDynamic = true;
|
|
3458
|
+
dynamicEvidence.push(nodeEvidence(root, path, sourceFile, node));
|
|
3459
|
+
}
|
|
3460
|
+
} else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
|
|
3258
3461
|
hasRuntimeWiring = true;
|
|
3462
|
+
let hasUnresolvedTarget = false;
|
|
3463
|
+
for (const argument of node.arguments) {
|
|
3464
|
+
if (ts.isStringLiteralLike(argument)) continue;
|
|
3465
|
+
if (addImportedBindingReference(argument, node)) continue;
|
|
3466
|
+
if (!isKnownLocal(argument)) hasUnresolvedTarget = true;
|
|
3467
|
+
}
|
|
3468
|
+
const receiver = node.expression.expression;
|
|
3469
|
+
if (hasUnresolvedTarget && !isKnownLocal(receiver)) hasUnresolvedRuntimeWiring = true;
|
|
3259
3470
|
}
|
|
3260
3471
|
}
|
|
3261
3472
|
ts.forEachChild(node, visit);
|
|
@@ -3264,12 +3475,15 @@ var moduleReferences = (root, path, sourceFile) => {
|
|
|
3264
3475
|
return {
|
|
3265
3476
|
references,
|
|
3266
3477
|
exports: exportedNames(sourceFile),
|
|
3478
|
+
dynamicEvidence,
|
|
3267
3479
|
hasDynamic,
|
|
3268
|
-
|
|
3480
|
+
hasLiteralDynamic,
|
|
3481
|
+
hasRuntimeWiring,
|
|
3482
|
+
hasUnresolvedRuntimeWiring
|
|
3269
3483
|
};
|
|
3270
3484
|
};
|
|
3271
3485
|
var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
|
|
3272
|
-
const base = resolve8(
|
|
3486
|
+
const base = resolve8(dirname4(containingFile), specifier);
|
|
3273
3487
|
const extension = extname(base);
|
|
3274
3488
|
const extensionlessBase = extension ? base.slice(0, -extension.length) : base;
|
|
3275
3489
|
const candidates = [
|
|
@@ -3338,11 +3552,11 @@ var artifact = (root, config, files, entities, relations, coverage) => {
|
|
|
3338
3552
|
sourceRevision: revision.value,
|
|
3339
3553
|
sourceRevisionKind: revision.kind,
|
|
3340
3554
|
configurationHash: sha256NormalizedV1(config ?? {}),
|
|
3341
|
-
pipelineVersion: "1.
|
|
3342
|
-
analyzerVersions: { repository: "1.
|
|
3555
|
+
pipelineVersion: "1.1.8",
|
|
3556
|
+
analyzerVersions: { repository: "1.1.1", "js-ts": "1.3.4" },
|
|
3343
3557
|
entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3344
3558
|
relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3345
|
-
coverage: [
|
|
3559
|
+
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.1.1", "js-ts": "1.3.4" }[entry.analyzer] ?? "1.0.0") }))
|
|
3346
3560
|
};
|
|
3347
3561
|
return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
3348
3562
|
};
|
|
@@ -3402,12 +3616,12 @@ var discoverRepository = (opts = {}) => {
|
|
|
3402
3616
|
const sourceFile = ts.createSourceFile(absPath, text, ts.ScriptTarget.Latest, true, scriptKind(absPath));
|
|
3403
3617
|
const exports = exportedNames(sourceFile);
|
|
3404
3618
|
modules.set(resolve8(absPath), { absPath, path, entityId: id, ...pkg ? { packageId: pkg.id } : {} });
|
|
3405
|
-
addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test:
|
|
3619
|
+
addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test: TEST_MODULE_PATTERN.test(path) } } : {} });
|
|
3406
3620
|
if (pkg) addRelation({ id: entityId("relation", `${pkg.id}:contains:${id}`), kind: "contains", from: pkg.id, to: id, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1)] });
|
|
3407
3621
|
}
|
|
3408
3622
|
for (const absPath of documentPaths) {
|
|
3409
3623
|
const path = relativePath(root, absPath);
|
|
3410
|
-
addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)] });
|
|
3624
|
+
addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)], metadata: { classification: documentClassification(path) } });
|
|
3411
3625
|
}
|
|
3412
3626
|
const compiler = readCompilerOptions(root);
|
|
3413
3627
|
const coverage = [
|
|
@@ -3415,8 +3629,8 @@ var discoverRepository = (opts = {}) => {
|
|
|
3415
3629
|
{ analyzer: "repository", scope: "package-manager", status: hasPackageManagerMetadata(root, rootManifest) ? "complete" : "partial", ...!hasPackageManagerMetadata(root, rootManifest) ? { reason: `No package manager metadata found; default helper would fall back to ${detectPackageManager(root)}.` } : {} },
|
|
3416
3630
|
{ analyzer: "repository", scope: "workspace-packages", status: packageResult.coverage.some((item) => item.status === "partial") ? "partial" : "complete", ...packageResult.coverage.find((item) => item.reason)?.reason ? { reason: packageResult.coverage.find((item) => item.reason)?.reason } : {} },
|
|
3417
3631
|
{ analyzer: "js-ts", scope: "static-imports-and-exports", status: compiler.error ? "partial" : "complete", ...compiler.error ? { reason: compiler.error } : {} },
|
|
3418
|
-
{ analyzer: "js-ts", scope: "dynamic-imports", status: "not-
|
|
3419
|
-
{ analyzer: "js-ts", scope: "runtime-wiring", status: "not-
|
|
3632
|
+
{ analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." },
|
|
3633
|
+
{ analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." },
|
|
3420
3634
|
{ analyzer: "js-ts", scope: "generated-code", status: "not-analyzed", reason: "Generated code is not interpreted as source architecture." }
|
|
3421
3635
|
];
|
|
3422
3636
|
for (const pkg of packageResult.packages) {
|
|
@@ -3427,10 +3641,28 @@ var discoverRepository = (opts = {}) => {
|
|
|
3427
3641
|
addRelation({ id: entityId("relation", `${pkg.id}:depends-on:${target}:${dependency.type}`), kind: "depends-on", from: pkg.id, to: target, provenance: "observed", evidence: [lineEvidence("configuration", root, pkg.manifestPath, firstLineContaining(text, `"${dependency.name}"`))], metadata: { dependencyType: dependency.type } });
|
|
3428
3642
|
}
|
|
3429
3643
|
}
|
|
3644
|
+
const dynamicCoverageIndex = coverage.findIndex((entry) => entry.scope === "dynamic-imports");
|
|
3645
|
+
const configuredRuntimeWiringMethods = /* @__PURE__ */ new Set([
|
|
3646
|
+
...opts.config?.analysis?.jsTs?.runtimeWiringMethods ?? [],
|
|
3647
|
+
...opts.config?.analysis?.jsTs?.runtimeWiringAdapters?.flatMap((adapter) => adapter.methods) ?? []
|
|
3648
|
+
]);
|
|
3649
|
+
if (!configuredRuntimeWiringMethods.size) for (const method of DEFAULT_RUNTIME_WIRING_METHODS) configuredRuntimeWiringMethods.add(method);
|
|
3650
|
+
const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
|
|
3651
|
+
let observedLiteralDynamic = false;
|
|
3652
|
+
let observedUnresolvedDynamic = false;
|
|
3653
|
+
const observedDynamicEvidence = [];
|
|
3654
|
+
let observedRuntimeWiring = false;
|
|
3655
|
+
let observedUnresolvedRuntimeWiring = false;
|
|
3430
3656
|
for (const module of modules.values()) {
|
|
3431
3657
|
const text = readFileSync9(module.absPath, "utf8");
|
|
3432
3658
|
const sourceFile = ts.createSourceFile(module.absPath, text, ts.ScriptTarget.Latest, true, scriptKind(module.absPath));
|
|
3433
|
-
const
|
|
3659
|
+
const runtimeWiringMethods = includeTestRuntimeWiring || !TEST_MODULE_PATTERN.test(module.path) ? configuredRuntimeWiringMethods : /* @__PURE__ */ new Set();
|
|
3660
|
+
const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
|
|
3661
|
+
observedLiteralDynamic ||= references.hasLiteralDynamic;
|
|
3662
|
+
observedUnresolvedDynamic ||= references.hasDynamic;
|
|
3663
|
+
observedDynamicEvidence.push(...references.dynamicEvidence);
|
|
3664
|
+
observedRuntimeWiring ||= references.hasRuntimeWiring;
|
|
3665
|
+
observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
|
|
3434
3666
|
for (const reference of references.references) {
|
|
3435
3667
|
const target = resolveReference(reference, module.absPath, modules, packageResult.packages, compiler.options);
|
|
3436
3668
|
if (!target) continue;
|
|
@@ -3438,11 +3670,14 @@ var discoverRepository = (opts = {}) => {
|
|
|
3438
3670
|
const externalName = target.targetId.replace(/^external:/, "");
|
|
3439
3671
|
addEntity({ id: target.targetId, kind: "external", name: externalName, provenance: "observed", evidence: [reference.evidence] });
|
|
3440
3672
|
}
|
|
3441
|
-
addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence] });
|
|
3673
|
+
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 } } : {} });
|
|
3442
3674
|
}
|
|
3443
|
-
if (references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: "not-analyzed", reason: "A dynamic import
|
|
3444
|
-
if (references.
|
|
3675
|
+
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)] });
|
|
3676
|
+
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)] });
|
|
3445
3677
|
}
|
|
3678
|
+
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." };
|
|
3679
|
+
const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
|
|
3680
|
+
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." };
|
|
3446
3681
|
return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
|
|
3447
3682
|
};
|
|
3448
3683
|
|
|
@@ -3574,9 +3809,20 @@ var defaultFetchText = async (url) => {
|
|
|
3574
3809
|
if (!res.ok) throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
3575
3810
|
return res.text();
|
|
3576
3811
|
};
|
|
3812
|
+
var httpUrl = (value) => {
|
|
3813
|
+
try {
|
|
3814
|
+
const parsed = new URL(value);
|
|
3815
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
3816
|
+
if (parsed.username || parsed.password) return void 0;
|
|
3817
|
+
return parsed.href;
|
|
3818
|
+
} catch {
|
|
3819
|
+
return void 0;
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3577
3822
|
var sourceText = async (root, source, fetchText) => {
|
|
3578
3823
|
try {
|
|
3579
|
-
|
|
3824
|
+
const remote = httpUrl(source);
|
|
3825
|
+
if (remote) return await fetchText(remote);
|
|
3580
3826
|
const path = resolve9(root, source);
|
|
3581
3827
|
if (!existsSync11(path)) return null;
|
|
3582
3828
|
return readFileSync10(path, "utf8");
|
|
@@ -3589,22 +3835,57 @@ var sameOrigin = (base, target) => {
|
|
|
3589
3835
|
return new URL(base).origin === new URL(target).origin;
|
|
3590
3836
|
};
|
|
3591
3837
|
var parseLlmsTxtLinks = (raw) => {
|
|
3592
|
-
const links = [
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3838
|
+
const links = [];
|
|
3839
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
3840
|
+
let cursor = 0;
|
|
3841
|
+
while (cursor < line.length) {
|
|
3842
|
+
const open = line.indexOf("[", cursor);
|
|
3843
|
+
if (open < 0) break;
|
|
3844
|
+
const titleEnd = line.indexOf("]", open + 1);
|
|
3845
|
+
const urlStart = titleEnd < 0 ? -1 : line.indexOf("(", titleEnd + 1);
|
|
3846
|
+
const urlEnd = urlStart < 0 ? -1 : line.indexOf(")", urlStart + 1);
|
|
3847
|
+
if (titleEnd < 0 || urlStart !== titleEnd + 1 || urlEnd < 0) {
|
|
3848
|
+
cursor = open + 1;
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
const title = line.slice(open + 1, titleEnd).trim();
|
|
3852
|
+
const url = line.slice(urlStart + 1, urlEnd).trim();
|
|
3853
|
+
const description = line.slice(urlEnd + 1).trim().replace(/^:\s*/, "");
|
|
3854
|
+
if (url) links.push({ title: title || url, url, ...description ? { description } : {} });
|
|
3855
|
+
cursor = urlEnd + 1;
|
|
3856
|
+
}
|
|
3857
|
+
for (const token of line.split(/\s+/)) {
|
|
3858
|
+
let end = token.length;
|
|
3859
|
+
while (end > 0 && "),.;:".includes(token[end - 1] ?? "")) end -= 1;
|
|
3860
|
+
const url = token.slice(0, end);
|
|
3861
|
+
if (!/^https?:\/\//i.test(url) || links.some((link) => link.url === url)) continue;
|
|
3862
|
+
links.push({ title: slugFromPath(url), url });
|
|
3863
|
+
}
|
|
3600
3864
|
}
|
|
3601
3865
|
return links;
|
|
3602
3866
|
};
|
|
3867
|
+
var firstMatchingLine = (section, predicate) => section.split(/\r?\n/).find((line) => predicate(line.trim()))?.trim();
|
|
3868
|
+
var sectionTitle = (section, sourceUrl) => {
|
|
3869
|
+
const titleLine = firstMatchingLine(section, (line) => line.startsWith("title:"));
|
|
3870
|
+
if (titleLine) return titleLine.slice("title:".length).trim();
|
|
3871
|
+
const urlLine = firstMatchingLine(section, (line) => /^https?:\/\//i.test(line));
|
|
3872
|
+
if (urlLine) {
|
|
3873
|
+
try {
|
|
3874
|
+
return new URL(urlLine).pathname.split("/").filter(Boolean).at(-1) ?? slugFromPath(sourceUrl);
|
|
3875
|
+
} catch {
|
|
3876
|
+
return slugFromPath(sourceUrl);
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
const heading = firstMatchingLine(section, (line) => line.startsWith("#"));
|
|
3880
|
+
if (heading) return heading.replace(/^#+\s*/, "").trim();
|
|
3881
|
+
return slugFromPath(sourceUrl);
|
|
3882
|
+
};
|
|
3603
3883
|
var chunksFromMarkdown = (property, raw, sourceUrl) => {
|
|
3604
|
-
const
|
|
3884
|
+
const frontmatterEnd = raw.startsWith("---\n") ? raw.indexOf("\n---", 4) : -1;
|
|
3885
|
+
const searchable = raw.includes("\n==== ") ? raw : frontmatterEnd >= 0 ? raw.slice(frontmatterEnd + "\n---".length).replace(/^\n/, "") : raw;
|
|
3605
3886
|
const sections = searchable.includes("\n==== ") ? searchable.split(/\n====\s+/).filter((section) => section.trim()) : searchable.split(/\n(?=##?\s+)/);
|
|
3606
3887
|
return sections.map((section, index) => {
|
|
3607
|
-
const title =
|
|
3888
|
+
const title = sectionTitle(section, sourceUrl);
|
|
3608
3889
|
const id = slugFromPath(title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")) || `${index}`;
|
|
3609
3890
|
return {
|
|
3610
3891
|
chunkKey: `${property}:federated:${id}`,
|
|
@@ -3632,8 +3913,15 @@ var loadFederatedChunks = async (root, config, options = {}) => {
|
|
|
3632
3913
|
chunks.push(...chunksFromMarkdown(source.id, llms, source.llmsTxt));
|
|
3633
3914
|
const links = parseLlmsTxtLinks(llms);
|
|
3634
3915
|
for (const link of links) {
|
|
3635
|
-
const
|
|
3636
|
-
|
|
3916
|
+
const linkUrl = httpUrl(link.url);
|
|
3917
|
+
const baseUrl = source.rawBaseUrl ? httpUrl(source.rawBaseUrl) : void 0;
|
|
3918
|
+
const url = linkUrl ?? (baseUrl ? new URL(link.url, baseUrl).href : link.url);
|
|
3919
|
+
let pathname = url;
|
|
3920
|
+
try {
|
|
3921
|
+
pathname = new URL(url).pathname;
|
|
3922
|
+
} catch {
|
|
3923
|
+
}
|
|
3924
|
+
if (!/\.(md|txt)$/i.test(pathname)) continue;
|
|
3637
3925
|
if (!sameOrigin(source.llmsTxt, url)) continue;
|
|
3638
3926
|
const raw = await sourceText(root, url, fetchText);
|
|
3639
3927
|
if (raw) chunks.push(...chunksFromMarkdown(source.id, raw, url));
|
|
@@ -3839,9 +4127,14 @@ var MemoryCandidateV1Schema = z6.object({
|
|
|
3839
4127
|
}).strict();
|
|
3840
4128
|
|
|
3841
4129
|
// src/validate.ts
|
|
4130
|
+
var zodMessage = (issue) => {
|
|
4131
|
+
if (issue.code === "invalid_type" && issue.message.endsWith("received undefined")) return "Required";
|
|
4132
|
+
if (issue.code === "invalid_value" && "values" in issue) return "Invalid enum value";
|
|
4133
|
+
return issue.message;
|
|
4134
|
+
};
|
|
3842
4135
|
var zodIssues = (error) => error.issues.map((issue) => ({
|
|
3843
4136
|
path: issue.path.join(".") || "(root)",
|
|
3844
|
-
message: issue
|
|
4137
|
+
message: zodMessage(issue)
|
|
3845
4138
|
}));
|
|
3846
4139
|
var safeParseAgentHandoff = (input) => {
|
|
3847
4140
|
const legacy = AgentHandoffLegacySchema.safeParse(input);
|
|
@@ -4183,8 +4476,20 @@ var evaluateRules = (report, options = {}) => {
|
|
|
4183
4476
|
const sortedFindings = [...findings].sort((a, b) => a.id.localeCompare(b.id));
|
|
4184
4477
|
return { mode: resolved.mode, findings: sortedFindings, exitCode: sortedFindings.some((finding) => finding.severity === "error") ? 1 : 0 };
|
|
4185
4478
|
};
|
|
4186
|
-
var parseRuleId = (value) =>
|
|
4187
|
-
|
|
4479
|
+
var parseRuleId = (value) => {
|
|
4480
|
+
try {
|
|
4481
|
+
return RuleIdSchema.parse(value);
|
|
4482
|
+
} catch {
|
|
4483
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
4484
|
+
}
|
|
4485
|
+
};
|
|
4486
|
+
var parseRuleSeverity = (value) => {
|
|
4487
|
+
try {
|
|
4488
|
+
return RuleSeveritySchema.parse(value);
|
|
4489
|
+
} catch {
|
|
4490
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4188
4493
|
|
|
4189
4494
|
// src/query/query.ts
|
|
4190
4495
|
var handoffForPackage = (index, id, config) => {
|
|
@@ -4855,7 +5160,7 @@ ${auth.out}`
|
|
|
4855
5160
|
|
|
4856
5161
|
// src/index-builder/watch-index.ts
|
|
4857
5162
|
import { existsSync as existsSync15, watch } from "fs";
|
|
4858
|
-
import { dirname as
|
|
5163
|
+
import { dirname as dirname5, resolve as resolve11 } from "path";
|
|
4859
5164
|
var WATCH_PATTERN = /\.(md|mdx|json|ya?ml|mdc)$/i;
|
|
4860
5165
|
var NX_MANIFEST_PATTERN = /(^|[/\\])(project|package)\.json$/i;
|
|
4861
5166
|
var collectWatchRoots = (root, config, configPath) => {
|
|
@@ -4869,7 +5174,7 @@ var collectWatchRoots = (root, config, configPath) => {
|
|
|
4869
5174
|
if (typeof value === "string" && value.length) roots.add(resolve11(root, value));
|
|
4870
5175
|
}
|
|
4871
5176
|
}
|
|
4872
|
-
if (configPath) roots.add(
|
|
5177
|
+
if (configPath) roots.add(dirname5(resolve11(configPath)));
|
|
4873
5178
|
return [...roots].filter((dir) => existsSync15(dir));
|
|
4874
5179
|
};
|
|
4875
5180
|
var watchDocBridgeIndex = (opts) => {
|
|
@@ -4938,7 +5243,7 @@ var watchDocBridgeIndex = (opts) => {
|
|
|
4938
5243
|
|
|
4939
5244
|
// src/workflow/engine.ts
|
|
4940
5245
|
import { appendFileSync, existsSync as existsSync16, mkdirSync as mkdirSync3, readFileSync as readFileSync15, renameSync, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
4941
|
-
import { join as
|
|
5246
|
+
import { join as join14, relative as relative7, resolve as resolve12 } from "path";
|
|
4942
5247
|
var WORKFLOW_STAGES = ["collect", "normalize", "reconcile", "evaluate", "report"];
|
|
4943
5248
|
var stageState = {
|
|
4944
5249
|
collect: "discovering",
|
|
@@ -4947,7 +5252,7 @@ var stageState = {
|
|
|
4947
5252
|
evaluate: "proposed",
|
|
4948
5253
|
report: "delivered"
|
|
4949
5254
|
};
|
|
4950
|
-
var defaultStateDir = (root) =>
|
|
5255
|
+
var defaultStateDir = (root) => join14(root, ".doc-bridge", "workflow");
|
|
4951
5256
|
var atomicWrite = (path, value) => {
|
|
4952
5257
|
const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
4953
5258
|
writeFileSync3(temp, `${JSON.stringify(value, null, 2)}
|
|
@@ -4955,13 +5260,29 @@ var atomicWrite = (path, value) => {
|
|
|
4955
5260
|
renameSync(temp, path);
|
|
4956
5261
|
};
|
|
4957
5262
|
var writeManifest = (stateDir, run2) => {
|
|
4958
|
-
atomicWrite(
|
|
5263
|
+
atomicWrite(join14(stateDir, "manifest.json"), run2);
|
|
4959
5264
|
};
|
|
4960
5265
|
var appendTransition = (stateDir, transition2) => {
|
|
4961
|
-
appendFileSync(
|
|
5266
|
+
appendFileSync(join14(stateDir, "transitions.jsonl"), `${JSON.stringify(transition2)}
|
|
4962
5267
|
`, "utf8");
|
|
4963
5268
|
};
|
|
4964
5269
|
var transition = (run2, to, reason) => {
|
|
5270
|
+
const allowed = {
|
|
5271
|
+
created: ["created", "discovering", "failed", "cancelled"],
|
|
5272
|
+
discovering: ["discovering", "analyzed", "failed", "cancelled", "stale"],
|
|
5273
|
+
analyzed: ["analyzed", "compared", "failed", "cancelled", "stale"],
|
|
5274
|
+
compared: ["compared", "proposed", "failed", "cancelled", "stale"],
|
|
5275
|
+
"awaiting-agent": ["awaiting-agent", "proposed", "failed", "cancelled", "stale"],
|
|
5276
|
+
proposed: ["proposed", "validating", "delivered", "failed", "cancelled", "stale"],
|
|
5277
|
+
"awaiting-approval": ["awaiting-approval", "validating", "failed", "cancelled", "stale"],
|
|
5278
|
+
validating: ["validating", "delivered", "failed", "cancelled", "stale"],
|
|
5279
|
+
delivered: ["delivered", "stale", "failed", "cancelled"],
|
|
5280
|
+
failed: ["failed", "discovering", "analyzed", "compared", "proposed", "validating", "delivered", "cancelled"],
|
|
5281
|
+
cancelled: ["cancelled", "discovering", "analyzed", "compared", "proposed", "validating", "delivered"],
|
|
5282
|
+
stale: [],
|
|
5283
|
+
superseded: []
|
|
5284
|
+
};
|
|
5285
|
+
if (!allowed[run2.state].includes(to)) throw new Error(`Illegal workflow transition ${run2.state} -> ${to}.`);
|
|
4965
5286
|
const item = {
|
|
4966
5287
|
from: run2.state,
|
|
4967
5288
|
to,
|
|
@@ -4971,21 +5292,32 @@ var transition = (run2, to, reason) => {
|
|
|
4971
5292
|
return { ...run2, state: to, transitions: [...run2.transitions, item] };
|
|
4972
5293
|
};
|
|
4973
5294
|
var runId = () => `${Date.now()}-${process.pid}`;
|
|
4974
|
-
var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
4975
|
-
var stageArtifactPath = (stateDir, stage, inputHash) =>
|
|
5295
|
+
var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5296
|
+
var stageArtifactPath = (stateDir, stage, inputHash) => join14(stateDir, "artifacts", `${stage}-${inputHash}.json`);
|
|
4976
5297
|
var readArtifact = (path) => JSON.parse(readFileSync15(path, "utf8"));
|
|
4977
|
-
var
|
|
5298
|
+
var readVerifiedArtifact = (path, stage, step) => {
|
|
5299
|
+
const artifact2 = readArtifact(path);
|
|
5300
|
+
if (artifact2.type !== "workflow-step-artifact" || artifact2.stage !== stage || artifact2.inputHash !== step.inputHash) throw new Error(`Invalid workflow artifact for stage "${stage}".`);
|
|
5301
|
+
if (sha256NormalizedV1(artifact2.value) !== artifact2.outputHash || artifact2.outputHash !== step.outputHash) throw new Error(`Workflow artifact hash mismatch for stage "${stage}".`);
|
|
5302
|
+
return artifact2;
|
|
5303
|
+
};
|
|
5304
|
+
var stepArtifactPath = (stateDir, step) => {
|
|
5305
|
+
const path = resolve12(stateDir, step.artifactRefs?.[0] ?? "");
|
|
5306
|
+
const pathRelativeToState = relative7(stateDir, path);
|
|
5307
|
+
if (pathRelativeToState.startsWith("..") || pathRelativeToState.startsWith("/")) throw new Error(`Workflow artifact escapes state directory for stage "${step.name}".`);
|
|
5308
|
+
return path;
|
|
5309
|
+
};
|
|
4978
5310
|
var stepOutput = (stateDir, run2, stage) => {
|
|
4979
5311
|
const step = run2.steps.find((item) => item.name === stage);
|
|
4980
5312
|
if (!step || step.status !== "completed" || !step.artifactRefs?.[0]) return null;
|
|
4981
|
-
return
|
|
5313
|
+
return readVerifiedArtifact(stepArtifactPath(stateDir, step), stage, step).value;
|
|
4982
5314
|
};
|
|
4983
5315
|
var acquireLock = (stateDir) => {
|
|
4984
|
-
const lock =
|
|
5316
|
+
const lock = join14(stateDir, ".lock");
|
|
4985
5317
|
try {
|
|
4986
5318
|
mkdirSync3(lock);
|
|
4987
5319
|
} catch {
|
|
4988
|
-
const ownerPath =
|
|
5320
|
+
const ownerPath = join14(lock, "owner.json");
|
|
4989
5321
|
try {
|
|
4990
5322
|
const owner = JSON.parse(readFileSync15(ownerPath, "utf8"));
|
|
4991
5323
|
if (typeof owner.pid === "number") process.kill(owner.pid, 0);
|
|
@@ -4996,16 +5328,16 @@ var acquireLock = (stateDir) => {
|
|
|
4996
5328
|
mkdirSync3(lock);
|
|
4997
5329
|
}
|
|
4998
5330
|
}
|
|
4999
|
-
writeFileSync3(
|
|
5331
|
+
writeFileSync3(join14(lock, "owner.json"), JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
5000
5332
|
return () => rmSync(lock, { recursive: true, force: true });
|
|
5001
5333
|
};
|
|
5002
5334
|
var loadManifest = (stateDir) => {
|
|
5003
|
-
const path =
|
|
5335
|
+
const path = join14(stateDir, "manifest.json");
|
|
5004
5336
|
if (!existsSync16(path)) return void 0;
|
|
5005
5337
|
return WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(path, "utf8")));
|
|
5006
5338
|
};
|
|
5007
5339
|
var baseRun = (options, stateDir, supersedes) => {
|
|
5008
|
-
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5340
|
+
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5009
5341
|
return WorkflowRunV1Schema.parse({
|
|
5010
5342
|
type: "workflow-run",
|
|
5011
5343
|
schemaVersion: 1,
|
|
@@ -5015,8 +5347,8 @@ var baseRun = (options, stateDir, supersedes) => {
|
|
|
5015
5347
|
sourceRevision: options.sourceRevision,
|
|
5016
5348
|
sourceRevisionKind: "content",
|
|
5017
5349
|
configurationHash: options.configurationHash,
|
|
5018
|
-
pipelineVersion: "1.0.0",
|
|
5019
|
-
analyzerVersions: { workflow: options.toolVersion ?? "1.0.0" },
|
|
5350
|
+
pipelineVersion: options.pipelineVersion ?? "1.0.0",
|
|
5351
|
+
analyzerVersions: { ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" },
|
|
5020
5352
|
runId: options.runId ?? runId(),
|
|
5021
5353
|
state: "created",
|
|
5022
5354
|
steps: WORKFLOW_STAGES.map((name) => ({ name, status: "pending", inputHash })),
|
|
@@ -5025,12 +5357,12 @@ var baseRun = (options, stateDir, supersedes) => {
|
|
|
5025
5357
|
});
|
|
5026
5358
|
};
|
|
5027
5359
|
var withHash = (run2) => WorkflowRunV1Schema.parse({ ...run2, contentHash: contentHashForArtifactV1(run2) });
|
|
5028
|
-
var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.
|
|
5360
|
+
var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.pipelineVersion === (options.pipelineVersion ?? "1.0.0") && sha256NormalizedV1(run2.analyzerVersions) === sha256NormalizedV1({ ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" });
|
|
5029
5361
|
var selectedStages = (stage) => stage && stage !== "all" ? [stage] : WORKFLOW_STAGES;
|
|
5030
5362
|
var runWorkflow = (options) => {
|
|
5031
5363
|
const root = resolve12(options.root);
|
|
5032
5364
|
const stateDir = resolve12(root, options.stateDir ?? defaultStateDir(root));
|
|
5033
|
-
mkdirSync3(
|
|
5365
|
+
mkdirSync3(join14(stateDir, "artifacts"), { recursive: true });
|
|
5034
5366
|
const release = acquireLock(stateDir);
|
|
5035
5367
|
try {
|
|
5036
5368
|
let run2 = loadManifest(stateDir);
|
|
@@ -5050,7 +5382,17 @@ var runWorkflow = (options) => {
|
|
|
5050
5382
|
if (!run2) throw new Error("Workflow manifest was not initialized.");
|
|
5051
5383
|
const firstSelectedStage = selectedStages(options.stage)[0];
|
|
5052
5384
|
const previousStageIndex = firstSelectedStage ? WORKFLOW_STAGES.indexOf(firstSelectedStage) - 1 : -1;
|
|
5053
|
-
let previousOutput =
|
|
5385
|
+
let previousOutput = null;
|
|
5386
|
+
if (previousStageIndex >= 0) {
|
|
5387
|
+
try {
|
|
5388
|
+
previousOutput = stepOutput(stateDir, run2, WORKFLOW_STAGES[previousStageIndex]);
|
|
5389
|
+
} catch (error) {
|
|
5390
|
+
run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
|
|
5391
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
5392
|
+
writeManifest(stateDir, run2);
|
|
5393
|
+
throw error;
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5054
5396
|
const reusedStages = [];
|
|
5055
5397
|
for (const stage of selectedStages(options.stage)) {
|
|
5056
5398
|
const input = options.inputs?.[stage] ?? previousOutput;
|
|
@@ -5058,10 +5400,23 @@ var runWorkflow = (options) => {
|
|
|
5058
5400
|
const existing = run2.steps.find((step) => step.name === stage);
|
|
5059
5401
|
const artifactPath = existing?.artifactRefs?.[0] ? resolve12(stateDir, existing.artifactRefs[0]) : stageArtifactPath(stateDir, stage, inputHash);
|
|
5060
5402
|
if (existing?.status === "completed" && existing.inputHash === inputHash && existing.outputHash && existsSync16(artifactPath)) {
|
|
5061
|
-
|
|
5403
|
+
try {
|
|
5404
|
+
previousOutput = readVerifiedArtifact(artifactPath, stage, existing).value;
|
|
5405
|
+
} catch (error) {
|
|
5406
|
+
run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
|
|
5407
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
5408
|
+
writeManifest(stateDir, run2);
|
|
5409
|
+
throw error;
|
|
5410
|
+
}
|
|
5062
5411
|
reusedStages.push(stage);
|
|
5063
5412
|
continue;
|
|
5064
5413
|
}
|
|
5414
|
+
if (options.shouldCancel?.()) {
|
|
5415
|
+
run2 = withHash(transition(run2, "cancelled", `Workflow cancellation requested before stage "${stage}".`));
|
|
5416
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
5417
|
+
writeManifest(stateDir, run2);
|
|
5418
|
+
return { run: run2, stateDir, reusedStages };
|
|
5419
|
+
}
|
|
5065
5420
|
const handler = options.handlers[stage];
|
|
5066
5421
|
if (!handler) throw new Error(`No handler configured for workflow stage "${stage}".`);
|
|
5067
5422
|
run2 = withHash(transition(run2, stageState[stage]));
|
|
@@ -5073,7 +5428,7 @@ var runWorkflow = (options) => {
|
|
|
5073
5428
|
const value = handler({ root, stage, input, previousOutput });
|
|
5074
5429
|
const outputHash = sha256NormalizedV1(value);
|
|
5075
5430
|
const artifact2 = { type: "workflow-step-artifact", stage, inputHash, outputHash, value };
|
|
5076
|
-
mkdirSync3(
|
|
5431
|
+
mkdirSync3(join14(stateDir, "artifacts"), { recursive: true });
|
|
5077
5432
|
if (existsSync16(artifactPath)) {
|
|
5078
5433
|
const existingArtifact = readArtifact(artifactPath);
|
|
5079
5434
|
if (existingArtifact.outputHash !== outputHash) throw new Error(`Immutable workflow artifact collision for stage "${stage}".`);
|
|
@@ -5099,14 +5454,14 @@ var runWorkflow = (options) => {
|
|
|
5099
5454
|
run2 = complete;
|
|
5100
5455
|
}
|
|
5101
5456
|
writeManifest(stateDir, run2);
|
|
5102
|
-
if (run2.state === "delivered") atomicWrite(
|
|
5457
|
+
if (run2.state === "delivered") atomicWrite(join14(stateDir, "last-known-good.json"), { runId: run2.runId, manifestHash: run2.contentHash, report: run2.steps.find((step) => step.name === "report")?.artifactRefs?.[0] });
|
|
5103
5458
|
}
|
|
5104
5459
|
return { run: run2, stateDir, reusedStages };
|
|
5105
5460
|
} finally {
|
|
5106
5461
|
release();
|
|
5107
5462
|
}
|
|
5108
5463
|
};
|
|
5109
|
-
var loadWorkflowManifest = (stateDir) => WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(
|
|
5464
|
+
var loadWorkflowManifest = (stateDir) => WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(join14(resolve12(stateDir), "manifest.json"), "utf8")));
|
|
5110
5465
|
var loadWorkflowStepOutput = (stateDir, stage) => stepOutput(resolve12(stateDir), loadWorkflowManifest(stateDir), stage);
|
|
5111
5466
|
|
|
5112
5467
|
// src/doctor/badge.ts
|
|
@@ -5270,13 +5625,13 @@ var docBridgePatternPayload = () => ({
|
|
|
5270
5625
|
// src/cli/demo.ts
|
|
5271
5626
|
import { cpSync, existsSync as existsSync18, mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync2 } from "fs";
|
|
5272
5627
|
import { tmpdir } from "os";
|
|
5273
|
-
import { dirname as
|
|
5628
|
+
import { dirname as dirname7, join as join16, resolve as resolve14 } from "path";
|
|
5274
5629
|
import { fileURLToPath } from "url";
|
|
5275
5630
|
|
|
5276
5631
|
// src/mcp/install.ts
|
|
5277
5632
|
import { existsSync as existsSync17, mkdirSync as mkdirSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync4 } from "fs";
|
|
5278
5633
|
import { homedir } from "os";
|
|
5279
|
-
import { dirname as
|
|
5634
|
+
import { dirname as dirname6, join as join15, resolve as resolve13 } from "path";
|
|
5280
5635
|
var SERVER_NAME = "ak-docs";
|
|
5281
5636
|
var mcpServerEntry = (root) => ({
|
|
5282
5637
|
command: "npx",
|
|
@@ -5293,13 +5648,13 @@ var readJson2 = (path) => {
|
|
|
5293
5648
|
}
|
|
5294
5649
|
};
|
|
5295
5650
|
var writeJson = (path, value) => {
|
|
5296
|
-
mkdirSync4(
|
|
5651
|
+
mkdirSync4(dirname6(path), { recursive: true });
|
|
5297
5652
|
writeFileSync4(path, `${JSON.stringify(value, null, 2)}
|
|
5298
5653
|
`, "utf8");
|
|
5299
5654
|
};
|
|
5300
5655
|
var resolveTargetPath = (target, root) => {
|
|
5301
5656
|
if (target === "cursor") return resolve13(root, ".cursor", "mcp.json");
|
|
5302
|
-
return
|
|
5657
|
+
return join15(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
5303
5658
|
};
|
|
5304
5659
|
var installMcpConfig = (root, target) => {
|
|
5305
5660
|
const configPath = resolveTargetPath(target, root);
|
|
@@ -5328,12 +5683,12 @@ var installMcpConfig = (root, target) => {
|
|
|
5328
5683
|
var mcpSnippet = (root) => JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpServerEntry(root) } }, null, 2);
|
|
5329
5684
|
|
|
5330
5685
|
// src/cli/demo.ts
|
|
5331
|
-
var packageRoot = resolve14(
|
|
5686
|
+
var packageRoot = resolve14(dirname7(fileURLToPath(import.meta.url)), "..", "..");
|
|
5332
5687
|
var fixturePath = (fixture) => {
|
|
5333
5688
|
if (fixture === "monorepo") {
|
|
5334
|
-
return
|
|
5689
|
+
return join16(packageRoot, "examples", "demo-monorepo");
|
|
5335
5690
|
}
|
|
5336
|
-
return
|
|
5691
|
+
return join16(packageRoot, "examples", "demo-example");
|
|
5337
5692
|
};
|
|
5338
5693
|
var formatHandoffText = (handoff) => {
|
|
5339
5694
|
const bridge = handoff.bridge?.humanDoc === "missing" ? `human guide: missing \u2192 ${handoff.bridge.action ?? "ak-docs bootstrap agent-docs"}` : handoff.humanDoc ? `human guide: ${handoff.humanDoc}` : "human guide: (none)";
|
|
@@ -5351,9 +5706,9 @@ var runDemo = (root, config, fixture = "example", options = {}) => {
|
|
|
5351
5706
|
const fixtureDir = fixturePath(fixture);
|
|
5352
5707
|
if (options.copyFixture && existsSync18(fixtureDir)) {
|
|
5353
5708
|
for (const rel of ["doc-bridge.config.json", "docs", "packages", "pnpm-workspace.yaml", "package.json"]) {
|
|
5354
|
-
const src =
|
|
5709
|
+
const src = join16(fixtureDir, rel);
|
|
5355
5710
|
if (!existsSync18(src)) continue;
|
|
5356
|
-
const dest =
|
|
5711
|
+
const dest = join16(root, rel);
|
|
5357
5712
|
cpSync(src, dest, { recursive: true });
|
|
5358
5713
|
}
|
|
5359
5714
|
}
|
|
@@ -5408,14 +5763,14 @@ var formatDemoText = (result) => {
|
|
|
5408
5763
|
return lines;
|
|
5409
5764
|
};
|
|
5410
5765
|
var withDemoWorkspace = (fixture, fn) => {
|
|
5411
|
-
const dir = mkdtempSync(
|
|
5766
|
+
const dir = mkdtempSync(join16(tmpdir(), "ak-docs-demo-"));
|
|
5412
5767
|
try {
|
|
5413
5768
|
const fixtureDir = fixturePath(fixture);
|
|
5414
5769
|
if (!existsSync18(fixtureDir)) {
|
|
5415
5770
|
throw new Error(`Demo fixture "${fixture}" not found at ${fixtureDir}`);
|
|
5416
5771
|
}
|
|
5417
5772
|
cpSync(fixtureDir, dir, { recursive: true });
|
|
5418
|
-
const config = JSON.parse(readFileSync17(
|
|
5773
|
+
const config = JSON.parse(readFileSync17(join16(dir, "doc-bridge.config.json"), "utf8"));
|
|
5419
5774
|
return fn(dir, config);
|
|
5420
5775
|
} finally {
|
|
5421
5776
|
rmSync2(dir, { recursive: true, force: true });
|
|
@@ -5430,16 +5785,6 @@ var gradeForScore = (score) => {
|
|
|
5430
5785
|
if (score >= 40) return "D";
|
|
5431
5786
|
return "F";
|
|
5432
5787
|
};
|
|
5433
|
-
var agentDocPaths = (index) => {
|
|
5434
|
-
const paths = /* @__PURE__ */ new Set();
|
|
5435
|
-
for (const owner of Object.values(index.lookup?.ownership ?? {})) {
|
|
5436
|
-
if (owner.agentDoc) paths.add(owner.agentDoc);
|
|
5437
|
-
}
|
|
5438
|
-
for (const handoff of Object.values(index.handoffs ?? {})) {
|
|
5439
|
-
if (handoff.startHere) paths.add(handoff.startHere);
|
|
5440
|
-
}
|
|
5441
|
-
return paths;
|
|
5442
|
-
};
|
|
5443
5788
|
var computeScore = (coverage) => {
|
|
5444
5789
|
let score = 0;
|
|
5445
5790
|
if (coverage.freshness.hasIndex) score += 15;
|
|
@@ -5527,7 +5872,7 @@ var runDoctor = (root, config) => {
|
|
|
5527
5872
|
let index;
|
|
5528
5873
|
let hasIndex = true;
|
|
5529
5874
|
let freshnessOk = false;
|
|
5530
|
-
let freshnessMessage
|
|
5875
|
+
let freshnessMessage;
|
|
5531
5876
|
try {
|
|
5532
5877
|
index = loadDocBridgeIndex(root, config);
|
|
5533
5878
|
const next = buildDocBridgeIndex({ root, config, write: false }).index.contentHash;
|
|
@@ -5547,8 +5892,6 @@ var runDoctor = (root, config) => {
|
|
|
5547
5892
|
const missingAgentDoc = ownership.filter(([, owner]) => !owner.agentDoc || owner.agentDoc === config.corpus.agent.index).map(([id]) => id);
|
|
5548
5893
|
const missingHumanDoc = ownership.filter(([, owner]) => !owner.humanDoc).map(([id]) => id);
|
|
5549
5894
|
const indexedPaths = new Set(index.knowledge.map((entry) => entry.path));
|
|
5550
|
-
const expectedAgentDocs = agentDocPaths(index);
|
|
5551
|
-
const unindexed = [...expectedAgentDocs].filter((path) => !indexedPaths.has(path));
|
|
5552
5895
|
const corpusDocs = scanAgentCorpus(root, config).filter(
|
|
5553
5896
|
(doc) => doc.path !== config.corpus.agent.index
|
|
5554
5897
|
);
|
|
@@ -5626,13 +5969,13 @@ var formatDoctorText = (report) => {
|
|
|
5626
5969
|
};
|
|
5627
5970
|
|
|
5628
5971
|
// src/mcp/server.ts
|
|
5629
|
-
import { mkdirSync as
|
|
5630
|
-
import { join as
|
|
5972
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync20, realpathSync as realpathSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
5973
|
+
import { join as join19, relative as relative9, resolve as resolve17 } from "path";
|
|
5631
5974
|
import { z as z8, ZodError } from "zod";
|
|
5632
5975
|
|
|
5633
5976
|
// src/fixes/proposals.ts
|
|
5634
|
-
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync18, realpathSync as realpathSync8, renameSync as renameSync2,
|
|
5635
|
-
import { basename as basename5, dirname as
|
|
5977
|
+
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync18, realpathSync as realpathSync8, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync5 } from "fs";
|
|
5978
|
+
import { basename as basename5, dirname as dirname8, extname as extname2, join as join17, relative as relative8, resolve as resolve15, sep as sep8 } from "path";
|
|
5636
5979
|
var hash2 = (value) => sha256NormalizedV1(value);
|
|
5637
5980
|
var artifactMetadata = (root, options) => ({
|
|
5638
5981
|
schemaVersion: 1,
|
|
@@ -5671,7 +6014,7 @@ var makeProposal = (root, options, changes, preconditions, postconditions) => {
|
|
|
5671
6014
|
};
|
|
5672
6015
|
var walkMarkdown = (root, directory = root) => readdirSync4(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
5673
6016
|
if (entry.name === ".git" || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build") return [];
|
|
5674
|
-
const path =
|
|
6017
|
+
const path = join17(directory, entry.name);
|
|
5675
6018
|
if (entry.isDirectory()) return walkMarkdown(root, path);
|
|
5676
6019
|
return entry.isFile() && [".md", ".mdx"].includes(extname2(entry.name).toLowerCase()) ? [relative8(root, path).split(sep8).join("/")] : [];
|
|
5677
6020
|
});
|
|
@@ -5682,18 +6025,18 @@ var createMarkdownLinkFixProposal = (root, options) => {
|
|
|
5682
6025
|
const paths = walkMarkdown(projectRoot);
|
|
5683
6026
|
const changes = [];
|
|
5684
6027
|
for (const path of paths) {
|
|
5685
|
-
const content = readFileSync18(
|
|
6028
|
+
const content = readFileSync18(join17(projectRoot, path), "utf8");
|
|
5686
6029
|
let next = content;
|
|
5687
6030
|
for (const match of content.matchAll(localLink)) {
|
|
5688
6031
|
const target = match[3];
|
|
5689
6032
|
if (!target || match[1] === "!" || /^(?:[a-z]+:|\/|#)/i.test(target)) continue;
|
|
5690
6033
|
const targetPath = target.split("#")[0]?.split("?")[0];
|
|
5691
|
-
if (!targetPath || existsSync19(resolve15(projectRoot,
|
|
6034
|
+
if (!targetPath || existsSync19(resolve15(projectRoot, dirname8(path), targetPath))) continue;
|
|
5692
6035
|
const targetStem = basename5(targetPath, extname2(targetPath)).toLowerCase();
|
|
5693
6036
|
const labelStem = (match[2] ?? "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
5694
6037
|
const candidates = paths.filter((candidate) => basename5(candidate, extname2(candidate)).toLowerCase() === targetStem || basename5(candidate, extname2(candidate)).toLowerCase().replace(/[^a-z0-9]+/g, "") === labelStem);
|
|
5695
6038
|
if (candidates.length !== 1) continue;
|
|
5696
|
-
let replacement = relative8(
|
|
6039
|
+
let replacement = relative8(dirname8(path), candidates[0]).split(sep8).join("/");
|
|
5697
6040
|
if (target.startsWith("./") && !replacement.startsWith(".")) replacement = `./${replacement}`;
|
|
5698
6041
|
next = next.replace(match[0], match[0].replace(target, replacement));
|
|
5699
6042
|
}
|
|
@@ -5705,8 +6048,13 @@ var createArtifactNormalizationProposal = (root, artifactPath, options) => {
|
|
|
5705
6048
|
const projectRoot = realpathSync8.native(resolve15(root));
|
|
5706
6049
|
const path = artifactPath.split(sep8).join("/");
|
|
5707
6050
|
const absolute = containedPath(projectRoot, path);
|
|
5708
|
-
if (!absolute
|
|
5709
|
-
|
|
6051
|
+
if (!absolute) return void 0;
|
|
6052
|
+
let before;
|
|
6053
|
+
try {
|
|
6054
|
+
before = readFileSync18(absolute, "utf8");
|
|
6055
|
+
} catch {
|
|
6056
|
+
return void 0;
|
|
6057
|
+
}
|
|
5710
6058
|
let after;
|
|
5711
6059
|
try {
|
|
5712
6060
|
after = `${JSON.stringify(sortJson(JSON.parse(before)), null, 2)}
|
|
@@ -5771,8 +6119,8 @@ var applyFixProposal = (root, proposalInput, options = {}) => {
|
|
|
5771
6119
|
};
|
|
5772
6120
|
|
|
5773
6121
|
// src/agents/registry-adapter.ts
|
|
5774
|
-
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync6, mkdirSync as
|
|
5775
|
-
import { join as
|
|
6122
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5 } from "fs";
|
|
6123
|
+
import { join as join18, resolve as resolve16 } from "path";
|
|
5776
6124
|
import { pathToFileURL } from "url";
|
|
5777
6125
|
import { z as z7 } from "zod";
|
|
5778
6126
|
var DEFAULT_REGISTRY_AGENT_ID = "ecosystem-doc-bridge-corpus-scanner";
|
|
@@ -5794,7 +6142,7 @@ var registryConfig = (config) => config.intelligence?.registry;
|
|
|
5794
6142
|
var loadRegistryAgentRunner = async (root, config) => {
|
|
5795
6143
|
const metadata = loadRegistryAgentMetadata(root, config);
|
|
5796
6144
|
const configured = registryConfig(config)?.runnerModule;
|
|
5797
|
-
const modulePath = configured ? containedPath(root, configured) : containedPath(root,
|
|
6145
|
+
const modulePath = configured ? containedPath(root, configured) : containedPath(root, join18(metadata.root, "doc-bridge-adapter.js"));
|
|
5798
6146
|
if (!modulePath || !existsSync20(modulePath)) throw new Error(`Registry agent "${metadata.id}" has no local runner module. Configure intelligence.registry.runnerModule or add doc-bridge-adapter.js to the installed agent.`);
|
|
5799
6147
|
const loaded = await import(pathToFileURL(modulePath).href);
|
|
5800
6148
|
const runner = typeof loaded.run === "function" ? loaded.run : typeof loaded.default === "function" ? loaded.default : loaded.default && typeof loaded.default === "object" && "run" in loaded.default && typeof loaded.default.run === "function" ? loaded.default.run : void 0;
|
|
@@ -5805,9 +6153,9 @@ var loadRegistryAgentMetadata = (root, config) => {
|
|
|
5805
6153
|
const settings = registryConfig(config);
|
|
5806
6154
|
const id = settings?.agentId ?? DEFAULT_REGISTRY_AGENT_ID;
|
|
5807
6155
|
const agentRoot = settings?.agentRoot ?? "agents";
|
|
5808
|
-
const agentPath = containedPath(root,
|
|
5809
|
-
if (!agentPath || !existsSync20(agentPath)) throw new Error(`AgentsKit Registry agent "${id}" is not installed at ${
|
|
5810
|
-
const metadataPath = [
|
|
6156
|
+
const agentPath = containedPath(root, join18(agentRoot, id));
|
|
6157
|
+
if (!agentPath || !existsSync20(agentPath)) throw new Error(`AgentsKit Registry agent "${id}" is not installed at ${join18(agentRoot, id)}. Install it with: npx agentskit add ${id}`);
|
|
6158
|
+
const metadataPath = [join18(agentPath, "agent.json"), join18(agentPath, "manifest.json")].find(existsSync20);
|
|
5811
6159
|
if (!metadataPath) throw new Error(`Registry agent "${id}" is installed but has no agent.json or manifest.json metadata.`);
|
|
5812
6160
|
const metadata = RegistryAgentMetadataSchema.parse(JSON.parse(readFileSync19(metadataPath, "utf8")));
|
|
5813
6161
|
if (metadata.id !== id) throw new Error(`Installed Registry agent metadata id "${metadata.id}" does not match configured id "${id}".`);
|
|
@@ -5816,15 +6164,40 @@ var loadRegistryAgentMetadata = (root, config) => {
|
|
|
5816
6164
|
var createRegistryAgentAdapter = (root, config, runner) => {
|
|
5817
6165
|
if (!registryConfig(config)?.enabled) throw new Error("Registry agents are disabled. Set intelligence.registry.enabled: true to run an assisted workflow.");
|
|
5818
6166
|
const metadata = loadRegistryAgentMetadata(resolve16(root), config);
|
|
6167
|
+
const settings = registryConfig(config) ?? {};
|
|
6168
|
+
const timeoutMs = settings.timeoutMs ?? 12e4;
|
|
6169
|
+
const maxResponseBytes = settings.maxResponseBytes ?? 256e3;
|
|
6170
|
+
const maxTokens = settings.maxTokens ?? Math.ceil(maxResponseBytes / 4);
|
|
6171
|
+
const maxConcurrency = settings.maxConcurrency ?? 1;
|
|
6172
|
+
let active = 0;
|
|
6173
|
+
const deterministicCache = /* @__PURE__ */ new Map();
|
|
5819
6174
|
return {
|
|
5820
6175
|
metadata,
|
|
5821
6176
|
run: async (snapshot, report, evidence2 = report.diagnostics.flatMap((diagnostic2) => diagnostic2.evidence).slice(0, 64)) => {
|
|
5822
|
-
|
|
5823
|
-
const
|
|
5824
|
-
if (
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
6177
|
+
if (active >= maxConcurrency) throw new Error(`Registry agent concurrency limit ${maxConcurrency} exceeded.`);
|
|
6178
|
+
const cacheKey = sha256NormalizedV1({ snapshotHash: snapshot.contentHash, reportHash: report.contentHash, agentId: metadata.id, agentVersion: metadata.version, evidence: evidence2 });
|
|
6179
|
+
if (settings.deterministic && deterministicCache.has(cacheKey)) return deterministicCache.get(cacheKey);
|
|
6180
|
+
active += 1;
|
|
6181
|
+
let timer;
|
|
6182
|
+
try {
|
|
6183
|
+
const context = deepFreeze({ snapshot: redactValue(snapshot), report: redactValue(report), evidence: redactValue(evidence2), capabilities: ["snapshot.read", "evidence.read", "proposal.write"], network: false, shell: false, deterministic: settings.deterministic ?? true });
|
|
6184
|
+
const timeout = new Promise((_, reject) => {
|
|
6185
|
+
timer = setTimeout(() => reject(new Error(`Registry agent timed out after ${timeoutMs}ms.`)), timeoutMs);
|
|
6186
|
+
});
|
|
6187
|
+
const raw = await Promise.race([Promise.resolve(runner(context)), timeout]);
|
|
6188
|
+
const responseBytes = Buffer.byteLength(JSON.stringify(raw));
|
|
6189
|
+
if (responseBytes > maxResponseBytes) throw new Error(`Registry agent response limit ${maxResponseBytes} bytes exceeded.`);
|
|
6190
|
+
if (Math.ceil(responseBytes / 4) > maxTokens) throw new Error(`Registry agent token budget ${maxTokens} exceeded.`);
|
|
6191
|
+
const proposal = AgentProposalV1Schema.parse(raw);
|
|
6192
|
+
if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Registry agent proposal contentHash does not match its canonical contents.");
|
|
6193
|
+
if (proposal.baseSnapshotHash !== snapshot.contentHash || proposal.baseReportHash !== report.contentHash) throw new Error("Registry agent proposal is not based on the supplied snapshot/report hashes.");
|
|
6194
|
+
if (proposal.origin.kind !== "registry-agent" || proposal.origin.id !== metadata.id) throw new Error(`Registry agent proposal origin must be ${metadata.id}.`);
|
|
6195
|
+
if (settings.deterministic) deterministicCache.set(cacheKey, proposal);
|
|
6196
|
+
return proposal;
|
|
6197
|
+
} finally {
|
|
6198
|
+
if (timer) clearTimeout(timer);
|
|
6199
|
+
active -= 1;
|
|
6200
|
+
}
|
|
5828
6201
|
}
|
|
5829
6202
|
};
|
|
5830
6203
|
};
|
|
@@ -5832,8 +6205,8 @@ var persistRegistryAgentProposal = (stateDir, proposal) => {
|
|
|
5832
6205
|
AgentProposalV1Schema.parse(proposal);
|
|
5833
6206
|
if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Cannot persist a Registry agent proposal with an invalid contentHash.");
|
|
5834
6207
|
const safeHash = contentHashForArtifactV1(proposal);
|
|
5835
|
-
|
|
5836
|
-
const path =
|
|
6208
|
+
mkdirSync5(join18(resolve16(stateDir), "agents"), { recursive: true });
|
|
6209
|
+
const path = join18(resolve16(stateDir), "agents", `${proposal.origin.id}-${safeHash}.json`);
|
|
5837
6210
|
writeFileSync6(path, `${JSON.stringify(proposal, null, 2)}
|
|
5838
6211
|
`, "utf8");
|
|
5839
6212
|
return path;
|
|
@@ -6030,7 +6403,7 @@ var workflowReport = (ctx, runId2) => {
|
|
|
6030
6403
|
ensureLatestRun(ctx, runId2);
|
|
6031
6404
|
return parseReconciliationReport(loadWorkflowStepOutput(workflowStateDir(ctx), "reconcile"));
|
|
6032
6405
|
};
|
|
6033
|
-
var proposalPath = (ctx) =>
|
|
6406
|
+
var proposalPath = (ctx) => join19(ctx.root, ".doc-bridge", "proposal.json");
|
|
6034
6407
|
var readSavedProposal = (ctx, input) => {
|
|
6035
6408
|
if (input !== void 0) return FixProposalV1Schema.parse(input);
|
|
6036
6409
|
try {
|
|
@@ -6040,7 +6413,7 @@ var readSavedProposal = (ctx, input) => {
|
|
|
6040
6413
|
}
|
|
6041
6414
|
};
|
|
6042
6415
|
var saveProposal = (ctx, proposal) => {
|
|
6043
|
-
|
|
6416
|
+
mkdirSync6(join19(ctx.root, ".doc-bridge"), { recursive: true });
|
|
6044
6417
|
writeFileSync7(proposalPath(ctx), `${JSON.stringify(proposal, null, 2)}
|
|
6045
6418
|
`, "utf8");
|
|
6046
6419
|
};
|
|
@@ -6257,11 +6630,13 @@ var startMcpStdioServer = (ctx) => {
|
|
|
6257
6630
|
|
|
6258
6631
|
// src/reconciliation/reconcile.ts
|
|
6259
6632
|
var ignoredDocumentationRelations = /* @__PURE__ */ new Set(["covers"]);
|
|
6633
|
+
var isInternalEntity = (id) => !id.startsWith("external:") && !id.startsWith("unresolved:");
|
|
6260
6634
|
var metadataDetection = (relation) => {
|
|
6261
6635
|
const detection = relation.metadata?.detection;
|
|
6262
6636
|
return typeof detection === "string" ? detection : void 0;
|
|
6263
6637
|
};
|
|
6264
|
-
var
|
|
6638
|
+
var normalizeDetection = (value) => value === "dynamic-literal" ? "dynamic" : value;
|
|
6639
|
+
var relationDetection = (relation) => normalizeDetection(relation.discriminator ?? metadataDetection(relation) ?? "static");
|
|
6265
6640
|
var entityResolver = (snapshots) => {
|
|
6266
6641
|
const references = /* @__PURE__ */ new Map();
|
|
6267
6642
|
const entities = snapshots.flatMap((snapshot) => snapshot.entities).sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -6282,6 +6657,25 @@ var mergeEvidence = (...relations) => {
|
|
|
6282
6657
|
}
|
|
6283
6658
|
return [...merged.values()].sort((a, b) => evidenceKey(a).localeCompare(evidenceKey(b)));
|
|
6284
6659
|
};
|
|
6660
|
+
var boundedEvidence = (evidence2) => [...evidence2].slice(0, 64);
|
|
6661
|
+
var diagnosticCounts = (values) => {
|
|
6662
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6663
|
+
for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
6664
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
6665
|
+
};
|
|
6666
|
+
var documentClass = (entity) => {
|
|
6667
|
+
const classification = entity.metadata?.classification;
|
|
6668
|
+
return typeof classification === "string" && classification.length > 0 ? classification : "unclassified";
|
|
6669
|
+
};
|
|
6670
|
+
var countDocumentClasses = (documents, selectedIds) => {
|
|
6671
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6672
|
+
for (const document of documents) {
|
|
6673
|
+
if (selectedIds && !selectedIds.has(document.id)) continue;
|
|
6674
|
+
const classification = documentClass(document);
|
|
6675
|
+
counts.set(classification, (counts.get(classification) ?? 0) + 1);
|
|
6676
|
+
}
|
|
6677
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
6678
|
+
};
|
|
6285
6679
|
var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
|
|
6286
6680
|
var coverageAvailable = (snapshot, relation) => {
|
|
6287
6681
|
const status = snapshot.coverage.find(
|
|
@@ -6297,6 +6691,62 @@ var entityById = (snapshots) => {
|
|
|
6297
6691
|
return entities;
|
|
6298
6692
|
};
|
|
6299
6693
|
var isUnresolved = (id, entities) => id.startsWith("unresolved:") || entities.get(id)?.kind === "unresolved-reference";
|
|
6694
|
+
var normalizedPath = (path) => path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
6695
|
+
var semanticEntityId = (id, scope, entities, packageByModule) => {
|
|
6696
|
+
if (scope === "file" || id.startsWith("external:") || id.startsWith("unresolved:")) return id;
|
|
6697
|
+
const entity = entities.get(id);
|
|
6698
|
+
if (!entity || entity.kind !== "module" || !entity.path) return id;
|
|
6699
|
+
if (scope === "module") return id;
|
|
6700
|
+
return packageByModule.get(id) ?? id;
|
|
6701
|
+
};
|
|
6702
|
+
var packageLookup = (scope, entities) => {
|
|
6703
|
+
if (scope !== "package") return /* @__PURE__ */ new Map();
|
|
6704
|
+
const packages = [...entities.values()].filter((entity) => entity.kind === "package" && entity.path).map((entity) => ({ id: entity.id, path: normalizedPath(entity.path) })).sort((a, b) => b.path.length - a.path.length || a.id.localeCompare(b.id));
|
|
6705
|
+
const result = /* @__PURE__ */ new Map();
|
|
6706
|
+
for (const entity of entities.values()) {
|
|
6707
|
+
if (entity.kind !== "module" || !entity.path) continue;
|
|
6708
|
+
const modulePath = normalizedPath(entity.path);
|
|
6709
|
+
const packageEntity = packages.find(({ path }) => path === "." || modulePath === path || modulePath.startsWith(`${path}/`));
|
|
6710
|
+
if (packageEntity) result.set(entity.id, packageEntity.id);
|
|
6711
|
+
}
|
|
6712
|
+
return result;
|
|
6713
|
+
};
|
|
6714
|
+
var aggregatedRelations = (relations, scope, entities, packageByModule) => {
|
|
6715
|
+
if (scope === "file") return [...relations];
|
|
6716
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6717
|
+
for (const relation of relations) {
|
|
6718
|
+
const from = semanticEntityId(relation.from, scope, entities, packageByModule);
|
|
6719
|
+
const to = semanticEntityId(relation.to, scope, entities, packageByModule);
|
|
6720
|
+
const detection = relationDetection(relation);
|
|
6721
|
+
const key = `${from}\0${to}\0${relation.kind}\0${detection}`;
|
|
6722
|
+
const group = groups.get(key) ?? [];
|
|
6723
|
+
group.push(relation);
|
|
6724
|
+
groups.set(key, group);
|
|
6725
|
+
}
|
|
6726
|
+
return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, group]) => {
|
|
6727
|
+
const first = group[0];
|
|
6728
|
+
const parts = key.split("\0");
|
|
6729
|
+
const from = parts[0] ?? first.from;
|
|
6730
|
+
const to = parts[1] ?? first.to;
|
|
6731
|
+
const detection = parts[3] ?? relationDetection(first);
|
|
6732
|
+
const mergedEvidence = mergeEvidence(...group);
|
|
6733
|
+
return {
|
|
6734
|
+
...first,
|
|
6735
|
+
id: `relation:aggregated:${scope}:${sha256NormalizedV1(key).slice(0, 32)}`,
|
|
6736
|
+
from,
|
|
6737
|
+
to,
|
|
6738
|
+
...detection === "static" && !first.discriminator ? {} : { discriminator: detection },
|
|
6739
|
+
evidence: boundedEvidence(mergedEvidence),
|
|
6740
|
+
metadata: {
|
|
6741
|
+
...first.metadata ?? {},
|
|
6742
|
+
aggregationScope: scope,
|
|
6743
|
+
aggregatedRelationCount: group.length,
|
|
6744
|
+
aggregatedEvidenceCount: mergedEvidence.length,
|
|
6745
|
+
...mergedEvidence.length > 64 ? { evidenceTruncated: true } : {}
|
|
6746
|
+
}
|
|
6747
|
+
};
|
|
6748
|
+
});
|
|
6749
|
+
};
|
|
6300
6750
|
var relationMatches = (observed, declared, resolveEntity2) => {
|
|
6301
6751
|
if (relationBase(observed, resolveEntity2) !== relationBase(declared, resolveEntity2)) return false;
|
|
6302
6752
|
return relationDetection(declared) === relationDetection(observed);
|
|
@@ -6307,7 +6757,7 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
6307
6757
|
status,
|
|
6308
6758
|
severity,
|
|
6309
6759
|
message,
|
|
6310
|
-
evidence:
|
|
6760
|
+
evidence: boundedEvidence(evidence2),
|
|
6311
6761
|
...entityIds?.length ? { entityIds: [...entityIds] } : {},
|
|
6312
6762
|
...relationIds?.length ? { relationIds: [...relationIds] } : {},
|
|
6313
6763
|
...remediation ? { remediation } : {}
|
|
@@ -6315,10 +6765,30 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
6315
6765
|
var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
6316
6766
|
const resolveEntity2 = entityResolver([observed, declared]);
|
|
6317
6767
|
const entities = entityById([observed, declared]);
|
|
6318
|
-
const
|
|
6319
|
-
const
|
|
6768
|
+
const scope = options.scope ?? "file";
|
|
6769
|
+
const packageByModule = packageLookup(scope, entities);
|
|
6770
|
+
const allDeclaredRelations = declared.relations.filter((relation) => relation.provenance === "declared");
|
|
6771
|
+
const observedRelations = aggregatedRelations(observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
|
|
6772
|
+
const declaredRelations = aggregatedRelations(declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
|
|
6320
6773
|
const requiredRelationKinds = options.requiredRelationKinds === void 0 ? void 0 : new Set(options.requiredRelationKinds);
|
|
6321
6774
|
const diagnostics = [];
|
|
6775
|
+
if (options.includeOrphanedDocuments) {
|
|
6776
|
+
const declaredDocumentIds = new Set(allDeclaredRelations.map((relation) => relation.from).filter((id) => id.startsWith("document:")));
|
|
6777
|
+
for (const document of observed.entities.filter((entity) => entity.kind === "document").sort((a, b) => a.id.localeCompare(b.id))) {
|
|
6778
|
+
if (declaredDocumentIds.has(document.id)) continue;
|
|
6779
|
+
diagnostics.push(reportDiagnostic(
|
|
6780
|
+
"DOCUMENTATION_ORPHANED",
|
|
6781
|
+
"undocumented",
|
|
6782
|
+
"info",
|
|
6783
|
+
"Markdown document has no Doc Bridge declaration linking it to observed knowledge.",
|
|
6784
|
+
document.evidence,
|
|
6785
|
+
document.id,
|
|
6786
|
+
[document.id],
|
|
6787
|
+
void 0,
|
|
6788
|
+
"Add a docbridge declaration or exclude this document from the documentation comparison scope."
|
|
6789
|
+
));
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6322
6792
|
for (const entity of declared.entities.filter((item) => isUnresolved(item.id, entities)).sort((a, b) => a.id.localeCompare(b.id))) {
|
|
6323
6793
|
diagnostics.push(reportDiagnostic(
|
|
6324
6794
|
"UNRESOLVED_ENTITY_REFERENCE",
|
|
@@ -6339,9 +6809,18 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6339
6809
|
group.push(relation);
|
|
6340
6810
|
declaredByBase.set(base2, group);
|
|
6341
6811
|
}
|
|
6812
|
+
const observedDetectionsByBase = /* @__PURE__ */ new Map();
|
|
6813
|
+
for (const relation of observedRelations) {
|
|
6814
|
+
const base2 = relationBase(relation, resolveEntity2);
|
|
6815
|
+
const detections = observedDetectionsByBase.get(base2) ?? /* @__PURE__ */ new Set();
|
|
6816
|
+
detections.add(relationDetection(relation));
|
|
6817
|
+
observedDetectionsByBase.set(base2, detections);
|
|
6818
|
+
}
|
|
6342
6819
|
for (const group of declaredByBase.values()) {
|
|
6343
6820
|
const detections = new Set(group.map(relationDetection));
|
|
6344
6821
|
if (detections.size < 2) continue;
|
|
6822
|
+
const observedDetections = observedDetectionsByBase.get(relationBase(group[0], resolveEntity2));
|
|
6823
|
+
if (observedDetections && [...detections].every((detection) => observedDetections.has(detection))) continue;
|
|
6345
6824
|
diagnostics.push(reportDiagnostic(
|
|
6346
6825
|
"CONFLICTING_DECLARATIONS",
|
|
6347
6826
|
"conflict",
|
|
@@ -6368,7 +6847,7 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6368
6847
|
void 0,
|
|
6369
6848
|
[relation.id, match.id]
|
|
6370
6849
|
));
|
|
6371
|
-
} else if (coverageAvailable(observed, relation) && (requiredRelationKinds === void 0 || requiredRelationKinds.has(relation.kind))) {
|
|
6850
|
+
} 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))) {
|
|
6372
6851
|
diagnostics.push(reportDiagnostic(
|
|
6373
6852
|
"RELATION_UNDOCUMENTED",
|
|
6374
6853
|
"undocumented",
|
|
@@ -6414,6 +6893,33 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6414
6893
|
}
|
|
6415
6894
|
}
|
|
6416
6895
|
const sortedDiagnostics = [...diagnostics].sort((a, b) => a.id.localeCompare(b.id));
|
|
6896
|
+
const packageEntities = observed.entities.filter((entity) => entity.kind === "package");
|
|
6897
|
+
const packageStatus = { fresh: 0, stale: 0, missing: 0, unverified: 0 };
|
|
6898
|
+
const diagnosticStatusByEntity = /* @__PURE__ */ new Map();
|
|
6899
|
+
const relationEndpoints = new Map([...observedRelations, ...declaredRelations].map((relation) => [relation.id, [relation.from, relation.to]]));
|
|
6900
|
+
for (const diagnostic2 of sortedDiagnostics) for (const id of [...diagnostic2.entityIds ?? [], ...(diagnostic2.relationIds ?? []).flatMap((relationId) => relationEndpoints.get(relationId) ?? [])]) {
|
|
6901
|
+
const statuses = diagnosticStatusByEntity.get(id) ?? /* @__PURE__ */ new Set();
|
|
6902
|
+
statuses.add(diagnostic2.status);
|
|
6903
|
+
diagnosticStatusByEntity.set(id, statuses);
|
|
6904
|
+
}
|
|
6905
|
+
for (const packageEntity of packageEntities) {
|
|
6906
|
+
const docs = allDeclaredRelations.filter((relation) => relation.from.startsWith("document:") && relation.to === packageEntity.id);
|
|
6907
|
+
const statuses = diagnosticStatusByEntity.get(packageEntity.id) ?? /* @__PURE__ */ new Set();
|
|
6908
|
+
if (!docs.length) packageStatus.missing += 1;
|
|
6909
|
+
else if (statuses.has("conflict") || statuses.has("stale-or-unverified")) packageStatus.stale += 1;
|
|
6910
|
+
else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
|
|
6911
|
+
else packageStatus.fresh += 1;
|
|
6912
|
+
}
|
|
6913
|
+
const documents = observed.entities.filter((entity) => entity.kind === "document");
|
|
6914
|
+
const documentedDocumentIds = new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from));
|
|
6915
|
+
const documentation = {
|
|
6916
|
+
documentCount: documents.length,
|
|
6917
|
+
documentedDocumentCount: documentedDocumentIds.size,
|
|
6918
|
+
documentClassificationCounts: countDocumentClasses(documents),
|
|
6919
|
+
documentedDocumentClassificationCounts: countDocumentClasses(documents, documentedDocumentIds),
|
|
6920
|
+
packageCount: packageEntities.length,
|
|
6921
|
+
packageStatus
|
|
6922
|
+
};
|
|
6417
6923
|
const base = {
|
|
6418
6924
|
type: "reconciliation-report",
|
|
6419
6925
|
schemaVersion: 1,
|
|
@@ -6431,7 +6937,12 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6431
6937
|
entityCount: observed.entities.length,
|
|
6432
6938
|
relationCount: observedRelations.length,
|
|
6433
6939
|
diagnosticCount: sortedDiagnostics.length,
|
|
6434
|
-
...options.
|
|
6940
|
+
...options.scope === void 0 ? {} : { scope: options.scope },
|
|
6941
|
+
...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
|
|
6942
|
+
...options.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: options.requiredRelationTargets },
|
|
6943
|
+
diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
|
|
6944
|
+
diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
|
|
6945
|
+
documentation
|
|
6435
6946
|
}
|
|
6436
6947
|
};
|
|
6437
6948
|
return ReconciliationReportV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
@@ -6454,6 +6965,7 @@ var reportGroupKey = (entity, appLayout) => {
|
|
|
6454
6965
|
return packageFamily(entity);
|
|
6455
6966
|
};
|
|
6456
6967
|
var titleCase = (value) => value.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
6968
|
+
var levelChunkName = (scope, id) => `chunks/levels-${scope}-${sha256NormalizedV1(id).slice(0, 12)}.js`;
|
|
6457
6969
|
var buildReportViewModel = (snapshot) => {
|
|
6458
6970
|
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
6459
6971
|
const packages = snapshot.entities.filter((entity) => entity.kind === "package");
|
|
@@ -6467,6 +6979,7 @@ var buildReportViewModel = (snapshot) => {
|
|
|
6467
6979
|
const appLayout = packages.some((entity) => pathParts(entity.path)[0] === "apps");
|
|
6468
6980
|
const groups = /* @__PURE__ */ new Map();
|
|
6469
6981
|
const entityGroup = {};
|
|
6982
|
+
const groupModuleCounts = /* @__PURE__ */ new Map();
|
|
6470
6983
|
for (const entity of packages) {
|
|
6471
6984
|
const family = reportGroupKey(entity, appLayout);
|
|
6472
6985
|
const id = `group:${family}`;
|
|
@@ -6483,6 +6996,7 @@ var buildReportViewModel = (snapshot) => {
|
|
|
6483
6996
|
for (const [entityId2, packageId2] of packageForEntity) {
|
|
6484
6997
|
const groupId = entityGroup[packageId2];
|
|
6485
6998
|
if (groupId) entityGroup[entityId2] = groupId;
|
|
6999
|
+
if (groupId && entities.get(entityId2)?.kind === "module") groupModuleCounts.set(groupId, (groupModuleCounts.get(groupId) ?? 0) + 1);
|
|
6486
7000
|
}
|
|
6487
7001
|
if (externalIds.size) {
|
|
6488
7002
|
const externalGroupId = "group:external-dependencies";
|
|
@@ -6524,61 +7038,184 @@ var buildReportViewModel = (snapshot) => {
|
|
|
6524
7038
|
nodes: overviewNodes.sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id)),
|
|
6525
7039
|
edges: [...edgeMap.values()].filter((edge) => overviewNodeIds.has(edge.from) && overviewNodeIds.has(edge.to)).map((edge) => ({ ...edge, kinds: [...edge.kinds].sort(), relationIds: [...edge.relationIds].sort() })).sort((left, right) => right.count - left.count || left.from.localeCompare(right.from) || left.to.localeCompare(right.to))
|
|
6526
7040
|
},
|
|
6527
|
-
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort() }])),
|
|
6528
|
-
entityGroup
|
|
7041
|
+
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort(), moduleCount: groupModuleCounts.get(id) ?? 0 }])),
|
|
7042
|
+
entityGroup,
|
|
7043
|
+
levelChunks: {
|
|
7044
|
+
default: "chunks/levels-packages.js",
|
|
7045
|
+
groups: Object.fromEntries([...groups.keys()].map((id) => [id, levelChunkName("group", id)])),
|
|
7046
|
+
packages: [...packages].sort((left, right) => left.id.localeCompare(right.id)).map((entity) => levelChunkName("package", entity.id))
|
|
7047
|
+
}
|
|
6529
7048
|
};
|
|
6530
7049
|
};
|
|
6531
7050
|
var escapeHtml = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6532
7051
|
var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
6533
7052
|
var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
6534
|
-
var reportData = (snapshot, report, includeSnippets) =>
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
7053
|
+
var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
|
|
7054
|
+
const evidence2 = (items) => items.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item);
|
|
7055
|
+
const base = {
|
|
7056
|
+
project: snapshot.project,
|
|
7057
|
+
revision: snapshot.sourceRevision,
|
|
7058
|
+
revisionKind: snapshot.sourceRevisionKind,
|
|
7059
|
+
snapshotHash: snapshot.contentHash,
|
|
7060
|
+
reportHash: report.contentHash,
|
|
7061
|
+
configurationHash: snapshot.configurationHash,
|
|
7062
|
+
pipelineVersion: snapshot.pipelineVersion,
|
|
7063
|
+
analyzerVersions: snapshot.analyzerVersions,
|
|
7064
|
+
diagnosticCount: report.diagnostics.length,
|
|
7065
|
+
actionableCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length,
|
|
7066
|
+
confirmedCount: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length,
|
|
7067
|
+
requiredRelationKinds: report.summary.requiredRelationKinds,
|
|
7068
|
+
requiredRelationTargets: report.summary.requiredRelationTargets,
|
|
7069
|
+
documentation: report.summary.documentation,
|
|
7070
|
+
diagnosticSummary: {
|
|
7071
|
+
errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
|
|
7072
|
+
warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
|
|
7073
|
+
undocumented: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "undocumented").length,
|
|
7074
|
+
drift: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "stale-or-unverified" || diagnostic2.status === "conflict").length
|
|
7075
|
+
},
|
|
7076
|
+
entities: snapshot.entities.map((entity, index) => ({
|
|
7077
|
+
id: entity.id,
|
|
7078
|
+
anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
|
|
7079
|
+
kind: entity.kind,
|
|
7080
|
+
name: entity.name,
|
|
7081
|
+
path: entity.path,
|
|
7082
|
+
provenance: entity.provenance,
|
|
7083
|
+
evidence: evidence2(entity.evidence)
|
|
7084
|
+
})),
|
|
7085
|
+
relations: snapshot.relations.map((relation) => ({
|
|
7086
|
+
id: relation.id,
|
|
7087
|
+
kind: relation.kind,
|
|
7088
|
+
from: relation.from,
|
|
7089
|
+
to: relation.to,
|
|
7090
|
+
provenance: relation.provenance,
|
|
7091
|
+
evidence: evidence2(relation.evidence)
|
|
7092
|
+
})),
|
|
7093
|
+
diagnostics: report.diagnostics.map((diagnostic2) => ({
|
|
7094
|
+
id: diagnostic2.id,
|
|
7095
|
+
code: diagnostic2.code,
|
|
7096
|
+
status: diagnostic2.status,
|
|
7097
|
+
severity: diagnostic2.severity,
|
|
7098
|
+
message: diagnostic2.message,
|
|
7099
|
+
entityIds: diagnostic2.entityIds ?? [],
|
|
7100
|
+
relationIds: diagnostic2.relationIds ?? [],
|
|
7101
|
+
remediation: diagnostic2.remediation,
|
|
7102
|
+
evidence: evidence2(diagnostic2.evidence)
|
|
7103
|
+
})),
|
|
7104
|
+
coverage: snapshot.coverage,
|
|
7105
|
+
view: buildReportViewModel(snapshot)
|
|
7106
|
+
};
|
|
7107
|
+
if (privacy !== "anonymized") return base;
|
|
7108
|
+
const pad = (value) => String(value).padStart(3, "0");
|
|
7109
|
+
const sortedEntities = [...base.entities].sort((left, right) => left.id.localeCompare(right.id));
|
|
7110
|
+
const entityOrdinals = /* @__PURE__ */ new Map();
|
|
7111
|
+
const entityIds = new Map(sortedEntities.map((entity) => {
|
|
7112
|
+
const next = (entityOrdinals.get(entity.kind) ?? 0) + 1;
|
|
7113
|
+
entityOrdinals.set(entity.kind, next);
|
|
7114
|
+
return [entity.id, `entity:${entity.kind}-${pad(next)}`];
|
|
7115
|
+
}));
|
|
7116
|
+
const entityNames = /* @__PURE__ */ new Map();
|
|
7117
|
+
const nameFor = (kind) => {
|
|
7118
|
+
const prefix = kind === "external" ? "dependency" : kind;
|
|
7119
|
+
const next = (entityNames.get(prefix) ?? 0) + 1;
|
|
7120
|
+
entityNames.set(prefix, next);
|
|
7121
|
+
return `${prefix}-${pad(next)}`;
|
|
7122
|
+
};
|
|
7123
|
+
const sortedRelations = [...base.relations].sort((left, right) => left.id.localeCompare(right.id));
|
|
7124
|
+
const relationIds = new Map(sortedRelations.map((relation, index) => [relation.id, `relation:${pad(index + 1)}`]));
|
|
7125
|
+
const sortedDiagnostics = [...base.diagnostics].sort((left, right) => left.id.localeCompare(right.id));
|
|
7126
|
+
const diagnosticIds = new Map(sortedDiagnostics.map((diagnostic2, index) => [diagnostic2.id, `finding:${pad(index + 1)}`]));
|
|
7127
|
+
const genericEvidence = (items) => items.map((item, index) => ({
|
|
7128
|
+
source: item.source,
|
|
7129
|
+
path: `evidence-${pad(index + 1)}`,
|
|
7130
|
+
...item.lineStart === void 0 ? {} : { lineStart: item.lineStart },
|
|
7131
|
+
...item.lineEnd === void 0 ? {} : { lineEnd: item.lineEnd }
|
|
7132
|
+
}));
|
|
7133
|
+
const groups = Object.entries(base.view.groups).sort(([left], [right]) => left.localeCompare(right));
|
|
7134
|
+
const groupIds = new Map(groups.map(([id], index) => [id, `group:${pad(index + 1)}`]));
|
|
7135
|
+
const groupNames = /* @__PURE__ */ new Map();
|
|
7136
|
+
const groupName = (kind) => {
|
|
7137
|
+
if (kind === "external") return "External dependencies";
|
|
7138
|
+
if (kind === "shared") return "Shared packages";
|
|
7139
|
+
if (kind === "workspace") return "Workspace";
|
|
7140
|
+
const next = (groupNames.get(kind) ?? 0) + 1;
|
|
7141
|
+
groupNames.set(kind, next);
|
|
7142
|
+
return `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${next}`;
|
|
7143
|
+
};
|
|
7144
|
+
const sanitizedGroups = Object.fromEntries(groups.map(([id, group]) => [groupIds.get(id), {
|
|
7145
|
+
kind: group.kind,
|
|
7146
|
+
name: groupName(group.kind),
|
|
7147
|
+
path: void 0,
|
|
7148
|
+
members: group.members.map((member) => entityIds.get(member)),
|
|
7149
|
+
moduleCount: group.moduleCount
|
|
7150
|
+
}]));
|
|
7151
|
+
const sanitizedOverviewNodes = base.view.overview.nodes.map((node) => ({
|
|
7152
|
+
id: groupIds.get(node.id),
|
|
7153
|
+
kind: node.kind,
|
|
7154
|
+
name: sanitizedGroups[groupIds.get(node.id)]?.name ?? "Scope",
|
|
7155
|
+
path: void 0,
|
|
7156
|
+
memberCount: node.memberCount
|
|
7157
|
+
}));
|
|
7158
|
+
const sanitizedView = {
|
|
7159
|
+
overview: {
|
|
7160
|
+
nodes: sanitizedOverviewNodes,
|
|
7161
|
+
edges: base.view.overview.edges.map((edge) => ({
|
|
7162
|
+
from: groupIds.get(edge.from),
|
|
7163
|
+
to: groupIds.get(edge.to),
|
|
7164
|
+
count: edge.count,
|
|
7165
|
+
kinds: edge.kinds,
|
|
7166
|
+
relationIds: edge.relationIds.map((id) => relationIds.get(id))
|
|
7167
|
+
}))
|
|
7168
|
+
},
|
|
7169
|
+
groups: sanitizedGroups,
|
|
7170
|
+
entityGroup: Object.fromEntries(Object.entries(base.view.entityGroup).map(([id, group]) => [entityIds.get(id), groupIds.get(group)])),
|
|
7171
|
+
levelChunks: {
|
|
7172
|
+
default: base.view.levelChunks.default,
|
|
7173
|
+
groups: Object.fromEntries([...groupIds.values()].map((id) => [id, levelChunkName("group", id)])),
|
|
7174
|
+
packages: [...entityIds.values()].filter((id) => id.startsWith("entity:package-")).sort().map((id) => levelChunkName("package", id))
|
|
7175
|
+
}
|
|
7176
|
+
};
|
|
7177
|
+
return {
|
|
7178
|
+
...base,
|
|
7179
|
+
project: { name: "Anonymized repository" },
|
|
7180
|
+
revision: "redacted",
|
|
7181
|
+
entities: base.entities.map((entity) => ({
|
|
7182
|
+
id: entityIds.get(entity.id),
|
|
7183
|
+
anchor: anchor("entity", entityIds.get(entity.id)),
|
|
7184
|
+
kind: entity.kind,
|
|
7185
|
+
name: nameFor(entity.kind),
|
|
7186
|
+
path: void 0,
|
|
7187
|
+
provenance: entity.provenance,
|
|
7188
|
+
evidence: genericEvidence(entity.evidence)
|
|
7189
|
+
})),
|
|
7190
|
+
relations: base.relations.map((relation) => ({
|
|
7191
|
+
id: relationIds.get(relation.id),
|
|
7192
|
+
kind: relation.kind,
|
|
7193
|
+
from: entityIds.get(relation.from),
|
|
7194
|
+
to: entityIds.get(relation.to),
|
|
7195
|
+
provenance: relation.provenance,
|
|
7196
|
+
evidence: genericEvidence(relation.evidence)
|
|
7197
|
+
})),
|
|
7198
|
+
diagnostics: base.diagnostics.map((diagnostic2) => ({
|
|
7199
|
+
id: diagnosticIds.get(diagnostic2.id),
|
|
7200
|
+
code: diagnostic2.code,
|
|
7201
|
+
status: diagnostic2.status,
|
|
7202
|
+
severity: diagnostic2.severity,
|
|
7203
|
+
message: `Finding ${diagnostic2.code}`,
|
|
7204
|
+
entityIds: diagnostic2.entityIds?.map((id) => entityIds.get(id)) ?? [],
|
|
7205
|
+
relationIds: diagnostic2.relationIds?.map((id) => relationIds.get(id)) ?? [],
|
|
7206
|
+
evidence: genericEvidence(diagnostic2.evidence)
|
|
7207
|
+
})),
|
|
7208
|
+
coverage: base.coverage.map((entry, index) => ({
|
|
7209
|
+
analyzer: entry.analyzer,
|
|
7210
|
+
analyzerVersion: entry.analyzerVersion,
|
|
7211
|
+
scope: `scope-${pad(index + 1)}`,
|
|
7212
|
+
status: entry.status,
|
|
7213
|
+
...entry.reason ? { reason: "Coverage boundary reported by analyzer." } : {},
|
|
7214
|
+
...entry.evidence ? { evidence: genericEvidence(entry.evidence) } : {}
|
|
7215
|
+
})),
|
|
7216
|
+
view: sanitizedView
|
|
7217
|
+
};
|
|
7218
|
+
};
|
|
6582
7219
|
var styles = String.raw`
|
|
6583
7220
|
:root{color-scheme:light;--ink:#17251f;--muted:#60706a;--paper:#f5f7f3;--panel:#fff;--line:#d9e0da;--green:#18794e;--blue:#236b83;--amber:#a9640b;--red:#b33c35;--shadow:0 12px 32px rgba(23,37,31,.08)}
|
|
6584
7221
|
*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select{font:inherit}button{cursor:pointer}a{color:var(--blue)}.shell{max-width:1500px;margin:auto;padding:24px}.masthead{display:flex;justify-content:space-between;gap:24px;align-items:flex-end;border-bottom:1px solid var(--line);padding:8px 0 24px}.eyebrow{color:var(--green);font-size:11px;font-weight:800;letter-spacing:.12em;text-transform:uppercase}.masthead h1{font:700 clamp(28px,4vw,52px)/1.03 Georgia,serif;letter-spacing:-.04em;margin:8px 0}.lede{color:var(--muted);font-size:16px;max-width:720px;margin:0}.run-meta{text-align:right;color:var(--muted);font-size:12px}.run-meta strong{display:block;color:var(--green);font-size:14px}.read-only{border:1px solid #b9d7c6;background:#edf8f0;color:#185b3b;border-radius:999px;display:inline-flex;padding:4px 10px;font-weight:700;font-size:12px}.summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:20px 0}.metric,.insight,.panel{background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:14px}.metric{padding:16px}.metric b{font-size:27px;display:block;line-height:1}.metric span{color:var(--muted);display:block;margin-top:7px}.metric.warn b{color:var(--amber)}.metric.bad b{color:var(--red)}.lens-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;margin:22px 0 12px}.tabs{display:flex;gap:4px;flex-wrap:wrap}.tab,.level{border:1px solid transparent;background:transparent;border-radius:8px;padding:8px 11px;color:var(--muted)}.tab:hover,.level:hover{background:#e8eee9;color:var(--ink)}.tab[aria-selected=true],.level[aria-pressed=true]{background:var(--ink);color:#fff}.filters{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.filters label{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:6px}.filters input,.filters select,.filters button{border:1px solid var(--line);background:#fff;border-radius:8px;padding:8px 10px;color:var(--ink)}.filters input{min-width:220px}.filters button{color:var(--blue)}.insights{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin:14px 0 20px}.insight{padding:14px}.insight h3{font-size:13px;margin:0 0 5px}.insight p{color:var(--muted);margin:0;font-size:12px}.tag{display:inline-block;border-radius:999px;padding:2px 7px;font-size:10px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;background:#edf0ee;color:var(--muted)}.tag.heuristic{background:#fff3dc;color:#8a570a}.tag.error{background:#fde8e6;color:var(--red)}.tag.warn{background:#fff2d9;color:var(--amber)}.tag.info{background:#e6f1f5;color:var(--blue)}.workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px}.panel{padding:18px}.panel h2{font:700 23px Georgia,serif;margin:0}.panel h3{font-size:14px;margin:0}.panel-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:14px}.subtle{color:var(--muted);font-size:12px}.map-wrap{border:1px solid var(--line);border-radius:10px;overflow:auto;background:linear-gradient(135deg,#fbfcfa,#f0f5f0)}#graph{display:block;width:100%;min-width:760px;height:560px}.edge{stroke:#b8c6bd;stroke-width:1.3;opacity:.65}.edge.alert{stroke:#d08a35;stroke-width:2}.edge-label{fill:var(--muted);font-size:10px}.graph-node{cursor:pointer}.graph-node rect{fill:#fff;stroke:#9eb0a4;stroke-width:1.2}.graph-node:hover rect,.graph-node:focus rect{stroke:var(--blue);stroke-width:2}.graph-node.selected rect{stroke:var(--green);stroke-width:3}.graph-node.issue rect{fill:#fffaf0;stroke:#c98a2c}.graph-node text{pointer-events:none}.node-label{font-size:12px;font-weight:750}.node-kind{font-size:10px;fill:var(--muted)}.node-count{font-size:10px;fill:var(--green);font-weight:800}.empty{padding:32px;text-align:center;color:var(--muted)}.side{position:sticky;top:14px;align-self:start}.detail-title{font:700 22px Georgia,serif;margin:4px 0}.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:12px 0;margin:14px 0}.detail-grid b{display:block;font-size:19px}.detail-grid span{color:var(--muted);font-size:11px}.path{overflow-wrap:anywhere;color:var(--muted);font-size:12px}.list{list-style:none;margin:10px 0 0;padding:0}.list li{border-top:1px solid var(--line);padding:8px 0;font-size:12px}.run{margin-top:14px}.run-summary{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0}.chip{border:1px solid var(--line);border-radius:999px;padding:5px 9px;color:var(--muted);font-size:12px}.chip b{color:var(--ink)}.finding{border-top:1px solid var(--line);padding:14px 0}.finding-head{display:flex;justify-content:space-between;gap:10px}.finding h3{margin:0;font-size:14px}.finding p{margin:5px 0;color:var(--muted)}.finding ul{margin:8px 0 0;padding-left:18px;color:var(--muted);font-size:12px}.coverage-row{display:grid;grid-template-columns:170px 1fr 90px;gap:10px;align-items:center;border-top:1px solid var(--line);padding:10px 0;font-size:12px}.bar{height:7px;background:#e7ece8;border-radius:99px;overflow:hidden}.bar i{display:block;height:100%;background:var(--green)}.bar i.partial{background:var(--amber)}.bar i.none{background:var(--red)}.metadata{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:14px}.metadata div{border-top:1px solid var(--line);padding-top:8px;color:var(--muted);font-size:12px;overflow-wrap:anywhere}.metadata b{display:block;color:var(--ink);font-size:11px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:3px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:900px){.summary{grid-template-columns:repeat(3,minmax(0,1fr))}.insights{grid-template-columns:repeat(2,minmax(0,1fr))}.workspace{grid-template-columns:1fr}.side{position:static}.run-meta{display:none}}@media(max-width:560px){.shell{padding:14px}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.insights{grid-template-columns:1fr}.masthead{display:block}.filters input{min-width:0;width:100%}.filters label:first-child{width:100%}.metadata{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}@media(prefers-color-scheme:dark){:root{color-scheme:dark;--ink:#edf5ef;--muted:#a8b8ae;--paper:#111815;--panel:#18221d;--line:#304037;--green:#6dd19c;--blue:#7dc4dc;--amber:#efb866;--red:#f08a80;--shadow:0 12px 32px rgba(0,0,0,.24)}.map-wrap{background:linear-gradient(135deg,#19231d,#101611)}.filters input,.filters select,.filters button{background:#1d2922;color:var(--ink)}.graph-node rect{fill:#1b2820;stroke:#719080}.graph-node.issue rect{fill:#332c1d}}
|
|
@@ -6587,76 +7224,86 @@ styles += String.raw`
|
|
|
6587
7224
|
:root{--ink:#182b2b;--muted:#536664;--paper:#efe9df;--panel:#f8f5ee;--line:#c8c8b9;--green:#176b51;--blue:#155a68;--amber:#93530b;--red:#9b302b;--shadow:none}
|
|
6588
7225
|
*{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)}}
|
|
6589
7226
|
`;
|
|
6590
|
-
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}}`;
|
|
6591
|
-
styles += String.raw`.
|
|
7227
|
+
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}}`;
|
|
7228
|
+
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}}`;
|
|
6592
7229
|
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}`;
|
|
6593
7230
|
styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
|
|
6594
7231
|
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}}`;
|
|
6595
7232
|
styles += String.raw`@media(max-width:700px){.map-wrap{overflow:auto;contain:layout paint}#graph{width:720px!important;min-width:720px!important;max-width:none!important}}`;
|
|
7233
|
+
styles += String.raw`.map-wrap.dense{overflow:auto}.map-wrap.dense #graph{width:2000px!important;max-width:none!important}`;
|
|
6596
7234
|
styles += String.raw`#lens-caption + p.subtle{display:none}`;
|
|
6597
7235
|
var script = String.raw`
|
|
6598
7236
|
const data=${"${DATA}"};
|
|
6599
7237
|
data.entities=data.entities||[];data.relations=data.relations||[];data.diagnostics=data.diagnostics||[];
|
|
6600
|
-
|
|
7238
|
+
globalThis.__DOC_BRIDGE_DATA__=data;
|
|
7239
|
+
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.";
|
|
6601
7240
|
const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
|
|
6602
7241
|
new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
|
|
6603
|
-
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],loadedChunks=new Set(),loadingChunks=new Map();
|
|
7242
|
+
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
|
|
6604
7243
|
const esc=(value)=>String(value??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll("\"",""").replaceAll("'","'");
|
|
6605
|
-
const byId=new Map(),parent=new Map(),relationById=new Map(),packageMembers=new Map(),findingIndex=new Map(),relationFindingIndex=new Map(),relationsByEntity=new Map(),groupEntityIds=new Map(),groupFindingCounts=new Map(),packageCache=new Map(),nodeIssueCache=new Map();
|
|
7244
|
+
const byId=new Map(),parent=new Map(),relationById=new Map(),packageMembers=new Map(),findingIndex=new Map(),relationFindingIndex=new Map(),relationsByEntity=new Map(),groupEntityIds=new Map(),groupFindingIndex=new Map(),groupFindingCounts=new Map(),packageCache=new Map(),nodeIssueCache=new Map();let graphCacheKey="",graphCache;
|
|
7245
|
+
let activeLevelChunk="";
|
|
6606
7246
|
const groupById=new Map(Object.entries(data.view?.groups||{}));
|
|
7247
|
+
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]]));
|
|
6607
7248
|
const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
|
|
6608
7249
|
const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
|
|
6609
|
-
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.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=
|
|
6610
|
-
const
|
|
6611
|
-
const
|
|
7250
|
+
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)})})};
|
|
7251
|
+
const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
|
|
7252
|
+
const isDetailChunk=(name)=>name.startsWith("chunks/details-");
|
|
7253
|
+
const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
|
|
7254
|
+
const loadChunk=(name)=>{if(hasChunk(name))return Promise.resolve();if(loadingChunks.has(name))return loadingChunks.get(name);const promise=new Promise((resolve,reject)=>{const element=document.createElement("script");element.src=name;element.onload=()=>{try{if(isLevelChunk(name)){const payload=globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__;if(!payload)throw new Error("Offline report level payload is missing: "+name);data.entities=payload.entities||[];data.relations=payload.relations||[];activeLevelChunk=name;delete globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__}else if(isDetailChunk(name)){const payload=globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__;if(!payload)throw new Error("Offline report detail payload is missing: "+name);const entities=new Map((payload.entities||[]).map((entity)=>[entity.id,entity]));const relations=new Map((payload.relations||[]).map((relation)=>[relation.id,relation]));data.entities=data.entities.map((entity)=>entities.get(entity.id)||entity);data.relations=data.relations.map((relation)=>relations.get(relation.id)||relation);delete globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__}loadedChunks.add(name);hydrate();loadingChunks.delete(name);resolve()}catch(error){loadingChunks.delete(name);reject(error)}};element.onerror=()=>{loadingChunks.delete(name);reject(new Error("Unable to load offline report chunk: "+name))};document.head.append(element)});loadingChunks.set(name,promise);return promise};
|
|
6612
7255
|
const readHash=()=>new URLSearchParams(location.hash.slice(1));
|
|
6613
7256
|
const hash=readHash();
|
|
6614
7257
|
const state={lens:hash.get("lens")||"architecture",level:hash.get("level")||"overview",selected:hash.get("selected"),query:hash.get("query")||"",status:hash.get("status")||"",severity:hash.get("severity")||"",findingGroup:hash.get("findingGroup"),findingPage:Number(hash.get("findingPage")||0)};
|
|
6615
7258
|
const syncHash=()=>{const params=new URLSearchParams();[["lens",state.lens,"architecture"],["level",state.level,"overview"],["selected",state.selected,""],["query",state.query,""],["status",state.status,""],["severity",state.severity,""],["findingGroup",state.findingGroup,""],["findingPage",state.findingPage,"0"]].forEach(([key,value,defaultValue])=>{if(value&&value!==defaultValue)params.set(key,String(value))});const next=params.toString();try{if(location.hash.slice(1)!==next)history.replaceState(null,"",next?"#"+next:"")}catch{}};
|
|
6616
7259
|
const rootOf=(id)=>{let current=id;const seen=new Set();while(parent.has(current)&&!seen.has(current)){seen.add(current);current=parent.get(current)}return current};
|
|
6617
7260
|
const packageOf=(id)=>{if(packageCache.has(id))return packageCache.get(id);let current=id;const seen=new Set();while(current&&!seen.has(current)){seen.add(current);if(byId.get(current)?.kind==="package"){packageCache.set(id,current);return current}current=parent.get(current)}packageCache.set(id,id);return id};
|
|
6618
|
-
const groupFor=(id)=>data.view?.entityGroup?.[id]||id;
|
|
7261
|
+
const groupFor=(id)=>{const direct=data.view?.entityGroup?.[id];if(direct)return direct;const entity=byId.get(id);return entity?.kind==="module"||entity?.kind==="file"?groupFor(packageOf(id)):id};
|
|
6619
7262
|
const packageNodes=()=>data.entities.filter((entity)=>entity.kind==="package");
|
|
6620
7263
|
const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",evidence:"Coverage"};
|
|
6621
7264
|
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};
|
|
6622
7265
|
const label=(entity)=>entity?.name||entity?.id||"Unknown";
|
|
6623
7266
|
const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
|
|
6624
|
-
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(
|
|
7267
|
+
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("")};
|
|
6625
7268
|
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};
|
|
6626
|
-
const relationHealth=(relation)=>{if(!relation)return "";const findings=
|
|
6627
|
-
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(),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};
|
|
7269
|
+
const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
|
|
7270
|
+
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};
|
|
6628
7271
|
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);
|
|
6629
|
-
const
|
|
7272
|
+
const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
|
|
7273
|
+
const relationInLens=(id)=>relationFindings(id).some(findingInLens);
|
|
6630
7274
|
const externalFor=(ids)=>{const externalIds=new Set();for(const id of ids)for(const relation of relationsByEntity.get(id)||[]){const other=relation.from===id?relation.to:relation.from;if(byId.get(other)?.kind==="external")externalIds.add(other)}return [...externalIds].map((id)=>byId.get(id)).filter(Boolean)};
|
|
7275
|
+
const levelChunkName=()=>{if(state.selected&&groupById.has(state.selected))return data.view?.levelChunks?.groups?.[state.selected]||data.view?.levelChunks?.default;const selected=state.selected?byId.get(state.selected):null;const packageId=selected?.kind==="package"?selected.id:selected?packageOf(selected.id):state.selected?.startsWith("package:")?state.selected:null;const packageChunk=packageId&&packageChunkNameById.get(packageId);if(packageChunk)return packageChunk;const selectedGroup=state.selected?data.view?.entityGroup?.[state.selected]:null;if(selectedGroup&&data.view?.levelChunks?.groups?.[selectedGroup])return data.view.levelChunks.groups[selectedGroup];return data.view?.levelChunks?.default};
|
|
7276
|
+
const detailChunkName=()=>{const entity=state.selected?byId.get(state.selected):null;const packageId=entity?.kind==="package"?entity.id:entity?packageOf(entity.id):null;const levelName=packageId&&packageChunkNameById.get(packageId);return levelName?.replace("chunks/levels-package-","chunks/details-")||null};
|
|
6631
7277
|
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)};
|
|
6632
|
-
const
|
|
6633
|
-
const
|
|
6634
|
-
const
|
|
7278
|
+
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}};
|
|
7279
|
+
const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
|
|
7280
|
+
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("")};
|
|
7281
|
+
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>"};
|
|
6635
7282
|
const mapState={scale:1,dragging:false,x:0,y:0};
|
|
6636
|
-
const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
|
|
7283
|
+
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"};
|
|
6637
7284
|
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)})};
|
|
6638
7285
|
const enterNode=(id)=>{const group=groupById.get(id),entity=byId.get(id);if(group){state.selected=id;state.level="package"}else if(entity?.kind==="package"){state.selected=id;state.level="module"}else if(entity?.kind==="module"){state.selected=id;state.level="file"}else return;mapState.scale=1;mapState.x=0;mapState.y=0;render()};
|
|
6639
7286
|
let nodeClickTimer;
|
|
6640
7287
|
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);
|
|
6641
|
-
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);
|
|
7288
|
+
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);
|
|
6642
7289
|
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);
|
|
6643
|
-
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
7290
|
+
const diagnosticsFor=(id)=>(findingIndex.get(id)||[]).filter((finding)=>finding.status!=="confirmed");
|
|
6644
7291
|
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>":"")};
|
|
6645
|
-
const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph");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,[]]));
|
|
7292
|
+
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+").":".");};
|
|
6646
7293
|
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)};
|
|
6647
|
-
const findingScope=(finding)=>{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"};
|
|
7294
|
+
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"})();
|
|
6648
7295
|
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))};
|
|
6649
7296
|
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>";
|
|
6650
|
-
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+"
|
|
7297
|
+
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>"};
|
|
6651
7298
|
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>"};
|
|
6652
|
-
const deferFindings=()=>{if(findingsLoaded())return;const
|
|
7299
|
+
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>"};
|
|
6653
7300
|
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>"}};
|
|
6654
7301
|
const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
|
|
6655
|
-
const levelsLoaded=()=>!
|
|
7302
|
+
const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
|
|
6656
7303
|
const setReportView=()=>{document.body.dataset.reportView={architecture:"architecture",drift:"insights",risks:"findings",evidence:"coverage"}[state.lens]||"architecture";ensureReportChrome()};
|
|
6657
|
-
const
|
|
6658
|
-
document.
|
|
6659
|
-
document.addEventListener("click",(event)=>{const
|
|
7304
|
+
const measureRender=(name,renderFn)=>{const started=performance.now(),value=renderFn(),timings=JSON.parse(document.documentElement.dataset.docBridgeRenderTimings||"{}");timings[name]=Math.round(performance.now()-started);document.documentElement.dataset.docBridgeRenderTimings=JSON.stringify(timings);return value};
|
|
7305
|
+
const render=()=>{if(state.level!=="overview"&&!levelsLoaded()){document.querySelector("#map-note").textContent="Loading canonical entities…";loadChunk(levelChunkName()).then(render).catch((error)=>{document.querySelector("#map-note").textContent=error.message});return}if(state.lens!=="architecture"&&!findingsLoaded()){document.querySelector("#lens-caption").textContent="Loading findings for this lens…";loadChunk("chunks/findings.js").then(render).catch((error)=>{document.querySelector("#lens-caption").textContent=error.message});return}syncHash();setReportView();renderBreadcrumbs();document.querySelectorAll(".tab[data-lens]").forEach((tab)=>tab.setAttribute("aria-selected",String(tab.dataset.lens===state.lens)));document.querySelectorAll(".level").forEach((button)=>button.setAttribute("aria-pressed",String(button.dataset.level===state.level)));document.querySelector("#lens-caption").textContent=state.lens==="architecture"?"The repository topology at the selected level.":state.lens==="drift"?"Where canonical code relations and documentation declarations need attention.":state.lens==="risks"?"Signals that deserve human review; connectivity warnings are heuristics, not architectural proof.":"What the analyzers observed, declared, or could not analyze.";document.documentElement.dataset.docBridgeRenderTimings="{}";measureRender("insights",renderInsights);if(state.lens!=="architecture")measureRender("insightsDashboard",renderInsightsDashboard);measureRender("graph",renderGraph);measureRender("details",renderDetails);if(state.lens!=="architecture")measureRender("findings",renderFindings);else document.querySelector("#findings")?.replaceChildren();deferFindings();measureRender("coverage",renderCoverage)};
|
|
7306
|
+
document.addEventListener("click",async(event)=>{const target=event.target.closest?.("[data-level],[data-lens],#load-findings");if(!target)return;if(target.id==="load-findings"){event.preventDefault();event.stopImmediatePropagation();await loadChunk("chunks/findings.js");render();return}if(target.dataset.level&&!levelsLoaded()){event.preventDefault();event.stopImmediatePropagation();state.level=target.dataset.level;await loadChunk(levelChunkName());render();return}if(target.dataset.lens&&target.dataset.lens!=="architecture"&&!findingsLoaded()){event.preventDefault();event.stopImmediatePropagation();state.lens=target.dataset.lens;state.findingGroup=null;await loadChunk("chunks/findings.js");render()}},true);
|
|
6660
7307
|
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);
|
|
6661
7308
|
document.addEventListener("input",async(event)=>{const target=event.target;if(target?.id!=="search"||findingsLoaded())return;event.stopImmediatePropagation();state.query=target.value;state.findingGroup=null;await loadChunk("chunks/findings.js");render()},true);
|
|
6662
7309
|
document.addEventListener("change",async(event)=>{const target=event.target;if(!["status","severity"].includes(target?.id)||findingsLoaded())return;event.stopImmediatePropagation();state[target.id]=target.value;state.findingGroup=null;await loadChunk("chunks/findings.js");render()},true);
|
|
@@ -6682,14 +7329,15 @@ hydrate();render();keepMobileGraphReadable();ensurePolicyNote();
|
|
|
6682
7329
|
var render = (input, options) => {
|
|
6683
7330
|
const { snapshot, report } = input;
|
|
6684
7331
|
const includeSnippets = options.includeSnippets === true;
|
|
6685
|
-
const data = reportData(snapshot, report, includeSnippets);
|
|
7332
|
+
const data = reportData(snapshot, report, includeSnippets, options.privacy);
|
|
6686
7333
|
const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
|
|
6687
7334
|
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.";
|
|
6688
|
-
const
|
|
7335
|
+
const actionableCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status !== "confirmed").length;
|
|
7336
|
+
const confirmedCount = report.diagnostics.filter((diagnostic2) => diagnostic2.status === "confirmed").length;
|
|
6689
7337
|
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
6690
|
-
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.";
|
|
7338
|
+
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.";
|
|
6691
7339
|
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
6692
|
-
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(
|
|
7340
|
+
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>`;
|
|
6693
7341
|
};
|
|
6694
7342
|
var parseReportInput = (input) => {
|
|
6695
7343
|
if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
|
|
@@ -6700,31 +7348,186 @@ var parseReportInput = (input) => {
|
|
|
6700
7348
|
return { snapshot, report };
|
|
6701
7349
|
};
|
|
6702
7350
|
var chunkScript = (payload) => `globalThis.__DOC_BRIDGE_DATA__=Object.assign(globalThis.__DOC_BRIDGE_DATA__||{},${embeddedJson(payload)});`;
|
|
7351
|
+
var levelChunkScript = (payload) => `globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__=${embeddedJson(payload)};`;
|
|
7352
|
+
var detailChunkScript = (payload) => `globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__=${embeddedJson(payload)};`;
|
|
6703
7353
|
var byteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
6704
7354
|
var renderOfflineReportArtifact = (input, options = {}) => {
|
|
6705
7355
|
const parsed = parseReportInput(input);
|
|
6706
7356
|
const thresholdBytes = options.thresholdBytes ?? DEFAULT_LARGE_REPORT_THRESHOLD_BYTES;
|
|
6707
|
-
const renderOptions = { includeSnippets: options.includeSnippets === true, thresholdBytes };
|
|
7357
|
+
const renderOptions = { includeSnippets: options.includeSnippets === true, privacy: options.privacy ?? "private", thresholdBytes };
|
|
6708
7358
|
const singleFile = render(parsed, options);
|
|
6709
7359
|
if (byteLength(singleFile) <= thresholdBytes) {
|
|
6710
7360
|
const files2 = { "index.html": sha256NormalizedV1(singleFile) };
|
|
6711
|
-
const manifestPayload2 = { schemaVersion: 1, mode: "single-file", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: parsed.snapshot.sourceRevision, renderOptions, files: files2 };
|
|
7361
|
+
const manifestPayload2 = { schemaVersion: 1, mode: "single-file", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: options.privacy === "anonymized" ? "redacted" : parsed.snapshot.sourceRevision, renderOptions, files: files2 };
|
|
6712
7362
|
const manifest2 = JSON.stringify({ ...manifestPayload2, artifactHash: sha256NormalizedV1(manifestPayload2) }, null, 2);
|
|
6713
7363
|
return { mode: "single-file", indexHtml: singleFile, files: { "index.html": singleFile }, manifest: manifest2 };
|
|
6714
7364
|
}
|
|
6715
|
-
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true);
|
|
7365
|
+
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true, options.privacy);
|
|
7366
|
+
const entitiesById = new Map(data.entities.map((entity) => [entity.id, entity]));
|
|
7367
|
+
const packageEntities = data.entities.filter((entity) => entity.kind === "package");
|
|
7368
|
+
const packageIds = new Set(packageEntities.map((entity) => entity.id));
|
|
7369
|
+
const packageRelations = data.relations.filter((relation) => packageIds.has(relation.from) && packageIds.has(relation.to));
|
|
7370
|
+
const diagnosticGroup = Object.fromEntries(data.diagnostics.map((diagnostic2) => {
|
|
7371
|
+
const groups = /* @__PURE__ */ new Set();
|
|
7372
|
+
for (const entityId2 of diagnostic2.entityIds ?? []) {
|
|
7373
|
+
const groupId = data.view.entityGroup[entityId2];
|
|
7374
|
+
if (groupId) groups.add(groupId);
|
|
7375
|
+
}
|
|
7376
|
+
for (const relationId of diagnostic2.relationIds ?? []) {
|
|
7377
|
+
const relation = data.relations.find((candidate) => candidate.id === relationId);
|
|
7378
|
+
if (!relation) continue;
|
|
7379
|
+
for (const endpoint of [relation.from, relation.to]) {
|
|
7380
|
+
const groupId = data.view.entityGroup[endpoint];
|
|
7381
|
+
if (groupId) groups.add(groupId);
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
return [diagnostic2.id, [...groups].sort()];
|
|
7385
|
+
}));
|
|
7386
|
+
const diagnosticRelationFindings = /* @__PURE__ */ new Map();
|
|
7387
|
+
for (const diagnostic2 of data.diagnostics) {
|
|
7388
|
+
for (const relationId of diagnostic2.relationIds ?? []) {
|
|
7389
|
+
const findings = diagnosticRelationFindings.get(relationId) ?? [];
|
|
7390
|
+
findings.push({ status: diagnostic2.status, severity: diagnostic2.severity });
|
|
7391
|
+
diagnosticRelationFindings.set(relationId, findings);
|
|
7392
|
+
}
|
|
7393
|
+
}
|
|
7394
|
+
const containsRelations = data.relations.filter((relation) => relation.kind === "contains");
|
|
7395
|
+
const collectScope = (roots) => {
|
|
7396
|
+
const ids = new Set(roots);
|
|
7397
|
+
let changed = true;
|
|
7398
|
+
while (changed) {
|
|
7399
|
+
changed = false;
|
|
7400
|
+
for (const relation of containsRelations) {
|
|
7401
|
+
if (ids.has(relation.from) && !ids.has(relation.to)) {
|
|
7402
|
+
ids.add(relation.to);
|
|
7403
|
+
changed = true;
|
|
7404
|
+
}
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
return ids;
|
|
7408
|
+
};
|
|
7409
|
+
const scopedPayload = (ids) => ({
|
|
7410
|
+
entities: data.entities.filter((entity) => ids.has(entity.id)),
|
|
7411
|
+
relations: data.relations.filter((relation) => ids.has(relation.from) && ids.has(relation.to))
|
|
7412
|
+
});
|
|
7413
|
+
const packagePayload = (ids) => ({
|
|
7414
|
+
entities: data.entities.filter((entity) => ids.has(entity.id) && entity.kind === "package"),
|
|
7415
|
+
relations: data.relations.filter((relation) => ids.has(relation.from) && ids.has(relation.to) && entitiesById.get(relation.from)?.kind === "package" && entitiesById.get(relation.to)?.kind === "package")
|
|
7416
|
+
});
|
|
7417
|
+
const compactPayload = (payload) => ({
|
|
7418
|
+
entities: payload.entities.map(({ evidence: _evidence, anchor: _anchor, provenance: _provenance, ...entity }) => entity),
|
|
7419
|
+
relations: payload.relations.map(({ evidence: _evidence, provenance: _provenance, ...relation }) => relation)
|
|
7420
|
+
});
|
|
7421
|
+
const levelFiles = {};
|
|
7422
|
+
const detailFiles = {};
|
|
7423
|
+
for (const [groupId, group] of Object.entries(data.view.groups)) {
|
|
7424
|
+
const ids = new Set(group.members.filter((id) => entitiesById.get(id)?.kind === "package"));
|
|
7425
|
+
const chunkName = data.view.levelChunks.groups[groupId];
|
|
7426
|
+
if (chunkName) levelFiles[chunkName] = levelChunkScript(packagePayload(ids));
|
|
7427
|
+
}
|
|
7428
|
+
const packageIdList = packageEntities.map((entity) => entity.id);
|
|
7429
|
+
const packageIdSet = new Set(packageIdList);
|
|
7430
|
+
levelFiles[data.view.levelChunks.default] = levelChunkScript(scopedPayload(packageIdSet));
|
|
7431
|
+
const sortedPackageIds = [...packageIds].sort();
|
|
7432
|
+
for (const [index, packageId2] of sortedPackageIds.entries()) {
|
|
7433
|
+
const chunkName = data.view.levelChunks.packages[index];
|
|
7434
|
+
const packageScope = scopedPayload(collectScope([packageId2]));
|
|
7435
|
+
if (chunkName) levelFiles[chunkName] = levelChunkScript(compactPayload(packageScope));
|
|
7436
|
+
const detailName = chunkName?.replace("chunks/levels-package-", "chunks/details-");
|
|
7437
|
+
if (detailName) detailFiles[detailName] = detailChunkScript(packageScope);
|
|
7438
|
+
}
|
|
7439
|
+
const overviewView = {
|
|
7440
|
+
...data.view,
|
|
7441
|
+
entityGroup: Object.fromEntries(Object.entries(data.view.entityGroup).filter(([id]) => data.entities.find((entity) => entity.id === id)?.kind === "package")),
|
|
7442
|
+
diagnosticGroup,
|
|
7443
|
+
diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
|
|
7444
|
+
};
|
|
6716
7445
|
const files = {
|
|
6717
|
-
"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,
|
|
6718
|
-
|
|
7446
|
+
"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 }),
|
|
7447
|
+
...levelFiles,
|
|
7448
|
+
...detailFiles,
|
|
6719
7449
|
"chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
|
|
6720
7450
|
};
|
|
6721
|
-
const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_LAZY_CHUNKS__
|
|
7451
|
+
const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__=true;globalThis.__DOC_BRIDGE_LAZY_CHUNKS__=["chunks/findings.js"]</script><script src="chunks/overview.js"></script>` });
|
|
6722
7452
|
const fileHashes = Object.fromEntries(Object.entries({ ...files, "index.html": indexHtml }).map(([file, content]) => [file, sha256NormalizedV1(content)]));
|
|
6723
|
-
const manifestPayload = { schemaVersion: 1, mode: "directory", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: parsed.snapshot.sourceRevision, renderOptions, files: fileHashes };
|
|
7453
|
+
const manifestPayload = { schemaVersion: 1, mode: "directory", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: options.privacy === "anonymized" ? "redacted" : parsed.snapshot.sourceRevision, renderOptions, files: fileHashes };
|
|
6724
7454
|
const manifest = JSON.stringify({ ...manifestPayload, artifactHash: sha256NormalizedV1(manifestPayload) }, null, 2);
|
|
6725
7455
|
return { mode: "directory", indexHtml, files: { ...files, "index.html": indexHtml }, manifest };
|
|
6726
7456
|
};
|
|
6727
7457
|
|
|
7458
|
+
// src/metrics/benchmark.ts
|
|
7459
|
+
import { z as z9 } from "zod";
|
|
7460
|
+
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
7461
|
+
var stringList = z9.array(z9.string().min(1).max(512)).max(1e5);
|
|
7462
|
+
var benchmarkSets = z9.object({
|
|
7463
|
+
entities: stringList.default([]),
|
|
7464
|
+
relations: stringList.default([]),
|
|
7465
|
+
findings: stringList.default([])
|
|
7466
|
+
}).strict();
|
|
7467
|
+
var BenchmarkFixtureV1Schema = z9.object({
|
|
7468
|
+
schemaVersion: z9.literal(BENCHMARK_SCHEMA_VERSION),
|
|
7469
|
+
supported: benchmarkSets,
|
|
7470
|
+
excluded: benchmarkSets.optional()
|
|
7471
|
+
}).strict();
|
|
7472
|
+
var unique = (values) => new Set(values);
|
|
7473
|
+
var ratio = (numerator, denominator) => denominator === 0 ? 1 : numerator / denominator;
|
|
7474
|
+
var setMetrics = (actualValues, expectedValues, excludedValues) => {
|
|
7475
|
+
const excluded = unique(excludedValues);
|
|
7476
|
+
const activeActualValues = actualValues.filter((value) => !excluded.has(value));
|
|
7477
|
+
const actual = unique(activeActualValues);
|
|
7478
|
+
const expected = unique(expectedValues.filter((value) => !excluded.has(value)));
|
|
7479
|
+
const truePositives = [...actual].filter((value) => expected.has(value)).length;
|
|
7480
|
+
const falsePositives = actual.size - truePositives;
|
|
7481
|
+
const falseNegatives = expected.size - truePositives;
|
|
7482
|
+
return {
|
|
7483
|
+
truePositives,
|
|
7484
|
+
falsePositives,
|
|
7485
|
+
falseNegatives,
|
|
7486
|
+
precision: ratio(truePositives, actual.size),
|
|
7487
|
+
recall: ratio(truePositives, expected.size),
|
|
7488
|
+
duplicateCount: activeActualValues.length - actual.size
|
|
7489
|
+
};
|
|
7490
|
+
};
|
|
7491
|
+
var benchmarkFixture = (fixture) => BenchmarkFixtureV1Schema.parse(fixture);
|
|
7492
|
+
var measureBenchmark = (observation, fixture, thresholds = {}) => {
|
|
7493
|
+
const excluded = fixture.excluded ?? { entities: [], relations: [], findings: [] };
|
|
7494
|
+
const quality = {
|
|
7495
|
+
entities: setMetrics(observation.entities, fixture.supported.entities, excluded.entities),
|
|
7496
|
+
relations: setMetrics(observation.relations, fixture.supported.relations, excluded.relations),
|
|
7497
|
+
findings: setMetrics(observation.findings, fixture.supported.findings, excluded.findings)
|
|
7498
|
+
};
|
|
7499
|
+
const actualEntities = unique(observation.entities);
|
|
7500
|
+
const evidenced = unique(observation.evidenced ?? []);
|
|
7501
|
+
const evidenceRatio = ratio([...actualEntities].filter((id) => evidenced.has(id)).length, actualEntities.size);
|
|
7502
|
+
const findingDensity = ratio(unique(observation.findings).size, actualEntities.size);
|
|
7503
|
+
const precisionThreshold = thresholds.precision ?? 0.95;
|
|
7504
|
+
const recallThreshold = thresholds.recall ?? 1;
|
|
7505
|
+
const regressions = [];
|
|
7506
|
+
for (const [name, metrics] of Object.entries(quality)) {
|
|
7507
|
+
if (metrics.precision < precisionThreshold) regressions.push(`${name}.precision below ${precisionThreshold}`);
|
|
7508
|
+
if (metrics.recall < recallThreshold) regressions.push(`${name}.recall below ${recallThreshold}`);
|
|
7509
|
+
}
|
|
7510
|
+
return {
|
|
7511
|
+
schemaVersion: BENCHMARK_SCHEMA_VERSION,
|
|
7512
|
+
quality,
|
|
7513
|
+
evidenceRatio,
|
|
7514
|
+
findingDensity,
|
|
7515
|
+
findingCategoryDistribution: Object.fromEntries(Object.entries(observation.findingCategories ?? {}).sort(([a], [b]) => a.localeCompare(b))),
|
|
7516
|
+
excludedCaseCount: excluded.entities.length + excluded.relations.length + excluded.findings.length,
|
|
7517
|
+
excludedCaseIds: { entities: excluded.entities.length, relations: excluded.relations.length, findings: excluded.findings.length },
|
|
7518
|
+
thresholds: { precision: precisionThreshold, recall: recallThreshold },
|
|
7519
|
+
regressions
|
|
7520
|
+
};
|
|
7521
|
+
};
|
|
7522
|
+
var formatBenchmarkText = (result) => [
|
|
7523
|
+
`Precision: entities ${result.quality.entities.precision.toFixed(3)}, relations ${result.quality.relations.precision.toFixed(3)}, findings ${result.quality.findings.precision.toFixed(3)}`,
|
|
7524
|
+
`Recall: entities ${result.quality.entities.recall.toFixed(3)}, relations ${result.quality.relations.recall.toFixed(3)}, findings ${result.quality.findings.recall.toFixed(3)}`,
|
|
7525
|
+
`Evidence ratio: ${result.evidenceRatio.toFixed(3)}`,
|
|
7526
|
+
`Finding density: ${result.findingDensity.toFixed(3)}`,
|
|
7527
|
+
`Excluded cases: ${result.excludedCaseCount}`,
|
|
7528
|
+
`Regressions: ${result.regressions.length ? result.regressions.join("; ") : "none"}`
|
|
7529
|
+
].join("\n");
|
|
7530
|
+
|
|
6728
7531
|
// src/cli/program.ts
|
|
6729
7532
|
var usage = `ak-docs \u2014 human\u2194agent documentation bridge (@agentskit/doc-bridge)
|
|
6730
7533
|
|
|
@@ -6734,6 +7537,7 @@ Core (no API key):
|
|
|
6734
7537
|
ak-docs doctor [--text] [--badge] [--write-badge]
|
|
6735
7538
|
ak-docs index [--watch]
|
|
6736
7539
|
ak-docs discover [--text|--json]
|
|
7540
|
+
ak-docs benchmark <fixture.json> <observation.json> [--text|--json]
|
|
6737
7541
|
ak-docs scan | reconcile | check | map [--text|--json] [--html] [--report-threshold <bytes>]
|
|
6738
7542
|
ak-docs fix propose links|normalize <artifact> [--output <file>]
|
|
6739
7543
|
ak-docs fix approve|apply <proposal.json> [--by <name>]
|
|
@@ -6804,6 +7608,7 @@ var parseArgs = (argv) => {
|
|
|
6804
7608
|
else if (positional[0] === "playbook") command = "playbook";
|
|
6805
7609
|
else if (positional[0] === "registry") command = "registry";
|
|
6806
7610
|
else if (positional[0] === "discover") command = "discover";
|
|
7611
|
+
else if (positional[0] === "benchmark") command = "benchmark";
|
|
6807
7612
|
else if (positional[0] === "scan") command = "scan";
|
|
6808
7613
|
else if (positional[0] === "reconcile") command = "reconcile";
|
|
6809
7614
|
else if (positional[0] === "check") command = "check";
|
|
@@ -7019,33 +7824,44 @@ var diagnosticNextCommands = (config, result) => {
|
|
|
7019
7824
|
"ak-docs index"
|
|
7020
7825
|
];
|
|
7021
7826
|
};
|
|
7022
|
-
var workflowOptions = (root, config, sourceRevision2, stage, handlers) => ({
|
|
7827
|
+
var workflowOptions = (root, config, sourceRevision2, stage, handlers, versions) => ({
|
|
7023
7828
|
root,
|
|
7024
7829
|
...config.workflow?.stateDir ? { stateDir: config.workflow.stateDir } : {},
|
|
7025
7830
|
sourceRevision: sourceRevision2,
|
|
7026
7831
|
configurationHash: sha256NormalizedV1(config),
|
|
7027
|
-
toolVersion:
|
|
7832
|
+
toolVersion: PACKAGE_VERSION,
|
|
7833
|
+
...versions?.pipelineVersion ? { pipelineVersion: versions.pipelineVersion } : {},
|
|
7834
|
+
...versions?.analyzerVersions ? { analyzerVersions: versions.analyzerVersions } : {},
|
|
7028
7835
|
stage,
|
|
7029
7836
|
handlers
|
|
7030
7837
|
});
|
|
7031
7838
|
var scanWorkflow = (root, config) => {
|
|
7032
7839
|
const discovered = discoverRepository({ root, config });
|
|
7033
|
-
|
|
7034
|
-
|
|
7840
|
+
const versions = { pipelineVersion: discovered.pipelineVersion, analyzerVersions: discovered.analyzerVersions };
|
|
7841
|
+
runWorkflow(workflowOptions(root, config, discovered.sourceRevision, "collect", { collect: () => discovered }, versions));
|
|
7842
|
+
return runWorkflow(workflowOptions(root, config, discovered.sourceRevision, "normalize", { normalize: ({ input }) => input }, versions));
|
|
7035
7843
|
};
|
|
7036
7844
|
var documentationInputs = (root, snapshot) => snapshot.entities.filter((entity) => entity.kind === "document" && entity.path).map((entity) => ({ path: entity.path, content: readFileSync21(resolve18(root, entity.path), "utf8") }));
|
|
7037
7845
|
var reconcileWorkflow = (root, config) => {
|
|
7038
7846
|
const scanned = scanWorkflow(root, config);
|
|
7039
7847
|
const snapshot = parseDiscoverySnapshot(loadWorkflowStepOutput(scanned.stateDir, "normalize"));
|
|
7040
|
-
const declared = applyDocumentationDeclarations(snapshot, documentationInputs(root, snapshot)
|
|
7041
|
-
|
|
7042
|
-
|
|
7848
|
+
const declared = applyDocumentationDeclarations(snapshot, documentationInputs(root, snapshot), {
|
|
7849
|
+
agentRoot: config.corpus.agent.root
|
|
7850
|
+
}).snapshot;
|
|
7851
|
+
const report = reconcileKnowledge(snapshot, declared, {
|
|
7852
|
+
...config.reconciliation?.scope === void 0 ? {} : { scope: config.reconciliation.scope },
|
|
7853
|
+
...config.reconciliation?.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: config.reconciliation.requiredRelationKinds },
|
|
7854
|
+
...config.reconciliation?.requiredRelationTargets === void 0 ? {} : { requiredRelationTargets: config.reconciliation.requiredRelationTargets },
|
|
7855
|
+
...config.reconciliation?.includeOrphanedDocuments === void 0 ? {} : { includeOrphanedDocuments: config.reconciliation.includeOrphanedDocuments }
|
|
7856
|
+
});
|
|
7857
|
+
return runWorkflow(workflowOptions(root, config, snapshot.sourceRevision, "reconcile", { reconcile: () => report }, { pipelineVersion: snapshot.pipelineVersion, analyzerVersions: snapshot.analyzerVersions }));
|
|
7043
7858
|
};
|
|
7044
7859
|
var checkWorkflow = (root, config) => {
|
|
7045
7860
|
const reconciled = reconcileWorkflow(root, config);
|
|
7046
7861
|
const report = parseReconciliationReport(loadWorkflowStepOutput(reconciled.stateDir, "reconcile"));
|
|
7047
|
-
const
|
|
7048
|
-
|
|
7862
|
+
const versions = { pipelineVersion: report.pipelineVersion, analyzerVersions: report.analyzerVersions };
|
|
7863
|
+
runWorkflow(workflowOptions(root, config, report.sourceRevision, "evaluate", { evaluate: () => evaluateRules(report, { ...config.rules ? { config: config.rules } : {} }) }, versions));
|
|
7864
|
+
return runWorkflow(workflowOptions(root, config, report.sourceRevision, "report", { report: ({ input }) => input }, versions));
|
|
7049
7865
|
};
|
|
7050
7866
|
var workflowOutput = (result) => {
|
|
7051
7867
|
const snapshot = (() => {
|
|
@@ -7089,7 +7905,7 @@ var writeAtomicFile = (path, content) => {
|
|
|
7089
7905
|
renameSync3(temporaryPath, path);
|
|
7090
7906
|
};
|
|
7091
7907
|
var writeReportArtifact = (htmlPath, artifact2) => {
|
|
7092
|
-
|
|
7908
|
+
mkdirSync7(dirname9(htmlPath), { recursive: true });
|
|
7093
7909
|
if (artifact2.mode === "single-file") {
|
|
7094
7910
|
writeAtomicFile(htmlPath, artifact2.indexHtml);
|
|
7095
7911
|
const artifactDir2 = htmlPath.replace(/\.html?$/i, "");
|
|
@@ -7102,14 +7918,14 @@ var writeReportArtifact = (htmlPath, artifact2) => {
|
|
|
7102
7918
|
const launcherTemp = `${htmlPath}.tmp-${process.pid}`;
|
|
7103
7919
|
rmSync3(temporaryDir, { recursive: true, force: true });
|
|
7104
7920
|
rmSync3(backupDir, { recursive: true, force: true });
|
|
7105
|
-
|
|
7921
|
+
mkdirSync7(temporaryDir, { recursive: true });
|
|
7106
7922
|
for (const [file, content] of Object.entries(artifact2.files)) {
|
|
7107
7923
|
const filePath = resolve18(temporaryDir, file);
|
|
7108
|
-
|
|
7924
|
+
mkdirSync7(dirname9(filePath), { recursive: true });
|
|
7109
7925
|
writeFileSync8(filePath, content, "utf8");
|
|
7110
7926
|
}
|
|
7111
7927
|
writeFileSync8(resolve18(temporaryDir, "manifest.json"), artifact2.manifest, "utf8");
|
|
7112
|
-
const frameSource = `${relative10(
|
|
7928
|
+
const frameSource = `${relative10(dirname9(htmlPath), artifactDir)}/index.html`;
|
|
7113
7929
|
const launcher = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report</title></head><body style="margin:0"><iframe title="Doc Bridge report" src="${frameSource}" style="border:0;width:100vw;height:100vh"></iframe></body></html>`;
|
|
7114
7930
|
writeFileSync8(launcherTemp, launcher, "utf8");
|
|
7115
7931
|
try {
|
|
@@ -7141,11 +7957,11 @@ var runWorkflowCommand = (command, flags, configPath, argv) => {
|
|
|
7141
7957
|
const report = parseReconciliationReport(loadWorkflowStepOutput(result.stateDir, "reconcile"));
|
|
7142
7958
|
const outputPath = optionValues(argv, "--output")[0] ?? ".doc-bridge/report.html";
|
|
7143
7959
|
const htmlPath = resolve18(root, outputPath);
|
|
7144
|
-
|
|
7960
|
+
mkdirSync7(dirname9(htmlPath), { recursive: true });
|
|
7145
7961
|
const thresholdValue = optionValues(argv, "--report-threshold")[0];
|
|
7146
7962
|
const thresholdBytes = thresholdValue === void 0 ? void 0 : Number(thresholdValue);
|
|
7147
7963
|
if (thresholdBytes !== void 0 && (!Number.isSafeInteger(thresholdBytes) || thresholdBytes < 1)) throw new Error("--report-threshold must be a positive integer.");
|
|
7148
|
-
const artifact2 = renderOfflineReportArtifact({ snapshot, report }, { ...thresholdBytes === void 0 ? {} : { thresholdBytes } });
|
|
7964
|
+
const artifact2 = renderOfflineReportArtifact({ snapshot, report }, { ...config.report?.privacy ? { privacy: config.report.privacy } : {}, ...thresholdBytes === void 0 ? {} : { thresholdBytes } });
|
|
7149
7965
|
output.htmlPath = writeReportArtifact(htmlPath, artifact2);
|
|
7150
7966
|
output.htmlMode = artifact2.mode;
|
|
7151
7967
|
}
|
|
@@ -7219,7 +8035,7 @@ var runFixCommand = (argv, positional, configPath) => {
|
|
|
7219
8035
|
}
|
|
7220
8036
|
const outputPath = optionValues(argv, "--output")[0];
|
|
7221
8037
|
if (outputPath) {
|
|
7222
|
-
|
|
8038
|
+
mkdirSync7(dirname9(resolve18(root, outputPath)), { recursive: true });
|
|
7223
8039
|
writeFileSync8(resolve18(root, outputPath), `${JSON.stringify(proposal2, null, 2)}
|
|
7224
8040
|
`, "utf8");
|
|
7225
8041
|
}
|
|
@@ -7259,10 +8075,19 @@ var runSuggestCommand = async (flags, configPath) => {
|
|
|
7259
8075
|
}
|
|
7260
8076
|
};
|
|
7261
8077
|
var writeIfMissing = (path, contents) => {
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
8078
|
+
mkdirSync7(dirname9(path), { recursive: true });
|
|
8079
|
+
try {
|
|
8080
|
+
const fd = openSync3(path, "wx");
|
|
8081
|
+
try {
|
|
8082
|
+
writeFileSync8(fd, contents, "utf8");
|
|
8083
|
+
return true;
|
|
8084
|
+
} finally {
|
|
8085
|
+
closeSync3(fd);
|
|
8086
|
+
}
|
|
8087
|
+
} catch (error) {
|
|
8088
|
+
if (error.code === "EEXIST") return false;
|
|
8089
|
+
throw error;
|
|
8090
|
+
}
|
|
7266
8091
|
};
|
|
7267
8092
|
var demoOwnership = {
|
|
7268
8093
|
example: {
|
|
@@ -7473,6 +8298,26 @@ var runCli = (argv) => {
|
|
|
7473
8298
|
return 0;
|
|
7474
8299
|
} catch (error) {
|
|
7475
8300
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
8301
|
+
`);
|
|
8302
|
+
return 2;
|
|
8303
|
+
}
|
|
8304
|
+
}
|
|
8305
|
+
if (command === "benchmark") {
|
|
8306
|
+
const fixturePath2 = positional[1];
|
|
8307
|
+
const observationPath = positional[2];
|
|
8308
|
+
if (!fixturePath2 || !observationPath) {
|
|
8309
|
+
process.stderr.write("Usage: ak-docs benchmark <fixture.json> <observation.json> [--text|--json]\n");
|
|
8310
|
+
return 1;
|
|
8311
|
+
}
|
|
8312
|
+
try {
|
|
8313
|
+
const fixture = benchmarkFixture(JSON.parse(readFileSync21(resolve18(fixturePath2), "utf8")));
|
|
8314
|
+
const observation = JSON.parse(readFileSync21(resolve18(observationPath), "utf8"));
|
|
8315
|
+
const result = measureBenchmark(observation, fixture);
|
|
8316
|
+
if (flags.has("--text")) writeLines([formatBenchmarkText(result)]);
|
|
8317
|
+
else writeJson2(result);
|
|
8318
|
+
return result.regressions.length ? 1 : 0;
|
|
8319
|
+
} catch (error) {
|
|
8320
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
7476
8321
|
`);
|
|
7477
8322
|
return 2;
|
|
7478
8323
|
}
|
|
@@ -7741,7 +8586,7 @@ var runCli = (argv) => {
|
|
|
7741
8586
|
const report = runDoctor(root, config);
|
|
7742
8587
|
if (flags.has("--write-badge")) {
|
|
7743
8588
|
const badgePath = resolve18(root, ".doc-bridge", "coverage-badge.json");
|
|
7744
|
-
|
|
8589
|
+
mkdirSync7(dirname9(badgePath), { recursive: true });
|
|
7745
8590
|
writeFileSync8(badgePath, `${formatDoctorBadgeJson(report.badge)}
|
|
7746
8591
|
`, "utf8");
|
|
7747
8592
|
}
|