@pratik7368patil/anchor-core 0.1.31 → 0.1.33
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/dist/index.d.ts +36 -1
- package/dist/index.js +605 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/db/schema.sql +14 -0
package/dist/index.js
CHANGED
|
@@ -617,8 +617,12 @@ CREATE TABLE IF NOT EXISTS org_cross_repo_edges (
|
|
|
617
617
|
source_path TEXT NOT NULL,
|
|
618
618
|
target_repo TEXT NOT NULL,
|
|
619
619
|
target_path TEXT,
|
|
620
|
+
layer TEXT NOT NULL DEFAULT 'file',
|
|
620
621
|
relationship TEXT NOT NULL,
|
|
621
622
|
evidence_json TEXT NOT NULL,
|
|
623
|
+
match_reasons_json TEXT NOT NULL DEFAULT '[]',
|
|
624
|
+
evidence_count INTEGER NOT NULL DEFAULT 0,
|
|
625
|
+
is_weak INTEGER NOT NULL DEFAULT 0,
|
|
622
626
|
confidence REAL NOT NULL,
|
|
623
627
|
created_at TEXT NOT NULL
|
|
624
628
|
);
|
|
@@ -643,6 +647,9 @@ CREATE TABLE IF NOT EXISTS org_api_consumers (
|
|
|
643
647
|
consumer_path TEXT NOT NULL,
|
|
644
648
|
contract TEXT NOT NULL,
|
|
645
649
|
evidence_json TEXT NOT NULL,
|
|
650
|
+
match_reasons_json TEXT NOT NULL DEFAULT '[]',
|
|
651
|
+
evidence_count INTEGER NOT NULL DEFAULT 0,
|
|
652
|
+
is_weak INTEGER NOT NULL DEFAULT 0,
|
|
646
653
|
confidence REAL NOT NULL,
|
|
647
654
|
created_at TEXT NOT NULL
|
|
648
655
|
);
|
|
@@ -667,6 +674,10 @@ CREATE TABLE IF NOT EXISTS org_graph_state (
|
|
|
667
674
|
last_status TEXT NOT NULL DEFAULT 'unknown',
|
|
668
675
|
last_duration_ms INTEGER,
|
|
669
676
|
edge_count INTEGER NOT NULL DEFAULT 0,
|
|
677
|
+
visible_edge_count INTEGER NOT NULL DEFAULT 0,
|
|
678
|
+
weak_edge_count INTEGER NOT NULL DEFAULT 0,
|
|
679
|
+
edge_confidence_json TEXT NOT NULL DEFAULT '{"strong":0,"moderate":0,"weak":0}',
|
|
680
|
+
last_render_prep_ms INTEGER,
|
|
670
681
|
api_contract_count INTEGER NOT NULL DEFAULT 0,
|
|
671
682
|
api_consumer_count INTEGER NOT NULL DEFAULT 0,
|
|
672
683
|
last_error TEXT,
|
|
@@ -707,8 +718,11 @@ CREATE INDEX IF NOT EXISTS idx_org_repositories_org ON org_repositories(org);
|
|
|
707
718
|
CREATE INDEX IF NOT EXISTS idx_org_repo_state_org ON org_repo_state(org);
|
|
708
719
|
CREATE INDEX IF NOT EXISTS idx_org_edges_source ON org_cross_repo_edges(org, source_repo);
|
|
709
720
|
CREATE INDEX IF NOT EXISTS idx_org_edges_target ON org_cross_repo_edges(org, target_repo);
|
|
721
|
+
CREATE INDEX IF NOT EXISTS idx_org_edges_layer ON org_cross_repo_edges(org, layer, confidence);
|
|
722
|
+
CREATE INDEX IF NOT EXISTS idx_org_edges_repo_pair ON org_cross_repo_edges(org, layer, source_repo, target_repo, relationship);
|
|
710
723
|
CREATE INDEX IF NOT EXISTS idx_org_consumers_provider ON org_api_consumers(org, provider_repo);
|
|
711
724
|
CREATE INDEX IF NOT EXISTS idx_org_consumers_consumer ON org_api_consumers(org, consumer_repo);
|
|
725
|
+
CREATE INDEX IF NOT EXISTS idx_org_consumers_contract ON org_api_consumers(org, contract);
|
|
712
726
|
CREATE INDEX IF NOT EXISTS idx_org_anomalies_org ON org_anomaly_events(org, severity);
|
|
713
727
|
CREATE INDEX IF NOT EXISTS idx_org_graph_state_status ON org_graph_state(org, last_status);
|
|
714
728
|
|
|
@@ -1783,6 +1797,41 @@ function initializeSchema(db) {
|
|
|
1783
1797
|
ensureColumn(db, "sync_state", "graphql_cursor_reason", "TEXT");
|
|
1784
1798
|
ensureColumn(db, "sync_state", "graphql_cursor_updated_at", "TEXT");
|
|
1785
1799
|
ensureColumn(db, "code_index_state", "last_indexed_commit", "TEXT");
|
|
1800
|
+
ensureColumn(db, "org_cross_repo_edges", "layer", "TEXT NOT NULL DEFAULT 'file'");
|
|
1801
|
+
ensureColumn(
|
|
1802
|
+
db,
|
|
1803
|
+
"org_cross_repo_edges",
|
|
1804
|
+
"match_reasons_json",
|
|
1805
|
+
"TEXT NOT NULL DEFAULT '[]'"
|
|
1806
|
+
);
|
|
1807
|
+
ensureColumn(db, "org_cross_repo_edges", "evidence_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1808
|
+
ensureColumn(db, "org_cross_repo_edges", "is_weak", "INTEGER NOT NULL DEFAULT 0");
|
|
1809
|
+
ensureColumn(
|
|
1810
|
+
db,
|
|
1811
|
+
"org_api_consumers",
|
|
1812
|
+
"match_reasons_json",
|
|
1813
|
+
"TEXT NOT NULL DEFAULT '[]'"
|
|
1814
|
+
);
|
|
1815
|
+
ensureColumn(db, "org_api_consumers", "evidence_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1816
|
+
ensureColumn(db, "org_api_consumers", "is_weak", "INTEGER NOT NULL DEFAULT 0");
|
|
1817
|
+
ensureColumn(db, "org_graph_state", "visible_edge_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1818
|
+
ensureColumn(db, "org_graph_state", "weak_edge_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1819
|
+
ensureColumn(
|
|
1820
|
+
db,
|
|
1821
|
+
"org_graph_state",
|
|
1822
|
+
"edge_confidence_json",
|
|
1823
|
+
`TEXT NOT NULL DEFAULT '{"strong":0,"moderate":0,"weak":0}'`
|
|
1824
|
+
);
|
|
1825
|
+
ensureColumn(db, "org_graph_state", "last_render_prep_ms", "INTEGER");
|
|
1826
|
+
db.exec(
|
|
1827
|
+
"CREATE INDEX IF NOT EXISTS idx_org_edges_layer ON org_cross_repo_edges(org, layer, confidence)"
|
|
1828
|
+
);
|
|
1829
|
+
db.exec(
|
|
1830
|
+
"CREATE INDEX IF NOT EXISTS idx_org_edges_repo_pair ON org_cross_repo_edges(org, layer, source_repo, target_repo, relationship)"
|
|
1831
|
+
);
|
|
1832
|
+
db.exec(
|
|
1833
|
+
"CREATE INDEX IF NOT EXISTS idx_org_consumers_contract ON org_api_consumers(org, contract)"
|
|
1834
|
+
);
|
|
1786
1835
|
}
|
|
1787
1836
|
function ensureColumn(db, tableName, columnName, definition) {
|
|
1788
1837
|
const columns = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
@@ -2695,19 +2744,36 @@ function insertPrCochangeTestLinks(db, repoId, filePaths) {
|
|
|
2695
2744
|
}
|
|
2696
2745
|
}
|
|
2697
2746
|
function insertTestAwareness(db, repoId, repo, testFiles, testLinks, options = {}) {
|
|
2747
|
+
const dedupedTestFilesByPath = /* @__PURE__ */ new Map();
|
|
2748
|
+
for (const file of testFiles) dedupedTestFilesByPath.set(file.path, file);
|
|
2749
|
+
const dedupedTestFiles = [...dedupedTestFilesByPath.values()];
|
|
2750
|
+
const dedupedTestLinksByKey = /* @__PURE__ */ new Map();
|
|
2751
|
+
for (const link of testLinks) {
|
|
2752
|
+
const key = `${link.sourcePath}\0${link.testPath}\0${link.reason}`;
|
|
2753
|
+
const existing = dedupedTestLinksByKey.get(key);
|
|
2754
|
+
if (!existing || link.strength > existing.strength) {
|
|
2755
|
+
dedupedTestLinksByKey.set(key, link);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
const dedupedTestLinks = [...dedupedTestLinksByKey.values()];
|
|
2698
2759
|
const insertTestFile = db.prepare(
|
|
2699
2760
|
`INSERT INTO test_files
|
|
2700
2761
|
(repo_id, path, language, size_bytes, content_hash, updated_at)
|
|
2701
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
2762
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
2763
|
+
ON CONFLICT(repo_id, path) DO UPDATE SET
|
|
2764
|
+
language = excluded.language,
|
|
2765
|
+
size_bytes = excluded.size_bytes,
|
|
2766
|
+
content_hash = excluded.content_hash,
|
|
2767
|
+
updated_at = excluded.updated_at`
|
|
2702
2768
|
);
|
|
2703
2769
|
options.onProgress?.({
|
|
2704
2770
|
stage: "writing_test_awareness",
|
|
2705
2771
|
repo,
|
|
2706
2772
|
current: 0,
|
|
2707
|
-
total:
|
|
2773
|
+
total: dedupedTestFiles.length,
|
|
2708
2774
|
kind: "test_files"
|
|
2709
2775
|
});
|
|
2710
|
-
for (const [index, file] of
|
|
2776
|
+
for (const [index, file] of dedupedTestFiles.entries()) {
|
|
2711
2777
|
insertTestFile.run(
|
|
2712
2778
|
repoId,
|
|
2713
2779
|
file.path,
|
|
@@ -2717,36 +2783,38 @@ function insertTestAwareness(db, repoId, repo, testFiles, testLinks, options = {
|
|
|
2717
2783
|
file.updatedAt
|
|
2718
2784
|
);
|
|
2719
2785
|
const current = index + 1;
|
|
2720
|
-
if (shouldEmitCodeWriteProgress(current,
|
|
2786
|
+
if (shouldEmitCodeWriteProgress(current, dedupedTestFiles.length)) {
|
|
2721
2787
|
options.onProgress?.({
|
|
2722
2788
|
stage: "writing_test_awareness",
|
|
2723
2789
|
repo,
|
|
2724
2790
|
current,
|
|
2725
|
-
total:
|
|
2791
|
+
total: dedupedTestFiles.length,
|
|
2726
2792
|
kind: "test_files"
|
|
2727
2793
|
});
|
|
2728
2794
|
}
|
|
2729
2795
|
}
|
|
2730
2796
|
const insertTestLink = db.prepare(
|
|
2731
2797
|
`INSERT INTO test_links (repo_id, source_path, test_path, reason, strength)
|
|
2732
|
-
VALUES (?, ?, ?, ?, ?)
|
|
2798
|
+
VALUES (?, ?, ?, ?, ?)
|
|
2799
|
+
ON CONFLICT(repo_id, source_path, test_path, reason) DO UPDATE SET
|
|
2800
|
+
strength = excluded.strength`
|
|
2733
2801
|
);
|
|
2734
2802
|
options.onProgress?.({
|
|
2735
2803
|
stage: "writing_test_awareness",
|
|
2736
2804
|
repo,
|
|
2737
2805
|
current: 0,
|
|
2738
|
-
total:
|
|
2806
|
+
total: dedupedTestLinks.length,
|
|
2739
2807
|
kind: "test_links"
|
|
2740
2808
|
});
|
|
2741
|
-
for (const [index, link] of
|
|
2809
|
+
for (const [index, link] of dedupedTestLinks.entries()) {
|
|
2742
2810
|
insertTestLink.run(repoId, link.sourcePath, link.testPath, link.reason, link.strength);
|
|
2743
2811
|
const current = index + 1;
|
|
2744
|
-
if (shouldEmitCodeWriteProgress(current,
|
|
2812
|
+
if (shouldEmitCodeWriteProgress(current, dedupedTestLinks.length)) {
|
|
2745
2813
|
options.onProgress?.({
|
|
2746
2814
|
stage: "writing_test_awareness",
|
|
2747
2815
|
repo,
|
|
2748
2816
|
current,
|
|
2749
|
-
total:
|
|
2817
|
+
total: dedupedTestLinks.length,
|
|
2750
2818
|
kind: "test_links"
|
|
2751
2819
|
});
|
|
2752
2820
|
}
|
|
@@ -8687,6 +8755,24 @@ function resolveOrgForTool(org, baseDir = defaultOrgBaseDir()) {
|
|
|
8687
8755
|
|
|
8688
8756
|
// src/org/database.ts
|
|
8689
8757
|
import fs10 from "fs";
|
|
8758
|
+
var DEFAULT_EDGE_DISTRIBUTION = {
|
|
8759
|
+
strong: 0,
|
|
8760
|
+
moderate: 0,
|
|
8761
|
+
weak: 0
|
|
8762
|
+
};
|
|
8763
|
+
function parseEdgeDistribution(value) {
|
|
8764
|
+
if (!value) return { ...DEFAULT_EDGE_DISTRIBUTION };
|
|
8765
|
+
try {
|
|
8766
|
+
const parsed = JSON.parse(value);
|
|
8767
|
+
return {
|
|
8768
|
+
strong: Number(parsed.strong ?? 0),
|
|
8769
|
+
moderate: Number(parsed.moderate ?? 0),
|
|
8770
|
+
weak: Number(parsed.weak ?? 0)
|
|
8771
|
+
};
|
|
8772
|
+
} catch {
|
|
8773
|
+
return { ...DEFAULT_EDGE_DISTRIBUTION };
|
|
8774
|
+
}
|
|
8775
|
+
}
|
|
8690
8776
|
function openOrgDatabase(org, baseDir) {
|
|
8691
8777
|
const root = orgRoot(org, baseDir);
|
|
8692
8778
|
const db = openAnchorDatabase(root, orgDatabasePath(org, baseDir));
|
|
@@ -8816,8 +8902,9 @@ function recordOrgGraphState(db, input) {
|
|
|
8816
8902
|
db.prepare(
|
|
8817
8903
|
`INSERT INTO org_graph_state
|
|
8818
8904
|
(org, last_built_at, last_status, last_duration_ms, edge_count, api_contract_count,
|
|
8819
|
-
api_consumer_count,
|
|
8820
|
-
|
|
8905
|
+
api_consumer_count, visible_edge_count, weak_edge_count, edge_confidence_json,
|
|
8906
|
+
last_render_prep_ms, last_error, updated_at)
|
|
8907
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8821
8908
|
ON CONFLICT(org) DO UPDATE SET
|
|
8822
8909
|
last_built_at = COALESCE(excluded.last_built_at, org_graph_state.last_built_at),
|
|
8823
8910
|
last_status = excluded.last_status,
|
|
@@ -8825,6 +8912,10 @@ function recordOrgGraphState(db, input) {
|
|
|
8825
8912
|
edge_count = excluded.edge_count,
|
|
8826
8913
|
api_contract_count = excluded.api_contract_count,
|
|
8827
8914
|
api_consumer_count = excluded.api_consumer_count,
|
|
8915
|
+
visible_edge_count = excluded.visible_edge_count,
|
|
8916
|
+
weak_edge_count = excluded.weak_edge_count,
|
|
8917
|
+
edge_confidence_json = excluded.edge_confidence_json,
|
|
8918
|
+
last_render_prep_ms = excluded.last_render_prep_ms,
|
|
8828
8919
|
last_error = excluded.last_error,
|
|
8829
8920
|
updated_at = excluded.updated_at`
|
|
8830
8921
|
).run(
|
|
@@ -8835,6 +8926,10 @@ function recordOrgGraphState(db, input) {
|
|
|
8835
8926
|
input.edgeCount ?? 0,
|
|
8836
8927
|
input.apiContractCount ?? 0,
|
|
8837
8928
|
input.apiConsumerCount ?? 0,
|
|
8929
|
+
input.visibleEdgeCount ?? input.edgeCount ?? 0,
|
|
8930
|
+
input.weakEdgeCount ?? 0,
|
|
8931
|
+
JSON.stringify(input.edgeConfidenceDistribution ?? DEFAULT_EDGE_DISTRIBUTION),
|
|
8932
|
+
input.lastRenderPrepMs ?? null,
|
|
8838
8933
|
input.error ?? null,
|
|
8839
8934
|
now
|
|
8840
8935
|
);
|
|
@@ -8849,6 +8944,10 @@ function getOrgGraphState(db, org) {
|
|
|
8849
8944
|
lastStatus: row.last_status ?? void 0,
|
|
8850
8945
|
lastDurationMs: row.last_duration_ms ?? void 0,
|
|
8851
8946
|
edgeCount: row.edge_count ?? void 0,
|
|
8947
|
+
visibleEdgeCount: row.visible_edge_count ?? void 0,
|
|
8948
|
+
weakEdgeCount: row.weak_edge_count ?? void 0,
|
|
8949
|
+
edgeConfidenceDistribution: parseEdgeDistribution(row.edge_confidence_json),
|
|
8950
|
+
lastRenderPrepMs: row.last_render_prep_ms ?? void 0,
|
|
8852
8951
|
apiContractCount: row.api_contract_count ?? void 0,
|
|
8853
8952
|
apiConsumerCount: row.api_consumer_count ?? void 0,
|
|
8854
8953
|
lastError: row.last_error ?? void 0
|
|
@@ -8861,7 +8960,19 @@ function count(db, table, where = "", params = []) {
|
|
|
8861
8960
|
function getOrgGraphCounts(db, org) {
|
|
8862
8961
|
initializeSchema(db);
|
|
8863
8962
|
return {
|
|
8864
|
-
edges: count(db, "org_cross_repo_edges", "WHERE org = ?", [org]),
|
|
8963
|
+
edges: count(db, "org_cross_repo_edges", "WHERE org = ? AND layer = 'repo'", [org]),
|
|
8964
|
+
visibleEdges: count(
|
|
8965
|
+
db,
|
|
8966
|
+
"org_cross_repo_edges",
|
|
8967
|
+
"WHERE org = ? AND layer = 'repo' AND is_weak = 0",
|
|
8968
|
+
[org]
|
|
8969
|
+
),
|
|
8970
|
+
weakEdges: count(
|
|
8971
|
+
db,
|
|
8972
|
+
"org_cross_repo_edges",
|
|
8973
|
+
"WHERE org = ? AND layer = 'repo' AND is_weak = 1",
|
|
8974
|
+
[org]
|
|
8975
|
+
),
|
|
8865
8976
|
apiContracts: count(db, "org_api_contracts", "WHERE org = ?", [org]),
|
|
8866
8977
|
apiConsumers: count(db, "org_api_consumers", "WHERE org = ?", [org])
|
|
8867
8978
|
};
|
|
@@ -8888,11 +8999,22 @@ function getOrgStatus(db, config, baseDir, options = {}) {
|
|
|
8888
8999
|
const codeFileCount = count(db, "code_files");
|
|
8889
9000
|
const codeChunkCount = count(db, "code_chunks");
|
|
8890
9001
|
const wisdomUnitCount = count(db, "wisdom_units");
|
|
8891
|
-
const crossRepoEdgeCount = count(
|
|
9002
|
+
const crossRepoEdgeCount = count(
|
|
9003
|
+
db,
|
|
9004
|
+
"org_cross_repo_edges",
|
|
9005
|
+
"WHERE org = ? AND layer = 'repo' AND is_weak = 0",
|
|
9006
|
+
[config.org]
|
|
9007
|
+
);
|
|
9008
|
+
const graphWeakEdgeCount = count(
|
|
9009
|
+
db,
|
|
9010
|
+
"org_cross_repo_edges",
|
|
9011
|
+
"WHERE org = ? AND layer = 'repo' AND is_weak = 1",
|
|
9012
|
+
[config.org]
|
|
9013
|
+
);
|
|
8892
9014
|
const apiContractCount = count(db, "org_api_contracts", "WHERE org = ?", [config.org]);
|
|
8893
9015
|
const apiConsumerCount = count(db, "org_api_consumers", "WHERE org = ?", [config.org]);
|
|
8894
9016
|
const anomalyCount = count(db, "org_anomaly_events", "WHERE org = ?", [config.org]);
|
|
8895
|
-
const graphState = db
|
|
9017
|
+
const graphState = getOrgGraphState(db, config.org);
|
|
8896
9018
|
let score = 0;
|
|
8897
9019
|
const reasons = [];
|
|
8898
9020
|
if (enabledRepos.length > 0) {
|
|
@@ -8936,10 +9058,14 @@ function getOrgStatus(db, config, baseDir, options = {}) {
|
|
|
8936
9058
|
apiContractCount,
|
|
8937
9059
|
apiConsumerCount,
|
|
8938
9060
|
anomalyCount,
|
|
8939
|
-
graphLastBuiltAt: graphState?.
|
|
8940
|
-
graphLastStatus: graphState?.
|
|
8941
|
-
graphLastDurationMs: graphState?.
|
|
8942
|
-
graphLastError: graphState?.
|
|
9061
|
+
graphLastBuiltAt: graphState?.lastBuiltAt,
|
|
9062
|
+
graphLastStatus: graphState?.lastStatus,
|
|
9063
|
+
graphLastDurationMs: graphState?.lastDurationMs,
|
|
9064
|
+
graphLastError: graphState?.lastError,
|
|
9065
|
+
graphVisibleEdgeCount: graphState?.visibleEdgeCount ?? crossRepoEdgeCount,
|
|
9066
|
+
graphWeakEdgeCount: graphState?.weakEdgeCount ?? graphWeakEdgeCount,
|
|
9067
|
+
graphRenderPrepMs: graphState?.lastRenderPrepMs,
|
|
9068
|
+
graphEdgeConfidenceDistribution: graphState?.edgeConfidenceDistribution ?? { ...DEFAULT_EDGE_DISTRIBUTION },
|
|
8943
9069
|
coverageScore: score,
|
|
8944
9070
|
coverageGrade: grade(score),
|
|
8945
9071
|
coverageReasons: reasons,
|
|
@@ -9243,6 +9369,34 @@ function orgCloneStateFromResult(org, repo, result) {
|
|
|
9243
9369
|
import crypto9 from "crypto";
|
|
9244
9370
|
import fs13 from "fs";
|
|
9245
9371
|
import path23 from "path";
|
|
9372
|
+
var MIN_FILE_EDGE_CONFIDENCE = 0.62;
|
|
9373
|
+
var MIN_REPO_EDGE_CONFIDENCE = 0.7;
|
|
9374
|
+
var MIN_VISIBLE_EVIDENCE = 2;
|
|
9375
|
+
var MIN_API_CONSUMER_CONFIDENCE = 0.68;
|
|
9376
|
+
var MAX_EDGE_EVIDENCE = 8;
|
|
9377
|
+
var MAX_EDGE_REASONS = 6;
|
|
9378
|
+
var MAX_CONTRACTS_PER_CHUNK = 24;
|
|
9379
|
+
var CONTRACT_IGNORE = /* @__PURE__ */ new Set([
|
|
9380
|
+
"api",
|
|
9381
|
+
"v1",
|
|
9382
|
+
"v2",
|
|
9383
|
+
"v3",
|
|
9384
|
+
"graphql",
|
|
9385
|
+
"query",
|
|
9386
|
+
"mutation",
|
|
9387
|
+
"subscription",
|
|
9388
|
+
"schema",
|
|
9389
|
+
"route",
|
|
9390
|
+
"routes",
|
|
9391
|
+
"controller",
|
|
9392
|
+
"client",
|
|
9393
|
+
"request"
|
|
9394
|
+
]);
|
|
9395
|
+
var DEFAULT_EDGE_DISTRIBUTION2 = {
|
|
9396
|
+
strong: 0,
|
|
9397
|
+
moderate: 0,
|
|
9398
|
+
weak: 0
|
|
9399
|
+
};
|
|
9246
9400
|
function stableId(parts) {
|
|
9247
9401
|
return crypto9.createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
|
|
9248
9402
|
}
|
|
@@ -9255,6 +9409,9 @@ function fileEvidence(repo, filePath, note) {
|
|
|
9255
9409
|
note
|
|
9256
9410
|
};
|
|
9257
9411
|
}
|
|
9412
|
+
function evidenceJson(evidence) {
|
|
9413
|
+
return JSON.stringify(evidence);
|
|
9414
|
+
}
|
|
9258
9415
|
function readPackageManifest(repoPath) {
|
|
9259
9416
|
const packagePath = path23.join(repoPath, "package.json");
|
|
9260
9417
|
if (!fs13.existsSync(packagePath)) return void 0;
|
|
@@ -9300,19 +9457,53 @@ function parseJsonArray10(value) {
|
|
|
9300
9457
|
return [];
|
|
9301
9458
|
}
|
|
9302
9459
|
}
|
|
9303
|
-
function
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
if (operation) contracts.push(operation);
|
|
9460
|
+
function normalizeToken(value) {
|
|
9461
|
+
return value.toLowerCase().replace(/[{}()[\],:"'`]/g, "").replace(/\/+/g, "/").replace(/\/$/g, "").replace(/^-+/g, "").trim();
|
|
9462
|
+
}
|
|
9463
|
+
function splitTokenSymbols(value) {
|
|
9464
|
+
return value.split(/[^A-Za-z0-9_/-]+/).map((item) => normalizeToken(item)).filter((item) => item.length >= 3 && !CONTRACT_IGNORE.has(item)).slice(0, 12);
|
|
9465
|
+
}
|
|
9466
|
+
function normalizeContract(contract, kind) {
|
|
9467
|
+
if (kind === "route") {
|
|
9468
|
+
const normalized = normalizeToken(contract);
|
|
9469
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
9314
9470
|
}
|
|
9315
|
-
return
|
|
9471
|
+
return normalizeToken(contract);
|
|
9472
|
+
}
|
|
9473
|
+
function isGenericRoute(route) {
|
|
9474
|
+
const normalized = normalizeToken(route);
|
|
9475
|
+
if (!normalized.startsWith("/")) return true;
|
|
9476
|
+
const segments = normalized.split("/").filter((segment) => segment && !segment.startsWith(":") && segment !== "*");
|
|
9477
|
+
if (segments.length === 0) return true;
|
|
9478
|
+
const informative = segments.filter((segment) => !CONTRACT_IGNORE.has(segment));
|
|
9479
|
+
return informative.length < 1;
|
|
9480
|
+
}
|
|
9481
|
+
function extractContracts(text) {
|
|
9482
|
+
const tokens = [];
|
|
9483
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9484
|
+
const pushToken = (rawValue, kind) => {
|
|
9485
|
+
const sanitized = sanitizeHistoricalText(rawValue).slice(0, 180);
|
|
9486
|
+
if (!sanitized) return;
|
|
9487
|
+
const normalized = normalizeContract(sanitized, kind);
|
|
9488
|
+
if (!normalized || CONTRACT_IGNORE.has(normalized)) return;
|
|
9489
|
+
if (kind === "route" && isGenericRoute(normalized)) return;
|
|
9490
|
+
const key = `${kind}\0${normalized}`;
|
|
9491
|
+
if (seen.has(key)) return;
|
|
9492
|
+
seen.add(key);
|
|
9493
|
+
tokens.push({
|
|
9494
|
+
raw: sanitized,
|
|
9495
|
+
normalized,
|
|
9496
|
+
kind,
|
|
9497
|
+
symbols: splitTokenSymbols(sanitized)
|
|
9498
|
+
});
|
|
9499
|
+
};
|
|
9500
|
+
const routeMatches = text.matchAll(/["'`]((?:\/api)?\/[A-Za-z0-9_./:{}-]{3,})["'`]/g);
|
|
9501
|
+
for (const match of routeMatches) pushToken(match[1] ?? "", "route");
|
|
9502
|
+
const gqlMatches = text.matchAll(/\b(query|mutation|subscription)\s+([A-Za-z][A-Za-z0-9_]{2,})/g);
|
|
9503
|
+
for (const match of gqlMatches) pushToken(match[2] ?? "", "graphql");
|
|
9504
|
+
const schemaMatches = text.matchAll(/\b(?:type|interface|enum|input)\s+([A-Z][A-Za-z0-9_]{2,})\b/g);
|
|
9505
|
+
for (const match of schemaMatches) pushToken(match[1] ?? "", "schema");
|
|
9506
|
+
return tokens.slice(0, MAX_CONTRACTS_PER_CHUNK);
|
|
9316
9507
|
}
|
|
9317
9508
|
function isApiProviderPath(filePath) {
|
|
9318
9509
|
const normalized = filePath.toLowerCase();
|
|
@@ -9323,28 +9514,216 @@ function isApiProviderPath(filePath) {
|
|
|
9323
9514
|
function isApiConsumerText(text) {
|
|
9324
9515
|
return /\b(fetch|axios|ky|graphql|gql|client|sdk|request)\b/i.test(text);
|
|
9325
9516
|
}
|
|
9326
|
-
function evidenceJson(evidence) {
|
|
9327
|
-
return JSON.stringify(evidence);
|
|
9328
|
-
}
|
|
9329
9517
|
function shouldEmitProgress3(current, total, interval = 100) {
|
|
9330
9518
|
return current === 1 || current === total || current % interval === 0;
|
|
9331
9519
|
}
|
|
9332
9520
|
function resolveOptions(baseDirOrOptions) {
|
|
9333
9521
|
return typeof baseDirOrOptions === "string" ? { baseDir: baseDirOrOptions } : baseDirOrOptions ?? {};
|
|
9334
9522
|
}
|
|
9523
|
+
function clampConfidence(value) {
|
|
9524
|
+
if (Number.isNaN(value)) return 0;
|
|
9525
|
+
return Math.max(0, Math.min(0.99, Number(value.toFixed(3))));
|
|
9526
|
+
}
|
|
9527
|
+
function confidenceBucket(confidence) {
|
|
9528
|
+
if (confidence >= 0.82) return "strong";
|
|
9529
|
+
if (confidence >= 0.68) return "moderate";
|
|
9530
|
+
return "weak";
|
|
9531
|
+
}
|
|
9532
|
+
function uniqueEvidenceRefs(evidence) {
|
|
9533
|
+
const map = /* @__PURE__ */ new Map();
|
|
9534
|
+
for (const item of evidence) {
|
|
9535
|
+
const key = `${item.prNumber}|${item.prUrl}|${item.sourceType}|${item.filePath ?? ""}|${item.note ?? ""}`;
|
|
9536
|
+
if (!map.has(key)) map.set(key, item);
|
|
9537
|
+
}
|
|
9538
|
+
return [...map.values()].slice(0, MAX_EDGE_EVIDENCE);
|
|
9539
|
+
}
|
|
9540
|
+
function mergeReasons(a, b) {
|
|
9541
|
+
return uniqueStrings([...a, ...b]).slice(0, MAX_EDGE_REASONS);
|
|
9542
|
+
}
|
|
9543
|
+
function updateWeakFlag(edge, minConfidence2, minEvidence) {
|
|
9544
|
+
edge.evidence = uniqueEvidenceRefs(edge.evidence);
|
|
9545
|
+
edge.evidenceCount = edge.evidence.length;
|
|
9546
|
+
edge.confidence = clampConfidence(edge.confidence);
|
|
9547
|
+
edge.weak = edge.confidence < minConfidence2 || edge.evidenceCount < minEvidence;
|
|
9548
|
+
}
|
|
9549
|
+
function fileEdgeKey(edge) {
|
|
9550
|
+
return [
|
|
9551
|
+
edge.layer,
|
|
9552
|
+
edge.sourceRepo,
|
|
9553
|
+
edge.sourcePath,
|
|
9554
|
+
edge.targetRepo,
|
|
9555
|
+
edge.targetPath ?? "",
|
|
9556
|
+
edge.relationship
|
|
9557
|
+
].join("\0");
|
|
9558
|
+
}
|
|
9559
|
+
function repoEdgeKey(edge) {
|
|
9560
|
+
return [edge.layer, edge.sourceRepo, edge.targetRepo, edge.relationship].join("\0");
|
|
9561
|
+
}
|
|
9562
|
+
function upsertEdge(map, edge, minConfidence2, minEvidence) {
|
|
9563
|
+
const layer = edge.layer ?? "file";
|
|
9564
|
+
if (edge.sourceRepo === edge.targetRepo) return { inserted: false, updated: false };
|
|
9565
|
+
const key = layer === "repo" ? repoEdgeKey({ ...edge, layer }) : fileEdgeKey({ ...edge, layer });
|
|
9566
|
+
const existing = map.get(key);
|
|
9567
|
+
if (!existing) {
|
|
9568
|
+
const created = {
|
|
9569
|
+
...edge,
|
|
9570
|
+
layer,
|
|
9571
|
+
evidence: uniqueEvidenceRefs(edge.evidence),
|
|
9572
|
+
matchReasons: mergeReasons([], edge.matchReasons),
|
|
9573
|
+
evidenceCount: 0,
|
|
9574
|
+
weak: false
|
|
9575
|
+
};
|
|
9576
|
+
updateWeakFlag(created, minConfidence2, minEvidence);
|
|
9577
|
+
map.set(key, created);
|
|
9578
|
+
return { inserted: true, updated: false };
|
|
9579
|
+
}
|
|
9580
|
+
const merged = {
|
|
9581
|
+
...existing,
|
|
9582
|
+
sourcePath: existing.layer === "repo" ? "*" : existing.sourcePath,
|
|
9583
|
+
targetPath: existing.layer === "repo" ? void 0 : existing.targetPath ?? edge.targetPath,
|
|
9584
|
+
evidence: uniqueEvidenceRefs([...existing.evidence, ...edge.evidence]),
|
|
9585
|
+
matchReasons: mergeReasons(existing.matchReasons, edge.matchReasons),
|
|
9586
|
+
confidence: Math.max(existing.confidence, edge.confidence),
|
|
9587
|
+
evidenceCount: 0,
|
|
9588
|
+
weak: false
|
|
9589
|
+
};
|
|
9590
|
+
updateWeakFlag(merged, minConfidence2, minEvidence);
|
|
9591
|
+
map.set(key, merged);
|
|
9592
|
+
return { inserted: false, updated: true };
|
|
9593
|
+
}
|
|
9594
|
+
function scorePackageDependency(dependency) {
|
|
9595
|
+
const normalized = sanitizeHistoricalText(dependency);
|
|
9596
|
+
const reasons = ["exact_package_dependency"];
|
|
9597
|
+
const confidence = normalized.startsWith("@") ? 0.93 : 0.9;
|
|
9598
|
+
return { confidence, reasons };
|
|
9599
|
+
}
|
|
9600
|
+
function scoreImportEdge(input) {
|
|
9601
|
+
let score = 0.58;
|
|
9602
|
+
const reasons = ["cross_repo_import"];
|
|
9603
|
+
if (input.specifier.includes("/")) {
|
|
9604
|
+
score += 0.12;
|
|
9605
|
+
reasons.push("qualified_specifier");
|
|
9606
|
+
}
|
|
9607
|
+
if (input.importedPath) {
|
|
9608
|
+
score += 0.16;
|
|
9609
|
+
reasons.push("resolved_import_path");
|
|
9610
|
+
}
|
|
9611
|
+
if (input.importedSymbols.length > 0) {
|
|
9612
|
+
score += 0.11;
|
|
9613
|
+
reasons.push("explicit_import_symbols");
|
|
9614
|
+
}
|
|
9615
|
+
return { confidence: clampConfidence(score), reasons };
|
|
9616
|
+
}
|
|
9617
|
+
function scoreContract(input) {
|
|
9618
|
+
let score = 0.66;
|
|
9619
|
+
if (input.token.kind === "route") score += 0.08;
|
|
9620
|
+
if (input.token.kind === "graphql") score += 0.06;
|
|
9621
|
+
if (input.token.kind === "schema") score += 0.03;
|
|
9622
|
+
if (/\b(route|controller|api|schema|client)\b/i.test(input.filePath)) score += 0.05;
|
|
9623
|
+
if (input.token.symbols.length > 0) score += 0.04;
|
|
9624
|
+
return clampConfidence(score);
|
|
9625
|
+
}
|
|
9626
|
+
function overlapScore(a, b) {
|
|
9627
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
9628
|
+
const set = new Set(a);
|
|
9629
|
+
let overlaps = 0;
|
|
9630
|
+
for (const value of b) {
|
|
9631
|
+
if (set.has(value)) overlaps += 1;
|
|
9632
|
+
}
|
|
9633
|
+
return overlaps / Math.max(1, Math.min(a.length, b.length));
|
|
9634
|
+
}
|
|
9635
|
+
function scoreConsumerMatch(input) {
|
|
9636
|
+
let score = 0.58;
|
|
9637
|
+
const reasons = ["matched_contract_token"];
|
|
9638
|
+
if (input.consumerToken.kind === input.contract.kind) {
|
|
9639
|
+
score += 0.12;
|
|
9640
|
+
reasons.push("matching_contract_kind");
|
|
9641
|
+
}
|
|
9642
|
+
const symbolOverlap = overlapScore(
|
|
9643
|
+
uniqueStrings([...input.contract.symbols, ...splitTokenSymbols(input.contract.contract)]),
|
|
9644
|
+
uniqueStrings([...input.chunkSymbols, ...input.consumerToken.symbols])
|
|
9645
|
+
);
|
|
9646
|
+
if (symbolOverlap > 0) {
|
|
9647
|
+
score += Math.min(0.2, symbolOverlap * 0.22);
|
|
9648
|
+
reasons.push("symbol_overlap");
|
|
9649
|
+
}
|
|
9650
|
+
if (input.contract.kind === "route" && input.consumerToken.raw.includes("/")) {
|
|
9651
|
+
score += 0.08;
|
|
9652
|
+
reasons.push("route_literal_match");
|
|
9653
|
+
}
|
|
9654
|
+
return { confidence: clampConfidence(score), reasons };
|
|
9655
|
+
}
|
|
9656
|
+
function aggregateRepoEdges(fileEdges) {
|
|
9657
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
9658
|
+
for (const edge of fileEdges) {
|
|
9659
|
+
const key = repoEdgeKey({
|
|
9660
|
+
layer: "repo",
|
|
9661
|
+
sourceRepo: edge.sourceRepo,
|
|
9662
|
+
targetRepo: edge.targetRepo,
|
|
9663
|
+
relationship: edge.relationship
|
|
9664
|
+
});
|
|
9665
|
+
const bucket = grouped.get(key) ?? [];
|
|
9666
|
+
bucket.push(edge);
|
|
9667
|
+
grouped.set(key, bucket);
|
|
9668
|
+
}
|
|
9669
|
+
const repoEdges = [];
|
|
9670
|
+
for (const [key, group] of grouped.entries()) {
|
|
9671
|
+
const [layerValue = "repo", sourceRepo = "", targetRepo = ""] = key.split("\0");
|
|
9672
|
+
const relationship = group[0]?.relationship ?? "imports";
|
|
9673
|
+
const confidences = group.map((edge) => edge.confidence);
|
|
9674
|
+
const maxConfidence = Math.max(...confidences);
|
|
9675
|
+
const avgConfidence = confidences.reduce((sum, value) => sum + value, 0) / confidences.length;
|
|
9676
|
+
const repetitionBoost = Math.min(0.18, Math.log2(group.length + 1) * 0.06);
|
|
9677
|
+
const confidence = clampConfidence(maxConfidence * 0.6 + avgConfidence * 0.25 + repetitionBoost);
|
|
9678
|
+
const evidence = uniqueEvidenceRefs(group.flatMap((edge) => edge.evidence));
|
|
9679
|
+
const matchReasons6 = mergeReasons([], group.flatMap((edge) => edge.matchReasons));
|
|
9680
|
+
const repoEdge = {
|
|
9681
|
+
org: group[0]?.org ?? "",
|
|
9682
|
+
sourceRepo,
|
|
9683
|
+
sourcePath: "*",
|
|
9684
|
+
targetRepo,
|
|
9685
|
+
targetPath: void 0,
|
|
9686
|
+
layer: layerValue,
|
|
9687
|
+
relationship,
|
|
9688
|
+
evidence,
|
|
9689
|
+
matchReasons: matchReasons6,
|
|
9690
|
+
evidenceCount: 0,
|
|
9691
|
+
weak: false,
|
|
9692
|
+
confidence
|
|
9693
|
+
};
|
|
9694
|
+
updateWeakFlag(repoEdge, MIN_REPO_EDGE_CONFIDENCE, MIN_VISIBLE_EVIDENCE);
|
|
9695
|
+
repoEdges.push(repoEdge);
|
|
9696
|
+
}
|
|
9697
|
+
return repoEdges.sort((a, b) => b.confidence - a.confidence);
|
|
9698
|
+
}
|
|
9699
|
+
function buildQuality(repoEdges, hiddenRepoEdges) {
|
|
9700
|
+
const distribution = { ...DEFAULT_EDGE_DISTRIBUTION2 };
|
|
9701
|
+
for (const edge of repoEdges) {
|
|
9702
|
+
distribution[confidenceBucket(edge.confidence)] += 1;
|
|
9703
|
+
}
|
|
9704
|
+
return {
|
|
9705
|
+
edgeConfidenceDistribution: distribution,
|
|
9706
|
+
weakEdgesFiltered: hiddenRepoEdges.length,
|
|
9707
|
+
minVisibleConfidence: MIN_REPO_EDGE_CONFIDENCE,
|
|
9708
|
+
minVisibleEvidence: MIN_VISIBLE_EVIDENCE
|
|
9709
|
+
};
|
|
9710
|
+
}
|
|
9711
|
+
function isVisibleRepoEdge(edge) {
|
|
9712
|
+
return !edge.weak && edge.confidence >= MIN_REPO_EDGE_CONFIDENCE && edge.evidenceCount >= MIN_VISIBLE_EVIDENCE;
|
|
9713
|
+
}
|
|
9335
9714
|
function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
9336
9715
|
initializeSchema(db);
|
|
9337
9716
|
const options = resolveOptions(baseDirOrOptions);
|
|
9338
9717
|
const startedAt = Date.now();
|
|
9339
9718
|
try {
|
|
9719
|
+
const enabledRepos = config.repos.filter((repo) => repo.enabled);
|
|
9720
|
+
const repoByName = new Map(enabledRepos.map((repo) => [repo.fullName, repo]));
|
|
9340
9721
|
options.onProgress?.({
|
|
9341
9722
|
stage: "loading_package_manifests",
|
|
9342
9723
|
org: config.org,
|
|
9343
|
-
totalRepos:
|
|
9724
|
+
totalRepos: enabledRepos.length
|
|
9344
9725
|
});
|
|
9345
9726
|
const packageNames = repoPackageNames(config, options.baseDir);
|
|
9346
|
-
const enabledRepos = config.repos.filter((repo) => repo.enabled);
|
|
9347
|
-
const repoByName = new Map(enabledRepos.map((repo) => [repo.fullName, repo]));
|
|
9348
9727
|
const packageToRepo = /* @__PURE__ */ new Map();
|
|
9349
9728
|
for (const [repo, names] of packageNames.entries()) {
|
|
9350
9729
|
for (const name of names) packageToRepo.set(name, repo);
|
|
@@ -9355,31 +9734,22 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9355
9734
|
repos: enabledRepos.length,
|
|
9356
9735
|
packageNames: packageToRepo.size
|
|
9357
9736
|
});
|
|
9358
|
-
const
|
|
9359
|
-
const
|
|
9360
|
-
|
|
9361
|
-
if (edge.sourceRepo === edge.targetRepo) return;
|
|
9362
|
-
const key = [
|
|
9363
|
-
edge.sourceRepo,
|
|
9364
|
-
edge.sourcePath,
|
|
9365
|
-
edge.targetRepo,
|
|
9366
|
-
edge.targetPath ?? "",
|
|
9367
|
-
edge.relationship
|
|
9368
|
-
].join("\0");
|
|
9369
|
-
if (edgeKeys.has(key)) return;
|
|
9370
|
-
edgeKeys.add(key);
|
|
9371
|
-
edges.push(edge);
|
|
9737
|
+
const fileEdgeMap = /* @__PURE__ */ new Map();
|
|
9738
|
+
const addFileEdge = (edge) => {
|
|
9739
|
+
upsertEdge(fileEdgeMap, { ...edge, layer: "file" }, MIN_FILE_EDGE_CONFIDENCE, 1);
|
|
9372
9740
|
};
|
|
9373
9741
|
enabledRepos.forEach((repo, index) => {
|
|
9374
9742
|
const manifest = readPackageManifest(orgRepoLocalPath(config.org, repo, options.baseDir));
|
|
9375
9743
|
for (const dependency of dependenciesFor(manifest)) {
|
|
9376
9744
|
const targetRepo = packageToRepo.get(dependency);
|
|
9377
9745
|
if (!targetRepo || targetRepo === repo.fullName) continue;
|
|
9378
|
-
|
|
9746
|
+
const score = scorePackageDependency(dependency);
|
|
9747
|
+
addFileEdge({
|
|
9379
9748
|
org: config.org,
|
|
9380
9749
|
sourceRepo: repo.fullName,
|
|
9381
9750
|
sourcePath: "package.json",
|
|
9382
9751
|
targetRepo,
|
|
9752
|
+
targetPath: "package.json",
|
|
9383
9753
|
relationship: "depends_on_package",
|
|
9384
9754
|
evidence: [
|
|
9385
9755
|
fileEvidence(
|
|
@@ -9388,7 +9758,8 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9388
9758
|
`depends on ${sanitizeHistoricalText(dependency)}`
|
|
9389
9759
|
)
|
|
9390
9760
|
],
|
|
9391
|
-
|
|
9761
|
+
matchReasons: score.reasons,
|
|
9762
|
+
confidence: score.confidence
|
|
9392
9763
|
});
|
|
9393
9764
|
}
|
|
9394
9765
|
options.onProgress?.({
|
|
@@ -9397,7 +9768,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9397
9768
|
current: index + 1,
|
|
9398
9769
|
total: enabledRepos.length,
|
|
9399
9770
|
repo: repo.fullName,
|
|
9400
|
-
edges:
|
|
9771
|
+
edges: fileEdgeMap.size
|
|
9401
9772
|
});
|
|
9402
9773
|
});
|
|
9403
9774
|
options.onProgress?.({ stage: "loading_imports", org: config.org });
|
|
@@ -9412,7 +9783,13 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9412
9783
|
const rootSpecifier = packageRootForSpecifier(item.specifier);
|
|
9413
9784
|
const targetRepo = packageToRepo.get(rootSpecifier) ?? packageToRepo.get(item.specifier);
|
|
9414
9785
|
if (targetRepo && targetRepo !== item.repo) {
|
|
9415
|
-
|
|
9786
|
+
const importedSymbols = parseJsonArray10(item.imported_symbols_json);
|
|
9787
|
+
const score = scoreImportEdge({
|
|
9788
|
+
specifier: item.specifier,
|
|
9789
|
+
importedPath: item.imported_path,
|
|
9790
|
+
importedSymbols
|
|
9791
|
+
});
|
|
9792
|
+
addFileEdge({
|
|
9416
9793
|
org: config.org,
|
|
9417
9794
|
sourceRepo: item.repo,
|
|
9418
9795
|
sourcePath: item.source_path,
|
|
@@ -9426,7 +9803,8 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9426
9803
|
`imports ${sanitizeHistoricalText(rootSpecifier || item.specifier)}`
|
|
9427
9804
|
)
|
|
9428
9805
|
],
|
|
9429
|
-
|
|
9806
|
+
matchReasons: score.reasons,
|
|
9807
|
+
confidence: score.confidence
|
|
9430
9808
|
});
|
|
9431
9809
|
}
|
|
9432
9810
|
if (shouldEmitProgress3(index + 1, imports.length)) {
|
|
@@ -9436,7 +9814,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9436
9814
|
current: index + 1,
|
|
9437
9815
|
total: imports.length,
|
|
9438
9816
|
sourcePath: item.source_path,
|
|
9439
|
-
edges:
|
|
9817
|
+
edges: fileEdgeMap.size
|
|
9440
9818
|
});
|
|
9441
9819
|
}
|
|
9442
9820
|
});
|
|
@@ -9450,25 +9828,27 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9450
9828
|
(chunk) => repoByName.has(chunk.repo) && isApiProviderPath(chunk.file_path)
|
|
9451
9829
|
);
|
|
9452
9830
|
const apiContracts = [];
|
|
9453
|
-
const
|
|
9831
|
+
const contractByKey = /* @__PURE__ */ new Map();
|
|
9454
9832
|
const contractsByToken = /* @__PURE__ */ new Map();
|
|
9455
9833
|
providerChunks.forEach((chunk, index) => {
|
|
9456
|
-
for (const
|
|
9457
|
-
const
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
contractKeys.add(key);
|
|
9461
|
-
const apiContract = {
|
|
9834
|
+
for (const token of extractContracts(chunk.sanitized_text)) {
|
|
9835
|
+
const key = [chunk.repo, chunk.file_path, token.kind, token.normalized].join("\0");
|
|
9836
|
+
if (contractByKey.has(key)) continue;
|
|
9837
|
+
const contract = {
|
|
9462
9838
|
repo: chunk.repo,
|
|
9463
9839
|
filePath: chunk.file_path,
|
|
9464
|
-
contract:
|
|
9465
|
-
|
|
9466
|
-
|
|
9840
|
+
contract: token.raw,
|
|
9841
|
+
normalizedContract: token.normalized,
|
|
9842
|
+
kind: token.kind,
|
|
9843
|
+
symbols: token.symbols,
|
|
9844
|
+
evidence: [fileEvidence(chunk.repo, chunk.file_path, `defines ${token.raw}`)],
|
|
9845
|
+
confidence: scoreContract({ token, filePath: chunk.file_path })
|
|
9467
9846
|
};
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
bucket.
|
|
9471
|
-
|
|
9847
|
+
contractByKey.set(key, contract);
|
|
9848
|
+
apiContracts.push(contract);
|
|
9849
|
+
const bucket = contractsByToken.get(contract.normalizedContract) ?? [];
|
|
9850
|
+
bucket.push(contract);
|
|
9851
|
+
contractsByToken.set(contract.normalizedContract, bucket);
|
|
9472
9852
|
}
|
|
9473
9853
|
if (shouldEmitProgress3(index + 1, providerChunks.length)) {
|
|
9474
9854
|
options.onProgress?.({
|
|
@@ -9481,28 +9861,39 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9481
9861
|
});
|
|
9482
9862
|
}
|
|
9483
9863
|
});
|
|
9484
|
-
const apiConsumers = [];
|
|
9485
|
-
const consumerKeys = /* @__PURE__ */ new Set();
|
|
9486
9864
|
const consumerChunks = chunks.filter(
|
|
9487
9865
|
(chunk) => repoByName.has(chunk.repo) && isApiConsumerText(chunk.sanitized_text)
|
|
9488
9866
|
);
|
|
9867
|
+
const apiConsumers = [];
|
|
9868
|
+
const consumerKeySet = /* @__PURE__ */ new Set();
|
|
9489
9869
|
consumerChunks.forEach((chunk, index) => {
|
|
9490
|
-
const
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9870
|
+
const chunkTokens = extractContracts(chunk.sanitized_text);
|
|
9871
|
+
const chunkSymbols = parseJsonArray10(chunk.symbols_json);
|
|
9872
|
+
let matchesForChunk = 0;
|
|
9873
|
+
for (const consumerToken of chunkTokens) {
|
|
9874
|
+
const contracts = contractsByToken.get(consumerToken.normalized);
|
|
9875
|
+
if (!contracts?.length) continue;
|
|
9495
9876
|
for (const contract of contracts) {
|
|
9496
9877
|
if (chunk.repo === contract.repo) continue;
|
|
9878
|
+
const score = scoreConsumerMatch({
|
|
9879
|
+
consumerToken,
|
|
9880
|
+
contract,
|
|
9881
|
+
chunkSymbols
|
|
9882
|
+
});
|
|
9883
|
+
if (score.confidence < MIN_API_CONSUMER_CONFIDENCE) continue;
|
|
9497
9884
|
const consumerKey = [
|
|
9498
9885
|
contract.repo,
|
|
9499
9886
|
contract.filePath,
|
|
9500
9887
|
chunk.repo,
|
|
9501
9888
|
chunk.file_path,
|
|
9502
|
-
contract.
|
|
9889
|
+
contract.normalizedContract
|
|
9503
9890
|
].join("\0");
|
|
9504
|
-
if (
|
|
9505
|
-
|
|
9891
|
+
if (consumerKeySet.has(consumerKey)) continue;
|
|
9892
|
+
consumerKeySet.add(consumerKey);
|
|
9893
|
+
const evidence = uniqueEvidenceRefs([
|
|
9894
|
+
...contract.evidence,
|
|
9895
|
+
fileEvidence(chunk.repo, chunk.file_path, `consumes ${contract.contract}`)
|
|
9896
|
+
]);
|
|
9506
9897
|
const consumer = {
|
|
9507
9898
|
org: config.org,
|
|
9508
9899
|
providerRepo: contract.repo,
|
|
@@ -9510,23 +9901,24 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9510
9901
|
consumerRepo: chunk.repo,
|
|
9511
9902
|
consumerPath: chunk.file_path,
|
|
9512
9903
|
contract: contract.contract,
|
|
9513
|
-
evidence
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
confidence:
|
|
9904
|
+
evidence,
|
|
9905
|
+
matchReasons: mergeReasons([], score.reasons),
|
|
9906
|
+
evidenceCount: evidence.length,
|
|
9907
|
+
weak: score.confidence < MIN_API_CONSUMER_CONFIDENCE || evidence.length < MIN_VISIBLE_EVIDENCE,
|
|
9908
|
+
confidence: score.confidence
|
|
9518
9909
|
};
|
|
9519
|
-
chunkMatches += 1;
|
|
9520
9910
|
apiConsumers.push(consumer);
|
|
9521
|
-
|
|
9911
|
+
matchesForChunk += 1;
|
|
9912
|
+
addFileEdge({
|
|
9522
9913
|
org: config.org,
|
|
9523
9914
|
sourceRepo: chunk.repo,
|
|
9524
9915
|
sourcePath: chunk.file_path,
|
|
9525
9916
|
targetRepo: contract.repo,
|
|
9526
9917
|
targetPath: contract.filePath,
|
|
9527
9918
|
relationship: "api_consumer",
|
|
9528
|
-
evidence
|
|
9529
|
-
|
|
9919
|
+
evidence,
|
|
9920
|
+
matchReasons: score.reasons,
|
|
9921
|
+
confidence: score.confidence
|
|
9530
9922
|
});
|
|
9531
9923
|
}
|
|
9532
9924
|
}
|
|
@@ -9537,46 +9929,79 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9537
9929
|
current: index + 1,
|
|
9538
9930
|
total: consumerChunks.length,
|
|
9539
9931
|
filePath: chunk.file_path,
|
|
9540
|
-
matches:
|
|
9932
|
+
matches: matchesForChunk
|
|
9541
9933
|
});
|
|
9542
9934
|
}
|
|
9543
9935
|
});
|
|
9936
|
+
const allFileEdges = [...fileEdgeMap.values()].sort((a, b) => b.confidence - a.confidence);
|
|
9937
|
+
const repoEdges = aggregateRepoEdges(allFileEdges);
|
|
9938
|
+
const visibleRepoEdges = repoEdges.filter(isVisibleRepoEdge);
|
|
9939
|
+
const hiddenRepoEdges = repoEdges.filter((edge) => !isVisibleRepoEdge(edge));
|
|
9940
|
+
const hiddenFileEdges = allFileEdges.filter(
|
|
9941
|
+
(edge) => edge.confidence < MIN_FILE_EDGE_CONFIDENCE || edge.evidenceCount < 1
|
|
9942
|
+
);
|
|
9943
|
+
const visibleFileEdges = allFileEdges.filter((edge) => !hiddenFileEdges.includes(edge));
|
|
9944
|
+
const quality = buildQuality(visibleRepoEdges, hiddenRepoEdges);
|
|
9544
9945
|
options.onProgress?.({
|
|
9545
9946
|
stage: "writing_org_graph",
|
|
9546
9947
|
org: config.org,
|
|
9547
|
-
edges:
|
|
9948
|
+
edges: repoEdges.length,
|
|
9548
9949
|
apiContracts: apiContracts.length,
|
|
9549
9950
|
apiConsumers: apiConsumers.length
|
|
9550
9951
|
});
|
|
9551
9952
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9953
|
+
const renderPrepStartedAt = Date.now();
|
|
9552
9954
|
const transaction = db.transaction(() => {
|
|
9553
9955
|
db.prepare("DELETE FROM org_cross_repo_edges WHERE org = ?").run(config.org);
|
|
9554
9956
|
db.prepare("DELETE FROM org_api_contracts WHERE org = ?").run(config.org);
|
|
9555
9957
|
db.prepare("DELETE FROM org_api_consumers WHERE org = ?").run(config.org);
|
|
9556
9958
|
const insertEdge = db.prepare(
|
|
9557
9959
|
`INSERT INTO org_cross_repo_edges
|
|
9558
|
-
(id, org, source_repo, source_path, target_repo, target_path,
|
|
9559
|
-
|
|
9960
|
+
(id, org, source_repo, source_path, target_repo, target_path, layer, relationship,
|
|
9961
|
+
evidence_json, match_reasons_json, evidence_count, is_weak, confidence, created_at)
|
|
9962
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9560
9963
|
ON CONFLICT(id) DO UPDATE SET
|
|
9964
|
+
source_path = excluded.source_path,
|
|
9965
|
+
target_path = excluded.target_path,
|
|
9966
|
+
layer = excluded.layer,
|
|
9561
9967
|
evidence_json = excluded.evidence_json,
|
|
9968
|
+
match_reasons_json = excluded.match_reasons_json,
|
|
9969
|
+
evidence_count = excluded.evidence_count,
|
|
9970
|
+
is_weak = excluded.is_weak,
|
|
9562
9971
|
confidence = excluded.confidence,
|
|
9563
9972
|
created_at = excluded.created_at`
|
|
9564
9973
|
);
|
|
9565
|
-
|
|
9974
|
+
const persistEdge = (edge) => {
|
|
9566
9975
|
insertEdge.run(
|
|
9567
|
-
`oge_${stableId([
|
|
9976
|
+
`oge_${stableId([
|
|
9977
|
+
edge.org,
|
|
9978
|
+
edge.layer,
|
|
9979
|
+
edge.sourceRepo,
|
|
9980
|
+
edge.sourcePath,
|
|
9981
|
+
edge.targetRepo,
|
|
9982
|
+
edge.targetPath ?? "",
|
|
9983
|
+
edge.relationship
|
|
9984
|
+
])}`,
|
|
9568
9985
|
edge.org,
|
|
9569
9986
|
edge.sourceRepo,
|
|
9570
9987
|
edge.sourcePath,
|
|
9571
9988
|
edge.targetRepo,
|
|
9572
9989
|
edge.targetPath ?? null,
|
|
9990
|
+
edge.layer,
|
|
9573
9991
|
edge.relationship,
|
|
9574
9992
|
evidenceJson(edge.evidence),
|
|
9993
|
+
JSON.stringify(edge.matchReasons),
|
|
9994
|
+
edge.evidenceCount,
|
|
9995
|
+
edge.weak ? 1 : 0,
|
|
9575
9996
|
edge.confidence,
|
|
9576
9997
|
now
|
|
9577
9998
|
);
|
|
9999
|
+
};
|
|
10000
|
+
const persistedEdges = [...repoEdges, ...allFileEdges];
|
|
10001
|
+
for (const [index, edge] of persistedEdges.entries()) {
|
|
10002
|
+
persistEdge(edge);
|
|
9578
10003
|
const current = index + 1;
|
|
9579
|
-
if (shouldEmitProgress3(current,
|
|
10004
|
+
if (shouldEmitProgress3(current, persistedEdges.length, 500)) {
|
|
9580
10005
|
options.onProgress?.({
|
|
9581
10006
|
stage: "writing_org_graph",
|
|
9582
10007
|
org: config.org,
|
|
@@ -9584,7 +10009,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9584
10009
|
apiContracts: apiContracts.length,
|
|
9585
10010
|
apiConsumers: apiConsumers.length,
|
|
9586
10011
|
current,
|
|
9587
|
-
total:
|
|
10012
|
+
total: persistedEdges.length,
|
|
9588
10013
|
kind: "edges"
|
|
9589
10014
|
});
|
|
9590
10015
|
}
|
|
@@ -9601,7 +10026,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9601
10026
|
);
|
|
9602
10027
|
for (const [index, contract] of apiContracts.entries()) {
|
|
9603
10028
|
insertContract.run(
|
|
9604
|
-
`oac_${stableId([config.org, contract.repo, contract.filePath, contract.
|
|
10029
|
+
`oac_${stableId([config.org, contract.repo, contract.filePath, contract.normalizedContract])}`,
|
|
9605
10030
|
config.org,
|
|
9606
10031
|
contract.repo,
|
|
9607
10032
|
contract.filePath,
|
|
@@ -9615,7 +10040,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9615
10040
|
options.onProgress?.({
|
|
9616
10041
|
stage: "writing_org_graph",
|
|
9617
10042
|
org: config.org,
|
|
9618
|
-
edges:
|
|
10043
|
+
edges: persistedEdges.length,
|
|
9619
10044
|
apiContracts: current,
|
|
9620
10045
|
apiConsumers: apiConsumers.length,
|
|
9621
10046
|
current,
|
|
@@ -9626,11 +10051,15 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9626
10051
|
}
|
|
9627
10052
|
const insertConsumer = db.prepare(
|
|
9628
10053
|
`INSERT INTO org_api_consumers
|
|
9629
|
-
(id, org, provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
9630
|
-
|
|
10054
|
+
(id, org, provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
10055
|
+
match_reasons_json, evidence_count, is_weak, confidence, created_at)
|
|
10056
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9631
10057
|
ON CONFLICT(id) DO UPDATE SET
|
|
9632
10058
|
contract = excluded.contract,
|
|
9633
10059
|
evidence_json = excluded.evidence_json,
|
|
10060
|
+
match_reasons_json = excluded.match_reasons_json,
|
|
10061
|
+
evidence_count = excluded.evidence_count,
|
|
10062
|
+
is_weak = excluded.is_weak,
|
|
9634
10063
|
confidence = excluded.confidence,
|
|
9635
10064
|
created_at = excluded.created_at`
|
|
9636
10065
|
);
|
|
@@ -9642,7 +10071,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9642
10071
|
consumer.providerPath ?? "",
|
|
9643
10072
|
consumer.consumerRepo,
|
|
9644
10073
|
consumer.consumerPath,
|
|
9645
|
-
consumer.contract
|
|
10074
|
+
normalizeToken(consumer.contract)
|
|
9646
10075
|
])}`,
|
|
9647
10076
|
consumer.org,
|
|
9648
10077
|
consumer.providerRepo,
|
|
@@ -9651,6 +10080,9 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9651
10080
|
consumer.consumerPath,
|
|
9652
10081
|
sanitizeHistoricalText(consumer.contract),
|
|
9653
10082
|
evidenceJson(consumer.evidence),
|
|
10083
|
+
JSON.stringify(consumer.matchReasons),
|
|
10084
|
+
consumer.evidenceCount,
|
|
10085
|
+
consumer.weak ? 1 : 0,
|
|
9654
10086
|
consumer.confidence,
|
|
9655
10087
|
now
|
|
9656
10088
|
);
|
|
@@ -9659,7 +10091,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9659
10091
|
options.onProgress?.({
|
|
9660
10092
|
stage: "writing_org_graph",
|
|
9661
10093
|
org: config.org,
|
|
9662
|
-
edges:
|
|
10094
|
+
edges: persistedEdges.length,
|
|
9663
10095
|
apiContracts: apiContracts.length,
|
|
9664
10096
|
apiConsumers: current,
|
|
9665
10097
|
current,
|
|
@@ -9670,6 +10102,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9670
10102
|
}
|
|
9671
10103
|
});
|
|
9672
10104
|
transaction();
|
|
10105
|
+
const renderPrepMs = Date.now() - renderPrepStartedAt;
|
|
9673
10106
|
const durationMs = Date.now() - startedAt;
|
|
9674
10107
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9675
10108
|
recordOrgGraphState(db, {
|
|
@@ -9677,22 +10110,40 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9677
10110
|
status: "success",
|
|
9678
10111
|
builtAt: finishedAt,
|
|
9679
10112
|
durationMs,
|
|
9680
|
-
edgeCount:
|
|
10113
|
+
edgeCount: repoEdges.length,
|
|
10114
|
+
visibleEdgeCount: visibleRepoEdges.length,
|
|
10115
|
+
weakEdgeCount: hiddenRepoEdges.length,
|
|
10116
|
+
edgeConfidenceDistribution: quality.edgeConfidenceDistribution,
|
|
10117
|
+
lastRenderPrepMs: renderPrepMs,
|
|
9681
10118
|
apiContractCount: apiContracts.length,
|
|
9682
10119
|
apiConsumerCount: apiConsumers.length
|
|
9683
10120
|
});
|
|
9684
10121
|
options.onProgress?.({
|
|
9685
10122
|
stage: "completed_org_graph",
|
|
9686
10123
|
org: config.org,
|
|
9687
|
-
edges:
|
|
10124
|
+
edges: repoEdges.length,
|
|
9688
10125
|
apiContracts: apiContracts.length,
|
|
9689
10126
|
apiConsumers: apiConsumers.length,
|
|
9690
10127
|
durationMs
|
|
9691
10128
|
});
|
|
9692
10129
|
return {
|
|
9693
|
-
edges,
|
|
10130
|
+
edges: visibleRepoEdges,
|
|
10131
|
+
repoEdges: visibleRepoEdges,
|
|
10132
|
+
fileEdges: visibleFileEdges,
|
|
10133
|
+
hiddenFileEdges,
|
|
10134
|
+
hiddenRepoEdges,
|
|
9694
10135
|
apiConsumers,
|
|
9695
|
-
apiContracts
|
|
10136
|
+
apiContracts: apiContracts.map((contract) => ({
|
|
10137
|
+
repo: contract.repo,
|
|
10138
|
+
filePath: contract.filePath,
|
|
10139
|
+
contract: contract.contract,
|
|
10140
|
+
evidence: contract.evidence,
|
|
10141
|
+
confidence: contract.confidence
|
|
10142
|
+
})),
|
|
10143
|
+
quality: {
|
|
10144
|
+
...quality,
|
|
10145
|
+
lastRenderPrepMs: renderPrepMs
|
|
10146
|
+
},
|
|
9696
10147
|
durationMs
|
|
9697
10148
|
};
|
|
9698
10149
|
} catch (error) {
|
|
@@ -10036,6 +10487,8 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
10036
10487
|
org: config.org,
|
|
10037
10488
|
status: "skipped",
|
|
10038
10489
|
edgeCount: counts.edges,
|
|
10490
|
+
visibleEdgeCount: counts.visibleEdges,
|
|
10491
|
+
weakEdgeCount: counts.weakEdges,
|
|
10039
10492
|
apiContractCount: counts.apiContracts,
|
|
10040
10493
|
apiConsumerCount: counts.apiConsumers
|
|
10041
10494
|
});
|
|
@@ -10045,7 +10498,12 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
10045
10498
|
command,
|
|
10046
10499
|
reason: "Graph skipped because --no-graph was passed."
|
|
10047
10500
|
});
|
|
10048
|
-
graph = {
|
|
10501
|
+
graph = {
|
|
10502
|
+
edges: counts.visibleEdges,
|
|
10503
|
+
apiConsumers: counts.apiConsumers,
|
|
10504
|
+
apiContracts: counts.apiContracts,
|
|
10505
|
+
skipped: true
|
|
10506
|
+
};
|
|
10049
10507
|
} else {
|
|
10050
10508
|
try {
|
|
10051
10509
|
const rebuiltGraph = rebuildOrgGraph(db, config, {
|
|
@@ -10060,7 +10518,12 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
10060
10518
|
} catch (error) {
|
|
10061
10519
|
const message = error instanceof Error ? error.message : String(error);
|
|
10062
10520
|
const counts = getOrgGraphCounts(db, config.org);
|
|
10063
|
-
graph = {
|
|
10521
|
+
graph = {
|
|
10522
|
+
edges: counts.visibleEdges,
|
|
10523
|
+
apiConsumers: counts.apiConsumers,
|
|
10524
|
+
apiContracts: counts.apiContracts,
|
|
10525
|
+
error: message
|
|
10526
|
+
};
|
|
10064
10527
|
}
|
|
10065
10528
|
}
|
|
10066
10529
|
recordOrgIndexRun(db, {
|
|
@@ -10104,6 +10567,15 @@ function parseEvidence2(value) {
|
|
|
10104
10567
|
return [];
|
|
10105
10568
|
}
|
|
10106
10569
|
}
|
|
10570
|
+
function parseStringArray(value) {
|
|
10571
|
+
if (!value) return [];
|
|
10572
|
+
try {
|
|
10573
|
+
const parsed = JSON.parse(value);
|
|
10574
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
10575
|
+
} catch {
|
|
10576
|
+
return [];
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10107
10579
|
function fileEvidence2(repo, filePath, note) {
|
|
10108
10580
|
return {
|
|
10109
10581
|
prNumber: 0,
|
|
@@ -10135,7 +10607,7 @@ function affectedConsumers(db, org, repo, changedFiles) {
|
|
|
10135
10607
|
const rows = db.prepare(
|
|
10136
10608
|
`SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json, confidence
|
|
10137
10609
|
FROM org_api_consumers
|
|
10138
|
-
WHERE org =
|
|
10610
|
+
WHERE org = ? AND is_weak = 0`
|
|
10139
10611
|
).all(org);
|
|
10140
10612
|
return rows.filter((row) => !repo || row.provider_repo === repo || row.consumer_repo === repo).filter((row) => {
|
|
10141
10613
|
if (changedFiles.length === 0) return true;
|
|
@@ -10150,14 +10622,18 @@ function affectedConsumers(db, org, repo, changedFiles) {
|
|
|
10150
10622
|
consumerPath: row.consumer_path,
|
|
10151
10623
|
contract: sanitizeHistoricalText(row.contract),
|
|
10152
10624
|
evidence: parseEvidence2(row.evidence_json),
|
|
10625
|
+
matchReasons: parseStringArray(row.match_reasons_json),
|
|
10626
|
+
evidenceCount: row.evidence_count ?? parseEvidence2(row.evidence_json).length,
|
|
10627
|
+
weak: (row.is_weak ?? 0) === 1,
|
|
10153
10628
|
confidence: row.confidence
|
|
10154
10629
|
}));
|
|
10155
10630
|
}
|
|
10156
10631
|
function affectedEdges(db, org, repo, changedFiles) {
|
|
10157
10632
|
const rows = db.prepare(
|
|
10158
|
-
`SELECT source_repo, source_path, target_repo, target_path, relationship, evidence_json,
|
|
10633
|
+
`SELECT source_repo, source_path, target_repo, target_path, layer, relationship, evidence_json,
|
|
10634
|
+
match_reasons_json, evidence_count, is_weak, confidence
|
|
10159
10635
|
FROM org_cross_repo_edges
|
|
10160
|
-
WHERE org =
|
|
10636
|
+
WHERE org = ? AND layer = 'file'`
|
|
10161
10637
|
).all(org);
|
|
10162
10638
|
return rows.filter((row) => !repo || row.source_repo === repo || row.target_repo === repo).filter((row) => {
|
|
10163
10639
|
if (changedFiles.length === 0) return true;
|
|
@@ -10168,8 +10644,12 @@ function affectedEdges(db, org, repo, changedFiles) {
|
|
|
10168
10644
|
sourcePath: row.source_path,
|
|
10169
10645
|
targetRepo: row.target_repo,
|
|
10170
10646
|
targetPath: row.target_path ?? void 0,
|
|
10647
|
+
layer: row.layer,
|
|
10171
10648
|
relationship: row.relationship,
|
|
10172
10649
|
evidence: parseEvidence2(row.evidence_json),
|
|
10650
|
+
matchReasons: parseStringArray(row.match_reasons_json),
|
|
10651
|
+
evidenceCount: row.evidence_count ?? parseEvidence2(row.evidence_json).length,
|
|
10652
|
+
weak: (row.is_weak ?? 0) === 1,
|
|
10173
10653
|
confidence: row.confidence
|
|
10174
10654
|
}));
|
|
10175
10655
|
}
|
|
@@ -10487,7 +10967,7 @@ function evidenceLabel(evidence) {
|
|
|
10487
10967
|
if (first.prNumber > 0) return `PR #${first.prNumber}`;
|
|
10488
10968
|
return first.filePath ? `file ${first.filePath}` : first.note ?? "local file evidence";
|
|
10489
10969
|
}
|
|
10490
|
-
function
|
|
10970
|
+
function parseStringArray2(value) {
|
|
10491
10971
|
try {
|
|
10492
10972
|
const parsed = JSON.parse(value);
|
|
10493
10973
|
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
@@ -10534,7 +11014,7 @@ function getWisdom(db, input, limit) {
|
|
|
10534
11014
|
const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
|
|
10535
11015
|
return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
|
|
10536
11016
|
row,
|
|
10537
|
-
score: rowScore(input, row.sanitized_text,
|
|
11017
|
+
score: rowScore(input, row.sanitized_text, parseStringArray2(row.file_paths_json), [], lowerTerms)
|
|
10538
11018
|
})).filter((item) => item.score > 0 || (input.files ?? []).length === 0).sort((a, b) => b.score - a.score || b.row.confidence - a.row.confidence).slice(0, limit).map((item) => item.row);
|
|
10539
11019
|
}
|
|
10540
11020
|
function getCodeEvidence(db, input, limit) {
|
|
@@ -10551,7 +11031,7 @@ function getCodeEvidence(db, input, limit) {
|
|
|
10551
11031
|
input,
|
|
10552
11032
|
row.sanitized_text,
|
|
10553
11033
|
[row.file_path],
|
|
10554
|
-
|
|
11034
|
+
parseStringArray2(row.symbols_json),
|
|
10555
11035
|
lowerTerms
|
|
10556
11036
|
)
|
|
10557
11037
|
})).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((item) => item.row);
|
|
@@ -10569,7 +11049,7 @@ function getArchitecture(db, input, limit) {
|
|
|
10569
11049
|
score: rowScore(
|
|
10570
11050
|
input,
|
|
10571
11051
|
row.summary_sanitized,
|
|
10572
|
-
|
|
11052
|
+
parseStringArray2(row.source_files_json),
|
|
10573
11053
|
[],
|
|
10574
11054
|
lowerTerms
|
|
10575
11055
|
)
|
|
@@ -10580,9 +11060,10 @@ function findOrgApiConsumers(db, config, input) {
|
|
|
10580
11060
|
const repoClause = input.repo ? " AND (provider_repo = ? OR consumer_repo = ?)" : "";
|
|
10581
11061
|
const repoParams = input.repo ? [input.repo, input.repo] : [];
|
|
10582
11062
|
const rows = db.prepare(
|
|
10583
|
-
`SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
11063
|
+
`SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
11064
|
+
match_reasons_json, evidence_count, is_weak, confidence
|
|
10584
11065
|
FROM org_api_consumers
|
|
10585
|
-
WHERE org =
|
|
11066
|
+
WHERE org = ? AND is_weak = 0${repoClause}
|
|
10586
11067
|
ORDER BY confidence DESC`
|
|
10587
11068
|
).all(config.org, ...repoParams);
|
|
10588
11069
|
const limit = Math.max(1, Math.min(input.maxResults ?? 8, 25));
|
|
@@ -10598,6 +11079,9 @@ function findOrgApiConsumers(db, config, input) {
|
|
|
10598
11079
|
consumerPath: row.consumer_path,
|
|
10599
11080
|
contract: sanitizeHistoricalText(row.contract),
|
|
10600
11081
|
evidence: parseEvidence3(row.evidence_json),
|
|
11082
|
+
matchReasons: parseStringArray2(row.match_reasons_json ?? "[]"),
|
|
11083
|
+
evidenceCount: row.evidence_count ?? parseEvidence3(row.evidence_json).length,
|
|
11084
|
+
weak: (row.is_weak ?? 0) === 1,
|
|
10601
11085
|
confidence: row.confidence
|
|
10602
11086
|
}));
|
|
10603
11087
|
}
|
|
@@ -10606,7 +11090,7 @@ function getOrgArchitectureMap(db, config, format = "mermaid") {
|
|
|
10606
11090
|
const rows = db.prepare(
|
|
10607
11091
|
`SELECT source_repo, source_path, target_repo, target_path, relationship, confidence
|
|
10608
11092
|
FROM org_cross_repo_edges
|
|
10609
|
-
WHERE org = ?
|
|
11093
|
+
WHERE org = ? AND layer = 'repo' AND is_weak = 0
|
|
10610
11094
|
ORDER BY confidence DESC, source_repo, target_repo`
|
|
10611
11095
|
).all(config.org);
|
|
10612
11096
|
const nodes = uniqueStrings(rows.flatMap((row) => [row.source_repo, row.target_repo])).map(
|
|
@@ -10684,7 +11168,7 @@ function buildOrgContextResult(db, config, input) {
|
|
|
10684
11168
|
if (architecture.length === 0) lines.push("- No matching architecture patterns found.");
|
|
10685
11169
|
else {
|
|
10686
11170
|
for (const pattern of architecture) {
|
|
10687
|
-
const files =
|
|
11171
|
+
const files = parseStringArray2(pattern.source_files_json);
|
|
10688
11172
|
lines.push(
|
|
10689
11173
|
`- [${pattern.repo}] [${pattern.area}] ${pattern.summary_sanitized} Evidence: ${files[0] ?? "indexed current code"}.`
|
|
10690
11174
|
);
|