truecourse 0.4.3 → 0.4.5
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/cli.mjs +764 -798
- package/package.json +1 -1
- package/public/assets/{index-CCtFa3au.js → index-8ZCcQOMQ.js} +124 -124
- package/public/index.html +1 -1
- package/server.mjs +1113 -1128
package/server.mjs
CHANGED
|
@@ -17990,17 +17990,17 @@ var require_router = __commonJS({
|
|
|
17990
17990
|
var toString = Object.prototype.toString;
|
|
17991
17991
|
var proto = module.exports = function(options) {
|
|
17992
17992
|
var opts = options || {};
|
|
17993
|
-
function
|
|
17994
|
-
|
|
17995
|
-
}
|
|
17996
|
-
setPrototypeOf(
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
|
|
18000
|
-
|
|
18001
|
-
|
|
18002
|
-
|
|
18003
|
-
return
|
|
17993
|
+
function router10(req, res, next) {
|
|
17994
|
+
router10.handle(req, res, next);
|
|
17995
|
+
}
|
|
17996
|
+
setPrototypeOf(router10, proto);
|
|
17997
|
+
router10.params = {};
|
|
17998
|
+
router10._params = [];
|
|
17999
|
+
router10.caseSensitive = opts.caseSensitive;
|
|
18000
|
+
router10.mergeParams = opts.mergeParams;
|
|
18001
|
+
router10.strict = opts.strict;
|
|
18002
|
+
router10.stack = [];
|
|
18003
|
+
return router10;
|
|
18004
18004
|
};
|
|
18005
18005
|
proto.param = function param(name, fn) {
|
|
18006
18006
|
if (typeof name === "function") {
|
|
@@ -20592,7 +20592,7 @@ var require_application = __commonJS({
|
|
|
20592
20592
|
"node_modules/.pnpm/express@4.22.1/node_modules/express/lib/application.js"(exports, module) {
|
|
20593
20593
|
"use strict";
|
|
20594
20594
|
var finalhandler = require_finalhandler();
|
|
20595
|
-
var
|
|
20595
|
+
var Router10 = require_router();
|
|
20596
20596
|
var methods = require_methods();
|
|
20597
20597
|
var middleware = require_init();
|
|
20598
20598
|
var query = require_query();
|
|
@@ -20657,7 +20657,7 @@ var require_application = __commonJS({
|
|
|
20657
20657
|
};
|
|
20658
20658
|
app.lazyrouter = function lazyrouter() {
|
|
20659
20659
|
if (!this._router) {
|
|
20660
|
-
this._router = new
|
|
20660
|
+
this._router = new Router10({
|
|
20661
20661
|
caseSensitive: this.enabled("case sensitive routing"),
|
|
20662
20662
|
strict: this.enabled("strict routing")
|
|
20663
20663
|
});
|
|
@@ -20666,17 +20666,17 @@ var require_application = __commonJS({
|
|
|
20666
20666
|
}
|
|
20667
20667
|
};
|
|
20668
20668
|
app.handle = function handle(req, res, callback) {
|
|
20669
|
-
var
|
|
20669
|
+
var router10 = this._router;
|
|
20670
20670
|
var done = callback || finalhandler(req, res, {
|
|
20671
20671
|
env: this.get("env"),
|
|
20672
20672
|
onerror: logerror.bind(this)
|
|
20673
20673
|
});
|
|
20674
|
-
if (!
|
|
20674
|
+
if (!router10) {
|
|
20675
20675
|
debug2("no routes defined on app");
|
|
20676
20676
|
done();
|
|
20677
20677
|
return;
|
|
20678
20678
|
}
|
|
20679
|
-
|
|
20679
|
+
router10.handle(req, res, done);
|
|
20680
20680
|
};
|
|
20681
20681
|
app.use = function use(fn) {
|
|
20682
20682
|
var offset = 0;
|
|
@@ -20696,15 +20696,15 @@ var require_application = __commonJS({
|
|
|
20696
20696
|
throw new TypeError("app.use() requires a middleware function");
|
|
20697
20697
|
}
|
|
20698
20698
|
this.lazyrouter();
|
|
20699
|
-
var
|
|
20699
|
+
var router10 = this._router;
|
|
20700
20700
|
fns.forEach(function(fn2) {
|
|
20701
20701
|
if (!fn2 || !fn2.handle || !fn2.set) {
|
|
20702
|
-
return
|
|
20702
|
+
return router10.use(path17, fn2);
|
|
20703
20703
|
}
|
|
20704
20704
|
debug2(".use app under %s", path17);
|
|
20705
20705
|
fn2.mountpath = path17;
|
|
20706
20706
|
fn2.parent = this;
|
|
20707
|
-
|
|
20707
|
+
router10.use(path17, function mounted_app(req, res, next) {
|
|
20708
20708
|
var orig = req.app;
|
|
20709
20709
|
fn2.handle(req, res, function(err) {
|
|
20710
20710
|
setPrototypeOf(req, orig.request);
|
|
@@ -22521,7 +22521,7 @@ var require_express = __commonJS({
|
|
|
22521
22521
|
var mixin = require_merge_descriptors();
|
|
22522
22522
|
var proto = require_application();
|
|
22523
22523
|
var Route = require_route();
|
|
22524
|
-
var
|
|
22524
|
+
var Router10 = require_router();
|
|
22525
22525
|
var req = require_request();
|
|
22526
22526
|
var res = require_response();
|
|
22527
22527
|
exports = module.exports = createApplication;
|
|
@@ -22544,7 +22544,7 @@ var require_express = __commonJS({
|
|
|
22544
22544
|
exports.request = req;
|
|
22545
22545
|
exports.response = res;
|
|
22546
22546
|
exports.Route = Route;
|
|
22547
|
-
exports.Router =
|
|
22547
|
+
exports.Router = Router10;
|
|
22548
22548
|
exports.json = bodyParser.json;
|
|
22549
22549
|
exports.query = require_query();
|
|
22550
22550
|
exports.raw = bodyParser.raw;
|
|
@@ -40157,6 +40157,219 @@ var require_dist4 = __commonJS({
|
|
|
40157
40157
|
}
|
|
40158
40158
|
});
|
|
40159
40159
|
|
|
40160
|
+
// apps/server/src/lib/atomic-write.ts
|
|
40161
|
+
import fs6 from "node:fs";
|
|
40162
|
+
import path5 from "node:path";
|
|
40163
|
+
function atomicWriteJson(targetPath, data) {
|
|
40164
|
+
fs6.mkdirSync(path5.dirname(targetPath), { recursive: true });
|
|
40165
|
+
const tmp = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
|
|
40166
|
+
fs6.writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
40167
|
+
fs6.renameSync(tmp, targetPath);
|
|
40168
|
+
}
|
|
40169
|
+
function lockPath(repoPath) {
|
|
40170
|
+
return path5.join(repoPath, ".truecourse", LOCK_FILENAME);
|
|
40171
|
+
}
|
|
40172
|
+
function acquireAnalyzeLock(repoPath) {
|
|
40173
|
+
const file = lockPath(repoPath);
|
|
40174
|
+
fs6.mkdirSync(path5.dirname(file), { recursive: true });
|
|
40175
|
+
try {
|
|
40176
|
+
const fd = fs6.openSync(file, "wx");
|
|
40177
|
+
fs6.writeSync(fd, `${process.pid}
|
|
40178
|
+
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
40179
|
+
`);
|
|
40180
|
+
fs6.closeSync(fd);
|
|
40181
|
+
} catch (err) {
|
|
40182
|
+
if (err.code === "EEXIST") {
|
|
40183
|
+
let owner = null;
|
|
40184
|
+
try {
|
|
40185
|
+
owner = parseInt(fs6.readFileSync(file, "utf-8").split("\n")[0], 10) || null;
|
|
40186
|
+
} catch {
|
|
40187
|
+
}
|
|
40188
|
+
throw new AnalyzeLockError(repoPath, owner);
|
|
40189
|
+
}
|
|
40190
|
+
throw err;
|
|
40191
|
+
}
|
|
40192
|
+
}
|
|
40193
|
+
function releaseAnalyzeLock(repoPath) {
|
|
40194
|
+
try {
|
|
40195
|
+
fs6.unlinkSync(lockPath(repoPath));
|
|
40196
|
+
} catch (err) {
|
|
40197
|
+
if (err.code !== "ENOENT") throw err;
|
|
40198
|
+
}
|
|
40199
|
+
}
|
|
40200
|
+
var LOCK_FILENAME, AnalyzeLockError;
|
|
40201
|
+
var init_atomic_write = __esm({
|
|
40202
|
+
"apps/server/src/lib/atomic-write.ts"() {
|
|
40203
|
+
"use strict";
|
|
40204
|
+
LOCK_FILENAME = ".analyze.lock";
|
|
40205
|
+
AnalyzeLockError = class extends Error {
|
|
40206
|
+
constructor(repoPath, ownerPid) {
|
|
40207
|
+
const who = ownerPid != null ? ` (held by pid ${ownerPid})` : "";
|
|
40208
|
+
super(
|
|
40209
|
+
`Another analyze is already running for ${repoPath}${who}. If you're sure no analyze is in progress, remove ${lockPath(repoPath)} and retry.`
|
|
40210
|
+
);
|
|
40211
|
+
this.name = "AnalyzeLockError";
|
|
40212
|
+
}
|
|
40213
|
+
};
|
|
40214
|
+
}
|
|
40215
|
+
});
|
|
40216
|
+
|
|
40217
|
+
// apps/server/src/lib/analysis-store.ts
|
|
40218
|
+
var analysis_store_exports = {};
|
|
40219
|
+
__export(analysis_store_exports, {
|
|
40220
|
+
analysisFilePath: () => analysisFilePath,
|
|
40221
|
+
appendHistory: () => appendHistory,
|
|
40222
|
+
buildAnalysisFilename: () => buildAnalysisFilename,
|
|
40223
|
+
clearLatestCache: () => clearLatestCache,
|
|
40224
|
+
deleteAnalysis: () => deleteAnalysis,
|
|
40225
|
+
deleteDiff: () => deleteDiff,
|
|
40226
|
+
deleteLatest: () => deleteLatest,
|
|
40227
|
+
diffPath: () => diffPath,
|
|
40228
|
+
findAnalysisFilename: () => findAnalysisFilename,
|
|
40229
|
+
historyPath: () => historyPath,
|
|
40230
|
+
latestPath: () => latestPath,
|
|
40231
|
+
listAnalyses: () => listAnalyses,
|
|
40232
|
+
readAnalysis: () => readAnalysis,
|
|
40233
|
+
readDiff: () => readDiff,
|
|
40234
|
+
readHistory: () => readHistory,
|
|
40235
|
+
readLatest: () => readLatest,
|
|
40236
|
+
removeFromHistory: () => removeFromHistory,
|
|
40237
|
+
writeAnalysis: () => writeAnalysis,
|
|
40238
|
+
writeDiff: () => writeDiff,
|
|
40239
|
+
writeLatest: () => writeLatest
|
|
40240
|
+
});
|
|
40241
|
+
import fs7 from "node:fs";
|
|
40242
|
+
import path6 from "node:path";
|
|
40243
|
+
function storeDir(repoPath) {
|
|
40244
|
+
return path6.join(repoPath, TRUECOURSE_DIR2);
|
|
40245
|
+
}
|
|
40246
|
+
function analysesDir(repoPath) {
|
|
40247
|
+
return path6.join(storeDir(repoPath), ANALYSES_DIR);
|
|
40248
|
+
}
|
|
40249
|
+
function analysisFilePath(repoPath, filename) {
|
|
40250
|
+
return path6.join(analysesDir(repoPath), filename);
|
|
40251
|
+
}
|
|
40252
|
+
function latestPath(repoPath) {
|
|
40253
|
+
return path6.join(storeDir(repoPath), LATEST_FILE);
|
|
40254
|
+
}
|
|
40255
|
+
function historyPath(repoPath) {
|
|
40256
|
+
return path6.join(storeDir(repoPath), HISTORY_FILE);
|
|
40257
|
+
}
|
|
40258
|
+
function diffPath(repoPath) {
|
|
40259
|
+
return path6.join(storeDir(repoPath), DIFF_FILE);
|
|
40260
|
+
}
|
|
40261
|
+
function buildAnalysisFilename(analysisId, createdAt) {
|
|
40262
|
+
const iso = createdAt.replace(/[:.]/g, "-").replace(/-\d{3}Z$/, "Z");
|
|
40263
|
+
const shortId = analysisId.replace(/-/g, "").slice(0, 8);
|
|
40264
|
+
return `${iso}_${shortId}.json`;
|
|
40265
|
+
}
|
|
40266
|
+
function readLatest(repoPath) {
|
|
40267
|
+
const file = latestPath(repoPath);
|
|
40268
|
+
let mtime;
|
|
40269
|
+
try {
|
|
40270
|
+
mtime = fs7.statSync(file).mtimeMs;
|
|
40271
|
+
} catch (err) {
|
|
40272
|
+
if (err.code === "ENOENT") {
|
|
40273
|
+
latestCache.delete(repoPath);
|
|
40274
|
+
return null;
|
|
40275
|
+
}
|
|
40276
|
+
throw err;
|
|
40277
|
+
}
|
|
40278
|
+
const cached = latestCache.get(repoPath);
|
|
40279
|
+
if (cached && cached.mtime === mtime) return cached.data;
|
|
40280
|
+
const data = JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
40281
|
+
latestCache.set(repoPath, { mtime, data });
|
|
40282
|
+
return data;
|
|
40283
|
+
}
|
|
40284
|
+
function writeLatest(repoPath, latest) {
|
|
40285
|
+
atomicWriteJson(latestPath(repoPath), latest);
|
|
40286
|
+
latestCache.delete(repoPath);
|
|
40287
|
+
}
|
|
40288
|
+
function deleteLatest(repoPath) {
|
|
40289
|
+
try {
|
|
40290
|
+
fs7.unlinkSync(latestPath(repoPath));
|
|
40291
|
+
} catch (err) {
|
|
40292
|
+
if (err.code !== "ENOENT") throw err;
|
|
40293
|
+
}
|
|
40294
|
+
latestCache.delete(repoPath);
|
|
40295
|
+
}
|
|
40296
|
+
function writeAnalysis(repoPath, snapshot) {
|
|
40297
|
+
const filename = buildAnalysisFilename(snapshot.id, snapshot.createdAt);
|
|
40298
|
+
atomicWriteJson(analysisFilePath(repoPath, filename), snapshot);
|
|
40299
|
+
return { filename, snapshot };
|
|
40300
|
+
}
|
|
40301
|
+
function readAnalysis(repoPath, filename) {
|
|
40302
|
+
const file = analysisFilePath(repoPath, filename);
|
|
40303
|
+
if (!fs7.existsSync(file)) return null;
|
|
40304
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
40305
|
+
}
|
|
40306
|
+
function listAnalyses(repoPath) {
|
|
40307
|
+
const dir = analysesDir(repoPath);
|
|
40308
|
+
if (!fs7.existsSync(dir)) return [];
|
|
40309
|
+
return fs7.readdirSync(dir).filter((name) => name.endsWith(".json")).sort();
|
|
40310
|
+
}
|
|
40311
|
+
function findAnalysisFilename(repoPath, analysisId) {
|
|
40312
|
+
for (const name of listAnalyses(repoPath).reverse()) {
|
|
40313
|
+
const snap = readAnalysis(repoPath, name);
|
|
40314
|
+
if (snap?.id === analysisId) return name;
|
|
40315
|
+
}
|
|
40316
|
+
return null;
|
|
40317
|
+
}
|
|
40318
|
+
function deleteAnalysis(repoPath, filename) {
|
|
40319
|
+
try {
|
|
40320
|
+
fs7.unlinkSync(analysisFilePath(repoPath, filename));
|
|
40321
|
+
} catch (err) {
|
|
40322
|
+
if (err.code !== "ENOENT") throw err;
|
|
40323
|
+
}
|
|
40324
|
+
}
|
|
40325
|
+
function readHistory(repoPath) {
|
|
40326
|
+
const file = historyPath(repoPath);
|
|
40327
|
+
if (!fs7.existsSync(file)) return { analyses: [] };
|
|
40328
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
40329
|
+
}
|
|
40330
|
+
function appendHistory(repoPath, entry) {
|
|
40331
|
+
const history = readHistory(repoPath);
|
|
40332
|
+
history.analyses.push(entry);
|
|
40333
|
+
atomicWriteJson(historyPath(repoPath), history);
|
|
40334
|
+
}
|
|
40335
|
+
function removeFromHistory(repoPath, analysisId) {
|
|
40336
|
+
const history = readHistory(repoPath);
|
|
40337
|
+
const next = history.analyses.filter((a) => a.id !== analysisId);
|
|
40338
|
+
if (next.length === history.analyses.length) return;
|
|
40339
|
+
atomicWriteJson(historyPath(repoPath), { analyses: next });
|
|
40340
|
+
}
|
|
40341
|
+
function readDiff(repoPath) {
|
|
40342
|
+
const file = diffPath(repoPath);
|
|
40343
|
+
if (!fs7.existsSync(file)) return null;
|
|
40344
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
40345
|
+
}
|
|
40346
|
+
function writeDiff(repoPath, diff) {
|
|
40347
|
+
atomicWriteJson(diffPath(repoPath), diff);
|
|
40348
|
+
}
|
|
40349
|
+
function deleteDiff(repoPath) {
|
|
40350
|
+
try {
|
|
40351
|
+
fs7.unlinkSync(diffPath(repoPath));
|
|
40352
|
+
} catch (err) {
|
|
40353
|
+
if (err.code !== "ENOENT") throw err;
|
|
40354
|
+
}
|
|
40355
|
+
}
|
|
40356
|
+
function clearLatestCache() {
|
|
40357
|
+
latestCache.clear();
|
|
40358
|
+
}
|
|
40359
|
+
var TRUECOURSE_DIR2, ANALYSES_DIR, LATEST_FILE, HISTORY_FILE, DIFF_FILE, latestCache;
|
|
40360
|
+
var init_analysis_store = __esm({
|
|
40361
|
+
"apps/server/src/lib/analysis-store.ts"() {
|
|
40362
|
+
"use strict";
|
|
40363
|
+
init_atomic_write();
|
|
40364
|
+
TRUECOURSE_DIR2 = ".truecourse";
|
|
40365
|
+
ANALYSES_DIR = "analyses";
|
|
40366
|
+
LATEST_FILE = "LATEST.json";
|
|
40367
|
+
HISTORY_FILE = "history.json";
|
|
40368
|
+
DIFF_FILE = "diff.json";
|
|
40369
|
+
latestCache = /* @__PURE__ */ new Map();
|
|
40370
|
+
}
|
|
40371
|
+
});
|
|
40372
|
+
|
|
40160
40373
|
// node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
|
|
40161
40374
|
var require_ignore = __commonJS({
|
|
40162
40375
|
"node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js"(exports, module) {
|
|
@@ -45316,7 +45529,7 @@ var init_escape = __esm({
|
|
|
45316
45529
|
});
|
|
45317
45530
|
|
|
45318
45531
|
// node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js
|
|
45319
|
-
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform,
|
|
45532
|
+
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path7, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
|
|
45320
45533
|
var init_esm3 = __esm({
|
|
45321
45534
|
"node_modules/.pnpm/minimatch@10.2.4/node_modules/minimatch/dist/esm/index.js"() {
|
|
45322
45535
|
init_esm2();
|
|
@@ -45385,11 +45598,11 @@ var init_esm3 = __esm({
|
|
|
45385
45598
|
return (f2) => f2.length === len && f2 !== "." && f2 !== "..";
|
|
45386
45599
|
};
|
|
45387
45600
|
defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
45388
|
-
|
|
45601
|
+
path7 = {
|
|
45389
45602
|
win32: { sep: "\\" },
|
|
45390
45603
|
posix: { sep: "/" }
|
|
45391
45604
|
};
|
|
45392
|
-
sep = defaultPlatform === "win32" ?
|
|
45605
|
+
sep = defaultPlatform === "win32" ? path7.win32.sep : path7.posix.sep;
|
|
45393
45606
|
minimatch.sep = sep;
|
|
45394
45607
|
GLOBSTAR = Symbol("globstar **");
|
|
45395
45608
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -47435,7 +47648,7 @@ var init_database_detector = __esm({
|
|
|
47435
47648
|
});
|
|
47436
47649
|
|
|
47437
47650
|
// packages/analyzer/dist/module-extractor.js
|
|
47438
|
-
import
|
|
47651
|
+
import path8 from "path";
|
|
47439
47652
|
function extractModulesAndMethods(analyses, layerDetails, fileDependencies) {
|
|
47440
47653
|
const modules = [];
|
|
47441
47654
|
const methods = [];
|
|
@@ -47582,7 +47795,7 @@ function deriveModuleName(analysis) {
|
|
|
47582
47795
|
return localExports[0].name;
|
|
47583
47796
|
}
|
|
47584
47797
|
const baseName = fileBaseName(analysis.filePath);
|
|
47585
|
-
const strippedName = stripExtension(
|
|
47798
|
+
const strippedName = stripExtension(path8.basename(analysis.filePath));
|
|
47586
47799
|
if (INDEX_NAMES.has(strippedName)) {
|
|
47587
47800
|
return baseName;
|
|
47588
47801
|
}
|
|
@@ -47599,10 +47812,10 @@ function deriveModuleName(analysis) {
|
|
|
47599
47812
|
return baseName;
|
|
47600
47813
|
}
|
|
47601
47814
|
function deriveNextjsRouteName(filePath) {
|
|
47602
|
-
const base =
|
|
47815
|
+
const base = path8.basename(filePath).replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
47603
47816
|
if (base !== "route" && base !== "page")
|
|
47604
47817
|
return null;
|
|
47605
|
-
const parts = filePath.split(
|
|
47818
|
+
const parts = filePath.split(path8.sep);
|
|
47606
47819
|
const appIdx = parts.lastIndexOf("app");
|
|
47607
47820
|
if (appIdx === -1)
|
|
47608
47821
|
return null;
|
|
@@ -47620,9 +47833,9 @@ function stripExtension(filename) {
|
|
|
47620
47833
|
return filename;
|
|
47621
47834
|
}
|
|
47622
47835
|
function fileBaseName(filePath) {
|
|
47623
|
-
const name = stripExtension(
|
|
47836
|
+
const name = stripExtension(path8.basename(filePath));
|
|
47624
47837
|
if (INDEX_NAMES.has(name)) {
|
|
47625
|
-
return
|
|
47838
|
+
return path8.basename(path8.dirname(filePath));
|
|
47626
47839
|
}
|
|
47627
47840
|
return name;
|
|
47628
47841
|
}
|
|
@@ -74385,10 +74598,10 @@ var init_secret_rules = __esm({
|
|
|
74385
74598
|
});
|
|
74386
74599
|
|
|
74387
74600
|
// packages/analyzer/dist/rules/security/secret-scanner.js
|
|
74388
|
-
import
|
|
74601
|
+
import path9 from "node:path";
|
|
74389
74602
|
function isSensitiveFile(filePath) {
|
|
74390
|
-
const basename2 =
|
|
74391
|
-
const ext2 =
|
|
74603
|
+
const basename2 = path9.basename(filePath);
|
|
74604
|
+
const ext2 = path9.extname(filePath);
|
|
74392
74605
|
if (basename2.startsWith(".env")) {
|
|
74393
74606
|
const envVariant = basename2;
|
|
74394
74607
|
if (SENSITIVE_FILE_EXTENSIONS.has(envVariant)) {
|
|
@@ -123988,211 +124201,6 @@ var init_dist2 = __esm({
|
|
|
123988
124201
|
}
|
|
123989
124202
|
});
|
|
123990
124203
|
|
|
123991
|
-
// apps/server/src/lib/atomic-write.ts
|
|
123992
|
-
import fs6 from "node:fs";
|
|
123993
|
-
import path9 from "node:path";
|
|
123994
|
-
function atomicWriteJson(targetPath, data) {
|
|
123995
|
-
fs6.mkdirSync(path9.dirname(targetPath), { recursive: true });
|
|
123996
|
-
const tmp = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
|
|
123997
|
-
fs6.writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
123998
|
-
fs6.renameSync(tmp, targetPath);
|
|
123999
|
-
}
|
|
124000
|
-
function lockPath(repoPath) {
|
|
124001
|
-
return path9.join(repoPath, ".truecourse", LOCK_FILENAME);
|
|
124002
|
-
}
|
|
124003
|
-
function acquireAnalyzeLock(repoPath) {
|
|
124004
|
-
const file = lockPath(repoPath);
|
|
124005
|
-
fs6.mkdirSync(path9.dirname(file), { recursive: true });
|
|
124006
|
-
try {
|
|
124007
|
-
const fd = fs6.openSync(file, "wx");
|
|
124008
|
-
fs6.writeSync(fd, `${process.pid}
|
|
124009
|
-
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
124010
|
-
`);
|
|
124011
|
-
fs6.closeSync(fd);
|
|
124012
|
-
} catch (err) {
|
|
124013
|
-
if (err.code === "EEXIST") {
|
|
124014
|
-
let owner = null;
|
|
124015
|
-
try {
|
|
124016
|
-
owner = parseInt(fs6.readFileSync(file, "utf-8").split("\n")[0], 10) || null;
|
|
124017
|
-
} catch {
|
|
124018
|
-
}
|
|
124019
|
-
throw new AnalyzeLockError(repoPath, owner);
|
|
124020
|
-
}
|
|
124021
|
-
throw err;
|
|
124022
|
-
}
|
|
124023
|
-
}
|
|
124024
|
-
function releaseAnalyzeLock(repoPath) {
|
|
124025
|
-
try {
|
|
124026
|
-
fs6.unlinkSync(lockPath(repoPath));
|
|
124027
|
-
} catch (err) {
|
|
124028
|
-
if (err.code !== "ENOENT") throw err;
|
|
124029
|
-
}
|
|
124030
|
-
}
|
|
124031
|
-
var LOCK_FILENAME, AnalyzeLockError;
|
|
124032
|
-
var init_atomic_write = __esm({
|
|
124033
|
-
"apps/server/src/lib/atomic-write.ts"() {
|
|
124034
|
-
"use strict";
|
|
124035
|
-
LOCK_FILENAME = ".analyze.lock";
|
|
124036
|
-
AnalyzeLockError = class extends Error {
|
|
124037
|
-
constructor(repoPath, ownerPid) {
|
|
124038
|
-
const who = ownerPid != null ? ` (held by pid ${ownerPid})` : "";
|
|
124039
|
-
super(
|
|
124040
|
-
`Another analyze is already running for ${repoPath}${who}. If you're sure no analyze is in progress, remove ${lockPath(repoPath)} and retry.`
|
|
124041
|
-
);
|
|
124042
|
-
this.name = "AnalyzeLockError";
|
|
124043
|
-
}
|
|
124044
|
-
};
|
|
124045
|
-
}
|
|
124046
|
-
});
|
|
124047
|
-
|
|
124048
|
-
// apps/server/src/lib/analysis-store.ts
|
|
124049
|
-
var analysis_store_exports = {};
|
|
124050
|
-
__export(analysis_store_exports, {
|
|
124051
|
-
analysisFilePath: () => analysisFilePath,
|
|
124052
|
-
appendHistory: () => appendHistory,
|
|
124053
|
-
buildAnalysisFilename: () => buildAnalysisFilename,
|
|
124054
|
-
clearLatestCache: () => clearLatestCache,
|
|
124055
|
-
deleteAnalysis: () => deleteAnalysis,
|
|
124056
|
-
deleteDiff: () => deleteDiff,
|
|
124057
|
-
deleteLatest: () => deleteLatest,
|
|
124058
|
-
diffPath: () => diffPath,
|
|
124059
|
-
historyPath: () => historyPath,
|
|
124060
|
-
latestPath: () => latestPath,
|
|
124061
|
-
listAnalyses: () => listAnalyses,
|
|
124062
|
-
readAnalysis: () => readAnalysis,
|
|
124063
|
-
readDiff: () => readDiff,
|
|
124064
|
-
readHistory: () => readHistory,
|
|
124065
|
-
readLatest: () => readLatest,
|
|
124066
|
-
removeFromHistory: () => removeFromHistory,
|
|
124067
|
-
writeAnalysis: () => writeAnalysis,
|
|
124068
|
-
writeDiff: () => writeDiff,
|
|
124069
|
-
writeLatest: () => writeLatest
|
|
124070
|
-
});
|
|
124071
|
-
import fs7 from "node:fs";
|
|
124072
|
-
import path10 from "node:path";
|
|
124073
|
-
function storeDir(repoPath) {
|
|
124074
|
-
return path10.join(repoPath, TRUECOURSE_DIR2);
|
|
124075
|
-
}
|
|
124076
|
-
function analysesDir(repoPath) {
|
|
124077
|
-
return path10.join(storeDir(repoPath), ANALYSES_DIR);
|
|
124078
|
-
}
|
|
124079
|
-
function analysisFilePath(repoPath, filename) {
|
|
124080
|
-
return path10.join(analysesDir(repoPath), filename);
|
|
124081
|
-
}
|
|
124082
|
-
function latestPath(repoPath) {
|
|
124083
|
-
return path10.join(storeDir(repoPath), LATEST_FILE);
|
|
124084
|
-
}
|
|
124085
|
-
function historyPath(repoPath) {
|
|
124086
|
-
return path10.join(storeDir(repoPath), HISTORY_FILE);
|
|
124087
|
-
}
|
|
124088
|
-
function diffPath(repoPath) {
|
|
124089
|
-
return path10.join(storeDir(repoPath), DIFF_FILE);
|
|
124090
|
-
}
|
|
124091
|
-
function buildAnalysisFilename(analysisId, createdAt) {
|
|
124092
|
-
const iso = createdAt.replace(/[:.]/g, "-").replace(/-\d{3}Z$/, "Z");
|
|
124093
|
-
const shortId = analysisId.replace(/-/g, "").slice(0, 8);
|
|
124094
|
-
return `${iso}_${shortId}.json`;
|
|
124095
|
-
}
|
|
124096
|
-
function readLatest(repoPath) {
|
|
124097
|
-
const file = latestPath(repoPath);
|
|
124098
|
-
let mtime;
|
|
124099
|
-
try {
|
|
124100
|
-
mtime = fs7.statSync(file).mtimeMs;
|
|
124101
|
-
} catch (err) {
|
|
124102
|
-
if (err.code === "ENOENT") {
|
|
124103
|
-
latestCache.delete(repoPath);
|
|
124104
|
-
return null;
|
|
124105
|
-
}
|
|
124106
|
-
throw err;
|
|
124107
|
-
}
|
|
124108
|
-
const cached = latestCache.get(repoPath);
|
|
124109
|
-
if (cached && cached.mtime === mtime) return cached.data;
|
|
124110
|
-
const data = JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
124111
|
-
latestCache.set(repoPath, { mtime, data });
|
|
124112
|
-
return data;
|
|
124113
|
-
}
|
|
124114
|
-
function writeLatest(repoPath, latest) {
|
|
124115
|
-
atomicWriteJson(latestPath(repoPath), latest);
|
|
124116
|
-
latestCache.delete(repoPath);
|
|
124117
|
-
}
|
|
124118
|
-
function deleteLatest(repoPath) {
|
|
124119
|
-
try {
|
|
124120
|
-
fs7.unlinkSync(latestPath(repoPath));
|
|
124121
|
-
} catch (err) {
|
|
124122
|
-
if (err.code !== "ENOENT") throw err;
|
|
124123
|
-
}
|
|
124124
|
-
latestCache.delete(repoPath);
|
|
124125
|
-
}
|
|
124126
|
-
function writeAnalysis(repoPath, snapshot) {
|
|
124127
|
-
const filename = buildAnalysisFilename(snapshot.id, snapshot.createdAt);
|
|
124128
|
-
atomicWriteJson(analysisFilePath(repoPath, filename), snapshot);
|
|
124129
|
-
return { filename, snapshot };
|
|
124130
|
-
}
|
|
124131
|
-
function readAnalysis(repoPath, filename) {
|
|
124132
|
-
const file = analysisFilePath(repoPath, filename);
|
|
124133
|
-
if (!fs7.existsSync(file)) return null;
|
|
124134
|
-
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
124135
|
-
}
|
|
124136
|
-
function listAnalyses(repoPath) {
|
|
124137
|
-
const dir = analysesDir(repoPath);
|
|
124138
|
-
if (!fs7.existsSync(dir)) return [];
|
|
124139
|
-
return fs7.readdirSync(dir).filter((name) => name.endsWith(".json")).sort();
|
|
124140
|
-
}
|
|
124141
|
-
function deleteAnalysis(repoPath, filename) {
|
|
124142
|
-
try {
|
|
124143
|
-
fs7.unlinkSync(analysisFilePath(repoPath, filename));
|
|
124144
|
-
} catch (err) {
|
|
124145
|
-
if (err.code !== "ENOENT") throw err;
|
|
124146
|
-
}
|
|
124147
|
-
}
|
|
124148
|
-
function readHistory(repoPath) {
|
|
124149
|
-
const file = historyPath(repoPath);
|
|
124150
|
-
if (!fs7.existsSync(file)) return { analyses: [] };
|
|
124151
|
-
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
124152
|
-
}
|
|
124153
|
-
function appendHistory(repoPath, entry) {
|
|
124154
|
-
const history = readHistory(repoPath);
|
|
124155
|
-
history.analyses.push(entry);
|
|
124156
|
-
atomicWriteJson(historyPath(repoPath), history);
|
|
124157
|
-
}
|
|
124158
|
-
function removeFromHistory(repoPath, analysisId) {
|
|
124159
|
-
const history = readHistory(repoPath);
|
|
124160
|
-
const next = history.analyses.filter((a) => a.id !== analysisId);
|
|
124161
|
-
if (next.length === history.analyses.length) return;
|
|
124162
|
-
atomicWriteJson(historyPath(repoPath), { analyses: next });
|
|
124163
|
-
}
|
|
124164
|
-
function readDiff(repoPath) {
|
|
124165
|
-
const file = diffPath(repoPath);
|
|
124166
|
-
if (!fs7.existsSync(file)) return null;
|
|
124167
|
-
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
124168
|
-
}
|
|
124169
|
-
function writeDiff(repoPath, diff) {
|
|
124170
|
-
atomicWriteJson(diffPath(repoPath), diff);
|
|
124171
|
-
}
|
|
124172
|
-
function deleteDiff(repoPath) {
|
|
124173
|
-
try {
|
|
124174
|
-
fs7.unlinkSync(diffPath(repoPath));
|
|
124175
|
-
} catch (err) {
|
|
124176
|
-
if (err.code !== "ENOENT") throw err;
|
|
124177
|
-
}
|
|
124178
|
-
}
|
|
124179
|
-
function clearLatestCache() {
|
|
124180
|
-
latestCache.clear();
|
|
124181
|
-
}
|
|
124182
|
-
var TRUECOURSE_DIR2, ANALYSES_DIR, LATEST_FILE, HISTORY_FILE, DIFF_FILE, latestCache;
|
|
124183
|
-
var init_analysis_store = __esm({
|
|
124184
|
-
"apps/server/src/lib/analysis-store.ts"() {
|
|
124185
|
-
"use strict";
|
|
124186
|
-
init_atomic_write();
|
|
124187
|
-
TRUECOURSE_DIR2 = ".truecourse";
|
|
124188
|
-
ANALYSES_DIR = "analyses";
|
|
124189
|
-
LATEST_FILE = "LATEST.json";
|
|
124190
|
-
HISTORY_FILE = "history.json";
|
|
124191
|
-
DIFF_FILE = "diff.json";
|
|
124192
|
-
latestCache = /* @__PURE__ */ new Map();
|
|
124193
|
-
}
|
|
124194
|
-
});
|
|
124195
|
-
|
|
124196
124204
|
// apps/server/src/services/analysis-registry.ts
|
|
124197
124205
|
function registerAnalysis(repoId, analysisId) {
|
|
124198
124206
|
const existing = activeAnalyses2.get(repoId);
|
|
@@ -139790,7 +139798,7 @@ var require_dagre = __commonJS({
|
|
|
139790
139798
|
});
|
|
139791
139799
|
|
|
139792
139800
|
// apps/server/src/index.ts
|
|
139793
|
-
var
|
|
139801
|
+
var import_express10 = __toESM(require_express2(), 1);
|
|
139794
139802
|
var import_cors = __toESM(require_lib3(), 1);
|
|
139795
139803
|
init_config();
|
|
139796
139804
|
import fs12 from "node:fs";
|
|
@@ -140294,9 +140302,12 @@ var CreateRepoSchema = external_exports.object({
|
|
|
140294
140302
|
path: external_exports.string().min(1)
|
|
140295
140303
|
});
|
|
140296
140304
|
var AnalyzeRepoSchema = external_exports.object({
|
|
140297
|
-
|
|
140298
|
-
|
|
140299
|
-
|
|
140305
|
+
/** Which mode to run — full analyze (HEAD committed state) or diff (working tree
|
|
140306
|
+
* vs LATEST). Required; no silent default. */
|
|
140307
|
+
mode: external_exports.enum(["full", "diff"]),
|
|
140308
|
+
/** Skip git ops (branch detection, commit hash read, pre-parse stash). Useful
|
|
140309
|
+
* for non-git dirs or test environments. No per-repo-config equivalent —
|
|
140310
|
+
* only way to opt out for a single run. */
|
|
140300
140311
|
skipGit: external_exports.boolean().optional().default(false)
|
|
140301
140312
|
});
|
|
140302
140313
|
var GenerateViolationsSchema = external_exports.object({
|
|
@@ -140426,6 +140437,40 @@ function emitAnalysisCanceled(repoId) {
|
|
|
140426
140437
|
const io3 = getIO();
|
|
140427
140438
|
io3.to(`repo:${repoId}`).emit("analysis:canceled", { repoId });
|
|
140428
140439
|
}
|
|
140440
|
+
function createSocketLlmEstimateHandler(repoId) {
|
|
140441
|
+
return (estimate) => new Promise((resolve7) => {
|
|
140442
|
+
const io3 = getIO();
|
|
140443
|
+
const room = `repo:${repoId}`;
|
|
140444
|
+
io3.to(room).emit("analysis:llm-estimate", {
|
|
140445
|
+
repoId,
|
|
140446
|
+
estimate: {
|
|
140447
|
+
totalEstimatedTokens: estimate.totalEstimatedTokens,
|
|
140448
|
+
tiers: estimate.tiers,
|
|
140449
|
+
uniqueFileCount: estimate.uniqueFileCount,
|
|
140450
|
+
uniqueRuleCount: estimate.uniqueRuleCount
|
|
140451
|
+
}
|
|
140452
|
+
});
|
|
140453
|
+
const timeout = setTimeout(() => {
|
|
140454
|
+
cleanup();
|
|
140455
|
+
resolve7(true);
|
|
140456
|
+
}, 6e4);
|
|
140457
|
+
function onProceed(data) {
|
|
140458
|
+
if (data.repoId !== repoId) return;
|
|
140459
|
+
cleanup();
|
|
140460
|
+
io3.to(room).emit("analysis:llm-resolved", { repoId, proceed: data.proceed });
|
|
140461
|
+
resolve7(data.proceed);
|
|
140462
|
+
}
|
|
140463
|
+
function cleanup() {
|
|
140464
|
+
clearTimeout(timeout);
|
|
140465
|
+
for (const [, socket] of io3.sockets.sockets) {
|
|
140466
|
+
socket.removeListener("analysis:llm-proceed", onProceed);
|
|
140467
|
+
}
|
|
140468
|
+
}
|
|
140469
|
+
for (const [, socket] of io3.sockets.sockets) {
|
|
140470
|
+
socket.on("analysis:llm-proceed", onProceed);
|
|
140471
|
+
}
|
|
140472
|
+
});
|
|
140473
|
+
}
|
|
140429
140474
|
|
|
140430
140475
|
// apps/server/src/socket/index.ts
|
|
140431
140476
|
var io2 = null;
|
|
@@ -145331,9 +145376,9 @@ router.get("/:id/config", async (req, res, next) => {
|
|
|
145331
145376
|
});
|
|
145332
145377
|
var repos_default = router;
|
|
145333
145378
|
|
|
145334
|
-
// apps/server/src/routes/
|
|
145379
|
+
// apps/server/src/routes/analyses.ts
|
|
145335
145380
|
var import_express2 = __toESM(require_express2(), 1);
|
|
145336
|
-
import
|
|
145381
|
+
import path14 from "node:path";
|
|
145337
145382
|
|
|
145338
145383
|
// apps/server/src/config/current-project.ts
|
|
145339
145384
|
function resolveProjectForRequest(slug) {
|
|
@@ -145347,11 +145392,14 @@ function resolveProjectForRequest(slug) {
|
|
|
145347
145392
|
}
|
|
145348
145393
|
|
|
145349
145394
|
// apps/server/src/commands/analyze-in-process.ts
|
|
145395
|
+
init_analysis_store();
|
|
145396
|
+
|
|
145397
|
+
// apps/server/src/commands/analyze-core.ts
|
|
145350
145398
|
init_logger();
|
|
145351
145399
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
145352
145400
|
|
|
145353
145401
|
// apps/server/src/services/analyzer.service.ts
|
|
145354
|
-
import
|
|
145402
|
+
import path10 from "node:path";
|
|
145355
145403
|
init_logger();
|
|
145356
145404
|
init_dist2();
|
|
145357
145405
|
function runDeterministicModuleChecks(result, enabledDeterministic) {
|
|
@@ -145419,7 +145467,7 @@ async function runAnalysis(repoPath, _branch, onProgress, options) {
|
|
|
145419
145467
|
const statusResult = await git.status();
|
|
145420
145468
|
hasChanges = !statusResult.isClean();
|
|
145421
145469
|
const gitRoot = (await git.revparse(["--show-toplevel"])).trim();
|
|
145422
|
-
isSubdirectory =
|
|
145470
|
+
isSubdirectory = path10.resolve(repoPath) !== path10.resolve(gitRoot);
|
|
145423
145471
|
}
|
|
145424
145472
|
if (hasChanges && !options?.skipStash && !isSubdirectory && git) {
|
|
145425
145473
|
onProgress({ step: "stash", percent: 2, detail: "Stashing pending changes to analyze committed state..." });
|
|
@@ -147808,7 +147856,7 @@ function processLlmCodeViolations(codeResult, validFilePaths, fileContents, allC
|
|
|
147808
147856
|
}
|
|
147809
147857
|
}
|
|
147810
147858
|
|
|
147811
|
-
// apps/server/src/commands/analyze-
|
|
147859
|
+
// apps/server/src/commands/analyze-core.ts
|
|
147812
147860
|
init_provider();
|
|
147813
147861
|
|
|
147814
147862
|
// apps/server/src/services/usage.service.ts
|
|
@@ -147828,10 +147876,10 @@ function toUsageRecords(records) {
|
|
|
147828
147876
|
}));
|
|
147829
147877
|
}
|
|
147830
147878
|
|
|
147831
|
-
// apps/server/src/commands/analyze-
|
|
147879
|
+
// apps/server/src/commands/analyze-core.ts
|
|
147832
147880
|
init_analysis_store();
|
|
147833
147881
|
init_atomic_write();
|
|
147834
|
-
async function
|
|
147882
|
+
async function analyzeCore(project, options) {
|
|
147835
147883
|
try {
|
|
147836
147884
|
acquireAnalyzeLock(project.path);
|
|
147837
147885
|
} catch (err) {
|
|
@@ -147839,22 +147887,37 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
147839
147887
|
throw err;
|
|
147840
147888
|
}
|
|
147841
147889
|
try {
|
|
147842
|
-
const
|
|
147843
|
-
const
|
|
147890
|
+
const { mode, signal } = options;
|
|
147891
|
+
const isDiff2 = mode === "diff";
|
|
147892
|
+
const skipGit = !isDiff2 && !!options.skipGit;
|
|
147844
147893
|
const projectConfig = readProjectConfig(project.path);
|
|
147894
|
+
const latestBaseline = readLatest(project.path);
|
|
147895
|
+
if (isDiff2 && !latestBaseline) {
|
|
147896
|
+
throw new Error("Run a full analysis first before checking a diff.");
|
|
147897
|
+
}
|
|
147845
147898
|
let branch = options.branch ?? null;
|
|
147846
147899
|
let commitHash = options.commitHash ?? null;
|
|
147847
|
-
if (
|
|
147900
|
+
if (isDiff2) {
|
|
147901
|
+
branch = latestBaseline.analysis.branch ?? branch;
|
|
147902
|
+
if (commitHash === null) {
|
|
147903
|
+
try {
|
|
147904
|
+
const git = await getGit(project.path);
|
|
147905
|
+
commitHash = (await git.revparse(["HEAD"])).trim() || null;
|
|
147906
|
+
} catch {
|
|
147907
|
+
commitHash = null;
|
|
147908
|
+
}
|
|
147909
|
+
}
|
|
147910
|
+
} else if (!skipGit && (branch === null || commitHash === null)) {
|
|
147848
147911
|
const git = await getGit(project.path);
|
|
147849
147912
|
if (branch === null) branch = (await git.branch()).current || null;
|
|
147850
147913
|
if (commitHash === null) commitHash = (await git.revparse(["HEAD"])).trim();
|
|
147851
147914
|
}
|
|
147852
147915
|
const analysisId = randomUUID6();
|
|
147853
147916
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
147854
|
-
const
|
|
147917
|
+
const start = Date.now();
|
|
147855
147918
|
const effectiveCategories = options.enabledCategoriesOverride?.length ? options.enabledCategoriesOverride : projectConfig.enabledCategories ?? void 0;
|
|
147856
147919
|
const effectiveLlmRules = projectConfig.enableLlmRules ?? options.enableLlmRulesOverride ?? true;
|
|
147857
|
-
options.tracker?.start("parse", "Starting analysis...");
|
|
147920
|
+
options.tracker?.start("parse", isDiff2 ? "Analyzing working tree..." : "Starting analysis...");
|
|
147858
147921
|
const result = await runAnalysis(
|
|
147859
147922
|
project.path,
|
|
147860
147923
|
branch ?? void 0,
|
|
@@ -147862,68 +147925,86 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
147862
147925
|
options.tracker?.detail("parse", progress.detail ?? "Analyzing...");
|
|
147863
147926
|
options.onProgress?.({ detail: progress.detail });
|
|
147864
147927
|
},
|
|
147865
|
-
{ signal }
|
|
147928
|
+
{ signal, skipStash: isDiff2 }
|
|
147866
147929
|
);
|
|
147867
|
-
if (signal?.aborted)
|
|
147868
|
-
|
|
147869
|
-
const previousLatest = readLatest(project.path);
|
|
147870
|
-
const previousAnalysisId = previousLatest?.analysis.id ?? null;
|
|
147871
|
-
const previousActiveViolations = previousLatest ? previousLatest.violations.filter(
|
|
147872
|
-
(v) => branch == null || previousLatest.analysis.branch == null || previousLatest.analysis.branch === branch
|
|
147873
|
-
) : [];
|
|
147874
|
-
try {
|
|
147875
|
-
graph.flows = detectFlows(result);
|
|
147876
|
-
} catch (flowError) {
|
|
147877
|
-
log.error(`[Flows] Detection failed: ${flowError instanceof Error ? flowError.message : String(flowError)}`);
|
|
147878
|
-
graph.flows = [];
|
|
147930
|
+
if (signal?.aborted) {
|
|
147931
|
+
throw new DOMException(isDiff2 ? "Diff cancelled" : "Analysis cancelled", "AbortError");
|
|
147879
147932
|
}
|
|
147880
|
-
|
|
147933
|
+
const { graph, serviceIdMap, moduleIdMap, methodIdMap, dbIdMap } = buildGraph(result);
|
|
147934
|
+
let changedFiles = [];
|
|
147881
147935
|
let changedFileSet;
|
|
147882
|
-
if (
|
|
147936
|
+
if (isDiff2) {
|
|
147883
147937
|
try {
|
|
147884
147938
|
const git = await getGit(project.path);
|
|
147885
|
-
const
|
|
147939
|
+
const statusResult = await git.status();
|
|
147940
|
+
for (const f2 of statusResult.not_added) changedFiles.push({ path: f2, status: "new" });
|
|
147941
|
+
for (const f2 of statusResult.created) changedFiles.push({ path: f2, status: "new" });
|
|
147942
|
+
for (const f2 of statusResult.modified) changedFiles.push({ path: f2, status: "modified" });
|
|
147943
|
+
for (const f2 of statusResult.staged) {
|
|
147944
|
+
if (!changedFiles.some((cf) => cf.path === f2)) {
|
|
147945
|
+
changedFiles.push({ path: f2, status: "modified" });
|
|
147946
|
+
}
|
|
147947
|
+
}
|
|
147948
|
+
for (const f2 of statusResult.deleted) changedFiles.push({ path: f2, status: "deleted" });
|
|
147949
|
+
} catch (err) {
|
|
147950
|
+
log.warn(`[Diff] git status failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
147951
|
+
}
|
|
147952
|
+
} else if (latestBaseline?.analysis.commitHash && !skipGit) {
|
|
147953
|
+
try {
|
|
147954
|
+
const git = await getGit(project.path);
|
|
147955
|
+
const diffOutput = await git.diff([latestBaseline.analysis.commitHash, "HEAD", "--name-only"]);
|
|
147886
147956
|
const files = diffOutput.trim().split("\n").filter(Boolean);
|
|
147887
147957
|
if (files.length > 0) changedFileSet = new Set(files);
|
|
147888
147958
|
} catch {
|
|
147889
147959
|
}
|
|
147890
147960
|
}
|
|
147961
|
+
if (!isDiff2) {
|
|
147962
|
+
try {
|
|
147963
|
+
graph.flows = detectFlows(result);
|
|
147964
|
+
} catch (flowError) {
|
|
147965
|
+
log.error(
|
|
147966
|
+
`[Flows] Detection failed: ${flowError instanceof Error ? flowError.message : String(flowError)}`
|
|
147967
|
+
);
|
|
147968
|
+
graph.flows = [];
|
|
147969
|
+
}
|
|
147970
|
+
touchProject(project.slug);
|
|
147971
|
+
}
|
|
147891
147972
|
options.tracker?.done(
|
|
147892
147973
|
"parse",
|
|
147893
147974
|
`${result.services.length} services, ${result.fileAnalyses?.length ?? 0} files`
|
|
147894
147975
|
);
|
|
147976
|
+
const previousActiveViolations = latestBaseline ? latestBaseline.violations.filter(
|
|
147977
|
+
(v) => branch == null || latestBaseline.analysis.branch == null || latestBaseline.analysis.branch === branch
|
|
147978
|
+
) : [];
|
|
147979
|
+
const previousAnalysisId = latestBaseline?.analysis.id ?? null;
|
|
147895
147980
|
const provider = options.provider ?? (effectiveLlmRules ? createLLMProvider() : void 0);
|
|
147896
147981
|
if (provider) {
|
|
147897
147982
|
provider.setAnalysisId(analysisId);
|
|
147898
147983
|
provider.setRepoPath(project.path);
|
|
147899
147984
|
if (signal) provider.setAbortSignal(signal);
|
|
147900
147985
|
}
|
|
147901
|
-
|
|
147902
|
-
|
|
147903
|
-
|
|
147904
|
-
|
|
147905
|
-
|
|
147906
|
-
|
|
147907
|
-
|
|
147908
|
-
|
|
147909
|
-
|
|
147910
|
-
|
|
147911
|
-
|
|
147912
|
-
|
|
147913
|
-
|
|
147914
|
-
|
|
147915
|
-
|
|
147916
|
-
|
|
147917
|
-
|
|
147918
|
-
|
|
147919
|
-
|
|
147920
|
-
|
|
147921
|
-
|
|
147922
|
-
|
|
147923
|
-
} : void 0
|
|
147924
|
-
});
|
|
147925
|
-
} finally {
|
|
147926
|
-
}
|
|
147986
|
+
const pipelineResult = await runViolationPipeline({
|
|
147987
|
+
repoPath: project.path,
|
|
147988
|
+
analysisId,
|
|
147989
|
+
now,
|
|
147990
|
+
result,
|
|
147991
|
+
serviceIdMap,
|
|
147992
|
+
moduleIdMap,
|
|
147993
|
+
methodIdMap,
|
|
147994
|
+
dbIdMap,
|
|
147995
|
+
previousActiveViolations,
|
|
147996
|
+
changedFileSet,
|
|
147997
|
+
tracker: options.tracker,
|
|
147998
|
+
enabledCategories: effectiveCategories,
|
|
147999
|
+
enableLlmRules: effectiveLlmRules,
|
|
148000
|
+
provider,
|
|
148001
|
+
signal,
|
|
148002
|
+
onLlmEstimate: options.onLlmEstimate ? async (estimate) => {
|
|
148003
|
+
const proceed = await options.onLlmEstimate(estimate);
|
|
148004
|
+
options.onLlmResolved?.(proceed);
|
|
148005
|
+
return proceed;
|
|
148006
|
+
} : void 0
|
|
148007
|
+
});
|
|
147927
148008
|
if (pipelineResult.serviceDescriptions.length > 0) {
|
|
147928
148009
|
for (const desc of pipelineResult.serviceDescriptions) {
|
|
147929
148010
|
const svc = graph.services.find((s) => s.id === desc.id);
|
|
@@ -147934,56 +148015,100 @@ async function analyzeInProcess(project, options = {}) {
|
|
|
147934
148015
|
enforceLocationInvariant(pipelineResult.added);
|
|
147935
148016
|
enforceLocationInvariant(pipelineResult.unchanged);
|
|
147936
148017
|
enforceLocationInvariant(pipelineResult.resolved);
|
|
147937
|
-
|
|
147938
|
-
|
|
147939
|
-
|
|
148018
|
+
log.info(
|
|
148019
|
+
`[${isDiff2 ? "Diff" : "Analysis"}] core complete in ${Date.now() - start}ms \u2014 ${pipelineResult.added.length} added, ${pipelineResult.unchanged.length} unchanged, ${pipelineResult.resolvedRefs.length} resolved`
|
|
148020
|
+
);
|
|
148021
|
+
return {
|
|
148022
|
+
mode,
|
|
148023
|
+
analysisId,
|
|
148024
|
+
now,
|
|
147940
148025
|
branch,
|
|
147941
148026
|
commitHash,
|
|
147942
148027
|
architecture: result.architecture,
|
|
147943
|
-
status: "completed",
|
|
147944
148028
|
metadata: result.metadata ?? null,
|
|
147945
148029
|
graph,
|
|
147946
|
-
|
|
147947
|
-
|
|
147948
|
-
|
|
147949
|
-
|
|
147950
|
-
|
|
147951
|
-
|
|
147952
|
-
};
|
|
147953
|
-
const latest = buildLatestSnapshot(snapshot, filename, pipelineResult.unchanged, pipelineResult.added);
|
|
147954
|
-
const { bySeverity, total } = summarizeActiveViolations(latest.violations);
|
|
147955
|
-
writeAnalysis(project.path, snapshot);
|
|
147956
|
-
writeLatest(project.path, latest);
|
|
147957
|
-
const historyEntry = buildHistoryEntry(snapshot, filename, pipelineResult);
|
|
147958
|
-
appendHistory(project.path, historyEntry);
|
|
147959
|
-
deleteDiff(project.path);
|
|
147960
|
-
setLastAnalyzed(project.slug, now);
|
|
147961
|
-
return {
|
|
147962
|
-
analysisId,
|
|
147963
|
-
filename,
|
|
147964
|
-
serviceCount: result.services.length,
|
|
147965
|
-
fileCount: result.fileAnalyses?.length ?? 0,
|
|
147966
|
-
architecture: result.architecture,
|
|
147967
|
-
durationMs: Date.now() - start,
|
|
147968
|
-
violationsSummary: { total, bySeverity }
|
|
148030
|
+
changedFiles,
|
|
148031
|
+
pipelineResult,
|
|
148032
|
+
usage,
|
|
148033
|
+
latestBaseline,
|
|
148034
|
+
previousAnalysisId,
|
|
148035
|
+
analysisResult: result
|
|
147969
148036
|
};
|
|
147970
148037
|
} finally {
|
|
147971
148038
|
releaseAnalyzeLock(project.path);
|
|
147972
148039
|
}
|
|
147973
148040
|
}
|
|
148041
|
+
function enforceLocationInvariant(violations) {
|
|
148042
|
+
for (const v of violations) {
|
|
148043
|
+
const hasFile = v.filePath != null;
|
|
148044
|
+
const hasRange = v.lineStart != null && v.lineEnd != null;
|
|
148045
|
+
if (hasFile === hasRange) continue;
|
|
148046
|
+
log.warn(
|
|
148047
|
+
`[Violations] ${v.ruleKey}: partial location (filePath=${v.filePath}, lineStart=${v.lineStart}, lineEnd=${v.lineEnd}) \u2014 dropping to uphold the location invariant`
|
|
148048
|
+
);
|
|
148049
|
+
v.filePath = null;
|
|
148050
|
+
v.lineStart = null;
|
|
148051
|
+
v.lineEnd = null;
|
|
148052
|
+
}
|
|
148053
|
+
}
|
|
148054
|
+
|
|
148055
|
+
// apps/server/src/commands/analyze-persist.ts
|
|
148056
|
+
init_logger();
|
|
148057
|
+
import path12 from "node:path";
|
|
148058
|
+
init_analysis_store();
|
|
148059
|
+
function persistFullAnalysis(project, core, startedAt) {
|
|
148060
|
+
const filename = buildAnalysisFilename(core.analysisId, core.now);
|
|
148061
|
+
const snapshot = {
|
|
148062
|
+
id: core.analysisId,
|
|
148063
|
+
createdAt: core.now,
|
|
148064
|
+
branch: core.branch,
|
|
148065
|
+
commitHash: core.commitHash,
|
|
148066
|
+
architecture: core.architecture,
|
|
148067
|
+
status: "completed",
|
|
148068
|
+
metadata: core.metadata,
|
|
148069
|
+
graph: core.graph,
|
|
148070
|
+
violations: {
|
|
148071
|
+
added: core.pipelineResult.added,
|
|
148072
|
+
resolved: core.pipelineResult.resolvedRefs,
|
|
148073
|
+
previousAnalysisId: core.previousAnalysisId
|
|
148074
|
+
},
|
|
148075
|
+
usage: core.usage
|
|
148076
|
+
};
|
|
148077
|
+
const latest = buildLatestSnapshot(
|
|
148078
|
+
snapshot,
|
|
148079
|
+
filename,
|
|
148080
|
+
core.pipelineResult.unchanged,
|
|
148081
|
+
core.pipelineResult.added
|
|
148082
|
+
);
|
|
148083
|
+
const { bySeverity, total } = summarizeActiveViolations(latest.violations);
|
|
148084
|
+
writeAnalysis(project.path, snapshot);
|
|
148085
|
+
writeLatest(project.path, latest);
|
|
148086
|
+
appendHistory(project.path, buildHistoryEntry(snapshot, filename, core.pipelineResult));
|
|
148087
|
+
deleteDiff(project.path);
|
|
148088
|
+
setLastAnalyzed(project.slug, core.now);
|
|
148089
|
+
return {
|
|
148090
|
+
analysisId: core.analysisId,
|
|
148091
|
+
filename,
|
|
148092
|
+
serviceCount: core.graph.services.length,
|
|
148093
|
+
fileCount: core.analysisResult.fileAnalyses?.length ?? 0,
|
|
148094
|
+
architecture: core.architecture,
|
|
148095
|
+
durationMs: Date.now() - startedAt,
|
|
148096
|
+
violationsSummary: { total, bySeverity }
|
|
148097
|
+
};
|
|
148098
|
+
}
|
|
148099
|
+
function persistDiffAnalysis(project, core) {
|
|
148100
|
+
if (!core.latestBaseline) {
|
|
148101
|
+
throw new Error("Diff persist requires a latestBaseline \u2014 analyzeCore should have enforced this.");
|
|
148102
|
+
}
|
|
148103
|
+
const diff = buildDiffSnapshot(project.path, core, core.latestBaseline);
|
|
148104
|
+
writeDiff(project.path, diff);
|
|
148105
|
+
log.info(
|
|
148106
|
+
`[Diff] Done \u2014 ${diff.summary.newCount} new, ${diff.summary.unchangedCount} unchanged, ${diff.summary.resolvedCount} resolved across ${diff.changedFiles.length} changed files`
|
|
148107
|
+
);
|
|
148108
|
+
return { diff, isStale: false };
|
|
148109
|
+
}
|
|
147974
148110
|
function buildLatestSnapshot(snapshot, filename, unchanged, added) {
|
|
147975
|
-
const
|
|
147976
|
-
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
147977
|
-
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
147978
|
-
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
147979
|
-
const databaseById = new Map(graph.databases.map((d) => [d.id, d.name]));
|
|
147980
|
-
const denormalize = (v) => ({
|
|
147981
|
-
...v,
|
|
147982
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
147983
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
147984
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
147985
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
147986
|
-
});
|
|
148111
|
+
const denormalize = makeDenormalizer(snapshot.graph);
|
|
147987
148112
|
return {
|
|
147988
148113
|
head: filename,
|
|
147989
148114
|
analysis: {
|
|
@@ -147995,7 +148120,7 @@ function buildLatestSnapshot(snapshot, filename, unchanged, added) {
|
|
|
147995
148120
|
metadata: snapshot.metadata,
|
|
147996
148121
|
status: "completed"
|
|
147997
148122
|
},
|
|
147998
|
-
graph,
|
|
148123
|
+
graph: snapshot.graph,
|
|
147999
148124
|
violations: [...added.map(denormalize), ...unchanged.map(denormalize)]
|
|
148000
148125
|
};
|
|
148001
148126
|
}
|
|
@@ -148059,27 +148184,99 @@ function buildHistoryEntry(snapshot, filename, pipeline) {
|
|
|
148059
148184
|
}
|
|
148060
148185
|
};
|
|
148061
148186
|
}
|
|
148062
|
-
function
|
|
148063
|
-
|
|
148064
|
-
|
|
148065
|
-
|
|
148066
|
-
|
|
148067
|
-
|
|
148068
|
-
|
|
148069
|
-
|
|
148070
|
-
|
|
148071
|
-
|
|
148072
|
-
|
|
148187
|
+
function buildDiffSnapshot(repoPath, core, baseline) {
|
|
148188
|
+
const { graph, changedFiles, pipelineResult } = core;
|
|
148189
|
+
const denormalize = makeDenormalizer(graph);
|
|
148190
|
+
const newViolations = pipelineResult.added.map(denormalize);
|
|
148191
|
+
const latestById = new Map(baseline.violations.map((v) => [v.id, v]));
|
|
148192
|
+
const resolvedViolations = pipelineResult.resolvedRefs.map((r) => latestById.get(r.id)).filter((v) => !!v);
|
|
148193
|
+
const changedAbs = new Set(changedFiles.map((c) => path12.resolve(repoPath, c.path)));
|
|
148194
|
+
const matchesChanged = (p) => !!p && (changedAbs.has(p) || changedAbs.has(path12.resolve(repoPath, p)));
|
|
148195
|
+
const affectedModules = graph.modules.filter((m) => matchesChanged(m.filePath));
|
|
148196
|
+
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
148197
|
+
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
148198
|
+
const layerKeyById = new Map(
|
|
148199
|
+
graph.layers.map((l) => [l.id, `${l.serviceName}::${l.layer}`])
|
|
148200
|
+
);
|
|
148201
|
+
const affectedServices = /* @__PURE__ */ new Set();
|
|
148202
|
+
const affectedLayers = /* @__PURE__ */ new Set();
|
|
148203
|
+
const affectedModuleKeys = /* @__PURE__ */ new Set();
|
|
148204
|
+
for (const mod of affectedModules) {
|
|
148205
|
+
const svcName = serviceNameById.get(mod.serviceId);
|
|
148206
|
+
if (svcName) {
|
|
148207
|
+
affectedServices.add(svcName);
|
|
148208
|
+
affectedModuleKeys.add(`${svcName}::${mod.name}`);
|
|
148209
|
+
}
|
|
148210
|
+
const layerKey = layerKeyById.get(mod.layerId);
|
|
148211
|
+
if (layerKey) affectedLayers.add(layerKey);
|
|
148212
|
+
}
|
|
148213
|
+
const moduleNameById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
148214
|
+
const affectedMethodKeys = [];
|
|
148215
|
+
for (const method of graph.methods) {
|
|
148216
|
+
if (!affectedModuleIdSet.has(method.moduleId)) continue;
|
|
148217
|
+
const modName = moduleNameById.get(method.moduleId);
|
|
148218
|
+
const mod = graph.modules.find((m) => m.id === method.moduleId);
|
|
148219
|
+
const svcName = mod ? serviceNameById.get(mod.serviceId) : void 0;
|
|
148220
|
+
if (svcName && modName) affectedMethodKeys.push(`${svcName}::${modName}::${method.name}`);
|
|
148073
148221
|
}
|
|
148222
|
+
return {
|
|
148223
|
+
id: core.analysisId,
|
|
148224
|
+
baseAnalysisId: baseline.analysis.id,
|
|
148225
|
+
createdAt: core.now,
|
|
148226
|
+
branch: core.branch,
|
|
148227
|
+
commitHash: core.commitHash,
|
|
148228
|
+
graph,
|
|
148229
|
+
changedFiles,
|
|
148230
|
+
newViolations,
|
|
148231
|
+
resolvedViolations,
|
|
148232
|
+
affectedNodeIds: {
|
|
148233
|
+
services: [...affectedServices],
|
|
148234
|
+
layers: [...affectedLayers],
|
|
148235
|
+
modules: [...affectedModuleKeys],
|
|
148236
|
+
methods: affectedMethodKeys
|
|
148237
|
+
},
|
|
148238
|
+
summary: {
|
|
148239
|
+
newCount: newViolations.length,
|
|
148240
|
+
unchangedCount: pipelineResult.unchanged.length,
|
|
148241
|
+
resolvedCount: resolvedViolations.length
|
|
148242
|
+
},
|
|
148243
|
+
usage: core.usage
|
|
148244
|
+
};
|
|
148245
|
+
}
|
|
148246
|
+
function makeDenormalizer(graph) {
|
|
148247
|
+
const serviceById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
148248
|
+
const moduleById = new Map(graph.modules.map((m) => [m.id, m.name]));
|
|
148249
|
+
const methodById = new Map(graph.methods.map((m) => [m.id, m.name]));
|
|
148250
|
+
const databaseById = new Map(graph.databases.map((d) => [d.id, d.name]));
|
|
148251
|
+
return (v) => ({
|
|
148252
|
+
...v,
|
|
148253
|
+
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
148254
|
+
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
148255
|
+
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
148256
|
+
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
148257
|
+
});
|
|
148258
|
+
}
|
|
148259
|
+
|
|
148260
|
+
// apps/server/src/commands/analyze-in-process.ts
|
|
148261
|
+
async function analyzeInProcess(project, options = {}) {
|
|
148262
|
+
const startedAt = Date.now();
|
|
148263
|
+
const core = await analyzeCore(project, { ...options, mode: "full" });
|
|
148264
|
+
return persistFullAnalysis(project, core, startedAt);
|
|
148265
|
+
}
|
|
148266
|
+
|
|
148267
|
+
// apps/server/src/commands/diff-in-process.ts
|
|
148268
|
+
async function diffInProcess(project, options = {}) {
|
|
148269
|
+
const core = await analyzeCore(project, { ...options, mode: "diff" });
|
|
148270
|
+
return persistDiffAnalysis(project, core);
|
|
148074
148271
|
}
|
|
148075
148272
|
|
|
148076
|
-
// apps/server/src/routes/
|
|
148273
|
+
// apps/server/src/routes/analyses.ts
|
|
148077
148274
|
init_analysis_registry();
|
|
148078
148275
|
init_provider();
|
|
148079
148276
|
|
|
148080
148277
|
// apps/server/src/services/telemetry.service.ts
|
|
148081
148278
|
import fs9 from "node:fs";
|
|
148082
|
-
import
|
|
148279
|
+
import path13 from "node:path";
|
|
148083
148280
|
import os3 from "node:os";
|
|
148084
148281
|
import crypto from "node:crypto";
|
|
148085
148282
|
|
|
@@ -151868,7 +152065,7 @@ var DEFAULT_CONFIG = {
|
|
|
151868
152065
|
var POSTHOG_API_KEY = "phc_ys9Ykf49KmNqAC3fhq3jugTejc4BDqyKqRS8qRoYZYew";
|
|
151869
152066
|
var TOOL_VERSION = "0.2.2";
|
|
151870
152067
|
function getTelemetryConfigPath() {
|
|
151871
|
-
return
|
|
152068
|
+
return path13.join(os3.homedir(), ".truecourse", "telemetry.json");
|
|
151872
152069
|
}
|
|
151873
152070
|
function readTelemetryConfig() {
|
|
151874
152071
|
const configPath = getTelemetryConfigPath();
|
|
@@ -151892,7 +152089,7 @@ function readTelemetryConfig() {
|
|
|
151892
152089
|
}
|
|
151893
152090
|
function writeTelemetryConfig(partial) {
|
|
151894
152091
|
const configPath = getTelemetryConfigPath();
|
|
151895
|
-
const dir =
|
|
152092
|
+
const dir = path13.dirname(configPath);
|
|
151896
152093
|
fs9.mkdirSync(dir, { recursive: true });
|
|
151897
152094
|
let current;
|
|
151898
152095
|
try {
|
|
@@ -151960,96 +152157,109 @@ function trackEvent(event, properties) {
|
|
|
151960
152157
|
}
|
|
151961
152158
|
}
|
|
151962
152159
|
|
|
151963
|
-
// apps/server/src/
|
|
152160
|
+
// apps/server/src/services/violation-query.service.ts
|
|
152161
|
+
init_analysis_store();
|
|
152162
|
+
var SEVERITY_ORDER2 = {
|
|
152163
|
+
critical: 0,
|
|
152164
|
+
high: 1,
|
|
152165
|
+
medium: 2,
|
|
152166
|
+
low: 3,
|
|
152167
|
+
info: 4
|
|
152168
|
+
};
|
|
152169
|
+
function listViolations(repoPath, options = {}) {
|
|
152170
|
+
const latest = readLatest(repoPath);
|
|
152171
|
+
if (!latest) return { violations: [], total: 0 };
|
|
152172
|
+
if (options.analysisId && latest.analysis.id !== options.analysisId) {
|
|
152173
|
+
return { violations: [], total: 0 };
|
|
152174
|
+
}
|
|
152175
|
+
const statusMode = options.status ?? "active";
|
|
152176
|
+
let filtered;
|
|
152177
|
+
if (statusMode === "resolved") {
|
|
152178
|
+
filtered = latest.violations.filter((v) => v.status === "resolved");
|
|
152179
|
+
} else if (statusMode === "all") {
|
|
152180
|
+
filtered = latest.violations;
|
|
152181
|
+
} else {
|
|
152182
|
+
const active = ["new", "unchanged"];
|
|
152183
|
+
filtered = latest.violations.filter((v) => active.includes(v.status));
|
|
152184
|
+
}
|
|
152185
|
+
if (options.filePath) {
|
|
152186
|
+
const absPath = options.filePath.startsWith("/") ? options.filePath : `${repoPath}/${options.filePath}`;
|
|
152187
|
+
filtered = filtered.filter(
|
|
152188
|
+
(v) => v.type === "code" && (v.filePath === absPath || v.filePath === options.filePath)
|
|
152189
|
+
);
|
|
152190
|
+
}
|
|
152191
|
+
filtered.sort((a, b) => {
|
|
152192
|
+
const sa = SEVERITY_ORDER2[a.severity] ?? 5;
|
|
152193
|
+
const sb = SEVERITY_ORDER2[b.severity] ?? 5;
|
|
152194
|
+
if (sa !== sb) return sa - sb;
|
|
152195
|
+
return b.createdAt.localeCompare(a.createdAt);
|
|
152196
|
+
});
|
|
152197
|
+
const total = filtered.length;
|
|
152198
|
+
const limit = options.limit ?? 0;
|
|
152199
|
+
const offset = options.offset ?? 0;
|
|
152200
|
+
const paged = limit > 0 ? filtered.slice(offset, offset + limit) : filtered;
|
|
152201
|
+
return { violations: paged, total };
|
|
152202
|
+
}
|
|
152203
|
+
function getDiffResult(repoPath) {
|
|
152204
|
+
const diff = readDiff(repoPath);
|
|
152205
|
+
if (!diff) return null;
|
|
152206
|
+
const latest = readLatest(repoPath);
|
|
152207
|
+
const isStale = latest ? latest.analysis.id !== diff.baseAnalysisId : false;
|
|
152208
|
+
return { diff, isStale };
|
|
152209
|
+
}
|
|
152210
|
+
|
|
152211
|
+
// apps/server/src/routes/analyses.ts
|
|
152212
|
+
init_analysis_store();
|
|
151964
152213
|
init_logger();
|
|
151965
152214
|
var router2 = (0, import_express2.Router)();
|
|
151966
|
-
router2.post("/:id/
|
|
152215
|
+
router2.post("/:id/analyses", async (req, res, next) => {
|
|
151967
152216
|
try {
|
|
151968
152217
|
const id = req.params.id;
|
|
151969
152218
|
const parsed = AnalyzeRepoSchema.safeParse(req.body);
|
|
151970
152219
|
if (!parsed.success) throw createAppError("Invalid request body", 400);
|
|
151971
|
-
const {
|
|
152220
|
+
const { mode, skipGit } = parsed.data;
|
|
151972
152221
|
const repo = resolveProjectForRequest(id);
|
|
152222
|
+
if (mode === "diff" && !readLatest(repo.path)) {
|
|
152223
|
+
throw createAppError("Run a full analysis first before checking a diff.", 400);
|
|
152224
|
+
}
|
|
151973
152225
|
const projectConfig = readProjectConfig(repo.path);
|
|
151974
|
-
const effectiveCategories =
|
|
151975
|
-
const effectiveLlmRules = projectConfig.enableLlmRules ??
|
|
152226
|
+
const effectiveCategories = projectConfig.enabledCategories ?? void 0;
|
|
152227
|
+
const effectiveLlmRules = projectConfig.enableLlmRules ?? true;
|
|
151976
152228
|
const abortController = registerAnalysis(id, "pending");
|
|
151977
|
-
res.status(202).json({
|
|
151978
|
-
message: "Analysis started",
|
|
151979
|
-
repoId: id
|
|
151980
|
-
});
|
|
152229
|
+
res.status(202).json({ message: `${mode === "diff" ? "Diff check" : "Analysis"} started`, repoId: id, mode });
|
|
151981
152230
|
const trackerSteps = buildAnalysisSteps(effectiveCategories, effectiveLlmRules);
|
|
151982
152231
|
const tracker = createSocketTracker(id, trackerSteps);
|
|
151983
|
-
const provider = effectiveLlmRules ? createLLMProvider() : void 0;
|
|
151984
|
-
if (provider) {
|
|
151985
|
-
provider.setRepoId(id);
|
|
151986
|
-
provider.setRepoPath(repo.path);
|
|
151987
|
-
provider.setAbortSignal(abortController.signal);
|
|
151988
|
-
}
|
|
151989
152232
|
pushLogger({
|
|
151990
|
-
filePath:
|
|
152233
|
+
filePath: path14.join(repo.path, ".truecourse/logs/analyze.log"),
|
|
151991
152234
|
tee: process.env.TRUECOURSE_DEV === "1"
|
|
151992
152235
|
});
|
|
151993
152236
|
try {
|
|
151994
|
-
|
|
151995
|
-
|
|
151996
|
-
|
|
151997
|
-
|
|
151998
|
-
|
|
151999
|
-
|
|
152000
|
-
|
|
152001
|
-
|
|
152002
|
-
|
|
152003
|
-
|
|
152004
|
-
|
|
152005
|
-
|
|
152006
|
-
|
|
152007
|
-
|
|
152008
|
-
tiers: estimate.tiers,
|
|
152009
|
-
uniqueFileCount: estimate.uniqueFileCount,
|
|
152010
|
-
uniqueRuleCount: estimate.uniqueRuleCount
|
|
152011
|
-
}
|
|
152012
|
-
});
|
|
152013
|
-
const timeout = setTimeout(() => {
|
|
152014
|
-
cleanup();
|
|
152015
|
-
resolve7(true);
|
|
152016
|
-
}, 6e4);
|
|
152017
|
-
function onProceed(data) {
|
|
152018
|
-
if (data.repoId !== id) return;
|
|
152019
|
-
cleanup();
|
|
152020
|
-
io3.to(room).emit("analysis:llm-resolved", { repoId: id, proceed: data.proceed });
|
|
152021
|
-
resolve7(data.proceed);
|
|
152022
|
-
}
|
|
152023
|
-
function cleanup() {
|
|
152024
|
-
clearTimeout(timeout);
|
|
152025
|
-
for (const [, socket] of io3.sockets.sockets) {
|
|
152026
|
-
socket.removeListener("analysis:llm-proceed", onProceed);
|
|
152027
|
-
}
|
|
152028
|
-
}
|
|
152029
|
-
for (const [, socket] of io3.sockets.sockets) {
|
|
152030
|
-
socket.on("analysis:llm-proceed", onProceed);
|
|
152031
|
-
}
|
|
152032
|
-
})
|
|
152033
|
-
});
|
|
152034
|
-
emitViolationsReady(id, outcome.analysisId);
|
|
152035
|
-
emitAnalysisComplete(id, outcome.analysisId);
|
|
152036
|
-
trackEvent("analyze", {
|
|
152037
|
-
serviceCount: outcome.serviceCount,
|
|
152038
|
-
fileCountRange: bucketFileCount(outcome.fileCount),
|
|
152039
|
-
languages: [],
|
|
152040
|
-
architecture: outcome.architecture,
|
|
152041
|
-
durationRange: bucketDuration(outcome.durationMs)
|
|
152042
|
-
});
|
|
152237
|
+
if (mode === "full") {
|
|
152238
|
+
await runFullAnalyze(id, repo, {
|
|
152239
|
+
skipGit,
|
|
152240
|
+
effectiveCategories,
|
|
152241
|
+
effectiveLlmRules,
|
|
152242
|
+
tracker,
|
|
152243
|
+
signal: abortController.signal
|
|
152244
|
+
});
|
|
152245
|
+
} else {
|
|
152246
|
+
await runDiffAnalyze(id, repo, {
|
|
152247
|
+
tracker,
|
|
152248
|
+
signal: abortController.signal
|
|
152249
|
+
});
|
|
152250
|
+
}
|
|
152043
152251
|
} catch (error) {
|
|
152044
152252
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
152045
|
-
log.info(`[Analysis] Cancelled for repo ${id}`);
|
|
152253
|
+
log.info(`[${mode === "diff" ? "Diff" : "Analysis"}] Cancelled for repo ${id}`);
|
|
152046
152254
|
emitAnalysisCanceled(id);
|
|
152047
152255
|
} else {
|
|
152048
|
-
log.error(
|
|
152256
|
+
log.error(
|
|
152257
|
+
`[${mode === "diff" ? "Diff" : "Analysis"}] Failed for repo ${id}: ${error instanceof Error ? error.message : String(error)}`
|
|
152258
|
+
);
|
|
152049
152259
|
emitAnalysisProgress(id, {
|
|
152050
152260
|
step: "error",
|
|
152051
152261
|
percent: -1,
|
|
152052
|
-
detail: error instanceof Error ? error.message : "Analysis failed
|
|
152262
|
+
detail: error instanceof Error ? error.message : `${mode === "diff" ? "Diff check" : "Analysis"} failed`
|
|
152053
152263
|
});
|
|
152054
152264
|
}
|
|
152055
152265
|
} finally {
|
|
@@ -152060,7 +152270,7 @@ router2.post("/:id/analyze", async (req, res, next) => {
|
|
|
152060
152270
|
next(error);
|
|
152061
152271
|
}
|
|
152062
152272
|
});
|
|
152063
|
-
router2.post("/:id/
|
|
152273
|
+
router2.post("/:id/analyses/cancel", async (req, res, next) => {
|
|
152064
152274
|
try {
|
|
152065
152275
|
const id = req.params.id;
|
|
152066
152276
|
const canceled = cancelAnalysis(id);
|
|
@@ -152069,159 +152279,187 @@ router2.post("/:id/analyze/cancel", async (req, res, next) => {
|
|
|
152069
152279
|
next(error);
|
|
152070
152280
|
}
|
|
152071
152281
|
});
|
|
152072
|
-
|
|
152073
|
-
|
|
152074
|
-
|
|
152075
|
-
|
|
152076
|
-
|
|
152077
|
-
|
|
152078
|
-
|
|
152079
|
-
|
|
152080
|
-
|
|
152081
|
-
|
|
152082
|
-
|
|
152083
|
-
|
|
152084
|
-
|
|
152085
|
-
|
|
152086
|
-
|
|
152087
|
-
|
|
152088
|
-
|
|
152089
|
-
|
|
152090
|
-
|
|
152091
|
-
|
|
152092
|
-
|
|
152093
|
-
|
|
152094
|
-
|
|
152095
|
-
|
|
152096
|
-
|
|
152097
|
-
|
|
152098
|
-
|
|
152099
|
-
|
|
152100
|
-
|
|
152101
|
-
|
|
152102
|
-
|
|
152103
|
-
|
|
152104
|
-
|
|
152105
|
-
|
|
152106
|
-
|
|
152107
|
-
|
|
152108
|
-
|
|
152109
|
-
|
|
152110
|
-
|
|
152111
|
-
|
|
152112
|
-
|
|
152113
|
-
|
|
152114
|
-
|
|
152115
|
-
|
|
152116
|
-
|
|
152117
|
-
|
|
152118
|
-
|
|
152119
|
-
|
|
152120
|
-
|
|
152121
|
-
|
|
152122
|
-
|
|
152123
|
-
|
|
152124
|
-
|
|
152125
|
-
|
|
152126
|
-
|
|
152127
|
-
|
|
152128
|
-
|
|
152129
|
-
|
|
152130
|
-
|
|
152131
|
-
|
|
152132
|
-
|
|
152133
|
-
|
|
152134
|
-
|
|
152135
|
-
|
|
152136
|
-
|
|
152282
|
+
router2.get("/:id/analyses", async (req, res, next) => {
|
|
152283
|
+
try {
|
|
152284
|
+
const id = req.params.id;
|
|
152285
|
+
const repo = resolveProjectForRequest(id);
|
|
152286
|
+
const history = readHistory(repo.path);
|
|
152287
|
+
const entries = history.analyses.filter((e) => !e.metadata?.isDiffAnalysis).slice(-20).reverse();
|
|
152288
|
+
res.json(
|
|
152289
|
+
entries.map((e) => ({
|
|
152290
|
+
id: e.id,
|
|
152291
|
+
status: "completed",
|
|
152292
|
+
branch: e.branch,
|
|
152293
|
+
commitHash: e.commitHash,
|
|
152294
|
+
architecture: null,
|
|
152295
|
+
createdAt: e.createdAt,
|
|
152296
|
+
serviceCount: e.counts.services,
|
|
152297
|
+
violationsBySeverity: e.counts.violations.bySeverity,
|
|
152298
|
+
durationMs: e.usage.durationMs,
|
|
152299
|
+
totalTokens: e.usage.totalTokens,
|
|
152300
|
+
totalCost: e.usage.totalCostUsd,
|
|
152301
|
+
provider: e.usage.provider
|
|
152302
|
+
}))
|
|
152303
|
+
);
|
|
152304
|
+
} catch (error) {
|
|
152305
|
+
next(error);
|
|
152306
|
+
}
|
|
152307
|
+
});
|
|
152308
|
+
router2.get("/:id/analyses/diff", async (req, res, next) => {
|
|
152309
|
+
try {
|
|
152310
|
+
const id = req.params.id;
|
|
152311
|
+
const repo = resolveProjectForRequest(id);
|
|
152312
|
+
const result = getDiffResult(repo.path);
|
|
152313
|
+
if (!result) {
|
|
152314
|
+
res.json(null);
|
|
152315
|
+
return;
|
|
152316
|
+
}
|
|
152317
|
+
const { diff, isStale } = result;
|
|
152318
|
+
res.json({
|
|
152319
|
+
resolvedViolations: diff.resolvedViolations,
|
|
152320
|
+
newViolations: diff.newViolations,
|
|
152321
|
+
affectedNodeIds: diff.affectedNodeIds,
|
|
152322
|
+
summary: diff.summary,
|
|
152323
|
+
changedFiles: diff.changedFiles,
|
|
152324
|
+
isStale,
|
|
152325
|
+
diffAnalysisId: diff.id
|
|
152326
|
+
});
|
|
152327
|
+
} catch (error) {
|
|
152328
|
+
next(error);
|
|
152329
|
+
}
|
|
152330
|
+
});
|
|
152331
|
+
router2.get("/:id/analyses/:analysisId/usage", async (req, res, next) => {
|
|
152332
|
+
try {
|
|
152333
|
+
const id = req.params.id;
|
|
152334
|
+
const analysisId = req.params.analysisId;
|
|
152335
|
+
const repo = resolveProjectForRequest(id);
|
|
152336
|
+
const latest = readLatest(repo.path);
|
|
152337
|
+
if (latest?.analysis.id === analysisId) {
|
|
152338
|
+
const snap2 = readAnalysis(repo.path, latest.head);
|
|
152339
|
+
res.json(snap2?.usage ?? []);
|
|
152340
|
+
return;
|
|
152341
|
+
}
|
|
152342
|
+
const filename = findAnalysisFilename(repo.path, analysisId);
|
|
152343
|
+
if (!filename) {
|
|
152344
|
+
res.json([]);
|
|
152345
|
+
return;
|
|
152346
|
+
}
|
|
152347
|
+
const snap = readAnalysis(repo.path, filename);
|
|
152348
|
+
res.json(snap?.usage ?? []);
|
|
152349
|
+
} catch (error) {
|
|
152350
|
+
next(error);
|
|
152351
|
+
}
|
|
152352
|
+
});
|
|
152353
|
+
router2.delete("/:id/analyses/:analysisId", async (req, res, next) => {
|
|
152354
|
+
try {
|
|
152355
|
+
const id = req.params.id;
|
|
152356
|
+
const analysisId = req.params.analysisId;
|
|
152357
|
+
const repo = resolveProjectForRequest(id);
|
|
152358
|
+
const filename = findAnalysisFilename(repo.path, analysisId);
|
|
152359
|
+
if (!filename) throw createAppError("Analysis not found", 404);
|
|
152360
|
+
deleteAnalysis(repo.path, filename);
|
|
152361
|
+
removeFromHistory(repo.path, analysisId);
|
|
152362
|
+
const latest = readLatest(repo.path);
|
|
152363
|
+
if (latest?.head === filename) {
|
|
152364
|
+
rebuildLatestFromHistory(repo.path);
|
|
152365
|
+
deleteDiff(repo.path);
|
|
152366
|
+
}
|
|
152367
|
+
res.json({ ok: true });
|
|
152368
|
+
} catch (error) {
|
|
152369
|
+
next(error);
|
|
152370
|
+
}
|
|
152371
|
+
});
|
|
152372
|
+
async function runFullAnalyze(id, repo, opts) {
|
|
152373
|
+
const provider = opts.effectiveLlmRules ? createLLMProvider() : void 0;
|
|
152374
|
+
if (provider) {
|
|
152375
|
+
provider.setRepoId(id);
|
|
152376
|
+
provider.setRepoPath(repo.path);
|
|
152377
|
+
provider.setAbortSignal(opts.signal);
|
|
152378
|
+
}
|
|
152379
|
+
const outcome = await analyzeInProcess(repo, {
|
|
152380
|
+
skipGit: opts.skipGit,
|
|
152381
|
+
enabledCategoriesOverride: opts.effectiveCategories,
|
|
152382
|
+
enableLlmRulesOverride: opts.effectiveLlmRules,
|
|
152383
|
+
tracker: opts.tracker,
|
|
152384
|
+
signal: opts.signal,
|
|
152385
|
+
provider,
|
|
152386
|
+
onLlmEstimate: createSocketLlmEstimateHandler(id)
|
|
152137
152387
|
});
|
|
152138
|
-
|
|
152139
|
-
|
|
152140
|
-
|
|
152141
|
-
|
|
152142
|
-
|
|
152143
|
-
|
|
152144
|
-
|
|
152145
|
-
|
|
152146
|
-
pipelineResult,
|
|
152147
|
-
repoPath: project.path
|
|
152388
|
+
emitViolationsReady(id, outcome.analysisId);
|
|
152389
|
+
emitAnalysisComplete(id, outcome.analysisId);
|
|
152390
|
+
trackEvent("analyze", {
|
|
152391
|
+
serviceCount: outcome.serviceCount,
|
|
152392
|
+
fileCountRange: bucketFileCount(outcome.fileCount),
|
|
152393
|
+
languages: [],
|
|
152394
|
+
architecture: outcome.architecture,
|
|
152395
|
+
durationRange: bucketDuration(outcome.durationMs)
|
|
152148
152396
|
});
|
|
152149
|
-
writeDiff(project.path, diff);
|
|
152150
|
-
log.info(
|
|
152151
|
-
`[Diff] Done \u2014 ${diff.summary.newCount} new, ${diff.summary.unchangedCount} unchanged, ${diff.summary.resolvedCount} resolved across ${diff.changedFiles.length} changed files`
|
|
152152
|
-
);
|
|
152153
|
-
return { diff, isStale: false };
|
|
152154
152397
|
}
|
|
152155
|
-
function
|
|
152156
|
-
const {
|
|
152157
|
-
|
|
152158
|
-
|
|
152159
|
-
|
|
152160
|
-
const databaseById = new Map(graph.databases.map((d) => [d.id, d.name]));
|
|
152161
|
-
const denormalize = (v) => ({
|
|
152162
|
-
...v,
|
|
152163
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
152164
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
152165
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
152166
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
152398
|
+
async function runDiffAnalyze(id, repo, opts) {
|
|
152399
|
+
const { diff } = await diffInProcess(repo, {
|
|
152400
|
+
tracker: opts.tracker,
|
|
152401
|
+
signal: opts.signal,
|
|
152402
|
+
onLlmEstimate: createSocketLlmEstimateHandler(id)
|
|
152167
152403
|
});
|
|
152168
|
-
|
|
152169
|
-
|
|
152170
|
-
|
|
152171
|
-
|
|
152172
|
-
|
|
152173
|
-
)
|
|
152174
|
-
|
|
152175
|
-
|
|
152176
|
-
const affectedModuleIdSet = new Set(affectedModules.map((m) => m.id));
|
|
152177
|
-
const serviceNameById = new Map(graph.services.map((s) => [s.id, s.name]));
|
|
152178
|
-
const layerKeyById = new Map(
|
|
152179
|
-
graph.layers.map((l) => [l.id, `${l.serviceName}::${l.layer}`])
|
|
152180
|
-
);
|
|
152181
|
-
const affectedServices = /* @__PURE__ */ new Set();
|
|
152182
|
-
const affectedLayers = /* @__PURE__ */ new Set();
|
|
152183
|
-
const affectedModuleKeys = /* @__PURE__ */ new Set();
|
|
152184
|
-
for (const mod of affectedModules) {
|
|
152185
|
-
const svcName = serviceNameById.get(mod.serviceId);
|
|
152186
|
-
if (svcName) {
|
|
152187
|
-
affectedServices.add(svcName);
|
|
152188
|
-
affectedModuleKeys.add(`${svcName}::${mod.name}`);
|
|
152189
|
-
}
|
|
152190
|
-
const layerKey = layerKeyById.get(mod.layerId);
|
|
152191
|
-
if (layerKey) affectedLayers.add(layerKey);
|
|
152404
|
+
emitViolationsReady(id, diff.id);
|
|
152405
|
+
emitAnalysisComplete(id, diff.id);
|
|
152406
|
+
}
|
|
152407
|
+
function rebuildLatestFromHistory(repoPath) {
|
|
152408
|
+
const files = listAnalyses(repoPath);
|
|
152409
|
+
if (files.length === 0) {
|
|
152410
|
+
deleteLatest(repoPath);
|
|
152411
|
+
return;
|
|
152192
152412
|
}
|
|
152193
|
-
const
|
|
152194
|
-
const
|
|
152195
|
-
|
|
152196
|
-
|
|
152197
|
-
|
|
152198
|
-
const mod = graph.modules.find((m) => m.id === method.moduleId);
|
|
152199
|
-
const svcName = mod ? serviceNameById.get(mod.serviceId) : void 0;
|
|
152200
|
-
if (svcName && modName) affectedMethodKeys.push(`${svcName}::${modName}::${method.name}`);
|
|
152413
|
+
const newest = files[files.length - 1];
|
|
152414
|
+
const snap = readAnalysis(repoPath, newest);
|
|
152415
|
+
if (!snap) {
|
|
152416
|
+
deleteLatest(repoPath);
|
|
152417
|
+
return;
|
|
152201
152418
|
}
|
|
152202
|
-
|
|
152203
|
-
|
|
152204
|
-
|
|
152205
|
-
|
|
152206
|
-
|
|
152207
|
-
|
|
152208
|
-
|
|
152209
|
-
|
|
152210
|
-
|
|
152211
|
-
|
|
152212
|
-
|
|
152213
|
-
|
|
152214
|
-
|
|
152215
|
-
|
|
152216
|
-
|
|
152419
|
+
const active = /* @__PURE__ */ new Map();
|
|
152420
|
+
for (const fname of files) {
|
|
152421
|
+
const s = readAnalysis(repoPath, fname);
|
|
152422
|
+
if (!s) continue;
|
|
152423
|
+
for (const r of s.violations.resolved) active.delete(r.id);
|
|
152424
|
+
for (const a of s.violations.added) active.set(a.id, s);
|
|
152425
|
+
}
|
|
152426
|
+
const serviceById = new Map(snap.graph.services.map((s) => [s.id, s.name]));
|
|
152427
|
+
const moduleById = new Map(snap.graph.modules.map((m) => [m.id, m.name]));
|
|
152428
|
+
const methodById = new Map(snap.graph.methods.map((m) => [m.id, m.name]));
|
|
152429
|
+
const databaseById = new Map(snap.graph.databases.map((d) => [d.id, d.name]));
|
|
152430
|
+
const latest = {
|
|
152431
|
+
head: newest,
|
|
152432
|
+
analysis: {
|
|
152433
|
+
id: snap.id,
|
|
152434
|
+
createdAt: snap.createdAt,
|
|
152435
|
+
branch: snap.branch,
|
|
152436
|
+
commitHash: snap.commitHash,
|
|
152437
|
+
architecture: snap.architecture,
|
|
152438
|
+
metadata: snap.metadata,
|
|
152439
|
+
status: "completed"
|
|
152217
152440
|
},
|
|
152218
|
-
|
|
152219
|
-
|
|
152220
|
-
unchangedCount: pipelineResult.unchanged.length,
|
|
152221
|
-
resolvedCount: resolvedViolations.length
|
|
152222
|
-
}
|
|
152441
|
+
graph: snap.graph,
|
|
152442
|
+
violations: []
|
|
152223
152443
|
};
|
|
152444
|
+
for (const snapshot of new Set(active.values())) {
|
|
152445
|
+
if (!snapshot) continue;
|
|
152446
|
+
for (const v of snapshot.violations.added) {
|
|
152447
|
+
if (!active.has(v.id)) continue;
|
|
152448
|
+
latest.violations.push({
|
|
152449
|
+
...v,
|
|
152450
|
+
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
152451
|
+
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
152452
|
+
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
152453
|
+
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
152454
|
+
});
|
|
152455
|
+
}
|
|
152456
|
+
}
|
|
152457
|
+
writeLatest(repoPath, latest);
|
|
152224
152458
|
}
|
|
152459
|
+
var analyses_default = router2;
|
|
152460
|
+
|
|
152461
|
+
// apps/server/src/routes/graph.ts
|
|
152462
|
+
var import_express3 = __toESM(require_express2(), 1);
|
|
152225
152463
|
|
|
152226
152464
|
// apps/server/src/config/ui-state.ts
|
|
152227
152465
|
import fs10 from "node:fs";
|
|
@@ -152949,249 +153187,139 @@ function markDependencyViolations(edges, detViolations) {
|
|
|
152949
153187
|
}
|
|
152950
153188
|
}
|
|
152951
153189
|
|
|
152952
|
-
// apps/server/src/routes/
|
|
153190
|
+
// apps/server/src/routes/graph.ts
|
|
152953
153191
|
init_analysis_store();
|
|
152954
|
-
|
|
152955
|
-
// apps/server/src/services/violation-query.service.ts
|
|
152956
|
-
init_analysis_store();
|
|
152957
|
-
var SEVERITY_ORDER2 = {
|
|
152958
|
-
critical: 0,
|
|
152959
|
-
high: 1,
|
|
152960
|
-
medium: 2,
|
|
152961
|
-
low: 3,
|
|
152962
|
-
info: 4
|
|
152963
|
-
};
|
|
152964
|
-
function listViolations(repoPath, options = {}) {
|
|
152965
|
-
const latest = readLatest(repoPath);
|
|
152966
|
-
if (!latest) return { violations: [], total: 0 };
|
|
152967
|
-
if (options.analysisId && latest.analysis.id !== options.analysisId) {
|
|
152968
|
-
return { violations: [], total: 0 };
|
|
152969
|
-
}
|
|
152970
|
-
const statusMode = options.status ?? "active";
|
|
152971
|
-
let filtered;
|
|
152972
|
-
if (statusMode === "resolved") {
|
|
152973
|
-
filtered = latest.violations.filter((v) => v.status === "resolved");
|
|
152974
|
-
} else if (statusMode === "all") {
|
|
152975
|
-
filtered = latest.violations;
|
|
152976
|
-
} else {
|
|
152977
|
-
const active = ["new", "unchanged"];
|
|
152978
|
-
filtered = latest.violations.filter((v) => active.includes(v.status));
|
|
152979
|
-
}
|
|
152980
|
-
if (options.filePath) {
|
|
152981
|
-
const absPath = options.filePath.startsWith("/") ? options.filePath : `${repoPath}/${options.filePath}`;
|
|
152982
|
-
filtered = filtered.filter(
|
|
152983
|
-
(v) => v.type === "code" && (v.filePath === absPath || v.filePath === options.filePath)
|
|
152984
|
-
);
|
|
152985
|
-
}
|
|
152986
|
-
filtered.sort((a, b) => {
|
|
152987
|
-
const sa = SEVERITY_ORDER2[a.severity] ?? 5;
|
|
152988
|
-
const sb = SEVERITY_ORDER2[b.severity] ?? 5;
|
|
152989
|
-
if (sa !== sb) return sa - sb;
|
|
152990
|
-
return b.createdAt.localeCompare(a.createdAt);
|
|
152991
|
-
});
|
|
152992
|
-
const total = filtered.length;
|
|
152993
|
-
const limit = options.limit ?? 0;
|
|
152994
|
-
const offset = options.offset ?? 0;
|
|
152995
|
-
const paged = limit > 0 ? filtered.slice(offset, offset + limit) : filtered;
|
|
152996
|
-
return { violations: paged, total };
|
|
152997
|
-
}
|
|
152998
|
-
function getDiffResult(repoPath) {
|
|
152999
|
-
const diff = readDiff(repoPath);
|
|
153000
|
-
if (!diff) return null;
|
|
153001
|
-
const latest = readLatest(repoPath);
|
|
153002
|
-
const isStale = latest ? latest.analysis.id !== diff.baseAnalysisId : false;
|
|
153003
|
-
return { diff, isStale };
|
|
153004
|
-
}
|
|
153005
|
-
|
|
153006
|
-
// apps/server/src/routes/analysis.ts
|
|
153007
153192
|
var router3 = (0, import_express3.Router)();
|
|
153008
|
-
router3.get(
|
|
153009
|
-
|
|
153010
|
-
|
|
153011
|
-
|
|
153012
|
-
|
|
153013
|
-
|
|
153014
|
-
|
|
153015
|
-
|
|
153016
|
-
|
|
153017
|
-
|
|
153018
|
-
|
|
153019
|
-
|
|
153020
|
-
|
|
153021
|
-
|
|
153022
|
-
|
|
153023
|
-
|
|
153024
|
-
|
|
153025
|
-
|
|
153026
|
-
|
|
153027
|
-
|
|
153028
|
-
|
|
153029
|
-
|
|
153030
|
-
|
|
153031
|
-
|
|
153032
|
-
|
|
153033
|
-
|
|
153034
|
-
|
|
153035
|
-
|
|
153036
|
-
|
|
153037
|
-
router3.get(
|
|
153038
|
-
"/:id/analyses/:analysisId/usage",
|
|
153039
|
-
async (req, res, next) => {
|
|
153040
|
-
try {
|
|
153041
|
-
const id = req.params.id;
|
|
153042
|
-
const analysisId = req.params.analysisId;
|
|
153043
|
-
const repo = resolveProjectForRequest(id);
|
|
153044
|
-
const latest = readLatest(repo.path);
|
|
153045
|
-
if (latest?.analysis.id === analysisId) {
|
|
153046
|
-
const snap2 = readAnalysis(repo.path, latest.head);
|
|
153047
|
-
res.json(snap2?.usage ?? []);
|
|
153048
|
-
return;
|
|
153049
|
-
}
|
|
153050
|
-
const filename = await findAnalysisFilename(repo.path, analysisId);
|
|
153051
|
-
if (!filename) {
|
|
153052
|
-
res.json([]);
|
|
153053
|
-
return;
|
|
153054
|
-
}
|
|
153055
|
-
const snap = readAnalysis(repo.path, filename);
|
|
153056
|
-
res.json(snap?.usage ?? []);
|
|
153057
|
-
} catch (error) {
|
|
153058
|
-
next(error);
|
|
153059
|
-
}
|
|
153060
|
-
}
|
|
153061
|
-
);
|
|
153062
|
-
router3.get(
|
|
153063
|
-
"/:id/graph",
|
|
153064
|
-
async (req, res, next) => {
|
|
153065
|
-
try {
|
|
153066
|
-
const id = req.params.id;
|
|
153067
|
-
const repo = resolveProjectForRequest(id);
|
|
153068
|
-
const analysisIdParam = req.query.analysisId;
|
|
153069
|
-
const level = req.query.level || "services";
|
|
153070
|
-
let snapshot = readLatest(repo.path);
|
|
153071
|
-
if (analysisIdParam && (!snapshot || snapshot.analysis.id !== analysisIdParam)) {
|
|
153072
|
-
const diff = readDiff(repo.path);
|
|
153073
|
-
if (diff && diff.id === analysisIdParam) {
|
|
153074
|
-
snapshot = {
|
|
153075
|
-
head: `diff-${diff.id}`,
|
|
153076
|
-
analysis: {
|
|
153077
|
-
id: diff.id,
|
|
153078
|
-
createdAt: diff.createdAt,
|
|
153079
|
-
branch: diff.branch,
|
|
153080
|
-
commitHash: diff.commitHash,
|
|
153081
|
-
architecture: snapshot?.analysis.architecture ?? "monolith",
|
|
153082
|
-
metadata: null,
|
|
153083
|
-
status: "completed"
|
|
153084
|
-
},
|
|
153085
|
-
graph: diff.graph,
|
|
153086
|
-
violations: diff.newViolations
|
|
153087
|
-
};
|
|
153088
|
-
} else {
|
|
153089
|
-
const filename = await findAnalysisFilename(repo.path, analysisIdParam);
|
|
153090
|
-
if (!filename) {
|
|
153091
|
-
res.json({ nodes: [], edges: [] });
|
|
153092
|
-
return;
|
|
153093
|
-
}
|
|
153094
|
-
const snap = readAnalysis(repo.path, filename);
|
|
153095
|
-
if (!snap) {
|
|
153096
|
-
res.json({ nodes: [], edges: [] });
|
|
153097
|
-
return;
|
|
153098
|
-
}
|
|
153099
|
-
snapshot = {
|
|
153100
|
-
head: filename,
|
|
153101
|
-
analysis: {
|
|
153102
|
-
id: snap.id,
|
|
153103
|
-
createdAt: snap.createdAt,
|
|
153104
|
-
branch: snap.branch,
|
|
153105
|
-
commitHash: snap.commitHash,
|
|
153106
|
-
architecture: snap.architecture,
|
|
153107
|
-
metadata: snap.metadata,
|
|
153108
|
-
status: "completed"
|
|
153109
|
-
},
|
|
153110
|
-
graph: snap.graph,
|
|
153111
|
-
violations: []
|
|
153112
|
-
};
|
|
153193
|
+
router3.get("/:id/graph", async (req, res, next) => {
|
|
153194
|
+
try {
|
|
153195
|
+
const id = req.params.id;
|
|
153196
|
+
const repo = resolveProjectForRequest(id);
|
|
153197
|
+
const analysisIdParam = req.query.analysisId;
|
|
153198
|
+
const level = req.query.level || "services";
|
|
153199
|
+
let snapshot = readLatest(repo.path);
|
|
153200
|
+
if (analysisIdParam && (!snapshot || snapshot.analysis.id !== analysisIdParam)) {
|
|
153201
|
+
const diff = readDiff(repo.path);
|
|
153202
|
+
if (diff && diff.id === analysisIdParam) {
|
|
153203
|
+
snapshot = {
|
|
153204
|
+
head: `diff-${diff.id}`,
|
|
153205
|
+
analysis: {
|
|
153206
|
+
id: diff.id,
|
|
153207
|
+
createdAt: diff.createdAt,
|
|
153208
|
+
branch: diff.branch,
|
|
153209
|
+
commitHash: diff.commitHash,
|
|
153210
|
+
architecture: snapshot?.analysis.architecture ?? "monolith",
|
|
153211
|
+
metadata: null,
|
|
153212
|
+
status: "completed"
|
|
153213
|
+
},
|
|
153214
|
+
graph: diff.graph,
|
|
153215
|
+
violations: diff.newViolations
|
|
153216
|
+
};
|
|
153217
|
+
} else {
|
|
153218
|
+
const filename = findAnalysisFilename(repo.path, analysisIdParam);
|
|
153219
|
+
if (!filename) {
|
|
153220
|
+
res.json({ nodes: [], edges: [] });
|
|
153221
|
+
return;
|
|
153113
153222
|
}
|
|
153223
|
+
const snap = readAnalysis(repo.path, filename);
|
|
153224
|
+
if (!snap) {
|
|
153225
|
+
res.json({ nodes: [], edges: [] });
|
|
153226
|
+
return;
|
|
153227
|
+
}
|
|
153228
|
+
snapshot = {
|
|
153229
|
+
head: filename,
|
|
153230
|
+
analysis: {
|
|
153231
|
+
id: snap.id,
|
|
153232
|
+
createdAt: snap.createdAt,
|
|
153233
|
+
branch: snap.branch,
|
|
153234
|
+
commitHash: snap.commitHash,
|
|
153235
|
+
architecture: snap.architecture,
|
|
153236
|
+
metadata: snap.metadata,
|
|
153237
|
+
status: "completed"
|
|
153238
|
+
},
|
|
153239
|
+
graph: snap.graph,
|
|
153240
|
+
violations: []
|
|
153241
|
+
};
|
|
153114
153242
|
}
|
|
153115
|
-
if (!snapshot) {
|
|
153116
|
-
res.json({ nodes: [], edges: [] });
|
|
153117
|
-
return;
|
|
153118
|
-
}
|
|
153119
|
-
const graphLevel = level.replace(/s$/, "");
|
|
153120
|
-
const serviceIdToName = new Map(snapshot.graph.services.map((s) => [s.id, s.name]));
|
|
153121
|
-
const moduleIdToName = new Map(snapshot.graph.modules.map((m) => [m.id, m.name]));
|
|
153122
|
-
const dependencyViolations = snapshot.violations.filter((v) => !!(v.relatedServiceId || v.relatedModuleId)).map((v) => ({
|
|
153123
|
-
id: v.id,
|
|
153124
|
-
ruleKey: v.ruleKey,
|
|
153125
|
-
category: v.type === "service" ? "service" : v.type === "function" ? "method" : "module",
|
|
153126
|
-
title: v.title,
|
|
153127
|
-
severity: v.severity,
|
|
153128
|
-
serviceName: v.targetServiceId ? serviceIdToName.get(v.targetServiceId) ?? "" : "",
|
|
153129
|
-
moduleName: v.targetModuleId ? moduleIdToName.get(v.targetModuleId) ?? null : null,
|
|
153130
|
-
targetModuleId: v.targetModuleId ?? null,
|
|
153131
|
-
relatedServiceId: v.relatedServiceId ?? null,
|
|
153132
|
-
relatedModuleId: v.relatedModuleId ?? null,
|
|
153133
|
-
isDependencyViolation: !!(v.relatedServiceId || v.relatedModuleId)
|
|
153134
|
-
}));
|
|
153135
|
-
const unifiedInput = {
|
|
153136
|
-
services: snapshot.graph.services.map((s) => ({
|
|
153137
|
-
...s,
|
|
153138
|
-
analysisId: snapshot.analysis.id,
|
|
153139
|
-
createdAt: new Date(snapshot.analysis.createdAt)
|
|
153140
|
-
})),
|
|
153141
|
-
serviceDeps: snapshot.graph.serviceDependencies.map((d) => ({
|
|
153142
|
-
...d,
|
|
153143
|
-
analysisId: snapshot.analysis.id
|
|
153144
|
-
})),
|
|
153145
|
-
layers: snapshot.graph.layers,
|
|
153146
|
-
modules: snapshot.graph.modules.map((m) => ({
|
|
153147
|
-
...m,
|
|
153148
|
-
analysisId: snapshot.analysis.id
|
|
153149
|
-
})),
|
|
153150
|
-
moduleDeps: snapshot.graph.moduleDeps.map((d) => ({
|
|
153151
|
-
...d,
|
|
153152
|
-
analysisId: snapshot.analysis.id
|
|
153153
|
-
})),
|
|
153154
|
-
methods: snapshot.graph.methods.map((m) => ({
|
|
153155
|
-
...m,
|
|
153156
|
-
analysisId: snapshot.analysis.id
|
|
153157
|
-
})),
|
|
153158
|
-
methodDeps: snapshot.graph.methodDeps.map((d) => ({
|
|
153159
|
-
...d,
|
|
153160
|
-
analysisId: snapshot.analysis.id
|
|
153161
|
-
})),
|
|
153162
|
-
databases: snapshot.graph.databases.map((d) => ({
|
|
153163
|
-
...d,
|
|
153164
|
-
analysisId: snapshot.analysis.id,
|
|
153165
|
-
createdAt: new Date(snapshot.analysis.createdAt)
|
|
153166
|
-
})),
|
|
153167
|
-
dbConnections: snapshot.graph.databaseConnections.map((c) => ({
|
|
153168
|
-
...c,
|
|
153169
|
-
analysisId: snapshot.analysis.id
|
|
153170
|
-
})),
|
|
153171
|
-
dependencyViolations
|
|
153172
|
-
};
|
|
153173
|
-
const graphData = buildUnifiedGraph(graphLevel, unifiedInput);
|
|
153174
|
-
const keyMap = buildStableKeyMap({
|
|
153175
|
-
services: snapshot.graph.services,
|
|
153176
|
-
layers: snapshot.graph.layers,
|
|
153177
|
-
modules: snapshot.graph.modules,
|
|
153178
|
-
methods: snapshot.graph.methods
|
|
153179
|
-
});
|
|
153180
|
-
const savedStablePositions = getScopedPositions(repo.path, snapshot.analysis.branch, level);
|
|
153181
|
-
const savedPositions = positionsToUuid(keyMap, savedStablePositions);
|
|
153182
|
-
for (const node2 of graphData.nodes) {
|
|
153183
|
-
const pos = savedPositions[node2.id];
|
|
153184
|
-
if (pos) node2.position = pos;
|
|
153185
|
-
}
|
|
153186
|
-
const savedStableCollapsed = getScopedCollapsed(repo.path, snapshot.analysis.branch, level);
|
|
153187
|
-
const collapsedIds = idsToUuid(keyMap, savedStableCollapsed);
|
|
153188
|
-
res.set("Cache-Control", "no-store");
|
|
153189
|
-
res.json({ ...graphData, collapsedIds });
|
|
153190
|
-
} catch (error) {
|
|
153191
|
-
next(error);
|
|
153192
153243
|
}
|
|
153244
|
+
if (!snapshot) {
|
|
153245
|
+
res.json({ nodes: [], edges: [] });
|
|
153246
|
+
return;
|
|
153247
|
+
}
|
|
153248
|
+
const graphLevel = level.replace(/s$/, "");
|
|
153249
|
+
const serviceIdToName = new Map(snapshot.graph.services.map((s) => [s.id, s.name]));
|
|
153250
|
+
const moduleIdToName = new Map(snapshot.graph.modules.map((m) => [m.id, m.name]));
|
|
153251
|
+
const dependencyViolations = snapshot.violations.filter((v) => !!(v.relatedServiceId || v.relatedModuleId)).map((v) => ({
|
|
153252
|
+
id: v.id,
|
|
153253
|
+
ruleKey: v.ruleKey,
|
|
153254
|
+
category: v.type === "service" ? "service" : v.type === "function" ? "method" : "module",
|
|
153255
|
+
title: v.title,
|
|
153256
|
+
severity: v.severity,
|
|
153257
|
+
serviceName: v.targetServiceId ? serviceIdToName.get(v.targetServiceId) ?? "" : "",
|
|
153258
|
+
moduleName: v.targetModuleId ? moduleIdToName.get(v.targetModuleId) ?? null : null,
|
|
153259
|
+
targetModuleId: v.targetModuleId ?? null,
|
|
153260
|
+
relatedServiceId: v.relatedServiceId ?? null,
|
|
153261
|
+
relatedModuleId: v.relatedModuleId ?? null,
|
|
153262
|
+
isDependencyViolation: !!(v.relatedServiceId || v.relatedModuleId)
|
|
153263
|
+
}));
|
|
153264
|
+
const unifiedInput = {
|
|
153265
|
+
services: snapshot.graph.services.map((s) => ({
|
|
153266
|
+
...s,
|
|
153267
|
+
analysisId: snapshot.analysis.id,
|
|
153268
|
+
createdAt: new Date(snapshot.analysis.createdAt)
|
|
153269
|
+
})),
|
|
153270
|
+
serviceDeps: snapshot.graph.serviceDependencies.map((d) => ({
|
|
153271
|
+
...d,
|
|
153272
|
+
analysisId: snapshot.analysis.id
|
|
153273
|
+
})),
|
|
153274
|
+
layers: snapshot.graph.layers,
|
|
153275
|
+
modules: snapshot.graph.modules.map((m) => ({
|
|
153276
|
+
...m,
|
|
153277
|
+
analysisId: snapshot.analysis.id
|
|
153278
|
+
})),
|
|
153279
|
+
moduleDeps: snapshot.graph.moduleDeps.map((d) => ({
|
|
153280
|
+
...d,
|
|
153281
|
+
analysisId: snapshot.analysis.id
|
|
153282
|
+
})),
|
|
153283
|
+
methods: snapshot.graph.methods.map((m) => ({
|
|
153284
|
+
...m,
|
|
153285
|
+
analysisId: snapshot.analysis.id
|
|
153286
|
+
})),
|
|
153287
|
+
methodDeps: snapshot.graph.methodDeps.map((d) => ({
|
|
153288
|
+
...d,
|
|
153289
|
+
analysisId: snapshot.analysis.id
|
|
153290
|
+
})),
|
|
153291
|
+
databases: snapshot.graph.databases.map((d) => ({
|
|
153292
|
+
...d,
|
|
153293
|
+
analysisId: snapshot.analysis.id,
|
|
153294
|
+
createdAt: new Date(snapshot.analysis.createdAt)
|
|
153295
|
+
})),
|
|
153296
|
+
dbConnections: snapshot.graph.databaseConnections.map((c) => ({
|
|
153297
|
+
...c,
|
|
153298
|
+
analysisId: snapshot.analysis.id
|
|
153299
|
+
})),
|
|
153300
|
+
dependencyViolations
|
|
153301
|
+
};
|
|
153302
|
+
const graphData = buildUnifiedGraph(graphLevel, unifiedInput);
|
|
153303
|
+
const keyMap = buildStableKeyMap({
|
|
153304
|
+
services: snapshot.graph.services,
|
|
153305
|
+
layers: snapshot.graph.layers,
|
|
153306
|
+
modules: snapshot.graph.modules,
|
|
153307
|
+
methods: snapshot.graph.methods
|
|
153308
|
+
});
|
|
153309
|
+
const savedStablePositions = getScopedPositions(repo.path, snapshot.analysis.branch, level);
|
|
153310
|
+
const savedPositions = positionsToUuid(keyMap, savedStablePositions);
|
|
153311
|
+
for (const node2 of graphData.nodes) {
|
|
153312
|
+
const pos = savedPositions[node2.id];
|
|
153313
|
+
if (pos) node2.position = pos;
|
|
153314
|
+
}
|
|
153315
|
+
const savedStableCollapsed = getScopedCollapsed(repo.path, snapshot.analysis.branch, level);
|
|
153316
|
+
const collapsedIds = idsToUuid(keyMap, savedStableCollapsed);
|
|
153317
|
+
res.set("Cache-Control", "no-store");
|
|
153318
|
+
res.json({ ...graphData, collapsedIds });
|
|
153319
|
+
} catch (error) {
|
|
153320
|
+
next(error);
|
|
153193
153321
|
}
|
|
153194
|
-
);
|
|
153322
|
+
});
|
|
153195
153323
|
function keyMapFromLatest(latest) {
|
|
153196
153324
|
return buildStableKeyMap({
|
|
153197
153325
|
services: latest.graph.services,
|
|
@@ -153200,322 +153328,178 @@ function keyMapFromLatest(latest) {
|
|
|
153200
153328
|
methods: latest.graph.methods
|
|
153201
153329
|
});
|
|
153202
153330
|
}
|
|
153203
|
-
router3.put(
|
|
153204
|
-
|
|
153205
|
-
|
|
153206
|
-
|
|
153207
|
-
|
|
153208
|
-
|
|
153209
|
-
|
|
153210
|
-
if (!positions || typeof positions !== "object") {
|
|
153211
|
-
throw createAppError("Invalid positions data", 400);
|
|
153212
|
-
}
|
|
153213
|
-
const repo = resolveProjectForRequest(id);
|
|
153214
|
-
const latest = readLatest(repo.path);
|
|
153215
|
-
if (!latest) throw createAppError("No analysis found", 404);
|
|
153216
|
-
const keyMap = keyMapFromLatest(latest);
|
|
153217
|
-
const stablePositions = positionsToStable(keyMap, positions);
|
|
153218
|
-
setPositions(repo.path, latest.analysis.branch, level, stablePositions);
|
|
153219
|
-
res.json({ ok: true });
|
|
153220
|
-
} catch (error) {
|
|
153221
|
-
next(error);
|
|
153222
|
-
}
|
|
153223
|
-
}
|
|
153224
|
-
);
|
|
153225
|
-
router3.delete(
|
|
153226
|
-
"/:id/graph/positions",
|
|
153227
|
-
async (req, res, next) => {
|
|
153228
|
-
try {
|
|
153229
|
-
const id = req.params.id;
|
|
153230
|
-
const branch = req.query.branch;
|
|
153231
|
-
const level = req.query.level || "services";
|
|
153232
|
-
const repo = resolveProjectForRequest(id);
|
|
153233
|
-
clearPositions(repo.path, branch ?? null, level);
|
|
153234
|
-
res.json({ ok: true });
|
|
153235
|
-
} catch (error) {
|
|
153236
|
-
next(error);
|
|
153237
|
-
}
|
|
153238
|
-
}
|
|
153239
|
-
);
|
|
153240
|
-
router3.put(
|
|
153241
|
-
"/:id/graph/collapsed",
|
|
153242
|
-
async (req, res, next) => {
|
|
153243
|
-
try {
|
|
153244
|
-
const id = req.params.id;
|
|
153245
|
-
const level = req.query.level || "modules";
|
|
153246
|
-
const { collapsedIds: ids } = req.body;
|
|
153247
|
-
if (!Array.isArray(ids)) throw createAppError("Invalid collapsedIds data", 400);
|
|
153248
|
-
const repo = resolveProjectForRequest(id);
|
|
153249
|
-
const latest = readLatest(repo.path);
|
|
153250
|
-
if (!latest) throw createAppError("No analysis found", 404);
|
|
153251
|
-
const keyMap = keyMapFromLatest(latest);
|
|
153252
|
-
const stableIds = idsToStable(keyMap, ids);
|
|
153253
|
-
setCollapsed(repo.path, latest.analysis.branch, level, stableIds);
|
|
153254
|
-
res.json({ ok: true });
|
|
153255
|
-
} catch (error) {
|
|
153256
|
-
next(error);
|
|
153257
|
-
}
|
|
153258
|
-
}
|
|
153259
|
-
);
|
|
153260
|
-
router3.delete(
|
|
153261
|
-
"/:id/analyses/:analysisId",
|
|
153262
|
-
async (req, res, next) => {
|
|
153263
|
-
try {
|
|
153264
|
-
const id = req.params.id;
|
|
153265
|
-
const analysisId = req.params.analysisId;
|
|
153266
|
-
const repo = resolveProjectForRequest(id);
|
|
153267
|
-
const filename = await findAnalysisFilename(repo.path, analysisId);
|
|
153268
|
-
if (!filename) throw createAppError("Analysis not found", 404);
|
|
153269
|
-
deleteAnalysis(repo.path, filename);
|
|
153270
|
-
removeFromHistory(repo.path, analysisId);
|
|
153271
|
-
const latest = readLatest(repo.path);
|
|
153272
|
-
if (latest?.head === filename) {
|
|
153273
|
-
await rebuildLatestFromHistory(repo.path);
|
|
153274
|
-
deleteDiff(repo.path);
|
|
153275
|
-
}
|
|
153276
|
-
res.json({ ok: true });
|
|
153277
|
-
} catch (error) {
|
|
153278
|
-
next(error);
|
|
153331
|
+
router3.put("/:id/graph/positions", async (req, res, next) => {
|
|
153332
|
+
try {
|
|
153333
|
+
const id = req.params.id;
|
|
153334
|
+
const level = req.query.level || "services";
|
|
153335
|
+
const { positions } = req.body;
|
|
153336
|
+
if (!positions || typeof positions !== "object") {
|
|
153337
|
+
throw createAppError("Invalid positions data", 400);
|
|
153279
153338
|
}
|
|
153339
|
+
const repo = resolveProjectForRequest(id);
|
|
153340
|
+
const latest = readLatest(repo.path);
|
|
153341
|
+
if (!latest) throw createAppError("No analysis found", 404);
|
|
153342
|
+
const keyMap = keyMapFromLatest(latest);
|
|
153343
|
+
const stablePositions = positionsToStable(keyMap, positions);
|
|
153344
|
+
setPositions(repo.path, latest.analysis.branch, level, stablePositions);
|
|
153345
|
+
res.json({ ok: true });
|
|
153346
|
+
} catch (error) {
|
|
153347
|
+
next(error);
|
|
153280
153348
|
}
|
|
153281
|
-
);
|
|
153282
|
-
async
|
|
153283
|
-
|
|
153284
|
-
|
|
153285
|
-
|
|
153286
|
-
|
|
153287
|
-
|
|
153288
|
-
|
|
153289
|
-
|
|
153290
|
-
|
|
153291
|
-
|
|
153292
|
-
return;
|
|
153293
|
-
}
|
|
153294
|
-
const sorted2 = [...files];
|
|
153295
|
-
const active = /* @__PURE__ */ new Map();
|
|
153296
|
-
for (const fname of sorted2) {
|
|
153297
|
-
const s = readAnalysis(repoPath, fname);
|
|
153298
|
-
if (!s) continue;
|
|
153299
|
-
for (const r of s.violations.resolved) active.delete(r.id);
|
|
153300
|
-
for (const a of s.violations.added) active.set(a.id, s);
|
|
153349
|
+
});
|
|
153350
|
+
router3.delete("/:id/graph/positions", async (req, res, next) => {
|
|
153351
|
+
try {
|
|
153352
|
+
const id = req.params.id;
|
|
153353
|
+
const branch = req.query.branch;
|
|
153354
|
+
const level = req.query.level || "services";
|
|
153355
|
+
const repo = resolveProjectForRequest(id);
|
|
153356
|
+
clearPositions(repo.path, branch ?? null, level);
|
|
153357
|
+
res.json({ ok: true });
|
|
153358
|
+
} catch (error) {
|
|
153359
|
+
next(error);
|
|
153301
153360
|
}
|
|
153302
|
-
|
|
153303
|
-
|
|
153304
|
-
|
|
153305
|
-
|
|
153306
|
-
|
|
153307
|
-
|
|
153308
|
-
|
|
153309
|
-
|
|
153310
|
-
|
|
153311
|
-
|
|
153312
|
-
|
|
153313
|
-
|
|
153314
|
-
|
|
153315
|
-
|
|
153316
|
-
|
|
153317
|
-
|
|
153318
|
-
violations: []
|
|
153319
|
-
// Best-effort rebuild — populated from the snapshot that owns each active id.
|
|
153320
|
-
};
|
|
153321
|
-
for (const snapshot of new Set(active.values())) {
|
|
153322
|
-
if (!snapshot) continue;
|
|
153323
|
-
for (const v of snapshot.violations.added) {
|
|
153324
|
-
if (!active.has(v.id)) continue;
|
|
153325
|
-
latest.violations.push({
|
|
153326
|
-
...v,
|
|
153327
|
-
targetServiceName: v.targetServiceId ? serviceById.get(v.targetServiceId) ?? null : null,
|
|
153328
|
-
targetModuleName: v.targetModuleId ? moduleById.get(v.targetModuleId) ?? null : null,
|
|
153329
|
-
targetMethodName: v.targetMethodId ? methodById.get(v.targetMethodId) ?? null : null,
|
|
153330
|
-
targetDatabaseName: v.targetDatabaseId ? databaseById.get(v.targetDatabaseId) ?? null : null
|
|
153331
|
-
});
|
|
153332
|
-
}
|
|
153361
|
+
});
|
|
153362
|
+
router3.put("/:id/graph/collapsed", async (req, res, next) => {
|
|
153363
|
+
try {
|
|
153364
|
+
const id = req.params.id;
|
|
153365
|
+
const level = req.query.level || "modules";
|
|
153366
|
+
const { collapsedIds: ids } = req.body;
|
|
153367
|
+
if (!Array.isArray(ids)) throw createAppError("Invalid collapsedIds data", 400);
|
|
153368
|
+
const repo = resolveProjectForRequest(id);
|
|
153369
|
+
const latest = readLatest(repo.path);
|
|
153370
|
+
if (!latest) throw createAppError("No analysis found", 404);
|
|
153371
|
+
const keyMap = keyMapFromLatest(latest);
|
|
153372
|
+
const stableIds = idsToStable(keyMap, ids);
|
|
153373
|
+
setCollapsed(repo.path, latest.analysis.branch, level, stableIds);
|
|
153374
|
+
res.json({ ok: true });
|
|
153375
|
+
} catch (error) {
|
|
153376
|
+
next(error);
|
|
153333
153377
|
}
|
|
153334
|
-
|
|
153335
|
-
|
|
153336
|
-
|
|
153337
|
-
|
|
153338
|
-
|
|
153339
|
-
|
|
153340
|
-
|
|
153341
|
-
|
|
153342
|
-
|
|
153343
|
-
|
|
153344
|
-
|
|
153345
|
-
|
|
153346
|
-
|
|
153347
|
-
|
|
153348
|
-
|
|
153349
|
-
|
|
153350
|
-
|
|
153351
|
-
|
|
153378
|
+
});
|
|
153379
|
+
var graph_default = router3;
|
|
153380
|
+
|
|
153381
|
+
// apps/server/src/routes/files.ts
|
|
153382
|
+
var import_express4 = __toESM(require_express2(), 1);
|
|
153383
|
+
import fs11 from "node:fs";
|
|
153384
|
+
import path15 from "node:path";
|
|
153385
|
+
init_analysis_store();
|
|
153386
|
+
var router4 = (0, import_express4.Router)();
|
|
153387
|
+
router4.get("/:id/files", async (req, res, next) => {
|
|
153388
|
+
try {
|
|
153389
|
+
const id = req.params.id;
|
|
153390
|
+
const ref = req.query.ref;
|
|
153391
|
+
const repo = resolveProjectForRequest(id);
|
|
153392
|
+
const git = await getGit(repo.path);
|
|
153393
|
+
const result = await git.raw(["ls-files"]);
|
|
153394
|
+
const files = result.split("\n").filter((f2) => f2.length > 0);
|
|
153395
|
+
if (ref === "working-tree") {
|
|
153396
|
+
const untrackedResult = await git.raw(["ls-files", "--others", "--exclude-standard"]);
|
|
153397
|
+
const untrackedFiles = untrackedResult.split("\n").filter((f2) => f2.length > 0);
|
|
153398
|
+
for (const f2 of untrackedFiles) {
|
|
153399
|
+
if (!files.includes(f2)) files.push(f2);
|
|
153352
153400
|
}
|
|
153353
|
-
res.json({ root: repo.path, files });
|
|
153354
|
-
} catch (error) {
|
|
153355
|
-
next(error);
|
|
153356
153401
|
}
|
|
153402
|
+
res.json({ root: repo.path, files });
|
|
153403
|
+
} catch (error) {
|
|
153404
|
+
next(error);
|
|
153357
153405
|
}
|
|
153358
|
-
);
|
|
153359
|
-
|
|
153360
|
-
|
|
153361
|
-
|
|
153362
|
-
|
|
153363
|
-
|
|
153364
|
-
|
|
153406
|
+
});
|
|
153407
|
+
router4.get("/:id/file-content", async (req, res, next) => {
|
|
153408
|
+
try {
|
|
153409
|
+
const id = req.params.id;
|
|
153410
|
+
const filePath = req.query.path;
|
|
153411
|
+
const ref = req.query.ref;
|
|
153412
|
+
if (!filePath) throw createAppError('Missing "path" query parameter', 400);
|
|
153413
|
+
const repo = resolveProjectForRequest(id);
|
|
153414
|
+
const resolved = path15.resolve(repo.path, filePath);
|
|
153415
|
+
if (!resolved.startsWith(path15.resolve(repo.path) + path15.sep) && resolved !== path15.resolve(repo.path)) {
|
|
153416
|
+
throw createAppError("Path traversal not allowed", 403);
|
|
153417
|
+
}
|
|
153418
|
+
let content;
|
|
153419
|
+
if (ref === "working-tree") {
|
|
153420
|
+
if (!fs11.existsSync(resolved)) throw createAppError("File not found", 404);
|
|
153421
|
+
const stat = fs11.statSync(resolved);
|
|
153422
|
+
if (!stat.isFile()) throw createAppError("Path is not a file", 400);
|
|
153423
|
+
content = fs11.readFileSync(resolved, "utf-8");
|
|
153424
|
+
} else {
|
|
153365
153425
|
const git = await getGit(repo.path);
|
|
153366
|
-
|
|
153367
|
-
|
|
153368
|
-
|
|
153369
|
-
for (const f2 of statusResult.created) changedFiles.push({ path: f2, status: "new" });
|
|
153370
|
-
for (const f2 of statusResult.modified) changedFiles.push({ path: f2, status: "modified" });
|
|
153371
|
-
for (const f2 of statusResult.staged) {
|
|
153372
|
-
if (!changedFiles.some((cf) => cf.path === f2)) changedFiles.push({ path: f2, status: "modified" });
|
|
153373
|
-
}
|
|
153374
|
-
for (const f2 of statusResult.deleted) changedFiles.push({ path: f2, status: "deleted" });
|
|
153375
|
-
const latest = readLatest(repo.path);
|
|
153376
|
-
const affectedServices = [];
|
|
153377
|
-
if (latest) {
|
|
153378
|
-
for (const svc of latest.graph.services) {
|
|
153379
|
-
const svcRoot = svc.rootPath;
|
|
153380
|
-
const isAffected = changedFiles.some(
|
|
153381
|
-
(cf) => cf.path.startsWith(svcRoot + "/") || cf.path === svcRoot
|
|
153382
|
-
);
|
|
153383
|
-
if (isAffected) affectedServices.push(svc.id);
|
|
153384
|
-
}
|
|
153385
|
-
}
|
|
153386
|
-
res.json({ changedFiles, affectedServices });
|
|
153387
|
-
} catch (error) {
|
|
153388
|
-
next(error);
|
|
153389
|
-
}
|
|
153390
|
-
}
|
|
153391
|
-
);
|
|
153392
|
-
router3.post(
|
|
153393
|
-
"/:id/diff-check",
|
|
153394
|
-
async (req, res, next) => {
|
|
153395
|
-
try {
|
|
153396
|
-
const id = req.params.id;
|
|
153397
|
-
const repo = resolveProjectForRequest(id);
|
|
153398
|
-
const { diff } = await diffInProcess(repo);
|
|
153399
|
-
res.json({
|
|
153400
|
-
resolvedViolations: diff.resolvedViolations,
|
|
153401
|
-
newViolations: diff.newViolations,
|
|
153402
|
-
affectedNodeIds: diff.affectedNodeIds,
|
|
153403
|
-
summary: diff.summary,
|
|
153404
|
-
changedFiles: diff.changedFiles,
|
|
153405
|
-
isStale: false,
|
|
153406
|
-
diffAnalysisId: diff.id
|
|
153407
|
-
});
|
|
153408
|
-
} catch (error) {
|
|
153409
|
-
if (error instanceof Error && error.message.includes("Run a full analysis first")) {
|
|
153410
|
-
next(createAppError(error.message, 400));
|
|
153411
|
-
return;
|
|
153412
|
-
}
|
|
153413
|
-
next(error);
|
|
153414
|
-
}
|
|
153415
|
-
}
|
|
153416
|
-
);
|
|
153417
|
-
router3.get(
|
|
153418
|
-
"/:id/diff-check",
|
|
153419
|
-
async (req, res, next) => {
|
|
153420
|
-
try {
|
|
153421
|
-
const id = req.params.id;
|
|
153422
|
-
const repo = resolveProjectForRequest(id);
|
|
153423
|
-
const result = getDiffResult(repo.path);
|
|
153424
|
-
if (!result) {
|
|
153425
|
-
res.json(null);
|
|
153426
|
-
return;
|
|
153427
|
-
}
|
|
153428
|
-
const { diff, isStale } = result;
|
|
153429
|
-
res.json({
|
|
153430
|
-
resolvedViolations: diff.resolvedViolations,
|
|
153431
|
-
newViolations: diff.newViolations,
|
|
153432
|
-
affectedNodeIds: diff.affectedNodeIds,
|
|
153433
|
-
summary: diff.summary,
|
|
153434
|
-
changedFiles: diff.changedFiles,
|
|
153435
|
-
isStale,
|
|
153436
|
-
diffAnalysisId: diff.id
|
|
153437
|
-
});
|
|
153438
|
-
} catch (error) {
|
|
153439
|
-
next(error);
|
|
153440
|
-
}
|
|
153441
|
-
}
|
|
153442
|
-
);
|
|
153443
|
-
router3.get(
|
|
153444
|
-
"/:id/file-content",
|
|
153445
|
-
async (req, res, next) => {
|
|
153446
|
-
try {
|
|
153447
|
-
const id = req.params.id;
|
|
153448
|
-
const filePath = req.query.path;
|
|
153449
|
-
const ref = req.query.ref;
|
|
153450
|
-
if (!filePath) throw createAppError('Missing "path" query parameter', 400);
|
|
153451
|
-
const repo = resolveProjectForRequest(id);
|
|
153452
|
-
const resolved = path15.resolve(repo.path, filePath);
|
|
153453
|
-
if (!resolved.startsWith(path15.resolve(repo.path) + path15.sep) && resolved !== path15.resolve(repo.path)) {
|
|
153454
|
-
throw createAppError("Path traversal not allowed", 403);
|
|
153455
|
-
}
|
|
153456
|
-
let content;
|
|
153457
|
-
if (ref === "working-tree") {
|
|
153426
|
+
try {
|
|
153427
|
+
content = await git.show([`HEAD:${filePath}`]);
|
|
153428
|
+
} catch {
|
|
153458
153429
|
if (!fs11.existsSync(resolved)) throw createAppError("File not found", 404);
|
|
153459
153430
|
const stat = fs11.statSync(resolved);
|
|
153460
153431
|
if (!stat.isFile()) throw createAppError("Path is not a file", 400);
|
|
153461
153432
|
content = fs11.readFileSync(resolved, "utf-8");
|
|
153462
|
-
}
|
|
153463
|
-
const git = await getGit(repo.path);
|
|
153464
|
-
try {
|
|
153465
|
-
content = await git.show([`HEAD:${filePath}`]);
|
|
153466
|
-
} catch {
|
|
153467
|
-
if (!fs11.existsSync(resolved)) throw createAppError("File not found", 404);
|
|
153468
|
-
const stat = fs11.statSync(resolved);
|
|
153469
|
-
if (!stat.isFile()) throw createAppError("Path is not a file", 400);
|
|
153470
|
-
content = fs11.readFileSync(resolved, "utf-8");
|
|
153471
|
-
}
|
|
153472
|
-
}
|
|
153473
|
-
const ext2 = path15.extname(resolved).slice(1).toLowerCase();
|
|
153474
|
-
const langMap = {
|
|
153475
|
-
ts: "typescript",
|
|
153476
|
-
tsx: "typescript",
|
|
153477
|
-
js: "javascript",
|
|
153478
|
-
jsx: "javascript",
|
|
153479
|
-
json: "json",
|
|
153480
|
-
md: "markdown",
|
|
153481
|
-
css: "css",
|
|
153482
|
-
html: "html",
|
|
153483
|
-
yaml: "yaml",
|
|
153484
|
-
yml: "yaml",
|
|
153485
|
-
sql: "sql",
|
|
153486
|
-
sh: "shell",
|
|
153487
|
-
py: "python",
|
|
153488
|
-
go: "go",
|
|
153489
|
-
rs: "rust",
|
|
153490
|
-
java: "java",
|
|
153491
|
-
rb: "ruby",
|
|
153492
|
-
php: "php",
|
|
153493
|
-
c: "c",
|
|
153494
|
-
cpp: "cpp",
|
|
153495
|
-
h: "c",
|
|
153496
|
-
hpp: "cpp"
|
|
153497
|
-
};
|
|
153498
|
-
const language = langMap[ext2] || "text";
|
|
153499
|
-
res.json({ content, language });
|
|
153500
|
-
} catch (error) {
|
|
153501
|
-
next(error);
|
|
153433
|
+
}
|
|
153502
153434
|
}
|
|
153435
|
+
const ext2 = path15.extname(resolved).slice(1).toLowerCase();
|
|
153436
|
+
const langMap = {
|
|
153437
|
+
ts: "typescript",
|
|
153438
|
+
tsx: "typescript",
|
|
153439
|
+
js: "javascript",
|
|
153440
|
+
jsx: "javascript",
|
|
153441
|
+
json: "json",
|
|
153442
|
+
md: "markdown",
|
|
153443
|
+
css: "css",
|
|
153444
|
+
html: "html",
|
|
153445
|
+
yaml: "yaml",
|
|
153446
|
+
yml: "yaml",
|
|
153447
|
+
sql: "sql",
|
|
153448
|
+
sh: "shell",
|
|
153449
|
+
py: "python",
|
|
153450
|
+
go: "go",
|
|
153451
|
+
rs: "rust",
|
|
153452
|
+
java: "java",
|
|
153453
|
+
rb: "ruby",
|
|
153454
|
+
php: "php",
|
|
153455
|
+
c: "c",
|
|
153456
|
+
cpp: "cpp",
|
|
153457
|
+
h: "c",
|
|
153458
|
+
hpp: "cpp"
|
|
153459
|
+
};
|
|
153460
|
+
const language = langMap[ext2] || "text";
|
|
153461
|
+
res.json({ content, language });
|
|
153462
|
+
} catch (error) {
|
|
153463
|
+
next(error);
|
|
153503
153464
|
}
|
|
153504
|
-
);
|
|
153505
|
-
async
|
|
153506
|
-
|
|
153507
|
-
const
|
|
153508
|
-
|
|
153465
|
+
});
|
|
153466
|
+
router4.get("/:id/changes", async (req, res, next) => {
|
|
153467
|
+
try {
|
|
153468
|
+
const id = req.params.id;
|
|
153469
|
+
const repo = resolveProjectForRequest(id);
|
|
153470
|
+
const git = await getGit(repo.path);
|
|
153471
|
+
const statusResult = await git.status();
|
|
153472
|
+
const changedFiles = [];
|
|
153473
|
+
for (const f2 of statusResult.not_added) changedFiles.push({ path: f2, status: "new" });
|
|
153474
|
+
for (const f2 of statusResult.created) changedFiles.push({ path: f2, status: "new" });
|
|
153475
|
+
for (const f2 of statusResult.modified) changedFiles.push({ path: f2, status: "modified" });
|
|
153476
|
+
for (const f2 of statusResult.staged) {
|
|
153477
|
+
if (!changedFiles.some((cf) => cf.path === f2)) changedFiles.push({ path: f2, status: "modified" });
|
|
153478
|
+
}
|
|
153479
|
+
for (const f2 of statusResult.deleted) changedFiles.push({ path: f2, status: "deleted" });
|
|
153480
|
+
const latest = readLatest(repo.path);
|
|
153481
|
+
const affectedServices = [];
|
|
153482
|
+
if (latest) {
|
|
153483
|
+
for (const svc of latest.graph.services) {
|
|
153484
|
+
const svcRoot = svc.rootPath;
|
|
153485
|
+
const isAffected = changedFiles.some(
|
|
153486
|
+
(cf) => cf.path.startsWith(svcRoot + "/") || cf.path === svcRoot
|
|
153487
|
+
);
|
|
153488
|
+
if (isAffected) affectedServices.push(svc.id);
|
|
153489
|
+
}
|
|
153490
|
+
}
|
|
153491
|
+
res.json({ changedFiles, affectedServices });
|
|
153492
|
+
} catch (error) {
|
|
153493
|
+
next(error);
|
|
153509
153494
|
}
|
|
153510
|
-
|
|
153511
|
-
|
|
153512
|
-
var analysis_default = router3;
|
|
153495
|
+
});
|
|
153496
|
+
var files_default = router4;
|
|
153513
153497
|
|
|
153514
153498
|
// apps/server/src/routes/violations.ts
|
|
153515
|
-
var
|
|
153499
|
+
var import_express5 = __toESM(require_express2(), 1);
|
|
153516
153500
|
init_analysis_store();
|
|
153517
|
-
var
|
|
153518
|
-
|
|
153501
|
+
var router5 = (0, import_express5.Router)();
|
|
153502
|
+
router5.get(
|
|
153519
153503
|
"/:id/violations",
|
|
153520
153504
|
async (req, res, next) => {
|
|
153521
153505
|
try {
|
|
@@ -153542,7 +153526,7 @@ router4.get(
|
|
|
153542
153526
|
}
|
|
153543
153527
|
}
|
|
153544
153528
|
);
|
|
153545
|
-
|
|
153529
|
+
router5.get(
|
|
153546
153530
|
"/:id/violations/summary",
|
|
153547
153531
|
async (req, res, next) => {
|
|
153548
153532
|
try {
|
|
@@ -153598,13 +153582,13 @@ async function findAnalysisFilename2(repoPath, analysisId) {
|
|
|
153598
153582
|
}
|
|
153599
153583
|
return null;
|
|
153600
153584
|
}
|
|
153601
|
-
var violations_default =
|
|
153585
|
+
var violations_default = router5;
|
|
153602
153586
|
|
|
153603
153587
|
// apps/server/src/routes/databases.ts
|
|
153604
|
-
var
|
|
153588
|
+
var import_express6 = __toESM(require_express2(), 1);
|
|
153605
153589
|
init_analysis_store();
|
|
153606
|
-
var
|
|
153607
|
-
|
|
153590
|
+
var router6 = (0, import_express6.Router)();
|
|
153591
|
+
router6.get(
|
|
153608
153592
|
"/:id/databases",
|
|
153609
153593
|
async (req, res, next) => {
|
|
153610
153594
|
try {
|
|
@@ -153630,7 +153614,7 @@ router5.get(
|
|
|
153630
153614
|
}
|
|
153631
153615
|
}
|
|
153632
153616
|
);
|
|
153633
|
-
|
|
153617
|
+
router6.get(
|
|
153634
153618
|
"/:id/databases/:dbId/schema",
|
|
153635
153619
|
async (req, res, next) => {
|
|
153636
153620
|
try {
|
|
@@ -153654,21 +153638,21 @@ router5.get(
|
|
|
153654
153638
|
}
|
|
153655
153639
|
}
|
|
153656
153640
|
);
|
|
153657
|
-
var databases_default =
|
|
153641
|
+
var databases_default = router6;
|
|
153658
153642
|
|
|
153659
153643
|
// apps/server/src/routes/rules.ts
|
|
153660
|
-
var
|
|
153661
|
-
var
|
|
153662
|
-
|
|
153644
|
+
var import_express7 = __toESM(require_express2(), 1);
|
|
153645
|
+
var router7 = (0, import_express7.Router)();
|
|
153646
|
+
router7.get("/", async (_req, res) => {
|
|
153663
153647
|
res.json(await getRules());
|
|
153664
153648
|
});
|
|
153665
|
-
var rules_default =
|
|
153649
|
+
var rules_default = router7;
|
|
153666
153650
|
|
|
153667
153651
|
// apps/server/src/routes/flows.ts
|
|
153668
|
-
var
|
|
153652
|
+
var import_express8 = __toESM(require_express2(), 1);
|
|
153669
153653
|
init_analysis_store();
|
|
153670
|
-
var
|
|
153671
|
-
|
|
153654
|
+
var router8 = (0, import_express8.Router)();
|
|
153655
|
+
router8.get(
|
|
153672
153656
|
"/:id/flows",
|
|
153673
153657
|
async (req, res, next) => {
|
|
153674
153658
|
try {
|
|
@@ -153685,7 +153669,7 @@ router7.get(
|
|
|
153685
153669
|
}
|
|
153686
153670
|
}
|
|
153687
153671
|
);
|
|
153688
|
-
|
|
153672
|
+
router8.get(
|
|
153689
153673
|
"/:id/flows/:flowId",
|
|
153690
153674
|
async (req, res, next) => {
|
|
153691
153675
|
try {
|
|
@@ -153700,7 +153684,7 @@ router7.get(
|
|
|
153700
153684
|
}
|
|
153701
153685
|
}
|
|
153702
153686
|
);
|
|
153703
|
-
|
|
153687
|
+
router8.post(
|
|
153704
153688
|
"/:id/flows/:flowId/enrich",
|
|
153705
153689
|
async (req, res, next) => {
|
|
153706
153690
|
try {
|
|
@@ -153717,10 +153701,10 @@ router7.post(
|
|
|
153717
153701
|
}
|
|
153718
153702
|
}
|
|
153719
153703
|
);
|
|
153720
|
-
var flows_default =
|
|
153704
|
+
var flows_default = router8;
|
|
153721
153705
|
|
|
153722
153706
|
// apps/server/src/routes/analytics.ts
|
|
153723
|
-
var
|
|
153707
|
+
var import_express9 = __toESM(require_express2(), 1);
|
|
153724
153708
|
|
|
153725
153709
|
// apps/server/src/services/analytics.service.ts
|
|
153726
153710
|
init_analysis_store();
|
|
@@ -153876,8 +153860,8 @@ function loadActiveViolations(repoPath, branch, specificAnalysisId) {
|
|
|
153876
153860
|
}
|
|
153877
153861
|
|
|
153878
153862
|
// apps/server/src/routes/analytics.ts
|
|
153879
|
-
var
|
|
153880
|
-
|
|
153863
|
+
var router9 = (0, import_express9.Router)();
|
|
153864
|
+
router9.get(
|
|
153881
153865
|
"/:id/analytics/trend",
|
|
153882
153866
|
async (req, res, next) => {
|
|
153883
153867
|
try {
|
|
@@ -153891,7 +153875,7 @@ router8.get(
|
|
|
153891
153875
|
}
|
|
153892
153876
|
}
|
|
153893
153877
|
);
|
|
153894
|
-
|
|
153878
|
+
router9.get(
|
|
153895
153879
|
"/:id/analytics/breakdown",
|
|
153896
153880
|
async (req, res, next) => {
|
|
153897
153881
|
try {
|
|
@@ -153905,7 +153889,7 @@ router8.get(
|
|
|
153905
153889
|
}
|
|
153906
153890
|
}
|
|
153907
153891
|
);
|
|
153908
|
-
|
|
153892
|
+
router9.get(
|
|
153909
153893
|
"/:id/analytics/top-offenders",
|
|
153910
153894
|
async (req, res, next) => {
|
|
153911
153895
|
try {
|
|
@@ -153919,7 +153903,7 @@ router8.get(
|
|
|
153919
153903
|
}
|
|
153920
153904
|
}
|
|
153921
153905
|
);
|
|
153922
|
-
|
|
153906
|
+
router9.get(
|
|
153923
153907
|
"/:id/analytics/resolution",
|
|
153924
153908
|
async (req, res, next) => {
|
|
153925
153909
|
try {
|
|
@@ -153932,7 +153916,7 @@ router8.get(
|
|
|
153932
153916
|
}
|
|
153933
153917
|
}
|
|
153934
153918
|
);
|
|
153935
|
-
var analytics_default =
|
|
153919
|
+
var analytics_default = router9;
|
|
153936
153920
|
|
|
153937
153921
|
// apps/server/src/services/watcher.service.ts
|
|
153938
153922
|
init_logger();
|
|
@@ -153969,14 +153953,15 @@ async function main() {
|
|
|
153969
153953
|
log.info("[Storage] Legacy Postgres data wiped. Re-analyze to repopulate.");
|
|
153970
153954
|
}
|
|
153971
153955
|
log.info(`[LLM] Provider: claude-code, model: ${config.claudeCodeModel || "default"}`);
|
|
153972
|
-
const app = (0,
|
|
153956
|
+
const app = (0, import_express10.default)();
|
|
153973
153957
|
const httpServer = createServer(app);
|
|
153974
153958
|
setupSocket(httpServer);
|
|
153975
153959
|
app.use((0, import_cors.default)());
|
|
153976
|
-
app.use(
|
|
153960
|
+
app.use(import_express10.default.json());
|
|
153977
153961
|
app.use("/api/repos", repos_default);
|
|
153978
|
-
app.use("/api/repos",
|
|
153979
|
-
app.use("/api/repos", projectResolver,
|
|
153962
|
+
app.use("/api/repos", projectResolver, analyses_default);
|
|
153963
|
+
app.use("/api/repos", projectResolver, graph_default);
|
|
153964
|
+
app.use("/api/repos", projectResolver, files_default);
|
|
153980
153965
|
app.use("/api/repos", projectResolver, violations_default);
|
|
153981
153966
|
app.use("/api/repos", projectResolver, databases_default);
|
|
153982
153967
|
app.use("/api/repos", projectResolver, flows_default);
|
|
@@ -153988,7 +153973,7 @@ async function main() {
|
|
|
153988
153973
|
app.use(errorHandler);
|
|
153989
153974
|
const staticDir = path16.join(__dirname3, "public");
|
|
153990
153975
|
if (fs12.existsSync(staticDir)) {
|
|
153991
|
-
app.use(
|
|
153976
|
+
app.use(import_express10.default.static(staticDir));
|
|
153992
153977
|
app.get("*", (_req, res) => {
|
|
153993
153978
|
res.sendFile(path16.join(staticDir, "index.html"));
|
|
153994
153979
|
});
|