@agentskit/doc-bridge 1.6.4 → 1.7.44
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 +243 -0
- package/action.yml +1 -1
- package/dist/cli/program.js +793 -137
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +38 -2
- package/dist/config/index.js.map +1 -1
- package/dist/{index-DudNuwI5.d.ts → index-C2PCQSrB.d.ts} +216 -25
- package/dist/index.d.ts +837 -67
- package/dist/index.js +858 -127
- package/dist/index.js.map +1 -1
- package/docs/PRD-enterprise-hardening.md +288 -0
- package/docs/adr/0001-enterprise-verification-contract.md +35 -0
- package/docs/knowledge-engine-runbook.md +18 -2
- package/docs/spec/analyzer-plugin-v1.md +24 -0
- package/docs/spec/benchmark-v1.md +30 -0
- package/docs/spec/config-v1.md +111 -0
- package/docs/validation-cycle-plan.md +236 -0
- package/docs/verification-harness.md +33 -4
- package/mcpb/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/report-visual-check.mjs +45 -10
- package/scripts/verification-harness.mjs +216 -13
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +31 -7
- package/src/cli/program.ts +44 -11
- package/src/config/index.ts +2 -0
- package/src/config/schema.ts +56 -0
- package/src/discovery/documentation.ts +46 -5
- package/src/discovery/repository.ts +95 -16
- package/src/index.ts +29 -0
- package/src/metrics/benchmark.ts +176 -0
- package/src/plugins/contract.ts +89 -0
- package/src/reconciliation/reconcile.ts +137 -3
- package/src/report/html.ts +302 -78
- package/src/schemas/knowledge.ts +16 -1
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +65 -9
package/dist/index.js
CHANGED
|
@@ -205,8 +205,32 @@ var RulesConfigSchema = z.object({
|
|
|
205
205
|
warningThresholds: z.record(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
|
|
206
206
|
}).strict();
|
|
207
207
|
var ReconciliationConfigSchema = z.object({
|
|
208
|
+
/** Semantic comparison level. Raw discovery always keeps file-level relations. */
|
|
209
|
+
scope: z.enum(["file", "module", "package"]).optional(),
|
|
208
210
|
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
209
|
-
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional()
|
|
211
|
+
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
|
|
212
|
+
includeOrphanedDocuments: z.boolean().optional()
|
|
213
|
+
}).strict();
|
|
214
|
+
var AnalysisConfigSchema = z.object({
|
|
215
|
+
plugins: z.array(
|
|
216
|
+
z.object({
|
|
217
|
+
id: z.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
|
|
218
|
+
enabled: z.boolean().optional(),
|
|
219
|
+
order: z.number().int().nonnegative().optional(),
|
|
220
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
221
|
+
reason: z.string().min(1).max(1024).optional()
|
|
222
|
+
}).strict()
|
|
223
|
+
).max(128).optional(),
|
|
224
|
+
jsTs: z.object({
|
|
225
|
+
runtimeWiringMethods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).max(64).optional(),
|
|
226
|
+
runtimeWiringAdapters: z.array(
|
|
227
|
+
z.object({
|
|
228
|
+
id: z.string().min(1).max(128),
|
|
229
|
+
methods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).min(1).max(64)
|
|
230
|
+
}).strict()
|
|
231
|
+
).max(32).optional(),
|
|
232
|
+
includeTestRuntimeWiring: z.boolean().optional()
|
|
233
|
+
}).strict().optional()
|
|
210
234
|
}).strict();
|
|
211
235
|
var WorkflowConfigSchema = z.object({
|
|
212
236
|
stateDir: z.string().min(1).max(512).optional()
|
|
@@ -219,6 +243,10 @@ var RepositorySafetyConfigSchema = z.object({
|
|
|
219
243
|
maxMemoryMb: z.number().int().positive().max(1048576).optional(),
|
|
220
244
|
redactSecrets: z.boolean().optional()
|
|
221
245
|
}).strict();
|
|
246
|
+
var ReportConfigSchema = z.object({
|
|
247
|
+
/** Public report privacy mode. Private is the default; anonymized preserves topology without project identity. */
|
|
248
|
+
privacy: z.enum(["private", "anonymized"]).optional()
|
|
249
|
+
}).strict();
|
|
222
250
|
var SurfacesConfigSchema = z.object({
|
|
223
251
|
cli: z.object({
|
|
224
252
|
bin: z.string().min(1).max(64).optional(),
|
|
@@ -290,7 +318,12 @@ var IntelligenceConfigSchema = z.object({
|
|
|
290
318
|
enabled: z.boolean().optional(),
|
|
291
319
|
agentId: z.string().min(1).max(256).optional(),
|
|
292
320
|
agentRoot: z.string().min(1).max(512).optional(),
|
|
293
|
-
runnerModule: z.string().min(1).max(512).optional()
|
|
321
|
+
runnerModule: z.string().min(1).max(512).optional(),
|
|
322
|
+
deterministic: z.boolean().optional(),
|
|
323
|
+
timeoutMs: z.number().int().positive().max(6e5).optional(),
|
|
324
|
+
maxTokens: z.number().int().positive().max(1e6).optional(),
|
|
325
|
+
maxResponseBytes: z.number().int().positive().max(1e7).optional(),
|
|
326
|
+
maxConcurrency: z.number().int().positive().max(64).optional()
|
|
294
327
|
}).strict().optional()
|
|
295
328
|
}).strict();
|
|
296
329
|
var FederationConfigSchema = z.object({
|
|
@@ -382,9 +415,11 @@ var DocBridgeConfigV1Schema = z.object({
|
|
|
382
415
|
routing: RoutingConfigSchema.optional(),
|
|
383
416
|
gates: GatesConfigSchema.optional(),
|
|
384
417
|
reconciliation: ReconciliationConfigSchema.optional(),
|
|
418
|
+
analysis: AnalysisConfigSchema.optional(),
|
|
385
419
|
rules: RulesConfigSchema.optional(),
|
|
386
420
|
workflow: WorkflowConfigSchema.optional(),
|
|
387
421
|
safety: RepositorySafetyConfigSchema.optional(),
|
|
422
|
+
report: ReportConfigSchema.optional(),
|
|
388
423
|
surfaces: SurfacesConfigSchema.optional(),
|
|
389
424
|
intelligence: IntelligenceConfigSchema.optional(),
|
|
390
425
|
federation: FederationConfigSchema.optional(),
|
|
@@ -1110,9 +1145,10 @@ var EvidenceSchema = z5.object({
|
|
|
1110
1145
|
});
|
|
1111
1146
|
}
|
|
1112
1147
|
});
|
|
1113
|
-
var CoverageStatusSchema = z5.enum(["complete", "partial", "not-analyzed"]);
|
|
1148
|
+
var CoverageStatusSchema = z5.enum(["complete", "partial", "not-analyzed", "not-applicable"]);
|
|
1114
1149
|
var CoverageSchema = z5.object({
|
|
1115
1150
|
analyzer: boundedString(128),
|
|
1151
|
+
analyzerVersion: boundedString(64).optional(),
|
|
1116
1152
|
scope: boundedString(512),
|
|
1117
1153
|
status: CoverageStatusSchema,
|
|
1118
1154
|
reason: z5.string().max(1024).optional(),
|
|
@@ -1195,7 +1231,21 @@ var ReconciliationReportV1Schema = z5.object({
|
|
|
1195
1231
|
entityCount: z5.number().int().nonnegative(),
|
|
1196
1232
|
relationCount: z5.number().int().nonnegative(),
|
|
1197
1233
|
diagnosticCount: z5.number().int().nonnegative(),
|
|
1198
|
-
|
|
1234
|
+
scope: z5.enum(["file", "module", "package"]).optional(),
|
|
1235
|
+
requiredRelationKinds: z5.array(boundedString(128)).max(128).optional(),
|
|
1236
|
+
diagnosticsByCode: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
|
|
1237
|
+
diagnosticsByStatus: z5.record(z5.string().max(128), z5.number().int().nonnegative()).optional(),
|
|
1238
|
+
documentation: z5.object({
|
|
1239
|
+
documentCount: z5.number().int().nonnegative(),
|
|
1240
|
+
documentedDocumentCount: z5.number().int().nonnegative(),
|
|
1241
|
+
packageCount: z5.number().int().nonnegative(),
|
|
1242
|
+
packageStatus: z5.object({
|
|
1243
|
+
fresh: z5.number().int().nonnegative(),
|
|
1244
|
+
stale: z5.number().int().nonnegative(),
|
|
1245
|
+
missing: z5.number().int().nonnegative(),
|
|
1246
|
+
unverified: z5.number().int().nonnegative()
|
|
1247
|
+
}).strict()
|
|
1248
|
+
}).strict().optional()
|
|
1199
1249
|
}).strict()
|
|
1200
1250
|
}).strict();
|
|
1201
1251
|
var WorkflowStateSchema = z5.enum([
|
|
@@ -1863,7 +1913,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1863
1913
|
};
|
|
1864
1914
|
|
|
1865
1915
|
// src/version.ts
|
|
1866
|
-
var PACKAGE_VERSION = "1.
|
|
1916
|
+
var PACKAGE_VERSION = "1.7.44";
|
|
1867
1917
|
|
|
1868
1918
|
// src/index-builder/capabilities.ts
|
|
1869
1919
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1920,9 +1970,9 @@ var contentHashForArtifactV1 = (artifact2) => {
|
|
|
1920
1970
|
|
|
1921
1971
|
// src/index-builder/llms-txt.ts
|
|
1922
1972
|
var routePath = (path, pathPrefix) => {
|
|
1923
|
-
const
|
|
1973
|
+
const normalizedPath2 = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
|
|
1924
1974
|
const normalizedPrefix = pathPrefix?.replaceAll("\\", "/").replace(/\/$/, "");
|
|
1925
|
-
const relativePath3 = normalizedPrefix &&
|
|
1975
|
+
const relativePath3 = normalizedPrefix && normalizedPath2.startsWith(`${normalizedPrefix}/`) ? normalizedPath2.slice(normalizedPrefix.length + 1) : normalizedPath2;
|
|
1926
1976
|
return relativePath3.replace(/^\/+/, "");
|
|
1927
1977
|
};
|
|
1928
1978
|
var knowledgeUrl = (path, options = {}) => {
|
|
@@ -2670,6 +2720,8 @@ var SOURCE_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", "
|
|
|
2670
2720
|
var DOCUMENT_EXTENSIONS = [".md", ".mdx"];
|
|
2671
2721
|
var DEFAULT_MAX_FILES2 = 1e4;
|
|
2672
2722
|
var EMPTY_HASH = "0".repeat(64);
|
|
2723
|
+
var DEFAULT_RUNTIME_WIRING_METHODS = ["register", "use", "mount", "attach"];
|
|
2724
|
+
var TEST_MODULE_PATTERN = /(?:\.test|\.spec|__tests__)/;
|
|
2673
2725
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2674
2726
|
var readJson = (path) => {
|
|
2675
2727
|
try {
|
|
@@ -2698,6 +2750,12 @@ var firstLineContaining = (text, pattern) => {
|
|
|
2698
2750
|
const line = text.split(/\r?\n/).findIndex((value) => value.includes(pattern));
|
|
2699
2751
|
return line >= 0 ? line + 1 : void 0;
|
|
2700
2752
|
};
|
|
2753
|
+
var documentClassification = (path) => {
|
|
2754
|
+
if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
|
|
2755
|
+
if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
|
|
2756
|
+
if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
|
|
2757
|
+
return "unclassified";
|
|
2758
|
+
};
|
|
2701
2759
|
var packageName = (manifest, fallback) => typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : fallback || void 0;
|
|
2702
2760
|
var workspacePatterns = (root, rootManifest) => {
|
|
2703
2761
|
const fromPackageJson = rootManifest?.workspaces;
|
|
@@ -2839,29 +2897,67 @@ var exportedNames = (sourceFile) => {
|
|
|
2839
2897
|
visit(sourceFile);
|
|
2840
2898
|
return [...names].sort();
|
|
2841
2899
|
};
|
|
2842
|
-
var moduleReferences = (root, path, sourceFile) => {
|
|
2900
|
+
var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
2843
2901
|
const references = [];
|
|
2844
2902
|
let hasDynamic = false;
|
|
2903
|
+
let hasLiteralDynamic = false;
|
|
2845
2904
|
let hasRuntimeWiring = false;
|
|
2846
|
-
|
|
2847
|
-
|
|
2905
|
+
let hasUnresolvedRuntimeWiring = false;
|
|
2906
|
+
const importedBindings = /* @__PURE__ */ new Map();
|
|
2907
|
+
const staticStringBindings = /* @__PURE__ */ new Map();
|
|
2908
|
+
const collectStaticStringBindings = (node) => {
|
|
2909
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isStringLiteralLike(node.initializer) && ts.isVariableDeclarationList(node.parent) && (node.parent.flags & ts.NodeFlags.Const) !== 0) {
|
|
2910
|
+
const previous = staticStringBindings.get(node.name.text);
|
|
2911
|
+
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === node.initializer.text ? node.initializer.text : void 0);
|
|
2912
|
+
}
|
|
2913
|
+
ts.forEachChild(node, collectStaticStringBindings);
|
|
2914
|
+
};
|
|
2915
|
+
collectStaticStringBindings(sourceFile);
|
|
2916
|
+
const addReference = (specifier, kind, node, detection) => {
|
|
2917
|
+
references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
|
|
2848
2918
|
};
|
|
2849
2919
|
const visit = (node) => {
|
|
2850
2920
|
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
2851
2921
|
addReference(node.moduleSpecifier, "imports", node);
|
|
2922
|
+
const clause = node.importClause;
|
|
2923
|
+
if (clause?.name) importedBindings.set(clause.name.text, node.moduleSpecifier.text);
|
|
2924
|
+
if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings)) importedBindings.set(clause.namedBindings.name.text, node.moduleSpecifier.text);
|
|
2925
|
+
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
2926
|
+
for (const element of clause.namedBindings.elements) importedBindings.set((element.name ?? element.propertyName)?.text ?? "", node.moduleSpecifier.text);
|
|
2927
|
+
}
|
|
2852
2928
|
} else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
2853
2929
|
addReference(node.moduleSpecifier, "re-exports", node);
|
|
2854
2930
|
} else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) {
|
|
2855
2931
|
addReference(node.moduleReference.expression, "imports", node);
|
|
2932
|
+
importedBindings.set(node.name.text, node.moduleReference.expression.text);
|
|
2856
2933
|
} else if (ts.isCallExpression(node)) {
|
|
2857
2934
|
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
2858
|
-
if (
|
|
2935
|
+
if (node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {
|
|
2936
|
+
hasLiteralDynamic = true;
|
|
2937
|
+
addReference(node.arguments[0], "imports", node, "dynamic-literal");
|
|
2938
|
+
} else if (node.arguments[0] && ts.isIdentifier(node.arguments[0]) && staticStringBindings.get(node.arguments[0].text)) {
|
|
2939
|
+
hasLiteralDynamic = true;
|
|
2940
|
+
addReference({ text: staticStringBindings.get(node.arguments[0].text) }, "imports", node, "dynamic-literal");
|
|
2941
|
+
} else hasDynamic = true;
|
|
2859
2942
|
} else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
2860
2943
|
const argument = node.arguments[0];
|
|
2861
2944
|
if (argument && ts.isStringLiteralLike(argument)) addReference(argument, "imports", node);
|
|
2945
|
+
else if (argument && ts.isIdentifier(argument) && staticStringBindings.get(argument.text)) addReference({ text: staticStringBindings.get(argument.text) }, "imports", node);
|
|
2862
2946
|
else hasDynamic = true;
|
|
2863
|
-
} else if (ts.isPropertyAccessExpression(node.expression) && node.expression.name.text
|
|
2947
|
+
} else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
|
|
2864
2948
|
hasRuntimeWiring = true;
|
|
2949
|
+
const before = references.length;
|
|
2950
|
+
let hasPotentialTargetArgument = false;
|
|
2951
|
+
for (const argument of node.arguments) {
|
|
2952
|
+
if (ts.isIdentifier(argument)) {
|
|
2953
|
+
hasPotentialTargetArgument = true;
|
|
2954
|
+
const specifier = importedBindings.get(argument.text);
|
|
2955
|
+
if (specifier) addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
2956
|
+
} else if (ts.isPropertyAccessExpression(argument) || ts.isCallExpression(argument)) {
|
|
2957
|
+
hasPotentialTargetArgument = true;
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
if (hasPotentialTargetArgument && references.length === before) hasUnresolvedRuntimeWiring = true;
|
|
2865
2961
|
}
|
|
2866
2962
|
}
|
|
2867
2963
|
ts.forEachChild(node, visit);
|
|
@@ -2871,7 +2967,9 @@ var moduleReferences = (root, path, sourceFile) => {
|
|
|
2871
2967
|
references,
|
|
2872
2968
|
exports: exportedNames(sourceFile),
|
|
2873
2969
|
hasDynamic,
|
|
2874
|
-
|
|
2970
|
+
hasLiteralDynamic,
|
|
2971
|
+
hasRuntimeWiring,
|
|
2972
|
+
hasUnresolvedRuntimeWiring
|
|
2875
2973
|
};
|
|
2876
2974
|
};
|
|
2877
2975
|
var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
|
|
@@ -2945,10 +3043,10 @@ var artifact = (root, config, files, entities, relations, coverage) => {
|
|
|
2945
3043
|
sourceRevisionKind: revision.kind,
|
|
2946
3044
|
configurationHash: sha256NormalizedV1(config ?? {}),
|
|
2947
3045
|
pipelineVersion: "1.0.0",
|
|
2948
|
-
analyzerVersions: { repository: "1.0.0", "js-ts": "1.
|
|
3046
|
+
analyzerVersions: { repository: "1.0.0", "js-ts": "1.3.0" },
|
|
2949
3047
|
entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
|
|
2950
3048
|
relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
|
|
2951
|
-
coverage: [
|
|
3049
|
+
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.0.0", "js-ts": "1.3.0" }[entry.analyzer] ?? "1.0.0") }))
|
|
2952
3050
|
};
|
|
2953
3051
|
return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
2954
3052
|
};
|
|
@@ -3008,12 +3106,12 @@ var discoverRepository = (opts = {}) => {
|
|
|
3008
3106
|
const sourceFile = ts.createSourceFile(absPath, text, ts.ScriptTarget.Latest, true, scriptKind(absPath));
|
|
3009
3107
|
const exports = exportedNames(sourceFile);
|
|
3010
3108
|
modules.set(resolve7(absPath), { absPath, path, entityId: id, ...pkg ? { packageId: pkg.id } : {} });
|
|
3011
|
-
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:
|
|
3109
|
+
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) } } : {} });
|
|
3012
3110
|
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)] });
|
|
3013
3111
|
}
|
|
3014
3112
|
for (const absPath of documentPaths) {
|
|
3015
3113
|
const path = relativePath(root, absPath);
|
|
3016
|
-
addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)] });
|
|
3114
|
+
addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)], metadata: { classification: documentClassification(path) } });
|
|
3017
3115
|
}
|
|
3018
3116
|
const compiler = readCompilerOptions(root);
|
|
3019
3117
|
const coverage = [
|
|
@@ -3021,8 +3119,8 @@ var discoverRepository = (opts = {}) => {
|
|
|
3021
3119
|
{ 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)}.` } : {} },
|
|
3022
3120
|
{ 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 } : {} },
|
|
3023
3121
|
{ analyzer: "js-ts", scope: "static-imports-and-exports", status: compiler.error ? "partial" : "complete", ...compiler.error ? { reason: compiler.error } : {} },
|
|
3024
|
-
{ analyzer: "js-ts", scope: "dynamic-imports", status: "not-
|
|
3025
|
-
{ analyzer: "js-ts", scope: "runtime-wiring", status: "not-
|
|
3122
|
+
{ analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." },
|
|
3123
|
+
{ analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." },
|
|
3026
3124
|
{ analyzer: "js-ts", scope: "generated-code", status: "not-analyzed", reason: "Generated code is not interpreted as source architecture." }
|
|
3027
3125
|
];
|
|
3028
3126
|
for (const pkg of packageResult.packages) {
|
|
@@ -3033,10 +3131,26 @@ var discoverRepository = (opts = {}) => {
|
|
|
3033
3131
|
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 } });
|
|
3034
3132
|
}
|
|
3035
3133
|
}
|
|
3134
|
+
const dynamicCoverageIndex = coverage.findIndex((entry) => entry.scope === "dynamic-imports");
|
|
3135
|
+
const configuredRuntimeWiringMethods = /* @__PURE__ */ new Set([
|
|
3136
|
+
...opts.config?.analysis?.jsTs?.runtimeWiringMethods ?? [],
|
|
3137
|
+
...opts.config?.analysis?.jsTs?.runtimeWiringAdapters?.flatMap((adapter) => adapter.methods) ?? []
|
|
3138
|
+
]);
|
|
3139
|
+
if (!configuredRuntimeWiringMethods.size) for (const method of DEFAULT_RUNTIME_WIRING_METHODS) configuredRuntimeWiringMethods.add(method);
|
|
3140
|
+
const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
|
|
3141
|
+
let observedLiteralDynamic = false;
|
|
3142
|
+
let observedUnresolvedDynamic = false;
|
|
3143
|
+
let observedRuntimeWiring = false;
|
|
3144
|
+
let observedUnresolvedRuntimeWiring = false;
|
|
3036
3145
|
for (const module of modules.values()) {
|
|
3037
3146
|
const text = readFileSync8(module.absPath, "utf8");
|
|
3038
3147
|
const sourceFile = ts.createSourceFile(module.absPath, text, ts.ScriptTarget.Latest, true, scriptKind(module.absPath));
|
|
3039
|
-
const
|
|
3148
|
+
const runtimeWiringMethods = includeTestRuntimeWiring || !TEST_MODULE_PATTERN.test(module.path) ? configuredRuntimeWiringMethods : /* @__PURE__ */ new Set();
|
|
3149
|
+
const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
|
|
3150
|
+
observedLiteralDynamic ||= references.hasLiteralDynamic;
|
|
3151
|
+
observedUnresolvedDynamic ||= references.hasDynamic;
|
|
3152
|
+
observedRuntimeWiring ||= references.hasRuntimeWiring;
|
|
3153
|
+
observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
|
|
3040
3154
|
for (const reference of references.references) {
|
|
3041
3155
|
const target = resolveReference(reference, module.absPath, modules, packageResult.packages, compiler.options);
|
|
3042
3156
|
if (!target) continue;
|
|
@@ -3044,11 +3158,14 @@ var discoverRepository = (opts = {}) => {
|
|
|
3044
3158
|
const externalName = target.targetId.replace(/^external:/, "");
|
|
3045
3159
|
addEntity({ id: target.targetId, kind: "external", name: externalName, provenance: "observed", evidence: [reference.evidence] });
|
|
3046
3160
|
}
|
|
3047
|
-
addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence] });
|
|
3161
|
+
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 } } : {} });
|
|
3048
3162
|
}
|
|
3049
|
-
if (references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: "not-analyzed", reason: "A dynamic import
|
|
3050
|
-
if (references.
|
|
3163
|
+
if (references.hasLiteralDynamic || references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: references.hasDynamic ? "not-analyzed" : "complete", reason: references.hasDynamic ? "A non-literal dynamic import was found; the target is unresolved." : "Literal dynamic imports were resolved.", evidence: [lineEvidence("code", root, module.absPath)] });
|
|
3164
|
+
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)] });
|
|
3051
3165
|
}
|
|
3166
|
+
if (dynamicCoverageIndex >= 0) coverage[dynamicCoverageIndex] = observedUnresolvedDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "partial", reason: "Literal dynamic imports are resolved; non-literal import expressions and require calls remain unresolved." } : observedLiteralDynamic ? { analyzer: "js-ts", scope: "dynamic-imports", status: "complete", reason: "All observed dynamic imports used literal targets and were resolved." } : { analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." };
|
|
3167
|
+
const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
|
|
3168
|
+
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." };
|
|
3052
3169
|
return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
|
|
3053
3170
|
};
|
|
3054
3171
|
|
|
@@ -3098,15 +3215,40 @@ var loadRegistryAgentMetadata = (root, config) => {
|
|
|
3098
3215
|
var createRegistryAgentAdapter = (root, config, runner) => {
|
|
3099
3216
|
if (!registryConfig(config)?.enabled) throw new Error("Registry agents are disabled. Set intelligence.registry.enabled: true to run an assisted workflow.");
|
|
3100
3217
|
const metadata = loadRegistryAgentMetadata(resolve8(root), config);
|
|
3218
|
+
const settings = registryConfig(config) ?? {};
|
|
3219
|
+
const timeoutMs = settings.timeoutMs ?? 12e4;
|
|
3220
|
+
const maxResponseBytes = settings.maxResponseBytes ?? 256e3;
|
|
3221
|
+
const maxTokens = settings.maxTokens ?? Math.ceil(maxResponseBytes / 4);
|
|
3222
|
+
const maxConcurrency = settings.maxConcurrency ?? 1;
|
|
3223
|
+
let active = 0;
|
|
3224
|
+
const deterministicCache = /* @__PURE__ */ new Map();
|
|
3101
3225
|
return {
|
|
3102
3226
|
metadata,
|
|
3103
3227
|
run: async (snapshot, report, evidence2 = report.diagnostics.flatMap((diagnostic2) => diagnostic2.evidence).slice(0, 64)) => {
|
|
3104
|
-
|
|
3105
|
-
const
|
|
3106
|
-
if (
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3228
|
+
if (active >= maxConcurrency) throw new Error(`Registry agent concurrency limit ${maxConcurrency} exceeded.`);
|
|
3229
|
+
const cacheKey = sha256NormalizedV1({ snapshotHash: snapshot.contentHash, reportHash: report.contentHash, agentId: metadata.id, agentVersion: metadata.version, evidence: evidence2 });
|
|
3230
|
+
if (settings.deterministic && deterministicCache.has(cacheKey)) return deterministicCache.get(cacheKey);
|
|
3231
|
+
active += 1;
|
|
3232
|
+
let timer;
|
|
3233
|
+
try {
|
|
3234
|
+
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 });
|
|
3235
|
+
const timeout = new Promise((_, reject) => {
|
|
3236
|
+
timer = setTimeout(() => reject(new Error(`Registry agent timed out after ${timeoutMs}ms.`)), timeoutMs);
|
|
3237
|
+
});
|
|
3238
|
+
const raw = await Promise.race([Promise.resolve(runner(context)), timeout]);
|
|
3239
|
+
const responseBytes = Buffer.byteLength(JSON.stringify(raw));
|
|
3240
|
+
if (responseBytes > maxResponseBytes) throw new Error(`Registry agent response limit ${maxResponseBytes} bytes exceeded.`);
|
|
3241
|
+
if (Math.ceil(responseBytes / 4) > maxTokens) throw new Error(`Registry agent token budget ${maxTokens} exceeded.`);
|
|
3242
|
+
const proposal = AgentProposalV1Schema.parse(raw);
|
|
3243
|
+
if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Registry agent proposal contentHash does not match its canonical contents.");
|
|
3244
|
+
if (proposal.baseSnapshotHash !== snapshot.contentHash || proposal.baseReportHash !== report.contentHash) throw new Error("Registry agent proposal is not based on the supplied snapshot/report hashes.");
|
|
3245
|
+
if (proposal.origin.kind !== "registry-agent" || proposal.origin.id !== metadata.id) throw new Error(`Registry agent proposal origin must be ${metadata.id}.`);
|
|
3246
|
+
if (settings.deterministic) deterministicCache.set(cacheKey, proposal);
|
|
3247
|
+
return proposal;
|
|
3248
|
+
} finally {
|
|
3249
|
+
if (timer) clearTimeout(timer);
|
|
3250
|
+
active -= 1;
|
|
3251
|
+
}
|
|
3110
3252
|
}
|
|
3111
3253
|
};
|
|
3112
3254
|
};
|
|
@@ -3137,6 +3279,14 @@ var scalar = (value) => {
|
|
|
3137
3279
|
}
|
|
3138
3280
|
return trimmed;
|
|
3139
3281
|
};
|
|
3282
|
+
var conventionalPackageReference = (path, agentRoot) => {
|
|
3283
|
+
const prefix = `${agentRoot.replace(/\/$/, "")}/`;
|
|
3284
|
+
if (!path.startsWith(prefix)) return void 0;
|
|
3285
|
+
const relative10 = path.slice(prefix.length);
|
|
3286
|
+
const [scope, file] = relative10.split("/");
|
|
3287
|
+
if (scope !== "packages" && scope !== "apps" || !file) return void 0;
|
|
3288
|
+
return file.replace(/\.mdx?$/, "");
|
|
3289
|
+
};
|
|
3140
3290
|
var list = (value) => {
|
|
3141
3291
|
const trimmed = value.trim();
|
|
3142
3292
|
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
@@ -3153,7 +3303,15 @@ var addDiagnostic = (diagnostics, input, code, message, line, endLine = line) =>
|
|
|
3153
3303
|
diagnostics.push(diagnostic(input.path, code, message, line, endLine));
|
|
3154
3304
|
};
|
|
3155
3305
|
var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
|
|
3156
|
-
const
|
|
3306
|
+
const direct = entities.find((entity2) => entity2.id === reference || entity2.aliases?.includes(reference));
|
|
3307
|
+
if (direct) return direct;
|
|
3308
|
+
const packageReference = reference.replace(/^package:/, "");
|
|
3309
|
+
const packageCandidates = entities.filter((entity2) => {
|
|
3310
|
+
if (entity2.kind !== "package") return false;
|
|
3311
|
+
const pathName = entity2.path?.split("/").pop();
|
|
3312
|
+
return entity2.name === reference || entity2.path === reference || pathName === packageReference || entity2.name.endsWith(`/${packageReference}`);
|
|
3313
|
+
});
|
|
3314
|
+
const resolved = packageCandidates.length === 1 ? packageCandidates[0] : void 0;
|
|
3157
3315
|
if (resolved) return resolved;
|
|
3158
3316
|
const id = `unresolved:${reference}`;
|
|
3159
3317
|
const existing = unresolved.get(id);
|
|
@@ -3169,9 +3327,13 @@ var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
|
|
|
3169
3327
|
return entity;
|
|
3170
3328
|
};
|
|
3171
3329
|
var relationKey = (from, to, kind) => `${from}\0${to}\0${kind}`;
|
|
3172
|
-
var parseBlock = (input) => {
|
|
3330
|
+
var parseBlock = (input, options = {}) => {
|
|
3331
|
+
const conventionalPath = conventionalPackageReference(input.path, options.agentRoot ?? "docs/for-agents");
|
|
3173
3332
|
const frontmatter = findFrontmatter(input.content);
|
|
3174
3333
|
if (!frontmatter) {
|
|
3334
|
+
if (conventionalPath && !input.content.replace(/^\uFEFF/, "").startsWith("---")) {
|
|
3335
|
+
return { covers: [{ value: conventionalPath, line: 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
3336
|
+
}
|
|
3175
3337
|
if (input.content.replace(/^\uFEFF/, "").startsWith("---")) {
|
|
3176
3338
|
return {
|
|
3177
3339
|
covers: [],
|
|
@@ -3183,8 +3345,24 @@ var parseBlock = (input) => {
|
|
|
3183
3345
|
return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
|
|
3184
3346
|
}
|
|
3185
3347
|
const { lines, end } = frontmatter;
|
|
3186
|
-
|
|
3187
|
-
|
|
3348
|
+
let docbridgeLine = -1;
|
|
3349
|
+
let typeLine = -1;
|
|
3350
|
+
let packageLine = -1;
|
|
3351
|
+
let humanDocLine = -1;
|
|
3352
|
+
for (let index = 1; index < end; index += 1) {
|
|
3353
|
+
const line = lines[index] ?? "";
|
|
3354
|
+
if (docbridgeLine < 0 && /^docbridge\s*:/.test(line)) docbridgeLine = index;
|
|
3355
|
+
if (typeLine < 0 && /^type\s*:/.test(line)) typeLine = index;
|
|
3356
|
+
if (packageLine < 0 && /^package\s*:/.test(line)) packageLine = index;
|
|
3357
|
+
if (humanDocLine < 0 && /^humanDoc\s*:/.test(line)) humanDocLine = index;
|
|
3358
|
+
}
|
|
3359
|
+
if (docbridgeLine < 0) {
|
|
3360
|
+
const type = typeLine >= 0 ? scalar(lines[typeLine]?.slice("type:".length) ?? "") : "";
|
|
3361
|
+
const packageReference = packageLine >= 0 ? scalar(lines[packageLine]?.slice("package:".length) ?? "") : conventionalPath ?? "";
|
|
3362
|
+
if (type === "package" && packageReference) return { covers: [{ value: packageReference, line: packageLine >= 0 ? packageLine + 1 : typeLine + 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
3363
|
+
if (conventionalPath) return { covers: [{ value: packageReference, line: humanDocLine >= 0 ? humanDocLine + 1 : 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
3364
|
+
return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
|
|
3365
|
+
}
|
|
3188
3366
|
const diagnostics = [];
|
|
3189
3367
|
const covers = [];
|
|
3190
3368
|
const relations = [];
|
|
@@ -3269,7 +3447,7 @@ var parseBlock = (input) => {
|
|
|
3269
3447
|
return { covers, relations, diagnostics, hasDocbridge: true };
|
|
3270
3448
|
};
|
|
3271
3449
|
var parseDocumentationDeclarations = (input, options) => {
|
|
3272
|
-
const parsed = parseBlock(input);
|
|
3450
|
+
const parsed = parseBlock(input, options);
|
|
3273
3451
|
if (!parsed.hasDocbridge) return { hasDocbridge: false, entities: [], relations: [], diagnostics: [] };
|
|
3274
3452
|
const diagnostics = [...parsed.diagnostics];
|
|
3275
3453
|
const unresolved = /* @__PURE__ */ new Map();
|
|
@@ -3330,12 +3508,12 @@ var parseDocumentationDeclarations = (input, options) => {
|
|
|
3330
3508
|
diagnostics
|
|
3331
3509
|
};
|
|
3332
3510
|
};
|
|
3333
|
-
var applyDocumentationDeclarations = (snapshot, documents) => {
|
|
3511
|
+
var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
|
|
3334
3512
|
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
3335
3513
|
const relations = new Map(snapshot.relations.map((relation) => [relation.id, relation]));
|
|
3336
3514
|
const diagnostics = [];
|
|
3337
3515
|
for (const document of documents) {
|
|
3338
|
-
const result = parseDocumentationDeclarations(document, { snapshot });
|
|
3516
|
+
const result = parseDocumentationDeclarations(document, { snapshot, ...options });
|
|
3339
3517
|
diagnostics.push(...result.diagnostics);
|
|
3340
3518
|
for (const entity of result.entities) entities.set(entity.id, entity);
|
|
3341
3519
|
for (const relation of result.relations) relations.set(relation.id, relation);
|
|
@@ -3374,6 +3552,12 @@ var mergeEvidence = (...relations) => {
|
|
|
3374
3552
|
}
|
|
3375
3553
|
return [...merged.values()].sort((a, b) => evidenceKey(a).localeCompare(evidenceKey(b)));
|
|
3376
3554
|
};
|
|
3555
|
+
var boundedEvidence = (evidence2) => [...evidence2].slice(0, 64);
|
|
3556
|
+
var diagnosticCounts = (values) => {
|
|
3557
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3558
|
+
for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
3559
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
3560
|
+
};
|
|
3377
3561
|
var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
|
|
3378
3562
|
var coverageAvailable = (snapshot, relation) => {
|
|
3379
3563
|
const status = snapshot.coverage.find(
|
|
@@ -3389,6 +3573,63 @@ var entityById = (snapshots) => {
|
|
|
3389
3573
|
return entities;
|
|
3390
3574
|
};
|
|
3391
3575
|
var isUnresolved = (id, entities) => id.startsWith("unresolved:") || entities.get(id)?.kind === "unresolved-reference";
|
|
3576
|
+
var normalizedPath = (path) => path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
3577
|
+
var semanticEntityId = (id, scope, entities, packageByModule) => {
|
|
3578
|
+
if (scope === "file" || id.startsWith("external:") || id.startsWith("unresolved:")) return id;
|
|
3579
|
+
const entity = entities.get(id);
|
|
3580
|
+
if (!entity || entity.kind !== "module" || !entity.path) return id;
|
|
3581
|
+
if (scope === "module") return id;
|
|
3582
|
+
return packageByModule.get(id) ?? id;
|
|
3583
|
+
};
|
|
3584
|
+
var packageLookup = (scope, entities) => {
|
|
3585
|
+
if (scope !== "package") return /* @__PURE__ */ new Map();
|
|
3586
|
+
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));
|
|
3587
|
+
const result = /* @__PURE__ */ new Map();
|
|
3588
|
+
for (const entity of entities.values()) {
|
|
3589
|
+
if (entity.kind !== "module" || !entity.path) continue;
|
|
3590
|
+
const modulePath = normalizedPath(entity.path);
|
|
3591
|
+
const packageEntity = packages.find(({ path }) => path === "." || modulePath === path || modulePath.startsWith(`${path}/`));
|
|
3592
|
+
if (packageEntity) result.set(entity.id, packageEntity.id);
|
|
3593
|
+
}
|
|
3594
|
+
return result;
|
|
3595
|
+
};
|
|
3596
|
+
var aggregatedRelations = (relations, scope, entities, packageByModule) => {
|
|
3597
|
+
if (scope === "file") return [...relations];
|
|
3598
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3599
|
+
for (const relation of relations) {
|
|
3600
|
+
const from = semanticEntityId(relation.from, scope, entities, packageByModule);
|
|
3601
|
+
const to = semanticEntityId(relation.to, scope, entities, packageByModule);
|
|
3602
|
+
const detection = relationDetection(relation);
|
|
3603
|
+
const key = `${from}\0${to}\0${relation.kind}\0${detection}`;
|
|
3604
|
+
const group = groups.get(key) ?? [];
|
|
3605
|
+
group.push(relation);
|
|
3606
|
+
groups.set(key, group);
|
|
3607
|
+
}
|
|
3608
|
+
return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, group]) => {
|
|
3609
|
+
const first = group[0];
|
|
3610
|
+
const parts = key.split("\0");
|
|
3611
|
+
const from = parts[0] ?? first.from;
|
|
3612
|
+
const to = parts[1] ?? first.to;
|
|
3613
|
+
const kind = parts[2] ?? first.kind;
|
|
3614
|
+
const detection = parts[3] ?? relationDetection(first);
|
|
3615
|
+
const mergedEvidence = mergeEvidence(...group);
|
|
3616
|
+
return {
|
|
3617
|
+
...first,
|
|
3618
|
+
id: `relation:aggregated:${scope}:${sha256NormalizedV1(key).slice(0, 32)}`,
|
|
3619
|
+
from,
|
|
3620
|
+
to,
|
|
3621
|
+
...detection === "static" && !first.discriminator ? {} : { discriminator: detection },
|
|
3622
|
+
evidence: boundedEvidence(mergedEvidence),
|
|
3623
|
+
metadata: {
|
|
3624
|
+
...first.metadata ?? {},
|
|
3625
|
+
aggregationScope: scope,
|
|
3626
|
+
aggregatedRelationCount: group.length,
|
|
3627
|
+
aggregatedEvidenceCount: mergedEvidence.length,
|
|
3628
|
+
...mergedEvidence.length > 64 ? { evidenceTruncated: true } : {}
|
|
3629
|
+
}
|
|
3630
|
+
};
|
|
3631
|
+
});
|
|
3632
|
+
};
|
|
3392
3633
|
var relationMatches = (observed, declared, resolveEntity2) => {
|
|
3393
3634
|
if (relationBase(observed, resolveEntity2) !== relationBase(declared, resolveEntity2)) return false;
|
|
3394
3635
|
return relationDetection(declared) === relationDetection(observed);
|
|
@@ -3399,7 +3640,7 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
3399
3640
|
status,
|
|
3400
3641
|
severity,
|
|
3401
3642
|
message,
|
|
3402
|
-
evidence:
|
|
3643
|
+
evidence: boundedEvidence(evidence2),
|
|
3403
3644
|
...entityIds?.length ? { entityIds: [...entityIds] } : {},
|
|
3404
3645
|
...relationIds?.length ? { relationIds: [...relationIds] } : {},
|
|
3405
3646
|
...remediation ? { remediation } : {}
|
|
@@ -3407,10 +3648,30 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
3407
3648
|
var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
3408
3649
|
const resolveEntity2 = entityResolver([observed, declared]);
|
|
3409
3650
|
const entities = entityById([observed, declared]);
|
|
3410
|
-
const
|
|
3411
|
-
const
|
|
3651
|
+
const scope = options.scope ?? "file";
|
|
3652
|
+
const packageByModule = packageLookup(scope, entities);
|
|
3653
|
+
const allDeclaredRelations = declared.relations.filter((relation) => relation.provenance === "declared");
|
|
3654
|
+
const observedRelations = aggregatedRelations(observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
|
|
3655
|
+
const declaredRelations = aggregatedRelations(declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
|
|
3412
3656
|
const requiredRelationKinds = options.requiredRelationKinds === void 0 ? void 0 : new Set(options.requiredRelationKinds);
|
|
3413
3657
|
const diagnostics = [];
|
|
3658
|
+
if (options.includeOrphanedDocuments) {
|
|
3659
|
+
const declaredDocumentIds = new Set(allDeclaredRelations.map((relation) => relation.from).filter((id) => id.startsWith("document:")));
|
|
3660
|
+
for (const document of observed.entities.filter((entity) => entity.kind === "document").sort((a, b) => a.id.localeCompare(b.id))) {
|
|
3661
|
+
if (declaredDocumentIds.has(document.id)) continue;
|
|
3662
|
+
diagnostics.push(reportDiagnostic(
|
|
3663
|
+
"DOCUMENTATION_ORPHANED",
|
|
3664
|
+
"undocumented",
|
|
3665
|
+
"info",
|
|
3666
|
+
"Markdown document has no Doc Bridge declaration linking it to observed knowledge.",
|
|
3667
|
+
document.evidence,
|
|
3668
|
+
document.id,
|
|
3669
|
+
[document.id],
|
|
3670
|
+
void 0,
|
|
3671
|
+
"Add a docbridge declaration or exclude this document from the documentation comparison scope."
|
|
3672
|
+
));
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3414
3675
|
for (const entity of declared.entities.filter((item) => isUnresolved(item.id, entities)).sort((a, b) => a.id.localeCompare(b.id))) {
|
|
3415
3676
|
diagnostics.push(reportDiagnostic(
|
|
3416
3677
|
"UNRESOLVED_ENTITY_REFERENCE",
|
|
@@ -3506,6 +3767,29 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
3506
3767
|
}
|
|
3507
3768
|
}
|
|
3508
3769
|
const sortedDiagnostics = [...diagnostics].sort((a, b) => a.id.localeCompare(b.id));
|
|
3770
|
+
const packageEntities = observed.entities.filter((entity) => entity.kind === "package");
|
|
3771
|
+
const packageStatus = { fresh: 0, stale: 0, missing: 0, unverified: 0 };
|
|
3772
|
+
const diagnosticStatusByEntity = /* @__PURE__ */ new Map();
|
|
3773
|
+
const relationEndpoints = new Map([...observedRelations, ...declaredRelations].map((relation) => [relation.id, [relation.from, relation.to]]));
|
|
3774
|
+
for (const diagnostic2 of sortedDiagnostics) for (const id of [...diagnostic2.entityIds ?? [], ...(diagnostic2.relationIds ?? []).flatMap((relationId) => relationEndpoints.get(relationId) ?? [])]) {
|
|
3775
|
+
const statuses = diagnosticStatusByEntity.get(id) ?? /* @__PURE__ */ new Set();
|
|
3776
|
+
statuses.add(diagnostic2.status);
|
|
3777
|
+
diagnosticStatusByEntity.set(id, statuses);
|
|
3778
|
+
}
|
|
3779
|
+
for (const packageEntity of packageEntities) {
|
|
3780
|
+
const docs = allDeclaredRelations.filter((relation) => relation.from.startsWith("document:") && relation.to === packageEntity.id);
|
|
3781
|
+
const statuses = diagnosticStatusByEntity.get(packageEntity.id) ?? /* @__PURE__ */ new Set();
|
|
3782
|
+
if (!docs.length) packageStatus.missing += 1;
|
|
3783
|
+
else if (statuses.has("conflict") || statuses.has("stale-or-unverified")) packageStatus.stale += 1;
|
|
3784
|
+
else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
|
|
3785
|
+
else packageStatus.fresh += 1;
|
|
3786
|
+
}
|
|
3787
|
+
const documentation = {
|
|
3788
|
+
documentCount: observed.entities.filter((entity) => entity.kind === "document").length,
|
|
3789
|
+
documentedDocumentCount: new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from)).size,
|
|
3790
|
+
packageCount: packageEntities.length,
|
|
3791
|
+
packageStatus
|
|
3792
|
+
};
|
|
3509
3793
|
const base = {
|
|
3510
3794
|
type: "reconciliation-report",
|
|
3511
3795
|
schemaVersion: 1,
|
|
@@ -3523,7 +3807,11 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
3523
3807
|
entityCount: observed.entities.length,
|
|
3524
3808
|
relationCount: observedRelations.length,
|
|
3525
3809
|
diagnosticCount: sortedDiagnostics.length,
|
|
3526
|
-
...options.
|
|
3810
|
+
...options.scope === void 0 ? {} : { scope: options.scope },
|
|
3811
|
+
...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
|
|
3812
|
+
diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
|
|
3813
|
+
diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
|
|
3814
|
+
documentation
|
|
3527
3815
|
}
|
|
3528
3816
|
};
|
|
3529
3817
|
return ReconciliationReportV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
@@ -3546,6 +3834,7 @@ var reportGroupKey = (entity, appLayout) => {
|
|
|
3546
3834
|
return packageFamily(entity);
|
|
3547
3835
|
};
|
|
3548
3836
|
var titleCase = (value) => value.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
3837
|
+
var levelChunkName = (scope, id) => `chunks/levels-${scope}-${sha256NormalizedV1(id).slice(0, 12)}.js`;
|
|
3549
3838
|
var buildReportViewModel = (snapshot) => {
|
|
3550
3839
|
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
3551
3840
|
const packages = snapshot.entities.filter((entity) => entity.kind === "package");
|
|
@@ -3559,6 +3848,7 @@ var buildReportViewModel = (snapshot) => {
|
|
|
3559
3848
|
const appLayout = packages.some((entity) => pathParts(entity.path)[0] === "apps");
|
|
3560
3849
|
const groups = /* @__PURE__ */ new Map();
|
|
3561
3850
|
const entityGroup = {};
|
|
3851
|
+
const groupModuleCounts = /* @__PURE__ */ new Map();
|
|
3562
3852
|
for (const entity of packages) {
|
|
3563
3853
|
const family = reportGroupKey(entity, appLayout);
|
|
3564
3854
|
const id = `group:${family}`;
|
|
@@ -3575,6 +3865,7 @@ var buildReportViewModel = (snapshot) => {
|
|
|
3575
3865
|
for (const [entityId2, packageId2] of packageForEntity) {
|
|
3576
3866
|
const groupId = entityGroup[packageId2];
|
|
3577
3867
|
if (groupId) entityGroup[entityId2] = groupId;
|
|
3868
|
+
if (groupId && entities.get(entityId2)?.kind === "module") groupModuleCounts.set(groupId, (groupModuleCounts.get(groupId) ?? 0) + 1);
|
|
3578
3869
|
}
|
|
3579
3870
|
if (externalIds.size) {
|
|
3580
3871
|
const externalGroupId = "group:external-dependencies";
|
|
@@ -3616,62 +3907,181 @@ var buildReportViewModel = (snapshot) => {
|
|
|
3616
3907
|
nodes: overviewNodes.sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id)),
|
|
3617
3908
|
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))
|
|
3618
3909
|
},
|
|
3619
|
-
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort() }])),
|
|
3620
|
-
entityGroup
|
|
3910
|
+
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort(), moduleCount: groupModuleCounts.get(id) ?? 0 }])),
|
|
3911
|
+
entityGroup,
|
|
3912
|
+
levelChunks: {
|
|
3913
|
+
default: "chunks/levels-packages.js",
|
|
3914
|
+
groups: Object.fromEntries([...groups.keys()].map((id) => [id, levelChunkName("group", id)])),
|
|
3915
|
+
packages: [...packages].sort((left, right) => left.id.localeCompare(right.id)).map((entity) => levelChunkName("package", entity.id))
|
|
3916
|
+
}
|
|
3621
3917
|
};
|
|
3622
3918
|
};
|
|
3623
3919
|
var escapeHtml = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
3624
3920
|
var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
3625
3921
|
var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
3626
3922
|
var errorPage = (message) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Doc Bridge report error</title><style>body{font:16px system-ui;margin:3rem;color:#311}main{max-width:60rem;margin:auto;border:1px solid #d99;padding:2rem;border-radius:8px;background:#fff8f8}code{white-space:pre-wrap}</style></head><body><main><h1>Doc Bridge report unavailable</h1><p>The saved snapshot/report could not be rendered.</p><code>${escapeHtml(message)}</code><p>Run <code>ak-docs check</code> to regenerate valid artifacts.</p></main></body></html>`;
|
|
3627
|
-
var reportData = (snapshot, report, includeSnippets) =>
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3923
|
+
var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
|
|
3924
|
+
const evidence2 = (items) => items.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item);
|
|
3925
|
+
const base = {
|
|
3926
|
+
project: snapshot.project,
|
|
3927
|
+
revision: snapshot.sourceRevision,
|
|
3928
|
+
revisionKind: snapshot.sourceRevisionKind,
|
|
3929
|
+
snapshotHash: snapshot.contentHash,
|
|
3930
|
+
reportHash: report.contentHash,
|
|
3931
|
+
configurationHash: snapshot.configurationHash,
|
|
3932
|
+
pipelineVersion: snapshot.pipelineVersion,
|
|
3933
|
+
analyzerVersions: snapshot.analyzerVersions,
|
|
3934
|
+
diagnosticCount: report.diagnostics.length,
|
|
3935
|
+
requiredRelationKinds: report.summary.requiredRelationKinds,
|
|
3936
|
+
diagnosticSummary: {
|
|
3937
|
+
errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
|
|
3938
|
+
warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
|
|
3939
|
+
undocumented: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "undocumented").length,
|
|
3940
|
+
drift: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "stale-or-unverified" || diagnostic2.status === "conflict").length
|
|
3941
|
+
},
|
|
3942
|
+
entities: snapshot.entities.map((entity, index) => ({
|
|
3943
|
+
id: entity.id,
|
|
3944
|
+
anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
|
|
3945
|
+
kind: entity.kind,
|
|
3946
|
+
name: entity.name,
|
|
3947
|
+
path: entity.path,
|
|
3948
|
+
provenance: entity.provenance,
|
|
3949
|
+
evidence: evidence2(entity.evidence)
|
|
3950
|
+
})),
|
|
3951
|
+
relations: snapshot.relations.map((relation) => ({
|
|
3952
|
+
id: relation.id,
|
|
3953
|
+
kind: relation.kind,
|
|
3954
|
+
from: relation.from,
|
|
3955
|
+
to: relation.to,
|
|
3956
|
+
provenance: relation.provenance,
|
|
3957
|
+
evidence: evidence2(relation.evidence)
|
|
3958
|
+
})),
|
|
3959
|
+
diagnostics: report.diagnostics.map((diagnostic2) => ({
|
|
3960
|
+
id: diagnostic2.id,
|
|
3961
|
+
code: diagnostic2.code,
|
|
3962
|
+
status: diagnostic2.status,
|
|
3963
|
+
severity: diagnostic2.severity,
|
|
3964
|
+
message: diagnostic2.message,
|
|
3965
|
+
entityIds: diagnostic2.entityIds ?? [],
|
|
3966
|
+
relationIds: diagnostic2.relationIds ?? [],
|
|
3967
|
+
remediation: diagnostic2.remediation,
|
|
3968
|
+
evidence: evidence2(diagnostic2.evidence)
|
|
3969
|
+
})),
|
|
3970
|
+
coverage: snapshot.coverage,
|
|
3971
|
+
view: buildReportViewModel(snapshot)
|
|
3972
|
+
};
|
|
3973
|
+
if (privacy !== "anonymized") return base;
|
|
3974
|
+
const pad = (value) => String(value).padStart(3, "0");
|
|
3975
|
+
const sortedEntities = [...base.entities].sort((left, right) => left.id.localeCompare(right.id));
|
|
3976
|
+
const entityOrdinals = /* @__PURE__ */ new Map();
|
|
3977
|
+
const entityIds = new Map(sortedEntities.map((entity) => {
|
|
3978
|
+
const next = (entityOrdinals.get(entity.kind) ?? 0) + 1;
|
|
3979
|
+
entityOrdinals.set(entity.kind, next);
|
|
3980
|
+
return [entity.id, `entity:${entity.kind}-${pad(next)}`];
|
|
3981
|
+
}));
|
|
3982
|
+
const entityNames = /* @__PURE__ */ new Map();
|
|
3983
|
+
const nameFor = (kind) => {
|
|
3984
|
+
const prefix = kind === "external" ? "dependency" : kind;
|
|
3985
|
+
const next = (entityNames.get(prefix) ?? 0) + 1;
|
|
3986
|
+
entityNames.set(prefix, next);
|
|
3987
|
+
return `${prefix}-${pad(next)}`;
|
|
3988
|
+
};
|
|
3989
|
+
const sortedRelations = [...base.relations].sort((left, right) => left.id.localeCompare(right.id));
|
|
3990
|
+
const relationIds = new Map(sortedRelations.map((relation, index) => [relation.id, `relation:${pad(index + 1)}`]));
|
|
3991
|
+
const sortedDiagnostics = [...base.diagnostics].sort((left, right) => left.id.localeCompare(right.id));
|
|
3992
|
+
const diagnosticIds = new Map(sortedDiagnostics.map((diagnostic2, index) => [diagnostic2.id, `finding:${pad(index + 1)}`]));
|
|
3993
|
+
const genericEvidence = (items) => items.map((item, index) => ({
|
|
3994
|
+
source: item.source,
|
|
3995
|
+
path: `evidence-${pad(index + 1)}`,
|
|
3996
|
+
...item.lineStart === void 0 ? {} : { lineStart: item.lineStart },
|
|
3997
|
+
...item.lineEnd === void 0 ? {} : { lineEnd: item.lineEnd }
|
|
3998
|
+
}));
|
|
3999
|
+
const groups = Object.entries(base.view.groups).sort(([left], [right]) => left.localeCompare(right));
|
|
4000
|
+
const groupIds = new Map(groups.map(([id], index) => [id, `group:${pad(index + 1)}`]));
|
|
4001
|
+
const groupNames = /* @__PURE__ */ new Map();
|
|
4002
|
+
const groupName = (kind) => {
|
|
4003
|
+
if (kind === "external") return "External dependencies";
|
|
4004
|
+
if (kind === "shared") return "Shared packages";
|
|
4005
|
+
if (kind === "workspace") return "Workspace";
|
|
4006
|
+
const next = (groupNames.get(kind) ?? 0) + 1;
|
|
4007
|
+
groupNames.set(kind, next);
|
|
4008
|
+
return `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${next}`;
|
|
4009
|
+
};
|
|
4010
|
+
const sanitizedGroups = Object.fromEntries(groups.map(([id, group]) => [groupIds.get(id), {
|
|
4011
|
+
kind: group.kind,
|
|
4012
|
+
name: groupName(group.kind),
|
|
4013
|
+
path: void 0,
|
|
4014
|
+
members: group.members.map((member) => entityIds.get(member)),
|
|
4015
|
+
moduleCount: group.moduleCount
|
|
4016
|
+
}]));
|
|
4017
|
+
const sanitizedOverviewNodes = base.view.overview.nodes.map((node) => ({
|
|
4018
|
+
id: groupIds.get(node.id),
|
|
4019
|
+
kind: node.kind,
|
|
4020
|
+
name: sanitizedGroups[groupIds.get(node.id)]?.name ?? "Scope",
|
|
4021
|
+
path: void 0,
|
|
4022
|
+
memberCount: node.memberCount
|
|
4023
|
+
}));
|
|
4024
|
+
const sanitizedView = {
|
|
4025
|
+
overview: {
|
|
4026
|
+
nodes: sanitizedOverviewNodes,
|
|
4027
|
+
edges: base.view.overview.edges.map((edge) => ({
|
|
4028
|
+
from: groupIds.get(edge.from),
|
|
4029
|
+
to: groupIds.get(edge.to),
|
|
4030
|
+
count: edge.count,
|
|
4031
|
+
kinds: edge.kinds,
|
|
4032
|
+
relationIds: edge.relationIds.map((id) => relationIds.get(id))
|
|
4033
|
+
}))
|
|
4034
|
+
},
|
|
4035
|
+
groups: sanitizedGroups,
|
|
4036
|
+
entityGroup: Object.fromEntries(Object.entries(base.view.entityGroup).map(([id, group]) => [entityIds.get(id), groupIds.get(group)])),
|
|
4037
|
+
levelChunks: {
|
|
4038
|
+
default: base.view.levelChunks.default,
|
|
4039
|
+
groups: Object.fromEntries([...groupIds.values()].map((id) => [id, levelChunkName("group", id)])),
|
|
4040
|
+
packages: [...entityIds.values()].filter((id) => id.startsWith("entity:package-")).sort().map((id) => levelChunkName("package", id))
|
|
4041
|
+
}
|
|
4042
|
+
};
|
|
4043
|
+
return {
|
|
4044
|
+
...base,
|
|
4045
|
+
project: { name: "Anonymized repository" },
|
|
4046
|
+
revision: "redacted",
|
|
4047
|
+
entities: base.entities.map((entity) => ({
|
|
4048
|
+
id: entityIds.get(entity.id),
|
|
4049
|
+
anchor: anchor("entity", entityIds.get(entity.id)),
|
|
4050
|
+
kind: entity.kind,
|
|
4051
|
+
name: nameFor(entity.kind),
|
|
4052
|
+
path: void 0,
|
|
4053
|
+
provenance: entity.provenance,
|
|
4054
|
+
evidence: genericEvidence(entity.evidence)
|
|
4055
|
+
})),
|
|
4056
|
+
relations: base.relations.map((relation) => ({
|
|
4057
|
+
id: relationIds.get(relation.id),
|
|
4058
|
+
kind: relation.kind,
|
|
4059
|
+
from: entityIds.get(relation.from),
|
|
4060
|
+
to: entityIds.get(relation.to),
|
|
4061
|
+
provenance: relation.provenance,
|
|
4062
|
+
evidence: genericEvidence(relation.evidence)
|
|
4063
|
+
})),
|
|
4064
|
+
diagnostics: base.diagnostics.map((diagnostic2) => ({
|
|
4065
|
+
id: diagnosticIds.get(diagnostic2.id),
|
|
4066
|
+
code: diagnostic2.code,
|
|
4067
|
+
status: diagnostic2.status,
|
|
4068
|
+
severity: diagnostic2.severity,
|
|
4069
|
+
message: `Finding ${diagnostic2.code}`,
|
|
4070
|
+
entityIds: diagnostic2.entityIds?.map((id) => entityIds.get(id)) ?? [],
|
|
4071
|
+
relationIds: diagnostic2.relationIds?.map((id) => relationIds.get(id)) ?? [],
|
|
4072
|
+
evidence: genericEvidence(diagnostic2.evidence)
|
|
4073
|
+
})),
|
|
4074
|
+
coverage: base.coverage.map((entry, index) => ({
|
|
4075
|
+
analyzer: entry.analyzer,
|
|
4076
|
+
analyzerVersion: entry.analyzerVersion,
|
|
4077
|
+
scope: `scope-${pad(index + 1)}`,
|
|
4078
|
+
status: entry.status,
|
|
4079
|
+
...entry.reason ? { reason: "Coverage boundary reported by analyzer." } : {},
|
|
4080
|
+
...entry.evidence ? { evidence: genericEvidence(entry.evidence) } : {}
|
|
4081
|
+
})),
|
|
4082
|
+
view: sanitizedView
|
|
4083
|
+
};
|
|
4084
|
+
};
|
|
3675
4085
|
var styles = String.raw`
|
|
3676
4086
|
: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)}
|
|
3677
4087
|
*{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}}
|
|
@@ -3686,6 +4096,7 @@ styles += String.raw`body[data-report-view="architecture"] #insights,body[data-r
|
|
|
3686
4096
|
styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
|
|
3687
4097
|
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}}`;
|
|
3688
4098
|
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}}`;
|
|
4099
|
+
styles += String.raw`.map-wrap.dense{overflow:auto}.map-wrap.dense #graph{width:2000px!important;max-width:none!important}`;
|
|
3689
4100
|
styles += String.raw`#lens-caption + p.subtle{display:none}`;
|
|
3690
4101
|
var script = String.raw`
|
|
3691
4102
|
const data=${"${DATA}"};
|
|
@@ -3693,22 +4104,26 @@ data.entities=data.entities||[];data.relations=data.relations||[];data.diagnosti
|
|
|
3693
4104
|
const policySuffix=data.requiredRelationKinds===undefined?"Policy: all observed relation kinds require declarations.":data.requiredRelationKinds.length?"Policy: declarations required for "+data.requiredRelationKinds.join(", ")+".":"Policy: missing relation declarations disabled; stale, conflicting, and unresolved declarations remain checked.";
|
|
3694
4105
|
const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
|
|
3695
4106
|
new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
|
|
3696
|
-
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],loadedChunks=new Set(),loadingChunks=new Map();
|
|
4107
|
+
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
|
|
3697
4108
|
const esc=(value)=>String(value??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll("\"",""").replaceAll("'","'");
|
|
3698
|
-
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();
|
|
4109
|
+
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;
|
|
4110
|
+
let activeLevelChunk="";
|
|
3699
4111
|
const groupById=new Map(Object.entries(data.view?.groups||{}));
|
|
4112
|
+
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]]));
|
|
3700
4113
|
const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
|
|
3701
4114
|
const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
|
|
3702
|
-
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=
|
|
3703
|
-
const
|
|
3704
|
-
const
|
|
4115
|
+
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingIndex.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=data.view?.diagnosticGroup?.[finding.id]||[];scopes.forEach((scope)=>{addFinding(groupFindingIndex,scope,finding);groupFindingCounts.set(scope,(groupFindingCounts.get(scope)||0)+1)})})};
|
|
4116
|
+
const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
|
|
4117
|
+
const isDetailChunk=(name)=>name.startsWith("chunks/details-");
|
|
4118
|
+
const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
|
|
4119
|
+
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};
|
|
3705
4120
|
const readHash=()=>new URLSearchParams(location.hash.slice(1));
|
|
3706
4121
|
const hash=readHash();
|
|
3707
4122
|
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)};
|
|
3708
4123
|
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{}};
|
|
3709
4124
|
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};
|
|
3710
4125
|
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};
|
|
3711
|
-
const groupFor=(id)=>data.view?.entityGroup?.[id]||id;
|
|
4126
|
+
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};
|
|
3712
4127
|
const packageNodes=()=>data.entities.filter((entity)=>entity.kind==="package");
|
|
3713
4128
|
const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",evidence:"Coverage"};
|
|
3714
4129
|
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};
|
|
@@ -3716,28 +4131,32 @@ const label=(entity)=>entity?.name||entity?.id||"Unknown";
|
|
|
3716
4131
|
const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
|
|
3717
4132
|
const renderBreadcrumbs=()=>{const breadcrumbs=ensureReportChrome(),items=[{label:"Repository",selected:"",level:"overview"}];if(state.selected){const group=groupById.get(state.selected),entity=byId.get(state.selected);if(group)items.push({label:group.name,selected:group.id,level:"package"});else if(entity){const packageId=packageOf(entity.id),packageEntity=byId.get(packageId),groupId=groupFor(packageId);if(groupById.has(groupId))items.push({label:groupById.get(groupId).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("")};
|
|
3718
4133
|
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};
|
|
3719
|
-
const relationHealth=(relation)=>{if(!relation)return "";const findings=
|
|
3720
|
-
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};
|
|
4134
|
+
const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
|
|
4135
|
+
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(groupFindingIndex.get(nodeId)||[]),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
3721
4136
|
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);
|
|
3722
|
-
const
|
|
4137
|
+
const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
|
|
4138
|
+
const relationInLens=(id)=>relationFindings(id).some(findingInLens);
|
|
3723
4139
|
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)};
|
|
4140
|
+
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};
|
|
4141
|
+
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};
|
|
3724
4142
|
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)};
|
|
3725
|
-
const
|
|
4143
|
+
const graphModelUncached=()=>{if(state.level==="overview"){const overviewNodes=data.view?.overview?.nodes||[],overviewNodeIds=new Set(overviewNodes.map((node)=>node.id)),base=(data.view?.overview?.edges||[]).filter((edge)=>overviewNodeIds.has(edge.from)&&overviewNodeIds.has(edge.to)),edges=state.lens==="architecture"?base:base.filter((edge)=>edge.relationIds.some(relationInLens)),visible=new Set(edges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?overviewNodes:overviewNodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:edges.map((edge)=>({...edge,health:edge.relationIds.some((id)=>relationHealth(relationById.get(id)))?"error":""}))}}const nodes=nodesFor(),ids=new Set(nodes.map((node)=>node.id)),aggregate=state.level==="package"&&(!state.selected||state.selected.startsWith("group:")),edges=new Map();data.relations.forEach((relation)=>{const essential=state.level==="package"?relation.kind==="depends-on":relation.kind==="imports"||relation.kind==="re-exports";if(relation.kind==="contains"|| (state.lens==="architecture"&&!essential)||(state.lens!=="architecture"&&!relationInLens(relation.id)))return;const from=aggregate?packageOf(relation.from):relation.from,to=aggregate?packageOf(relation.to):relation.to;if(from===to||!ids.has(from)||!ids.has(to))return;const key=from+"→"+to,current=edges.get(key)||{from,to,count:0,kinds:new Set(),relationIds:new Set(),health:""};current.count++;current.kinds.add(relation.kind);current.relationIds.add(relation.id);current.health=current.health==="error"||relationHealth(relation)==="error"?"error":current.health||relationHealth(relation);edges.set(key,current)});const visibleEdges=[...edges.values()].sort((a,b)=>b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to));const visible=new Set(visibleEdges.flatMap((edge)=>[edge.from,edge.to]));return{nodes:state.lens==="architecture"?nodes:nodes.filter((node)=>visible.has(node.id)||nodeIssues(node.id).some((finding)=>findingInLens(finding))),edges:visibleEdges}};
|
|
4144
|
+
const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
|
|
3726
4145
|
const renderInsights=()=>{const summary=data.diagnosticSummary||{},undocumented=summary.undocumented??data.diagnostics.filter((finding)=>finding.status==="undocumented").length,drift=summary.drift??data.diagnostics.filter((finding)=>finding.status==="stale-or-unverified"||finding.status==="conflict").length,unsupported=(data.coverage||[]).filter((entry)=>entry.status==="not-analyzed"||entry.status==="partial").length,model=graphModel(),degrees=degreeMap(model.edges),values=[...degrees.values()].sort((a,b)=>a-b),median=values.length?values[Math.floor(values.length/2)]:0,hot=[...degrees.values()].filter((value)=>value>=Math.max(4,median*2)).length,isolated=model.nodes.filter((node)=>!model.edges.some((edge)=>edge.from===node.id||edge.to===node.id)).length;document.querySelector("#insights").innerHTML=[["Documentation drift",undocumented+drift,"Relations or docs needing comparison.",""],["Unanalyzed scope",unsupported,"Coverage gaps are explicit.",""],["Connectivity hotspots",hot,"Heuristic: unusually connected nodes.","heuristic"],["Disconnected nodes",isolated,"Heuristic: no visible edge at this level.","heuristic"]].map(([title,count,copy,tag])=>"<article class=\"insight\"><h3>"+esc(title)+" <span class=\"tag "+tag+"\">"+(tag?"heuristic":"signal")+"</span></h3><p><strong>"+count+"</strong> · "+esc(copy)+"</p></article>").join("")};
|
|
3727
|
-
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}if(!data.diagnostics.length){dashboard.innerHTML="<p class=\"empty\">Open Findings or load the findings data to calculate insights.</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=[...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>"};
|
|
4146
|
+
const renderInsightsDashboard=()=>{let dashboard=document.querySelector("#report-dashboard");if(!dashboard){dashboard=document.createElement("section");dashboard.id="report-dashboard";dashboard.className="report-dashboard";document.querySelector(".filters")?.before(dashboard)}if(!data.diagnostics.length){dashboard.innerHTML="<p class=\"empty\">Open Findings or load the findings data to calculate insights.</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>"};
|
|
3728
4147
|
const mapState={scale:1,dragging:false,x:0,y:0};
|
|
3729
|
-
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"};
|
|
4148
|
+
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"};
|
|
3730
4149
|
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)})};
|
|
3731
4150
|
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()};
|
|
3732
4151
|
let nodeClickTimer;
|
|
3733
4152
|
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);
|
|
3734
|
-
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);
|
|
4153
|
+
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);
|
|
3735
4154
|
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);
|
|
3736
4155
|
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
3737
4156
|
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>":"")};
|
|
3738
|
-
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,[]]));
|
|
4157
|
+
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+").":".");};
|
|
3739
4158
|
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)};
|
|
3740
|
-
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"};
|
|
4159
|
+
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"})();
|
|
3741
4160
|
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))};
|
|
3742
4161
|
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>";
|
|
3743
4162
|
const renderFindings=()=>{let findings=data.diagnostics.filter(findingMatches);if(state.lens==="risks")findings=findings.filter((finding)=>finding.severity==="error"||finding.severity==="warn");if(state.lens==="evidence")findings=findings.filter((finding)=>finding.evidence.length);if(state.lens==="drift")findings=findings.filter((finding)=>finding.status!=="confirmed");const groups=findingGroups(findings),active=groups.find((group)=>group.key===state.findingGroup);if(!active){state.findingGroup=null;state.findingPage=0}syncHash();const selected=active||null,pageSize=40,pageCount=selected?Math.ceil(selected.findings.length/pageSize):0,page=Math.max(0,Math.min(state.findingPage,Math.max(0,pageCount-1))),start=page*pageSize;document.querySelector("#finding-count").textContent=findings.length+" findings · "+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> finding"+(group.findings.length===1?"":"s")+" · "+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+" total</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 findings match the current lens and filters.</p>"};
|
|
@@ -3745,11 +4164,11 @@ const renderCoverage=()=>{document.querySelector("#coverage-list").innerHTML=(da
|
|
|
3745
4164
|
const deferFindings=()=>{if(findingsLoaded())return;const count=data.diagnosticCount||0;document.querySelector("#finding-count").textContent=count+" findings · available on demand";document.querySelector("#findings").innerHTML="<div class=\"empty\"><p>Findings stay out of the first paint so large repositories remain responsive.</p><button id=\"load-findings\" class=\"tab\" type=\"button\">Load findings</button></div>"};
|
|
3746
4165
|
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>"}};
|
|
3747
4166
|
const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
|
|
3748
|
-
const levelsLoaded=()=>!
|
|
4167
|
+
const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
|
|
3749
4168
|
const setReportView=()=>{document.body.dataset.reportView={architecture:"architecture",drift:"insights",risks:"findings",evidence:"coverage"}[state.lens]||"architecture";ensureReportChrome()};
|
|
3750
|
-
const
|
|
3751
|
-
document.
|
|
3752
|
-
document.addEventListener("click",(event)=>{const
|
|
4169
|
+
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};
|
|
4170
|
+
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)};
|
|
4171
|
+
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);
|
|
3753
4172
|
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);
|
|
3754
4173
|
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);
|
|
3755
4174
|
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);
|
|
@@ -3775,14 +4194,14 @@ hydrate();render();keepMobileGraphReadable();ensurePolicyNote();
|
|
|
3775
4194
|
var render = (input, options) => {
|
|
3776
4195
|
const { snapshot, report } = input;
|
|
3777
4196
|
const includeSnippets = options.includeSnippets === true;
|
|
3778
|
-
const data = reportData(snapshot, report, includeSnippets);
|
|
4197
|
+
const data = reportData(snapshot, report, includeSnippets, options.privacy);
|
|
3779
4198
|
const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
|
|
3780
4199
|
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.";
|
|
3781
4200
|
const issueCount = report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error" || diagnostic2.severity === "warn").length;
|
|
3782
4201
|
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
3783
4202
|
const policyNote = report.summary.requiredRelationKinds === void 0 ? "Missing relation declarations are checked for every observed relation kind." : report.summary.requiredRelationKinds.length ? `Missing relation declarations are checked for: ${report.summary.requiredRelationKinds.join(", ")}.` : "Missing relation declarations are disabled by configuration; stale, conflicting, and unresolved declarations remain checked.";
|
|
3784
4203
|
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
3785
|
-
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(
|
|
4204
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(data.project.name)}</title><style>${styles}</style></head><body>${options.dataScripts ?? ""}<main class="shell"><header class="masthead"><div><div class="eyebrow">Doc Bridge / Knowledge report</div><h1>${escapeHtml(data.project.name)}</h1><p class="lede">A read-only architecture and documentation map. Start broad, then follow evidence to the exact entity, relation, or finding.</p></div><div class="run-meta"><span class="read-only">Read-only snapshot</span><strong>${escapeHtml(data.revision)}</strong><span>${escapeHtml(data.revisionKind)} \xB7 pipeline ${escapeHtml(data.pipelineVersion)}</span></div></header><section class="summary" aria-label="Snapshot summary"><div class="metric"><b>${snapshot.entities.length}</b><span>entities</span></div><div class="metric"><b>${snapshot.relations.length}</b><span>canonical relations</span></div><div class="metric ${issueCount ? "warn" : ""}"><b>${issueCount}</b><span>warn/error findings</span></div><div class="metric ${report.diagnostics.length ? "warn" : ""}"><b>${report.diagnostics.length}</b><span>total findings</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 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">${report.diagnostics.length} shown</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></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>`;
|
|
3786
4205
|
};
|
|
3787
4206
|
var parseReportInput = (input) => {
|
|
3788
4207
|
if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
|
|
@@ -3793,6 +4212,8 @@ var parseReportInput = (input) => {
|
|
|
3793
4212
|
return { snapshot, report };
|
|
3794
4213
|
};
|
|
3795
4214
|
var chunkScript = (payload) => `globalThis.__DOC_BRIDGE_DATA__=Object.assign(globalThis.__DOC_BRIDGE_DATA__||{},${embeddedJson(payload)});`;
|
|
4215
|
+
var levelChunkScript = (payload) => `globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__=${embeddedJson(payload)};`;
|
|
4216
|
+
var detailChunkScript = (payload) => `globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__=${embeddedJson(payload)};`;
|
|
3796
4217
|
var byteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
3797
4218
|
var renderOfflineReport = (input, options = {}) => {
|
|
3798
4219
|
try {
|
|
@@ -3804,23 +4225,103 @@ var renderOfflineReport = (input, options = {}) => {
|
|
|
3804
4225
|
var renderOfflineReportArtifact = (input, options = {}) => {
|
|
3805
4226
|
const parsed = parseReportInput(input);
|
|
3806
4227
|
const thresholdBytes = options.thresholdBytes ?? DEFAULT_LARGE_REPORT_THRESHOLD_BYTES;
|
|
3807
|
-
const renderOptions = { includeSnippets: options.includeSnippets === true, thresholdBytes };
|
|
4228
|
+
const renderOptions = { includeSnippets: options.includeSnippets === true, privacy: options.privacy ?? "private", thresholdBytes };
|
|
3808
4229
|
const singleFile = render(parsed, options);
|
|
3809
4230
|
if (byteLength(singleFile) <= thresholdBytes) {
|
|
3810
4231
|
const files2 = { "index.html": sha256NormalizedV1(singleFile) };
|
|
3811
|
-
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 };
|
|
4232
|
+
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 };
|
|
3812
4233
|
const manifest2 = JSON.stringify({ ...manifestPayload2, artifactHash: sha256NormalizedV1(manifestPayload2) }, null, 2);
|
|
3813
4234
|
return { mode: "single-file", indexHtml: singleFile, files: { "index.html": singleFile }, manifest: manifest2 };
|
|
3814
4235
|
}
|
|
3815
|
-
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true);
|
|
4236
|
+
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true, options.privacy);
|
|
4237
|
+
const entitiesById = new Map(data.entities.map((entity) => [entity.id, entity]));
|
|
4238
|
+
const packageEntities = data.entities.filter((entity) => entity.kind === "package");
|
|
4239
|
+
const packageIds = new Set(packageEntities.map((entity) => entity.id));
|
|
4240
|
+
const packageRelations = data.relations.filter((relation) => packageIds.has(relation.from) && packageIds.has(relation.to));
|
|
4241
|
+
const diagnosticGroup = Object.fromEntries(data.diagnostics.map((diagnostic2) => {
|
|
4242
|
+
const groups = /* @__PURE__ */ new Set();
|
|
4243
|
+
for (const entityId2 of diagnostic2.entityIds ?? []) {
|
|
4244
|
+
const groupId = data.view.entityGroup[entityId2];
|
|
4245
|
+
if (groupId) groups.add(groupId);
|
|
4246
|
+
}
|
|
4247
|
+
for (const relationId of diagnostic2.relationIds ?? []) {
|
|
4248
|
+
const relation = data.relations.find((candidate) => candidate.id === relationId);
|
|
4249
|
+
if (!relation) continue;
|
|
4250
|
+
for (const endpoint of [relation.from, relation.to]) {
|
|
4251
|
+
const groupId = data.view.entityGroup[endpoint];
|
|
4252
|
+
if (groupId) groups.add(groupId);
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
return [diagnostic2.id, [...groups].sort()];
|
|
4256
|
+
}));
|
|
4257
|
+
const diagnosticRelationFindings = /* @__PURE__ */ new Map();
|
|
4258
|
+
for (const diagnostic2 of data.diagnostics) {
|
|
4259
|
+
for (const relationId of diagnostic2.relationIds ?? []) {
|
|
4260
|
+
const findings = diagnosticRelationFindings.get(relationId) ?? [];
|
|
4261
|
+
findings.push({ status: diagnostic2.status, severity: diagnostic2.severity });
|
|
4262
|
+
diagnosticRelationFindings.set(relationId, findings);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
const containsRelations = data.relations.filter((relation) => relation.kind === "contains");
|
|
4266
|
+
const collectScope = (roots) => {
|
|
4267
|
+
const ids = new Set(roots);
|
|
4268
|
+
let changed = true;
|
|
4269
|
+
while (changed) {
|
|
4270
|
+
changed = false;
|
|
4271
|
+
for (const relation of containsRelations) {
|
|
4272
|
+
if (ids.has(relation.from) && !ids.has(relation.to)) {
|
|
4273
|
+
ids.add(relation.to);
|
|
4274
|
+
changed = true;
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
return ids;
|
|
4279
|
+
};
|
|
4280
|
+
const scopedPayload = (ids) => ({
|
|
4281
|
+
entities: data.entities.filter((entity) => ids.has(entity.id)),
|
|
4282
|
+
relations: data.relations.filter((relation) => ids.has(relation.from) && ids.has(relation.to))
|
|
4283
|
+
});
|
|
4284
|
+
const packagePayload = (ids) => ({
|
|
4285
|
+
entities: data.entities.filter((entity) => ids.has(entity.id) && entity.kind === "package"),
|
|
4286
|
+
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")
|
|
4287
|
+
});
|
|
4288
|
+
const compactPayload = (payload) => ({
|
|
4289
|
+
entities: payload.entities.map(({ evidence: _evidence, anchor: _anchor, provenance: _provenance, ...entity }) => entity),
|
|
4290
|
+
relations: payload.relations.map(({ evidence: _evidence, provenance: _provenance, ...relation }) => relation)
|
|
4291
|
+
});
|
|
4292
|
+
const levelFiles = {};
|
|
4293
|
+
const detailFiles = {};
|
|
4294
|
+
for (const [groupId, group] of Object.entries(data.view.groups)) {
|
|
4295
|
+
const ids = new Set(group.members.filter((id) => entitiesById.get(id)?.kind === "package"));
|
|
4296
|
+
const chunkName = data.view.levelChunks.groups[groupId];
|
|
4297
|
+
if (chunkName) levelFiles[chunkName] = levelChunkScript(packagePayload(ids));
|
|
4298
|
+
}
|
|
4299
|
+
const packageIdList = packageEntities.map((entity) => entity.id);
|
|
4300
|
+
const packageIdSet = new Set(packageIdList);
|
|
4301
|
+
levelFiles[data.view.levelChunks.default] = levelChunkScript(scopedPayload(packageIdSet));
|
|
4302
|
+
const sortedPackageIds = [...packageIds].sort();
|
|
4303
|
+
for (const [index, packageId2] of sortedPackageIds.entries()) {
|
|
4304
|
+
const chunkName = data.view.levelChunks.packages[index];
|
|
4305
|
+
const packageScope = scopedPayload(collectScope([packageId2]));
|
|
4306
|
+
if (chunkName) levelFiles[chunkName] = levelChunkScript(compactPayload(packageScope));
|
|
4307
|
+
const detailName = chunkName?.replace("chunks/levels-package-", "chunks/details-");
|
|
4308
|
+
if (detailName) detailFiles[detailName] = detailChunkScript(packageScope);
|
|
4309
|
+
}
|
|
4310
|
+
const overviewView = {
|
|
4311
|
+
...data.view,
|
|
4312
|
+
entityGroup: Object.fromEntries(Object.entries(data.view.entityGroup).filter(([id]) => data.entities.find((entity) => entity.id === id)?.kind === "package")),
|
|
4313
|
+
diagnosticGroup,
|
|
4314
|
+
diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
|
|
4315
|
+
};
|
|
3816
4316
|
const files = {
|
|
3817
|
-
"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,
|
|
3818
|
-
|
|
4317
|
+
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, diagnosticSummary: data.diagnosticSummary, requiredRelationKinds: data.requiredRelationKinds, entities: packageEntities, relations: packageRelations, view: overviewView }),
|
|
4318
|
+
...levelFiles,
|
|
4319
|
+
...detailFiles,
|
|
3819
4320
|
"chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
|
|
3820
4321
|
};
|
|
3821
|
-
const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_LAZY_CHUNKS__
|
|
4322
|
+
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>` });
|
|
3822
4323
|
const fileHashes = Object.fromEntries(Object.entries({ ...files, "index.html": indexHtml }).map(([file, content]) => [file, sha256NormalizedV1(content)]));
|
|
3823
|
-
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 };
|
|
4324
|
+
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 };
|
|
3824
4325
|
const manifest = JSON.stringify({ ...manifestPayload, artifactHash: sha256NormalizedV1(manifestPayload) }, null, 2);
|
|
3825
4326
|
return { mode: "directory", indexHtml, files: { ...files, "index.html": indexHtml }, manifest };
|
|
3826
4327
|
};
|
|
@@ -3991,6 +4492,22 @@ var appendTransition = (stateDir, transition2) => {
|
|
|
3991
4492
|
`, "utf8");
|
|
3992
4493
|
};
|
|
3993
4494
|
var transition = (run2, to, reason) => {
|
|
4495
|
+
const allowed = {
|
|
4496
|
+
created: ["created", "discovering", "failed", "cancelled"],
|
|
4497
|
+
discovering: ["discovering", "analyzed", "failed", "cancelled", "stale"],
|
|
4498
|
+
analyzed: ["analyzed", "compared", "failed", "cancelled", "stale"],
|
|
4499
|
+
compared: ["compared", "proposed", "failed", "cancelled", "stale"],
|
|
4500
|
+
"awaiting-agent": ["awaiting-agent", "proposed", "failed", "cancelled", "stale"],
|
|
4501
|
+
proposed: ["proposed", "validating", "delivered", "failed", "cancelled", "stale"],
|
|
4502
|
+
"awaiting-approval": ["awaiting-approval", "validating", "failed", "cancelled", "stale"],
|
|
4503
|
+
validating: ["validating", "delivered", "failed", "cancelled", "stale"],
|
|
4504
|
+
delivered: ["delivered", "stale", "failed", "cancelled"],
|
|
4505
|
+
failed: ["failed", "discovering", "analyzed", "compared", "proposed", "validating", "delivered", "cancelled"],
|
|
4506
|
+
cancelled: ["cancelled", "discovering", "analyzed", "compared", "proposed", "validating", "delivered"],
|
|
4507
|
+
stale: [],
|
|
4508
|
+
superseded: []
|
|
4509
|
+
};
|
|
4510
|
+
if (!allowed[run2.state].includes(to)) throw new Error(`Illegal workflow transition ${run2.state} -> ${to}.`);
|
|
3994
4511
|
const item = {
|
|
3995
4512
|
from: run2.state,
|
|
3996
4513
|
to,
|
|
@@ -4000,14 +4517,25 @@ var transition = (run2, to, reason) => {
|
|
|
4000
4517
|
return { ...run2, state: to, transitions: [...run2.transitions, item] };
|
|
4001
4518
|
};
|
|
4002
4519
|
var runId = () => `${Date.now()}-${process.pid}`;
|
|
4003
|
-
var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
4520
|
+
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" });
|
|
4004
4521
|
var stageArtifactPath = (stateDir, stage, inputHash) => join11(stateDir, "artifacts", `${stage}-${inputHash}.json`);
|
|
4005
4522
|
var readArtifact = (path) => JSON.parse(readFileSync11(path, "utf8"));
|
|
4006
|
-
var
|
|
4523
|
+
var readVerifiedArtifact = (path, stage, step) => {
|
|
4524
|
+
const artifact2 = readArtifact(path);
|
|
4525
|
+
if (artifact2.type !== "workflow-step-artifact" || artifact2.stage !== stage || artifact2.inputHash !== step.inputHash) throw new Error(`Invalid workflow artifact for stage "${stage}".`);
|
|
4526
|
+
if (sha256NormalizedV1(artifact2.value) !== artifact2.outputHash || artifact2.outputHash !== step.outputHash) throw new Error(`Workflow artifact hash mismatch for stage "${stage}".`);
|
|
4527
|
+
return artifact2;
|
|
4528
|
+
};
|
|
4529
|
+
var stepArtifactPath = (stateDir, step) => {
|
|
4530
|
+
const path = resolve10(stateDir, step.artifactRefs?.[0] ?? "");
|
|
4531
|
+
const pathRelativeToState = relative7(stateDir, path);
|
|
4532
|
+
if (pathRelativeToState.startsWith("..") || pathRelativeToState.startsWith("/")) throw new Error(`Workflow artifact escapes state directory for stage "${step.name}".`);
|
|
4533
|
+
return path;
|
|
4534
|
+
};
|
|
4007
4535
|
var stepOutput = (stateDir, run2, stage) => {
|
|
4008
4536
|
const step = run2.steps.find((item) => item.name === stage);
|
|
4009
4537
|
if (!step || step.status !== "completed" || !step.artifactRefs?.[0]) return null;
|
|
4010
|
-
return
|
|
4538
|
+
return readVerifiedArtifact(stepArtifactPath(stateDir, step), stage, step).value;
|
|
4011
4539
|
};
|
|
4012
4540
|
var acquireLock = (stateDir) => {
|
|
4013
4541
|
const lock = join11(stateDir, ".lock");
|
|
@@ -4034,7 +4562,7 @@ var loadManifest = (stateDir) => {
|
|
|
4034
4562
|
return WorkflowRunV1Schema.parse(JSON.parse(readFileSync11(path, "utf8")));
|
|
4035
4563
|
};
|
|
4036
4564
|
var baseRun = (options, stateDir, supersedes) => {
|
|
4037
|
-
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
4565
|
+
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
4038
4566
|
return WorkflowRunV1Schema.parse({
|
|
4039
4567
|
type: "workflow-run",
|
|
4040
4568
|
schemaVersion: 1,
|
|
@@ -4044,8 +4572,8 @@ var baseRun = (options, stateDir, supersedes) => {
|
|
|
4044
4572
|
sourceRevision: options.sourceRevision,
|
|
4045
4573
|
sourceRevisionKind: "content",
|
|
4046
4574
|
configurationHash: options.configurationHash,
|
|
4047
|
-
pipelineVersion: "1.0.0",
|
|
4048
|
-
analyzerVersions: { workflow: options.toolVersion ?? "1.0.0" },
|
|
4575
|
+
pipelineVersion: options.pipelineVersion ?? "1.0.0",
|
|
4576
|
+
analyzerVersions: { ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" },
|
|
4049
4577
|
runId: options.runId ?? runId(),
|
|
4050
4578
|
state: "created",
|
|
4051
4579
|
steps: WORKFLOW_STAGES.map((name) => ({ name, status: "pending", inputHash })),
|
|
@@ -4054,7 +4582,7 @@ var baseRun = (options, stateDir, supersedes) => {
|
|
|
4054
4582
|
});
|
|
4055
4583
|
};
|
|
4056
4584
|
var withHash = (run2) => WorkflowRunV1Schema.parse({ ...run2, contentHash: contentHashForArtifactV1(run2) });
|
|
4057
|
-
var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.
|
|
4585
|
+
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" });
|
|
4058
4586
|
var selectedStages = (stage) => stage && stage !== "all" ? [stage] : WORKFLOW_STAGES;
|
|
4059
4587
|
var runWorkflow = (options) => {
|
|
4060
4588
|
const root = resolve10(options.root);
|
|
@@ -4079,7 +4607,17 @@ var runWorkflow = (options) => {
|
|
|
4079
4607
|
if (!run2) throw new Error("Workflow manifest was not initialized.");
|
|
4080
4608
|
const firstSelectedStage = selectedStages(options.stage)[0];
|
|
4081
4609
|
const previousStageIndex = firstSelectedStage ? WORKFLOW_STAGES.indexOf(firstSelectedStage) - 1 : -1;
|
|
4082
|
-
let previousOutput =
|
|
4610
|
+
let previousOutput = null;
|
|
4611
|
+
if (previousStageIndex >= 0) {
|
|
4612
|
+
try {
|
|
4613
|
+
previousOutput = stepOutput(stateDir, run2, WORKFLOW_STAGES[previousStageIndex]);
|
|
4614
|
+
} catch (error) {
|
|
4615
|
+
run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
|
|
4616
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
4617
|
+
writeManifest(stateDir, run2);
|
|
4618
|
+
throw error;
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4083
4621
|
const reusedStages = [];
|
|
4084
4622
|
for (const stage of selectedStages(options.stage)) {
|
|
4085
4623
|
const input = options.inputs?.[stage] ?? previousOutput;
|
|
@@ -4087,10 +4625,23 @@ var runWorkflow = (options) => {
|
|
|
4087
4625
|
const existing = run2.steps.find((step) => step.name === stage);
|
|
4088
4626
|
const artifactPath = existing?.artifactRefs?.[0] ? resolve10(stateDir, existing.artifactRefs[0]) : stageArtifactPath(stateDir, stage, inputHash);
|
|
4089
4627
|
if (existing?.status === "completed" && existing.inputHash === inputHash && existing.outputHash && existsSync12(artifactPath)) {
|
|
4090
|
-
|
|
4628
|
+
try {
|
|
4629
|
+
previousOutput = readVerifiedArtifact(artifactPath, stage, existing).value;
|
|
4630
|
+
} catch (error) {
|
|
4631
|
+
run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
|
|
4632
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
4633
|
+
writeManifest(stateDir, run2);
|
|
4634
|
+
throw error;
|
|
4635
|
+
}
|
|
4091
4636
|
reusedStages.push(stage);
|
|
4092
4637
|
continue;
|
|
4093
4638
|
}
|
|
4639
|
+
if (options.shouldCancel?.()) {
|
|
4640
|
+
run2 = withHash(transition(run2, "cancelled", `Workflow cancellation requested before stage "${stage}".`));
|
|
4641
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
4642
|
+
writeManifest(stateDir, run2);
|
|
4643
|
+
return { run: run2, stateDir, reusedStages };
|
|
4644
|
+
}
|
|
4094
4645
|
const handler = options.handlers[stage];
|
|
4095
4646
|
if (!handler) throw new Error(`No handler configured for workflow stage "${stage}".`);
|
|
4096
4647
|
run2 = withHash(transition(run2, stageState[stage]));
|
|
@@ -6306,6 +6857,174 @@ ${auth.out}`
|
|
|
6306
6857
|
};
|
|
6307
6858
|
};
|
|
6308
6859
|
|
|
6860
|
+
// src/plugins/contract.ts
|
|
6861
|
+
import { z as z9 } from "zod";
|
|
6862
|
+
var ANALYZER_PLUGIN_CONTRACT_VERSION = 1;
|
|
6863
|
+
var AnalyzerPluginManifestSchema = z9.object({
|
|
6864
|
+
id: z9.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
|
|
6865
|
+
version: z9.string().min(1).max(64),
|
|
6866
|
+
languages: z9.array(z9.string().min(1).max(64)).min(1).max(32),
|
|
6867
|
+
frameworks: z9.array(z9.string().min(1).max(128)).max(32).default([]),
|
|
6868
|
+
capabilities: z9.array(z9.string().min(1).max(128)).min(1).max(32),
|
|
6869
|
+
knowledgeSchemaVersion: z9.literal(1),
|
|
6870
|
+
compatibility: z9.object({ pipelineMajor: z9.number().int().nonnegative() }).strict(),
|
|
6871
|
+
unsupportedConstructs: z9.array(z9.string().min(1).max(256)).max(128).default([]),
|
|
6872
|
+
resourceLimits: z9.object({ maxFiles: z9.number().int().positive().optional(), maxBytes: z9.number().int().positive().optional() }).strict().default({})
|
|
6873
|
+
}).strict();
|
|
6874
|
+
var AnalyzerPluginOutputSchema = z9.object({
|
|
6875
|
+
entities: z9.array(EntitySchema).max(5e4).default([]),
|
|
6876
|
+
relations: z9.array(RelationSchema).max(1e5).default([]),
|
|
6877
|
+
coverage: z9.array(CoverageSchema).max(1e3).default([]),
|
|
6878
|
+
diagnostics: z9.array(DiagnosticSchema).max(1e5).default([])
|
|
6879
|
+
}).strict();
|
|
6880
|
+
var pipelineMajor = (version) => Number.parseInt(version.split(".")[0] ?? "", 10);
|
|
6881
|
+
var createAnalyzerRegistry = (options = {}) => {
|
|
6882
|
+
const plugins = /* @__PURE__ */ new Map();
|
|
6883
|
+
const pipeline = pipelineMajor(options.pipelineVersion ?? "1.0.0");
|
|
6884
|
+
return {
|
|
6885
|
+
register(plugin) {
|
|
6886
|
+
const manifest = AnalyzerPluginManifestSchema.parse(plugin.manifest);
|
|
6887
|
+
if (manifest.compatibility.pipelineMajor !== pipeline) throw new Error(`Analyzer plugin "${manifest.id}" requires pipeline major ${manifest.compatibility.pipelineMajor}; current pipeline is ${pipeline}.`);
|
|
6888
|
+
if (plugins.has(manifest.id)) throw new Error(`Analyzer plugin "${manifest.id}" is already registered.`);
|
|
6889
|
+
if (options.maxPlugins !== void 0 && plugins.size >= options.maxPlugins) throw new Error(`Analyzer plugin limit ${options.maxPlugins} exceeded.`);
|
|
6890
|
+
plugins.set(manifest.id, { ...plugin, manifest });
|
|
6891
|
+
},
|
|
6892
|
+
list() {
|
|
6893
|
+
return [...plugins.values()].map((plugin) => plugin.manifest).sort((a, b) => a.id.localeCompare(b.id));
|
|
6894
|
+
},
|
|
6895
|
+
async analyze(id, input) {
|
|
6896
|
+
const plugin = plugins.get(id);
|
|
6897
|
+
if (!plugin) throw new Error(`Analyzer plugin "${id}" is not registered.`);
|
|
6898
|
+
const { maxFiles, maxBytes } = plugin.manifest.resourceLimits;
|
|
6899
|
+
const bytes = input.files.reduce((total, file) => total + file.bytes, 0);
|
|
6900
|
+
if (maxFiles !== void 0 && input.files.length > maxFiles) throw new Error(`Analyzer plugin "${id}" file limit ${maxFiles} exceeded.`);
|
|
6901
|
+
if (maxBytes !== void 0 && bytes > maxBytes) throw new Error(`Analyzer plugin "${id}" byte limit ${maxBytes} exceeded.`);
|
|
6902
|
+
try {
|
|
6903
|
+
const output = AnalyzerPluginOutputSchema.parse(await plugin.analyze(input));
|
|
6904
|
+
return {
|
|
6905
|
+
...output,
|
|
6906
|
+
coverage: output.coverage.map((entry) => ({ ...entry, analyzer: plugin.manifest.id, analyzerVersion: plugin.manifest.version }))
|
|
6907
|
+
};
|
|
6908
|
+
} catch (error) {
|
|
6909
|
+
return {
|
|
6910
|
+
entities: [],
|
|
6911
|
+
relations: [],
|
|
6912
|
+
diagnostics: [],
|
|
6913
|
+
coverage: [{ analyzer: plugin.manifest.id, analyzerVersion: plugin.manifest.version, scope: "plugin", status: "not-analyzed", reason: `Plugin failed safely: ${error instanceof Error ? error.message : String(error)}` }]
|
|
6914
|
+
};
|
|
6915
|
+
}
|
|
6916
|
+
}
|
|
6917
|
+
};
|
|
6918
|
+
};
|
|
6919
|
+
|
|
6920
|
+
// src/metrics/benchmark.ts
|
|
6921
|
+
import { z as z10 } from "zod";
|
|
6922
|
+
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
6923
|
+
var stringList = z10.array(z10.string().min(1).max(512)).max(1e5);
|
|
6924
|
+
var benchmarkSets = z10.object({
|
|
6925
|
+
entities: stringList.default([]),
|
|
6926
|
+
relations: stringList.default([]),
|
|
6927
|
+
findings: stringList.default([])
|
|
6928
|
+
}).strict();
|
|
6929
|
+
var BenchmarkFixtureV1Schema = z10.object({
|
|
6930
|
+
schemaVersion: z10.literal(BENCHMARK_SCHEMA_VERSION),
|
|
6931
|
+
supported: benchmarkSets,
|
|
6932
|
+
excluded: benchmarkSets.optional()
|
|
6933
|
+
}).strict();
|
|
6934
|
+
var unique = (values) => new Set(values);
|
|
6935
|
+
var ratio = (numerator, denominator) => denominator === 0 ? 1 : numerator / denominator;
|
|
6936
|
+
var setMetrics = (actualValues, expectedValues, excludedValues) => {
|
|
6937
|
+
const excluded = unique(excludedValues);
|
|
6938
|
+
const activeActualValues = actualValues.filter((value) => !excluded.has(value));
|
|
6939
|
+
const actual = unique(activeActualValues);
|
|
6940
|
+
const expected = unique(expectedValues.filter((value) => !excluded.has(value)));
|
|
6941
|
+
const truePositives = [...actual].filter((value) => expected.has(value)).length;
|
|
6942
|
+
const falsePositives = actual.size - truePositives;
|
|
6943
|
+
const falseNegatives = expected.size - truePositives;
|
|
6944
|
+
return {
|
|
6945
|
+
truePositives,
|
|
6946
|
+
falsePositives,
|
|
6947
|
+
falseNegatives,
|
|
6948
|
+
precision: ratio(truePositives, actual.size),
|
|
6949
|
+
recall: ratio(truePositives, expected.size),
|
|
6950
|
+
duplicateCount: activeActualValues.length - actual.size
|
|
6951
|
+
};
|
|
6952
|
+
};
|
|
6953
|
+
var benchmarkFixture = (fixture) => BenchmarkFixtureV1Schema.parse(fixture);
|
|
6954
|
+
var measureBenchmark = (observation, fixture, thresholds = {}) => {
|
|
6955
|
+
const excluded = fixture.excluded ?? { entities: [], relations: [], findings: [] };
|
|
6956
|
+
const quality = {
|
|
6957
|
+
entities: setMetrics(observation.entities, fixture.supported.entities, excluded.entities),
|
|
6958
|
+
relations: setMetrics(observation.relations, fixture.supported.relations, excluded.relations),
|
|
6959
|
+
findings: setMetrics(observation.findings, fixture.supported.findings, excluded.findings)
|
|
6960
|
+
};
|
|
6961
|
+
const actualEntities = unique(observation.entities);
|
|
6962
|
+
const evidenced = unique(observation.evidenced ?? []);
|
|
6963
|
+
const evidenceRatio = ratio([...actualEntities].filter((id) => evidenced.has(id)).length, actualEntities.size);
|
|
6964
|
+
const findingDensity = ratio(unique(observation.findings).size, actualEntities.size);
|
|
6965
|
+
const precisionThreshold = thresholds.precision ?? 0.95;
|
|
6966
|
+
const recallThreshold = thresholds.recall ?? 1;
|
|
6967
|
+
const regressions = [];
|
|
6968
|
+
for (const [name, metrics] of Object.entries(quality)) {
|
|
6969
|
+
if (metrics.precision < precisionThreshold) regressions.push(`${name}.precision below ${precisionThreshold}`);
|
|
6970
|
+
if (metrics.recall < recallThreshold) regressions.push(`${name}.recall below ${recallThreshold}`);
|
|
6971
|
+
}
|
|
6972
|
+
return {
|
|
6973
|
+
schemaVersion: BENCHMARK_SCHEMA_VERSION,
|
|
6974
|
+
quality,
|
|
6975
|
+
evidenceRatio,
|
|
6976
|
+
findingDensity,
|
|
6977
|
+
findingCategoryDistribution: Object.fromEntries(Object.entries(observation.findingCategories ?? {}).sort(([a], [b]) => a.localeCompare(b))),
|
|
6978
|
+
excludedCaseCount: excluded.entities.length + excluded.relations.length + excluded.findings.length,
|
|
6979
|
+
excludedCaseIds: { entities: excluded.entities.length, relations: excluded.relations.length, findings: excluded.findings.length },
|
|
6980
|
+
thresholds: { precision: precisionThreshold, recall: recallThreshold },
|
|
6981
|
+
regressions
|
|
6982
|
+
};
|
|
6983
|
+
};
|
|
6984
|
+
var compareBenchmarkSnapshots = (previous, current) => {
|
|
6985
|
+
const ids = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(current)]);
|
|
6986
|
+
let added = 0;
|
|
6987
|
+
let removed = 0;
|
|
6988
|
+
let unchanged = 0;
|
|
6989
|
+
let reclassified = 0;
|
|
6990
|
+
let unchangedEvidence = 0;
|
|
6991
|
+
for (const id of ids) {
|
|
6992
|
+
if (!(id in previous)) {
|
|
6993
|
+
added += 1;
|
|
6994
|
+
continue;
|
|
6995
|
+
}
|
|
6996
|
+
if (!(id in current)) {
|
|
6997
|
+
removed += 1;
|
|
6998
|
+
continue;
|
|
6999
|
+
}
|
|
7000
|
+
if (previous[id] === current[id]) unchanged += 1;
|
|
7001
|
+
else reclassified += 1;
|
|
7002
|
+
if (previous[id] === current[id]) unchangedEvidence += 1;
|
|
7003
|
+
}
|
|
7004
|
+
return { added, removed, unchanged, reclassified, unchangedEvidence };
|
|
7005
|
+
};
|
|
7006
|
+
var percentile95 = (values) => {
|
|
7007
|
+
if (!values.length) return 0;
|
|
7008
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
7009
|
+
return sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * 0.95) - 1)] ?? 0;
|
|
7010
|
+
};
|
|
7011
|
+
var measureAgentEfficiency = (observation) => ({
|
|
7012
|
+
hitRate: ratio(observation.hits, observation.queries),
|
|
7013
|
+
latencyP95Ms: percentile95(observation.latencyMs),
|
|
7014
|
+
responseBytesP95: percentile95(observation.responseBytes),
|
|
7015
|
+
estimatedTokensP95: percentile95(observation.estimatedTokens),
|
|
7016
|
+
corpusBytes: observation.corpusBytes,
|
|
7017
|
+
contextReduction: observation.corpusBytes > 0 ? 1 - percentile95(observation.responseBytes) / observation.corpusBytes : 0
|
|
7018
|
+
});
|
|
7019
|
+
var formatBenchmarkText = (result) => [
|
|
7020
|
+
`Precision: entities ${result.quality.entities.precision.toFixed(3)}, relations ${result.quality.relations.precision.toFixed(3)}, findings ${result.quality.findings.precision.toFixed(3)}`,
|
|
7021
|
+
`Recall: entities ${result.quality.entities.recall.toFixed(3)}, relations ${result.quality.relations.recall.toFixed(3)}, findings ${result.quality.findings.recall.toFixed(3)}`,
|
|
7022
|
+
`Evidence ratio: ${result.evidenceRatio.toFixed(3)}`,
|
|
7023
|
+
`Finding density: ${result.findingDensity.toFixed(3)}`,
|
|
7024
|
+
`Excluded cases: ${result.excludedCaseCount}`,
|
|
7025
|
+
`Regressions: ${result.regressions.length ? result.regressions.join("; ") : "none"}`
|
|
7026
|
+
].join("\n");
|
|
7027
|
+
|
|
6309
7028
|
// src/federation/llms.ts
|
|
6310
7029
|
import { existsSync as existsSync19, readFileSync as readFileSync18 } from "fs";
|
|
6311
7030
|
import { resolve as resolve16 } from "path";
|
|
@@ -6824,12 +7543,17 @@ var docBridgePatternPayload = () => ({
|
|
|
6824
7543
|
body: docBridgePatternMarkdown()
|
|
6825
7544
|
});
|
|
6826
7545
|
export {
|
|
7546
|
+
ANALYZER_PLUGIN_CONTRACT_VERSION,
|
|
6827
7547
|
AffectedFileSchema,
|
|
6828
7548
|
AgentHandoffLegacySchema,
|
|
6829
7549
|
AgentHandoffV1JsonSchema,
|
|
6830
7550
|
AgentHandoffV1Schema,
|
|
6831
7551
|
AgentProposalV1Schema,
|
|
6832
7552
|
AgentSearchV1Schema,
|
|
7553
|
+
AnalyzerPluginManifestSchema,
|
|
7554
|
+
AnalyzerPluginOutputSchema,
|
|
7555
|
+
BENCHMARK_SCHEMA_VERSION,
|
|
7556
|
+
BenchmarkFixtureV1Schema,
|
|
6833
7557
|
ConfigNotFoundError,
|
|
6834
7558
|
CoverageSchema,
|
|
6835
7559
|
DEFAULT_LARGE_REPORT_THRESHOLD_BYTES,
|
|
@@ -6874,6 +7598,7 @@ export {
|
|
|
6874
7598
|
ProvenanceSchema,
|
|
6875
7599
|
ReconciliationReportV1Schema,
|
|
6876
7600
|
RelationSchema,
|
|
7601
|
+
ReportConfigSchema,
|
|
6877
7602
|
RepositorySafetyConfigSchema,
|
|
6878
7603
|
RuleIdSchema,
|
|
6879
7604
|
RuleSeveritySchema,
|
|
@@ -6888,14 +7613,17 @@ export {
|
|
|
6888
7613
|
applyDocumentationDeclarations,
|
|
6889
7614
|
applyFixProposal,
|
|
6890
7615
|
approveFixProposal,
|
|
7616
|
+
benchmarkFixture,
|
|
6891
7617
|
buildDocBridgeIndex,
|
|
6892
7618
|
buildLookup,
|
|
6893
7619
|
canonicalJsonV1,
|
|
6894
7620
|
chunksFromMarkdown,
|
|
6895
7621
|
classifyMemoryCandidates,
|
|
6896
7622
|
collectPackages,
|
|
7623
|
+
compareBenchmarkSnapshots,
|
|
6897
7624
|
containedPath,
|
|
6898
7625
|
contentHashForArtifactV1,
|
|
7626
|
+
createAnalyzerRegistry,
|
|
6899
7627
|
createArtifactNormalizationProposal,
|
|
6900
7628
|
createDocBridgeRag,
|
|
6901
7629
|
createDocBridgeRetriever,
|
|
@@ -6910,6 +7638,7 @@ export {
|
|
|
6910
7638
|
doctorBadgeMetrics,
|
|
6911
7639
|
draftMemoryPromotion,
|
|
6912
7640
|
evaluateRules,
|
|
7641
|
+
formatBenchmarkText,
|
|
6913
7642
|
formatDoctorBadgeJson,
|
|
6914
7643
|
formatDoctorBadgeMarkdown,
|
|
6915
7644
|
formatDoctorText,
|
|
@@ -6931,6 +7660,8 @@ export {
|
|
|
6931
7660
|
loadWorkflowManifest,
|
|
6932
7661
|
loadWorkflowStepOutput,
|
|
6933
7662
|
mcpSnippet,
|
|
7663
|
+
measureAgentEfficiency,
|
|
7664
|
+
measureBenchmark,
|
|
6934
7665
|
normalizeAgentHandoff,
|
|
6935
7666
|
parseAgentHandoff,
|
|
6936
7667
|
parseAgentProposal,
|