@straiffi/archon 1.8.5 → 1.8.7
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/client/assets/ActivityView-D4Qh96zr.js +1 -0
- package/dist/client/assets/GitHubActionsDialog-Bg03OmVv.js +1 -0
- package/dist/client/assets/RepositoryGraphDialog-CS_hOqPw.js +1 -0
- package/dist/client/assets/VerificationDialog-BYH-Ek8n.js +4 -0
- package/dist/client/assets/{arc-D2Nn9xIj.js → arc-C83vRtlz.js} +1 -1
- package/dist/client/assets/architecture-7EHR7CIX-Dm8H7N6Z.js +1 -0
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BB0biSU2.js → architectureDiagram-3BPJPVTR-2lxabjSl.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-Bxm9GAiX.js → blockDiagram-GPEHLZMM-C80HvJmb.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CC3YHLX3.js → c4Diagram-AAUBKEIU-BM3k_lKz.js} +1 -1
- package/dist/client/assets/channel-HjENOCZ0.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-ComBHKwP.js → chunk-2J33WTMH-BTk3lX07.js} +1 -1
- package/dist/client/assets/{chunk-3OPIFGDE-DSpFzCXF.js → chunk-3OPIFGDE-DAxdBZ_4.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-CtLS8D71.js → chunk-4BX2VUAB-C5pU9ApX.js} +1 -1
- package/dist/client/assets/{chunk-4EGX6M5U-Btnm0QNe.js → chunk-4EGX6M5U-CAjWPjND.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-74CcJaub.js → chunk-55IACEB6-DhO4-AG4.js} +1 -1
- package/dist/client/assets/{chunk-5DO6E6H7-WLZ8L4ko.js → chunk-5DO6E6H7-BDKA-zHS.js} +1 -1
- package/dist/client/assets/{chunk-5ZQYHXKU-pL2M3Ax3.js → chunk-5ZQYHXKU-BYm9s62a.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-BPi_lguN.js → chunk-727SXJPM-iVUM8xuV.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-Coc6laVv.js → chunk-AQP2D5EJ-DVM7riMJ.js} +1 -1
- package/dist/client/assets/{chunk-BR22UD5L-Cou5beGe.js → chunk-BR22UD5L-12D2GWh4.js} +1 -1
- package/dist/client/assets/{chunk-BSJP7CBP-DJ57XcV5.js → chunk-BSJP7CBP-DMi77e84.js} +1 -1
- package/dist/client/assets/{chunk-CSCIHK7Q-CpaX09-x.js → chunk-CSCIHK7Q-_D1FzP9B.js} +2 -2
- package/dist/client/assets/{chunk-FHYWG6QK-Ba10e2kj.js → chunk-FHYWG6QK-BhSajKiB.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-CEo2wZMx.js → chunk-FMBD7UC4-Drmlc0wM.js} +1 -1
- package/dist/client/assets/{chunk-KSCS5N6A-CLzz2KFY.js → chunk-KSCS5N6A-D3XouAHf.js} +1 -1
- package/dist/client/assets/{chunk-L5ZTLDWV-DyyDuDZm.js → chunk-L5ZTLDWV-B7NYWgT4.js} +1 -1
- package/dist/client/assets/{chunk-LZXEDZCA-Cm5npwZA.js → chunk-LZXEDZCA-Dgkkbb6A.js} +2 -2
- package/dist/client/assets/{chunk-MPE355IW-MqEt-J8G.js → chunk-MPE355IW-Cw8YmjTC.js} +1 -1
- package/dist/client/assets/{chunk-MZUSXYTE-DdNi9lhx.js → chunk-MZUSXYTE-jvxRCZ7x.js} +1 -1
- package/dist/client/assets/{chunk-N66VUXT2-CjemTW4E.js → chunk-N66VUXT2-00i4y0yD.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-CaopONUA.js → chunk-ND2GUHAM-CzO1oVCi.js} +1 -1
- package/dist/client/assets/{chunk-NZK2D7GU-DIzt4PM_.js → chunk-NZK2D7GU-re0VVZne.js} +1 -1
- package/dist/client/assets/{chunk-O5CBEL6O-C7Re_6FB.js → chunk-O5CBEL6O-BwsS59iy.js} +1 -1
- package/dist/client/assets/{chunk-PUPMXCY4-ZvwTw0To.js → chunk-PUPMXCY4-DfWourL7.js} +1 -1
- package/dist/client/assets/chunk-QZHKN3VN-CtbsU1Bj.js +1 -0
- package/dist/client/assets/{chunk-UIBZB4QT-BTY9be9s.js → chunk-UIBZB4QT-v-a5ZfN6.js} +1 -1
- package/dist/client/assets/{chunk-WCWK7LTN-Db1n3n3-.js → chunk-WCWK7LTN-CtYzhvvb.js} +1 -1
- package/dist/client/assets/chunk-WU5MYG2G-CLyh-xCA.js +1 -0
- package/dist/client/assets/{chunk-XPW4576I-DObFPxjB.js → chunk-XPW4576I-CvnAuXH2.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DnhdaVrK.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C-BjeSGJ.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-MlOPiG6P.js → cose-bilkent-S5V4N54A-fchD-dH2.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BdFEW6fG.js → dagre-BM42HDAG-x71fYlqQ.js} +1 -1
- package/dist/client/assets/{dagre-BWNHuDdf.js → dagre-DSrzm-Gq.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-D2ZuWfhH.js → diagram-2AECGRRQ-CMdwzuYj.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-Cs_AmUDw.js → diagram-5GNKFQAL-B3l6I4qJ.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-DICE0UrL.js → diagram-KO2AKTUF-BNhypLBW.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-MwtTXZWl.js → diagram-LMA3HP47-CmrhbiDt.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-CxDJ75F5.js → diagram-OG6HWLK6-CRobDMLl.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-XgZtP-BK.js → erDiagram-TEJ5UH35-CqO7K0TS.js} +1 -1
- package/dist/client/assets/eventmodeling-FCH6USID-BWhw0UJ8.js +1 -0
- package/dist/client/assets/{flowDiagram-I6XJVG4X-B8uIExT5.js → flowDiagram-I6XJVG4X-CuLTJZU0.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-B6TrSB4S.js → ganttDiagram-6RSMTGT7-Bm7v1eRG.js} +1 -1
- package/dist/client/assets/gitGraph-WXDBUCRP-BSnxAa1J.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-qfMWSncV.js → gitGraphDiagram-PVQCEYII-CNHZr-5i.js} +1 -1
- package/dist/client/assets/index-Bs5SQZhz.css +2 -0
- package/dist/client/assets/index-LGNsdNYM.js +274 -0
- package/dist/client/assets/info-J43DQDTF-DcFwF1-h.js +1 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-CuOimNjJ.js → infoDiagram-5YYISTIA-z27pUr2X.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-BWHbLru4.js → ishikawaDiagram-YF4QCWOH-CDocZ0jf.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-SAbFsYxI.js → journeyDiagram-JHISSGLW-CgP31lsU.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-m4ZsWr_S.js → kanban-definition-UN3LZRKU-DxTm1_fc.js} +1 -1
- package/dist/client/assets/{line-C4dSESbS.js → line-hrZ7m44_.js} +1 -1
- package/dist/client/assets/{linear-DT4rSZvy.js → linear-BOwW57yX.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CstDlNL1.js → mermaid-parser.core-BThJAIK-.js} +2 -2
- package/dist/client/assets/{mermaid.core-B-Yj5Lxm.js → mermaid.core-CfVyP_kK.js} +3 -3
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-Bzf9o0Md.js → mindmap-definition-RKZ34NQL-CuISnLgW.js} +1 -1
- package/dist/client/assets/{ordinal-k--hYEme.js → ordinal-CZ33lFpK.js} +1 -1
- package/dist/client/assets/packet-YPE3B663-1VZiO_9r.js +1 -0
- package/dist/client/assets/pie-LRSECV5Y-BX1-tQCu.js +1 -0
- package/dist/client/assets/{pieDiagram-4H26LBE5-C8c0F0hh.js → pieDiagram-4H26LBE5-DnWAdd44.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BewK5Lws.js → quadrantDiagram-W4KKPZXB-Cb-RVl5A.js} +1 -1
- package/dist/client/assets/radar-GUYGQ44K-B2HRrCR4.js +1 -0
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-C_gYL5ls.js → requirementDiagram-4Y6WPE33-DrXg_gCS.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Dt8OrhUl.js → sankeyDiagram-5OEKKPKP-DzZjp_ZP.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Z578DkVy.js → sequenceDiagram-3UESZ5HK-CQJ3PhQD.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CyQ5eQTE.js → stateDiagram-AJRCARHV-B16j9pg1.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-vNiFfHwm.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DMtbr-l6.js → timeline-definition-PNZ67QCA-bUG3mZQt.js} +1 -1
- package/dist/client/assets/treeView-BLDUP644-CDfUVbUi.js +1 -0
- package/dist/client/assets/treemap-LRROVOQU-DUWqu8sK.js +1 -0
- package/dist/client/assets/{vennDiagram-CIIHVFJN-BToePHO4.js → vennDiagram-CIIHVFJN-DALCJcZD.js} +1 -1
- package/dist/client/assets/wardley-L42UT6IY-BEFuPUCy.js +1 -0
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-FAzbTjvI.js → wardleyDiagram-YWT4CUSO-DPQd3l9A.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-BrlUCOgp.js → xychartDiagram-2RQKCTM6-BIv1FqXG.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/db.js +399 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/index.js +183 -20
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/agent.js +9 -6
- package/dist/server/lib/agent.js.map +1 -1
- package/dist/server/lib/automationResumeDecision.js +6 -0
- package/dist/server/lib/automationResumeDecision.js.map +1 -1
- package/dist/server/lib/bundleReviewOperations.js +81 -2
- package/dist/server/lib/bundleReviewOperations.js.map +1 -1
- package/dist/server/lib/checkWorkspaceIdentity.js +2 -206
- package/dist/server/lib/checkWorkspaceIdentity.js.map +1 -1
- package/dist/server/lib/executionReconciliation.js +73 -0
- package/dist/server/lib/executionReconciliation.js.map +1 -1
- package/dist/server/lib/figmaResources.js +15 -1
- package/dist/server/lib/figmaResources.js.map +1 -1
- package/dist/server/lib/git.js +312 -32
- package/dist/server/lib/git.js.map +1 -1
- package/dist/server/lib/integrations/github.js +10 -1
- package/dist/server/lib/integrations/github.js.map +1 -1
- package/dist/server/lib/postBuildChecks.js +20 -12
- package/dist/server/lib/postBuildChecks.js.map +1 -1
- package/dist/server/lib/repositoryGraphAnalyzer.js +392 -0
- package/dist/server/lib/repositoryGraphAnalyzer.js.map +1 -0
- package/dist/server/lib/repositoryGraphGitHistory.js +371 -0
- package/dist/server/lib/repositoryGraphGitHistory.js.map +1 -0
- package/dist/server/lib/repositoryGraphIdentity.js +134 -0
- package/dist/server/lib/repositoryGraphIdentity.js.map +1 -0
- package/dist/server/lib/repositoryGraphManifest.js +386 -0
- package/dist/server/lib/repositoryGraphManifest.js.map +1 -0
- package/dist/server/lib/repositoryGraphQueries.js +370 -0
- package/dist/server/lib/repositoryGraphQueries.js.map +1 -0
- package/dist/server/lib/repositoryGraphRetention.js +104 -0
- package/dist/server/lib/repositoryGraphRetention.js.map +1 -0
- package/dist/server/lib/repositoryGraphRoutes.js +145 -0
- package/dist/server/lib/repositoryGraphRoutes.js.map +1 -0
- package/dist/server/lib/repositoryGraphScan.js +370 -0
- package/dist/server/lib/repositoryGraphScan.js.map +1 -0
- package/dist/server/lib/repositoryGraphStore.js +352 -0
- package/dist/server/lib/repositoryGraphStore.js.map +1 -0
- package/dist/server/lib/repositoryGraphTargets.js +30 -0
- package/dist/server/lib/repositoryGraphTargets.js.map +1 -0
- package/dist/server/lib/repositoryGraphTypeScript.js +288 -0
- package/dist/server/lib/repositoryGraphTypeScript.js.map +1 -0
- package/dist/server/lib/reviewFindings.js +31 -5
- package/dist/server/lib/reviewFindings.js.map +1 -1
- package/dist/server/lib/specArtifact.js +5 -1
- package/dist/server/lib/specArtifact.js.map +1 -1
- package/dist/server/lib/terminal.js +25 -1
- package/dist/server/lib/terminal.js.map +1 -1
- package/dist/server/lib/types.js +1 -0
- package/dist/server/lib/types.js.map +1 -1
- package/dist/server/lib/workspaceIdentity.js +433 -0
- package/dist/server/lib/workspaceIdentity.js.map +1 -0
- package/dist/server/workers/build.js +2 -2
- package/dist/server/workers/build.js.map +1 -1
- package/dist/server/workers/followUp.js +1 -1
- package/dist/server/workers/followUp.js.map +1 -1
- package/dist/server/workers/plan.js +9 -7
- package/dist/server/workers/plan.js.map +1 -1
- package/dist/server/workers/repositoryGraphAnalyzer.js +282 -0
- package/dist/server/workers/repositoryGraphAnalyzer.js.map +1 -0
- package/dist/server/workers/review.js +72 -6
- package/dist/server/workers/review.js.map +1 -1
- package/dist/server/workers/specSession.js +4 -1
- package/dist/server/workers/specSession.js.map +1 -1
- package/package.json +2 -1
- package/dist/client/assets/ActivityView-DGP4Juye.js +0 -1
- package/dist/client/assets/GitHubActionsDialog-P_mpYAOO.js +0 -1
- package/dist/client/assets/VerificationDialog-C6FVSzcF.js +0 -4
- package/dist/client/assets/architecture-7EHR7CIX-C-4Cp4ml.js +0 -1
- package/dist/client/assets/channel-CQOfjP3q.js +0 -1
- package/dist/client/assets/chunk-QZHKN3VN-LI_RZca2.js +0 -1
- package/dist/client/assets/chunk-WU5MYG2G-D5lmGfNl.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-DGUeerOX.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Cnum-Tdt.js +0 -1
- package/dist/client/assets/eventmodeling-FCH6USID-Dqb6QKoI.js +0 -1
- package/dist/client/assets/gitGraph-WXDBUCRP-DYEsqh93.js +0 -1
- package/dist/client/assets/index-CzRhzdmk.js +0 -274
- package/dist/client/assets/index-DZqqZx53.css +0 -2
- package/dist/client/assets/info-J43DQDTF-CfjXFZai.js +0 -1
- package/dist/client/assets/packet-YPE3B663-Dv0HkRHU.js +0 -1
- package/dist/client/assets/pie-LRSECV5Y-S5zPzZFN.js +0 -1
- package/dist/client/assets/radar-GUYGQ44K-B7psLv22.js +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C9e0OccX.js +0 -1
- package/dist/client/assets/treeView-BLDUP644-B8H0kT67.js +0 -1
- package/dist/client/assets/treemap-LRROVOQU-DPLhFHnh.js +0 -1
- package/dist/client/assets/wardley-L42UT6IY-B5-Vb08V.js +0 -1
- /package/dist/client/assets/{array-BNor45A1.js → array-B2f2amhX.js} +0 -0
- /package/dist/client/assets/{chunk-NNHCCRGN-CZB-V-Gv.js → chunk-NNHCCRGN-DgKrp8T-.js} +0 -0
- /package/dist/client/assets/{cytoscape.esm-B2lJcMSI.js → cytoscape.esm-BInZCgaJ.js} +0 -0
- /package/dist/client/assets/{defaultLocale-DPzUsThw.js → defaultLocale-sPXJ5urm.js} +0 -0
- /package/dist/client/assets/{dist-CERhmQhR.js → dist-DfHDMDTt.js} +0 -0
- /package/dist/client/assets/{graphlib-BHOW-eWz.js → graphlib-DacNv4Jd.js} +0 -0
- /package/dist/client/assets/{init-C0L3woqb.js → init-BVCnG1lj.js} +0 -0
- /package/dist/client/assets/{katex-CpjQsUQe.js → katex-BlkrXOwm.js} +0 -0
- /package/dist/client/assets/{path-sMK4d_s9.js → path-Bw5AvBNC.js} +0 -0
- /package/dist/client/assets/{rough.esm-BoTisKeL.js → rough.esm-DeLgKbOI.js} +0 -0
- /package/dist/client/assets/{src-BRjoYtUW.js → src-CFGsdLiG.js} +0 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { posix, win32 } from 'node:path';
|
|
2
|
+
import { REPOSITORY_GRAPH_ANALYZER_VERSION, REPOSITORY_GRAPH_SCHEMA_VERSION, } from './repositoryGraphAnalyzer.js';
|
|
3
|
+
import { captureRepositoryGraphIdentitySnapshot, composeRepositoryGraphIdentity, } from './repositoryGraphIdentity.js';
|
|
4
|
+
import { getLatestRepositoryGraphScanForTarget, getRepositoryGraphEdge, getRepositoryGraphEdgesForNodes, getRepositoryGraphNodesByPath, getRepositoryGraphNodesByIds, getRepositoryGraphScan, listRepositoryGraphDiagnostics, listRepositoryGraphEvidenceClasses, } from './repositoryGraphStore.js';
|
|
5
|
+
import { resolveRepositoryGraphTarget, } from './repositoryGraphTargets.js';
|
|
6
|
+
export const MAX_REPOSITORY_GRAPH_TRAVERSAL_DEPTH = 4;
|
|
7
|
+
export const MAX_REPOSITORY_GRAPH_TRAVERSAL_PATHS = 500;
|
|
8
|
+
export const MAX_REPOSITORY_GRAPH_STATUS_DIAGNOSTICS = 100;
|
|
9
|
+
export const REPOSITORY_GRAPH_FRESHNESS_CACHE_TTL_MS = 5_000;
|
|
10
|
+
const MAX_REPOSITORY_GRAPH_FRESHNESS_CACHE_ENTRIES = 50;
|
|
11
|
+
const MAX_CONCURRENT_REPOSITORY_GRAPH_FRESHNESS_CAPTURES = 2;
|
|
12
|
+
const freshnessCache = new Map();
|
|
13
|
+
const freshnessCaptures = new Map();
|
|
14
|
+
const freshnessCaptureQueue = [];
|
|
15
|
+
let activeFreshnessCaptures = 0;
|
|
16
|
+
const runFreshnessCapture = (operation) => new Promise((resolve, reject) => {
|
|
17
|
+
const run = () => {
|
|
18
|
+
activeFreshnessCaptures += 1;
|
|
19
|
+
void operation().then(resolve, reject).finally(() => {
|
|
20
|
+
activeFreshnessCaptures -= 1;
|
|
21
|
+
freshnessCaptureQueue.shift()?.();
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
if (activeFreshnessCaptures < MAX_CONCURRENT_REPOSITORY_GRAPH_FRESHNESS_CAPTURES) {
|
|
25
|
+
run();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
freshnessCaptureQueue.push(run);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const getFreshnessSnapshot = (key, cwd) => {
|
|
32
|
+
const cached = freshnessCache.get(key);
|
|
33
|
+
if (cached && Date.now() - cached.capturedAt < REPOSITORY_GRAPH_FRESHNESS_CACHE_TTL_MS) {
|
|
34
|
+
return Promise.resolve(cached.snapshot);
|
|
35
|
+
}
|
|
36
|
+
const existing = freshnessCaptures.get(key);
|
|
37
|
+
if (existing) {
|
|
38
|
+
return existing;
|
|
39
|
+
}
|
|
40
|
+
const capture = runFreshnessCapture(() => captureRepositoryGraphIdentitySnapshot(cwd))
|
|
41
|
+
.then(snapshot => {
|
|
42
|
+
freshnessCache.delete(key);
|
|
43
|
+
freshnessCache.set(key, { capturedAt: Date.now(), snapshot });
|
|
44
|
+
while (freshnessCache.size > MAX_REPOSITORY_GRAPH_FRESHNESS_CACHE_ENTRIES) {
|
|
45
|
+
const oldestKey = freshnessCache.keys().next().value;
|
|
46
|
+
if (oldestKey === undefined) {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
freshnessCache.delete(oldestKey);
|
|
50
|
+
}
|
|
51
|
+
return snapshot;
|
|
52
|
+
})
|
|
53
|
+
.finally(() => freshnessCaptures.delete(key));
|
|
54
|
+
freshnessCaptures.set(key, capture);
|
|
55
|
+
return capture;
|
|
56
|
+
};
|
|
57
|
+
export const clearRepositoryGraphFreshnessIdentityCache = () => {
|
|
58
|
+
freshnessCache.clear();
|
|
59
|
+
};
|
|
60
|
+
export class RepositoryGraphQueryError extends Error {
|
|
61
|
+
status;
|
|
62
|
+
reason;
|
|
63
|
+
constructor(status, reason, message) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.name = 'RepositoryGraphQueryError';
|
|
66
|
+
this.status = status;
|
|
67
|
+
this.reason = reason;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const scanTarget = (scan) => scan.target_kind === 'repo_root'
|
|
71
|
+
? { kind: 'repo_root', bundle_id: null }
|
|
72
|
+
: { kind: 'bundle', bundle_id: scan.target_bundle_id ?? '' };
|
|
73
|
+
const targetsMatch = (left, right) => left.kind === right.kind
|
|
74
|
+
&& left.bundle_id === right.bundle_id;
|
|
75
|
+
const parseIdentity = (scan) => {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(scan.identity_json);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
throw new RepositoryGraphQueryError(500, 'invalid_scan_identity', 'Repository graph scan identity is invalid');
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const scanIsTruncated = (scan) => scan.manifest_truncated
|
|
84
|
+
|| scan.typescript_truncated
|
|
85
|
+
|| scan.history_truncated
|
|
86
|
+
|| scan.nodes_truncated
|
|
87
|
+
|| scan.edges_truncated
|
|
88
|
+
|| scan.diagnostics_truncated;
|
|
89
|
+
const publicScan = ({ identity_json: _identityJson, analyzer_configuration_json: _analyzerConfigurationJson, ...scan }) => scan;
|
|
90
|
+
const getFreshness = async (project, scan) => {
|
|
91
|
+
if (scan.graph_schema_version !== REPOSITORY_GRAPH_SCHEMA_VERSION
|
|
92
|
+
|| scan.analyzer_version !== REPOSITORY_GRAPH_ANALYZER_VERSION) {
|
|
93
|
+
return {
|
|
94
|
+
state: 'stale',
|
|
95
|
+
is_current: false,
|
|
96
|
+
reason: 'This scan was produced by an older repository graph schema or analyzer.',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const target = scanTarget(scan);
|
|
100
|
+
const resolution = resolveRepositoryGraphTarget(project, target);
|
|
101
|
+
if (resolution.state === 'unavailable') {
|
|
102
|
+
return { state: 'unavailable', is_current: false, reason: resolution.message };
|
|
103
|
+
}
|
|
104
|
+
let analyzerConfiguration;
|
|
105
|
+
try {
|
|
106
|
+
analyzerConfiguration = JSON.parse(scan.analyzer_configuration_json);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return { state: 'unavailable', is_current: false, reason: 'Stored analyzer configuration is invalid.' };
|
|
110
|
+
}
|
|
111
|
+
const cacheKey = JSON.stringify([project.id, target.kind, target.bundle_id, resolution.cwd]);
|
|
112
|
+
const snapshot = await getFreshnessSnapshot(cacheKey, resolution.cwd);
|
|
113
|
+
if (snapshot.state === 'unavailable') {
|
|
114
|
+
return { state: 'unavailable', is_current: false, reason: snapshot.message };
|
|
115
|
+
}
|
|
116
|
+
const identity = composeRepositoryGraphIdentity({
|
|
117
|
+
projectId: project.id,
|
|
118
|
+
target,
|
|
119
|
+
workspaceIdentity: snapshot.workspaceIdentity,
|
|
120
|
+
historyFingerprint: snapshot.historyFingerprint,
|
|
121
|
+
graphSchemaVersion: scan.graph_schema_version,
|
|
122
|
+
analyzerVersion: scan.analyzer_version,
|
|
123
|
+
analyzerConfiguration,
|
|
124
|
+
});
|
|
125
|
+
const fresh = identity.fingerprint === scan.identity_fingerprint
|
|
126
|
+
&& JSON.stringify(identity.components) === scan.identity_json;
|
|
127
|
+
return {
|
|
128
|
+
state: fresh ? 'fresh' : 'stale',
|
|
129
|
+
is_current: fresh && scan.status === 'completed',
|
|
130
|
+
reason: fresh ? null : 'The target workspace no longer matches this scan identity.',
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
const responseContext = async (project, scan, queryTruncated = false) => {
|
|
134
|
+
const identity = parseIdentity(scan);
|
|
135
|
+
return {
|
|
136
|
+
scan: publicScan(scan),
|
|
137
|
+
workspace_identity: {
|
|
138
|
+
fingerprint: scan.identity_fingerprint,
|
|
139
|
+
head: identity.head,
|
|
140
|
+
dirty_fingerprint: identity.dirtyFingerprint,
|
|
141
|
+
target: scanTarget(scan),
|
|
142
|
+
},
|
|
143
|
+
graph_schema_version: scan.graph_schema_version,
|
|
144
|
+
analyzer_version: scan.analyzer_version,
|
|
145
|
+
freshness: await getFreshness(project, scan),
|
|
146
|
+
truncation: {
|
|
147
|
+
scan: scanIsTruncated(scan),
|
|
148
|
+
query: queryTruncated,
|
|
149
|
+
manifest: scan.manifest_truncated,
|
|
150
|
+
typescript: scan.typescript_truncated,
|
|
151
|
+
history: scan.history_truncated,
|
|
152
|
+
nodes: scan.nodes_truncated,
|
|
153
|
+
edges: scan.edges_truncated,
|
|
154
|
+
diagnostics: scan.diagnostics_truncated,
|
|
155
|
+
},
|
|
156
|
+
evidence_classes: listRepositoryGraphEvidenceClasses(project.id, scan.id),
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
export const requireRepositoryGraphScan = (projectId, scanId, target) => {
|
|
160
|
+
const scan = getRepositoryGraphScan(projectId, scanId);
|
|
161
|
+
if (!scan || !targetsMatch(scanTarget(scan), target)) {
|
|
162
|
+
throw new RepositoryGraphQueryError(404, 'scan_not_found', 'Repository graph scan not found');
|
|
163
|
+
}
|
|
164
|
+
return scan;
|
|
165
|
+
};
|
|
166
|
+
export const getRepositoryGraphStatus = async (project, target, scanId) => {
|
|
167
|
+
const scan = scanId
|
|
168
|
+
? requireRepositoryGraphScan(project.id, scanId, target)
|
|
169
|
+
: getLatestRepositoryGraphScanForTarget(project.id, target);
|
|
170
|
+
if (!scan) {
|
|
171
|
+
throw new RepositoryGraphQueryError(404, 'scan_not_found', 'No repository graph scan exists for this target');
|
|
172
|
+
}
|
|
173
|
+
const diagnostics = listRepositoryGraphDiagnostics(project.id, scan.id, MAX_REPOSITORY_GRAPH_STATUS_DIAGNOSTICS + 1);
|
|
174
|
+
const diagnosticsTruncated = diagnostics.length > MAX_REPOSITORY_GRAPH_STATUS_DIAGNOSTICS;
|
|
175
|
+
return {
|
|
176
|
+
...await responseContext(project, scan, diagnosticsTruncated),
|
|
177
|
+
diagnostic_summary: {
|
|
178
|
+
diagnostics: diagnostics.slice(0, MAX_REPOSITORY_GRAPH_STATUS_DIAGNOSTICS),
|
|
179
|
+
returned_count: Math.min(diagnostics.length, MAX_REPOSITORY_GRAPH_STATUS_DIAGNOSTICS),
|
|
180
|
+
total_count: scan.diagnostic_count,
|
|
181
|
+
truncated: diagnosticsTruncated || scan.diagnostics_truncated,
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
export const normalizeRepositoryGraphPath = (value) => {
|
|
186
|
+
const path = value.trim();
|
|
187
|
+
if (!path || path.includes('\0') || posix.isAbsolute(path) || win32.isAbsolute(path)) {
|
|
188
|
+
throw new RepositoryGraphQueryError(400, 'malformed_path', 'path must be a non-empty repository-relative path');
|
|
189
|
+
}
|
|
190
|
+
const normalized = posix.normalize(path.replaceAll('\\', '/'));
|
|
191
|
+
if (normalized === '..' || normalized.startsWith('../') || normalized === '.' || normalized.startsWith('./')) {
|
|
192
|
+
throw new RepositoryGraphQueryError(400, 'malformed_path', 'path must not escape the repository');
|
|
193
|
+
}
|
|
194
|
+
return normalized;
|
|
195
|
+
};
|
|
196
|
+
export const queryRepositoryGraphPathImpact = async (project, target, scanId, rawPath, options) => {
|
|
197
|
+
if (!Number.isSafeInteger(options.depth) || options.depth < 1 || options.depth > MAX_REPOSITORY_GRAPH_TRAVERSAL_DEPTH) {
|
|
198
|
+
throw new RepositoryGraphQueryError(400, 'invalid_depth', `depth must be an integer from 1 to ${MAX_REPOSITORY_GRAPH_TRAVERSAL_DEPTH}`);
|
|
199
|
+
}
|
|
200
|
+
if (!Number.isSafeInteger(options.limit) || options.limit < 1 || options.limit > MAX_REPOSITORY_GRAPH_TRAVERSAL_PATHS) {
|
|
201
|
+
throw new RepositoryGraphQueryError(400, 'invalid_limit', `limit must be an integer from 1 to ${MAX_REPOSITORY_GRAPH_TRAVERSAL_PATHS}`);
|
|
202
|
+
}
|
|
203
|
+
const scan = requireRepositoryGraphScan(project.id, scanId, target);
|
|
204
|
+
if (scan.status !== 'completed') {
|
|
205
|
+
throw new RepositoryGraphQueryError(409, 'scan_not_queryable', `Repository graph scan is ${scan.status} and has no queryable completed graph`);
|
|
206
|
+
}
|
|
207
|
+
const path = normalizeRepositoryGraphPath(rawPath);
|
|
208
|
+
const roots = getRepositoryGraphNodesByPath(project.id, scan.id, path);
|
|
209
|
+
if (roots.length === 0) {
|
|
210
|
+
throw new RepositoryGraphQueryError(404, 'path_not_found', 'Path was not found in the selected repository graph scan');
|
|
211
|
+
}
|
|
212
|
+
const rootPaths = roots.map(root => ({
|
|
213
|
+
path: root.path ?? path,
|
|
214
|
+
node: root,
|
|
215
|
+
depth: 0,
|
|
216
|
+
edge_ids: [],
|
|
217
|
+
evidence_classes: [],
|
|
218
|
+
}));
|
|
219
|
+
const paths = [rootPaths[0]];
|
|
220
|
+
const visited = new Set(roots.map(root => root.id));
|
|
221
|
+
let frontier = new Map(rootPaths.map(entry => [entry.node.id, entry]));
|
|
222
|
+
let truncated = false;
|
|
223
|
+
const directions = options.direction === 'combined'
|
|
224
|
+
? ['incoming', 'outgoing']
|
|
225
|
+
: [options.direction];
|
|
226
|
+
for (let depth = 1; depth <= options.depth && frontier.size > 0; depth += 1) {
|
|
227
|
+
const frontierIds = [...frontier.keys()];
|
|
228
|
+
const edgeBudget = Math.max(options.limit * 4, options.limit + 1);
|
|
229
|
+
const edgeGroups = directions.map(direction => ({
|
|
230
|
+
direction,
|
|
231
|
+
edges: getRepositoryGraphEdgesForNodes(project.id, scan.id, frontierIds, direction, edgeBudget + 1),
|
|
232
|
+
}));
|
|
233
|
+
if (edgeGroups.some(group => group.edges.length > edgeBudget)) {
|
|
234
|
+
truncated = true;
|
|
235
|
+
}
|
|
236
|
+
const edges = [...new Map(edgeGroups.flatMap(group => group.edges.slice(0, edgeBudget)
|
|
237
|
+
.map(edge => [edge.kind === 'co_changes' ? edge.id : `${group.direction}:${edge.id}`, {
|
|
238
|
+
direction: group.direction,
|
|
239
|
+
edge,
|
|
240
|
+
}]))).values()];
|
|
241
|
+
const traversalEndpoints = edges.map(({ direction, edge }) => {
|
|
242
|
+
if (edge.kind === 'co_changes') {
|
|
243
|
+
return frontier.has(edge.source)
|
|
244
|
+
? { parentId: edge.source, nodeId: edge.target }
|
|
245
|
+
: { parentId: edge.target, nodeId: edge.source };
|
|
246
|
+
}
|
|
247
|
+
return direction === 'outgoing'
|
|
248
|
+
? { parentId: edge.source, nodeId: edge.target }
|
|
249
|
+
: { parentId: edge.target, nodeId: edge.source };
|
|
250
|
+
});
|
|
251
|
+
const candidateIds = [...new Set(traversalEndpoints.map(({ nodeId }) => nodeId)
|
|
252
|
+
.filter(nodeId => !visited.has(nodeId)))];
|
|
253
|
+
const remainingNodeCapacity = MAX_REPOSITORY_GRAPH_TRAVERSAL_PATHS - visited.size;
|
|
254
|
+
if (candidateIds.length > remainingNodeCapacity) {
|
|
255
|
+
truncated = true;
|
|
256
|
+
}
|
|
257
|
+
const nodes = new Map(getRepositoryGraphNodesByIds(project.id, scan.id, candidateIds.slice(0, remainingNodeCapacity))
|
|
258
|
+
.map(node => [node.id, node]));
|
|
259
|
+
const next = new Map();
|
|
260
|
+
for (let index = 0; index < edges.length; index += 1) {
|
|
261
|
+
const { edge } = edges[index];
|
|
262
|
+
const { parentId, nodeId } = traversalEndpoints[index];
|
|
263
|
+
const parent = frontier.get(parentId);
|
|
264
|
+
const node = nodes.get(nodeId);
|
|
265
|
+
if (!parent || !node || visited.has(nodeId)) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
visited.add(nodeId);
|
|
269
|
+
const traversalPath = {
|
|
270
|
+
path: node.path ?? node.name,
|
|
271
|
+
node,
|
|
272
|
+
depth,
|
|
273
|
+
edge_ids: [...parent.edge_ids, edge.id],
|
|
274
|
+
evidence_classes: [...new Set([...parent.evidence_classes, edge.evidence])],
|
|
275
|
+
};
|
|
276
|
+
next.set(nodeId, traversalPath);
|
|
277
|
+
if (node.path) {
|
|
278
|
+
if (paths.length >= options.limit) {
|
|
279
|
+
truncated = true;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
paths.push(traversalPath);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
frontier = next;
|
|
286
|
+
if (truncated && paths.length >= options.limit) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
...await responseContext(project, scan, truncated),
|
|
292
|
+
query: { path, direction: options.direction, depth: options.depth, limit: options.limit },
|
|
293
|
+
paths,
|
|
294
|
+
returned_path_count: paths.length,
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
const edgeExplanation = (edge) => {
|
|
298
|
+
if (edge.kind === 'co_changes') {
|
|
299
|
+
return 'Git history recorded these paths changing together.';
|
|
300
|
+
}
|
|
301
|
+
if (edge.kind === 'tests') {
|
|
302
|
+
return 'Repository structure identifies this test as covering the target path.';
|
|
303
|
+
}
|
|
304
|
+
if (edge.kind === 'config_reference') {
|
|
305
|
+
return 'A repository configuration file references the target.';
|
|
306
|
+
}
|
|
307
|
+
return `Static repository analysis identified a ${edge.kind.replaceAll('_', ' ')} relationship.`;
|
|
308
|
+
};
|
|
309
|
+
const MAX_REPOSITORY_GRAPH_EVIDENCE_VALUE_LENGTH = 500;
|
|
310
|
+
const MAX_REPOSITORY_GRAPH_EVIDENCE_COMMIT_IDS = 10;
|
|
311
|
+
const MAX_REPOSITORY_GRAPH_EVIDENCE_COMMIT_ID_LENGTH = 80;
|
|
312
|
+
const edgeEvidenceDetail = (edge) => {
|
|
313
|
+
if (!edge.detail) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
if (edge.kind !== 'co_changes') {
|
|
317
|
+
return {
|
|
318
|
+
type: 'relationship_value',
|
|
319
|
+
value: edge.detail.slice(0, MAX_REPOSITORY_GRAPH_EVIDENCE_VALUE_LENGTH),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
const parsed = JSON.parse(edge.detail);
|
|
324
|
+
const count = parsed.count;
|
|
325
|
+
const sampledCommitIds = parsed.sampledCommitIds;
|
|
326
|
+
if (!Number.isSafeInteger(count) || count < 1 || !Array.isArray(sampledCommitIds)) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
type: 'co_changes',
|
|
331
|
+
change_count: count,
|
|
332
|
+
sampled_commit_ids: sampledCommitIds
|
|
333
|
+
.filter((value) => typeof value === 'string')
|
|
334
|
+
.slice(0, MAX_REPOSITORY_GRAPH_EVIDENCE_COMMIT_IDS)
|
|
335
|
+
.map(value => value.slice(0, MAX_REPOSITORY_GRAPH_EVIDENCE_COMMIT_ID_LENGTH)),
|
|
336
|
+
samples_truncated: parsed.truncated === true
|
|
337
|
+
|| sampledCommitIds.length > MAX_REPOSITORY_GRAPH_EVIDENCE_COMMIT_IDS,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
export const explainRepositoryGraphEdge = async (project, target, scanId, edgeId) => {
|
|
345
|
+
const scan = requireRepositoryGraphScan(project.id, scanId, target);
|
|
346
|
+
const edge = getRepositoryGraphEdge(project.id, scan.id, edgeId);
|
|
347
|
+
if (!edge) {
|
|
348
|
+
throw new RepositoryGraphQueryError(404, 'edge_not_found', 'Repository graph edge not found');
|
|
349
|
+
}
|
|
350
|
+
const nodes = new Map(getRepositoryGraphNodesByIds(project.id, scan.id, [edge.source, edge.target])
|
|
351
|
+
.map(node => [node.id, node]));
|
|
352
|
+
const source = nodes.get(edge.source);
|
|
353
|
+
const destination = nodes.get(edge.target);
|
|
354
|
+
if (!source || !destination) {
|
|
355
|
+
throw new RepositoryGraphQueryError(500, 'invalid_edge', 'Repository graph edge endpoints are unavailable');
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
...await responseContext(project, scan),
|
|
359
|
+
edge: {
|
|
360
|
+
id: edge.id,
|
|
361
|
+
kind: edge.kind,
|
|
362
|
+
evidence_class: edge.evidence,
|
|
363
|
+
source,
|
|
364
|
+
target: destination,
|
|
365
|
+
explanation: edgeExplanation(edge),
|
|
366
|
+
detail: edgeEvidenceDetail(edge),
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
//# sourceMappingURL=repositoryGraphQueries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositoryGraphQueries.js","sourceRoot":"","sources":["../../../server/lib/repositoryGraphQueries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import db from '../db.js';
|
|
2
|
+
export const REPOSITORY_GRAPH_COMPLETED_RETAIN_COUNT = 3;
|
|
3
|
+
export const REPOSITORY_GRAPH_NON_COMPLETED_RETAIN_COUNT = 10;
|
|
4
|
+
export const REPOSITORY_GRAPH_NON_COMPLETED_RETENTION_DAYS = 30;
|
|
5
|
+
export const DEFAULT_REPOSITORY_GRAPH_RETENTION_BATCH_SIZE = 100;
|
|
6
|
+
export const DEFAULT_REPOSITORY_GRAPH_RETENTION_MAX_BATCHES = 100;
|
|
7
|
+
const pruneTransaction = db.transaction((cutoff, batchSize) => {
|
|
8
|
+
const hasRepositoryGraphScans = Boolean(db.prepare(`
|
|
9
|
+
SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'repository_graph_scans'
|
|
10
|
+
`).get());
|
|
11
|
+
if (!hasRepositoryGraphScans) {
|
|
12
|
+
return { completed: 0, non_completed: 0, total: 0 };
|
|
13
|
+
}
|
|
14
|
+
const candidates = db.prepare(`
|
|
15
|
+
WITH ranked AS (
|
|
16
|
+
SELECT id, project_id, status,
|
|
17
|
+
ROW_NUMBER() OVER (
|
|
18
|
+
PARTITION BY project_id, target_key, CASE WHEN status = 'completed' THEN 'completed' ELSE 'non_completed' END
|
|
19
|
+
ORDER BY scan_sequence DESC
|
|
20
|
+
) AS retention_rank
|
|
21
|
+
FROM repository_graph_scans
|
|
22
|
+
WHERE status IN ('completed', 'failed', 'cancelled', 'interrupted')
|
|
23
|
+
)
|
|
24
|
+
SELECT ranked.id, ranked.project_id
|
|
25
|
+
FROM ranked
|
|
26
|
+
WHERE NOT EXISTS (SELECT 1 FROM repository_graph_scan_references refs WHERE refs.scan_id = ranked.id)
|
|
27
|
+
AND (
|
|
28
|
+
(ranked.status = 'completed' AND ranked.retention_rank > ?)
|
|
29
|
+
OR (ranked.status IN ('failed', 'cancelled', 'interrupted') AND (
|
|
30
|
+
ranked.retention_rank > ?
|
|
31
|
+
OR julianday((SELECT COALESCE(completed_at, updated_at, created_at) FROM repository_graph_scans WHERE id = ranked.id)) <= julianday(?)
|
|
32
|
+
))
|
|
33
|
+
)
|
|
34
|
+
ORDER BY ranked.project_id, ranked.id
|
|
35
|
+
LIMIT ?
|
|
36
|
+
`).all(REPOSITORY_GRAPH_COMPLETED_RETAIN_COUNT, REPOSITORY_GRAPH_NON_COMPLETED_RETAIN_COUNT, cutoff, batchSize);
|
|
37
|
+
let completed = 0;
|
|
38
|
+
let nonCompleted = 0;
|
|
39
|
+
const deleteCompleted = db.prepare(`
|
|
40
|
+
DELETE FROM repository_graph_scans
|
|
41
|
+
WHERE id = ? AND project_id = ? AND status = 'completed'
|
|
42
|
+
AND NOT EXISTS (SELECT 1 FROM repository_graph_scan_references refs WHERE refs.scan_id = repository_graph_scans.id)
|
|
43
|
+
AND (SELECT COUNT(*) FROM repository_graph_scans newer
|
|
44
|
+
WHERE newer.project_id = repository_graph_scans.project_id
|
|
45
|
+
AND newer.target_key = repository_graph_scans.target_key
|
|
46
|
+
AND newer.status = 'completed'
|
|
47
|
+
AND newer.scan_sequence > repository_graph_scans.scan_sequence) >= ?
|
|
48
|
+
`);
|
|
49
|
+
const deleteNonCompleted = db.prepare(`
|
|
50
|
+
DELETE FROM repository_graph_scans
|
|
51
|
+
WHERE id = ? AND project_id = ? AND status IN ('failed', 'cancelled', 'interrupted')
|
|
52
|
+
AND NOT EXISTS (SELECT 1 FROM repository_graph_scan_references refs WHERE refs.scan_id = repository_graph_scans.id)
|
|
53
|
+
AND (
|
|
54
|
+
julianday(COALESCE(completed_at, updated_at, created_at)) <= julianday(?)
|
|
55
|
+
OR (SELECT COUNT(*) FROM repository_graph_scans newer
|
|
56
|
+
WHERE newer.project_id = repository_graph_scans.project_id
|
|
57
|
+
AND newer.target_key = repository_graph_scans.target_key
|
|
58
|
+
AND newer.status IN ('failed', 'cancelled', 'interrupted')
|
|
59
|
+
AND newer.scan_sequence > repository_graph_scans.scan_sequence) >= ?
|
|
60
|
+
)
|
|
61
|
+
`);
|
|
62
|
+
for (const candidate of candidates) {
|
|
63
|
+
const row = db.prepare('SELECT status FROM repository_graph_scans WHERE id = ? AND project_id = ?')
|
|
64
|
+
.get(candidate.id, candidate.project_id);
|
|
65
|
+
if (row?.status === 'completed') {
|
|
66
|
+
completed += deleteCompleted.run(candidate.id, candidate.project_id, REPOSITORY_GRAPH_COMPLETED_RETAIN_COUNT).changes;
|
|
67
|
+
}
|
|
68
|
+
else if (row) {
|
|
69
|
+
nonCompleted += deleteNonCompleted.run(candidate.id, candidate.project_id, cutoff, REPOSITORY_GRAPH_NON_COMPLETED_RETAIN_COUNT).changes;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { completed, non_completed: nonCompleted, total: completed + nonCompleted };
|
|
73
|
+
});
|
|
74
|
+
export const pruneRepositoryGraphScans = ({ batchSize = DEFAULT_REPOSITORY_GRAPH_RETENTION_BATCH_SIZE, now = new Date(), nonCompletedRetentionDays = REPOSITORY_GRAPH_NON_COMPLETED_RETENTION_DAYS, } = {}) => {
|
|
75
|
+
if (!Number.isSafeInteger(batchSize) || batchSize < 1 || batchSize > 1_000) {
|
|
76
|
+
throw new Error('Repository graph retention batch size must be between 1 and 1000');
|
|
77
|
+
}
|
|
78
|
+
if (!Number.isSafeInteger(nonCompletedRetentionDays) || nonCompletedRetentionDays < 1 || nonCompletedRetentionDays > 36_500) {
|
|
79
|
+
throw new Error('Repository graph non-completed retention days must be between 1 and 36500');
|
|
80
|
+
}
|
|
81
|
+
if (Number.isNaN(now.getTime())) {
|
|
82
|
+
throw new Error('Repository graph retention time must be valid');
|
|
83
|
+
}
|
|
84
|
+
const cutoff = new Date(now.getTime() - nonCompletedRetentionDays * 24 * 60 * 60 * 1000);
|
|
85
|
+
return pruneTransaction(cutoff.toISOString(), batchSize);
|
|
86
|
+
};
|
|
87
|
+
export const pruneAllRepositoryGraphScans = (options = {}) => {
|
|
88
|
+
const { maxBatches = DEFAULT_REPOSITORY_GRAPH_RETENTION_MAX_BATCHES, ...pruneOptions } = options;
|
|
89
|
+
if (!Number.isSafeInteger(maxBatches) || maxBatches < 1 || maxBatches > 1_000) {
|
|
90
|
+
throw new Error('Repository graph retention max batches must be between 1 and 1000');
|
|
91
|
+
}
|
|
92
|
+
const total = { completed: 0, non_completed: 0, total: 0 };
|
|
93
|
+
for (let batch = 0; batch < maxBatches; batch += 1) {
|
|
94
|
+
const result = pruneRepositoryGraphScans(pruneOptions);
|
|
95
|
+
total.completed += result.completed;
|
|
96
|
+
total.non_completed += result.non_completed;
|
|
97
|
+
total.total += result.total;
|
|
98
|
+
if (result.total === 0) {
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return total;
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=repositoryGraphRetention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositoryGraphRetention.js","sourceRoot":"","sources":["../../../server/lib/repositoryGraphRetention.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { resolveRepositoryGraphAnalyzerConfiguration } from './repositoryGraphAnalyzer.js';
|
|
2
|
+
import { explainRepositoryGraphEdge, getRepositoryGraphStatus, MAX_REPOSITORY_GRAPH_TRAVERSAL_PATHS, queryRepositoryGraphPathImpact, RepositoryGraphQueryError, requireRepositoryGraphScan, } from './repositoryGraphQueries.js';
|
|
3
|
+
import { cancelRepositoryGraphScan, requestRepositoryGraphScan, RepositoryGraphScanAdmissionError, retryRepositoryGraphScan, } from './repositoryGraphScan.js';
|
|
4
|
+
import { getProjectById } from './projects.js';
|
|
5
|
+
const parseTarget = (input) => {
|
|
6
|
+
if (input.target_kind === 'repo_root') {
|
|
7
|
+
if (input.bundle_id !== undefined && input.bundle_id !== null && input.bundle_id !== '') {
|
|
8
|
+
throw new RepositoryGraphQueryError(400, 'invalid_target', 'repo_root target cannot include bundle_id');
|
|
9
|
+
}
|
|
10
|
+
return { kind: 'repo_root', bundle_id: null };
|
|
11
|
+
}
|
|
12
|
+
if (input.target_kind === 'bundle') {
|
|
13
|
+
if (typeof input.bundle_id !== 'string' || !input.bundle_id.trim()) {
|
|
14
|
+
throw new RepositoryGraphQueryError(400, 'invalid_target', 'bundle target requires bundle_id');
|
|
15
|
+
}
|
|
16
|
+
return { kind: 'bundle', bundle_id: input.bundle_id.trim() };
|
|
17
|
+
}
|
|
18
|
+
throw new RepositoryGraphQueryError(400, 'invalid_target', 'target_kind must be repo_root or bundle');
|
|
19
|
+
};
|
|
20
|
+
const parseRequiredString = (value, name) => {
|
|
21
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
22
|
+
throw new RepositoryGraphQueryError(400, `invalid_${name}`, `${name} must be a non-empty string`);
|
|
23
|
+
}
|
|
24
|
+
return value.trim();
|
|
25
|
+
};
|
|
26
|
+
const parseInteger = (value, fallback, name) => {
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
const parsed = typeof value === 'string' && /^\d+$/.test(value) ? Number(value) : value;
|
|
31
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
32
|
+
throw new RepositoryGraphQueryError(400, `invalid_${name}`, `${name} must be an integer`);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
};
|
|
36
|
+
const parseDirection = (value) => {
|
|
37
|
+
if (value === undefined) {
|
|
38
|
+
return 'combined';
|
|
39
|
+
}
|
|
40
|
+
if (value === 'incoming' || value === 'outgoing' || value === 'combined') {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
throw new RepositoryGraphQueryError(400, 'invalid_direction', 'direction must be incoming, outgoing, or combined');
|
|
44
|
+
};
|
|
45
|
+
const asyncRoute = (handler) => (req, res, next) => {
|
|
46
|
+
void handler(req, res).catch(next);
|
|
47
|
+
};
|
|
48
|
+
const projectForRequest = (req) => {
|
|
49
|
+
const projectId = typeof req.params.id === 'string' ? req.params.id : null;
|
|
50
|
+
const project = getProjectById(projectId);
|
|
51
|
+
if (!project) {
|
|
52
|
+
throw new RepositoryGraphQueryError(404, 'project_not_found', 'Not found');
|
|
53
|
+
}
|
|
54
|
+
return project;
|
|
55
|
+
};
|
|
56
|
+
const mapScanRequestError = (result) => {
|
|
57
|
+
if (result.state === 'unavailable') {
|
|
58
|
+
throw new RepositoryGraphQueryError(409, result.resolution.reason, result.resolution.message);
|
|
59
|
+
}
|
|
60
|
+
if (result.state === 'identity_unavailable') {
|
|
61
|
+
throw new RepositoryGraphQueryError(409, result.reason, result.message);
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
export const registerRepositoryGraphRoutes = (app, dependencies = {}) => {
|
|
66
|
+
app.post('/projects/:id/repository-graph/scans', asyncRoute(async (req, res) => {
|
|
67
|
+
const project = projectForRequest(req);
|
|
68
|
+
const body = (req.body ?? {});
|
|
69
|
+
const target = parseTarget(body);
|
|
70
|
+
let configuration;
|
|
71
|
+
try {
|
|
72
|
+
configuration = resolveRepositoryGraphAnalyzerConfiguration(body.configuration);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new RepositoryGraphQueryError(400, 'invalid_configuration', error instanceof Error ? error.message : 'Repository graph analyzer configuration is invalid');
|
|
76
|
+
}
|
|
77
|
+
const result = mapScanRequestError(await requestRepositoryGraphScan({
|
|
78
|
+
project,
|
|
79
|
+
target,
|
|
80
|
+
configuration,
|
|
81
|
+
emitter: dependencies.emitter,
|
|
82
|
+
}));
|
|
83
|
+
const status = await getRepositoryGraphStatus(project, target, result.scan.id);
|
|
84
|
+
return res.status(result.state === 'queued' ? 202 : 200).json({ state: result.state, ...status });
|
|
85
|
+
}));
|
|
86
|
+
app.get('/projects/:id/repository-graph/status', asyncRoute(async (req, res) => {
|
|
87
|
+
const project = projectForRequest(req);
|
|
88
|
+
const target = parseTarget(req.query);
|
|
89
|
+
const scanId = req.query.scan_id === undefined
|
|
90
|
+
? undefined
|
|
91
|
+
: parseRequiredString(req.query.scan_id, 'scan_id');
|
|
92
|
+
return res.json(await getRepositoryGraphStatus(project, target, scanId));
|
|
93
|
+
}));
|
|
94
|
+
app.post('/projects/:id/repository-graph/scans/:scanId/cancel', asyncRoute(async (req, res) => {
|
|
95
|
+
const project = projectForRequest(req);
|
|
96
|
+
const target = parseTarget((req.body ?? {}));
|
|
97
|
+
const scanId = parseRequiredString(req.params.scanId, 'scan_id');
|
|
98
|
+
const scan = requireRepositoryGraphScan(project.id, scanId, target);
|
|
99
|
+
await cancelRepositoryGraphScan(project.id, scan.id);
|
|
100
|
+
return res.json(await getRepositoryGraphStatus(project, target, scan.id));
|
|
101
|
+
}));
|
|
102
|
+
app.post('/projects/:id/repository-graph/scans/:scanId/retry', asyncRoute(async (req, res) => {
|
|
103
|
+
const project = projectForRequest(req);
|
|
104
|
+
const target = parseTarget((req.body ?? {}));
|
|
105
|
+
const scanId = parseRequiredString(req.params.scanId, 'scan_id');
|
|
106
|
+
const source = requireRepositoryGraphScan(project.id, scanId, target);
|
|
107
|
+
if (!source.execution_run_id) {
|
|
108
|
+
throw new RepositoryGraphQueryError(409, 'scan_not_retryable', 'Repository graph scan has no retryable execution');
|
|
109
|
+
}
|
|
110
|
+
if (!['failed', 'cancelled', 'interrupted'].includes(source.status)) {
|
|
111
|
+
throw new RepositoryGraphQueryError(409, 'scan_not_retryable', `Repository graph scan is ${source.status} and cannot be retried`);
|
|
112
|
+
}
|
|
113
|
+
const result = mapScanRequestError(await retryRepositoryGraphScan(project.id, source.execution_run_id, dependencies.emitter));
|
|
114
|
+
const status = await getRepositoryGraphStatus(project, target, result.scan.id);
|
|
115
|
+
return res.status(result.state === 'queued' ? 202 : 200).json({ state: result.state, ...status });
|
|
116
|
+
}));
|
|
117
|
+
app.get('/projects/:id/repository-graph/path-impact', asyncRoute(async (req, res) => {
|
|
118
|
+
const project = projectForRequest(req);
|
|
119
|
+
const target = parseTarget(req.query);
|
|
120
|
+
const scanId = parseRequiredString(req.query.scan_id, 'scan_id');
|
|
121
|
+
const path = parseRequiredString(req.query.path, 'path');
|
|
122
|
+
return res.json(await queryRepositoryGraphPathImpact(project, target, scanId, path, {
|
|
123
|
+
direction: parseDirection(req.query.direction),
|
|
124
|
+
depth: parseInteger(req.query.depth, 1, 'depth'),
|
|
125
|
+
limit: parseInteger(req.query.limit, MAX_REPOSITORY_GRAPH_TRAVERSAL_PATHS, 'limit'),
|
|
126
|
+
}));
|
|
127
|
+
}));
|
|
128
|
+
app.get('/projects/:id/repository-graph/edges/:edgeId/explanation', asyncRoute(async (req, res) => {
|
|
129
|
+
const project = projectForRequest(req);
|
|
130
|
+
const target = parseTarget(req.query);
|
|
131
|
+
const scanId = parseRequiredString(req.query.scan_id, 'scan_id');
|
|
132
|
+
const edgeId = parseRequiredString(req.params.edgeId, 'edge_id');
|
|
133
|
+
return res.json(await explainRepositoryGraphEdge(project, target, scanId, edgeId));
|
|
134
|
+
}));
|
|
135
|
+
app.use((error, _req, res, next) => {
|
|
136
|
+
if (error instanceof RepositoryGraphQueryError) {
|
|
137
|
+
return res.status(error.status).json({ error: error.message, reason: error.reason });
|
|
138
|
+
}
|
|
139
|
+
if (error instanceof RepositoryGraphScanAdmissionError) {
|
|
140
|
+
return res.status(503).json({ error: error.message, reason: 'scan_admissions_closed' });
|
|
141
|
+
}
|
|
142
|
+
return next(error);
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=repositoryGraphRoutes.js.map
|