@pratik7368patil/anchor-core 0.1.32 → 0.1.34
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 +598 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/db/schema.sql +12 -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,
|
|
@@ -709,6 +720,7 @@ CREATE INDEX IF NOT EXISTS idx_org_edges_source ON org_cross_repo_edges(org, sou
|
|
|
709
720
|
CREATE INDEX IF NOT EXISTS idx_org_edges_target ON org_cross_repo_edges(org, target_repo);
|
|
710
721
|
CREATE INDEX IF NOT EXISTS idx_org_consumers_provider ON org_api_consumers(org, provider_repo);
|
|
711
722
|
CREATE INDEX IF NOT EXISTS idx_org_consumers_consumer ON org_api_consumers(org, consumer_repo);
|
|
723
|
+
CREATE INDEX IF NOT EXISTS idx_org_consumers_contract ON org_api_consumers(org, contract);
|
|
712
724
|
CREATE INDEX IF NOT EXISTS idx_org_anomalies_org ON org_anomaly_events(org, severity);
|
|
713
725
|
CREATE INDEX IF NOT EXISTS idx_org_graph_state_status ON org_graph_state(org, last_status);
|
|
714
726
|
|
|
@@ -1783,6 +1795,41 @@ function initializeSchema(db) {
|
|
|
1783
1795
|
ensureColumn(db, "sync_state", "graphql_cursor_reason", "TEXT");
|
|
1784
1796
|
ensureColumn(db, "sync_state", "graphql_cursor_updated_at", "TEXT");
|
|
1785
1797
|
ensureColumn(db, "code_index_state", "last_indexed_commit", "TEXT");
|
|
1798
|
+
ensureColumn(db, "org_cross_repo_edges", "layer", "TEXT NOT NULL DEFAULT 'file'");
|
|
1799
|
+
ensureColumn(
|
|
1800
|
+
db,
|
|
1801
|
+
"org_cross_repo_edges",
|
|
1802
|
+
"match_reasons_json",
|
|
1803
|
+
"TEXT NOT NULL DEFAULT '[]'"
|
|
1804
|
+
);
|
|
1805
|
+
ensureColumn(db, "org_cross_repo_edges", "evidence_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1806
|
+
ensureColumn(db, "org_cross_repo_edges", "is_weak", "INTEGER NOT NULL DEFAULT 0");
|
|
1807
|
+
ensureColumn(
|
|
1808
|
+
db,
|
|
1809
|
+
"org_api_consumers",
|
|
1810
|
+
"match_reasons_json",
|
|
1811
|
+
"TEXT NOT NULL DEFAULT '[]'"
|
|
1812
|
+
);
|
|
1813
|
+
ensureColumn(db, "org_api_consumers", "evidence_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1814
|
+
ensureColumn(db, "org_api_consumers", "is_weak", "INTEGER NOT NULL DEFAULT 0");
|
|
1815
|
+
ensureColumn(db, "org_graph_state", "visible_edge_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1816
|
+
ensureColumn(db, "org_graph_state", "weak_edge_count", "INTEGER NOT NULL DEFAULT 0");
|
|
1817
|
+
ensureColumn(
|
|
1818
|
+
db,
|
|
1819
|
+
"org_graph_state",
|
|
1820
|
+
"edge_confidence_json",
|
|
1821
|
+
`TEXT NOT NULL DEFAULT '{"strong":0,"moderate":0,"weak":0}'`
|
|
1822
|
+
);
|
|
1823
|
+
ensureColumn(db, "org_graph_state", "last_render_prep_ms", "INTEGER");
|
|
1824
|
+
db.exec(
|
|
1825
|
+
"CREATE INDEX IF NOT EXISTS idx_org_edges_layer ON org_cross_repo_edges(org, layer, confidence)"
|
|
1826
|
+
);
|
|
1827
|
+
db.exec(
|
|
1828
|
+
"CREATE INDEX IF NOT EXISTS idx_org_edges_repo_pair ON org_cross_repo_edges(org, layer, source_repo, target_repo, relationship)"
|
|
1829
|
+
);
|
|
1830
|
+
db.exec(
|
|
1831
|
+
"CREATE INDEX IF NOT EXISTS idx_org_consumers_contract ON org_api_consumers(org, contract)"
|
|
1832
|
+
);
|
|
1786
1833
|
}
|
|
1787
1834
|
function ensureColumn(db, tableName, columnName, definition) {
|
|
1788
1835
|
const columns = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
@@ -8706,6 +8753,24 @@ function resolveOrgForTool(org, baseDir = defaultOrgBaseDir()) {
|
|
|
8706
8753
|
|
|
8707
8754
|
// src/org/database.ts
|
|
8708
8755
|
import fs10 from "fs";
|
|
8756
|
+
var DEFAULT_EDGE_DISTRIBUTION = {
|
|
8757
|
+
strong: 0,
|
|
8758
|
+
moderate: 0,
|
|
8759
|
+
weak: 0
|
|
8760
|
+
};
|
|
8761
|
+
function parseEdgeDistribution(value) {
|
|
8762
|
+
if (!value) return { ...DEFAULT_EDGE_DISTRIBUTION };
|
|
8763
|
+
try {
|
|
8764
|
+
const parsed = JSON.parse(value);
|
|
8765
|
+
return {
|
|
8766
|
+
strong: Number(parsed.strong ?? 0),
|
|
8767
|
+
moderate: Number(parsed.moderate ?? 0),
|
|
8768
|
+
weak: Number(parsed.weak ?? 0)
|
|
8769
|
+
};
|
|
8770
|
+
} catch {
|
|
8771
|
+
return { ...DEFAULT_EDGE_DISTRIBUTION };
|
|
8772
|
+
}
|
|
8773
|
+
}
|
|
8709
8774
|
function openOrgDatabase(org, baseDir) {
|
|
8710
8775
|
const root = orgRoot(org, baseDir);
|
|
8711
8776
|
const db = openAnchorDatabase(root, orgDatabasePath(org, baseDir));
|
|
@@ -8835,8 +8900,9 @@ function recordOrgGraphState(db, input) {
|
|
|
8835
8900
|
db.prepare(
|
|
8836
8901
|
`INSERT INTO org_graph_state
|
|
8837
8902
|
(org, last_built_at, last_status, last_duration_ms, edge_count, api_contract_count,
|
|
8838
|
-
api_consumer_count,
|
|
8839
|
-
|
|
8903
|
+
api_consumer_count, visible_edge_count, weak_edge_count, edge_confidence_json,
|
|
8904
|
+
last_render_prep_ms, last_error, updated_at)
|
|
8905
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8840
8906
|
ON CONFLICT(org) DO UPDATE SET
|
|
8841
8907
|
last_built_at = COALESCE(excluded.last_built_at, org_graph_state.last_built_at),
|
|
8842
8908
|
last_status = excluded.last_status,
|
|
@@ -8844,6 +8910,10 @@ function recordOrgGraphState(db, input) {
|
|
|
8844
8910
|
edge_count = excluded.edge_count,
|
|
8845
8911
|
api_contract_count = excluded.api_contract_count,
|
|
8846
8912
|
api_consumer_count = excluded.api_consumer_count,
|
|
8913
|
+
visible_edge_count = excluded.visible_edge_count,
|
|
8914
|
+
weak_edge_count = excluded.weak_edge_count,
|
|
8915
|
+
edge_confidence_json = excluded.edge_confidence_json,
|
|
8916
|
+
last_render_prep_ms = excluded.last_render_prep_ms,
|
|
8847
8917
|
last_error = excluded.last_error,
|
|
8848
8918
|
updated_at = excluded.updated_at`
|
|
8849
8919
|
).run(
|
|
@@ -8854,6 +8924,10 @@ function recordOrgGraphState(db, input) {
|
|
|
8854
8924
|
input.edgeCount ?? 0,
|
|
8855
8925
|
input.apiContractCount ?? 0,
|
|
8856
8926
|
input.apiConsumerCount ?? 0,
|
|
8927
|
+
input.visibleEdgeCount ?? input.edgeCount ?? 0,
|
|
8928
|
+
input.weakEdgeCount ?? 0,
|
|
8929
|
+
JSON.stringify(input.edgeConfidenceDistribution ?? DEFAULT_EDGE_DISTRIBUTION),
|
|
8930
|
+
input.lastRenderPrepMs ?? null,
|
|
8857
8931
|
input.error ?? null,
|
|
8858
8932
|
now
|
|
8859
8933
|
);
|
|
@@ -8868,6 +8942,10 @@ function getOrgGraphState(db, org) {
|
|
|
8868
8942
|
lastStatus: row.last_status ?? void 0,
|
|
8869
8943
|
lastDurationMs: row.last_duration_ms ?? void 0,
|
|
8870
8944
|
edgeCount: row.edge_count ?? void 0,
|
|
8945
|
+
visibleEdgeCount: row.visible_edge_count ?? void 0,
|
|
8946
|
+
weakEdgeCount: row.weak_edge_count ?? void 0,
|
|
8947
|
+
edgeConfidenceDistribution: parseEdgeDistribution(row.edge_confidence_json),
|
|
8948
|
+
lastRenderPrepMs: row.last_render_prep_ms ?? void 0,
|
|
8871
8949
|
apiContractCount: row.api_contract_count ?? void 0,
|
|
8872
8950
|
apiConsumerCount: row.api_consumer_count ?? void 0,
|
|
8873
8951
|
lastError: row.last_error ?? void 0
|
|
@@ -8877,10 +8955,44 @@ function count(db, table, where = "", params = []) {
|
|
|
8877
8955
|
const row = db.prepare(`SELECT COUNT(*) AS count FROM ${table} ${where}`).get(...params);
|
|
8878
8956
|
return row.count;
|
|
8879
8957
|
}
|
|
8958
|
+
function tableHasColumn(db, table, column) {
|
|
8959
|
+
try {
|
|
8960
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
8961
|
+
return rows.some((row) => row.name === column);
|
|
8962
|
+
} catch {
|
|
8963
|
+
return false;
|
|
8964
|
+
}
|
|
8965
|
+
}
|
|
8966
|
+
function orgEdgeCountWhere(hasLayer, hasWeakFlag, filter) {
|
|
8967
|
+
const clauses = ["org = ?"];
|
|
8968
|
+
if (hasLayer) clauses.push("layer = 'repo'");
|
|
8969
|
+
if (filter === "visible" && hasWeakFlag) clauses.push("is_weak = 0");
|
|
8970
|
+
if (filter === "weak") {
|
|
8971
|
+
if (hasWeakFlag) clauses.push("is_weak = 1");
|
|
8972
|
+
else clauses.push("1 = 0");
|
|
8973
|
+
}
|
|
8974
|
+
return `WHERE ${clauses.join(" AND ")}`;
|
|
8975
|
+
}
|
|
8880
8976
|
function getOrgGraphCounts(db, org) {
|
|
8881
8977
|
initializeSchema(db);
|
|
8978
|
+
const hasLayer = tableHasColumn(db, "org_cross_repo_edges", "layer");
|
|
8979
|
+
const hasWeakFlag = tableHasColumn(db, "org_cross_repo_edges", "is_weak");
|
|
8882
8980
|
return {
|
|
8883
|
-
edges: count(db, "org_cross_repo_edges",
|
|
8981
|
+
edges: count(db, "org_cross_repo_edges", orgEdgeCountWhere(hasLayer, hasWeakFlag, "all"), [
|
|
8982
|
+
org
|
|
8983
|
+
]),
|
|
8984
|
+
visibleEdges: count(
|
|
8985
|
+
db,
|
|
8986
|
+
"org_cross_repo_edges",
|
|
8987
|
+
orgEdgeCountWhere(hasLayer, hasWeakFlag, "visible"),
|
|
8988
|
+
[org]
|
|
8989
|
+
),
|
|
8990
|
+
weakEdges: count(
|
|
8991
|
+
db,
|
|
8992
|
+
"org_cross_repo_edges",
|
|
8993
|
+
orgEdgeCountWhere(hasLayer, hasWeakFlag, "weak"),
|
|
8994
|
+
[org]
|
|
8995
|
+
),
|
|
8884
8996
|
apiContracts: count(db, "org_api_contracts", "WHERE org = ?", [org]),
|
|
8885
8997
|
apiConsumers: count(db, "org_api_consumers", "WHERE org = ?", [org])
|
|
8886
8998
|
};
|
|
@@ -8907,11 +9019,24 @@ function getOrgStatus(db, config, baseDir, options = {}) {
|
|
|
8907
9019
|
const codeFileCount = count(db, "code_files");
|
|
8908
9020
|
const codeChunkCount = count(db, "code_chunks");
|
|
8909
9021
|
const wisdomUnitCount = count(db, "wisdom_units");
|
|
8910
|
-
const
|
|
9022
|
+
const hasLayer = tableHasColumn(db, "org_cross_repo_edges", "layer");
|
|
9023
|
+
const hasWeakFlag = tableHasColumn(db, "org_cross_repo_edges", "is_weak");
|
|
9024
|
+
const crossRepoEdgeCount = count(
|
|
9025
|
+
db,
|
|
9026
|
+
"org_cross_repo_edges",
|
|
9027
|
+
orgEdgeCountWhere(hasLayer, hasWeakFlag, "visible"),
|
|
9028
|
+
[config.org]
|
|
9029
|
+
);
|
|
9030
|
+
const graphWeakEdgeCount = count(
|
|
9031
|
+
db,
|
|
9032
|
+
"org_cross_repo_edges",
|
|
9033
|
+
orgEdgeCountWhere(hasLayer, hasWeakFlag, "weak"),
|
|
9034
|
+
[config.org]
|
|
9035
|
+
);
|
|
8911
9036
|
const apiContractCount = count(db, "org_api_contracts", "WHERE org = ?", [config.org]);
|
|
8912
9037
|
const apiConsumerCount = count(db, "org_api_consumers", "WHERE org = ?", [config.org]);
|
|
8913
9038
|
const anomalyCount = count(db, "org_anomaly_events", "WHERE org = ?", [config.org]);
|
|
8914
|
-
const graphState = db
|
|
9039
|
+
const graphState = getOrgGraphState(db, config.org);
|
|
8915
9040
|
let score = 0;
|
|
8916
9041
|
const reasons = [];
|
|
8917
9042
|
if (enabledRepos.length > 0) {
|
|
@@ -8955,10 +9080,14 @@ function getOrgStatus(db, config, baseDir, options = {}) {
|
|
|
8955
9080
|
apiContractCount,
|
|
8956
9081
|
apiConsumerCount,
|
|
8957
9082
|
anomalyCount,
|
|
8958
|
-
graphLastBuiltAt: graphState?.
|
|
8959
|
-
graphLastStatus: graphState?.
|
|
8960
|
-
graphLastDurationMs: graphState?.
|
|
8961
|
-
graphLastError: graphState?.
|
|
9083
|
+
graphLastBuiltAt: graphState?.lastBuiltAt,
|
|
9084
|
+
graphLastStatus: graphState?.lastStatus,
|
|
9085
|
+
graphLastDurationMs: graphState?.lastDurationMs,
|
|
9086
|
+
graphLastError: graphState?.lastError,
|
|
9087
|
+
graphVisibleEdgeCount: graphState?.visibleEdgeCount ?? crossRepoEdgeCount,
|
|
9088
|
+
graphWeakEdgeCount: graphState?.weakEdgeCount ?? graphWeakEdgeCount,
|
|
9089
|
+
graphRenderPrepMs: graphState?.lastRenderPrepMs,
|
|
9090
|
+
graphEdgeConfidenceDistribution: graphState?.edgeConfidenceDistribution ?? { ...DEFAULT_EDGE_DISTRIBUTION },
|
|
8962
9091
|
coverageScore: score,
|
|
8963
9092
|
coverageGrade: grade(score),
|
|
8964
9093
|
coverageReasons: reasons,
|
|
@@ -9262,6 +9391,34 @@ function orgCloneStateFromResult(org, repo, result) {
|
|
|
9262
9391
|
import crypto9 from "crypto";
|
|
9263
9392
|
import fs13 from "fs";
|
|
9264
9393
|
import path23 from "path";
|
|
9394
|
+
var MIN_FILE_EDGE_CONFIDENCE = 0.62;
|
|
9395
|
+
var MIN_REPO_EDGE_CONFIDENCE = 0.7;
|
|
9396
|
+
var MIN_VISIBLE_EVIDENCE = 2;
|
|
9397
|
+
var MIN_API_CONSUMER_CONFIDENCE = 0.68;
|
|
9398
|
+
var MAX_EDGE_EVIDENCE = 8;
|
|
9399
|
+
var MAX_EDGE_REASONS = 6;
|
|
9400
|
+
var MAX_CONTRACTS_PER_CHUNK = 24;
|
|
9401
|
+
var CONTRACT_IGNORE = /* @__PURE__ */ new Set([
|
|
9402
|
+
"api",
|
|
9403
|
+
"v1",
|
|
9404
|
+
"v2",
|
|
9405
|
+
"v3",
|
|
9406
|
+
"graphql",
|
|
9407
|
+
"query",
|
|
9408
|
+
"mutation",
|
|
9409
|
+
"subscription",
|
|
9410
|
+
"schema",
|
|
9411
|
+
"route",
|
|
9412
|
+
"routes",
|
|
9413
|
+
"controller",
|
|
9414
|
+
"client",
|
|
9415
|
+
"request"
|
|
9416
|
+
]);
|
|
9417
|
+
var DEFAULT_EDGE_DISTRIBUTION2 = {
|
|
9418
|
+
strong: 0,
|
|
9419
|
+
moderate: 0,
|
|
9420
|
+
weak: 0
|
|
9421
|
+
};
|
|
9265
9422
|
function stableId(parts) {
|
|
9266
9423
|
return crypto9.createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
|
|
9267
9424
|
}
|
|
@@ -9274,6 +9431,9 @@ function fileEvidence(repo, filePath, note) {
|
|
|
9274
9431
|
note
|
|
9275
9432
|
};
|
|
9276
9433
|
}
|
|
9434
|
+
function evidenceJson(evidence) {
|
|
9435
|
+
return JSON.stringify(evidence);
|
|
9436
|
+
}
|
|
9277
9437
|
function readPackageManifest(repoPath) {
|
|
9278
9438
|
const packagePath = path23.join(repoPath, "package.json");
|
|
9279
9439
|
if (!fs13.existsSync(packagePath)) return void 0;
|
|
@@ -9319,19 +9479,53 @@ function parseJsonArray10(value) {
|
|
|
9319
9479
|
return [];
|
|
9320
9480
|
}
|
|
9321
9481
|
}
|
|
9322
|
-
function
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
if (operation) contracts.push(operation);
|
|
9482
|
+
function normalizeToken(value) {
|
|
9483
|
+
return value.toLowerCase().replace(/[{}()[\],:"'`]/g, "").replace(/\/+/g, "/").replace(/\/$/g, "").replace(/^-+/g, "").trim();
|
|
9484
|
+
}
|
|
9485
|
+
function splitTokenSymbols(value) {
|
|
9486
|
+
return value.split(/[^A-Za-z0-9_/-]+/).map((item) => normalizeToken(item)).filter((item) => item.length >= 3 && !CONTRACT_IGNORE.has(item)).slice(0, 12);
|
|
9487
|
+
}
|
|
9488
|
+
function normalizeContract(contract, kind) {
|
|
9489
|
+
if (kind === "route") {
|
|
9490
|
+
const normalized = normalizeToken(contract);
|
|
9491
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
9333
9492
|
}
|
|
9334
|
-
return
|
|
9493
|
+
return normalizeToken(contract);
|
|
9494
|
+
}
|
|
9495
|
+
function isGenericRoute(route) {
|
|
9496
|
+
const normalized = normalizeToken(route);
|
|
9497
|
+
if (!normalized.startsWith("/")) return true;
|
|
9498
|
+
const segments = normalized.split("/").filter((segment) => segment && !segment.startsWith(":") && segment !== "*");
|
|
9499
|
+
if (segments.length === 0) return true;
|
|
9500
|
+
const informative = segments.filter((segment) => !CONTRACT_IGNORE.has(segment));
|
|
9501
|
+
return informative.length < 1;
|
|
9502
|
+
}
|
|
9503
|
+
function extractContracts(text) {
|
|
9504
|
+
const tokens = [];
|
|
9505
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9506
|
+
const pushToken = (rawValue, kind) => {
|
|
9507
|
+
const sanitized = sanitizeHistoricalText(rawValue).slice(0, 180);
|
|
9508
|
+
if (!sanitized) return;
|
|
9509
|
+
const normalized = normalizeContract(sanitized, kind);
|
|
9510
|
+
if (!normalized || CONTRACT_IGNORE.has(normalized)) return;
|
|
9511
|
+
if (kind === "route" && isGenericRoute(normalized)) return;
|
|
9512
|
+
const key = `${kind}\0${normalized}`;
|
|
9513
|
+
if (seen.has(key)) return;
|
|
9514
|
+
seen.add(key);
|
|
9515
|
+
tokens.push({
|
|
9516
|
+
raw: sanitized,
|
|
9517
|
+
normalized,
|
|
9518
|
+
kind,
|
|
9519
|
+
symbols: splitTokenSymbols(sanitized)
|
|
9520
|
+
});
|
|
9521
|
+
};
|
|
9522
|
+
const routeMatches = text.matchAll(/["'`]((?:\/api)?\/[A-Za-z0-9_./:{}-]{3,})["'`]/g);
|
|
9523
|
+
for (const match of routeMatches) pushToken(match[1] ?? "", "route");
|
|
9524
|
+
const gqlMatches = text.matchAll(/\b(query|mutation|subscription)\s+([A-Za-z][A-Za-z0-9_]{2,})/g);
|
|
9525
|
+
for (const match of gqlMatches) pushToken(match[2] ?? "", "graphql");
|
|
9526
|
+
const schemaMatches = text.matchAll(/\b(?:type|interface|enum|input)\s+([A-Z][A-Za-z0-9_]{2,})\b/g);
|
|
9527
|
+
for (const match of schemaMatches) pushToken(match[1] ?? "", "schema");
|
|
9528
|
+
return tokens.slice(0, MAX_CONTRACTS_PER_CHUNK);
|
|
9335
9529
|
}
|
|
9336
9530
|
function isApiProviderPath(filePath) {
|
|
9337
9531
|
const normalized = filePath.toLowerCase();
|
|
@@ -9342,28 +9536,216 @@ function isApiProviderPath(filePath) {
|
|
|
9342
9536
|
function isApiConsumerText(text) {
|
|
9343
9537
|
return /\b(fetch|axios|ky|graphql|gql|client|sdk|request)\b/i.test(text);
|
|
9344
9538
|
}
|
|
9345
|
-
function evidenceJson(evidence) {
|
|
9346
|
-
return JSON.stringify(evidence);
|
|
9347
|
-
}
|
|
9348
9539
|
function shouldEmitProgress3(current, total, interval = 100) {
|
|
9349
9540
|
return current === 1 || current === total || current % interval === 0;
|
|
9350
9541
|
}
|
|
9351
9542
|
function resolveOptions(baseDirOrOptions) {
|
|
9352
9543
|
return typeof baseDirOrOptions === "string" ? { baseDir: baseDirOrOptions } : baseDirOrOptions ?? {};
|
|
9353
9544
|
}
|
|
9545
|
+
function clampConfidence(value) {
|
|
9546
|
+
if (Number.isNaN(value)) return 0;
|
|
9547
|
+
return Math.max(0, Math.min(0.99, Number(value.toFixed(3))));
|
|
9548
|
+
}
|
|
9549
|
+
function confidenceBucket(confidence) {
|
|
9550
|
+
if (confidence >= 0.82) return "strong";
|
|
9551
|
+
if (confidence >= 0.68) return "moderate";
|
|
9552
|
+
return "weak";
|
|
9553
|
+
}
|
|
9554
|
+
function uniqueEvidenceRefs(evidence) {
|
|
9555
|
+
const map = /* @__PURE__ */ new Map();
|
|
9556
|
+
for (const item of evidence) {
|
|
9557
|
+
const key = `${item.prNumber}|${item.prUrl}|${item.sourceType}|${item.filePath ?? ""}|${item.note ?? ""}`;
|
|
9558
|
+
if (!map.has(key)) map.set(key, item);
|
|
9559
|
+
}
|
|
9560
|
+
return [...map.values()].slice(0, MAX_EDGE_EVIDENCE);
|
|
9561
|
+
}
|
|
9562
|
+
function mergeReasons(a, b) {
|
|
9563
|
+
return uniqueStrings([...a, ...b]).slice(0, MAX_EDGE_REASONS);
|
|
9564
|
+
}
|
|
9565
|
+
function updateWeakFlag(edge, minConfidence2, minEvidence) {
|
|
9566
|
+
edge.evidence = uniqueEvidenceRefs(edge.evidence);
|
|
9567
|
+
edge.evidenceCount = edge.evidence.length;
|
|
9568
|
+
edge.confidence = clampConfidence(edge.confidence);
|
|
9569
|
+
edge.weak = edge.confidence < minConfidence2 || edge.evidenceCount < minEvidence;
|
|
9570
|
+
}
|
|
9571
|
+
function fileEdgeKey(edge) {
|
|
9572
|
+
return [
|
|
9573
|
+
edge.layer,
|
|
9574
|
+
edge.sourceRepo,
|
|
9575
|
+
edge.sourcePath,
|
|
9576
|
+
edge.targetRepo,
|
|
9577
|
+
edge.targetPath ?? "",
|
|
9578
|
+
edge.relationship
|
|
9579
|
+
].join("\0");
|
|
9580
|
+
}
|
|
9581
|
+
function repoEdgeKey(edge) {
|
|
9582
|
+
return [edge.layer, edge.sourceRepo, edge.targetRepo, edge.relationship].join("\0");
|
|
9583
|
+
}
|
|
9584
|
+
function upsertEdge(map, edge, minConfidence2, minEvidence) {
|
|
9585
|
+
const layer = edge.layer ?? "file";
|
|
9586
|
+
if (edge.sourceRepo === edge.targetRepo) return { inserted: false, updated: false };
|
|
9587
|
+
const key = layer === "repo" ? repoEdgeKey({ ...edge, layer }) : fileEdgeKey({ ...edge, layer });
|
|
9588
|
+
const existing = map.get(key);
|
|
9589
|
+
if (!existing) {
|
|
9590
|
+
const created = {
|
|
9591
|
+
...edge,
|
|
9592
|
+
layer,
|
|
9593
|
+
evidence: uniqueEvidenceRefs(edge.evidence),
|
|
9594
|
+
matchReasons: mergeReasons([], edge.matchReasons),
|
|
9595
|
+
evidenceCount: 0,
|
|
9596
|
+
weak: false
|
|
9597
|
+
};
|
|
9598
|
+
updateWeakFlag(created, minConfidence2, minEvidence);
|
|
9599
|
+
map.set(key, created);
|
|
9600
|
+
return { inserted: true, updated: false };
|
|
9601
|
+
}
|
|
9602
|
+
const merged = {
|
|
9603
|
+
...existing,
|
|
9604
|
+
sourcePath: existing.layer === "repo" ? "*" : existing.sourcePath,
|
|
9605
|
+
targetPath: existing.layer === "repo" ? void 0 : existing.targetPath ?? edge.targetPath,
|
|
9606
|
+
evidence: uniqueEvidenceRefs([...existing.evidence, ...edge.evidence]),
|
|
9607
|
+
matchReasons: mergeReasons(existing.matchReasons, edge.matchReasons),
|
|
9608
|
+
confidence: Math.max(existing.confidence, edge.confidence),
|
|
9609
|
+
evidenceCount: 0,
|
|
9610
|
+
weak: false
|
|
9611
|
+
};
|
|
9612
|
+
updateWeakFlag(merged, minConfidence2, minEvidence);
|
|
9613
|
+
map.set(key, merged);
|
|
9614
|
+
return { inserted: false, updated: true };
|
|
9615
|
+
}
|
|
9616
|
+
function scorePackageDependency(dependency) {
|
|
9617
|
+
const normalized = sanitizeHistoricalText(dependency);
|
|
9618
|
+
const reasons = ["exact_package_dependency"];
|
|
9619
|
+
const confidence = normalized.startsWith("@") ? 0.93 : 0.9;
|
|
9620
|
+
return { confidence, reasons };
|
|
9621
|
+
}
|
|
9622
|
+
function scoreImportEdge(input) {
|
|
9623
|
+
let score = 0.58;
|
|
9624
|
+
const reasons = ["cross_repo_import"];
|
|
9625
|
+
if (input.specifier.includes("/")) {
|
|
9626
|
+
score += 0.12;
|
|
9627
|
+
reasons.push("qualified_specifier");
|
|
9628
|
+
}
|
|
9629
|
+
if (input.importedPath) {
|
|
9630
|
+
score += 0.16;
|
|
9631
|
+
reasons.push("resolved_import_path");
|
|
9632
|
+
}
|
|
9633
|
+
if (input.importedSymbols.length > 0) {
|
|
9634
|
+
score += 0.11;
|
|
9635
|
+
reasons.push("explicit_import_symbols");
|
|
9636
|
+
}
|
|
9637
|
+
return { confidence: clampConfidence(score), reasons };
|
|
9638
|
+
}
|
|
9639
|
+
function scoreContract(input) {
|
|
9640
|
+
let score = 0.66;
|
|
9641
|
+
if (input.token.kind === "route") score += 0.08;
|
|
9642
|
+
if (input.token.kind === "graphql") score += 0.06;
|
|
9643
|
+
if (input.token.kind === "schema") score += 0.03;
|
|
9644
|
+
if (/\b(route|controller|api|schema|client)\b/i.test(input.filePath)) score += 0.05;
|
|
9645
|
+
if (input.token.symbols.length > 0) score += 0.04;
|
|
9646
|
+
return clampConfidence(score);
|
|
9647
|
+
}
|
|
9648
|
+
function overlapScore(a, b) {
|
|
9649
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
9650
|
+
const set = new Set(a);
|
|
9651
|
+
let overlaps = 0;
|
|
9652
|
+
for (const value of b) {
|
|
9653
|
+
if (set.has(value)) overlaps += 1;
|
|
9654
|
+
}
|
|
9655
|
+
return overlaps / Math.max(1, Math.min(a.length, b.length));
|
|
9656
|
+
}
|
|
9657
|
+
function scoreConsumerMatch(input) {
|
|
9658
|
+
let score = 0.58;
|
|
9659
|
+
const reasons = ["matched_contract_token"];
|
|
9660
|
+
if (input.consumerToken.kind === input.contract.kind) {
|
|
9661
|
+
score += 0.12;
|
|
9662
|
+
reasons.push("matching_contract_kind");
|
|
9663
|
+
}
|
|
9664
|
+
const symbolOverlap = overlapScore(
|
|
9665
|
+
uniqueStrings([...input.contract.symbols, ...splitTokenSymbols(input.contract.contract)]),
|
|
9666
|
+
uniqueStrings([...input.chunkSymbols, ...input.consumerToken.symbols])
|
|
9667
|
+
);
|
|
9668
|
+
if (symbolOverlap > 0) {
|
|
9669
|
+
score += Math.min(0.2, symbolOverlap * 0.22);
|
|
9670
|
+
reasons.push("symbol_overlap");
|
|
9671
|
+
}
|
|
9672
|
+
if (input.contract.kind === "route" && input.consumerToken.raw.includes("/")) {
|
|
9673
|
+
score += 0.08;
|
|
9674
|
+
reasons.push("route_literal_match");
|
|
9675
|
+
}
|
|
9676
|
+
return { confidence: clampConfidence(score), reasons };
|
|
9677
|
+
}
|
|
9678
|
+
function aggregateRepoEdges(fileEdges) {
|
|
9679
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
9680
|
+
for (const edge of fileEdges) {
|
|
9681
|
+
const key = repoEdgeKey({
|
|
9682
|
+
layer: "repo",
|
|
9683
|
+
sourceRepo: edge.sourceRepo,
|
|
9684
|
+
targetRepo: edge.targetRepo,
|
|
9685
|
+
relationship: edge.relationship
|
|
9686
|
+
});
|
|
9687
|
+
const bucket = grouped.get(key) ?? [];
|
|
9688
|
+
bucket.push(edge);
|
|
9689
|
+
grouped.set(key, bucket);
|
|
9690
|
+
}
|
|
9691
|
+
const repoEdges = [];
|
|
9692
|
+
for (const [key, group] of grouped.entries()) {
|
|
9693
|
+
const [layerValue = "repo", sourceRepo = "", targetRepo = ""] = key.split("\0");
|
|
9694
|
+
const relationship = group[0]?.relationship ?? "imports";
|
|
9695
|
+
const confidences = group.map((edge) => edge.confidence);
|
|
9696
|
+
const maxConfidence = Math.max(...confidences);
|
|
9697
|
+
const avgConfidence = confidences.reduce((sum, value) => sum + value, 0) / confidences.length;
|
|
9698
|
+
const repetitionBoost = Math.min(0.18, Math.log2(group.length + 1) * 0.06);
|
|
9699
|
+
const confidence = clampConfidence(maxConfidence * 0.6 + avgConfidence * 0.25 + repetitionBoost);
|
|
9700
|
+
const evidence = uniqueEvidenceRefs(group.flatMap((edge) => edge.evidence));
|
|
9701
|
+
const matchReasons6 = mergeReasons([], group.flatMap((edge) => edge.matchReasons));
|
|
9702
|
+
const repoEdge = {
|
|
9703
|
+
org: group[0]?.org ?? "",
|
|
9704
|
+
sourceRepo,
|
|
9705
|
+
sourcePath: "*",
|
|
9706
|
+
targetRepo,
|
|
9707
|
+
targetPath: void 0,
|
|
9708
|
+
layer: layerValue,
|
|
9709
|
+
relationship,
|
|
9710
|
+
evidence,
|
|
9711
|
+
matchReasons: matchReasons6,
|
|
9712
|
+
evidenceCount: 0,
|
|
9713
|
+
weak: false,
|
|
9714
|
+
confidence
|
|
9715
|
+
};
|
|
9716
|
+
updateWeakFlag(repoEdge, MIN_REPO_EDGE_CONFIDENCE, MIN_VISIBLE_EVIDENCE);
|
|
9717
|
+
repoEdges.push(repoEdge);
|
|
9718
|
+
}
|
|
9719
|
+
return repoEdges.sort((a, b) => b.confidence - a.confidence);
|
|
9720
|
+
}
|
|
9721
|
+
function buildQuality(repoEdges, hiddenRepoEdges) {
|
|
9722
|
+
const distribution = { ...DEFAULT_EDGE_DISTRIBUTION2 };
|
|
9723
|
+
for (const edge of repoEdges) {
|
|
9724
|
+
distribution[confidenceBucket(edge.confidence)] += 1;
|
|
9725
|
+
}
|
|
9726
|
+
return {
|
|
9727
|
+
edgeConfidenceDistribution: distribution,
|
|
9728
|
+
weakEdgesFiltered: hiddenRepoEdges.length,
|
|
9729
|
+
minVisibleConfidence: MIN_REPO_EDGE_CONFIDENCE,
|
|
9730
|
+
minVisibleEvidence: MIN_VISIBLE_EVIDENCE
|
|
9731
|
+
};
|
|
9732
|
+
}
|
|
9733
|
+
function isVisibleRepoEdge(edge) {
|
|
9734
|
+
return !edge.weak && edge.confidence >= MIN_REPO_EDGE_CONFIDENCE && edge.evidenceCount >= MIN_VISIBLE_EVIDENCE;
|
|
9735
|
+
}
|
|
9354
9736
|
function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
9355
9737
|
initializeSchema(db);
|
|
9356
9738
|
const options = resolveOptions(baseDirOrOptions);
|
|
9357
9739
|
const startedAt = Date.now();
|
|
9358
9740
|
try {
|
|
9741
|
+
const enabledRepos = config.repos.filter((repo) => repo.enabled);
|
|
9742
|
+
const repoByName = new Map(enabledRepos.map((repo) => [repo.fullName, repo]));
|
|
9359
9743
|
options.onProgress?.({
|
|
9360
9744
|
stage: "loading_package_manifests",
|
|
9361
9745
|
org: config.org,
|
|
9362
|
-
totalRepos:
|
|
9746
|
+
totalRepos: enabledRepos.length
|
|
9363
9747
|
});
|
|
9364
9748
|
const packageNames = repoPackageNames(config, options.baseDir);
|
|
9365
|
-
const enabledRepos = config.repos.filter((repo) => repo.enabled);
|
|
9366
|
-
const repoByName = new Map(enabledRepos.map((repo) => [repo.fullName, repo]));
|
|
9367
9749
|
const packageToRepo = /* @__PURE__ */ new Map();
|
|
9368
9750
|
for (const [repo, names] of packageNames.entries()) {
|
|
9369
9751
|
for (const name of names) packageToRepo.set(name, repo);
|
|
@@ -9374,31 +9756,22 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9374
9756
|
repos: enabledRepos.length,
|
|
9375
9757
|
packageNames: packageToRepo.size
|
|
9376
9758
|
});
|
|
9377
|
-
const
|
|
9378
|
-
const
|
|
9379
|
-
|
|
9380
|
-
if (edge.sourceRepo === edge.targetRepo) return;
|
|
9381
|
-
const key = [
|
|
9382
|
-
edge.sourceRepo,
|
|
9383
|
-
edge.sourcePath,
|
|
9384
|
-
edge.targetRepo,
|
|
9385
|
-
edge.targetPath ?? "",
|
|
9386
|
-
edge.relationship
|
|
9387
|
-
].join("\0");
|
|
9388
|
-
if (edgeKeys.has(key)) return;
|
|
9389
|
-
edgeKeys.add(key);
|
|
9390
|
-
edges.push(edge);
|
|
9759
|
+
const fileEdgeMap = /* @__PURE__ */ new Map();
|
|
9760
|
+
const addFileEdge = (edge) => {
|
|
9761
|
+
upsertEdge(fileEdgeMap, { ...edge, layer: "file" }, MIN_FILE_EDGE_CONFIDENCE, 1);
|
|
9391
9762
|
};
|
|
9392
9763
|
enabledRepos.forEach((repo, index) => {
|
|
9393
9764
|
const manifest = readPackageManifest(orgRepoLocalPath(config.org, repo, options.baseDir));
|
|
9394
9765
|
for (const dependency of dependenciesFor(manifest)) {
|
|
9395
9766
|
const targetRepo = packageToRepo.get(dependency);
|
|
9396
9767
|
if (!targetRepo || targetRepo === repo.fullName) continue;
|
|
9397
|
-
|
|
9768
|
+
const score = scorePackageDependency(dependency);
|
|
9769
|
+
addFileEdge({
|
|
9398
9770
|
org: config.org,
|
|
9399
9771
|
sourceRepo: repo.fullName,
|
|
9400
9772
|
sourcePath: "package.json",
|
|
9401
9773
|
targetRepo,
|
|
9774
|
+
targetPath: "package.json",
|
|
9402
9775
|
relationship: "depends_on_package",
|
|
9403
9776
|
evidence: [
|
|
9404
9777
|
fileEvidence(
|
|
@@ -9407,7 +9780,8 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9407
9780
|
`depends on ${sanitizeHistoricalText(dependency)}`
|
|
9408
9781
|
)
|
|
9409
9782
|
],
|
|
9410
|
-
|
|
9783
|
+
matchReasons: score.reasons,
|
|
9784
|
+
confidence: score.confidence
|
|
9411
9785
|
});
|
|
9412
9786
|
}
|
|
9413
9787
|
options.onProgress?.({
|
|
@@ -9416,7 +9790,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9416
9790
|
current: index + 1,
|
|
9417
9791
|
total: enabledRepos.length,
|
|
9418
9792
|
repo: repo.fullName,
|
|
9419
|
-
edges:
|
|
9793
|
+
edges: fileEdgeMap.size
|
|
9420
9794
|
});
|
|
9421
9795
|
});
|
|
9422
9796
|
options.onProgress?.({ stage: "loading_imports", org: config.org });
|
|
@@ -9431,7 +9805,13 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9431
9805
|
const rootSpecifier = packageRootForSpecifier(item.specifier);
|
|
9432
9806
|
const targetRepo = packageToRepo.get(rootSpecifier) ?? packageToRepo.get(item.specifier);
|
|
9433
9807
|
if (targetRepo && targetRepo !== item.repo) {
|
|
9434
|
-
|
|
9808
|
+
const importedSymbols = parseJsonArray10(item.imported_symbols_json);
|
|
9809
|
+
const score = scoreImportEdge({
|
|
9810
|
+
specifier: item.specifier,
|
|
9811
|
+
importedPath: item.imported_path,
|
|
9812
|
+
importedSymbols
|
|
9813
|
+
});
|
|
9814
|
+
addFileEdge({
|
|
9435
9815
|
org: config.org,
|
|
9436
9816
|
sourceRepo: item.repo,
|
|
9437
9817
|
sourcePath: item.source_path,
|
|
@@ -9445,7 +9825,8 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9445
9825
|
`imports ${sanitizeHistoricalText(rootSpecifier || item.specifier)}`
|
|
9446
9826
|
)
|
|
9447
9827
|
],
|
|
9448
|
-
|
|
9828
|
+
matchReasons: score.reasons,
|
|
9829
|
+
confidence: score.confidence
|
|
9449
9830
|
});
|
|
9450
9831
|
}
|
|
9451
9832
|
if (shouldEmitProgress3(index + 1, imports.length)) {
|
|
@@ -9455,7 +9836,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9455
9836
|
current: index + 1,
|
|
9456
9837
|
total: imports.length,
|
|
9457
9838
|
sourcePath: item.source_path,
|
|
9458
|
-
edges:
|
|
9839
|
+
edges: fileEdgeMap.size
|
|
9459
9840
|
});
|
|
9460
9841
|
}
|
|
9461
9842
|
});
|
|
@@ -9469,25 +9850,27 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9469
9850
|
(chunk) => repoByName.has(chunk.repo) && isApiProviderPath(chunk.file_path)
|
|
9470
9851
|
);
|
|
9471
9852
|
const apiContracts = [];
|
|
9472
|
-
const
|
|
9853
|
+
const contractByKey = /* @__PURE__ */ new Map();
|
|
9473
9854
|
const contractsByToken = /* @__PURE__ */ new Map();
|
|
9474
9855
|
providerChunks.forEach((chunk, index) => {
|
|
9475
|
-
for (const
|
|
9476
|
-
const
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
contractKeys.add(key);
|
|
9480
|
-
const apiContract = {
|
|
9856
|
+
for (const token of extractContracts(chunk.sanitized_text)) {
|
|
9857
|
+
const key = [chunk.repo, chunk.file_path, token.kind, token.normalized].join("\0");
|
|
9858
|
+
if (contractByKey.has(key)) continue;
|
|
9859
|
+
const contract = {
|
|
9481
9860
|
repo: chunk.repo,
|
|
9482
9861
|
filePath: chunk.file_path,
|
|
9483
|
-
contract:
|
|
9484
|
-
|
|
9485
|
-
|
|
9862
|
+
contract: token.raw,
|
|
9863
|
+
normalizedContract: token.normalized,
|
|
9864
|
+
kind: token.kind,
|
|
9865
|
+
symbols: token.symbols,
|
|
9866
|
+
evidence: [fileEvidence(chunk.repo, chunk.file_path, `defines ${token.raw}`)],
|
|
9867
|
+
confidence: scoreContract({ token, filePath: chunk.file_path })
|
|
9486
9868
|
};
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
bucket.
|
|
9490
|
-
|
|
9869
|
+
contractByKey.set(key, contract);
|
|
9870
|
+
apiContracts.push(contract);
|
|
9871
|
+
const bucket = contractsByToken.get(contract.normalizedContract) ?? [];
|
|
9872
|
+
bucket.push(contract);
|
|
9873
|
+
contractsByToken.set(contract.normalizedContract, bucket);
|
|
9491
9874
|
}
|
|
9492
9875
|
if (shouldEmitProgress3(index + 1, providerChunks.length)) {
|
|
9493
9876
|
options.onProgress?.({
|
|
@@ -9500,28 +9883,39 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9500
9883
|
});
|
|
9501
9884
|
}
|
|
9502
9885
|
});
|
|
9503
|
-
const apiConsumers = [];
|
|
9504
|
-
const consumerKeys = /* @__PURE__ */ new Set();
|
|
9505
9886
|
const consumerChunks = chunks.filter(
|
|
9506
9887
|
(chunk) => repoByName.has(chunk.repo) && isApiConsumerText(chunk.sanitized_text)
|
|
9507
9888
|
);
|
|
9889
|
+
const apiConsumers = [];
|
|
9890
|
+
const consumerKeySet = /* @__PURE__ */ new Set();
|
|
9508
9891
|
consumerChunks.forEach((chunk, index) => {
|
|
9509
|
-
const
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9892
|
+
const chunkTokens = extractContracts(chunk.sanitized_text);
|
|
9893
|
+
const chunkSymbols = parseJsonArray10(chunk.symbols_json);
|
|
9894
|
+
let matchesForChunk = 0;
|
|
9895
|
+
for (const consumerToken of chunkTokens) {
|
|
9896
|
+
const contracts = contractsByToken.get(consumerToken.normalized);
|
|
9897
|
+
if (!contracts?.length) continue;
|
|
9514
9898
|
for (const contract of contracts) {
|
|
9515
9899
|
if (chunk.repo === contract.repo) continue;
|
|
9900
|
+
const score = scoreConsumerMatch({
|
|
9901
|
+
consumerToken,
|
|
9902
|
+
contract,
|
|
9903
|
+
chunkSymbols
|
|
9904
|
+
});
|
|
9905
|
+
if (score.confidence < MIN_API_CONSUMER_CONFIDENCE) continue;
|
|
9516
9906
|
const consumerKey = [
|
|
9517
9907
|
contract.repo,
|
|
9518
9908
|
contract.filePath,
|
|
9519
9909
|
chunk.repo,
|
|
9520
9910
|
chunk.file_path,
|
|
9521
|
-
contract.
|
|
9911
|
+
contract.normalizedContract
|
|
9522
9912
|
].join("\0");
|
|
9523
|
-
if (
|
|
9524
|
-
|
|
9913
|
+
if (consumerKeySet.has(consumerKey)) continue;
|
|
9914
|
+
consumerKeySet.add(consumerKey);
|
|
9915
|
+
const evidence = uniqueEvidenceRefs([
|
|
9916
|
+
...contract.evidence,
|
|
9917
|
+
fileEvidence(chunk.repo, chunk.file_path, `consumes ${contract.contract}`)
|
|
9918
|
+
]);
|
|
9525
9919
|
const consumer = {
|
|
9526
9920
|
org: config.org,
|
|
9527
9921
|
providerRepo: contract.repo,
|
|
@@ -9529,23 +9923,24 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9529
9923
|
consumerRepo: chunk.repo,
|
|
9530
9924
|
consumerPath: chunk.file_path,
|
|
9531
9925
|
contract: contract.contract,
|
|
9532
|
-
evidence
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
confidence:
|
|
9926
|
+
evidence,
|
|
9927
|
+
matchReasons: mergeReasons([], score.reasons),
|
|
9928
|
+
evidenceCount: evidence.length,
|
|
9929
|
+
weak: score.confidence < MIN_API_CONSUMER_CONFIDENCE || evidence.length < MIN_VISIBLE_EVIDENCE,
|
|
9930
|
+
confidence: score.confidence
|
|
9537
9931
|
};
|
|
9538
|
-
chunkMatches += 1;
|
|
9539
9932
|
apiConsumers.push(consumer);
|
|
9540
|
-
|
|
9933
|
+
matchesForChunk += 1;
|
|
9934
|
+
addFileEdge({
|
|
9541
9935
|
org: config.org,
|
|
9542
9936
|
sourceRepo: chunk.repo,
|
|
9543
9937
|
sourcePath: chunk.file_path,
|
|
9544
9938
|
targetRepo: contract.repo,
|
|
9545
9939
|
targetPath: contract.filePath,
|
|
9546
9940
|
relationship: "api_consumer",
|
|
9547
|
-
evidence
|
|
9548
|
-
|
|
9941
|
+
evidence,
|
|
9942
|
+
matchReasons: score.reasons,
|
|
9943
|
+
confidence: score.confidence
|
|
9549
9944
|
});
|
|
9550
9945
|
}
|
|
9551
9946
|
}
|
|
@@ -9556,46 +9951,79 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9556
9951
|
current: index + 1,
|
|
9557
9952
|
total: consumerChunks.length,
|
|
9558
9953
|
filePath: chunk.file_path,
|
|
9559
|
-
matches:
|
|
9954
|
+
matches: matchesForChunk
|
|
9560
9955
|
});
|
|
9561
9956
|
}
|
|
9562
9957
|
});
|
|
9958
|
+
const allFileEdges = [...fileEdgeMap.values()].sort((a, b) => b.confidence - a.confidence);
|
|
9959
|
+
const repoEdges = aggregateRepoEdges(allFileEdges);
|
|
9960
|
+
const visibleRepoEdges = repoEdges.filter(isVisibleRepoEdge);
|
|
9961
|
+
const hiddenRepoEdges = repoEdges.filter((edge) => !isVisibleRepoEdge(edge));
|
|
9962
|
+
const hiddenFileEdges = allFileEdges.filter(
|
|
9963
|
+
(edge) => edge.confidence < MIN_FILE_EDGE_CONFIDENCE || edge.evidenceCount < 1
|
|
9964
|
+
);
|
|
9965
|
+
const visibleFileEdges = allFileEdges.filter((edge) => !hiddenFileEdges.includes(edge));
|
|
9966
|
+
const quality = buildQuality(visibleRepoEdges, hiddenRepoEdges);
|
|
9563
9967
|
options.onProgress?.({
|
|
9564
9968
|
stage: "writing_org_graph",
|
|
9565
9969
|
org: config.org,
|
|
9566
|
-
edges:
|
|
9970
|
+
edges: repoEdges.length,
|
|
9567
9971
|
apiContracts: apiContracts.length,
|
|
9568
9972
|
apiConsumers: apiConsumers.length
|
|
9569
9973
|
});
|
|
9570
9974
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9975
|
+
const renderPrepStartedAt = Date.now();
|
|
9571
9976
|
const transaction = db.transaction(() => {
|
|
9572
9977
|
db.prepare("DELETE FROM org_cross_repo_edges WHERE org = ?").run(config.org);
|
|
9573
9978
|
db.prepare("DELETE FROM org_api_contracts WHERE org = ?").run(config.org);
|
|
9574
9979
|
db.prepare("DELETE FROM org_api_consumers WHERE org = ?").run(config.org);
|
|
9575
9980
|
const insertEdge = db.prepare(
|
|
9576
9981
|
`INSERT INTO org_cross_repo_edges
|
|
9577
|
-
(id, org, source_repo, source_path, target_repo, target_path,
|
|
9578
|
-
|
|
9982
|
+
(id, org, source_repo, source_path, target_repo, target_path, layer, relationship,
|
|
9983
|
+
evidence_json, match_reasons_json, evidence_count, is_weak, confidence, created_at)
|
|
9984
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9579
9985
|
ON CONFLICT(id) DO UPDATE SET
|
|
9986
|
+
source_path = excluded.source_path,
|
|
9987
|
+
target_path = excluded.target_path,
|
|
9988
|
+
layer = excluded.layer,
|
|
9580
9989
|
evidence_json = excluded.evidence_json,
|
|
9990
|
+
match_reasons_json = excluded.match_reasons_json,
|
|
9991
|
+
evidence_count = excluded.evidence_count,
|
|
9992
|
+
is_weak = excluded.is_weak,
|
|
9581
9993
|
confidence = excluded.confidence,
|
|
9582
9994
|
created_at = excluded.created_at`
|
|
9583
9995
|
);
|
|
9584
|
-
|
|
9996
|
+
const persistEdge = (edge) => {
|
|
9585
9997
|
insertEdge.run(
|
|
9586
|
-
`oge_${stableId([
|
|
9998
|
+
`oge_${stableId([
|
|
9999
|
+
edge.org,
|
|
10000
|
+
edge.layer,
|
|
10001
|
+
edge.sourceRepo,
|
|
10002
|
+
edge.sourcePath,
|
|
10003
|
+
edge.targetRepo,
|
|
10004
|
+
edge.targetPath ?? "",
|
|
10005
|
+
edge.relationship
|
|
10006
|
+
])}`,
|
|
9587
10007
|
edge.org,
|
|
9588
10008
|
edge.sourceRepo,
|
|
9589
10009
|
edge.sourcePath,
|
|
9590
10010
|
edge.targetRepo,
|
|
9591
10011
|
edge.targetPath ?? null,
|
|
10012
|
+
edge.layer,
|
|
9592
10013
|
edge.relationship,
|
|
9593
10014
|
evidenceJson(edge.evidence),
|
|
10015
|
+
JSON.stringify(edge.matchReasons),
|
|
10016
|
+
edge.evidenceCount,
|
|
10017
|
+
edge.weak ? 1 : 0,
|
|
9594
10018
|
edge.confidence,
|
|
9595
10019
|
now
|
|
9596
10020
|
);
|
|
10021
|
+
};
|
|
10022
|
+
const persistedEdges = [...repoEdges, ...allFileEdges];
|
|
10023
|
+
for (const [index, edge] of persistedEdges.entries()) {
|
|
10024
|
+
persistEdge(edge);
|
|
9597
10025
|
const current = index + 1;
|
|
9598
|
-
if (shouldEmitProgress3(current,
|
|
10026
|
+
if (shouldEmitProgress3(current, persistedEdges.length, 500)) {
|
|
9599
10027
|
options.onProgress?.({
|
|
9600
10028
|
stage: "writing_org_graph",
|
|
9601
10029
|
org: config.org,
|
|
@@ -9603,7 +10031,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9603
10031
|
apiContracts: apiContracts.length,
|
|
9604
10032
|
apiConsumers: apiConsumers.length,
|
|
9605
10033
|
current,
|
|
9606
|
-
total:
|
|
10034
|
+
total: persistedEdges.length,
|
|
9607
10035
|
kind: "edges"
|
|
9608
10036
|
});
|
|
9609
10037
|
}
|
|
@@ -9620,7 +10048,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9620
10048
|
);
|
|
9621
10049
|
for (const [index, contract] of apiContracts.entries()) {
|
|
9622
10050
|
insertContract.run(
|
|
9623
|
-
`oac_${stableId([config.org, contract.repo, contract.filePath, contract.
|
|
10051
|
+
`oac_${stableId([config.org, contract.repo, contract.filePath, contract.normalizedContract])}`,
|
|
9624
10052
|
config.org,
|
|
9625
10053
|
contract.repo,
|
|
9626
10054
|
contract.filePath,
|
|
@@ -9634,7 +10062,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9634
10062
|
options.onProgress?.({
|
|
9635
10063
|
stage: "writing_org_graph",
|
|
9636
10064
|
org: config.org,
|
|
9637
|
-
edges:
|
|
10065
|
+
edges: persistedEdges.length,
|
|
9638
10066
|
apiContracts: current,
|
|
9639
10067
|
apiConsumers: apiConsumers.length,
|
|
9640
10068
|
current,
|
|
@@ -9645,11 +10073,15 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9645
10073
|
}
|
|
9646
10074
|
const insertConsumer = db.prepare(
|
|
9647
10075
|
`INSERT INTO org_api_consumers
|
|
9648
|
-
(id, org, provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
9649
|
-
|
|
10076
|
+
(id, org, provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
10077
|
+
match_reasons_json, evidence_count, is_weak, confidence, created_at)
|
|
10078
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
9650
10079
|
ON CONFLICT(id) DO UPDATE SET
|
|
9651
10080
|
contract = excluded.contract,
|
|
9652
10081
|
evidence_json = excluded.evidence_json,
|
|
10082
|
+
match_reasons_json = excluded.match_reasons_json,
|
|
10083
|
+
evidence_count = excluded.evidence_count,
|
|
10084
|
+
is_weak = excluded.is_weak,
|
|
9653
10085
|
confidence = excluded.confidence,
|
|
9654
10086
|
created_at = excluded.created_at`
|
|
9655
10087
|
);
|
|
@@ -9661,7 +10093,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9661
10093
|
consumer.providerPath ?? "",
|
|
9662
10094
|
consumer.consumerRepo,
|
|
9663
10095
|
consumer.consumerPath,
|
|
9664
|
-
consumer.contract
|
|
10096
|
+
normalizeToken(consumer.contract)
|
|
9665
10097
|
])}`,
|
|
9666
10098
|
consumer.org,
|
|
9667
10099
|
consumer.providerRepo,
|
|
@@ -9670,6 +10102,9 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9670
10102
|
consumer.consumerPath,
|
|
9671
10103
|
sanitizeHistoricalText(consumer.contract),
|
|
9672
10104
|
evidenceJson(consumer.evidence),
|
|
10105
|
+
JSON.stringify(consumer.matchReasons),
|
|
10106
|
+
consumer.evidenceCount,
|
|
10107
|
+
consumer.weak ? 1 : 0,
|
|
9673
10108
|
consumer.confidence,
|
|
9674
10109
|
now
|
|
9675
10110
|
);
|
|
@@ -9678,7 +10113,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9678
10113
|
options.onProgress?.({
|
|
9679
10114
|
stage: "writing_org_graph",
|
|
9680
10115
|
org: config.org,
|
|
9681
|
-
edges:
|
|
10116
|
+
edges: persistedEdges.length,
|
|
9682
10117
|
apiContracts: apiContracts.length,
|
|
9683
10118
|
apiConsumers: current,
|
|
9684
10119
|
current,
|
|
@@ -9689,6 +10124,7 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9689
10124
|
}
|
|
9690
10125
|
});
|
|
9691
10126
|
transaction();
|
|
10127
|
+
const renderPrepMs = Date.now() - renderPrepStartedAt;
|
|
9692
10128
|
const durationMs = Date.now() - startedAt;
|
|
9693
10129
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9694
10130
|
recordOrgGraphState(db, {
|
|
@@ -9696,22 +10132,40 @@ function rebuildOrgGraph(db, config, baseDirOrOptions) {
|
|
|
9696
10132
|
status: "success",
|
|
9697
10133
|
builtAt: finishedAt,
|
|
9698
10134
|
durationMs,
|
|
9699
|
-
edgeCount:
|
|
10135
|
+
edgeCount: repoEdges.length,
|
|
10136
|
+
visibleEdgeCount: visibleRepoEdges.length,
|
|
10137
|
+
weakEdgeCount: hiddenRepoEdges.length,
|
|
10138
|
+
edgeConfidenceDistribution: quality.edgeConfidenceDistribution,
|
|
10139
|
+
lastRenderPrepMs: renderPrepMs,
|
|
9700
10140
|
apiContractCount: apiContracts.length,
|
|
9701
10141
|
apiConsumerCount: apiConsumers.length
|
|
9702
10142
|
});
|
|
9703
10143
|
options.onProgress?.({
|
|
9704
10144
|
stage: "completed_org_graph",
|
|
9705
10145
|
org: config.org,
|
|
9706
|
-
edges:
|
|
10146
|
+
edges: repoEdges.length,
|
|
9707
10147
|
apiContracts: apiContracts.length,
|
|
9708
10148
|
apiConsumers: apiConsumers.length,
|
|
9709
10149
|
durationMs
|
|
9710
10150
|
});
|
|
9711
10151
|
return {
|
|
9712
|
-
edges,
|
|
10152
|
+
edges: visibleRepoEdges,
|
|
10153
|
+
repoEdges: visibleRepoEdges,
|
|
10154
|
+
fileEdges: visibleFileEdges,
|
|
10155
|
+
hiddenFileEdges,
|
|
10156
|
+
hiddenRepoEdges,
|
|
9713
10157
|
apiConsumers,
|
|
9714
|
-
apiContracts
|
|
10158
|
+
apiContracts: apiContracts.map((contract) => ({
|
|
10159
|
+
repo: contract.repo,
|
|
10160
|
+
filePath: contract.filePath,
|
|
10161
|
+
contract: contract.contract,
|
|
10162
|
+
evidence: contract.evidence,
|
|
10163
|
+
confidence: contract.confidence
|
|
10164
|
+
})),
|
|
10165
|
+
quality: {
|
|
10166
|
+
...quality,
|
|
10167
|
+
lastRenderPrepMs: renderPrepMs
|
|
10168
|
+
},
|
|
9715
10169
|
durationMs
|
|
9716
10170
|
};
|
|
9717
10171
|
} catch (error) {
|
|
@@ -10055,6 +10509,8 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
10055
10509
|
org: config.org,
|
|
10056
10510
|
status: "skipped",
|
|
10057
10511
|
edgeCount: counts.edges,
|
|
10512
|
+
visibleEdgeCount: counts.visibleEdges,
|
|
10513
|
+
weakEdgeCount: counts.weakEdges,
|
|
10058
10514
|
apiContractCount: counts.apiContracts,
|
|
10059
10515
|
apiConsumerCount: counts.apiConsumers
|
|
10060
10516
|
});
|
|
@@ -10064,7 +10520,12 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
10064
10520
|
command,
|
|
10065
10521
|
reason: "Graph skipped because --no-graph was passed."
|
|
10066
10522
|
});
|
|
10067
|
-
graph = {
|
|
10523
|
+
graph = {
|
|
10524
|
+
edges: counts.visibleEdges,
|
|
10525
|
+
apiConsumers: counts.apiConsumers,
|
|
10526
|
+
apiContracts: counts.apiContracts,
|
|
10527
|
+
skipped: true
|
|
10528
|
+
};
|
|
10068
10529
|
} else {
|
|
10069
10530
|
try {
|
|
10070
10531
|
const rebuiltGraph = rebuildOrgGraph(db, config, {
|
|
@@ -10079,7 +10540,12 @@ async function indexOrgRepos(db, config, options = {}) {
|
|
|
10079
10540
|
} catch (error) {
|
|
10080
10541
|
const message = error instanceof Error ? error.message : String(error);
|
|
10081
10542
|
const counts = getOrgGraphCounts(db, config.org);
|
|
10082
|
-
graph = {
|
|
10543
|
+
graph = {
|
|
10544
|
+
edges: counts.visibleEdges,
|
|
10545
|
+
apiConsumers: counts.apiConsumers,
|
|
10546
|
+
apiContracts: counts.apiContracts,
|
|
10547
|
+
error: message
|
|
10548
|
+
};
|
|
10083
10549
|
}
|
|
10084
10550
|
}
|
|
10085
10551
|
recordOrgIndexRun(db, {
|
|
@@ -10123,6 +10589,15 @@ function parseEvidence2(value) {
|
|
|
10123
10589
|
return [];
|
|
10124
10590
|
}
|
|
10125
10591
|
}
|
|
10592
|
+
function parseStringArray(value) {
|
|
10593
|
+
if (!value) return [];
|
|
10594
|
+
try {
|
|
10595
|
+
const parsed = JSON.parse(value);
|
|
10596
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
10597
|
+
} catch {
|
|
10598
|
+
return [];
|
|
10599
|
+
}
|
|
10600
|
+
}
|
|
10126
10601
|
function fileEvidence2(repo, filePath, note) {
|
|
10127
10602
|
return {
|
|
10128
10603
|
prNumber: 0,
|
|
@@ -10154,7 +10629,7 @@ function affectedConsumers(db, org, repo, changedFiles) {
|
|
|
10154
10629
|
const rows = db.prepare(
|
|
10155
10630
|
`SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json, confidence
|
|
10156
10631
|
FROM org_api_consumers
|
|
10157
|
-
WHERE org =
|
|
10632
|
+
WHERE org = ? AND is_weak = 0`
|
|
10158
10633
|
).all(org);
|
|
10159
10634
|
return rows.filter((row) => !repo || row.provider_repo === repo || row.consumer_repo === repo).filter((row) => {
|
|
10160
10635
|
if (changedFiles.length === 0) return true;
|
|
@@ -10169,14 +10644,18 @@ function affectedConsumers(db, org, repo, changedFiles) {
|
|
|
10169
10644
|
consumerPath: row.consumer_path,
|
|
10170
10645
|
contract: sanitizeHistoricalText(row.contract),
|
|
10171
10646
|
evidence: parseEvidence2(row.evidence_json),
|
|
10647
|
+
matchReasons: parseStringArray(row.match_reasons_json),
|
|
10648
|
+
evidenceCount: row.evidence_count ?? parseEvidence2(row.evidence_json).length,
|
|
10649
|
+
weak: (row.is_weak ?? 0) === 1,
|
|
10172
10650
|
confidence: row.confidence
|
|
10173
10651
|
}));
|
|
10174
10652
|
}
|
|
10175
10653
|
function affectedEdges(db, org, repo, changedFiles) {
|
|
10176
10654
|
const rows = db.prepare(
|
|
10177
|
-
`SELECT source_repo, source_path, target_repo, target_path, relationship, evidence_json,
|
|
10655
|
+
`SELECT source_repo, source_path, target_repo, target_path, layer, relationship, evidence_json,
|
|
10656
|
+
match_reasons_json, evidence_count, is_weak, confidence
|
|
10178
10657
|
FROM org_cross_repo_edges
|
|
10179
|
-
WHERE org =
|
|
10658
|
+
WHERE org = ? AND layer = 'file'`
|
|
10180
10659
|
).all(org);
|
|
10181
10660
|
return rows.filter((row) => !repo || row.source_repo === repo || row.target_repo === repo).filter((row) => {
|
|
10182
10661
|
if (changedFiles.length === 0) return true;
|
|
@@ -10187,8 +10666,12 @@ function affectedEdges(db, org, repo, changedFiles) {
|
|
|
10187
10666
|
sourcePath: row.source_path,
|
|
10188
10667
|
targetRepo: row.target_repo,
|
|
10189
10668
|
targetPath: row.target_path ?? void 0,
|
|
10669
|
+
layer: row.layer,
|
|
10190
10670
|
relationship: row.relationship,
|
|
10191
10671
|
evidence: parseEvidence2(row.evidence_json),
|
|
10672
|
+
matchReasons: parseStringArray(row.match_reasons_json),
|
|
10673
|
+
evidenceCount: row.evidence_count ?? parseEvidence2(row.evidence_json).length,
|
|
10674
|
+
weak: (row.is_weak ?? 0) === 1,
|
|
10192
10675
|
confidence: row.confidence
|
|
10193
10676
|
}));
|
|
10194
10677
|
}
|
|
@@ -10506,7 +10989,7 @@ function evidenceLabel(evidence) {
|
|
|
10506
10989
|
if (first.prNumber > 0) return `PR #${first.prNumber}`;
|
|
10507
10990
|
return first.filePath ? `file ${first.filePath}` : first.note ?? "local file evidence";
|
|
10508
10991
|
}
|
|
10509
|
-
function
|
|
10992
|
+
function parseStringArray2(value) {
|
|
10510
10993
|
try {
|
|
10511
10994
|
const parsed = JSON.parse(value);
|
|
10512
10995
|
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
@@ -10553,7 +11036,7 @@ function getWisdom(db, input, limit) {
|
|
|
10553
11036
|
const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
|
|
10554
11037
|
return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
|
|
10555
11038
|
row,
|
|
10556
|
-
score: rowScore(input, row.sanitized_text,
|
|
11039
|
+
score: rowScore(input, row.sanitized_text, parseStringArray2(row.file_paths_json), [], lowerTerms)
|
|
10557
11040
|
})).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);
|
|
10558
11041
|
}
|
|
10559
11042
|
function getCodeEvidence(db, input, limit) {
|
|
@@ -10570,7 +11053,7 @@ function getCodeEvidence(db, input, limit) {
|
|
|
10570
11053
|
input,
|
|
10571
11054
|
row.sanitized_text,
|
|
10572
11055
|
[row.file_path],
|
|
10573
|
-
|
|
11056
|
+
parseStringArray2(row.symbols_json),
|
|
10574
11057
|
lowerTerms
|
|
10575
11058
|
)
|
|
10576
11059
|
})).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((item) => item.row);
|
|
@@ -10588,7 +11071,7 @@ function getArchitecture(db, input, limit) {
|
|
|
10588
11071
|
score: rowScore(
|
|
10589
11072
|
input,
|
|
10590
11073
|
row.summary_sanitized,
|
|
10591
|
-
|
|
11074
|
+
parseStringArray2(row.source_files_json),
|
|
10592
11075
|
[],
|
|
10593
11076
|
lowerTerms
|
|
10594
11077
|
)
|
|
@@ -10599,9 +11082,10 @@ function findOrgApiConsumers(db, config, input) {
|
|
|
10599
11082
|
const repoClause = input.repo ? " AND (provider_repo = ? OR consumer_repo = ?)" : "";
|
|
10600
11083
|
const repoParams = input.repo ? [input.repo, input.repo] : [];
|
|
10601
11084
|
const rows = db.prepare(
|
|
10602
|
-
`SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
11085
|
+
`SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json,
|
|
11086
|
+
match_reasons_json, evidence_count, is_weak, confidence
|
|
10603
11087
|
FROM org_api_consumers
|
|
10604
|
-
WHERE org =
|
|
11088
|
+
WHERE org = ? AND is_weak = 0${repoClause}
|
|
10605
11089
|
ORDER BY confidence DESC`
|
|
10606
11090
|
).all(config.org, ...repoParams);
|
|
10607
11091
|
const limit = Math.max(1, Math.min(input.maxResults ?? 8, 25));
|
|
@@ -10617,6 +11101,9 @@ function findOrgApiConsumers(db, config, input) {
|
|
|
10617
11101
|
consumerPath: row.consumer_path,
|
|
10618
11102
|
contract: sanitizeHistoricalText(row.contract),
|
|
10619
11103
|
evidence: parseEvidence3(row.evidence_json),
|
|
11104
|
+
matchReasons: parseStringArray2(row.match_reasons_json ?? "[]"),
|
|
11105
|
+
evidenceCount: row.evidence_count ?? parseEvidence3(row.evidence_json).length,
|
|
11106
|
+
weak: (row.is_weak ?? 0) === 1,
|
|
10620
11107
|
confidence: row.confidence
|
|
10621
11108
|
}));
|
|
10622
11109
|
}
|
|
@@ -10625,7 +11112,7 @@ function getOrgArchitectureMap(db, config, format = "mermaid") {
|
|
|
10625
11112
|
const rows = db.prepare(
|
|
10626
11113
|
`SELECT source_repo, source_path, target_repo, target_path, relationship, confidence
|
|
10627
11114
|
FROM org_cross_repo_edges
|
|
10628
|
-
WHERE org = ?
|
|
11115
|
+
WHERE org = ? AND layer = 'repo' AND is_weak = 0
|
|
10629
11116
|
ORDER BY confidence DESC, source_repo, target_repo`
|
|
10630
11117
|
).all(config.org);
|
|
10631
11118
|
const nodes = uniqueStrings(rows.flatMap((row) => [row.source_repo, row.target_repo])).map(
|
|
@@ -10703,7 +11190,7 @@ function buildOrgContextResult(db, config, input) {
|
|
|
10703
11190
|
if (architecture.length === 0) lines.push("- No matching architecture patterns found.");
|
|
10704
11191
|
else {
|
|
10705
11192
|
for (const pattern of architecture) {
|
|
10706
|
-
const files =
|
|
11193
|
+
const files = parseStringArray2(pattern.source_files_json);
|
|
10707
11194
|
lines.push(
|
|
10708
11195
|
`- [${pattern.repo}] [${pattern.area}] ${pattern.summary_sanitized} Evidence: ${files[0] ?? "indexed current code"}.`
|
|
10709
11196
|
);
|