@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/cli/program.js
CHANGED
|
@@ -474,8 +474,32 @@ var RulesConfigSchema = z.object({
|
|
|
474
474
|
warningThresholds: z.record(RuleIdSchema, z.number().int().min(1).max(1e5)).optional()
|
|
475
475
|
}).strict();
|
|
476
476
|
var ReconciliationConfigSchema = z.object({
|
|
477
|
+
/** Semantic comparison level. Raw discovery always keeps file-level relations. */
|
|
478
|
+
scope: z.enum(["file", "module", "package"]).optional(),
|
|
477
479
|
/** Relation kinds that must have documentation declarations. Omit to require all observed kinds; [] disables this signal. */
|
|
478
|
-
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional()
|
|
480
|
+
requiredRelationKinds: z.array(z.string().min(1).max(128)).max(128).optional(),
|
|
481
|
+
includeOrphanedDocuments: z.boolean().optional()
|
|
482
|
+
}).strict();
|
|
483
|
+
var AnalysisConfigSchema = z.object({
|
|
484
|
+
plugins: z.array(
|
|
485
|
+
z.object({
|
|
486
|
+
id: z.string().regex(/^[a-z][a-z0-9-]*$/).max(128),
|
|
487
|
+
enabled: z.boolean().optional(),
|
|
488
|
+
order: z.number().int().nonnegative().optional(),
|
|
489
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
490
|
+
reason: z.string().min(1).max(1024).optional()
|
|
491
|
+
}).strict()
|
|
492
|
+
).max(128).optional(),
|
|
493
|
+
jsTs: z.object({
|
|
494
|
+
runtimeWiringMethods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).max(64).optional(),
|
|
495
|
+
runtimeWiringAdapters: z.array(
|
|
496
|
+
z.object({
|
|
497
|
+
id: z.string().min(1).max(128),
|
|
498
|
+
methods: z.array(z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]*$/).max(64)).min(1).max(64)
|
|
499
|
+
}).strict()
|
|
500
|
+
).max(32).optional(),
|
|
501
|
+
includeTestRuntimeWiring: z.boolean().optional()
|
|
502
|
+
}).strict().optional()
|
|
479
503
|
}).strict();
|
|
480
504
|
var WorkflowConfigSchema = z.object({
|
|
481
505
|
stateDir: z.string().min(1).max(512).optional()
|
|
@@ -488,6 +512,10 @@ var RepositorySafetyConfigSchema = z.object({
|
|
|
488
512
|
maxMemoryMb: z.number().int().positive().max(1048576).optional(),
|
|
489
513
|
redactSecrets: z.boolean().optional()
|
|
490
514
|
}).strict();
|
|
515
|
+
var ReportConfigSchema = z.object({
|
|
516
|
+
/** Public report privacy mode. Private is the default; anonymized preserves topology without project identity. */
|
|
517
|
+
privacy: z.enum(["private", "anonymized"]).optional()
|
|
518
|
+
}).strict();
|
|
491
519
|
var SurfacesConfigSchema = z.object({
|
|
492
520
|
cli: z.object({
|
|
493
521
|
bin: z.string().min(1).max(64).optional(),
|
|
@@ -559,7 +587,12 @@ var IntelligenceConfigSchema = z.object({
|
|
|
559
587
|
enabled: z.boolean().optional(),
|
|
560
588
|
agentId: z.string().min(1).max(256).optional(),
|
|
561
589
|
agentRoot: z.string().min(1).max(512).optional(),
|
|
562
|
-
runnerModule: z.string().min(1).max(512).optional()
|
|
590
|
+
runnerModule: z.string().min(1).max(512).optional(),
|
|
591
|
+
deterministic: z.boolean().optional(),
|
|
592
|
+
timeoutMs: z.number().int().positive().max(6e5).optional(),
|
|
593
|
+
maxTokens: z.number().int().positive().max(1e6).optional(),
|
|
594
|
+
maxResponseBytes: z.number().int().positive().max(1e7).optional(),
|
|
595
|
+
maxConcurrency: z.number().int().positive().max(64).optional()
|
|
563
596
|
}).strict().optional()
|
|
564
597
|
}).strict();
|
|
565
598
|
var FederationConfigSchema = z.object({
|
|
@@ -651,9 +684,11 @@ var DocBridgeConfigV1Schema = z.object({
|
|
|
651
684
|
routing: RoutingConfigSchema.optional(),
|
|
652
685
|
gates: GatesConfigSchema.optional(),
|
|
653
686
|
reconciliation: ReconciliationConfigSchema.optional(),
|
|
687
|
+
analysis: AnalysisConfigSchema.optional(),
|
|
654
688
|
rules: RulesConfigSchema.optional(),
|
|
655
689
|
workflow: WorkflowConfigSchema.optional(),
|
|
656
690
|
safety: RepositorySafetyConfigSchema.optional(),
|
|
691
|
+
report: ReportConfigSchema.optional(),
|
|
657
692
|
surfaces: SurfacesConfigSchema.optional(),
|
|
658
693
|
intelligence: IntelligenceConfigSchema.optional(),
|
|
659
694
|
federation: FederationConfigSchema.optional(),
|
|
@@ -1448,7 +1483,7 @@ var buildLookup = (config, packages, corpus, indexOutFile, humanDocs = {}, root
|
|
|
1448
1483
|
};
|
|
1449
1484
|
|
|
1450
1485
|
// src/version.ts
|
|
1451
|
-
var PACKAGE_VERSION = "1.
|
|
1486
|
+
var PACKAGE_VERSION = "1.7.44";
|
|
1452
1487
|
|
|
1453
1488
|
// src/index-builder/capabilities.ts
|
|
1454
1489
|
var renderCapabilitiesJson = (config, index, paths) => {
|
|
@@ -1505,9 +1540,9 @@ var contentHashForArtifactV1 = (artifact2) => {
|
|
|
1505
1540
|
|
|
1506
1541
|
// src/index-builder/llms-txt.ts
|
|
1507
1542
|
var routePath = (path, pathPrefix) => {
|
|
1508
|
-
const
|
|
1543
|
+
const normalizedPath2 = path.replaceAll("\\", "/").replace(/\.(?:md|mdx)$/, "");
|
|
1509
1544
|
const normalizedPrefix = pathPrefix?.replaceAll("\\", "/").replace(/\/$/, "");
|
|
1510
|
-
const relativePath3 = normalizedPrefix &&
|
|
1545
|
+
const relativePath3 = normalizedPrefix && normalizedPath2.startsWith(`${normalizedPrefix}/`) ? normalizedPath2.slice(normalizedPrefix.length + 1) : normalizedPath2;
|
|
1511
1546
|
return relativePath3.replace(/^\/+/, "");
|
|
1512
1547
|
};
|
|
1513
1548
|
var knowledgeUrl = (path, options = {}) => {
|
|
@@ -2556,9 +2591,10 @@ var EvidenceSchema2 = z3.object({
|
|
|
2556
2591
|
});
|
|
2557
2592
|
}
|
|
2558
2593
|
});
|
|
2559
|
-
var CoverageStatusSchema = z3.enum(["complete", "partial", "not-analyzed"]);
|
|
2594
|
+
var CoverageStatusSchema = z3.enum(["complete", "partial", "not-analyzed", "not-applicable"]);
|
|
2560
2595
|
var CoverageSchema = z3.object({
|
|
2561
2596
|
analyzer: boundedString(128),
|
|
2597
|
+
analyzerVersion: boundedString(64).optional(),
|
|
2562
2598
|
scope: boundedString(512),
|
|
2563
2599
|
status: CoverageStatusSchema,
|
|
2564
2600
|
reason: z3.string().max(1024).optional(),
|
|
@@ -2641,7 +2677,21 @@ var ReconciliationReportV1Schema = z3.object({
|
|
|
2641
2677
|
entityCount: z3.number().int().nonnegative(),
|
|
2642
2678
|
relationCount: z3.number().int().nonnegative(),
|
|
2643
2679
|
diagnosticCount: z3.number().int().nonnegative(),
|
|
2644
|
-
|
|
2680
|
+
scope: z3.enum(["file", "module", "package"]).optional(),
|
|
2681
|
+
requiredRelationKinds: z3.array(boundedString(128)).max(128).optional(),
|
|
2682
|
+
diagnosticsByCode: z3.record(z3.string().max(128), z3.number().int().nonnegative()).optional(),
|
|
2683
|
+
diagnosticsByStatus: z3.record(z3.string().max(128), z3.number().int().nonnegative()).optional(),
|
|
2684
|
+
documentation: z3.object({
|
|
2685
|
+
documentCount: z3.number().int().nonnegative(),
|
|
2686
|
+
documentedDocumentCount: z3.number().int().nonnegative(),
|
|
2687
|
+
packageCount: z3.number().int().nonnegative(),
|
|
2688
|
+
packageStatus: z3.object({
|
|
2689
|
+
fresh: z3.number().int().nonnegative(),
|
|
2690
|
+
stale: z3.number().int().nonnegative(),
|
|
2691
|
+
missing: z3.number().int().nonnegative(),
|
|
2692
|
+
unverified: z3.number().int().nonnegative()
|
|
2693
|
+
}).strict()
|
|
2694
|
+
}).strict().optional()
|
|
2645
2695
|
}).strict()
|
|
2646
2696
|
}).strict();
|
|
2647
2697
|
var WorkflowStateSchema = z3.enum([
|
|
@@ -2759,6 +2809,14 @@ var scalar = (value) => {
|
|
|
2759
2809
|
}
|
|
2760
2810
|
return trimmed;
|
|
2761
2811
|
};
|
|
2812
|
+
var conventionalPackageReference = (path, agentRoot) => {
|
|
2813
|
+
const prefix = `${agentRoot.replace(/\/$/, "")}/`;
|
|
2814
|
+
if (!path.startsWith(prefix)) return void 0;
|
|
2815
|
+
const relative11 = path.slice(prefix.length);
|
|
2816
|
+
const [scope, file] = relative11.split("/");
|
|
2817
|
+
if (scope !== "packages" && scope !== "apps" || !file) return void 0;
|
|
2818
|
+
return file.replace(/\.mdx?$/, "");
|
|
2819
|
+
};
|
|
2762
2820
|
var list = (value) => {
|
|
2763
2821
|
const trimmed = value.trim();
|
|
2764
2822
|
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
@@ -2775,7 +2833,15 @@ var addDiagnostic = (diagnostics, input, code, message, line, endLine = line) =>
|
|
|
2775
2833
|
diagnostics.push(diagnostic(input.path, code, message, line, endLine));
|
|
2776
2834
|
};
|
|
2777
2835
|
var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
|
|
2778
|
-
const
|
|
2836
|
+
const direct = entities.find((entity2) => entity2.id === reference || entity2.aliases?.includes(reference));
|
|
2837
|
+
if (direct) return direct;
|
|
2838
|
+
const packageReference = reference.replace(/^package:/, "");
|
|
2839
|
+
const packageCandidates = entities.filter((entity2) => {
|
|
2840
|
+
if (entity2.kind !== "package") return false;
|
|
2841
|
+
const pathName = entity2.path?.split("/").pop();
|
|
2842
|
+
return entity2.name === reference || entity2.path === reference || pathName === packageReference || entity2.name.endsWith(`/${packageReference}`);
|
|
2843
|
+
});
|
|
2844
|
+
const resolved = packageCandidates.length === 1 ? packageCandidates[0] : void 0;
|
|
2779
2845
|
if (resolved) return resolved;
|
|
2780
2846
|
const id = `unresolved:${reference}`;
|
|
2781
2847
|
const existing = unresolved.get(id);
|
|
@@ -2791,9 +2857,13 @@ var resolveEntity = (reference, entities, input, lineStart, unresolved) => {
|
|
|
2791
2857
|
return entity;
|
|
2792
2858
|
};
|
|
2793
2859
|
var relationKey = (from, to, kind) => `${from}\0${to}\0${kind}`;
|
|
2794
|
-
var parseBlock = (input) => {
|
|
2860
|
+
var parseBlock = (input, options = {}) => {
|
|
2861
|
+
const conventionalPath = conventionalPackageReference(input.path, options.agentRoot ?? "docs/for-agents");
|
|
2795
2862
|
const frontmatter = findFrontmatter(input.content);
|
|
2796
2863
|
if (!frontmatter) {
|
|
2864
|
+
if (conventionalPath && !input.content.replace(/^\uFEFF/, "").startsWith("---")) {
|
|
2865
|
+
return { covers: [{ value: conventionalPath, line: 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
2866
|
+
}
|
|
2797
2867
|
if (input.content.replace(/^\uFEFF/, "").startsWith("---")) {
|
|
2798
2868
|
return {
|
|
2799
2869
|
covers: [],
|
|
@@ -2805,8 +2875,24 @@ var parseBlock = (input) => {
|
|
|
2805
2875
|
return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
|
|
2806
2876
|
}
|
|
2807
2877
|
const { lines, end } = frontmatter;
|
|
2808
|
-
|
|
2809
|
-
|
|
2878
|
+
let docbridgeLine = -1;
|
|
2879
|
+
let typeLine = -1;
|
|
2880
|
+
let packageLine = -1;
|
|
2881
|
+
let humanDocLine = -1;
|
|
2882
|
+
for (let index = 1; index < end; index += 1) {
|
|
2883
|
+
const line = lines[index] ?? "";
|
|
2884
|
+
if (docbridgeLine < 0 && /^docbridge\s*:/.test(line)) docbridgeLine = index;
|
|
2885
|
+
if (typeLine < 0 && /^type\s*:/.test(line)) typeLine = index;
|
|
2886
|
+
if (packageLine < 0 && /^package\s*:/.test(line)) packageLine = index;
|
|
2887
|
+
if (humanDocLine < 0 && /^humanDoc\s*:/.test(line)) humanDocLine = index;
|
|
2888
|
+
}
|
|
2889
|
+
if (docbridgeLine < 0) {
|
|
2890
|
+
const type = typeLine >= 0 ? scalar(lines[typeLine]?.slice("type:".length) ?? "") : "";
|
|
2891
|
+
const packageReference = packageLine >= 0 ? scalar(lines[packageLine]?.slice("package:".length) ?? "") : conventionalPath ?? "";
|
|
2892
|
+
if (type === "package" && packageReference) return { covers: [{ value: packageReference, line: packageLine >= 0 ? packageLine + 1 : typeLine + 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
2893
|
+
if (conventionalPath) return { covers: [{ value: packageReference, line: humanDocLine >= 0 ? humanDocLine + 1 : 1 }], relations: [], diagnostics: [], hasDocbridge: true };
|
|
2894
|
+
return { covers: [], relations: [], diagnostics: [], hasDocbridge: false };
|
|
2895
|
+
}
|
|
2810
2896
|
const diagnostics = [];
|
|
2811
2897
|
const covers = [];
|
|
2812
2898
|
const relations = [];
|
|
@@ -2891,7 +2977,7 @@ var parseBlock = (input) => {
|
|
|
2891
2977
|
return { covers, relations, diagnostics, hasDocbridge: true };
|
|
2892
2978
|
};
|
|
2893
2979
|
var parseDocumentationDeclarations = (input, options) => {
|
|
2894
|
-
const parsed = parseBlock(input);
|
|
2980
|
+
const parsed = parseBlock(input, options);
|
|
2895
2981
|
if (!parsed.hasDocbridge) return { hasDocbridge: false, entities: [], relations: [], diagnostics: [] };
|
|
2896
2982
|
const diagnostics = [...parsed.diagnostics];
|
|
2897
2983
|
const unresolved = /* @__PURE__ */ new Map();
|
|
@@ -2952,12 +3038,12 @@ var parseDocumentationDeclarations = (input, options) => {
|
|
|
2952
3038
|
diagnostics
|
|
2953
3039
|
};
|
|
2954
3040
|
};
|
|
2955
|
-
var applyDocumentationDeclarations = (snapshot, documents) => {
|
|
3041
|
+
var applyDocumentationDeclarations = (snapshot, documents, options = {}) => {
|
|
2956
3042
|
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
2957
3043
|
const relations = new Map(snapshot.relations.map((relation) => [relation.id, relation]));
|
|
2958
3044
|
const diagnostics = [];
|
|
2959
3045
|
for (const document of documents) {
|
|
2960
|
-
const result = parseDocumentationDeclarations(document, { snapshot });
|
|
3046
|
+
const result = parseDocumentationDeclarations(document, { snapshot, ...options });
|
|
2961
3047
|
diagnostics.push(...result.diagnostics);
|
|
2962
3048
|
for (const entity of result.entities) entities.set(entity.id, entity);
|
|
2963
3049
|
for (const relation of result.relations) relations.set(relation.id, relation);
|
|
@@ -3064,6 +3150,8 @@ var SOURCE_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", "
|
|
|
3064
3150
|
var DOCUMENT_EXTENSIONS = [".md", ".mdx"];
|
|
3065
3151
|
var DEFAULT_MAX_FILES2 = 1e4;
|
|
3066
3152
|
var EMPTY_HASH = "0".repeat(64);
|
|
3153
|
+
var DEFAULT_RUNTIME_WIRING_METHODS = ["register", "use", "mount", "attach"];
|
|
3154
|
+
var TEST_MODULE_PATTERN = /(?:\.test|\.spec|__tests__)/;
|
|
3067
3155
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3068
3156
|
var readJson = (path) => {
|
|
3069
3157
|
try {
|
|
@@ -3092,6 +3180,12 @@ var firstLineContaining = (text, pattern) => {
|
|
|
3092
3180
|
const line = text.split(/\r?\n/).findIndex((value) => value.includes(pattern));
|
|
3093
3181
|
return line >= 0 ? line + 1 : void 0;
|
|
3094
3182
|
};
|
|
3183
|
+
var documentClassification = (path) => {
|
|
3184
|
+
if (/(^|\/)docs\/for-agents(?:\/|$)/.test(path)) return "agent";
|
|
3185
|
+
if (/(^|\/)docs(?:\/|$)/.test(path)) return "human";
|
|
3186
|
+
if (/(^|\/)(README|CONTRIBUTING|SECURITY|CHANGELOG)(?:\.|$)/i.test(path)) return "project";
|
|
3187
|
+
return "unclassified";
|
|
3188
|
+
};
|
|
3095
3189
|
var packageName = (manifest, fallback) => typeof manifest.name === "string" && manifest.name.length > 0 ? manifest.name : fallback || void 0;
|
|
3096
3190
|
var workspacePatterns = (root, rootManifest) => {
|
|
3097
3191
|
const fromPackageJson = rootManifest?.workspaces;
|
|
@@ -3233,29 +3327,67 @@ var exportedNames = (sourceFile) => {
|
|
|
3233
3327
|
visit(sourceFile);
|
|
3234
3328
|
return [...names].sort();
|
|
3235
3329
|
};
|
|
3236
|
-
var moduleReferences = (root, path, sourceFile) => {
|
|
3330
|
+
var moduleReferences = (root, path, sourceFile, runtimeWiringMethods) => {
|
|
3237
3331
|
const references = [];
|
|
3238
3332
|
let hasDynamic = false;
|
|
3333
|
+
let hasLiteralDynamic = false;
|
|
3239
3334
|
let hasRuntimeWiring = false;
|
|
3240
|
-
|
|
3241
|
-
|
|
3335
|
+
let hasUnresolvedRuntimeWiring = false;
|
|
3336
|
+
const importedBindings = /* @__PURE__ */ new Map();
|
|
3337
|
+
const staticStringBindings = /* @__PURE__ */ new Map();
|
|
3338
|
+
const collectStaticStringBindings = (node) => {
|
|
3339
|
+
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) {
|
|
3340
|
+
const previous = staticStringBindings.get(node.name.text);
|
|
3341
|
+
staticStringBindings.set(node.name.text, !staticStringBindings.has(node.name.text) || previous === node.initializer.text ? node.initializer.text : void 0);
|
|
3342
|
+
}
|
|
3343
|
+
ts.forEachChild(node, collectStaticStringBindings);
|
|
3344
|
+
};
|
|
3345
|
+
collectStaticStringBindings(sourceFile);
|
|
3346
|
+
const addReference = (specifier, kind, node, detection) => {
|
|
3347
|
+
references.push({ specifier: specifier.text, kind, evidence: nodeEvidence(root, path, sourceFile, node), ...detection ? { detection } : {} });
|
|
3242
3348
|
};
|
|
3243
3349
|
const visit = (node) => {
|
|
3244
3350
|
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
3245
3351
|
addReference(node.moduleSpecifier, "imports", node);
|
|
3352
|
+
const clause = node.importClause;
|
|
3353
|
+
if (clause?.name) importedBindings.set(clause.name.text, node.moduleSpecifier.text);
|
|
3354
|
+
if (clause?.namedBindings && ts.isNamespaceImport(clause.namedBindings)) importedBindings.set(clause.namedBindings.name.text, node.moduleSpecifier.text);
|
|
3355
|
+
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
3356
|
+
for (const element of clause.namedBindings.elements) importedBindings.set((element.name ?? element.propertyName)?.text ?? "", node.moduleSpecifier.text);
|
|
3357
|
+
}
|
|
3246
3358
|
} else if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
3247
3359
|
addReference(node.moduleSpecifier, "re-exports", node);
|
|
3248
3360
|
} else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) && ts.isStringLiteral(node.moduleReference.expression)) {
|
|
3249
3361
|
addReference(node.moduleReference.expression, "imports", node);
|
|
3362
|
+
importedBindings.set(node.name.text, node.moduleReference.expression.text);
|
|
3250
3363
|
} else if (ts.isCallExpression(node)) {
|
|
3251
3364
|
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
3252
|
-
if (
|
|
3365
|
+
if (node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {
|
|
3366
|
+
hasLiteralDynamic = true;
|
|
3367
|
+
addReference(node.arguments[0], "imports", node, "dynamic-literal");
|
|
3368
|
+
} else if (node.arguments[0] && ts.isIdentifier(node.arguments[0]) && staticStringBindings.get(node.arguments[0].text)) {
|
|
3369
|
+
hasLiteralDynamic = true;
|
|
3370
|
+
addReference({ text: staticStringBindings.get(node.arguments[0].text) }, "imports", node, "dynamic-literal");
|
|
3371
|
+
} else hasDynamic = true;
|
|
3253
3372
|
} else if (ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
3254
3373
|
const argument = node.arguments[0];
|
|
3255
3374
|
if (argument && ts.isStringLiteralLike(argument)) addReference(argument, "imports", node);
|
|
3375
|
+
else if (argument && ts.isIdentifier(argument) && staticStringBindings.get(argument.text)) addReference({ text: staticStringBindings.get(argument.text) }, "imports", node);
|
|
3256
3376
|
else hasDynamic = true;
|
|
3257
|
-
} else if (ts.isPropertyAccessExpression(node.expression) && node.expression.name.text
|
|
3377
|
+
} else if (ts.isPropertyAccessExpression(node.expression) && runtimeWiringMethods.has(node.expression.name.text)) {
|
|
3258
3378
|
hasRuntimeWiring = true;
|
|
3379
|
+
const before = references.length;
|
|
3380
|
+
let hasPotentialTargetArgument = false;
|
|
3381
|
+
for (const argument of node.arguments) {
|
|
3382
|
+
if (ts.isIdentifier(argument)) {
|
|
3383
|
+
hasPotentialTargetArgument = true;
|
|
3384
|
+
const specifier = importedBindings.get(argument.text);
|
|
3385
|
+
if (specifier) addReference({ text: specifier }, "runtime-wiring", node, "runtime-wiring-static");
|
|
3386
|
+
} else if (ts.isPropertyAccessExpression(argument) || ts.isCallExpression(argument)) {
|
|
3387
|
+
hasPotentialTargetArgument = true;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
if (hasPotentialTargetArgument && references.length === before) hasUnresolvedRuntimeWiring = true;
|
|
3259
3391
|
}
|
|
3260
3392
|
}
|
|
3261
3393
|
ts.forEachChild(node, visit);
|
|
@@ -3265,7 +3397,9 @@ var moduleReferences = (root, path, sourceFile) => {
|
|
|
3265
3397
|
references,
|
|
3266
3398
|
exports: exportedNames(sourceFile),
|
|
3267
3399
|
hasDynamic,
|
|
3268
|
-
|
|
3400
|
+
hasLiteralDynamic,
|
|
3401
|
+
hasRuntimeWiring,
|
|
3402
|
+
hasUnresolvedRuntimeWiring
|
|
3269
3403
|
};
|
|
3270
3404
|
};
|
|
3271
3405
|
var resolveRelativeModule = (specifier, containingFile, modulePaths) => {
|
|
@@ -3339,10 +3473,10 @@ var artifact = (root, config, files, entities, relations, coverage) => {
|
|
|
3339
3473
|
sourceRevisionKind: revision.kind,
|
|
3340
3474
|
configurationHash: sha256NormalizedV1(config ?? {}),
|
|
3341
3475
|
pipelineVersion: "1.0.0",
|
|
3342
|
-
analyzerVersions: { repository: "1.0.0", "js-ts": "1.
|
|
3476
|
+
analyzerVersions: { repository: "1.0.0", "js-ts": "1.3.0" },
|
|
3343
3477
|
entities: [...entities].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3344
3478
|
relations: [...relations].sort((a, b) => a.id.localeCompare(b.id)),
|
|
3345
|
-
coverage: [
|
|
3479
|
+
coverage: coverage.map((entry) => ({ ...entry, analyzerVersion: entry.analyzerVersion ?? ({ repository: "1.0.0", "js-ts": "1.3.0" }[entry.analyzer] ?? "1.0.0") }))
|
|
3346
3480
|
};
|
|
3347
3481
|
return DiscoverySnapshotV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
3348
3482
|
};
|
|
@@ -3402,12 +3536,12 @@ var discoverRepository = (opts = {}) => {
|
|
|
3402
3536
|
const sourceFile = ts.createSourceFile(absPath, text, ts.ScriptTarget.Latest, true, scriptKind(absPath));
|
|
3403
3537
|
const exports = exportedNames(sourceFile);
|
|
3404
3538
|
modules.set(resolve8(absPath), { absPath, path, entityId: id, ...pkg ? { packageId: pkg.id } : {} });
|
|
3405
|
-
addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test:
|
|
3539
|
+
addEntity({ id, kind: "module", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1, sourceFile.getLineAndCharacterOfPosition(sourceFile.getEnd()).line + 1)], ...exports.length ? { metadata: { exports, test: TEST_MODULE_PATTERN.test(path) } } : {} });
|
|
3406
3540
|
if (pkg) addRelation({ id: entityId("relation", `${pkg.id}:contains:${id}`), kind: "contains", from: pkg.id, to: id, provenance: "observed", evidence: [lineEvidence("code", root, absPath, 1)] });
|
|
3407
3541
|
}
|
|
3408
3542
|
for (const absPath of documentPaths) {
|
|
3409
3543
|
const path = relativePath(root, absPath);
|
|
3410
|
-
addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)] });
|
|
3544
|
+
addEntity({ id: entityId("document", path), kind: "document", name: basename4(absPath), path, provenance: "observed", evidence: [lineEvidence("documentation", root, absPath, 1)], metadata: { classification: documentClassification(path) } });
|
|
3411
3545
|
}
|
|
3412
3546
|
const compiler = readCompilerOptions(root);
|
|
3413
3547
|
const coverage = [
|
|
@@ -3415,8 +3549,8 @@ var discoverRepository = (opts = {}) => {
|
|
|
3415
3549
|
{ analyzer: "repository", scope: "package-manager", status: hasPackageManagerMetadata(root, rootManifest) ? "complete" : "partial", ...!hasPackageManagerMetadata(root, rootManifest) ? { reason: `No package manager metadata found; default helper would fall back to ${detectPackageManager(root)}.` } : {} },
|
|
3416
3550
|
{ analyzer: "repository", scope: "workspace-packages", status: packageResult.coverage.some((item) => item.status === "partial") ? "partial" : "complete", ...packageResult.coverage.find((item) => item.reason)?.reason ? { reason: packageResult.coverage.find((item) => item.reason)?.reason } : {} },
|
|
3417
3551
|
{ analyzer: "js-ts", scope: "static-imports-and-exports", status: compiler.error ? "partial" : "complete", ...compiler.error ? { reason: compiler.error } : {} },
|
|
3418
|
-
{ analyzer: "js-ts", scope: "dynamic-imports", status: "not-
|
|
3419
|
-
{ analyzer: "js-ts", scope: "runtime-wiring", status: "not-
|
|
3552
|
+
{ analyzer: "js-ts", scope: "dynamic-imports", status: "not-applicable", reason: "No dynamic loading expression was observed." },
|
|
3553
|
+
{ analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." },
|
|
3420
3554
|
{ analyzer: "js-ts", scope: "generated-code", status: "not-analyzed", reason: "Generated code is not interpreted as source architecture." }
|
|
3421
3555
|
];
|
|
3422
3556
|
for (const pkg of packageResult.packages) {
|
|
@@ -3427,10 +3561,26 @@ var discoverRepository = (opts = {}) => {
|
|
|
3427
3561
|
addRelation({ id: entityId("relation", `${pkg.id}:depends-on:${target}:${dependency.type}`), kind: "depends-on", from: pkg.id, to: target, provenance: "observed", evidence: [lineEvidence("configuration", root, pkg.manifestPath, firstLineContaining(text, `"${dependency.name}"`))], metadata: { dependencyType: dependency.type } });
|
|
3428
3562
|
}
|
|
3429
3563
|
}
|
|
3564
|
+
const dynamicCoverageIndex = coverage.findIndex((entry) => entry.scope === "dynamic-imports");
|
|
3565
|
+
const configuredRuntimeWiringMethods = /* @__PURE__ */ new Set([
|
|
3566
|
+
...opts.config?.analysis?.jsTs?.runtimeWiringMethods ?? [],
|
|
3567
|
+
...opts.config?.analysis?.jsTs?.runtimeWiringAdapters?.flatMap((adapter) => adapter.methods) ?? []
|
|
3568
|
+
]);
|
|
3569
|
+
if (!configuredRuntimeWiringMethods.size) for (const method of DEFAULT_RUNTIME_WIRING_METHODS) configuredRuntimeWiringMethods.add(method);
|
|
3570
|
+
const includeTestRuntimeWiring = opts.config?.analysis?.jsTs?.includeTestRuntimeWiring ?? false;
|
|
3571
|
+
let observedLiteralDynamic = false;
|
|
3572
|
+
let observedUnresolvedDynamic = false;
|
|
3573
|
+
let observedRuntimeWiring = false;
|
|
3574
|
+
let observedUnresolvedRuntimeWiring = false;
|
|
3430
3575
|
for (const module of modules.values()) {
|
|
3431
3576
|
const text = readFileSync9(module.absPath, "utf8");
|
|
3432
3577
|
const sourceFile = ts.createSourceFile(module.absPath, text, ts.ScriptTarget.Latest, true, scriptKind(module.absPath));
|
|
3433
|
-
const
|
|
3578
|
+
const runtimeWiringMethods = includeTestRuntimeWiring || !TEST_MODULE_PATTERN.test(module.path) ? configuredRuntimeWiringMethods : /* @__PURE__ */ new Set();
|
|
3579
|
+
const references = moduleReferences(root, module.absPath, sourceFile, runtimeWiringMethods);
|
|
3580
|
+
observedLiteralDynamic ||= references.hasLiteralDynamic;
|
|
3581
|
+
observedUnresolvedDynamic ||= references.hasDynamic;
|
|
3582
|
+
observedRuntimeWiring ||= references.hasRuntimeWiring;
|
|
3583
|
+
observedUnresolvedRuntimeWiring ||= references.hasUnresolvedRuntimeWiring;
|
|
3434
3584
|
for (const reference of references.references) {
|
|
3435
3585
|
const target = resolveReference(reference, module.absPath, modules, packageResult.packages, compiler.options);
|
|
3436
3586
|
if (!target) continue;
|
|
@@ -3438,11 +3588,14 @@ var discoverRepository = (opts = {}) => {
|
|
|
3438
3588
|
const externalName = target.targetId.replace(/^external:/, "");
|
|
3439
3589
|
addEntity({ id: target.targetId, kind: "external", name: externalName, provenance: "observed", evidence: [reference.evidence] });
|
|
3440
3590
|
}
|
|
3441
|
-
addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence] });
|
|
3591
|
+
addRelation({ id: entityId("relation", `${module.entityId}:${reference.kind}:${target.targetId}`), kind: reference.kind, from: module.entityId, to: target.targetId, provenance: "observed", evidence: [reference.evidence], ...reference.detection ? { metadata: { detection: reference.detection } } : {} });
|
|
3442
3592
|
}
|
|
3443
|
-
if (references.hasDynamic) coverage.push({ analyzer: "js-ts", scope: `dynamic-imports:${module.path}`, status: "not-analyzed", reason: "A dynamic import
|
|
3444
|
-
if (references.
|
|
3593
|
+
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)] });
|
|
3594
|
+
if (references.hasUnresolvedRuntimeWiring) coverage.push({ analyzer: "js-ts", scope: `runtime-wiring:${module.path}`, status: "not-analyzed", reason: "A runtime registration/wiring call was found without a statically imported target.", evidence: [lineEvidence("code", root, module.absPath)] });
|
|
3445
3595
|
}
|
|
3596
|
+
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." };
|
|
3597
|
+
const runtimeCoverageIndex = coverage.findIndex((entry) => entry.scope === "runtime-wiring");
|
|
3598
|
+
if (runtimeCoverageIndex >= 0) coverage[runtimeCoverageIndex] = observedUnresolvedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "partial", reason: "Some configured runtime-wiring calls remain unresolved after static binding analysis." } : observedRuntimeWiring ? { analyzer: "js-ts", scope: "runtime-wiring", status: "complete", reason: "All observed configured runtime-wiring calls resolved to static bindings." } : { analyzer: "js-ts", scope: "runtime-wiring", status: "not-applicable", reason: "No configured runtime-wiring call was observed." };
|
|
3446
3599
|
return artifact(root, opts.config, allFiles, [...entities.values()], [...relations.values()], coverage);
|
|
3447
3600
|
};
|
|
3448
3601
|
|
|
@@ -4962,6 +5115,22 @@ var appendTransition = (stateDir, transition2) => {
|
|
|
4962
5115
|
`, "utf8");
|
|
4963
5116
|
};
|
|
4964
5117
|
var transition = (run2, to, reason) => {
|
|
5118
|
+
const allowed = {
|
|
5119
|
+
created: ["created", "discovering", "failed", "cancelled"],
|
|
5120
|
+
discovering: ["discovering", "analyzed", "failed", "cancelled", "stale"],
|
|
5121
|
+
analyzed: ["analyzed", "compared", "failed", "cancelled", "stale"],
|
|
5122
|
+
compared: ["compared", "proposed", "failed", "cancelled", "stale"],
|
|
5123
|
+
"awaiting-agent": ["awaiting-agent", "proposed", "failed", "cancelled", "stale"],
|
|
5124
|
+
proposed: ["proposed", "validating", "delivered", "failed", "cancelled", "stale"],
|
|
5125
|
+
"awaiting-approval": ["awaiting-approval", "validating", "failed", "cancelled", "stale"],
|
|
5126
|
+
validating: ["validating", "delivered", "failed", "cancelled", "stale"],
|
|
5127
|
+
delivered: ["delivered", "stale", "failed", "cancelled"],
|
|
5128
|
+
failed: ["failed", "discovering", "analyzed", "compared", "proposed", "validating", "delivered", "cancelled"],
|
|
5129
|
+
cancelled: ["cancelled", "discovering", "analyzed", "compared", "proposed", "validating", "delivered"],
|
|
5130
|
+
stale: [],
|
|
5131
|
+
superseded: []
|
|
5132
|
+
};
|
|
5133
|
+
if (!allowed[run2.state].includes(to)) throw new Error(`Illegal workflow transition ${run2.state} -> ${to}.`);
|
|
4965
5134
|
const item = {
|
|
4966
5135
|
from: run2.state,
|
|
4967
5136
|
to,
|
|
@@ -4971,14 +5140,25 @@ var transition = (run2, to, reason) => {
|
|
|
4971
5140
|
return { ...run2, state: to, transitions: [...run2.transitions, item] };
|
|
4972
5141
|
};
|
|
4973
5142
|
var runId = () => `${Date.now()}-${process.pid}`;
|
|
4974
|
-
var stageInputHash = (options, stage, input) => sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5143
|
+
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" });
|
|
4975
5144
|
var stageArtifactPath = (stateDir, stage, inputHash) => join15(stateDir, "artifacts", `${stage}-${inputHash}.json`);
|
|
4976
5145
|
var readArtifact = (path) => JSON.parse(readFileSync15(path, "utf8"));
|
|
4977
|
-
var
|
|
5146
|
+
var readVerifiedArtifact = (path, stage, step) => {
|
|
5147
|
+
const artifact2 = readArtifact(path);
|
|
5148
|
+
if (artifact2.type !== "workflow-step-artifact" || artifact2.stage !== stage || artifact2.inputHash !== step.inputHash) throw new Error(`Invalid workflow artifact for stage "${stage}".`);
|
|
5149
|
+
if (sha256NormalizedV1(artifact2.value) !== artifact2.outputHash || artifact2.outputHash !== step.outputHash) throw new Error(`Workflow artifact hash mismatch for stage "${stage}".`);
|
|
5150
|
+
return artifact2;
|
|
5151
|
+
};
|
|
5152
|
+
var stepArtifactPath = (stateDir, step) => {
|
|
5153
|
+
const path = resolve12(stateDir, step.artifactRefs?.[0] ?? "");
|
|
5154
|
+
const pathRelativeToState = relative7(stateDir, path);
|
|
5155
|
+
if (pathRelativeToState.startsWith("..") || pathRelativeToState.startsWith("/")) throw new Error(`Workflow artifact escapes state directory for stage "${step.name}".`);
|
|
5156
|
+
return path;
|
|
5157
|
+
};
|
|
4978
5158
|
var stepOutput = (stateDir, run2, stage) => {
|
|
4979
5159
|
const step = run2.steps.find((item) => item.name === stage);
|
|
4980
5160
|
if (!step || step.status !== "completed" || !step.artifactRefs?.[0]) return null;
|
|
4981
|
-
return
|
|
5161
|
+
return readVerifiedArtifact(stepArtifactPath(stateDir, step), stage, step).value;
|
|
4982
5162
|
};
|
|
4983
5163
|
var acquireLock = (stateDir) => {
|
|
4984
5164
|
const lock = join15(stateDir, ".lock");
|
|
@@ -5005,7 +5185,7 @@ var loadManifest = (stateDir) => {
|
|
|
5005
5185
|
return WorkflowRunV1Schema.parse(JSON.parse(readFileSync15(path, "utf8")));
|
|
5006
5186
|
};
|
|
5007
5187
|
var baseRun = (options, stateDir, supersedes) => {
|
|
5008
|
-
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5188
|
+
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? "1.0.0", analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? "1.0.0" });
|
|
5009
5189
|
return WorkflowRunV1Schema.parse({
|
|
5010
5190
|
type: "workflow-run",
|
|
5011
5191
|
schemaVersion: 1,
|
|
@@ -5015,8 +5195,8 @@ var baseRun = (options, stateDir, supersedes) => {
|
|
|
5015
5195
|
sourceRevision: options.sourceRevision,
|
|
5016
5196
|
sourceRevisionKind: "content",
|
|
5017
5197
|
configurationHash: options.configurationHash,
|
|
5018
|
-
pipelineVersion: "1.0.0",
|
|
5019
|
-
analyzerVersions: { workflow: options.toolVersion ?? "1.0.0" },
|
|
5198
|
+
pipelineVersion: options.pipelineVersion ?? "1.0.0",
|
|
5199
|
+
analyzerVersions: { ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" },
|
|
5020
5200
|
runId: options.runId ?? runId(),
|
|
5021
5201
|
state: "created",
|
|
5022
5202
|
steps: WORKFLOW_STAGES.map((name) => ({ name, status: "pending", inputHash })),
|
|
@@ -5025,7 +5205,7 @@ var baseRun = (options, stateDir, supersedes) => {
|
|
|
5025
5205
|
});
|
|
5026
5206
|
};
|
|
5027
5207
|
var withHash = (run2) => WorkflowRunV1Schema.parse({ ...run2, contentHash: contentHashForArtifactV1(run2) });
|
|
5028
|
-
var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.
|
|
5208
|
+
var sameInputs = (run2, options) => run2.sourceRevision === options.sourceRevision && run2.configurationHash === options.configurationHash && run2.pipelineVersion === (options.pipelineVersion ?? "1.0.0") && sha256NormalizedV1(run2.analyzerVersions) === sha256NormalizedV1({ ...options.analyzerVersions ?? {}, workflow: options.toolVersion ?? "1.0.0" });
|
|
5029
5209
|
var selectedStages = (stage) => stage && stage !== "all" ? [stage] : WORKFLOW_STAGES;
|
|
5030
5210
|
var runWorkflow = (options) => {
|
|
5031
5211
|
const root = resolve12(options.root);
|
|
@@ -5050,7 +5230,17 @@ var runWorkflow = (options) => {
|
|
|
5050
5230
|
if (!run2) throw new Error("Workflow manifest was not initialized.");
|
|
5051
5231
|
const firstSelectedStage = selectedStages(options.stage)[0];
|
|
5052
5232
|
const previousStageIndex = firstSelectedStage ? WORKFLOW_STAGES.indexOf(firstSelectedStage) - 1 : -1;
|
|
5053
|
-
let previousOutput =
|
|
5233
|
+
let previousOutput = null;
|
|
5234
|
+
if (previousStageIndex >= 0) {
|
|
5235
|
+
try {
|
|
5236
|
+
previousOutput = stepOutput(stateDir, run2, WORKFLOW_STAGES[previousStageIndex]);
|
|
5237
|
+
} catch (error) {
|
|
5238
|
+
run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
|
|
5239
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
5240
|
+
writeManifest(stateDir, run2);
|
|
5241
|
+
throw error;
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5054
5244
|
const reusedStages = [];
|
|
5055
5245
|
for (const stage of selectedStages(options.stage)) {
|
|
5056
5246
|
const input = options.inputs?.[stage] ?? previousOutput;
|
|
@@ -5058,10 +5248,23 @@ var runWorkflow = (options) => {
|
|
|
5058
5248
|
const existing = run2.steps.find((step) => step.name === stage);
|
|
5059
5249
|
const artifactPath = existing?.artifactRefs?.[0] ? resolve12(stateDir, existing.artifactRefs[0]) : stageArtifactPath(stateDir, stage, inputHash);
|
|
5060
5250
|
if (existing?.status === "completed" && existing.inputHash === inputHash && existing.outputHash && existsSync16(artifactPath)) {
|
|
5061
|
-
|
|
5251
|
+
try {
|
|
5252
|
+
previousOutput = readVerifiedArtifact(artifactPath, stage, existing).value;
|
|
5253
|
+
} catch (error) {
|
|
5254
|
+
run2 = withHash(transition(run2, "failed", error instanceof Error ? error.message : String(error)));
|
|
5255
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
5256
|
+
writeManifest(stateDir, run2);
|
|
5257
|
+
throw error;
|
|
5258
|
+
}
|
|
5062
5259
|
reusedStages.push(stage);
|
|
5063
5260
|
continue;
|
|
5064
5261
|
}
|
|
5262
|
+
if (options.shouldCancel?.()) {
|
|
5263
|
+
run2 = withHash(transition(run2, "cancelled", `Workflow cancellation requested before stage "${stage}".`));
|
|
5264
|
+
appendTransition(stateDir, run2.transitions[run2.transitions.length - 1]);
|
|
5265
|
+
writeManifest(stateDir, run2);
|
|
5266
|
+
return { run: run2, stateDir, reusedStages };
|
|
5267
|
+
}
|
|
5065
5268
|
const handler = options.handlers[stage];
|
|
5066
5269
|
if (!handler) throw new Error(`No handler configured for workflow stage "${stage}".`);
|
|
5067
5270
|
run2 = withHash(transition(run2, stageState[stage]));
|
|
@@ -5816,15 +6019,40 @@ var loadRegistryAgentMetadata = (root, config) => {
|
|
|
5816
6019
|
var createRegistryAgentAdapter = (root, config, runner) => {
|
|
5817
6020
|
if (!registryConfig(config)?.enabled) throw new Error("Registry agents are disabled. Set intelligence.registry.enabled: true to run an assisted workflow.");
|
|
5818
6021
|
const metadata = loadRegistryAgentMetadata(resolve16(root), config);
|
|
6022
|
+
const settings = registryConfig(config) ?? {};
|
|
6023
|
+
const timeoutMs = settings.timeoutMs ?? 12e4;
|
|
6024
|
+
const maxResponseBytes = settings.maxResponseBytes ?? 256e3;
|
|
6025
|
+
const maxTokens = settings.maxTokens ?? Math.ceil(maxResponseBytes / 4);
|
|
6026
|
+
const maxConcurrency = settings.maxConcurrency ?? 1;
|
|
6027
|
+
let active = 0;
|
|
6028
|
+
const deterministicCache = /* @__PURE__ */ new Map();
|
|
5819
6029
|
return {
|
|
5820
6030
|
metadata,
|
|
5821
6031
|
run: async (snapshot, report, evidence2 = report.diagnostics.flatMap((diagnostic2) => diagnostic2.evidence).slice(0, 64)) => {
|
|
5822
|
-
|
|
5823
|
-
const
|
|
5824
|
-
if (
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
6032
|
+
if (active >= maxConcurrency) throw new Error(`Registry agent concurrency limit ${maxConcurrency} exceeded.`);
|
|
6033
|
+
const cacheKey = sha256NormalizedV1({ snapshotHash: snapshot.contentHash, reportHash: report.contentHash, agentId: metadata.id, agentVersion: metadata.version, evidence: evidence2 });
|
|
6034
|
+
if (settings.deterministic && deterministicCache.has(cacheKey)) return deterministicCache.get(cacheKey);
|
|
6035
|
+
active += 1;
|
|
6036
|
+
let timer;
|
|
6037
|
+
try {
|
|
6038
|
+
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 });
|
|
6039
|
+
const timeout = new Promise((_, reject) => {
|
|
6040
|
+
timer = setTimeout(() => reject(new Error(`Registry agent timed out after ${timeoutMs}ms.`)), timeoutMs);
|
|
6041
|
+
});
|
|
6042
|
+
const raw = await Promise.race([Promise.resolve(runner(context)), timeout]);
|
|
6043
|
+
const responseBytes = Buffer.byteLength(JSON.stringify(raw));
|
|
6044
|
+
if (responseBytes > maxResponseBytes) throw new Error(`Registry agent response limit ${maxResponseBytes} bytes exceeded.`);
|
|
6045
|
+
if (Math.ceil(responseBytes / 4) > maxTokens) throw new Error(`Registry agent token budget ${maxTokens} exceeded.`);
|
|
6046
|
+
const proposal = AgentProposalV1Schema.parse(raw);
|
|
6047
|
+
if (proposal.contentHash !== contentHashForArtifactV1(proposal)) throw new Error("Registry agent proposal contentHash does not match its canonical contents.");
|
|
6048
|
+
if (proposal.baseSnapshotHash !== snapshot.contentHash || proposal.baseReportHash !== report.contentHash) throw new Error("Registry agent proposal is not based on the supplied snapshot/report hashes.");
|
|
6049
|
+
if (proposal.origin.kind !== "registry-agent" || proposal.origin.id !== metadata.id) throw new Error(`Registry agent proposal origin must be ${metadata.id}.`);
|
|
6050
|
+
if (settings.deterministic) deterministicCache.set(cacheKey, proposal);
|
|
6051
|
+
return proposal;
|
|
6052
|
+
} finally {
|
|
6053
|
+
if (timer) clearTimeout(timer);
|
|
6054
|
+
active -= 1;
|
|
6055
|
+
}
|
|
5828
6056
|
}
|
|
5829
6057
|
};
|
|
5830
6058
|
};
|
|
@@ -6282,6 +6510,12 @@ var mergeEvidence = (...relations) => {
|
|
|
6282
6510
|
}
|
|
6283
6511
|
return [...merged.values()].sort((a, b) => evidenceKey(a).localeCompare(evidenceKey(b)));
|
|
6284
6512
|
};
|
|
6513
|
+
var boundedEvidence = (evidence2) => [...evidence2].slice(0, 64);
|
|
6514
|
+
var diagnosticCounts = (values) => {
|
|
6515
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6516
|
+
for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
6517
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
6518
|
+
};
|
|
6285
6519
|
var diagnosticId = (code, value) => `reconciliation:${code}:${sha256NormalizedV1(value).slice(0, 32)}`;
|
|
6286
6520
|
var coverageAvailable = (snapshot, relation) => {
|
|
6287
6521
|
const status = snapshot.coverage.find(
|
|
@@ -6297,6 +6531,63 @@ var entityById = (snapshots) => {
|
|
|
6297
6531
|
return entities;
|
|
6298
6532
|
};
|
|
6299
6533
|
var isUnresolved = (id, entities) => id.startsWith("unresolved:") || entities.get(id)?.kind === "unresolved-reference";
|
|
6534
|
+
var normalizedPath = (path) => path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
6535
|
+
var semanticEntityId = (id, scope, entities, packageByModule) => {
|
|
6536
|
+
if (scope === "file" || id.startsWith("external:") || id.startsWith("unresolved:")) return id;
|
|
6537
|
+
const entity = entities.get(id);
|
|
6538
|
+
if (!entity || entity.kind !== "module" || !entity.path) return id;
|
|
6539
|
+
if (scope === "module") return id;
|
|
6540
|
+
return packageByModule.get(id) ?? id;
|
|
6541
|
+
};
|
|
6542
|
+
var packageLookup = (scope, entities) => {
|
|
6543
|
+
if (scope !== "package") return /* @__PURE__ */ new Map();
|
|
6544
|
+
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));
|
|
6545
|
+
const result = /* @__PURE__ */ new Map();
|
|
6546
|
+
for (const entity of entities.values()) {
|
|
6547
|
+
if (entity.kind !== "module" || !entity.path) continue;
|
|
6548
|
+
const modulePath = normalizedPath(entity.path);
|
|
6549
|
+
const packageEntity = packages.find(({ path }) => path === "." || modulePath === path || modulePath.startsWith(`${path}/`));
|
|
6550
|
+
if (packageEntity) result.set(entity.id, packageEntity.id);
|
|
6551
|
+
}
|
|
6552
|
+
return result;
|
|
6553
|
+
};
|
|
6554
|
+
var aggregatedRelations = (relations, scope, entities, packageByModule) => {
|
|
6555
|
+
if (scope === "file") return [...relations];
|
|
6556
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6557
|
+
for (const relation of relations) {
|
|
6558
|
+
const from = semanticEntityId(relation.from, scope, entities, packageByModule);
|
|
6559
|
+
const to = semanticEntityId(relation.to, scope, entities, packageByModule);
|
|
6560
|
+
const detection = relationDetection(relation);
|
|
6561
|
+
const key = `${from}\0${to}\0${relation.kind}\0${detection}`;
|
|
6562
|
+
const group = groups.get(key) ?? [];
|
|
6563
|
+
group.push(relation);
|
|
6564
|
+
groups.set(key, group);
|
|
6565
|
+
}
|
|
6566
|
+
return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, group]) => {
|
|
6567
|
+
const first = group[0];
|
|
6568
|
+
const parts = key.split("\0");
|
|
6569
|
+
const from = parts[0] ?? first.from;
|
|
6570
|
+
const to = parts[1] ?? first.to;
|
|
6571
|
+
const kind = parts[2] ?? first.kind;
|
|
6572
|
+
const detection = parts[3] ?? relationDetection(first);
|
|
6573
|
+
const mergedEvidence = mergeEvidence(...group);
|
|
6574
|
+
return {
|
|
6575
|
+
...first,
|
|
6576
|
+
id: `relation:aggregated:${scope}:${sha256NormalizedV1(key).slice(0, 32)}`,
|
|
6577
|
+
from,
|
|
6578
|
+
to,
|
|
6579
|
+
...detection === "static" && !first.discriminator ? {} : { discriminator: detection },
|
|
6580
|
+
evidence: boundedEvidence(mergedEvidence),
|
|
6581
|
+
metadata: {
|
|
6582
|
+
...first.metadata ?? {},
|
|
6583
|
+
aggregationScope: scope,
|
|
6584
|
+
aggregatedRelationCount: group.length,
|
|
6585
|
+
aggregatedEvidenceCount: mergedEvidence.length,
|
|
6586
|
+
...mergedEvidence.length > 64 ? { evidenceTruncated: true } : {}
|
|
6587
|
+
}
|
|
6588
|
+
};
|
|
6589
|
+
});
|
|
6590
|
+
};
|
|
6300
6591
|
var relationMatches = (observed, declared, resolveEntity2) => {
|
|
6301
6592
|
if (relationBase(observed, resolveEntity2) !== relationBase(declared, resolveEntity2)) return false;
|
|
6302
6593
|
return relationDetection(declared) === relationDetection(observed);
|
|
@@ -6307,7 +6598,7 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
6307
6598
|
status,
|
|
6308
6599
|
severity,
|
|
6309
6600
|
message,
|
|
6310
|
-
evidence:
|
|
6601
|
+
evidence: boundedEvidence(evidence2),
|
|
6311
6602
|
...entityIds?.length ? { entityIds: [...entityIds] } : {},
|
|
6312
6603
|
...relationIds?.length ? { relationIds: [...relationIds] } : {},
|
|
6313
6604
|
...remediation ? { remediation } : {}
|
|
@@ -6315,10 +6606,30 @@ var reportDiagnostic = (code, status, severity, message, evidence2, value, entit
|
|
|
6315
6606
|
var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
6316
6607
|
const resolveEntity2 = entityResolver([observed, declared]);
|
|
6317
6608
|
const entities = entityById([observed, declared]);
|
|
6318
|
-
const
|
|
6319
|
-
const
|
|
6609
|
+
const scope = options.scope ?? "file";
|
|
6610
|
+
const packageByModule = packageLookup(scope, entities);
|
|
6611
|
+
const allDeclaredRelations = declared.relations.filter((relation) => relation.provenance === "declared");
|
|
6612
|
+
const observedRelations = aggregatedRelations(observed.relations.filter((relation) => relation.provenance === "observed" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
|
|
6613
|
+
const declaredRelations = aggregatedRelations(declared.relations.filter((relation) => relation.provenance === "declared" && !ignoredDocumentationRelations.has(relation.kind)), scope, entities, packageByModule);
|
|
6320
6614
|
const requiredRelationKinds = options.requiredRelationKinds === void 0 ? void 0 : new Set(options.requiredRelationKinds);
|
|
6321
6615
|
const diagnostics = [];
|
|
6616
|
+
if (options.includeOrphanedDocuments) {
|
|
6617
|
+
const declaredDocumentIds = new Set(allDeclaredRelations.map((relation) => relation.from).filter((id) => id.startsWith("document:")));
|
|
6618
|
+
for (const document of observed.entities.filter((entity) => entity.kind === "document").sort((a, b) => a.id.localeCompare(b.id))) {
|
|
6619
|
+
if (declaredDocumentIds.has(document.id)) continue;
|
|
6620
|
+
diagnostics.push(reportDiagnostic(
|
|
6621
|
+
"DOCUMENTATION_ORPHANED",
|
|
6622
|
+
"undocumented",
|
|
6623
|
+
"info",
|
|
6624
|
+
"Markdown document has no Doc Bridge declaration linking it to observed knowledge.",
|
|
6625
|
+
document.evidence,
|
|
6626
|
+
document.id,
|
|
6627
|
+
[document.id],
|
|
6628
|
+
void 0,
|
|
6629
|
+
"Add a docbridge declaration or exclude this document from the documentation comparison scope."
|
|
6630
|
+
));
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6322
6633
|
for (const entity of declared.entities.filter((item) => isUnresolved(item.id, entities)).sort((a, b) => a.id.localeCompare(b.id))) {
|
|
6323
6634
|
diagnostics.push(reportDiagnostic(
|
|
6324
6635
|
"UNRESOLVED_ENTITY_REFERENCE",
|
|
@@ -6414,6 +6725,29 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6414
6725
|
}
|
|
6415
6726
|
}
|
|
6416
6727
|
const sortedDiagnostics = [...diagnostics].sort((a, b) => a.id.localeCompare(b.id));
|
|
6728
|
+
const packageEntities = observed.entities.filter((entity) => entity.kind === "package");
|
|
6729
|
+
const packageStatus = { fresh: 0, stale: 0, missing: 0, unverified: 0 };
|
|
6730
|
+
const diagnosticStatusByEntity = /* @__PURE__ */ new Map();
|
|
6731
|
+
const relationEndpoints = new Map([...observedRelations, ...declaredRelations].map((relation) => [relation.id, [relation.from, relation.to]]));
|
|
6732
|
+
for (const diagnostic2 of sortedDiagnostics) for (const id of [...diagnostic2.entityIds ?? [], ...(diagnostic2.relationIds ?? []).flatMap((relationId) => relationEndpoints.get(relationId) ?? [])]) {
|
|
6733
|
+
const statuses = diagnosticStatusByEntity.get(id) ?? /* @__PURE__ */ new Set();
|
|
6734
|
+
statuses.add(diagnostic2.status);
|
|
6735
|
+
diagnosticStatusByEntity.set(id, statuses);
|
|
6736
|
+
}
|
|
6737
|
+
for (const packageEntity of packageEntities) {
|
|
6738
|
+
const docs = allDeclaredRelations.filter((relation) => relation.from.startsWith("document:") && relation.to === packageEntity.id);
|
|
6739
|
+
const statuses = diagnosticStatusByEntity.get(packageEntity.id) ?? /* @__PURE__ */ new Set();
|
|
6740
|
+
if (!docs.length) packageStatus.missing += 1;
|
|
6741
|
+
else if (statuses.has("conflict") || statuses.has("stale-or-unverified")) packageStatus.stale += 1;
|
|
6742
|
+
else if (statuses.has("undocumented") || statuses.has("unresolved") || statuses.has("not-analyzed")) packageStatus.unverified += 1;
|
|
6743
|
+
else packageStatus.fresh += 1;
|
|
6744
|
+
}
|
|
6745
|
+
const documentation = {
|
|
6746
|
+
documentCount: observed.entities.filter((entity) => entity.kind === "document").length,
|
|
6747
|
+
documentedDocumentCount: new Set(allDeclaredRelations.filter((relation) => relation.from.startsWith("document:")).map((relation) => relation.from)).size,
|
|
6748
|
+
packageCount: packageEntities.length,
|
|
6749
|
+
packageStatus
|
|
6750
|
+
};
|
|
6417
6751
|
const base = {
|
|
6418
6752
|
type: "reconciliation-report",
|
|
6419
6753
|
schemaVersion: 1,
|
|
@@ -6431,7 +6765,11 @@ var reconcileKnowledge = (observed, declared, options = {}) => {
|
|
|
6431
6765
|
entityCount: observed.entities.length,
|
|
6432
6766
|
relationCount: observedRelations.length,
|
|
6433
6767
|
diagnosticCount: sortedDiagnostics.length,
|
|
6434
|
-
...options.
|
|
6768
|
+
...options.scope === void 0 ? {} : { scope: options.scope },
|
|
6769
|
+
...options.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: [...new Set(options.requiredRelationKinds)].sort() },
|
|
6770
|
+
diagnosticsByCode: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.code)),
|
|
6771
|
+
diagnosticsByStatus: diagnosticCounts(sortedDiagnostics.map((diagnostic2) => diagnostic2.status)),
|
|
6772
|
+
documentation
|
|
6435
6773
|
}
|
|
6436
6774
|
};
|
|
6437
6775
|
return ReconciliationReportV1Schema.parse({ ...base, contentHash: contentHashForArtifactV1(base) });
|
|
@@ -6454,6 +6792,7 @@ var reportGroupKey = (entity, appLayout) => {
|
|
|
6454
6792
|
return packageFamily(entity);
|
|
6455
6793
|
};
|
|
6456
6794
|
var titleCase = (value) => value.split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
6795
|
+
var levelChunkName = (scope, id) => `chunks/levels-${scope}-${sha256NormalizedV1(id).slice(0, 12)}.js`;
|
|
6457
6796
|
var buildReportViewModel = (snapshot) => {
|
|
6458
6797
|
const entities = new Map(snapshot.entities.map((entity) => [entity.id, entity]));
|
|
6459
6798
|
const packages = snapshot.entities.filter((entity) => entity.kind === "package");
|
|
@@ -6467,6 +6806,7 @@ var buildReportViewModel = (snapshot) => {
|
|
|
6467
6806
|
const appLayout = packages.some((entity) => pathParts(entity.path)[0] === "apps");
|
|
6468
6807
|
const groups = /* @__PURE__ */ new Map();
|
|
6469
6808
|
const entityGroup = {};
|
|
6809
|
+
const groupModuleCounts = /* @__PURE__ */ new Map();
|
|
6470
6810
|
for (const entity of packages) {
|
|
6471
6811
|
const family = reportGroupKey(entity, appLayout);
|
|
6472
6812
|
const id = `group:${family}`;
|
|
@@ -6483,6 +6823,7 @@ var buildReportViewModel = (snapshot) => {
|
|
|
6483
6823
|
for (const [entityId2, packageId2] of packageForEntity) {
|
|
6484
6824
|
const groupId = entityGroup[packageId2];
|
|
6485
6825
|
if (groupId) entityGroup[entityId2] = groupId;
|
|
6826
|
+
if (groupId && entities.get(entityId2)?.kind === "module") groupModuleCounts.set(groupId, (groupModuleCounts.get(groupId) ?? 0) + 1);
|
|
6486
6827
|
}
|
|
6487
6828
|
if (externalIds.size) {
|
|
6488
6829
|
const externalGroupId = "group:external-dependencies";
|
|
@@ -6524,61 +6865,180 @@ var buildReportViewModel = (snapshot) => {
|
|
|
6524
6865
|
nodes: overviewNodes.sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id)),
|
|
6525
6866
|
edges: [...edgeMap.values()].filter((edge) => overviewNodeIds.has(edge.from) && overviewNodeIds.has(edge.to)).map((edge) => ({ ...edge, kinds: [...edge.kinds].sort(), relationIds: [...edge.relationIds].sort() })).sort((left, right) => right.count - left.count || left.from.localeCompare(right.from) || left.to.localeCompare(right.to))
|
|
6526
6867
|
},
|
|
6527
|
-
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort() }])),
|
|
6528
|
-
entityGroup
|
|
6868
|
+
groups: Object.fromEntries([...groups.entries()].map(([id, group]) => [id, { ...group, members: [...group.members].sort(), moduleCount: groupModuleCounts.get(id) ?? 0 }])),
|
|
6869
|
+
entityGroup,
|
|
6870
|
+
levelChunks: {
|
|
6871
|
+
default: "chunks/levels-packages.js",
|
|
6872
|
+
groups: Object.fromEntries([...groups.keys()].map((id) => [id, levelChunkName("group", id)])),
|
|
6873
|
+
packages: [...packages].sort((left, right) => left.id.localeCompare(right.id)).map((entity) => levelChunkName("package", entity.id))
|
|
6874
|
+
}
|
|
6529
6875
|
};
|
|
6530
6876
|
};
|
|
6531
6877
|
var escapeHtml = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
6532
6878
|
var anchor = (prefix, value) => `${prefix}-${value.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
6533
6879
|
var embeddedJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
6534
|
-
var reportData = (snapshot, report, includeSnippets) =>
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6880
|
+
var reportData = (snapshot, report, includeSnippets, privacy = "private") => {
|
|
6881
|
+
const evidence2 = (items) => items.map(({ context, ...item }) => includeSnippets && context ? { ...item, context: redactSecrets(context) } : item);
|
|
6882
|
+
const base = {
|
|
6883
|
+
project: snapshot.project,
|
|
6884
|
+
revision: snapshot.sourceRevision,
|
|
6885
|
+
revisionKind: snapshot.sourceRevisionKind,
|
|
6886
|
+
snapshotHash: snapshot.contentHash,
|
|
6887
|
+
reportHash: report.contentHash,
|
|
6888
|
+
configurationHash: snapshot.configurationHash,
|
|
6889
|
+
pipelineVersion: snapshot.pipelineVersion,
|
|
6890
|
+
analyzerVersions: snapshot.analyzerVersions,
|
|
6891
|
+
diagnosticCount: report.diagnostics.length,
|
|
6892
|
+
requiredRelationKinds: report.summary.requiredRelationKinds,
|
|
6893
|
+
diagnosticSummary: {
|
|
6894
|
+
errors: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error").length,
|
|
6895
|
+
warnings: report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "warn").length,
|
|
6896
|
+
undocumented: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "undocumented").length,
|
|
6897
|
+
drift: report.diagnostics.filter((diagnostic2) => diagnostic2.status === "stale-or-unverified" || diagnostic2.status === "conflict").length
|
|
6898
|
+
},
|
|
6899
|
+
entities: snapshot.entities.map((entity, index) => ({
|
|
6900
|
+
id: entity.id,
|
|
6901
|
+
anchor: entity.id.length > 64 ? `entity-n${index}` : anchor("entity", entity.id),
|
|
6902
|
+
kind: entity.kind,
|
|
6903
|
+
name: entity.name,
|
|
6904
|
+
path: entity.path,
|
|
6905
|
+
provenance: entity.provenance,
|
|
6906
|
+
evidence: evidence2(entity.evidence)
|
|
6907
|
+
})),
|
|
6908
|
+
relations: snapshot.relations.map((relation) => ({
|
|
6909
|
+
id: relation.id,
|
|
6910
|
+
kind: relation.kind,
|
|
6911
|
+
from: relation.from,
|
|
6912
|
+
to: relation.to,
|
|
6913
|
+
provenance: relation.provenance,
|
|
6914
|
+
evidence: evidence2(relation.evidence)
|
|
6915
|
+
})),
|
|
6916
|
+
diagnostics: report.diagnostics.map((diagnostic2) => ({
|
|
6917
|
+
id: diagnostic2.id,
|
|
6918
|
+
code: diagnostic2.code,
|
|
6919
|
+
status: diagnostic2.status,
|
|
6920
|
+
severity: diagnostic2.severity,
|
|
6921
|
+
message: diagnostic2.message,
|
|
6922
|
+
entityIds: diagnostic2.entityIds ?? [],
|
|
6923
|
+
relationIds: diagnostic2.relationIds ?? [],
|
|
6924
|
+
remediation: diagnostic2.remediation,
|
|
6925
|
+
evidence: evidence2(diagnostic2.evidence)
|
|
6926
|
+
})),
|
|
6927
|
+
coverage: snapshot.coverage,
|
|
6928
|
+
view: buildReportViewModel(snapshot)
|
|
6929
|
+
};
|
|
6930
|
+
if (privacy !== "anonymized") return base;
|
|
6931
|
+
const pad = (value) => String(value).padStart(3, "0");
|
|
6932
|
+
const sortedEntities = [...base.entities].sort((left, right) => left.id.localeCompare(right.id));
|
|
6933
|
+
const entityOrdinals = /* @__PURE__ */ new Map();
|
|
6934
|
+
const entityIds = new Map(sortedEntities.map((entity) => {
|
|
6935
|
+
const next = (entityOrdinals.get(entity.kind) ?? 0) + 1;
|
|
6936
|
+
entityOrdinals.set(entity.kind, next);
|
|
6937
|
+
return [entity.id, `entity:${entity.kind}-${pad(next)}`];
|
|
6938
|
+
}));
|
|
6939
|
+
const entityNames = /* @__PURE__ */ new Map();
|
|
6940
|
+
const nameFor = (kind) => {
|
|
6941
|
+
const prefix = kind === "external" ? "dependency" : kind;
|
|
6942
|
+
const next = (entityNames.get(prefix) ?? 0) + 1;
|
|
6943
|
+
entityNames.set(prefix, next);
|
|
6944
|
+
return `${prefix}-${pad(next)}`;
|
|
6945
|
+
};
|
|
6946
|
+
const sortedRelations = [...base.relations].sort((left, right) => left.id.localeCompare(right.id));
|
|
6947
|
+
const relationIds = new Map(sortedRelations.map((relation, index) => [relation.id, `relation:${pad(index + 1)}`]));
|
|
6948
|
+
const sortedDiagnostics = [...base.diagnostics].sort((left, right) => left.id.localeCompare(right.id));
|
|
6949
|
+
const diagnosticIds = new Map(sortedDiagnostics.map((diagnostic2, index) => [diagnostic2.id, `finding:${pad(index + 1)}`]));
|
|
6950
|
+
const genericEvidence = (items) => items.map((item, index) => ({
|
|
6951
|
+
source: item.source,
|
|
6952
|
+
path: `evidence-${pad(index + 1)}`,
|
|
6953
|
+
...item.lineStart === void 0 ? {} : { lineStart: item.lineStart },
|
|
6954
|
+
...item.lineEnd === void 0 ? {} : { lineEnd: item.lineEnd }
|
|
6955
|
+
}));
|
|
6956
|
+
const groups = Object.entries(base.view.groups).sort(([left], [right]) => left.localeCompare(right));
|
|
6957
|
+
const groupIds = new Map(groups.map(([id], index) => [id, `group:${pad(index + 1)}`]));
|
|
6958
|
+
const groupNames = /* @__PURE__ */ new Map();
|
|
6959
|
+
const groupName = (kind) => {
|
|
6960
|
+
if (kind === "external") return "External dependencies";
|
|
6961
|
+
if (kind === "shared") return "Shared packages";
|
|
6962
|
+
if (kind === "workspace") return "Workspace";
|
|
6963
|
+
const next = (groupNames.get(kind) ?? 0) + 1;
|
|
6964
|
+
groupNames.set(kind, next);
|
|
6965
|
+
return `${kind.charAt(0).toUpperCase()}${kind.slice(1)} ${next}`;
|
|
6966
|
+
};
|
|
6967
|
+
const sanitizedGroups = Object.fromEntries(groups.map(([id, group]) => [groupIds.get(id), {
|
|
6968
|
+
kind: group.kind,
|
|
6969
|
+
name: groupName(group.kind),
|
|
6970
|
+
path: void 0,
|
|
6971
|
+
members: group.members.map((member) => entityIds.get(member)),
|
|
6972
|
+
moduleCount: group.moduleCount
|
|
6973
|
+
}]));
|
|
6974
|
+
const sanitizedOverviewNodes = base.view.overview.nodes.map((node) => ({
|
|
6975
|
+
id: groupIds.get(node.id),
|
|
6976
|
+
kind: node.kind,
|
|
6977
|
+
name: sanitizedGroups[groupIds.get(node.id)]?.name ?? "Scope",
|
|
6978
|
+
path: void 0,
|
|
6979
|
+
memberCount: node.memberCount
|
|
6980
|
+
}));
|
|
6981
|
+
const sanitizedView = {
|
|
6982
|
+
overview: {
|
|
6983
|
+
nodes: sanitizedOverviewNodes,
|
|
6984
|
+
edges: base.view.overview.edges.map((edge) => ({
|
|
6985
|
+
from: groupIds.get(edge.from),
|
|
6986
|
+
to: groupIds.get(edge.to),
|
|
6987
|
+
count: edge.count,
|
|
6988
|
+
kinds: edge.kinds,
|
|
6989
|
+
relationIds: edge.relationIds.map((id) => relationIds.get(id))
|
|
6990
|
+
}))
|
|
6991
|
+
},
|
|
6992
|
+
groups: sanitizedGroups,
|
|
6993
|
+
entityGroup: Object.fromEntries(Object.entries(base.view.entityGroup).map(([id, group]) => [entityIds.get(id), groupIds.get(group)])),
|
|
6994
|
+
levelChunks: {
|
|
6995
|
+
default: base.view.levelChunks.default,
|
|
6996
|
+
groups: Object.fromEntries([...groupIds.values()].map((id) => [id, levelChunkName("group", id)])),
|
|
6997
|
+
packages: [...entityIds.values()].filter((id) => id.startsWith("entity:package-")).sort().map((id) => levelChunkName("package", id))
|
|
6998
|
+
}
|
|
6999
|
+
};
|
|
7000
|
+
return {
|
|
7001
|
+
...base,
|
|
7002
|
+
project: { name: "Anonymized repository" },
|
|
7003
|
+
revision: "redacted",
|
|
7004
|
+
entities: base.entities.map((entity) => ({
|
|
7005
|
+
id: entityIds.get(entity.id),
|
|
7006
|
+
anchor: anchor("entity", entityIds.get(entity.id)),
|
|
7007
|
+
kind: entity.kind,
|
|
7008
|
+
name: nameFor(entity.kind),
|
|
7009
|
+
path: void 0,
|
|
7010
|
+
provenance: entity.provenance,
|
|
7011
|
+
evidence: genericEvidence(entity.evidence)
|
|
7012
|
+
})),
|
|
7013
|
+
relations: base.relations.map((relation) => ({
|
|
7014
|
+
id: relationIds.get(relation.id),
|
|
7015
|
+
kind: relation.kind,
|
|
7016
|
+
from: entityIds.get(relation.from),
|
|
7017
|
+
to: entityIds.get(relation.to),
|
|
7018
|
+
provenance: relation.provenance,
|
|
7019
|
+
evidence: genericEvidence(relation.evidence)
|
|
7020
|
+
})),
|
|
7021
|
+
diagnostics: base.diagnostics.map((diagnostic2) => ({
|
|
7022
|
+
id: diagnosticIds.get(diagnostic2.id),
|
|
7023
|
+
code: diagnostic2.code,
|
|
7024
|
+
status: diagnostic2.status,
|
|
7025
|
+
severity: diagnostic2.severity,
|
|
7026
|
+
message: `Finding ${diagnostic2.code}`,
|
|
7027
|
+
entityIds: diagnostic2.entityIds?.map((id) => entityIds.get(id)) ?? [],
|
|
7028
|
+
relationIds: diagnostic2.relationIds?.map((id) => relationIds.get(id)) ?? [],
|
|
7029
|
+
evidence: genericEvidence(diagnostic2.evidence)
|
|
7030
|
+
})),
|
|
7031
|
+
coverage: base.coverage.map((entry, index) => ({
|
|
7032
|
+
analyzer: entry.analyzer,
|
|
7033
|
+
analyzerVersion: entry.analyzerVersion,
|
|
7034
|
+
scope: `scope-${pad(index + 1)}`,
|
|
7035
|
+
status: entry.status,
|
|
7036
|
+
...entry.reason ? { reason: "Coverage boundary reported by analyzer." } : {},
|
|
7037
|
+
...entry.evidence ? { evidence: genericEvidence(entry.evidence) } : {}
|
|
7038
|
+
})),
|
|
7039
|
+
view: sanitizedView
|
|
7040
|
+
};
|
|
7041
|
+
};
|
|
6582
7042
|
var styles = String.raw`
|
|
6583
7043
|
:root{color-scheme:light;--ink:#17251f;--muted:#60706a;--paper:#f5f7f3;--panel:#fff;--line:#d9e0da;--green:#18794e;--blue:#236b83;--amber:#a9640b;--red:#b33c35;--shadow:0 12px 32px rgba(23,37,31,.08)}
|
|
6584
7044
|
*{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}}
|
|
@@ -6593,6 +7053,7 @@ styles += String.raw`body[data-report-view="architecture"] #insights,body[data-r
|
|
|
6593
7053
|
styles += String.raw`body[data-report-view="architecture"] #report-dashboard{display:none!important}.edge{fill:none}`;
|
|
6594
7054
|
styles += String.raw`:root{--ink:#17251f;--muted:#586b63;--paper:#f2f5f1;--panel:#fbfcf9;--line:#c9d5cc;--green:#13734a;--blue:#155f78;--amber:#995d08;--red:#a52e2b}html,body{max-width:100%;overflow-x:hidden}body{font-size:15px;line-height:1.55}.shell{width:100%;max-width:1600px;margin:0 auto;padding:28px clamp(16px,3vw,48px) 56px}.masthead{gap:32px;padding:0 0 24px;align-items:flex-start}.masthead h1{font-size:clamp(2.8rem,5.5vw,5.4rem);line-height:.94;margin:12px 0 16px;max-width:100%;overflow-wrap:anywhere}.lede{max-width:60ch;font-size:16px}.run-meta{min-width:0;max-width:300px}.summary{margin:0;padding:18px 0 20px;grid-template-columns:repeat(5,minmax(0,1fr));gap:0}.metric{min-width:0;padding:0 16px;border-inline-start:1px solid var(--line)}.metric:first-child{padding-inline-start:0;border-inline-start:0}.metric b{font-size:clamp(1.8rem,3vw,3rem);max-width:100%;overflow-wrap:anywhere}.metric span{font-size:10px;letter-spacing:.06em;line-height:1.25}.lens-bar{margin:24px 0 4px;align-items:flex-end}.lens-bar>.subtle{max-width:52ch}.report-tabs{max-width:100%;overflow-x:auto}.tab,.level{min-height:40px;padding:8px 12px}.filters{max-width:100%;align-items:center}.filters label{min-width:0}.filters input,.filters select{max-width:100%;min-width:0}.workspace{min-width:0;grid-template-columns:minmax(0,1fr) minmax(280px,340px);gap:20px}.panel{min-width:0}.map-wrap{min-width:0;max-width:100%;width:100%;overflow:hidden;contain:layout paint}#graph{display:block;width:100%!important;min-width:0!important;max-width:100%;height:clamp(480px,60vh,680px)!important}.side{min-width:0}.detail-title,.path,.list,.finding,.finding-group,.report-table{min-width:0;overflow-wrap:anywhere}.report-dashboard{min-width:0}.report-table{table-layout:fixed}.report-table th,.report-table td{overflow-wrap:anywhere}.bar-item{min-width:0}.bar-item span{min-width:0;overflow-wrap:anywhere}.tab:focus-visible,.level:focus-visible,input:focus-visible,select:focus-visible{outline:3px solid var(--blue);outline-offset:2px}@media(max-width:1100px){.workspace{grid-template-columns:1fr}.side{position:static}.summary{grid-template-columns:repeat(3,minmax(0,1fr));row-gap:16px}.summary .metric:nth-child(4){border-inline-start:0;padding-inline-start:0}}@media(max-width:700px){.shell{padding:12px 16px 32px}.masthead{padding-bottom:14px}.masthead h1{font-size:clamp(2.2rem,11vw,3.4rem);margin:6px 0 10px}.lede{font-size:14px;line-height:1.35}.run-meta{display:none}.summary{padding:10px 0 12px;grid-template-columns:repeat(2,minmax(0,1fr));row-gap:8px}.summary .metric:nth-child(even){border-inline-start:1px solid var(--line);padding-inline-start:12px}.summary .metric:nth-child(odd){border-inline-start:0;padding-inline-start:0}.metric{padding-inline:10px}.metric b{font-size:1.65rem}.lens-bar{display:block;margin-top:14px}.lens-bar>.subtle{display:none}.filters{display:grid;grid-template-columns:1fr;gap:10px}.filters label{display:grid;grid-template-columns:1fr;gap:5px}.filters input,.filters select,.filters button{width:100%;min-height:44px}.panel{padding:16px}.panel-head{display:block}.panel-head>.tabs{margin-top:12px}.finding-groups{grid-template-columns:1fr}.report-dashboard{grid-template-columns:1fr}#graph{height:520px!important}.lens-bar + #lens-caption{margin:8px 0}.lens-bar + #lens-caption + #insights{margin-top:8px}}@media(prefers-color-scheme:dark){:root{--ink:#edf5ef;--muted:#b7c6ba;--paper:#111815;--panel:#1a2821;--line:#405248;--green:#73d19d;--blue:#8bd1e6;--amber:#f0bd70;--red:#ff9389}}`;
|
|
6595
7055
|
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}}`;
|
|
7056
|
+
styles += String.raw`.map-wrap.dense{overflow:auto}.map-wrap.dense #graph{width:2000px!important;max-width:none!important}`;
|
|
6596
7057
|
styles += String.raw`#lens-caption + p.subtle{display:none}`;
|
|
6597
7058
|
var script = String.raw`
|
|
6598
7059
|
const data=${"${DATA}"};
|
|
@@ -6600,22 +7061,26 @@ data.entities=data.entities||[];data.relations=data.relations||[];data.diagnosti
|
|
|
6600
7061
|
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.";
|
|
6601
7062
|
const ensurePolicyNote=()=>{const note=document.querySelector("#map-note");if(note&&!note.textContent.includes(policySuffix))note.textContent=(note.textContent+" · "+policySuffix).trim()};
|
|
6602
7063
|
new MutationObserver(ensurePolicyNote).observe(document.querySelector("#map-note"),{childList:true,characterData:true});
|
|
6603
|
-
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],loadedChunks=new Set(),loadingChunks=new Map();
|
|
7064
|
+
const lazyChunks=globalThis.__DOC_BRIDGE_LAZY_CHUNKS__||[],hasLevelChunks=globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__===true,loadedChunks=new Set(),loadingChunks=new Map();
|
|
6604
7065
|
const esc=(value)=>String(value??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll("\"",""").replaceAll("'","'");
|
|
6605
|
-
const byId=new Map(),parent=new Map(),relationById=new Map(),packageMembers=new Map(),findingIndex=new Map(),relationFindingIndex=new Map(),relationsByEntity=new Map(),groupEntityIds=new Map(),groupFindingCounts=new Map(),packageCache=new Map(),nodeIssueCache=new Map();
|
|
7066
|
+
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;
|
|
7067
|
+
let activeLevelChunk="";
|
|
6606
7068
|
const groupById=new Map(Object.entries(data.view?.groups||{}));
|
|
7069
|
+
const packageChunkNameById=new Map([...groupById.values()].flatMap((group)=>group.members).filter((id)=>data.entities.find((entity)=>entity.id===id)?.kind==="package").sort().map((id,index)=>[id,data.view?.levelChunks?.packages?.[index]]));
|
|
6607
7070
|
const addFinding=(index,id,finding)=>{const values=index.get(id)||[];values.push(finding);index.set(id,values)};
|
|
6608
7071
|
const addRelation=(id,relation)=>{const values=relationsByEntity.get(id)||[];values.push(relation);relationsByEntity.set(id,values)};
|
|
6609
|
-
const hydrate=()=>{byId.clear();parent.clear();relationById.clear();packageMembers.clear();findingIndex.clear();relationFindingIndex.clear();relationsByEntity.clear();groupEntityIds.clear();groupFindingCounts.clear();packageCache.clear();nodeIssueCache.clear();data.entities.forEach((entity)=>{byId.set(entity.id,entity);const groupId=data.view?.entityGroup?.[entity.id];if(groupId){const ids=groupEntityIds.get(groupId)||new Set();ids.add(entity.id);groupEntityIds.set(groupId,ids)}});data.relations.forEach((relation)=>{relationById.set(relation.id,relation);addRelation(relation.from,relation);addRelation(relation.to,relation);if(relation.kind==="contains"){if(!parent.has(relation.to))parent.set(relation.to,relation.from);const members=packageMembers.get(relation.from)||new Set();members.add(relation.to);packageMembers.set(relation.from,members)}});data.diagnostics.forEach((finding)=>{(finding.entityIds||[]).forEach((id)=>addFinding(findingIndex,id,finding));(finding.relationIds||[]).forEach((id)=>addFinding(relationFindingIndex,id,finding));const scopes=
|
|
6610
|
-
const
|
|
6611
|
-
const
|
|
7072
|
+
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)})})};
|
|
7073
|
+
const isLevelChunk=(name)=>name.startsWith("chunks/levels-");
|
|
7074
|
+
const isDetailChunk=(name)=>name.startsWith("chunks/details-");
|
|
7075
|
+
const hasChunk=(name)=>isLevelChunk(name)?activeLevelChunk===name:isDetailChunk(name)?loadedChunks.has(name):!lazyChunks.includes(name)||loadedChunks.has(name);
|
|
7076
|
+
const loadChunk=(name)=>{if(hasChunk(name))return Promise.resolve();if(loadingChunks.has(name))return loadingChunks.get(name);const promise=new Promise((resolve,reject)=>{const element=document.createElement("script");element.src=name;element.onload=()=>{try{if(isLevelChunk(name)){const payload=globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__;if(!payload)throw new Error("Offline report level payload is missing: "+name);data.entities=payload.entities||[];data.relations=payload.relations||[];activeLevelChunk=name;delete globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__}else if(isDetailChunk(name)){const payload=globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__;if(!payload)throw new Error("Offline report detail payload is missing: "+name);const entities=new Map((payload.entities||[]).map((entity)=>[entity.id,entity]));const relations=new Map((payload.relations||[]).map((relation)=>[relation.id,relation]));data.entities=data.entities.map((entity)=>entities.get(entity.id)||entity);data.relations=data.relations.map((relation)=>relations.get(relation.id)||relation);delete globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__}loadedChunks.add(name);hydrate();loadingChunks.delete(name);resolve()}catch(error){loadingChunks.delete(name);reject(error)}};element.onerror=()=>{loadingChunks.delete(name);reject(new Error("Unable to load offline report chunk: "+name))};document.head.append(element)});loadingChunks.set(name,promise);return promise};
|
|
6612
7077
|
const readHash=()=>new URLSearchParams(location.hash.slice(1));
|
|
6613
7078
|
const hash=readHash();
|
|
6614
7079
|
const state={lens:hash.get("lens")||"architecture",level:hash.get("level")||"overview",selected:hash.get("selected"),query:hash.get("query")||"",status:hash.get("status")||"",severity:hash.get("severity")||"",findingGroup:hash.get("findingGroup"),findingPage:Number(hash.get("findingPage")||0)};
|
|
6615
7080
|
const syncHash=()=>{const params=new URLSearchParams();[["lens",state.lens,"architecture"],["level",state.level,"overview"],["selected",state.selected,""],["query",state.query,""],["status",state.status,""],["severity",state.severity,""],["findingGroup",state.findingGroup,""],["findingPage",state.findingPage,"0"]].forEach(([key,value,defaultValue])=>{if(value&&value!==defaultValue)params.set(key,String(value))});const next=params.toString();try{if(location.hash.slice(1)!==next)history.replaceState(null,"",next?"#"+next:"")}catch{}};
|
|
6616
7081
|
const rootOf=(id)=>{let current=id;const seen=new Set();while(parent.has(current)&&!seen.has(current)){seen.add(current);current=parent.get(current)}return current};
|
|
6617
7082
|
const packageOf=(id)=>{if(packageCache.has(id))return packageCache.get(id);let current=id;const seen=new Set();while(current&&!seen.has(current)){seen.add(current);if(byId.get(current)?.kind==="package"){packageCache.set(id,current);return current}current=parent.get(current)}packageCache.set(id,id);return id};
|
|
6618
|
-
const groupFor=(id)=>data.view?.entityGroup?.[id]||id;
|
|
7083
|
+
const groupFor=(id)=>{const direct=data.view?.entityGroup?.[id];if(direct)return direct;const entity=byId.get(id);return entity?.kind==="module"||entity?.kind==="file"?groupFor(packageOf(id)):id};
|
|
6619
7084
|
const packageNodes=()=>data.entities.filter((entity)=>entity.kind==="package");
|
|
6620
7085
|
const viewLabels={architecture:"Architecture",drift:"Insights",risks:"Findings",evidence:"Coverage"};
|
|
6621
7086
|
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};
|
|
@@ -6623,28 +7088,32 @@ const label=(entity)=>entity?.name||entity?.id||"Unknown";
|
|
|
6623
7088
|
const short=(value,max)=>{const text=String(value??""),limit=text==="External dependencies"?16:max;return text.length>limit?text.slice(0,limit-1)+"…":text};
|
|
6624
7089
|
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("")};
|
|
6625
7090
|
const degreeMap=(relations)=>{const degrees=new Map();relations.forEach((relation)=>{degrees.set(relation.from,(degrees.get(relation.from)||0)+1);degrees.set(relation.to,(degrees.get(relation.to)||0)+1)});return degrees};
|
|
6626
|
-
const relationHealth=(relation)=>{if(!relation)return "";const findings=
|
|
6627
|
-
const nodeIssues=(nodeId)=>{if(nodeIssueCache.has(nodeId))return nodeIssueCache.get(nodeId);const ids=groupEntityIds.get(nodeId)||new Set([nodeId]),findings=new Set(),relations=new Set();for(const id of ids){(findingIndex.get(id)||[]).forEach((finding)=>findings.add(finding));(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation))}relations.forEach((relation)=>(relationFindingIndex.get(relation.id)||[]).forEach((finding)=>findings.add(finding)));const result=[...findings];nodeIssueCache.set(nodeId,result);return result};
|
|
7091
|
+
const relationHealth=(relation)=>{if(!relation)return "";const findings=relationFindings(relation.id);return findings.some((finding)=>finding.severity==="error")?"error":findings.length?"warn":""};
|
|
7092
|
+
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};
|
|
6628
7093
|
const findingInLens=(finding)=>state.lens==="architecture"||(state.lens==="drift"&&finding.status!=="confirmed")||(state.lens==="risks"&&(finding.severity==="error"||finding.severity==="warn"))||(state.lens==="evidence"&&finding.evidence.length>0);
|
|
6629
|
-
const
|
|
7094
|
+
const relationFindings=(id)=>relationFindingIndex.get(id)||data.view?.diagnosticRelationFindings?.[id]||[];
|
|
7095
|
+
const relationInLens=(id)=>relationFindings(id).some(findingInLens);
|
|
6630
7096
|
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)};
|
|
7097
|
+
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};
|
|
7098
|
+
const detailChunkName=()=>{const entity=state.selected?byId.get(state.selected):null;const packageId=entity?.kind==="package"?entity.id:entity?packageOf(entity.id):null;const levelName=packageId&&packageChunkNameById.get(packageId);return levelName?.replace("chunks/levels-package-","chunks/details-")||null};
|
|
6631
7099
|
const nodesFor=()=>{if(state.level==="overview")return data.view?.overview?.nodes||[];const packages=packageNodes(),selectedEntity=state.selected?byId.get(state.selected):null,groupScope=state.selected&&groupById.has(state.selected)?state.selected:null,packageScope=selectedEntity?.kind==="package"?selectedEntity.id:state.selected&&!groupScope?packageOf(state.selected):null;if(state.level==="package"&&!state.selected){const packageIds=new Set(packages.map((node)=>node.id));return packages.concat(state.lens==="architecture"?[]:externalFor(packageIds))}if((state.level==="module"||state.level==="file")&&!state.selected)return[];let nodes=data.entities.filter((entity)=>{if(state.level==="package")return entity.kind==="package"&&(!groupScope||groupFor(entity.id)===groupScope)&&(!packageScope||entity.id===packageScope);if(state.level==="module")return entity.kind==="module"&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope);return Boolean(entity.path)&&(groupScope?groupFor(entity.id)===groupScope:packageOf(entity.id)===packageScope)});if(state.level==="module"&&selectedEntity?.kind==="module"){const neighborhood=new Set([selectedEntity.id]);(relationsByEntity.get(selectedEntity.id)||[]).forEach((relation)=>{if(!["imports","re-exports"].includes(relation.kind))return;if(relation.from===selectedEntity.id)neighborhood.add(relation.to);if(relation.to===selectedEntity.id)neighborhood.add(relation.from)});nodes=data.entities.filter((entity)=>entity.kind==="module"&&neighborhood.has(entity.id))}const scopedIds=new Set(nodes.map((node)=>node.id)),external=state.lens==="architecture"?[]:externalFor(scopedIds),degrees=degreeMap(data.relations);return nodes.concat(external).sort((a,b)=>(degrees.get(b.id)||0)-(degrees.get(a.id)||0)||a.id.localeCompare(b.id)).slice(0,state.level==="file"?80:60)};
|
|
6632
|
-
const
|
|
7100
|
+
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}};
|
|
7101
|
+
const graphModel=()=>{const key=state.lens+"|"+state.level+"|"+(state.selected||"");if(graphCacheKey===key&&graphCache)return graphCache;graphCacheKey=key;return graphCache=graphModelUncached()};
|
|
6633
7102
|
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("")};
|
|
6634
|
-
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>"};
|
|
7103
|
+
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>"};
|
|
6635
7104
|
const mapState={scale:1,dragging:false,x:0,y:0};
|
|
6636
|
-
const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
|
|
7105
|
+
const applyMapTransform=()=>{const svg=document.querySelector("#graph");if(!svg)return;const dense=svg.closest(".map-wrap")?.classList.contains("dense");svg.setAttribute("preserveAspectRatio",dense?"xMinYMin meet":"xMidYMid meet");svg.style.transform="translate("+mapState.x+"px,"+mapState.y+"px) scale("+mapState.scale+")";svg.style.transformOrigin="center center"};
|
|
6637
7106
|
const routeGraphEdges=()=>{const svg=document.querySelector("#graph");if(!svg)return;svg.querySelectorAll("line.edge").forEach((line,index)=>{const x1=Number(line.getAttribute("x1")),y1=Number(line.getAttribute("y1")),x2=Number(line.getAttribute("x2")),y2=Number(line.getAttribute("y2")),midX=(x1+x2)/2+((index%5)-2)*14,path=document.createElementNS("http://www.w3.org/2000/svg","path");path.setAttribute("class",line.getAttribute("class")||"edge");path.setAttribute("fill","none");path.setAttribute("marker-end",line.getAttribute("marker-end")||"url(#arrow)");path.setAttribute("d","M "+x1+" "+y1+" H "+midX+" V "+y2+" H "+x2);const label=line.parentElement?.querySelector("text.edge-label");if(label){label.setAttribute("x",String(midX+3));label.setAttribute("y",String((y1+y2)/2))}line.replaceWith(path)})};
|
|
6638
7107
|
const enterNode=(id)=>{const group=groupById.get(id),entity=byId.get(id);if(group){state.selected=id;state.level="package"}else if(entity?.kind==="package"){state.selected=id;state.level="module"}else if(entity?.kind==="module"){state.selected=id;state.level="file"}else return;mapState.scale=1;mapState.x=0;mapState.y=0;render()};
|
|
6639
7108
|
let nodeClickTimer;
|
|
6640
7109
|
document.addEventListener("dblclick",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);enterNode(node.dataset.node)},true);
|
|
6641
|
-
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);
|
|
7110
|
+
document.addEventListener("click",(event)=>{const node=event.target.closest?.("[data-node]");if(!node)return;event.preventDefault();event.stopImmediatePropagation();clearTimeout(nodeClickTimer);nodeClickTimer=setTimeout(async()=>{state.selected=node.dataset.node;render();const chunk=detailChunkName();if(chunk&&!hasChunk(chunk)){await loadChunk(chunk);render()}},380)},true);
|
|
6642
7111
|
document.addEventListener("click",(event)=>{const crumb=event.target.closest?.("[data-breadcrumb-level]");if(!crumb)return;event.preventDefault();event.stopImmediatePropagation();state.level=crumb.dataset.breadcrumbLevel;state.selected=crumb.dataset.breadcrumbSelected||null;mapState.scale=1;mapState.x=0;mapState.y=0;render()},true);
|
|
6643
7112
|
const diagnosticsFor=(id)=>findingIndex.get(id)||[];
|
|
6644
7113
|
const renderDetails=()=>{const panel=document.querySelector("#details"),entity=state.selected?byId.get(state.selected):null,group=state.selected?groupById.get(state.selected):null;if(group){const members=group.members.map((id)=>byId.get(id)).filter(Boolean),relations=new Set(),ids=groupEntityIds.get(state.selected)||new Set();ids.forEach((id)=>(relationsByEntity.get(id)||[]).forEach((relation)=>relations.add(relation)));const findings=nodeIssues(state.selected),evidence=members.flatMap((member)=>member.evidence||[]).slice(0,8),unit=group.kind==="external"?"dependencies":"packages";panel.innerHTML="<div class=\"eyebrow\">Selected group</div><h2 class=\"detail-title\">"+esc(group.name)+"</h2><span class=\"tag\">"+esc(group.kind)+"</span><p class=\"path\">"+esc(group.path||"Derived from stable package identity and repository structure")+"</p><div class=\"detail-grid\"><div><b>"+members.length+"</b><span>"+unit+"</span></div><div><b>"+relations.size+"</b><span>relations</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Members</h3><ul class=\"list\">"+(members.length?members.slice(0,8).map((member)=>"<li>"+esc(label(member))+"</li>").join(""):"<li>No members recorded.</li>")+(members.length>8?"<li class=\"subtle\">+"+(members.length-8)+" more — choose Package level to inspect all.</li>":"")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"");return}if(!entity){panel.innerHTML="<p class=\"subtle\">Select a node in the map to inspect its evidence, connectivity, and findings.</p>";return}const relations=relationsByEntity.get(entity.id)||[],incoming=relations.filter((relation)=>relation.to===entity.id),outgoing=relations.filter((relation)=>relation.from===entity.id),findings=diagnosticsFor(entity.id),evidence=[...(entity.evidence||[]),...incoming.flatMap((relation)=>relation.evidence||[]),...outgoing.flatMap((relation)=>relation.evidence||[])].slice(0,8);panel.innerHTML="<div class=\"eyebrow\">Selected entity</div><h2 class=\"detail-title\">"+esc(label(entity))+"</h2><span class=\"tag\">"+esc(entity.kind)+"</span><p class=\"path\">"+esc(entity.path||entity.id)+"</p><div class=\"detail-grid\"><div><b>"+incoming.length+"</b><span>incoming</span></div><div><b>"+outgoing.length+"</b><span>outgoing</span></div><div><b>"+findings.length+"</b><span>findings</span></div><div><b>"+evidence.length+"</b><span>evidence items</span></div></div><h3>Evidence</h3><ul class=\"list\">"+(evidence.length?evidence.map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join(""):"<li>No evidence recorded.</li>")+"</ul>"+(findings.length?"<h3 style=\"margin-top:16px\">Attention</h3><ul class=\"list\">"+findings.slice(0,5).map((finding)=>"<li><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> "+esc(finding.code)+"</li>").join("")+"</ul>":"")};
|
|
6645
|
-
const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph");if(!model.nodes.length){svg.innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>";return}const ids=new Set(model.nodes.map((node)=>node.id)),incoming=new Map(model.nodes.map((node)=>[node.id,0])),outgoing=new Map(model.nodes.map((node)=>[node.id,[]]));
|
|
7114
|
+
const renderGraph=()=>{const model=graphModel(),svg=document.querySelector("#graph"),dense=model.edges.length>64,edges=dense?[...model.edges].sort((a,b)=>(b.health?1:0)-(a.health?1:0)||b.count-a.count||a.from.localeCompare(b.from)||a.to.localeCompare(b.to)).slice(0,64):model.edges;if(!model.nodes.length){svg.innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>";return}const ids=new Set(model.nodes.map((node)=>node.id)),incoming=new Map(model.nodes.map((node)=>[node.id,0])),outgoing=new Map(model.nodes.map((node)=>[node.id,[]]));edges.forEach((edge)=>{if(!ids.has(edge.from)||!ids.has(edge.to))return;incoming.set(edge.to,(incoming.get(edge.to)||0)+1);outgoing.get(edge.from).push(edge.to)});const rank=new Map(),work=[];model.nodes.filter((node)=>(incoming.get(node.id)||0)===0).sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id)).forEach((node)=>{rank.set(node.id,0);work.push(node)});for(let index=0;index<work.length;index++){const node=work[index];for(const next of outgoing.get(node.id)||[]){const nextRank=Math.max(rank.get(next)||0,(rank.get(node.id)||0)+1);rank.set(next,nextRank);if(!work.some((item)=>item.id===next))work.push(byId.get(next)||{id:next,name:next})}}model.nodes.forEach((node)=>{if(!rank.has(node.id))rank.set(node.id,0)});const columns=new Map();model.nodes.forEach((node)=>{const items=columns.get(rank.get(node.id))||[];items.push(node);columns.set(rank.get(node.id),items)});for(const items of columns.values())items.sort((a,b)=>label(a).localeCompare(label(b))||a.id.localeCompare(b.id));if(dense){columns.clear();model.nodes.forEach((node,index)=>{const column=Math.floor(index/8),items=columns.get(column)||[];items.push(node);columns.set(column,items)})}const maxRows=Math.max(1,...[...columns.values()].map((items)=>items.length)),cellWidth=190,cellHeight=82,pad=28,columnCount=Math.max(1,...columns.keys())+1,width=Math.max(620,columnCount*cellWidth+pad*2,Math.min(1000,svg.clientWidth*1.05)),height=Math.max(520,maxRows*cellHeight+pad*2);svg.setAttribute("viewBox","0 0 "+width+" "+height);svg.style.width=dense?Math.max(1000,width)+"px":"100%";svg.style.minWidth="0";svg.style.height="100%";document.querySelector(".map-wrap")?.classList.toggle("dense",dense);const positions=new Map();for(const [column,items] of columns)items.forEach((node,row)=>positions.set(node.id,{x:pad+column*cellWidth+78,y:pad+row*cellHeight+27}));const defs="<defs><marker id=\"arrow\" markerWidth=\"8\" markerHeight=\"8\" refX=\"7\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L0,6 L7,3 z\" fill=\"#789087\"/></marker></defs>",edgeMarkup=edges.map((edge)=>{const from=positions.get(edge.from),to=positions.get(edge.to);if(!from||!to)return "";const midX=(from.x+to.x)/2,midY=(from.y+to.y)/2;return "<g><line class=\"edge "+(edge.health?"alert":"")+"\" x1=\""+from.x+"\" y1=\""+from.y+"\" x2=\""+to.x+"\" y2=\""+to.y+"\" marker-end=\"url(#arrow)\"/><text class=\"edge-label\" x=\""+midX+"\" y=\""+midY+"\">"+esc(edge.count>1?edge.count+"×":"")+"</text></g>"}).join(""),nodes=model.nodes.map((node)=>{const pos=positions.get(node.id),issues=nodeIssues(node.id),degree=degreeMap(edges).get(node.id)||0,selected=state.selected===node.id?" selected":"",issue=issues.length?" issue":"",nodeLabel=label(node),nodeKind=node.kind==="domain"?(node.memberCount||0)+" packages":node.kind;return "<g class=\"graph-node"+selected+issue+"\" transform=\"translate("+(pos.x-78)+","+(pos.y-27)+")\"><title>"+esc(nodeLabel)+" · "+esc(nodeKind)+"</title><rect width=\"156\" height=\"54\" rx=\"8\" role=\"button\" tabindex=\"0\" aria-label=\""+esc(nodeLabel+" "+nodeKind)+"\" data-node=\""+esc(node.id)+"\"></rect><text class=\"node-label\" x=\"10\" y=\"19\">"+esc(short(nodeLabel,22))+"</text><text class=\"node-kind\" x=\"10\" y=\"34\">"+esc(short(nodeKind,22))+"</text><text class=\"node-count\" x=\"144\" y=\"19\" text-anchor=\"end\">"+(degree||"")+"</text></g>"}).join("");svg.innerHTML=defs+edgeMarkup+nodes;routeGraphEdges();applyMapTransform();document.querySelector("#map-note").textContent=(state.level==="overview"?"Bounded domain view. ":state.level+" view. ")+"Directional edges are aggregated from canonical relations; "+model.edges.length+" visible connection groups"+(dense?" (showing "+edges.length+" prioritized of "+model.edges.length+").":".");};
|
|
6646
7115
|
const findingMatches=(finding)=>{const query=state.query.toLowerCase();return(!query||[finding.id,finding.code,finding.message,...finding.entityIds,...finding.relationIds].join(" ").toLowerCase().includes(query))&&(!state.status||finding.status===state.status)&&(!state.severity||finding.severity===state.severity)};
|
|
6647
|
-
const findingScope=(finding)=>{const ids=[...(finding.entityIds||[])];(finding.relationIds||[]).forEach((id)=>{const relation=relationById.get(id);if(relation)ids.push(relation.from,relation.to)});return ids.map(groupFor).sort()[0]||"repository"};
|
|
7116
|
+
const findingScope=(finding)=>data.view?.diagnosticGroup?.[finding.id]?.[0]||(()=>{const ids=[...(finding.entityIds||[])];(finding.relationIds||[]).forEach((id)=>{const relation=relationById.get(id);if(relation)ids.push(relation.from,relation.to)});return ids.map(groupFor).sort()[0]||"repository"})();
|
|
6648
7117
|
const findingGroups=(findings)=>{const groups=new Map();findings.forEach((finding)=>{const scope=findingScope(finding),key=[scope,finding.code,finding.status,finding.severity].join("|"),group=groups.get(key)||{key,scope,code:finding.code,status:finding.status,severity:finding.severity,findings:[]};group.findings.push(finding);groups.set(key,group)});return[...groups.values()].sort((left,right)=>right.findings.length-left.findings.length||left.code.localeCompare(right.code)||left.key.localeCompare(right.key))};
|
|
6649
7118
|
const renderFinding=(finding)=>"<article class=\"finding\" id=\""+esc("diagnostic-"+finding.id.replace(/[^A-Za-z0-9_-]+/g,"-"))+"\"><div class=\"finding-head\"><h3>"+esc(finding.code)+"</h3><span><span class=\"tag "+esc(finding.severity)+"\">"+esc(finding.severity)+"</span> <span class=\"tag\">"+esc(finding.status)+"</span></span></div><p>"+esc(finding.message)+"</p>"+(finding.evidence.length?"<ul>"+finding.evidence.slice(0,4).map((item)=>"<li>"+esc(item.path+(item.lineStart?":"+item.lineStart:"")+(item.context?" — "+item.context:""))+"</li>").join("")+"</ul>":"")+(finding.remediation?"<p><strong>Next check:</strong> "+esc(finding.remediation)+"</p>":"")+"</article>";
|
|
6650
7119
|
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>"};
|
|
@@ -6652,11 +7121,11 @@ const renderCoverage=()=>{document.querySelector("#coverage-list").innerHTML=(da
|
|
|
6652
7121
|
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>"};
|
|
6653
7122
|
const scopePrompt=()=>{if((state.level==="module"||state.level==="file")&&!state.selected){document.querySelector("#map-note").textContent="Select an app or package to inspect this level.";document.querySelector("#graph").innerHTML="<text x=\"500\" y=\"270\" text-anchor=\"middle\" class=\"subtle\">Select an app or package to expand this view.</text>"}};
|
|
6654
7123
|
const findingsLoaded=()=>!lazyChunks.includes("chunks/findings.js")||loadedChunks.has("chunks/findings.js");
|
|
6655
|
-
const levelsLoaded=()=>!
|
|
7124
|
+
const levelsLoaded=()=>!hasLevelChunks||state.level==="overview"||activeLevelChunk===levelChunkName();
|
|
6656
7125
|
const setReportView=()=>{document.body.dataset.reportView={architecture:"architecture",drift:"insights",risks:"findings",evidence:"coverage"}[state.lens]||"architecture";ensureReportChrome()};
|
|
6657
|
-
const
|
|
6658
|
-
document.
|
|
6659
|
-
document.addEventListener("click",(event)=>{const
|
|
7126
|
+
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};
|
|
7127
|
+
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)};
|
|
7128
|
+
document.addEventListener("click",async(event)=>{const target=event.target.closest?.("[data-level],[data-lens],#load-findings");if(!target)return;if(target.id==="load-findings"){event.preventDefault();event.stopImmediatePropagation();await loadChunk("chunks/findings.js");render();return}if(target.dataset.level&&!levelsLoaded()){event.preventDefault();event.stopImmediatePropagation();state.level=target.dataset.level;await loadChunk(levelChunkName());render();return}if(target.dataset.lens&&target.dataset.lens!=="architecture"&&!findingsLoaded()){event.preventDefault();event.stopImmediatePropagation();state.lens=target.dataset.lens;state.findingGroup=null;await loadChunk("chunks/findings.js");render()}},true);
|
|
6660
7129
|
document.addEventListener("click",(event)=>{const crumb=event.target.closest?.("[data-breadcrumb-level]");if(!crumb)return;event.preventDefault();event.stopImmediatePropagation();state.level=crumb.dataset.breadcrumbLevel;state.selected=crumb.dataset.breadcrumbSelected||null;mapState.scale=1;mapState.x=0;mapState.y=0;render()},true);
|
|
6661
7130
|
document.addEventListener("input",async(event)=>{const target=event.target;if(target?.id!=="search"||findingsLoaded())return;event.stopImmediatePropagation();state.query=target.value;state.findingGroup=null;await loadChunk("chunks/findings.js");render()},true);
|
|
6662
7131
|
document.addEventListener("change",async(event)=>{const target=event.target;if(!["status","severity"].includes(target?.id)||findingsLoaded())return;event.stopImmediatePropagation();state[target.id]=target.value;state.findingGroup=null;await loadChunk("chunks/findings.js");render()},true);
|
|
@@ -6682,14 +7151,14 @@ hydrate();render();keepMobileGraphReadable();ensurePolicyNote();
|
|
|
6682
7151
|
var render = (input, options) => {
|
|
6683
7152
|
const { snapshot, report } = input;
|
|
6684
7153
|
const includeSnippets = options.includeSnippets === true;
|
|
6685
|
-
const data = reportData(snapshot, report, includeSnippets);
|
|
7154
|
+
const data = reportData(snapshot, report, includeSnippets, options.privacy);
|
|
6686
7155
|
const scriptBody = script.replace("${DATA}", options.dataExpression ?? embeddedJson(data));
|
|
6687
7156
|
const largeNote = snapshot.entities.length > 500 || report.diagnostics.length > 1e3 ? "Large snapshots are rendered from compact canonical data with progressive graph levels." : "The viewer starts with a grouped topology and expands into canonical entities on demand.";
|
|
6688
7157
|
const issueCount = report.diagnostics.filter((diagnostic2) => diagnostic2.severity === "error" || diagnostic2.severity === "warn").length;
|
|
6689
7158
|
const unsupportedCount = snapshot.coverage.filter((entry) => entry.status !== "complete").length;
|
|
6690
7159
|
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.";
|
|
6691
7160
|
const statusOptions = ["confirmed", "undocumented", "stale-or-unverified", "conflict", "unresolved", "not-analyzed"];
|
|
6692
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Doc Bridge \u2014 ${escapeHtml(
|
|
7161
|
+
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>`;
|
|
6693
7162
|
};
|
|
6694
7163
|
var parseReportInput = (input) => {
|
|
6695
7164
|
if (!input || typeof input !== "object") throw new Error("Input must contain snapshot and report artifacts.");
|
|
@@ -6700,31 +7169,186 @@ var parseReportInput = (input) => {
|
|
|
6700
7169
|
return { snapshot, report };
|
|
6701
7170
|
};
|
|
6702
7171
|
var chunkScript = (payload) => `globalThis.__DOC_BRIDGE_DATA__=Object.assign(globalThis.__DOC_BRIDGE_DATA__||{},${embeddedJson(payload)});`;
|
|
7172
|
+
var levelChunkScript = (payload) => `globalThis.__DOC_BRIDGE_LEVEL_PAYLOAD__=${embeddedJson(payload)};`;
|
|
7173
|
+
var detailChunkScript = (payload) => `globalThis.__DOC_BRIDGE_DETAIL_PAYLOAD__=${embeddedJson(payload)};`;
|
|
6703
7174
|
var byteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
6704
7175
|
var renderOfflineReportArtifact = (input, options = {}) => {
|
|
6705
7176
|
const parsed = parseReportInput(input);
|
|
6706
7177
|
const thresholdBytes = options.thresholdBytes ?? DEFAULT_LARGE_REPORT_THRESHOLD_BYTES;
|
|
6707
|
-
const renderOptions = { includeSnippets: options.includeSnippets === true, thresholdBytes };
|
|
7178
|
+
const renderOptions = { includeSnippets: options.includeSnippets === true, privacy: options.privacy ?? "private", thresholdBytes };
|
|
6708
7179
|
const singleFile = render(parsed, options);
|
|
6709
7180
|
if (byteLength(singleFile) <= thresholdBytes) {
|
|
6710
7181
|
const files2 = { "index.html": sha256NormalizedV1(singleFile) };
|
|
6711
|
-
const manifestPayload2 = { schemaVersion: 1, mode: "single-file", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: parsed.snapshot.sourceRevision, renderOptions, files: files2 };
|
|
7182
|
+
const manifestPayload2 = { schemaVersion: 1, mode: "single-file", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: options.privacy === "anonymized" ? "redacted" : parsed.snapshot.sourceRevision, renderOptions, files: files2 };
|
|
6712
7183
|
const manifest2 = JSON.stringify({ ...manifestPayload2, artifactHash: sha256NormalizedV1(manifestPayload2) }, null, 2);
|
|
6713
7184
|
return { mode: "single-file", indexHtml: singleFile, files: { "index.html": singleFile }, manifest: manifest2 };
|
|
6714
7185
|
}
|
|
6715
|
-
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true);
|
|
7186
|
+
const data = reportData(parsed.snapshot, parsed.report, options.includeSnippets === true, options.privacy);
|
|
7187
|
+
const entitiesById = new Map(data.entities.map((entity) => [entity.id, entity]));
|
|
7188
|
+
const packageEntities = data.entities.filter((entity) => entity.kind === "package");
|
|
7189
|
+
const packageIds = new Set(packageEntities.map((entity) => entity.id));
|
|
7190
|
+
const packageRelations = data.relations.filter((relation) => packageIds.has(relation.from) && packageIds.has(relation.to));
|
|
7191
|
+
const diagnosticGroup = Object.fromEntries(data.diagnostics.map((diagnostic2) => {
|
|
7192
|
+
const groups = /* @__PURE__ */ new Set();
|
|
7193
|
+
for (const entityId2 of diagnostic2.entityIds ?? []) {
|
|
7194
|
+
const groupId = data.view.entityGroup[entityId2];
|
|
7195
|
+
if (groupId) groups.add(groupId);
|
|
7196
|
+
}
|
|
7197
|
+
for (const relationId of diagnostic2.relationIds ?? []) {
|
|
7198
|
+
const relation = data.relations.find((candidate) => candidate.id === relationId);
|
|
7199
|
+
if (!relation) continue;
|
|
7200
|
+
for (const endpoint of [relation.from, relation.to]) {
|
|
7201
|
+
const groupId = data.view.entityGroup[endpoint];
|
|
7202
|
+
if (groupId) groups.add(groupId);
|
|
7203
|
+
}
|
|
7204
|
+
}
|
|
7205
|
+
return [diagnostic2.id, [...groups].sort()];
|
|
7206
|
+
}));
|
|
7207
|
+
const diagnosticRelationFindings = /* @__PURE__ */ new Map();
|
|
7208
|
+
for (const diagnostic2 of data.diagnostics) {
|
|
7209
|
+
for (const relationId of diagnostic2.relationIds ?? []) {
|
|
7210
|
+
const findings = diagnosticRelationFindings.get(relationId) ?? [];
|
|
7211
|
+
findings.push({ status: diagnostic2.status, severity: diagnostic2.severity });
|
|
7212
|
+
diagnosticRelationFindings.set(relationId, findings);
|
|
7213
|
+
}
|
|
7214
|
+
}
|
|
7215
|
+
const containsRelations = data.relations.filter((relation) => relation.kind === "contains");
|
|
7216
|
+
const collectScope = (roots) => {
|
|
7217
|
+
const ids = new Set(roots);
|
|
7218
|
+
let changed = true;
|
|
7219
|
+
while (changed) {
|
|
7220
|
+
changed = false;
|
|
7221
|
+
for (const relation of containsRelations) {
|
|
7222
|
+
if (ids.has(relation.from) && !ids.has(relation.to)) {
|
|
7223
|
+
ids.add(relation.to);
|
|
7224
|
+
changed = true;
|
|
7225
|
+
}
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
return ids;
|
|
7229
|
+
};
|
|
7230
|
+
const scopedPayload = (ids) => ({
|
|
7231
|
+
entities: data.entities.filter((entity) => ids.has(entity.id)),
|
|
7232
|
+
relations: data.relations.filter((relation) => ids.has(relation.from) && ids.has(relation.to))
|
|
7233
|
+
});
|
|
7234
|
+
const packagePayload = (ids) => ({
|
|
7235
|
+
entities: data.entities.filter((entity) => ids.has(entity.id) && entity.kind === "package"),
|
|
7236
|
+
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")
|
|
7237
|
+
});
|
|
7238
|
+
const compactPayload = (payload) => ({
|
|
7239
|
+
entities: payload.entities.map(({ evidence: _evidence, anchor: _anchor, provenance: _provenance, ...entity }) => entity),
|
|
7240
|
+
relations: payload.relations.map(({ evidence: _evidence, provenance: _provenance, ...relation }) => relation)
|
|
7241
|
+
});
|
|
7242
|
+
const levelFiles = {};
|
|
7243
|
+
const detailFiles = {};
|
|
7244
|
+
for (const [groupId, group] of Object.entries(data.view.groups)) {
|
|
7245
|
+
const ids = new Set(group.members.filter((id) => entitiesById.get(id)?.kind === "package"));
|
|
7246
|
+
const chunkName = data.view.levelChunks.groups[groupId];
|
|
7247
|
+
if (chunkName) levelFiles[chunkName] = levelChunkScript(packagePayload(ids));
|
|
7248
|
+
}
|
|
7249
|
+
const packageIdList = packageEntities.map((entity) => entity.id);
|
|
7250
|
+
const packageIdSet = new Set(packageIdList);
|
|
7251
|
+
levelFiles[data.view.levelChunks.default] = levelChunkScript(scopedPayload(packageIdSet));
|
|
7252
|
+
const sortedPackageIds = [...packageIds].sort();
|
|
7253
|
+
for (const [index, packageId2] of sortedPackageIds.entries()) {
|
|
7254
|
+
const chunkName = data.view.levelChunks.packages[index];
|
|
7255
|
+
const packageScope = scopedPayload(collectScope([packageId2]));
|
|
7256
|
+
if (chunkName) levelFiles[chunkName] = levelChunkScript(compactPayload(packageScope));
|
|
7257
|
+
const detailName = chunkName?.replace("chunks/levels-package-", "chunks/details-");
|
|
7258
|
+
if (detailName) detailFiles[detailName] = detailChunkScript(packageScope);
|
|
7259
|
+
}
|
|
7260
|
+
const overviewView = {
|
|
7261
|
+
...data.view,
|
|
7262
|
+
entityGroup: Object.fromEntries(Object.entries(data.view.entityGroup).filter(([id]) => data.entities.find((entity) => entity.id === id)?.kind === "package")),
|
|
7263
|
+
diagnosticGroup,
|
|
7264
|
+
diagnosticRelationFindings: Object.fromEntries(diagnosticRelationFindings)
|
|
7265
|
+
};
|
|
6716
7266
|
const files = {
|
|
6717
|
-
"chunks/overview.js": chunkScript({ project: data.project, revision: data.revision, revisionKind: data.revisionKind, snapshotHash: data.snapshotHash, reportHash: data.reportHash, configurationHash: data.configurationHash, pipelineVersion: data.pipelineVersion, analyzerVersions: data.analyzerVersions, coverage: data.coverage, diagnosticCount: data.diagnosticCount, diagnosticSummary: data.diagnosticSummary, requiredRelationKinds: data.requiredRelationKinds,
|
|
6718
|
-
|
|
7267
|
+
"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 }),
|
|
7268
|
+
...levelFiles,
|
|
7269
|
+
...detailFiles,
|
|
6719
7270
|
"chunks/findings.js": chunkScript({ diagnostics: data.diagnostics })
|
|
6720
7271
|
};
|
|
6721
|
-
const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_LAZY_CHUNKS__
|
|
7272
|
+
const indexHtml = render(parsed, { ...options, dataExpression: "globalThis.__DOC_BRIDGE_DATA__", dataScripts: `<script>globalThis.__DOC_BRIDGE_HAS_LEVEL_CHUNKS__=true;globalThis.__DOC_BRIDGE_LAZY_CHUNKS__=["chunks/findings.js"]</script><script src="chunks/overview.js"></script>` });
|
|
6722
7273
|
const fileHashes = Object.fromEntries(Object.entries({ ...files, "index.html": indexHtml }).map(([file, content]) => [file, sha256NormalizedV1(content)]));
|
|
6723
|
-
const manifestPayload = { schemaVersion: 1, mode: "directory", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: parsed.snapshot.sourceRevision, renderOptions, files: fileHashes };
|
|
7274
|
+
const manifestPayload = { schemaVersion: 1, mode: "directory", generatedBy: "@agentskit/doc-bridge", snapshotHash: parsed.snapshot.contentHash, reportHash: parsed.report.contentHash, configurationHash: parsed.snapshot.configurationHash, sourceRevision: options.privacy === "anonymized" ? "redacted" : parsed.snapshot.sourceRevision, renderOptions, files: fileHashes };
|
|
6724
7275
|
const manifest = JSON.stringify({ ...manifestPayload, artifactHash: sha256NormalizedV1(manifestPayload) }, null, 2);
|
|
6725
7276
|
return { mode: "directory", indexHtml, files: { ...files, "index.html": indexHtml }, manifest };
|
|
6726
7277
|
};
|
|
6727
7278
|
|
|
7279
|
+
// src/metrics/benchmark.ts
|
|
7280
|
+
import { z as z9 } from "zod";
|
|
7281
|
+
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
7282
|
+
var stringList = z9.array(z9.string().min(1).max(512)).max(1e5);
|
|
7283
|
+
var benchmarkSets = z9.object({
|
|
7284
|
+
entities: stringList.default([]),
|
|
7285
|
+
relations: stringList.default([]),
|
|
7286
|
+
findings: stringList.default([])
|
|
7287
|
+
}).strict();
|
|
7288
|
+
var BenchmarkFixtureV1Schema = z9.object({
|
|
7289
|
+
schemaVersion: z9.literal(BENCHMARK_SCHEMA_VERSION),
|
|
7290
|
+
supported: benchmarkSets,
|
|
7291
|
+
excluded: benchmarkSets.optional()
|
|
7292
|
+
}).strict();
|
|
7293
|
+
var unique = (values) => new Set(values);
|
|
7294
|
+
var ratio = (numerator, denominator) => denominator === 0 ? 1 : numerator / denominator;
|
|
7295
|
+
var setMetrics = (actualValues, expectedValues, excludedValues) => {
|
|
7296
|
+
const excluded = unique(excludedValues);
|
|
7297
|
+
const activeActualValues = actualValues.filter((value) => !excluded.has(value));
|
|
7298
|
+
const actual = unique(activeActualValues);
|
|
7299
|
+
const expected = unique(expectedValues.filter((value) => !excluded.has(value)));
|
|
7300
|
+
const truePositives = [...actual].filter((value) => expected.has(value)).length;
|
|
7301
|
+
const falsePositives = actual.size - truePositives;
|
|
7302
|
+
const falseNegatives = expected.size - truePositives;
|
|
7303
|
+
return {
|
|
7304
|
+
truePositives,
|
|
7305
|
+
falsePositives,
|
|
7306
|
+
falseNegatives,
|
|
7307
|
+
precision: ratio(truePositives, actual.size),
|
|
7308
|
+
recall: ratio(truePositives, expected.size),
|
|
7309
|
+
duplicateCount: activeActualValues.length - actual.size
|
|
7310
|
+
};
|
|
7311
|
+
};
|
|
7312
|
+
var benchmarkFixture = (fixture) => BenchmarkFixtureV1Schema.parse(fixture);
|
|
7313
|
+
var measureBenchmark = (observation, fixture, thresholds = {}) => {
|
|
7314
|
+
const excluded = fixture.excluded ?? { entities: [], relations: [], findings: [] };
|
|
7315
|
+
const quality = {
|
|
7316
|
+
entities: setMetrics(observation.entities, fixture.supported.entities, excluded.entities),
|
|
7317
|
+
relations: setMetrics(observation.relations, fixture.supported.relations, excluded.relations),
|
|
7318
|
+
findings: setMetrics(observation.findings, fixture.supported.findings, excluded.findings)
|
|
7319
|
+
};
|
|
7320
|
+
const actualEntities = unique(observation.entities);
|
|
7321
|
+
const evidenced = unique(observation.evidenced ?? []);
|
|
7322
|
+
const evidenceRatio = ratio([...actualEntities].filter((id) => evidenced.has(id)).length, actualEntities.size);
|
|
7323
|
+
const findingDensity = ratio(unique(observation.findings).size, actualEntities.size);
|
|
7324
|
+
const precisionThreshold = thresholds.precision ?? 0.95;
|
|
7325
|
+
const recallThreshold = thresholds.recall ?? 1;
|
|
7326
|
+
const regressions = [];
|
|
7327
|
+
for (const [name, metrics] of Object.entries(quality)) {
|
|
7328
|
+
if (metrics.precision < precisionThreshold) regressions.push(`${name}.precision below ${precisionThreshold}`);
|
|
7329
|
+
if (metrics.recall < recallThreshold) regressions.push(`${name}.recall below ${recallThreshold}`);
|
|
7330
|
+
}
|
|
7331
|
+
return {
|
|
7332
|
+
schemaVersion: BENCHMARK_SCHEMA_VERSION,
|
|
7333
|
+
quality,
|
|
7334
|
+
evidenceRatio,
|
|
7335
|
+
findingDensity,
|
|
7336
|
+
findingCategoryDistribution: Object.fromEntries(Object.entries(observation.findingCategories ?? {}).sort(([a], [b]) => a.localeCompare(b))),
|
|
7337
|
+
excludedCaseCount: excluded.entities.length + excluded.relations.length + excluded.findings.length,
|
|
7338
|
+
excludedCaseIds: { entities: excluded.entities.length, relations: excluded.relations.length, findings: excluded.findings.length },
|
|
7339
|
+
thresholds: { precision: precisionThreshold, recall: recallThreshold },
|
|
7340
|
+
regressions
|
|
7341
|
+
};
|
|
7342
|
+
};
|
|
7343
|
+
var formatBenchmarkText = (result) => [
|
|
7344
|
+
`Precision: entities ${result.quality.entities.precision.toFixed(3)}, relations ${result.quality.relations.precision.toFixed(3)}, findings ${result.quality.findings.precision.toFixed(3)}`,
|
|
7345
|
+
`Recall: entities ${result.quality.entities.recall.toFixed(3)}, relations ${result.quality.relations.recall.toFixed(3)}, findings ${result.quality.findings.recall.toFixed(3)}`,
|
|
7346
|
+
`Evidence ratio: ${result.evidenceRatio.toFixed(3)}`,
|
|
7347
|
+
`Finding density: ${result.findingDensity.toFixed(3)}`,
|
|
7348
|
+
`Excluded cases: ${result.excludedCaseCount}`,
|
|
7349
|
+
`Regressions: ${result.regressions.length ? result.regressions.join("; ") : "none"}`
|
|
7350
|
+
].join("\n");
|
|
7351
|
+
|
|
6728
7352
|
// src/cli/program.ts
|
|
6729
7353
|
var usage = `ak-docs \u2014 human\u2194agent documentation bridge (@agentskit/doc-bridge)
|
|
6730
7354
|
|
|
@@ -6734,6 +7358,7 @@ Core (no API key):
|
|
|
6734
7358
|
ak-docs doctor [--text] [--badge] [--write-badge]
|
|
6735
7359
|
ak-docs index [--watch]
|
|
6736
7360
|
ak-docs discover [--text|--json]
|
|
7361
|
+
ak-docs benchmark <fixture.json> <observation.json> [--text|--json]
|
|
6737
7362
|
ak-docs scan | reconcile | check | map [--text|--json] [--html] [--report-threshold <bytes>]
|
|
6738
7363
|
ak-docs fix propose links|normalize <artifact> [--output <file>]
|
|
6739
7364
|
ak-docs fix approve|apply <proposal.json> [--by <name>]
|
|
@@ -6804,6 +7429,7 @@ var parseArgs = (argv) => {
|
|
|
6804
7429
|
else if (positional[0] === "playbook") command = "playbook";
|
|
6805
7430
|
else if (positional[0] === "registry") command = "registry";
|
|
6806
7431
|
else if (positional[0] === "discover") command = "discover";
|
|
7432
|
+
else if (positional[0] === "benchmark") command = "benchmark";
|
|
6807
7433
|
else if (positional[0] === "scan") command = "scan";
|
|
6808
7434
|
else if (positional[0] === "reconcile") command = "reconcile";
|
|
6809
7435
|
else if (positional[0] === "check") command = "check";
|
|
@@ -7019,33 +7645,43 @@ var diagnosticNextCommands = (config, result) => {
|
|
|
7019
7645
|
"ak-docs index"
|
|
7020
7646
|
];
|
|
7021
7647
|
};
|
|
7022
|
-
var workflowOptions = (root, config, sourceRevision2, stage, handlers) => ({
|
|
7648
|
+
var workflowOptions = (root, config, sourceRevision2, stage, handlers, versions) => ({
|
|
7023
7649
|
root,
|
|
7024
7650
|
...config.workflow?.stateDir ? { stateDir: config.workflow.stateDir } : {},
|
|
7025
7651
|
sourceRevision: sourceRevision2,
|
|
7026
7652
|
configurationHash: sha256NormalizedV1(config),
|
|
7027
|
-
toolVersion:
|
|
7653
|
+
toolVersion: PACKAGE_VERSION,
|
|
7654
|
+
...versions?.pipelineVersion ? { pipelineVersion: versions.pipelineVersion } : {},
|
|
7655
|
+
...versions?.analyzerVersions ? { analyzerVersions: versions.analyzerVersions } : {},
|
|
7028
7656
|
stage,
|
|
7029
7657
|
handlers
|
|
7030
7658
|
});
|
|
7031
7659
|
var scanWorkflow = (root, config) => {
|
|
7032
7660
|
const discovered = discoverRepository({ root, config });
|
|
7033
|
-
|
|
7034
|
-
|
|
7661
|
+
const versions = { pipelineVersion: discovered.pipelineVersion, analyzerVersions: discovered.analyzerVersions };
|
|
7662
|
+
runWorkflow(workflowOptions(root, config, discovered.sourceRevision, "collect", { collect: () => discovered }, versions));
|
|
7663
|
+
return runWorkflow(workflowOptions(root, config, discovered.sourceRevision, "normalize", { normalize: ({ input }) => input }, versions));
|
|
7035
7664
|
};
|
|
7036
7665
|
var documentationInputs = (root, snapshot) => snapshot.entities.filter((entity) => entity.kind === "document" && entity.path).map((entity) => ({ path: entity.path, content: readFileSync21(resolve18(root, entity.path), "utf8") }));
|
|
7037
7666
|
var reconcileWorkflow = (root, config) => {
|
|
7038
7667
|
const scanned = scanWorkflow(root, config);
|
|
7039
7668
|
const snapshot = parseDiscoverySnapshot(loadWorkflowStepOutput(scanned.stateDir, "normalize"));
|
|
7040
|
-
const declared = applyDocumentationDeclarations(snapshot, documentationInputs(root, snapshot)
|
|
7041
|
-
|
|
7042
|
-
|
|
7669
|
+
const declared = applyDocumentationDeclarations(snapshot, documentationInputs(root, snapshot), {
|
|
7670
|
+
agentRoot: config.corpus.agent.root
|
|
7671
|
+
}).snapshot;
|
|
7672
|
+
const report = reconcileKnowledge(snapshot, declared, {
|
|
7673
|
+
...config.reconciliation?.scope === void 0 ? {} : { scope: config.reconciliation.scope },
|
|
7674
|
+
...config.reconciliation?.requiredRelationKinds === void 0 ? {} : { requiredRelationKinds: config.reconciliation.requiredRelationKinds },
|
|
7675
|
+
...config.reconciliation?.includeOrphanedDocuments === void 0 ? {} : { includeOrphanedDocuments: config.reconciliation.includeOrphanedDocuments }
|
|
7676
|
+
});
|
|
7677
|
+
return runWorkflow(workflowOptions(root, config, snapshot.sourceRevision, "reconcile", { reconcile: () => report }, { pipelineVersion: snapshot.pipelineVersion, analyzerVersions: snapshot.analyzerVersions }));
|
|
7043
7678
|
};
|
|
7044
7679
|
var checkWorkflow = (root, config) => {
|
|
7045
7680
|
const reconciled = reconcileWorkflow(root, config);
|
|
7046
7681
|
const report = parseReconciliationReport(loadWorkflowStepOutput(reconciled.stateDir, "reconcile"));
|
|
7047
|
-
const
|
|
7048
|
-
|
|
7682
|
+
const versions = { pipelineVersion: report.pipelineVersion, analyzerVersions: report.analyzerVersions };
|
|
7683
|
+
runWorkflow(workflowOptions(root, config, report.sourceRevision, "evaluate", { evaluate: () => evaluateRules(report, { ...config.rules ? { config: config.rules } : {} }) }, versions));
|
|
7684
|
+
return runWorkflow(workflowOptions(root, config, report.sourceRevision, "report", { report: ({ input }) => input }, versions));
|
|
7049
7685
|
};
|
|
7050
7686
|
var workflowOutput = (result) => {
|
|
7051
7687
|
const snapshot = (() => {
|
|
@@ -7145,7 +7781,7 @@ var runWorkflowCommand = (command, flags, configPath, argv) => {
|
|
|
7145
7781
|
const thresholdValue = optionValues(argv, "--report-threshold")[0];
|
|
7146
7782
|
const thresholdBytes = thresholdValue === void 0 ? void 0 : Number(thresholdValue);
|
|
7147
7783
|
if (thresholdBytes !== void 0 && (!Number.isSafeInteger(thresholdBytes) || thresholdBytes < 1)) throw new Error("--report-threshold must be a positive integer.");
|
|
7148
|
-
const artifact2 = renderOfflineReportArtifact({ snapshot, report }, { ...thresholdBytes === void 0 ? {} : { thresholdBytes } });
|
|
7784
|
+
const artifact2 = renderOfflineReportArtifact({ snapshot, report }, { ...config.report?.privacy ? { privacy: config.report.privacy } : {}, ...thresholdBytes === void 0 ? {} : { thresholdBytes } });
|
|
7149
7785
|
output.htmlPath = writeReportArtifact(htmlPath, artifact2);
|
|
7150
7786
|
output.htmlMode = artifact2.mode;
|
|
7151
7787
|
}
|
|
@@ -7473,6 +8109,26 @@ var runCli = (argv) => {
|
|
|
7473
8109
|
return 0;
|
|
7474
8110
|
} catch (error) {
|
|
7475
8111
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
8112
|
+
`);
|
|
8113
|
+
return 2;
|
|
8114
|
+
}
|
|
8115
|
+
}
|
|
8116
|
+
if (command === "benchmark") {
|
|
8117
|
+
const fixturePath2 = positional[1];
|
|
8118
|
+
const observationPath = positional[2];
|
|
8119
|
+
if (!fixturePath2 || !observationPath) {
|
|
8120
|
+
process.stderr.write("Usage: ak-docs benchmark <fixture.json> <observation.json> [--text|--json]\n");
|
|
8121
|
+
return 1;
|
|
8122
|
+
}
|
|
8123
|
+
try {
|
|
8124
|
+
const fixture = benchmarkFixture(JSON.parse(readFileSync21(resolve18(fixturePath2), "utf8")));
|
|
8125
|
+
const observation = JSON.parse(readFileSync21(resolve18(observationPath), "utf8"));
|
|
8126
|
+
const result = measureBenchmark(observation, fixture);
|
|
8127
|
+
if (flags.has("--text")) writeLines([formatBenchmarkText(result)]);
|
|
8128
|
+
else writeJson2(result);
|
|
8129
|
+
return result.regressions.length ? 1 : 0;
|
|
8130
|
+
} catch (error) {
|
|
8131
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
7476
8132
|
`);
|
|
7477
8133
|
return 2;
|
|
7478
8134
|
}
|