@titan-design/code-graph 0.10.0 → 0.11.0
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/README.md +6 -0
- package/dist/index.d.ts +76 -1
- package/dist/index.js +143 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -281,6 +281,12 @@ file is dropped from coupling too. `symbolSetHash({ unitId, symbolIds }, footpri
|
|
|
281
281
|
doc unit's sorted `[symbolId, footprint.hash]` pairs, so it ignores member order and changes when
|
|
282
282
|
a member is added, removed or changed.
|
|
283
283
|
|
|
284
|
+
`diffFootprints(store, { fromSnapshotId, toSnapshotId, ...options })` compares the footprints of
|
|
285
|
+
two snapshots. From-side ids follow the alias chain first, and a symbol under a moved file follows
|
|
286
|
+
its file, so a move reads as `changed` with `reasons: ["renamed"]` alone when its footprint held.
|
|
287
|
+
Each other change is `added`, `removed`, or `changed` with the differing parts (`signature`,
|
|
288
|
+
`consumers`, `coupling`). `files` rolls the changes up to their declaring files, never to consumers.
|
|
289
|
+
|
|
284
290
|
`scripts/dag-check-self.mjs` in the repo root runs this repo's DAG check on this engine
|
|
285
291
|
instead of codewatch's CLI.
|
|
286
292
|
|
package/dist/index.d.ts
CHANGED
|
@@ -307,8 +307,12 @@ declare class TsMorphGraphExtractor implements Extractor<GraphFragment> {
|
|
|
307
307
|
private readonly repoRoot;
|
|
308
308
|
private readonly tsConfigPath?;
|
|
309
309
|
private project?;
|
|
310
|
+
private readonly ownsProject;
|
|
311
|
+
private readonly retired;
|
|
310
312
|
constructor(options: TsMorphGraphExtractorOptions);
|
|
311
313
|
extract(file: ParsedFile): GraphFragment[];
|
|
314
|
+
private retire;
|
|
315
|
+
private extractFrom;
|
|
312
316
|
private ensureProject;
|
|
313
317
|
private loadSourceFile;
|
|
314
318
|
private buildFileAndModuleNodes;
|
|
@@ -1423,6 +1427,27 @@ interface GraphDiffSummary {
|
|
|
1423
1427
|
removedEdges: number;
|
|
1424
1428
|
metricChanges: number;
|
|
1425
1429
|
}
|
|
1430
|
+
/** Why a symbol present in both snapshots changed; `renamed` alone means its footprint held. */
|
|
1431
|
+
type FootprintChangeReason = "signature" | "consumers" | "coupling" | "renamed";
|
|
1432
|
+
interface FootprintChange {
|
|
1433
|
+
/** The to-snapshot id; a removed symbol keeps its from-snapshot id. */
|
|
1434
|
+
symbolId: string;
|
|
1435
|
+
/** The from-snapshot id, set only when it differs from `symbolId`. */
|
|
1436
|
+
previousId?: string;
|
|
1437
|
+
status: "added" | "removed" | "changed";
|
|
1438
|
+
/** Empty for an added or removed symbol. */
|
|
1439
|
+
reasons: FootprintChangeReason[];
|
|
1440
|
+
/** The declaring file, in the to-snapshot's id space where it still exists there. */
|
|
1441
|
+
fileId: string;
|
|
1442
|
+
}
|
|
1443
|
+
interface FootprintDiff {
|
|
1444
|
+
fromSnapshotId: number;
|
|
1445
|
+
toSnapshotId: number;
|
|
1446
|
+
/** Sorted by `symbolId`; an unchanged symbol is absent. */
|
|
1447
|
+
changes: FootprintChange[];
|
|
1448
|
+
/** Sorted distinct declaring files of `changes`; consumer files never appear here. */
|
|
1449
|
+
files: string[];
|
|
1450
|
+
}
|
|
1426
1451
|
interface GraphDiff {
|
|
1427
1452
|
summary: GraphDiffSummary;
|
|
1428
1453
|
addedNodes: GraphNode[];
|
|
@@ -1511,6 +1536,56 @@ declare function computeFootprints(graph: FootprintGraph, options?: FootprintOpt
|
|
|
1511
1536
|
*/
|
|
1512
1537
|
declare function symbolSetHash(unit: FootprintUnit, footprints: ReadonlyMap<string, SymbolFootprint>): string;
|
|
1513
1538
|
|
|
1539
|
+
interface DiffFootprintsOptions extends FootprintOptions {
|
|
1540
|
+
fromSnapshotId: number;
|
|
1541
|
+
toSnapshotId: number;
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Symbols whose documentable footprint differs between two snapshots. From-snapshot ids
|
|
1545
|
+
* follow the alias chain first, so a moved file reads as `renamed`, not a remove plus an add.
|
|
1546
|
+
*/
|
|
1547
|
+
declare function diffFootprints(store: CodeGraphStore, options: DiffFootprintsOptions): FootprintDiff;
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* What a consumer generated a unit's doc from. Provenance lives with the consumer, not the
|
|
1551
|
+
* store, so it survives snapshot prune.
|
|
1552
|
+
*/
|
|
1553
|
+
interface UnitProvenance {
|
|
1554
|
+
unitId: string;
|
|
1555
|
+
/** The to-snapshot's commit hash; null for a working-tree snapshot. */
|
|
1556
|
+
commit: string | null;
|
|
1557
|
+
symbolSetHash: string;
|
|
1558
|
+
/** The generator the consumer used; the gate records it but never calls one. */
|
|
1559
|
+
model: string | null;
|
|
1560
|
+
}
|
|
1561
|
+
interface UnitProvenanceInput {
|
|
1562
|
+
unit: FootprintUnit;
|
|
1563
|
+
footprints: ReadonlyMap<string, SymbolFootprint>;
|
|
1564
|
+
snapshot: Pick<SnapshotRow, "commitHash">;
|
|
1565
|
+
model?: string | null;
|
|
1566
|
+
}
|
|
1567
|
+
type RegenerateReason = "new" | "changed";
|
|
1568
|
+
interface UnitRegeneration {
|
|
1569
|
+
unitId: string;
|
|
1570
|
+
reason: RegenerateReason;
|
|
1571
|
+
}
|
|
1572
|
+
interface GateUnitsInput {
|
|
1573
|
+
prior: readonly UnitProvenance[];
|
|
1574
|
+
units: readonly FootprintUnit[];
|
|
1575
|
+
footprints: ReadonlyMap<string, SymbolFootprint>;
|
|
1576
|
+
}
|
|
1577
|
+
/** An empty `regenerate` means the caller makes no LLM call. */
|
|
1578
|
+
interface GateResult {
|
|
1579
|
+
regenerate: UnitRegeneration[];
|
|
1580
|
+
skip: string[];
|
|
1581
|
+
/** Prior units that no longer exist. */
|
|
1582
|
+
orphaned: string[];
|
|
1583
|
+
}
|
|
1584
|
+
/** A fresh provenance record for one unit at the given snapshot. */
|
|
1585
|
+
declare function unitProvenance(input: UnitProvenanceInput): UnitProvenance;
|
|
1586
|
+
/** Splits units into those whose symbol-set hash moved since the prior record and those that did not. */
|
|
1587
|
+
declare function gateUnits(input: GateUnitsInput): GateResult;
|
|
1588
|
+
|
|
1514
1589
|
interface EmbeddableSymbol {
|
|
1515
1590
|
id: string;
|
|
1516
1591
|
name: string;
|
|
@@ -1727,4 +1802,4 @@ declare function getConventionMap(store: CodeGraphStore, snapshotId: number, mod
|
|
|
1727
1802
|
*/
|
|
1728
1803
|
declare function findConventions(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, model: string, opts?: FindConventionsOptions): Promise<ConventionQueryResult>;
|
|
1729
1804
|
|
|
1730
|
-
export { ALIAS_BASE_ATTR, ALL_ROLES, type AliasChain, type AliasChainInput, type AliasChainOptions, type AliasLoader, type AliasResolution, COMMUNITY_SUMMARY_NAMESPACE, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, type CheckResult, type CheckRule, type CheckRulesFile, type CheckSnapshotOptions, type CheckSnapshotResult, type CheckViolation, CodeGraphStore, type ConventionArea, type ConventionCorpus, type ConventionCoverage, type ConventionMap, type ConventionMatch, type ConventionOptions, type ConventionQueryResult, type ConventionSymbol, DEAD_CODE_METRIC_NAMES, DEFAULT_CHURN_WINDOWS, DEFAULT_OUTLIER_MIN_SAMPLE, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffSnapshotsOptions, EXCEPTION_METRIC_NAMES, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type ExternalDiagnostic, FINDING_DDL, type FileFingerprint, type FindConventionsOptions, type FindSimilarOptions, type Finding, type FindingKeyInput, type FootprintGraph, type FootprintOptions, type FootprintParts, type FootprintUnit, type ForbidImportRule, GROWTH_RISK_METRIC_NAMES, type GraphDiff, type GraphDiffSummary, type GraphEdge, type GraphFragment, type GraphMetric, type GraphNode, type HistoryMetricsOptions, INDEX_VERSION, type IdAlias, type IdAliasReason, type IndexOptions, type IndexResult, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LayeredDepsRule, type LineSpan, type Lineage, type LineageSnapshot, type LineageStep, type LinkMethod, type LinkTestsOptions, type LoadedHistory, METRIC_CATALOGUE, MIGRATIONS, type MemberInfo, type MetricAbsence, type MetricAggregate, type MetricDelta, type MetricDescriptor, type MetricDirection, type MetricMaxRule, type MetricMinRule, type MetricOutlierRule, type MetricProductMaxRule, type MetricRollup, type MetricSource, type MetricUnit, type NoInternalOnlyBarrelsRule, type NodeKind, type NodeRename, type NodeRole, type PackageFlag, type PackageLayer, type PackageStats, type PageRankOptions, type PageRankResult, type PageRankRow, type PairCoupling, type PairFlag, type ParamInfo, type PartitionQualityInput, type PartitionQualityResult, type PriorSnapshotOptions, type PruneOptions, type PrunePlan, type PruneResult, PythonGraphExtractor, type ReadFile, type ReferenceEdgeLite, type RelevanceOptions, type ResolveAliasOptions, type ReuseBasis, type RuleStore, type RunChecksOptions, SCHEMA_VERSION, SNAPSHOT_SCOPED_TABLES, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, SYMBOL_METRIC_NAMES, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type StoredFinding, type StoredVerdict, type SummarizeConventionsResult, type Summarizer, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolFootprint, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type ValidateRulesOptions, type VerdictCitation, type VerdictLabel, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildConventionAreas, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, carryForwardVerdicts, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, computeFootprints, computeGrowthRiskMetrics, computeMetrics, computePageRank, computePartitionQuality, computeRecencyWindows, computeRelevance, computeRoleHints, computeSourceMetrics, computeSymbolConsumers, computeSymbolCoupling, computeTestCoverageOwnership, createAliasChain, defaultTargetCount, describeMetric, describeMetrics, detectCommunities, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, externalToFinding, fileId, findConventions, findSimilarCapability, findingKey, getConventionMap, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, hashText, indexPaths, invertBuckets, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, keyFindings, lineagePath, linkTestsToSources, listEdges, listEdgesTouching, listEmbeddableSymbols, listFindings, listMetrics, listMetricsForNode, listNodes, listVerdicts, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, matchesAny, moduleId, normalizeFlaggedText, openCodeGraph, packageId, parentModuleId, parseSymbolId, patternToRegex, percentileOf, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, saveFindings, saveVerdicts, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, symbolSetHash, testCoverageCountMetrics, toFindings, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };
|
|
1805
|
+
export { ALIAS_BASE_ATTR, ALL_ROLES, type AliasChain, type AliasChainInput, type AliasChainOptions, type AliasLoader, type AliasResolution, COMMUNITY_SUMMARY_NAMESPACE, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, type CheckResult, type CheckRule, type CheckRulesFile, type CheckSnapshotOptions, type CheckSnapshotResult, type CheckViolation, CodeGraphStore, type ConventionArea, type ConventionCorpus, type ConventionCoverage, type ConventionMap, type ConventionMatch, type ConventionOptions, type ConventionQueryResult, type ConventionSymbol, DEAD_CODE_METRIC_NAMES, DEFAULT_CHURN_WINDOWS, DEFAULT_OUTLIER_MIN_SAMPLE, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffFootprintsOptions, type DiffSnapshotsOptions, EXCEPTION_METRIC_NAMES, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type ExternalDiagnostic, FINDING_DDL, type FileFingerprint, type FindConventionsOptions, type FindSimilarOptions, type Finding, type FindingKeyInput, type FootprintChange, type FootprintChangeReason, type FootprintDiff, type FootprintGraph, type FootprintOptions, type FootprintParts, type FootprintUnit, type ForbidImportRule, GROWTH_RISK_METRIC_NAMES, type GateResult, type GateUnitsInput, type GraphDiff, type GraphDiffSummary, type GraphEdge, type GraphFragment, type GraphMetric, type GraphNode, type HistoryMetricsOptions, INDEX_VERSION, type IdAlias, type IdAliasReason, type IndexOptions, type IndexResult, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LayeredDepsRule, type LineSpan, type Lineage, type LineageSnapshot, type LineageStep, type LinkMethod, type LinkTestsOptions, type LoadedHistory, METRIC_CATALOGUE, MIGRATIONS, type MemberInfo, type MetricAbsence, type MetricAggregate, type MetricDelta, type MetricDescriptor, type MetricDirection, type MetricMaxRule, type MetricMinRule, type MetricOutlierRule, type MetricProductMaxRule, type MetricRollup, type MetricSource, type MetricUnit, type NoInternalOnlyBarrelsRule, type NodeKind, type NodeRename, type NodeRole, type PackageFlag, type PackageLayer, type PackageStats, type PageRankOptions, type PageRankResult, type PageRankRow, type PairCoupling, type PairFlag, type ParamInfo, type PartitionQualityInput, type PartitionQualityResult, type PriorSnapshotOptions, type PruneOptions, type PrunePlan, type PruneResult, PythonGraphExtractor, type ReadFile, type ReferenceEdgeLite, type RegenerateReason, type RelevanceOptions, type ResolveAliasOptions, type ReuseBasis, type RuleStore, type RunChecksOptions, SCHEMA_VERSION, SNAPSHOT_SCOPED_TABLES, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, SYMBOL_METRIC_NAMES, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type StoredFinding, type StoredVerdict, type SummarizeConventionsResult, type Summarizer, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolFootprint, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type UnitProvenance, type UnitProvenanceInput, type UnitRegeneration, type ValidateRulesOptions, type VerdictCitation, type VerdictLabel, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildConventionAreas, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, carryForwardVerdicts, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, computeFootprints, computeGrowthRiskMetrics, computeMetrics, computePageRank, computePartitionQuality, computeRecencyWindows, computeRelevance, computeRoleHints, computeSourceMetrics, computeSymbolConsumers, computeSymbolCoupling, computeTestCoverageOwnership, createAliasChain, defaultTargetCount, describeMetric, describeMetrics, detectCommunities, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffFootprints, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, externalToFinding, fileId, findConventions, findSimilarCapability, findingKey, gateUnits, getConventionMap, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, hashText, indexPaths, invertBuckets, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, keyFindings, lineagePath, linkTestsToSources, listEdges, listEdgesTouching, listEmbeddableSymbols, listFindings, listMetrics, listMetricsForNode, listNodes, listVerdicts, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, matchesAny, moduleId, normalizeFlaggedText, openCodeGraph, packageId, parentModuleId, parseSymbolId, patternToRegex, percentileOf, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, saveFindings, saveVerdicts, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, symbolSetHash, testCoverageCountMetrics, toFindings, tryEmbedSnapshot, unitProvenance, validateRules, violationKey, walkSourceFiles, windowSuffix };
|
package/dist/index.js
CHANGED
|
@@ -1223,20 +1223,36 @@ function literalText(arg) {
|
|
|
1223
1223
|
}
|
|
1224
1224
|
|
|
1225
1225
|
// src/extractors/ts-morph-extractor.ts
|
|
1226
|
+
var RETIRE_BATCH = 100;
|
|
1226
1227
|
var TsMorphGraphExtractor = class {
|
|
1227
1228
|
name = "ts-morph-graph";
|
|
1228
1229
|
repoRoot;
|
|
1229
1230
|
tsConfigPath;
|
|
1230
1231
|
project;
|
|
1232
|
+
ownsProject;
|
|
1233
|
+
retired = [];
|
|
1231
1234
|
constructor(options) {
|
|
1232
1235
|
this.repoRoot = options.repoRoot;
|
|
1233
1236
|
this.tsConfigPath = options.tsConfigPath;
|
|
1234
1237
|
this.project = options.project;
|
|
1238
|
+
this.ownsProject = options.project === void 0;
|
|
1235
1239
|
}
|
|
1236
1240
|
extract(file) {
|
|
1237
1241
|
if (!isTypeScriptFile(file)) return [];
|
|
1238
1242
|
const project = this.ensureProject();
|
|
1239
1243
|
const sourceFile = this.loadSourceFile(project, file);
|
|
1244
|
+
try {
|
|
1245
|
+
return this.extractFrom(sourceFile, file);
|
|
1246
|
+
} finally {
|
|
1247
|
+
if (this.ownsProject) this.retire(project, sourceFile);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
retire(project, sourceFile) {
|
|
1251
|
+
this.retired.push(sourceFile);
|
|
1252
|
+
if (this.retired.length < RETIRE_BATCH) return;
|
|
1253
|
+
for (const sf of this.retired.splice(0)) project.removeSourceFile(sf);
|
|
1254
|
+
}
|
|
1255
|
+
extractFrom(sourceFile, file) {
|
|
1240
1256
|
const nodes = this.buildFileAndModuleNodes(sourceFile);
|
|
1241
1257
|
const symbolNodes2 = buildSymbolNodes(this.repoRoot, sourceFile, file);
|
|
1242
1258
|
const { edges, externalNodes } = this.collectEdges(sourceFile);
|
|
@@ -3461,16 +3477,16 @@ function targetFileId(edge) {
|
|
|
3461
3477
|
}
|
|
3462
3478
|
function computeDeltaAffected(basis, currentFileIds) {
|
|
3463
3479
|
const affected = /* @__PURE__ */ new Set();
|
|
3464
|
-
const
|
|
3480
|
+
const removed2 = /* @__PURE__ */ new Set();
|
|
3465
3481
|
for (const id of basis.fingerprints.keys()) {
|
|
3466
|
-
if (!currentFileIds.has(id))
|
|
3482
|
+
if (!currentFileIds.has(id)) removed2.add(id);
|
|
3467
3483
|
}
|
|
3468
|
-
const added = currentFileIds.size !== basis.fingerprints.size ||
|
|
3484
|
+
const added = currentFileIds.size !== basis.fingerprints.size || removed2.size > 0;
|
|
3469
3485
|
if (!added) return affected;
|
|
3470
3486
|
for (const [srcId, edges] of basis.edgesBySrc) {
|
|
3471
3487
|
if (!currentFileIds.has(srcId)) continue;
|
|
3472
3488
|
for (const edge of edges) {
|
|
3473
|
-
if (isEdgeAffected(edge, srcId,
|
|
3489
|
+
if (isEdgeAffected(edge, srcId, removed2, currentFileIds)) {
|
|
3474
3490
|
affected.add(srcId);
|
|
3475
3491
|
break;
|
|
3476
3492
|
}
|
|
@@ -3478,8 +3494,8 @@ function computeDeltaAffected(basis, currentFileIds) {
|
|
|
3478
3494
|
}
|
|
3479
3495
|
return affected;
|
|
3480
3496
|
}
|
|
3481
|
-
function isEdgeAffected(edge, srcId,
|
|
3482
|
-
if (
|
|
3497
|
+
function isEdgeAffected(edge, srcId, removed2, currentFileIds) {
|
|
3498
|
+
if (removed2.has(targetFileId(edge))) return true;
|
|
3483
3499
|
const specifier = edge.attrs?.specifier;
|
|
3484
3500
|
if (!specifier || !isRelative(specifier)) return false;
|
|
3485
3501
|
return resolveRelativeId(srcId, specifier, currentFileIds) !== targetFileId(edge);
|
|
@@ -6071,13 +6087,13 @@ function indexEdges(edges) {
|
|
|
6071
6087
|
function diffNodes(fromNodes, toNodes, aliases) {
|
|
6072
6088
|
const toIndex = new Map(toNodes.map((n) => [n.id, n]));
|
|
6073
6089
|
const matchedToIds = /* @__PURE__ */ new Set();
|
|
6074
|
-
const
|
|
6090
|
+
const removed2 = [];
|
|
6075
6091
|
const renamed = [];
|
|
6076
6092
|
for (const fromNode of fromNodes) {
|
|
6077
6093
|
const newId = resolveId(fromNode.id, aliases);
|
|
6078
6094
|
const toNode = toIndex.get(newId);
|
|
6079
6095
|
if (!toNode) {
|
|
6080
|
-
|
|
6096
|
+
removed2.push(fromNode);
|
|
6081
6097
|
continue;
|
|
6082
6098
|
}
|
|
6083
6099
|
matchedToIds.add(newId);
|
|
@@ -6086,7 +6102,7 @@ function diffNodes(fromNodes, toNodes, aliases) {
|
|
|
6086
6102
|
}
|
|
6087
6103
|
}
|
|
6088
6104
|
const added = toNodes.filter((toNode) => !matchedToIds.has(toNode.id));
|
|
6089
|
-
return { added, removed, renamed, commonNewIds: matchedToIds };
|
|
6105
|
+
return { added, removed: removed2, renamed, commonNewIds: matchedToIds };
|
|
6090
6106
|
}
|
|
6091
6107
|
function diffEdges(fromEdges, toEdges, aliases) {
|
|
6092
6108
|
const remappedKey = (e) => edgeKey2(resolveId(e.srcId, aliases), resolveId(e.dstId, aliases), e.kind);
|
|
@@ -6096,8 +6112,8 @@ function diffEdges(fromEdges, toEdges, aliases) {
|
|
|
6096
6112
|
for (const [k, e] of toIndex) {
|
|
6097
6113
|
if (!fromKeys.has(k)) added.push(e);
|
|
6098
6114
|
}
|
|
6099
|
-
const
|
|
6100
|
-
return { added, removed };
|
|
6115
|
+
const removed2 = fromEdges.filter((e) => !toIndex.has(remappedKey(e)));
|
|
6116
|
+
return { added, removed: removed2 };
|
|
6101
6117
|
}
|
|
6102
6118
|
function indexFromMetrics(metrics, aliases) {
|
|
6103
6119
|
const fromIndex = /* @__PURE__ */ new Map();
|
|
@@ -6264,6 +6280,119 @@ function partnersBySymbol(edges, minCoImports) {
|
|
|
6264
6280
|
return new Map([...partners].map(([id, set]) => [id, [...set].sort()]));
|
|
6265
6281
|
}
|
|
6266
6282
|
|
|
6283
|
+
// src/diff/footprint-diff.ts
|
|
6284
|
+
var PART_REASONS = ["signature", "consumers", "coupling"];
|
|
6285
|
+
function diffFootprints(store, options) {
|
|
6286
|
+
const { fromSnapshotId, toSnapshotId } = options;
|
|
6287
|
+
const resolve4 = symbolAwareResolver(aliasChain(store, fromSnapshotId, toSnapshotId));
|
|
6288
|
+
const fromGraph = loadSymbolLayer(store, fromSnapshotId);
|
|
6289
|
+
const toGraph = loadSymbolLayer(store, toSnapshotId);
|
|
6290
|
+
const { previousIds, mergedAway } = mergeSources(fromGraph.nodes, resolve4);
|
|
6291
|
+
const before = computeFootprints(remapGraph(fromGraph, resolve4, new Set(mergedAway)), options);
|
|
6292
|
+
const after = computeFootprints(toGraph, options);
|
|
6293
|
+
const changes = [
|
|
6294
|
+
...changedOrAdded(after, before, previousIds),
|
|
6295
|
+
...removed(before, after, previousIds, resolve4),
|
|
6296
|
+
...mergedAway.map((id) => ({ symbolId: id, status: "removed", reasons: [], fileId: resolve4(declaringFile(id)) }))
|
|
6297
|
+
].sort((a, b) => compare2(a.symbolId, b.symbolId));
|
|
6298
|
+
const files = [...new Set(changes.map((c) => c.fileId))].sort(compare2);
|
|
6299
|
+
return { fromSnapshotId, toSnapshotId, changes, files };
|
|
6300
|
+
}
|
|
6301
|
+
function loadSymbolLayer(store, snapshotId) {
|
|
6302
|
+
return {
|
|
6303
|
+
nodes: store.listNodes(snapshotId, { includeSymbols: true }).filter((n) => n.kind === "symbol"),
|
|
6304
|
+
edges: store.listEdges(snapshotId, { includeReferences: true })
|
|
6305
|
+
};
|
|
6306
|
+
}
|
|
6307
|
+
function symbolAwareResolver(aliases) {
|
|
6308
|
+
return (id) => {
|
|
6309
|
+
const direct = aliases.resolve(id);
|
|
6310
|
+
if (direct !== id) return direct;
|
|
6311
|
+
const parsed = parseSymbolId(id);
|
|
6312
|
+
return parsed ? symbolId(aliases.resolve(parsed.fileId), parsed.name) : id;
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
function remapGraph(graph, resolve4, losers) {
|
|
6316
|
+
return {
|
|
6317
|
+
nodes: graph.nodes.filter((n) => !losers.has(n.id)).map((n) => ({ ...n, id: resolve4(n.id) })),
|
|
6318
|
+
edges: graph.edges.filter((e) => !losers.has(e.srcId) && !losers.has(e.dstId)).map((e) => ({ ...e, srcId: resolve4(e.srcId), dstId: resolve4(e.dstId) }))
|
|
6319
|
+
};
|
|
6320
|
+
}
|
|
6321
|
+
function mergeSources(nodes, resolve4) {
|
|
6322
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6323
|
+
for (const node of nodes) {
|
|
6324
|
+
const id = resolve4(node.id);
|
|
6325
|
+
groups.set(id, [...groups.get(id) ?? [], node.id]);
|
|
6326
|
+
}
|
|
6327
|
+
const previousIds = /* @__PURE__ */ new Map();
|
|
6328
|
+
const mergedAway = [];
|
|
6329
|
+
for (const [id, sources] of groups) {
|
|
6330
|
+
const keeper = sources.includes(id) ? id : [...sources].sort(compare2)[0];
|
|
6331
|
+
if (keeper !== id) previousIds.set(id, keeper);
|
|
6332
|
+
mergedAway.push(...sources.filter((source) => source !== keeper));
|
|
6333
|
+
}
|
|
6334
|
+
return { previousIds, mergedAway };
|
|
6335
|
+
}
|
|
6336
|
+
function changedOrAdded(after, before, previousIds) {
|
|
6337
|
+
const out = [];
|
|
6338
|
+
for (const [id, footprint] of after) {
|
|
6339
|
+
const prior = before.get(id);
|
|
6340
|
+
const previousId = previousIds.get(id);
|
|
6341
|
+
if (!prior) {
|
|
6342
|
+
out.push({ symbolId: id, status: "added", reasons: [], fileId: declaringFile(id) });
|
|
6343
|
+
continue;
|
|
6344
|
+
}
|
|
6345
|
+
const reasons = reasonsFor(prior, footprint, previousId !== void 0);
|
|
6346
|
+
if (reasons.length === 0) continue;
|
|
6347
|
+
out.push({ symbolId: id, ...previousId ? { previousId } : {}, status: "changed", reasons, fileId: declaringFile(id) });
|
|
6348
|
+
}
|
|
6349
|
+
return out;
|
|
6350
|
+
}
|
|
6351
|
+
function reasonsFor(prior, current, renamed) {
|
|
6352
|
+
const reasons = PART_REASONS.filter((part) => prior.parts[part] !== current.parts[part]);
|
|
6353
|
+
if (renamed) reasons.push("renamed");
|
|
6354
|
+
return reasons;
|
|
6355
|
+
}
|
|
6356
|
+
function removed(before, after, previousIds, resolve4) {
|
|
6357
|
+
const out = [];
|
|
6358
|
+
for (const id of before.keys()) {
|
|
6359
|
+
if (after.has(id)) continue;
|
|
6360
|
+
const fromId = previousIds.get(id) ?? id;
|
|
6361
|
+
out.push({ symbolId: fromId, status: "removed", reasons: [], fileId: resolve4(declaringFile(fromId)) });
|
|
6362
|
+
}
|
|
6363
|
+
return out;
|
|
6364
|
+
}
|
|
6365
|
+
function declaringFile(id) {
|
|
6366
|
+
return parseSymbolId(id)?.fileId ?? id;
|
|
6367
|
+
}
|
|
6368
|
+
function compare2(a, b) {
|
|
6369
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
6370
|
+
}
|
|
6371
|
+
|
|
6372
|
+
// src/diff/gate.ts
|
|
6373
|
+
function unitProvenance(input) {
|
|
6374
|
+
return {
|
|
6375
|
+
unitId: input.unit.unitId,
|
|
6376
|
+
commit: input.snapshot.commitHash,
|
|
6377
|
+
symbolSetHash: symbolSetHash(input.unit, input.footprints),
|
|
6378
|
+
model: input.model ?? null
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
function gateUnits(input) {
|
|
6382
|
+
const priorHash = new Map(input.prior.map((p) => [p.unitId, p.symbolSetHash]));
|
|
6383
|
+
const current = new Set(input.units.map((u) => u.unitId));
|
|
6384
|
+
const regenerate = [];
|
|
6385
|
+
const skip = [];
|
|
6386
|
+
for (const unit of input.units) {
|
|
6387
|
+
const before = priorHash.get(unit.unitId);
|
|
6388
|
+
if (before === void 0) regenerate.push({ unitId: unit.unitId, reason: "new" });
|
|
6389
|
+
else if (before !== symbolSetHash(unit, input.footprints)) regenerate.push({ unitId: unit.unitId, reason: "changed" });
|
|
6390
|
+
else skip.push(unit.unitId);
|
|
6391
|
+
}
|
|
6392
|
+
const orphaned = [...priorHash.keys()].filter((id) => !current.has(id));
|
|
6393
|
+
return { regenerate, skip, orphaned };
|
|
6394
|
+
}
|
|
6395
|
+
|
|
6267
6396
|
// src/embeddings/corpus.ts
|
|
6268
6397
|
import { contentHashOf } from "@titan-design/store-sqlite";
|
|
6269
6398
|
function buildEmbedText(signature, purpose) {
|
|
@@ -6783,6 +6912,7 @@ export {
|
|
|
6783
6912
|
detectGitToplevel,
|
|
6784
6913
|
detectRenames,
|
|
6785
6914
|
diffCheckResults,
|
|
6915
|
+
diffFootprints,
|
|
6786
6916
|
diffSnapshots,
|
|
6787
6917
|
edgeWeight,
|
|
6788
6918
|
embedSnapshot,
|
|
@@ -6793,6 +6923,7 @@ export {
|
|
|
6793
6923
|
findConventions,
|
|
6794
6924
|
findSimilarCapability,
|
|
6795
6925
|
findingKey,
|
|
6926
|
+
gateUnits,
|
|
6796
6927
|
getConventionMap,
|
|
6797
6928
|
getEdgeWeight,
|
|
6798
6929
|
getLanguageFromPath3 as getLanguageFromPath,
|
|
@@ -6859,6 +6990,7 @@ export {
|
|
|
6859
6990
|
testCoverageCountMetrics,
|
|
6860
6991
|
toFindings,
|
|
6861
6992
|
tryEmbedSnapshot,
|
|
6993
|
+
unitProvenance,
|
|
6862
6994
|
validateRules,
|
|
6863
6995
|
violationKey,
|
|
6864
6996
|
walkSourceFiles,
|