@neat.is/core 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Q6DPK3RA.js → chunk-6H757ZNM.js} +2 -2
- package/dist/{chunk-UBQ4ZZT3.js → chunk-MDBE23Y3.js} +3 -3
- package/dist/{chunk-N5L3RBGP.js → chunk-P2ZEKJ35.js} +44 -2
- package/dist/chunk-P2ZEKJ35.js.map +1 -0
- package/dist/{chunk-5RIL3U5A.js → chunk-RR4LWQQB.js} +516 -229
- package/dist/chunk-RR4LWQQB.js.map +1 -0
- package/dist/cli.cjs +1351 -653
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +379 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +810 -480
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +819 -489
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-ZNC2QED2.js → otel-grpc-APVZSB6W.js} +3 -3
- package/dist/server.cjs +840 -510
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-5RIL3U5A.js.map +0 -1
- package/dist/chunk-N5L3RBGP.js.map +0 -1
- /package/dist/{chunk-Q6DPK3RA.js.map → chunk-6H757ZNM.js.map} +0 -0
- /package/dist/{chunk-UBQ4ZZT3.js.map → chunk-MDBE23Y3.js.map} +0 -0
- /package/dist/{otel-grpc-ZNC2QED2.js.map → otel-grpc-APVZSB6W.js.map} +0 -0
package/dist/server.cjs
CHANGED
|
@@ -59,8 +59,8 @@ function mountBearerAuth(app, opts) {
|
|
|
59
59
|
]);
|
|
60
60
|
const publicRead = opts.publicRead === true;
|
|
61
61
|
app.addHook("preHandler", (req, reply, done) => {
|
|
62
|
-
const
|
|
63
|
-
if (exactUnauthPaths.has(
|
|
62
|
+
const path59 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
63
|
+
if (exactUnauthPaths.has(path59) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path59)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -192,8 +192,8 @@ function reshapeGrpcRequest(req) {
|
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
194
|
function resolveProtoRoot() {
|
|
195
|
-
const here =
|
|
196
|
-
return
|
|
195
|
+
const here = import_node_path40.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
196
|
+
return import_node_path40.default.resolve(here, "..", "proto");
|
|
197
197
|
}
|
|
198
198
|
function loadTraceService() {
|
|
199
199
|
const protoRoot = resolveProtoRoot();
|
|
@@ -261,13 +261,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
261
261
|
})
|
|
262
262
|
};
|
|
263
263
|
}
|
|
264
|
-
var import_node_url,
|
|
264
|
+
var import_node_url, import_node_path40, import_node_crypto2, grpc, protoLoader;
|
|
265
265
|
var init_otel_grpc = __esm({
|
|
266
266
|
"src/otel-grpc.ts"() {
|
|
267
267
|
"use strict";
|
|
268
268
|
init_cjs_shims();
|
|
269
269
|
import_node_url = require("url");
|
|
270
|
-
|
|
270
|
+
import_node_path40 = __toESM(require("path"), 1);
|
|
271
271
|
import_node_crypto2 = require("crypto");
|
|
272
272
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
273
273
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -353,6 +353,46 @@ function tableFromSqlStatement(sql) {
|
|
|
353
353
|
const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
|
|
354
354
|
return m ? m[1] : null;
|
|
355
355
|
}
|
|
356
|
+
function columnsFromSqlStatement(sql) {
|
|
357
|
+
if (typeof sql !== "string" || sql.length === 0) return [];
|
|
358
|
+
const s = sql.replace(/\s+/g, " ").trim();
|
|
359
|
+
if (/\bjoin\b/i.test(s)) return [];
|
|
360
|
+
if ((s.match(/\bfrom\b/gi) ?? []).length > 1) return [];
|
|
361
|
+
const bare = (raw) => {
|
|
362
|
+
let t = raw.trim().replace(/"/g, "");
|
|
363
|
+
if (/[()*]/.test(t)) return null;
|
|
364
|
+
t = t.split(/\s+as\s+/i)[0].trim();
|
|
365
|
+
t = t.split(".").pop();
|
|
366
|
+
return /^[a-z_][\w$]*$/i.test(t) ? t.toLowerCase() : null;
|
|
367
|
+
};
|
|
368
|
+
const isCol = (c) => c !== null;
|
|
369
|
+
const cols = (list) => [...new Set(list.split(",").map(bare).filter(isCol))];
|
|
370
|
+
const whereCols = (w) => w ? [
|
|
371
|
+
...new Set(
|
|
372
|
+
[
|
|
373
|
+
...w.matchAll(
|
|
374
|
+
/(?:"?[\w$]+"?\.)?"?([a-z_][\w$]*)"?\s*(?:=|<|>|<=|>=|<>|!=|\bis\b|\bin\b|\blike\b)/gi
|
|
375
|
+
)
|
|
376
|
+
].map((match) => match[1].toLowerCase()).filter((c) => !/^(and|or|not|null)$/i.test(c))
|
|
377
|
+
)
|
|
378
|
+
] : [];
|
|
379
|
+
let m;
|
|
380
|
+
if (m = /\binsert\s+into\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s*\(([^)]*)\)/i.exec(s)) {
|
|
381
|
+
return cols(m[1]);
|
|
382
|
+
}
|
|
383
|
+
if (m = /\bupdate\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s+set\s+(.+?)(?:\bwhere\b(.+))?$/i.exec(s)) {
|
|
384
|
+
const set = m[1].split(",").map((p) => bare(p.split("=")[0])).filter(isCol);
|
|
385
|
+
return [.../* @__PURE__ */ new Set([...set, ...whereCols(m[2] ?? void 0)])];
|
|
386
|
+
}
|
|
387
|
+
if (m = /\bdelete\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
|
|
388
|
+
return whereCols(m[1] ?? void 0);
|
|
389
|
+
}
|
|
390
|
+
if (m = /\bselect\s+(.+?)\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
|
|
391
|
+
if (m[1].trim() === "*") return [];
|
|
392
|
+
return [.../* @__PURE__ */ new Set([...cols(m[1]), ...whereCols(m[2] ?? void 0)])];
|
|
393
|
+
}
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
356
396
|
function messagingDestinationOf(attrs) {
|
|
357
397
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
358
398
|
const v = attrs[key];
|
|
@@ -373,8 +413,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
373
413
|
const v = attrs[key];
|
|
374
414
|
if (typeof v === "string" && v.length > 0) {
|
|
375
415
|
const q = v.indexOf("?");
|
|
376
|
-
const
|
|
377
|
-
if (
|
|
416
|
+
const path59 = q === -1 ? v : v.slice(0, q);
|
|
417
|
+
if (path59.length > 0) return path59;
|
|
378
418
|
}
|
|
379
419
|
}
|
|
380
420
|
return void 0;
|
|
@@ -411,6 +451,7 @@ function parseOtlpRequest(body) {
|
|
|
411
451
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
412
452
|
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
413
453
|
dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
|
|
454
|
+
dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
|
|
414
455
|
httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
|
|
415
456
|
httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
|
|
416
457
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
@@ -432,10 +473,10 @@ function parseOtlpRequest(body) {
|
|
|
432
473
|
return out;
|
|
433
474
|
}
|
|
434
475
|
function loadProtoRoot() {
|
|
435
|
-
const here =
|
|
436
|
-
const protoRoot =
|
|
476
|
+
const here = import_node_path41.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
477
|
+
const protoRoot = import_node_path41.default.resolve(here, "..", "proto");
|
|
437
478
|
const root = new import_protobufjs.default.Root();
|
|
438
|
-
root.resolvePath = (_origin, target) =>
|
|
479
|
+
root.resolvePath = (_origin, target) => import_node_path41.default.resolve(protoRoot, target);
|
|
439
480
|
root.loadSync(
|
|
440
481
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
441
482
|
{ keepCase: true }
|
|
@@ -650,12 +691,12 @@ async function buildOtelReceiver(opts) {
|
|
|
650
691
|
};
|
|
651
692
|
return decorated;
|
|
652
693
|
}
|
|
653
|
-
var
|
|
694
|
+
var import_node_path41, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
654
695
|
var init_otel = __esm({
|
|
655
696
|
"src/otel.ts"() {
|
|
656
697
|
"use strict";
|
|
657
698
|
init_cjs_shims();
|
|
658
|
-
|
|
699
|
+
import_node_path41 = __toESM(require("path"), 1);
|
|
659
700
|
import_node_url2 = require("url");
|
|
660
701
|
import_fastify = __toESM(require("fastify"), 1);
|
|
661
702
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -671,7 +712,7 @@ var init_otel = __esm({
|
|
|
671
712
|
|
|
672
713
|
// src/server.ts
|
|
673
714
|
init_cjs_shims();
|
|
674
|
-
var
|
|
715
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
675
716
|
|
|
676
717
|
// src/graph.ts
|
|
677
718
|
init_cjs_shims();
|
|
@@ -695,7 +736,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
695
736
|
init_cjs_shims();
|
|
696
737
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
697
738
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
698
|
-
var
|
|
739
|
+
var import_types58 = require("@neat.is/types");
|
|
699
740
|
|
|
700
741
|
// src/extend/index.ts
|
|
701
742
|
init_cjs_shims();
|
|
@@ -1042,7 +1083,43 @@ async function rollbackExtension(ctx, args) {
|
|
|
1042
1083
|
|
|
1043
1084
|
// src/divergences.ts
|
|
1044
1085
|
init_cjs_shims();
|
|
1045
|
-
var
|
|
1086
|
+
var import_types8 = require("@neat.is/types");
|
|
1087
|
+
|
|
1088
|
+
// src/columns.ts
|
|
1089
|
+
init_cjs_shims();
|
|
1090
|
+
var import_types = require("@neat.is/types");
|
|
1091
|
+
var OBSERVED_COLUMN_CONFIDENCE = 0.9;
|
|
1092
|
+
function normalizeProvenances(provenances) {
|
|
1093
|
+
return [...new Set(provenances)].sort();
|
|
1094
|
+
}
|
|
1095
|
+
function foldColumns(existing, names, provenance, confidence) {
|
|
1096
|
+
const out = (existing ?? []).map((c) => ({
|
|
1097
|
+
...c,
|
|
1098
|
+
provenances: [...c.provenances]
|
|
1099
|
+
}));
|
|
1100
|
+
const byName = new Map(out.map((c) => [c.name, c]));
|
|
1101
|
+
for (const raw of names) {
|
|
1102
|
+
const name = raw.toLowerCase();
|
|
1103
|
+
const prior = byName.get(name);
|
|
1104
|
+
if (!prior) {
|
|
1105
|
+
const col = { name, provenances: [provenance], confidence };
|
|
1106
|
+
byName.set(name, col);
|
|
1107
|
+
out.push(col);
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
if (!prior.provenances.includes(provenance)) {
|
|
1111
|
+
prior.provenances = normalizeProvenances([...prior.provenances, provenance]);
|
|
1112
|
+
}
|
|
1113
|
+
if (confidence > prior.confidence) prior.confidence = confidence;
|
|
1114
|
+
}
|
|
1115
|
+
return out;
|
|
1116
|
+
}
|
|
1117
|
+
function columnIsDeclared(col) {
|
|
1118
|
+
return col.provenances.includes(import_types.Provenance.EXTRACTED);
|
|
1119
|
+
}
|
|
1120
|
+
function columnIsObserved(col) {
|
|
1121
|
+
return col.provenances.includes(import_types.Provenance.OBSERVED);
|
|
1122
|
+
}
|
|
1046
1123
|
|
|
1047
1124
|
// src/compat.ts
|
|
1048
1125
|
init_cjs_shims();
|
|
@@ -1370,7 +1447,7 @@ var sourceMapJs = __toESM(require("source-map-js"), 1);
|
|
|
1370
1447
|
init_cjs_shims();
|
|
1371
1448
|
var import_node_fs4 = require("fs");
|
|
1372
1449
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
1373
|
-
var
|
|
1450
|
+
var import_types2 = require("@neat.is/types");
|
|
1374
1451
|
|
|
1375
1452
|
// src/events.ts
|
|
1376
1453
|
init_cjs_shims();
|
|
@@ -1422,7 +1499,7 @@ var evaluateStructural = ({
|
|
|
1422
1499
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1423
1500
|
if (e.type !== rule.edgeType) continue;
|
|
1424
1501
|
const target = graph.getNodeAttributes(e.target);
|
|
1425
|
-
if (target.type ===
|
|
1502
|
+
if (target.type === import_types2.NodeType.FrontierNode) continue;
|
|
1426
1503
|
if (target.type === rule.toNodeType) {
|
|
1427
1504
|
satisfied = true;
|
|
1428
1505
|
break;
|
|
@@ -1534,16 +1611,16 @@ var evaluateCompatibility = ({
|
|
|
1534
1611
|
const wantsKind = (kind) => rule.kind === void 0 || rule.kind === kind;
|
|
1535
1612
|
graph.forEachNode((svcId, attrs) => {
|
|
1536
1613
|
const a = attrs;
|
|
1537
|
-
if (a.type !==
|
|
1614
|
+
if (a.type !== import_types2.NodeType.ServiceNode) return;
|
|
1538
1615
|
const svc = a;
|
|
1539
1616
|
const deps = svc.dependencies ?? {};
|
|
1540
1617
|
if (wantsKind("driver-engine")) {
|
|
1541
1618
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
1542
1619
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1543
|
-
if (e.type !==
|
|
1620
|
+
if (e.type !== import_types2.EdgeType.CONNECTS_TO) continue;
|
|
1544
1621
|
const dbAttrs = graph.getNodeAttributes(e.target);
|
|
1545
|
-
if (dbAttrs.type ===
|
|
1546
|
-
if (dbAttrs.type !==
|
|
1622
|
+
if (dbAttrs.type === import_types2.NodeType.FrontierNode) continue;
|
|
1623
|
+
if (dbAttrs.type !== import_types2.NodeType.DatabaseNode) continue;
|
|
1547
1624
|
const db = dbAttrs;
|
|
1548
1625
|
for (const pair of compatPairs()) {
|
|
1549
1626
|
if (pair.engine !== db.engine) continue;
|
|
@@ -1743,14 +1820,14 @@ function matchPolicyToNode(graph, policy, nodeId, node) {
|
|
|
1743
1820
|
}
|
|
1744
1821
|
case "compatibility": {
|
|
1745
1822
|
const kindLabel = rule.kind ?? "all compat shapes";
|
|
1746
|
-
if (node.type ===
|
|
1823
|
+
if (node.type === import_types2.NodeType.ServiceNode) {
|
|
1747
1824
|
return {
|
|
1748
1825
|
match: "subject",
|
|
1749
1826
|
reason: `this service's dependencies are compatibility-checked (${kindLabel})`
|
|
1750
1827
|
};
|
|
1751
1828
|
}
|
|
1752
1829
|
const reachesDriverEngine = rule.kind === void 0 || rule.kind === "driver-engine";
|
|
1753
|
-
if (reachesDriverEngine && node.type ===
|
|
1830
|
+
if (reachesDriverEngine && node.type === import_types2.NodeType.DatabaseNode && nodeTouchesEdgeType(graph, nodeId, import_types2.EdgeType.CONNECTS_TO)) {
|
|
1754
1831
|
return {
|
|
1755
1832
|
match: "region",
|
|
1756
1833
|
reason: "services connecting to this database have their driver/engine compatibility checked against it"
|
|
@@ -1785,7 +1862,7 @@ async function loadPolicyFile(policyPath) {
|
|
|
1785
1862
|
throw err;
|
|
1786
1863
|
}
|
|
1787
1864
|
const json = JSON.parse(raw);
|
|
1788
|
-
const file =
|
|
1865
|
+
const file = import_types2.PolicyFileSchema.parse(json);
|
|
1789
1866
|
return file.policies;
|
|
1790
1867
|
}
|
|
1791
1868
|
var PolicyViolationsLog = class {
|
|
@@ -1826,7 +1903,7 @@ var PolicyViolationsLog = class {
|
|
|
1826
1903
|
};
|
|
1827
1904
|
|
|
1828
1905
|
// src/ingest.ts
|
|
1829
|
-
var
|
|
1906
|
+
var import_types6 = require("@neat.is/types");
|
|
1830
1907
|
|
|
1831
1908
|
// src/extract/routes.ts
|
|
1832
1909
|
init_cjs_shims();
|
|
@@ -1835,14 +1912,14 @@ var import_tree_sitter = __toESM(require("tree-sitter"), 1);
|
|
|
1835
1912
|
var import_tree_sitter_javascript = __toESM(require("tree-sitter-javascript"), 1);
|
|
1836
1913
|
var import_tree_sitter_python = __toESM(require("tree-sitter-python"), 1);
|
|
1837
1914
|
var import_tree_sitter_go = __toESM(require("tree-sitter-go"), 1);
|
|
1838
|
-
var
|
|
1915
|
+
var import_types5 = require("@neat.is/types");
|
|
1839
1916
|
|
|
1840
1917
|
// src/extract/shared.ts
|
|
1841
1918
|
init_cjs_shims();
|
|
1842
1919
|
var import_node_fs5 = require("fs");
|
|
1843
1920
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
1844
1921
|
var import_yaml = require("yaml");
|
|
1845
|
-
var
|
|
1922
|
+
var import_types3 = require("@neat.is/types");
|
|
1846
1923
|
var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go"]);
|
|
1847
1924
|
var CONFIG_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
|
|
1848
1925
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -2097,7 +2174,7 @@ async function writeRejectedExtracted(drops, rejectedPath) {
|
|
|
2097
2174
|
init_cjs_shims();
|
|
2098
2175
|
var import_node_fs7 = require("fs");
|
|
2099
2176
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
2100
|
-
var
|
|
2177
|
+
var import_types4 = require("@neat.is/types");
|
|
2101
2178
|
function buildServiceHostIndex(services) {
|
|
2102
2179
|
const knownHosts = /* @__PURE__ */ new Set();
|
|
2103
2180
|
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
@@ -2175,12 +2252,12 @@ function languageForPath(relPath) {
|
|
|
2175
2252
|
function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
|
|
2176
2253
|
let nodesAdded = 0;
|
|
2177
2254
|
let edgesAdded = 0;
|
|
2178
|
-
const fileNodeId = (0,
|
|
2255
|
+
const fileNodeId = (0, import_types4.fileId)(serviceName, relPath);
|
|
2179
2256
|
if (!graph.hasNode(fileNodeId)) {
|
|
2180
2257
|
const language = languageForPath(relPath);
|
|
2181
2258
|
const node = {
|
|
2182
2259
|
id: fileNodeId,
|
|
2183
|
-
type:
|
|
2260
|
+
type: import_types4.NodeType.FileNode,
|
|
2184
2261
|
service: serviceName,
|
|
2185
2262
|
path: relPath,
|
|
2186
2263
|
...language ? { language } : {},
|
|
@@ -2189,15 +2266,15 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
|
|
|
2189
2266
|
graph.addNode(fileNodeId, node);
|
|
2190
2267
|
nodesAdded++;
|
|
2191
2268
|
}
|
|
2192
|
-
const containsId = (0,
|
|
2269
|
+
const containsId = (0, import_types4.extractedEdgeId)(serviceNodeId, fileNodeId, import_types4.EdgeType.CONTAINS);
|
|
2193
2270
|
if (!graph.hasEdge(containsId)) {
|
|
2194
2271
|
const edge = {
|
|
2195
2272
|
id: containsId,
|
|
2196
2273
|
source: serviceNodeId,
|
|
2197
2274
|
target: fileNodeId,
|
|
2198
|
-
type:
|
|
2199
|
-
provenance:
|
|
2200
|
-
confidence: (0,
|
|
2275
|
+
type: import_types4.EdgeType.CONTAINS,
|
|
2276
|
+
provenance: import_types4.Provenance.EXTRACTED,
|
|
2277
|
+
confidence: (0, import_types4.confidenceForExtracted)("structural"),
|
|
2201
2278
|
evidence: { file: relPath }
|
|
2202
2279
|
};
|
|
2203
2280
|
graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
|
|
@@ -2831,11 +2908,11 @@ async function addRoutes(graph, services) {
|
|
|
2831
2908
|
}
|
|
2832
2909
|
if (routes.length === 0) continue;
|
|
2833
2910
|
for (const route of routes) {
|
|
2834
|
-
const rid = (0,
|
|
2911
|
+
const rid = (0, import_types5.routeId)(service.pkg.name, route.method, route.pathTemplate);
|
|
2835
2912
|
if (!graph.hasNode(rid)) {
|
|
2836
2913
|
const node = {
|
|
2837
2914
|
id: rid,
|
|
2838
|
-
type:
|
|
2915
|
+
type: import_types5.NodeType.RouteNode,
|
|
2839
2916
|
name: `${route.method} ${route.pathTemplate}`,
|
|
2840
2917
|
service: service.pkg.name,
|
|
2841
2918
|
method: route.method,
|
|
@@ -2848,15 +2925,15 @@ async function addRoutes(graph, services) {
|
|
|
2848
2925
|
graph.addNode(rid, node);
|
|
2849
2926
|
nodesAdded++;
|
|
2850
2927
|
}
|
|
2851
|
-
const containsId = (0,
|
|
2928
|
+
const containsId = (0, import_types5.extractedEdgeId)(service.node.id, rid, import_types5.EdgeType.CONTAINS);
|
|
2852
2929
|
if (!graph.hasEdge(containsId)) {
|
|
2853
2930
|
const edge = {
|
|
2854
2931
|
id: containsId,
|
|
2855
2932
|
source: service.node.id,
|
|
2856
2933
|
target: rid,
|
|
2857
|
-
type:
|
|
2858
|
-
provenance:
|
|
2859
|
-
confidence: (0,
|
|
2934
|
+
type: import_types5.EdgeType.CONTAINS,
|
|
2935
|
+
provenance: import_types5.Provenance.EXTRACTED,
|
|
2936
|
+
confidence: (0, import_types5.confidenceForExtracted)("structural"),
|
|
2860
2937
|
evidence: {
|
|
2861
2938
|
file: relFile,
|
|
2862
2939
|
line: route.line,
|
|
@@ -3171,11 +3248,11 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
3171
3248
|
};
|
|
3172
3249
|
}
|
|
3173
3250
|
function reconcileObservedRelPath(graph, serviceName, relPath) {
|
|
3174
|
-
if (graph.hasNode((0,
|
|
3251
|
+
if (graph.hasNode((0, import_types6.fileId)(serviceName, relPath))) return relPath;
|
|
3175
3252
|
let best = null;
|
|
3176
3253
|
graph.forEachNode((_id, attrs) => {
|
|
3177
3254
|
const a = attrs;
|
|
3178
|
-
if (a.type !==
|
|
3255
|
+
if (a.type !== import_types6.NodeType.FileNode || a.service !== serviceName) return;
|
|
3179
3256
|
if (a.discoveredVia === "otel") return;
|
|
3180
3257
|
const p = a.path;
|
|
3181
3258
|
if (!p) return;
|
|
@@ -3187,14 +3264,14 @@ function reconcileObservedRelPath(graph, serviceName, relPath) {
|
|
|
3187
3264
|
}
|
|
3188
3265
|
function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
|
|
3189
3266
|
const svcAttrs = graph.hasNode(serviceNodeId) ? graph.getNodeAttributes(serviceNodeId) : void 0;
|
|
3190
|
-
const canonicalService = svcAttrs && svcAttrs.type ===
|
|
3267
|
+
const canonicalService = svcAttrs && svcAttrs.type === import_types6.NodeType.ServiceNode && typeof svcAttrs.name === "string" ? svcAttrs.name : serviceName;
|
|
3191
3268
|
const relPath = reconcileObservedRelPath(graph, canonicalService, callSite.relPath);
|
|
3192
|
-
const fileNodeId = (0,
|
|
3269
|
+
const fileNodeId = (0, import_types6.fileId)(canonicalService, relPath);
|
|
3193
3270
|
if (!graph.hasNode(fileNodeId)) {
|
|
3194
3271
|
const language = languageForExt(relPath);
|
|
3195
3272
|
const node = {
|
|
3196
3273
|
id: fileNodeId,
|
|
3197
|
-
type:
|
|
3274
|
+
type: import_types6.NodeType.FileNode,
|
|
3198
3275
|
service: canonicalService,
|
|
3199
3276
|
path: relPath,
|
|
3200
3277
|
...language ? { language } : {},
|
|
@@ -3203,14 +3280,14 @@ function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
|
|
|
3203
3280
|
};
|
|
3204
3281
|
graph.addNode(fileNodeId, node);
|
|
3205
3282
|
}
|
|
3206
|
-
const containsId = makeObservedEdgeId(
|
|
3283
|
+
const containsId = makeObservedEdgeId(import_types6.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
|
|
3207
3284
|
if (!graph.hasEdge(containsId)) {
|
|
3208
3285
|
const edge = {
|
|
3209
3286
|
id: containsId,
|
|
3210
3287
|
source: serviceNodeId,
|
|
3211
3288
|
target: fileNodeId,
|
|
3212
|
-
type:
|
|
3213
|
-
provenance:
|
|
3289
|
+
type: import_types6.EdgeType.CONTAINS,
|
|
3290
|
+
provenance: import_types6.Provenance.OBSERVED
|
|
3214
3291
|
};
|
|
3215
3292
|
graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
|
|
3216
3293
|
}
|
|
@@ -3234,11 +3311,11 @@ function pickContainingSymbol(candidates, fn) {
|
|
|
3234
3311
|
return [...candidates].sort(bySpan)[0].id;
|
|
3235
3312
|
}
|
|
3236
3313
|
function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line) {
|
|
3237
|
-
const sid = (0,
|
|
3314
|
+
const sid = (0, import_types6.symbolId)(service, relPath, fn);
|
|
3238
3315
|
if (!graph.hasNode(sid)) {
|
|
3239
3316
|
const node = {
|
|
3240
3317
|
id: sid,
|
|
3241
|
-
type:
|
|
3318
|
+
type: import_types6.NodeType.SymbolNode,
|
|
3242
3319
|
kind: "function",
|
|
3243
3320
|
qualname: fn,
|
|
3244
3321
|
span: { startLine: line, endLine: line },
|
|
@@ -3248,14 +3325,14 @@ function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line)
|
|
|
3248
3325
|
};
|
|
3249
3326
|
graph.addNode(sid, node);
|
|
3250
3327
|
}
|
|
3251
|
-
const containsId = makeObservedEdgeId(
|
|
3328
|
+
const containsId = makeObservedEdgeId(import_types6.EdgeType.CONTAINS, fileNodeId, sid);
|
|
3252
3329
|
if (!graph.hasEdge(containsId)) {
|
|
3253
3330
|
const edge = {
|
|
3254
3331
|
id: containsId,
|
|
3255
3332
|
source: fileNodeId,
|
|
3256
3333
|
target: sid,
|
|
3257
|
-
type:
|
|
3258
|
-
provenance:
|
|
3334
|
+
type: import_types6.EdgeType.CONTAINS,
|
|
3335
|
+
provenance: import_types6.Provenance.OBSERVED
|
|
3259
3336
|
};
|
|
3260
3337
|
graph.addEdgeWithKey(containsId, fileNodeId, sid, edge);
|
|
3261
3338
|
}
|
|
@@ -3267,9 +3344,9 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
|
|
|
3267
3344
|
let sawSymbol = false;
|
|
3268
3345
|
const candidates = [];
|
|
3269
3346
|
graph.forEachOutboundEdge(fileNodeId, (_edge, edgeAttrs, _source, target) => {
|
|
3270
|
-
if (edgeAttrs.type !==
|
|
3347
|
+
if (edgeAttrs.type !== import_types6.EdgeType.CONTAINS) return;
|
|
3271
3348
|
const t = graph.getNodeAttributes(target);
|
|
3272
|
-
if (t.type !==
|
|
3349
|
+
if (t.type !== import_types6.NodeType.SymbolNode) return;
|
|
3273
3350
|
sawSymbol = true;
|
|
3274
3351
|
if (line >= t.span.startLine && line <= t.span.endLine) {
|
|
3275
3352
|
candidates.push({ id: target, symbol: t });
|
|
@@ -3282,17 +3359,17 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
|
|
|
3282
3359
|
return fileNodeId;
|
|
3283
3360
|
}
|
|
3284
3361
|
function makeObservedEdgeId(type, source, target) {
|
|
3285
|
-
return (0,
|
|
3362
|
+
return (0, import_types6.observedEdgeId)(source, target, type);
|
|
3286
3363
|
}
|
|
3287
3364
|
function makeInferredEdgeId(type, source, target) {
|
|
3288
|
-
return (0,
|
|
3365
|
+
return (0, import_types6.inferredEdgeId)(source, target, type);
|
|
3289
3366
|
}
|
|
3290
3367
|
var INFERRED_CONFIDENCE = 0.6;
|
|
3291
3368
|
var STITCH_MAX_DEPTH = 2;
|
|
3292
3369
|
var STITCH_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3370
|
+
import_types6.EdgeType.CALLS,
|
|
3371
|
+
import_types6.EdgeType.CONNECTS_TO,
|
|
3372
|
+
import_types6.EdgeType.DEPENDS_ON
|
|
3296
3373
|
]);
|
|
3297
3374
|
var WIRE_SPAN_KIND_CLIENT = 3;
|
|
3298
3375
|
var WIRE_SPAN_KIND_PRODUCER = 4;
|
|
@@ -3308,11 +3385,11 @@ function spanServesGraphqlOperation(kind) {
|
|
|
3308
3385
|
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
3309
3386
|
}
|
|
3310
3387
|
function ensureGraphqlOperationNode(graph, serviceName, operationType, operationName) {
|
|
3311
|
-
const id = (0,
|
|
3388
|
+
const id = (0, import_types6.graphqlOperationId)(serviceName, operationType, operationName);
|
|
3312
3389
|
if (graph.hasNode(id)) return id;
|
|
3313
3390
|
const node = {
|
|
3314
3391
|
id,
|
|
3315
|
-
type:
|
|
3392
|
+
type: import_types6.NodeType.GraphQLOperationNode,
|
|
3316
3393
|
name: operationName,
|
|
3317
3394
|
service: serviceName,
|
|
3318
3395
|
operationType: operationType.toLowerCase(),
|
|
@@ -3326,11 +3403,11 @@ function spanServesGrpcMethod(kind) {
|
|
|
3326
3403
|
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
3327
3404
|
}
|
|
3328
3405
|
function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
|
|
3329
|
-
const id = (0,
|
|
3406
|
+
const id = (0, import_types6.grpcMethodId)(rpcService, rpcMethod);
|
|
3330
3407
|
if (graph.hasNode(id)) return id;
|
|
3331
3408
|
const node = {
|
|
3332
3409
|
id,
|
|
3333
|
-
type:
|
|
3410
|
+
type: import_types6.NodeType.GrpcMethodNode,
|
|
3334
3411
|
name: `${rpcService}/${rpcMethod}`,
|
|
3335
3412
|
rpcService,
|
|
3336
3413
|
rpcMethod,
|
|
@@ -3343,11 +3420,11 @@ function spanServesWebsocketChannel(kind) {
|
|
|
3343
3420
|
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
3344
3421
|
}
|
|
3345
3422
|
function ensureWebsocketChannelNode(graph, serviceName, channel) {
|
|
3346
|
-
const id = (0,
|
|
3423
|
+
const id = (0, import_types6.websocketChannelId)(serviceName, channel);
|
|
3347
3424
|
if (graph.hasNode(id)) return id;
|
|
3348
3425
|
const node = {
|
|
3349
3426
|
id,
|
|
3350
|
-
type:
|
|
3427
|
+
type: import_types6.NodeType.WebSocketChannelNode,
|
|
3351
3428
|
name: channel,
|
|
3352
3429
|
service: serviceName,
|
|
3353
3430
|
channel,
|
|
@@ -3360,11 +3437,11 @@ function messagingDestinationKind(system) {
|
|
|
3360
3437
|
return `${system}-topic`;
|
|
3361
3438
|
}
|
|
3362
3439
|
function ensureMessagingDestinationNode(graph, system, destination) {
|
|
3363
|
-
const id = (0,
|
|
3440
|
+
const id = (0, import_types6.infraId)(messagingDestinationKind(system), destination);
|
|
3364
3441
|
if (graph.hasNode(id)) return id;
|
|
3365
3442
|
const node = {
|
|
3366
3443
|
id,
|
|
3367
|
-
type:
|
|
3444
|
+
type: import_types6.NodeType.InfraNode,
|
|
3368
3445
|
name: destination,
|
|
3369
3446
|
provider: "self",
|
|
3370
3447
|
kind: messagingDestinationKind(system)
|
|
@@ -3408,9 +3485,9 @@ function lookupParentSpan(traceId, parentSpanId, now) {
|
|
|
3408
3485
|
};
|
|
3409
3486
|
}
|
|
3410
3487
|
function resolveServiceId(graph, host, env) {
|
|
3411
|
-
const envTagged = (0,
|
|
3488
|
+
const envTagged = (0, import_types6.serviceId)(host, env);
|
|
3412
3489
|
if (graph.hasNode(envTagged)) return envTagged;
|
|
3413
|
-
const envLess = (0,
|
|
3490
|
+
const envLess = (0, import_types6.serviceId)(host);
|
|
3414
3491
|
if (envLess !== envTagged && graph.hasNode(envLess)) return envLess;
|
|
3415
3492
|
let sameEnv = null;
|
|
3416
3493
|
let envLessMatch = null;
|
|
@@ -3418,7 +3495,7 @@ function resolveServiceId(graph, host, env) {
|
|
|
3418
3495
|
graph.forEachNode((id, attrs) => {
|
|
3419
3496
|
if (sameEnv) return;
|
|
3420
3497
|
const a = attrs;
|
|
3421
|
-
if (a.type !==
|
|
3498
|
+
if (a.type !== import_types6.NodeType.ServiceNode) return;
|
|
3422
3499
|
const matchesByName = a.name === host;
|
|
3423
3500
|
const matchesByAlias = a.aliases ? a.aliases.includes(host) : false;
|
|
3424
3501
|
if (!matchesByName && !matchesByAlias) return;
|
|
@@ -3433,14 +3510,14 @@ function resolveServiceId(graph, host, env) {
|
|
|
3433
3510
|
return sameEnv ?? envLessMatch ?? anyMatch;
|
|
3434
3511
|
}
|
|
3435
3512
|
function frontierIdFor(host) {
|
|
3436
|
-
return (0,
|
|
3513
|
+
return (0, import_types6.frontierId)(host);
|
|
3437
3514
|
}
|
|
3438
3515
|
function ensureServiceNode(graph, serviceName, env) {
|
|
3439
|
-
const id = (0,
|
|
3516
|
+
const id = (0, import_types6.serviceId)(serviceName, env);
|
|
3440
3517
|
if (graph.hasNode(id)) return id;
|
|
3441
3518
|
const wanted = serviceName.toLowerCase();
|
|
3442
3519
|
const extractedId = graph.findNode((_nid, attrs) => {
|
|
3443
|
-
if (attrs.type !==
|
|
3520
|
+
if (attrs.type !== import_types6.NodeType.ServiceNode) return false;
|
|
3444
3521
|
const svc = attrs;
|
|
3445
3522
|
if (svc.discoveredVia === "otel") return false;
|
|
3446
3523
|
return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
|
|
@@ -3448,7 +3525,7 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
3448
3525
|
if (extractedId) return extractedId;
|
|
3449
3526
|
const node = {
|
|
3450
3527
|
id,
|
|
3451
|
-
type:
|
|
3528
|
+
type: import_types6.NodeType.ServiceNode,
|
|
3452
3529
|
name: serviceName,
|
|
3453
3530
|
language: "unknown",
|
|
3454
3531
|
discoveredVia: "otel",
|
|
@@ -3458,11 +3535,11 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
3458
3535
|
return id;
|
|
3459
3536
|
}
|
|
3460
3537
|
function ensureInfraNode(graph, kind, name, provider) {
|
|
3461
|
-
const id = (0,
|
|
3538
|
+
const id = (0, import_types6.infraId)(kind, name);
|
|
3462
3539
|
if (graph.hasNode(id)) return id;
|
|
3463
3540
|
const node = {
|
|
3464
3541
|
id,
|
|
3465
|
-
type:
|
|
3542
|
+
type: import_types6.NodeType.InfraNode,
|
|
3466
3543
|
name,
|
|
3467
3544
|
provider,
|
|
3468
3545
|
kind
|
|
@@ -3470,12 +3547,27 @@ function ensureInfraNode(graph, kind, name, provider) {
|
|
|
3470
3547
|
graph.addNode(id, node);
|
|
3471
3548
|
return id;
|
|
3472
3549
|
}
|
|
3550
|
+
var COLUMN_BEARING_INFRA_KINDS = /* @__PURE__ */ new Set(["sql-table", "supabase-table"]);
|
|
3551
|
+
function mergeColumnsAt(graph, tableNodeId, columns, provenance, confidence) {
|
|
3552
|
+
if (!columns || columns.length === 0 || !graph.hasNode(tableNodeId)) return;
|
|
3553
|
+
const node = graph.getNodeAttributes(tableNodeId);
|
|
3554
|
+
if (node.type !== import_types6.NodeType.InfraNode || !node.kind || !COLUMN_BEARING_INFRA_KINDS.has(node.kind)) {
|
|
3555
|
+
return;
|
|
3556
|
+
}
|
|
3557
|
+
graph.replaceNodeAttributes(tableNodeId, {
|
|
3558
|
+
...node,
|
|
3559
|
+
columns: foldColumns(node.columns, columns, provenance, confidence)
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
function mergeObservedColumns(graph, tableNodeId, columns) {
|
|
3563
|
+
mergeColumnsAt(graph, tableNodeId, columns, import_types6.Provenance.OBSERVED, OBSERVED_COLUMN_CONFIDENCE);
|
|
3564
|
+
}
|
|
3473
3565
|
function ensureDatabaseNode(graph, host, engine) {
|
|
3474
|
-
const id = (0,
|
|
3566
|
+
const id = (0, import_types6.databaseId)(host);
|
|
3475
3567
|
if (graph.hasNode(id)) return id;
|
|
3476
3568
|
const node = {
|
|
3477
3569
|
id,
|
|
3478
|
-
type:
|
|
3570
|
+
type: import_types6.NodeType.DatabaseNode,
|
|
3479
3571
|
name: host,
|
|
3480
3572
|
engine,
|
|
3481
3573
|
engineVersion: "unknown",
|
|
@@ -3487,11 +3579,11 @@ function ensureDatabaseNode(graph, host, engine) {
|
|
|
3487
3579
|
return id;
|
|
3488
3580
|
}
|
|
3489
3581
|
function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
3490
|
-
const id = (0,
|
|
3582
|
+
const id = (0, import_types6.localDatabaseId)(serviceName, name);
|
|
3491
3583
|
if (graph.hasNode(id)) return id;
|
|
3492
3584
|
const node = {
|
|
3493
3585
|
id,
|
|
3494
|
-
type:
|
|
3586
|
+
type: import_types6.NodeType.DatabaseNode,
|
|
3495
3587
|
name,
|
|
3496
3588
|
engine,
|
|
3497
3589
|
engineVersion: "unknown",
|
|
@@ -3506,17 +3598,17 @@ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
|
|
|
3506
3598
|
const sources = [serviceNodeId];
|
|
3507
3599
|
for (const edgeId of graph.outboundEdges(serviceNodeId)) {
|
|
3508
3600
|
const e = graph.getEdgeAttributes(edgeId);
|
|
3509
|
-
if (e.type ===
|
|
3601
|
+
if (e.type === import_types6.EdgeType.CONTAINS) sources.push(e.target);
|
|
3510
3602
|
}
|
|
3511
3603
|
const matches = /* @__PURE__ */ new Set();
|
|
3512
3604
|
for (const src of sources) {
|
|
3513
3605
|
if (!graph.hasNode(src)) continue;
|
|
3514
3606
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
3515
3607
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3516
|
-
if (edge.type !==
|
|
3608
|
+
if (edge.type !== import_types6.EdgeType.CONNECTS_TO || edge.provenance !== import_types6.Provenance.EXTRACTED) continue;
|
|
3517
3609
|
if (!graph.hasNode(edge.target)) continue;
|
|
3518
3610
|
const target = graph.getNodeAttributes(edge.target);
|
|
3519
|
-
if (target.type !==
|
|
3611
|
+
if (target.type !== import_types6.NodeType.DatabaseNode || target.engine !== engine) continue;
|
|
3520
3612
|
matches.add(edge.target);
|
|
3521
3613
|
}
|
|
3522
3614
|
}
|
|
@@ -3531,7 +3623,7 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
3531
3623
|
}
|
|
3532
3624
|
const node = {
|
|
3533
3625
|
id,
|
|
3534
|
-
type:
|
|
3626
|
+
type: import_types6.NodeType.FrontierNode,
|
|
3535
3627
|
name: host,
|
|
3536
3628
|
host,
|
|
3537
3629
|
firstObserved: ts,
|
|
@@ -3555,11 +3647,11 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
3555
3647
|
};
|
|
3556
3648
|
const updated = {
|
|
3557
3649
|
...existing,
|
|
3558
|
-
provenance:
|
|
3650
|
+
provenance: import_types6.Provenance.OBSERVED,
|
|
3559
3651
|
lastObserved: ts,
|
|
3560
3652
|
callCount: newSpanCount,
|
|
3561
3653
|
signal: newSignal,
|
|
3562
|
-
confidence: (0,
|
|
3654
|
+
confidence: (0, import_types6.confidenceForObservedSignal)(newSignal),
|
|
3563
3655
|
grain
|
|
3564
3656
|
// backfills legacy edges that predate ADR-142
|
|
3565
3657
|
};
|
|
@@ -3576,8 +3668,8 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
3576
3668
|
source,
|
|
3577
3669
|
target,
|
|
3578
3670
|
type,
|
|
3579
|
-
provenance:
|
|
3580
|
-
confidence: (0,
|
|
3671
|
+
provenance: import_types6.Provenance.OBSERVED,
|
|
3672
|
+
confidence: (0, import_types6.confidenceForObservedSignal)(signal),
|
|
3581
3673
|
lastObserved: ts,
|
|
3582
3674
|
callCount: 1,
|
|
3583
3675
|
signal,
|
|
@@ -3599,9 +3691,9 @@ function stitchTrace(graph, sourceServiceId, ts) {
|
|
|
3599
3691
|
const outbound = graph.outboundEdges(nodeId);
|
|
3600
3692
|
for (const edgeId of outbound) {
|
|
3601
3693
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3602
|
-
if (edge.provenance !==
|
|
3694
|
+
if (edge.provenance !== import_types6.Provenance.EXTRACTED) continue;
|
|
3603
3695
|
if (!STITCH_EDGE_TYPES.has(edge.type)) continue;
|
|
3604
|
-
if (graph.hasEdge((0,
|
|
3696
|
+
if (graph.hasEdge((0, import_types6.observedEdgeId)(edge.source, edge.target, edge.type))) continue;
|
|
3605
3697
|
upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts);
|
|
3606
3698
|
if (!visited.has(edge.target)) {
|
|
3607
3699
|
visited.add(edge.target);
|
|
@@ -3623,7 +3715,7 @@ function upsertInferredEdge(graph, type, source, target, ts) {
|
|
|
3623
3715
|
source,
|
|
3624
3716
|
target,
|
|
3625
3717
|
type,
|
|
3626
|
-
provenance:
|
|
3718
|
+
provenance: import_types6.Provenance.INFERRED,
|
|
3627
3719
|
confidence: INFERRED_CONFIDENCE,
|
|
3628
3720
|
lastObserved: ts
|
|
3629
3721
|
};
|
|
@@ -3634,12 +3726,12 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
3634
3726
|
await import_node_fs8.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
3635
3727
|
}
|
|
3636
3728
|
function incidentAffectedNode(span, graph, scanPath) {
|
|
3637
|
-
const sid = (0,
|
|
3729
|
+
const sid = (0, import_types6.serviceId)(span.service, span.env);
|
|
3638
3730
|
const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
|
|
3639
3731
|
const callSite = callSiteFromSpan(span, serviceNode, scanPath);
|
|
3640
3732
|
if (callSite) {
|
|
3641
3733
|
const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
|
|
3642
|
-
return (0,
|
|
3734
|
+
return (0, import_types6.fileId)(span.service, relPath);
|
|
3643
3735
|
}
|
|
3644
3736
|
return sid;
|
|
3645
3737
|
}
|
|
@@ -3739,7 +3831,7 @@ function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
|
|
|
3739
3831
|
graph.forEachNode((id, attrs) => {
|
|
3740
3832
|
if (found) return;
|
|
3741
3833
|
const a = attrs;
|
|
3742
|
-
if (a.type !==
|
|
3834
|
+
if (a.type !== import_types6.NodeType.RouteNode || a.service !== serviceName) return;
|
|
3743
3835
|
if (m && a.method !== "ALL" && a.method !== m) return;
|
|
3744
3836
|
if (normalizePathTemplate(a.pathTemplate) === target) found = id;
|
|
3745
3837
|
});
|
|
@@ -3770,7 +3862,7 @@ async function handleSpan(ctx, span) {
|
|
|
3770
3862
|
let targetId;
|
|
3771
3863
|
if (host) {
|
|
3772
3864
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
3773
|
-
targetId = (0,
|
|
3865
|
+
targetId = (0, import_types6.databaseId)(host);
|
|
3774
3866
|
} else {
|
|
3775
3867
|
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
3776
3868
|
if (declared) {
|
|
@@ -3787,7 +3879,7 @@ async function handleSpan(ctx, span) {
|
|
|
3787
3879
|
}
|
|
3788
3880
|
const result = upsertObservedEdge(
|
|
3789
3881
|
ctx.graph,
|
|
3790
|
-
|
|
3882
|
+
import_types6.EdgeType.CONNECTS_TO,
|
|
3791
3883
|
observedSource(),
|
|
3792
3884
|
targetId,
|
|
3793
3885
|
ts,
|
|
@@ -3799,7 +3891,7 @@ async function handleSpan(ctx, span) {
|
|
|
3799
3891
|
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
3800
3892
|
upsertObservedEdge(
|
|
3801
3893
|
ctx.graph,
|
|
3802
|
-
|
|
3894
|
+
import_types6.EdgeType.CALLS,
|
|
3803
3895
|
observedSource(),
|
|
3804
3896
|
collectionId,
|
|
3805
3897
|
ts,
|
|
@@ -3811,13 +3903,14 @@ async function handleSpan(ctx, span) {
|
|
|
3811
3903
|
const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
|
|
3812
3904
|
upsertObservedEdge(
|
|
3813
3905
|
ctx.graph,
|
|
3814
|
-
|
|
3906
|
+
import_types6.EdgeType.CALLS,
|
|
3815
3907
|
observedSource(),
|
|
3816
3908
|
tableId,
|
|
3817
3909
|
ts,
|
|
3818
3910
|
isError,
|
|
3819
3911
|
callSiteEvidence
|
|
3820
3912
|
);
|
|
3913
|
+
mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
|
|
3821
3914
|
}
|
|
3822
3915
|
}
|
|
3823
3916
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
@@ -3826,7 +3919,7 @@ async function handleSpan(ctx, span) {
|
|
|
3826
3919
|
span.messagingSystem,
|
|
3827
3920
|
span.messagingDestination
|
|
3828
3921
|
);
|
|
3829
|
-
const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ?
|
|
3922
|
+
const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types6.EdgeType.CONSUMES_FROM : import_types6.EdgeType.PUBLISHES_TO;
|
|
3830
3923
|
const result = upsertObservedEdge(
|
|
3831
3924
|
ctx.graph,
|
|
3832
3925
|
edgeType,
|
|
@@ -3846,7 +3939,7 @@ async function handleSpan(ctx, span) {
|
|
|
3846
3939
|
);
|
|
3847
3940
|
const result = upsertObservedEdge(
|
|
3848
3941
|
ctx.graph,
|
|
3849
|
-
|
|
3942
|
+
import_types6.EdgeType.CONTAINS,
|
|
3850
3943
|
observedSource(),
|
|
3851
3944
|
targetId,
|
|
3852
3945
|
ts,
|
|
@@ -3858,7 +3951,7 @@ async function handleSpan(ctx, span) {
|
|
|
3858
3951
|
const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
|
|
3859
3952
|
const result = upsertObservedEdge(
|
|
3860
3953
|
ctx.graph,
|
|
3861
|
-
|
|
3954
|
+
import_types6.EdgeType.CONTAINS,
|
|
3862
3955
|
observedSource(),
|
|
3863
3956
|
targetId,
|
|
3864
3957
|
ts,
|
|
@@ -3874,7 +3967,7 @@ async function handleSpan(ctx, span) {
|
|
|
3874
3967
|
);
|
|
3875
3968
|
const result = upsertObservedEdge(
|
|
3876
3969
|
ctx.graph,
|
|
3877
|
-
|
|
3970
|
+
import_types6.EdgeType.CONNECTS_TO,
|
|
3878
3971
|
observedSource(),
|
|
3879
3972
|
targetId,
|
|
3880
3973
|
ts,
|
|
@@ -3890,7 +3983,7 @@ async function handleSpan(ctx, span) {
|
|
|
3890
3983
|
if (targetId && targetId !== sourceId) {
|
|
3891
3984
|
upsertObservedEdge(
|
|
3892
3985
|
ctx.graph,
|
|
3893
|
-
|
|
3986
|
+
import_types6.EdgeType.CALLS,
|
|
3894
3987
|
observedSource(),
|
|
3895
3988
|
targetId,
|
|
3896
3989
|
ts,
|
|
@@ -3903,7 +3996,7 @@ async function handleSpan(ctx, span) {
|
|
|
3903
3996
|
const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
|
|
3904
3997
|
upsertObservedEdge(
|
|
3905
3998
|
ctx.graph,
|
|
3906
|
-
|
|
3999
|
+
import_types6.EdgeType.CALLS,
|
|
3907
4000
|
observedSource(),
|
|
3908
4001
|
frontierNodeId,
|
|
3909
4002
|
ts,
|
|
@@ -3929,7 +4022,7 @@ async function handleSpan(ctx, span) {
|
|
|
3929
4022
|
} : void 0;
|
|
3930
4023
|
upsertObservedEdge(
|
|
3931
4024
|
ctx.graph,
|
|
3932
|
-
|
|
4025
|
+
import_types6.EdgeType.CALLS,
|
|
3933
4026
|
fallbackSource,
|
|
3934
4027
|
sourceId,
|
|
3935
4028
|
ts,
|
|
@@ -3939,7 +4032,7 @@ async function handleSpan(ctx, span) {
|
|
|
3939
4032
|
}
|
|
3940
4033
|
}
|
|
3941
4034
|
}
|
|
3942
|
-
if (span.httpRoute && (span.kind === 2 || span.kind === 0 || span.kind === void 0)) {
|
|
4035
|
+
if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
|
|
3943
4036
|
const routeNodeId = findRouteNodeByHttpRoute(
|
|
3944
4037
|
ctx.graph,
|
|
3945
4038
|
span.service,
|
|
@@ -3948,7 +4041,7 @@ async function handleSpan(ctx, span) {
|
|
|
3948
4041
|
);
|
|
3949
4042
|
if (routeNodeId) {
|
|
3950
4043
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
3951
|
-
upsertObservedEdge(ctx.graph,
|
|
4044
|
+
upsertObservedEdge(ctx.graph, import_types6.EdgeType.CONTAINS, (0, import_types6.serviceId)(routeSvc), routeNodeId, ts, isError);
|
|
3952
4045
|
}
|
|
3953
4046
|
}
|
|
3954
4047
|
if (span.statusCode === 2) {
|
|
@@ -3987,7 +4080,7 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3987
4080
|
const aliasIndex = /* @__PURE__ */ new Map();
|
|
3988
4081
|
graph.forEachNode((id, attrs) => {
|
|
3989
4082
|
const a = attrs;
|
|
3990
|
-
if (a.type !==
|
|
4083
|
+
if (a.type !== import_types6.NodeType.ServiceNode) return;
|
|
3991
4084
|
aliasIndex.set(a.name, id);
|
|
3992
4085
|
if (a.aliases) {
|
|
3993
4086
|
for (const alias of a.aliases) aliasIndex.set(alias, id);
|
|
@@ -3996,7 +4089,7 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3996
4089
|
const toPromote = [];
|
|
3997
4090
|
graph.forEachNode((id, attrs) => {
|
|
3998
4091
|
const a = attrs;
|
|
3999
|
-
if (a.type !==
|
|
4092
|
+
if (a.type !== import_types6.NodeType.FrontierNode) return;
|
|
4000
4093
|
const target = aliasIndex.get(a.host);
|
|
4001
4094
|
if (!target) return;
|
|
4002
4095
|
if (target === id) return;
|
|
@@ -4030,7 +4123,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId7) {
|
|
|
4030
4123
|
}
|
|
4031
4124
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
4032
4125
|
graph.dropEdge(oldEdgeId);
|
|
4033
|
-
const newId = edge.provenance ===
|
|
4126
|
+
const newId = edge.provenance === import_types6.Provenance.OBSERVED ? (0, import_types6.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types6.Provenance.INFERRED ? (0, import_types6.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types6.extractedEdgeId)(newSource, newTarget, edge.type);
|
|
4034
4127
|
if (graph.hasEdge(newId)) {
|
|
4035
4128
|
const existing = graph.getEdgeAttributes(newId);
|
|
4036
4129
|
const merged = {
|
|
@@ -4064,12 +4157,12 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
4064
4157
|
const project = options.project ?? DEFAULT_PROJECT;
|
|
4065
4158
|
graph.forEachEdge((id, attrs) => {
|
|
4066
4159
|
const e = attrs;
|
|
4067
|
-
if (e.provenance !==
|
|
4160
|
+
if (e.provenance !== import_types6.Provenance.OBSERVED) return;
|
|
4068
4161
|
if (!e.lastObserved) return;
|
|
4069
4162
|
const threshold = thresholdForEdgeType(e.type, thresholds);
|
|
4070
4163
|
const age = now - new Date(e.lastObserved).getTime();
|
|
4071
4164
|
if (age > threshold) {
|
|
4072
|
-
const updated = { ...e, provenance:
|
|
4165
|
+
const updated = { ...e, provenance: import_types6.Provenance.STALE, confidence: 0.3 };
|
|
4073
4166
|
graph.replaceEdgeAttributes(id, updated);
|
|
4074
4167
|
events.push({
|
|
4075
4168
|
edgeId: id,
|
|
@@ -4086,8 +4179,8 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
4086
4179
|
project,
|
|
4087
4180
|
payload: {
|
|
4088
4181
|
edgeId: id,
|
|
4089
|
-
from:
|
|
4090
|
-
to:
|
|
4182
|
+
from: import_types6.Provenance.OBSERVED,
|
|
4183
|
+
to: import_types6.Provenance.STALE
|
|
4091
4184
|
}
|
|
4092
4185
|
});
|
|
4093
4186
|
}
|
|
@@ -4196,7 +4289,7 @@ function mergeSnapshot(graph, snapshot) {
|
|
|
4196
4289
|
const validEdges = [];
|
|
4197
4290
|
for (const node of incomingNodes) {
|
|
4198
4291
|
if (node.attributes === void 0) continue;
|
|
4199
|
-
const parsed =
|
|
4292
|
+
const parsed = import_types6.GraphNodeSchema.safeParse(node.attributes);
|
|
4200
4293
|
if (!parsed.success) {
|
|
4201
4294
|
issues.push(`node "${node.key}": ${describeZodIssues(parsed.error)}`);
|
|
4202
4295
|
continue;
|
|
@@ -4205,7 +4298,7 @@ function mergeSnapshot(graph, snapshot) {
|
|
|
4205
4298
|
}
|
|
4206
4299
|
for (const edge of incomingEdges) {
|
|
4207
4300
|
if (edge.attributes === void 0) continue;
|
|
4208
|
-
const parsed =
|
|
4301
|
+
const parsed = import_types6.GraphEdgeSchema.safeParse(edge.attributes);
|
|
4209
4302
|
if (!parsed.success) {
|
|
4210
4303
|
const label = edge.key ?? `${edge.source}->${edge.target}`;
|
|
4211
4304
|
issues.push(`edge "${label}": ${describeZodIssues(parsed.error)}`);
|
|
@@ -4234,29 +4327,29 @@ function mergeSnapshot(graph, snapshot) {
|
|
|
4234
4327
|
}
|
|
4235
4328
|
|
|
4236
4329
|
// src/traverse.ts
|
|
4237
|
-
var
|
|
4330
|
+
var import_types7 = require("@neat.is/types");
|
|
4238
4331
|
var ROOT_CAUSE_MAX_DEPTH = 5;
|
|
4239
4332
|
var BLAST_RADIUS_DEFAULT_DEPTH = 10;
|
|
4240
4333
|
function isFrontierNode(graph, nodeId) {
|
|
4241
4334
|
if (!graph.hasNode(nodeId)) return false;
|
|
4242
4335
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4243
|
-
return attrs.type ===
|
|
4336
|
+
return attrs.type === import_types7.NodeType.FrontierNode;
|
|
4244
4337
|
}
|
|
4245
4338
|
function resolveOwningService(graph, nodeId) {
|
|
4246
4339
|
if (!graph.hasNode(nodeId)) return null;
|
|
4247
4340
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4248
|
-
if (attrs.type ===
|
|
4341
|
+
if (attrs.type === import_types7.NodeType.ServiceNode) {
|
|
4249
4342
|
return { id: nodeId, svc: attrs };
|
|
4250
4343
|
}
|
|
4251
|
-
if (attrs.type ===
|
|
4344
|
+
if (attrs.type === import_types7.NodeType.FileNode || attrs.type === import_types7.NodeType.SymbolNode) {
|
|
4252
4345
|
for (const edgeId of graph.inboundEdges(nodeId)) {
|
|
4253
4346
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4254
|
-
if (e.type !==
|
|
4347
|
+
if (e.type !== import_types7.EdgeType.CONTAINS) continue;
|
|
4255
4348
|
const owner = graph.getNodeAttributes(e.source);
|
|
4256
|
-
if (owner.type ===
|
|
4349
|
+
if (owner.type === import_types7.NodeType.ServiceNode) {
|
|
4257
4350
|
return { id: e.source, svc: owner };
|
|
4258
4351
|
}
|
|
4259
|
-
if (owner.type ===
|
|
4352
|
+
if (owner.type === import_types7.NodeType.FileNode) {
|
|
4260
4353
|
return resolveOwningService(graph, e.source);
|
|
4261
4354
|
}
|
|
4262
4355
|
}
|
|
@@ -4269,7 +4362,7 @@ function bestEdgeBySource(graph, edgeIds) {
|
|
|
4269
4362
|
const e = graph.getEdgeAttributes(id);
|
|
4270
4363
|
if (isFrontierNode(graph, e.source)) continue;
|
|
4271
4364
|
const cur = best.get(e.source);
|
|
4272
|
-
if (!cur ||
|
|
4365
|
+
if (!cur || import_types7.PROV_RANK[e.provenance] > import_types7.PROV_RANK[cur.provenance]) {
|
|
4273
4366
|
best.set(e.source, e);
|
|
4274
4367
|
}
|
|
4275
4368
|
}
|
|
@@ -4281,7 +4374,7 @@ function bestEdgeByTarget(graph, edgeIds) {
|
|
|
4281
4374
|
const e = graph.getEdgeAttributes(id);
|
|
4282
4375
|
if (isFrontierNode(graph, e.target)) continue;
|
|
4283
4376
|
const cur = best.get(e.target);
|
|
4284
|
-
if (!cur ||
|
|
4377
|
+
if (!cur || import_types7.PROV_RANK[e.provenance] > import_types7.PROV_RANK[cur.provenance]) {
|
|
4285
4378
|
best.set(e.target, e);
|
|
4286
4379
|
}
|
|
4287
4380
|
}
|
|
@@ -4344,19 +4437,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
4344
4437
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
4345
4438
|
let best = { path: [start], edges: [] };
|
|
4346
4439
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
4347
|
-
function step(node,
|
|
4348
|
-
if (
|
|
4349
|
-
best = { path: [...
|
|
4440
|
+
function step(node, path59, edges) {
|
|
4441
|
+
if (path59.length > best.path.length) {
|
|
4442
|
+
best = { path: [...path59], edges: [...edges] };
|
|
4350
4443
|
}
|
|
4351
|
-
if (
|
|
4444
|
+
if (path59.length - 1 >= maxDepth) return;
|
|
4352
4445
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
4353
4446
|
for (const [srcId, edge] of incoming) {
|
|
4354
4447
|
if (visited.has(srcId)) continue;
|
|
4355
4448
|
visited.add(srcId);
|
|
4356
|
-
|
|
4449
|
+
path59.push(srcId);
|
|
4357
4450
|
edges.push(edge);
|
|
4358
|
-
step(srcId,
|
|
4359
|
-
|
|
4451
|
+
step(srcId, path59, edges);
|
|
4452
|
+
path59.pop();
|
|
4360
4453
|
edges.pop();
|
|
4361
4454
|
visited.delete(srcId);
|
|
4362
4455
|
}
|
|
@@ -4443,10 +4536,10 @@ function symbolRootCauseShape(graph, origin, walk6) {
|
|
|
4443
4536
|
return serviceRootCauseShape(graph, owner.svc, walk6);
|
|
4444
4537
|
}
|
|
4445
4538
|
var rootCauseShapes = {
|
|
4446
|
-
[
|
|
4447
|
-
[
|
|
4448
|
-
[
|
|
4449
|
-
[
|
|
4539
|
+
[import_types7.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
4540
|
+
[import_types7.NodeType.ServiceNode]: serviceRootCauseShape,
|
|
4541
|
+
[import_types7.NodeType.FileNode]: fileRootCauseShape,
|
|
4542
|
+
[import_types7.NodeType.SymbolNode]: symbolRootCauseShape
|
|
4450
4543
|
};
|
|
4451
4544
|
function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
4452
4545
|
if (!graph.hasNode(errorNodeId)) return null;
|
|
@@ -4457,7 +4550,7 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
4457
4550
|
const match = shape(graph, origin, walk6);
|
|
4458
4551
|
if (match) {
|
|
4459
4552
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
4460
|
-
return
|
|
4553
|
+
return import_types7.RootCauseResultSchema.parse({
|
|
4461
4554
|
rootCauseNode: match.rootCauseNode,
|
|
4462
4555
|
rootCauseReason: reason,
|
|
4463
4556
|
traversalPath: walk6.path,
|
|
@@ -4467,7 +4560,7 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
4467
4560
|
});
|
|
4468
4561
|
}
|
|
4469
4562
|
}
|
|
4470
|
-
if (origin.type ===
|
|
4563
|
+
if (origin.type === import_types7.NodeType.ServiceNode) {
|
|
4471
4564
|
const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
|
|
4472
4565
|
if (crossService) return crossService;
|
|
4473
4566
|
}
|
|
@@ -4507,8 +4600,8 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
4507
4600
|
const loc = localizeFromIncidents(nodeId, incidents, errorEvent);
|
|
4508
4601
|
if (!loc) return null;
|
|
4509
4602
|
const traversalPath = loc.fileNode ? [nodeId, loc.fileNode] : [nodeId];
|
|
4510
|
-
const edgeProvenances = loc.fileNode ? [
|
|
4511
|
-
return
|
|
4603
|
+
const edgeProvenances = loc.fileNode ? [import_types7.Provenance.OBSERVED] : [];
|
|
4604
|
+
return import_types7.RootCauseResultSchema.parse({
|
|
4512
4605
|
rootCauseNode: loc.rootCauseNode,
|
|
4513
4606
|
rootCauseReason: loc.rootCauseReason,
|
|
4514
4607
|
traversalPath,
|
|
@@ -4518,21 +4611,21 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
4518
4611
|
});
|
|
4519
4612
|
}
|
|
4520
4613
|
function isFailingCallEdge(e) {
|
|
4521
|
-
return e.type ===
|
|
4614
|
+
return e.type === import_types7.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
4522
4615
|
}
|
|
4523
4616
|
function callSourcesForService(graph, serviceId7) {
|
|
4524
4617
|
const ids = [serviceId7];
|
|
4525
4618
|
for (const edgeId of graph.outboundEdges(serviceId7)) {
|
|
4526
4619
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4527
|
-
if (e.type !==
|
|
4620
|
+
if (e.type !== import_types7.EdgeType.CONTAINS) continue;
|
|
4528
4621
|
const tgt = graph.getNodeAttributes(e.target);
|
|
4529
|
-
if (tgt.type !==
|
|
4622
|
+
if (tgt.type !== import_types7.NodeType.FileNode) continue;
|
|
4530
4623
|
ids.push(e.target);
|
|
4531
4624
|
for (const symEdgeId of graph.outboundEdges(e.target)) {
|
|
4532
4625
|
const se = graph.getEdgeAttributes(symEdgeId);
|
|
4533
|
-
if (se.type !==
|
|
4626
|
+
if (se.type !== import_types7.EdgeType.CONTAINS) continue;
|
|
4534
4627
|
const sym = graph.getNodeAttributes(se.target);
|
|
4535
|
-
if (sym.type ===
|
|
4628
|
+
if (sym.type === import_types7.NodeType.SymbolNode) ids.push(se.target);
|
|
4536
4629
|
}
|
|
4537
4630
|
}
|
|
4538
4631
|
return ids;
|
|
@@ -4541,8 +4634,8 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
4541
4634
|
const ec = e.signal?.errorCount ?? 0;
|
|
4542
4635
|
const cc = curEdge.signal?.errorCount ?? 0;
|
|
4543
4636
|
if (ec !== cc) return ec > cc;
|
|
4544
|
-
if (
|
|
4545
|
-
return
|
|
4637
|
+
if (import_types7.PROV_RANK[e.provenance] !== import_types7.PROV_RANK[curEdge.provenance]) {
|
|
4638
|
+
return import_types7.PROV_RANK[e.provenance] > import_types7.PROV_RANK[curEdge.provenance];
|
|
4546
4639
|
}
|
|
4547
4640
|
return id < curId;
|
|
4548
4641
|
}
|
|
@@ -4563,26 +4656,26 @@ function dominantFailingCall(graph, serviceId7, visited) {
|
|
|
4563
4656
|
return best;
|
|
4564
4657
|
}
|
|
4565
4658
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
4566
|
-
const
|
|
4659
|
+
const path59 = [originServiceId];
|
|
4567
4660
|
const edges = [];
|
|
4568
4661
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
4569
4662
|
let current = originServiceId;
|
|
4570
4663
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
4571
4664
|
const hop = dominantFailingCall(graph, current, visited);
|
|
4572
4665
|
if (!hop) break;
|
|
4573
|
-
|
|
4666
|
+
path59.push(hop.nextService);
|
|
4574
4667
|
edges.push(hop.edge);
|
|
4575
4668
|
visited.add(hop.nextService);
|
|
4576
4669
|
current = hop.nextService;
|
|
4577
4670
|
}
|
|
4578
4671
|
if (edges.length === 0) return null;
|
|
4579
|
-
return { path:
|
|
4672
|
+
return { path: path59, edges, culprit: current };
|
|
4580
4673
|
}
|
|
4581
4674
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
4582
4675
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
4583
4676
|
if (!chain) return null;
|
|
4584
4677
|
const culprit = chain.culprit;
|
|
4585
|
-
const
|
|
4678
|
+
const path59 = [...chain.path];
|
|
4586
4679
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
4587
4680
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
4588
4681
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -4590,14 +4683,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
4590
4683
|
if (loc) {
|
|
4591
4684
|
let rootCauseNode = culprit;
|
|
4592
4685
|
if (loc.fileNode) {
|
|
4593
|
-
|
|
4594
|
-
edgeProvenances.push(
|
|
4686
|
+
path59.push(loc.fileNode);
|
|
4687
|
+
edgeProvenances.push(import_types7.Provenance.OBSERVED);
|
|
4595
4688
|
rootCauseNode = loc.fileNode;
|
|
4596
4689
|
}
|
|
4597
|
-
return
|
|
4690
|
+
return import_types7.RootCauseResultSchema.parse({
|
|
4598
4691
|
rootCauseNode,
|
|
4599
4692
|
rootCauseReason: loc.rootCauseReason,
|
|
4600
|
-
traversalPath:
|
|
4693
|
+
traversalPath: path59,
|
|
4601
4694
|
edgeProvenances,
|
|
4602
4695
|
confidence,
|
|
4603
4696
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -4606,10 +4699,10 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
4606
4699
|
const lastEdge = chain.edges[chain.edges.length - 1];
|
|
4607
4700
|
const errs = lastEdge.signal?.errorCount ?? 0;
|
|
4608
4701
|
const culpritName = culprit.replace(/^service:/, "");
|
|
4609
|
-
return
|
|
4702
|
+
return import_types7.RootCauseResultSchema.parse({
|
|
4610
4703
|
rootCauseNode: culprit,
|
|
4611
4704
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
4612
|
-
traversalPath:
|
|
4705
|
+
traversalPath: path59,
|
|
4613
4706
|
edgeProvenances,
|
|
4614
4707
|
confidence,
|
|
4615
4708
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -4617,7 +4710,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
4617
4710
|
}
|
|
4618
4711
|
function getBlastRadius(graph, nodeId, maxDepth = BLAST_RADIUS_DEFAULT_DEPTH) {
|
|
4619
4712
|
if (!graph.hasNode(nodeId)) {
|
|
4620
|
-
return
|
|
4713
|
+
return import_types7.BlastRadiusResultSchema.parse({ origin: nodeId, affectedNodes: [], totalAffected: 0 });
|
|
4621
4714
|
}
|
|
4622
4715
|
const seen = /* @__PURE__ */ new Map();
|
|
4623
4716
|
const queue = [{ nodeId, distance: 0, path: [nodeId], pathEdges: [] }];
|
|
@@ -4650,7 +4743,7 @@ function getBlastRadius(graph, nodeId, maxDepth = BLAST_RADIUS_DEFAULT_DEPTH) {
|
|
|
4650
4743
|
const affectedNodes = [...seen.values()].sort(
|
|
4651
4744
|
(a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId)
|
|
4652
4745
|
);
|
|
4653
|
-
return
|
|
4746
|
+
return import_types7.BlastRadiusResultSchema.parse({
|
|
4654
4747
|
origin: nodeId,
|
|
4655
4748
|
affectedNodes,
|
|
4656
4749
|
totalAffected: affectedNodes.length
|
|
@@ -4660,7 +4753,7 @@ var TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH = 3;
|
|
|
4660
4753
|
var TRANSITIVE_DEPENDENCIES_MAX_DEPTH = 10;
|
|
4661
4754
|
function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH) {
|
|
4662
4755
|
if (!graph.hasNode(nodeId)) {
|
|
4663
|
-
return
|
|
4756
|
+
return import_types7.TransitiveDependenciesResultSchema.parse({
|
|
4664
4757
|
origin: nodeId,
|
|
4665
4758
|
depth,
|
|
4666
4759
|
dependencies: [],
|
|
@@ -4672,7 +4765,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
|
|
|
4672
4765
|
const enqueued = /* @__PURE__ */ new Set([nodeId]);
|
|
4673
4766
|
while (queue.length > 0) {
|
|
4674
4767
|
const frame = queue.shift();
|
|
4675
|
-
if (frame.distance > 0 && frame.edge && frame.edge.type !==
|
|
4768
|
+
if (frame.distance > 0 && frame.edge && frame.edge.type !== import_types7.EdgeType.CONTAINS) {
|
|
4676
4769
|
seen.set(frame.nodeId, {
|
|
4677
4770
|
nodeId: frame.nodeId,
|
|
4678
4771
|
distance: frame.distance,
|
|
@@ -4691,7 +4784,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
|
|
|
4691
4784
|
const dependencies = [...seen.values()].sort(
|
|
4692
4785
|
(a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId)
|
|
4693
4786
|
);
|
|
4694
|
-
return
|
|
4787
|
+
return import_types7.TransitiveDependenciesResultSchema.parse({
|
|
4695
4788
|
origin: nodeId,
|
|
4696
4789
|
depth,
|
|
4697
4790
|
dependencies,
|
|
@@ -4700,7 +4793,7 @@ function getTransitiveDependencies(graph, nodeId, depth = TRANSITIVE_DEPENDENCIE
|
|
|
4700
4793
|
}
|
|
4701
4794
|
function getObservedDependencies(graph, nodeId) {
|
|
4702
4795
|
if (!graph.hasNode(nodeId)) {
|
|
4703
|
-
return
|
|
4796
|
+
return import_types7.ObservedDependenciesResultSchema.parse({
|
|
4704
4797
|
origin: nodeId,
|
|
4705
4798
|
dependencies: [],
|
|
4706
4799
|
observed: false,
|
|
@@ -4710,12 +4803,12 @@ function getObservedDependencies(graph, nodeId) {
|
|
|
4710
4803
|
}
|
|
4711
4804
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4712
4805
|
const scope = [nodeId];
|
|
4713
|
-
if (attrs.type ===
|
|
4806
|
+
if (attrs.type === import_types7.NodeType.ServiceNode) {
|
|
4714
4807
|
for (const edgeId of graph.outboundEdges(nodeId)) {
|
|
4715
4808
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4716
|
-
if (e.type !==
|
|
4809
|
+
if (e.type !== import_types7.EdgeType.CONTAINS) continue;
|
|
4717
4810
|
const owned = graph.getNodeAttributes(e.target);
|
|
4718
|
-
if (owned.type ===
|
|
4811
|
+
if (owned.type === import_types7.NodeType.FileNode) scope.push(e.target);
|
|
4719
4812
|
}
|
|
4720
4813
|
}
|
|
4721
4814
|
const dependencies = [];
|
|
@@ -4724,13 +4817,13 @@ function getObservedDependencies(graph, nodeId) {
|
|
|
4724
4817
|
for (const src of scope) {
|
|
4725
4818
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
4726
4819
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4727
|
-
if (e.type ===
|
|
4728
|
-
if (e.provenance ===
|
|
4820
|
+
if (e.type === import_types7.EdgeType.CONTAINS) continue;
|
|
4821
|
+
if (e.provenance === import_types7.Provenance.OBSERVED) {
|
|
4729
4822
|
if (!seenEdge.has(e.id)) {
|
|
4730
4823
|
seenEdge.add(e.id);
|
|
4731
4824
|
dependencies.push(e);
|
|
4732
4825
|
}
|
|
4733
|
-
} else if (e.provenance ===
|
|
4826
|
+
} else if (e.provenance === import_types7.Provenance.EXTRACTED) {
|
|
4734
4827
|
hasExtractedOutbound = true;
|
|
4735
4828
|
}
|
|
4736
4829
|
}
|
|
@@ -4739,14 +4832,14 @@ function getObservedDependencies(graph, nodeId) {
|
|
|
4739
4832
|
for (const tgt of scope) {
|
|
4740
4833
|
for (const edgeId of graph.inboundEdges(tgt)) {
|
|
4741
4834
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4742
|
-
if (e.type ===
|
|
4743
|
-
if (e.provenance ===
|
|
4835
|
+
if (e.type === import_types7.EdgeType.CONTAINS) continue;
|
|
4836
|
+
if (e.provenance === import_types7.Provenance.OBSERVED) inboundObservedCount += 1;
|
|
4744
4837
|
}
|
|
4745
4838
|
}
|
|
4746
4839
|
dependencies.sort(
|
|
4747
4840
|
(a, b) => a.target.localeCompare(b.target) || a.source.localeCompare(b.source) || a.id.localeCompare(b.id)
|
|
4748
4841
|
);
|
|
4749
|
-
return
|
|
4842
|
+
return import_types7.ObservedDependenciesResultSchema.parse({
|
|
4750
4843
|
origin: nodeId,
|
|
4751
4844
|
dependencies,
|
|
4752
4845
|
observed: dependencies.length > 0 || inboundObservedCount > 0,
|
|
@@ -4760,34 +4853,34 @@ function bucketKey(source, target, type) {
|
|
|
4760
4853
|
return `${type}|${source}|${target}`;
|
|
4761
4854
|
}
|
|
4762
4855
|
function bucketSourceFor(graph, edge) {
|
|
4763
|
-
if (edge.type !==
|
|
4764
|
-
const parsed = (0,
|
|
4856
|
+
if (edge.type !== import_types8.EdgeType.CONNECTS_TO) return edge.source;
|
|
4857
|
+
const parsed = (0, import_types8.parseFileId)(edge.source);
|
|
4765
4858
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
4766
4859
|
const target = graph.getNodeAttributes(edge.target);
|
|
4767
|
-
if (target.type !==
|
|
4768
|
-
return (0,
|
|
4860
|
+
if (target.type !== import_types8.NodeType.DatabaseNode) return edge.source;
|
|
4861
|
+
return (0, import_types8.serviceId)(parsed.service);
|
|
4769
4862
|
}
|
|
4770
4863
|
function bucketEdges(graph) {
|
|
4771
4864
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
4772
4865
|
graph.forEachEdge((id, attrs) => {
|
|
4773
4866
|
const e = attrs;
|
|
4774
|
-
const parsed = (0,
|
|
4867
|
+
const parsed = (0, import_types8.parseEdgeId)(id);
|
|
4775
4868
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
4776
4869
|
const source = bucketSourceFor(graph, e);
|
|
4777
4870
|
const key = bucketKey(source, e.target, e.type);
|
|
4778
4871
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
4779
4872
|
switch (provenance) {
|
|
4780
|
-
case
|
|
4873
|
+
case import_types8.Provenance.EXTRACTED:
|
|
4781
4874
|
cur.extracted = e;
|
|
4782
4875
|
break;
|
|
4783
|
-
case
|
|
4876
|
+
case import_types8.Provenance.OBSERVED:
|
|
4784
4877
|
cur.observed = e;
|
|
4785
4878
|
break;
|
|
4786
|
-
case
|
|
4879
|
+
case import_types8.Provenance.INFERRED:
|
|
4787
4880
|
cur.inferred = e;
|
|
4788
4881
|
break;
|
|
4789
4882
|
default:
|
|
4790
|
-
if (e.provenance ===
|
|
4883
|
+
if (e.provenance === import_types8.Provenance.STALE) cur.stale = e;
|
|
4791
4884
|
}
|
|
4792
4885
|
buckets2.set(key, cur);
|
|
4793
4886
|
});
|
|
@@ -4796,17 +4889,17 @@ function bucketEdges(graph) {
|
|
|
4796
4889
|
function nodeIsFrontier(graph, nodeId) {
|
|
4797
4890
|
if (!graph.hasNode(nodeId)) return false;
|
|
4798
4891
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4799
|
-
return attrs.type ===
|
|
4892
|
+
return attrs.type === import_types8.NodeType.FrontierNode;
|
|
4800
4893
|
}
|
|
4801
4894
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
4802
4895
|
if (!graph.hasNode(nodeId)) return false;
|
|
4803
4896
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4804
|
-
return attrs.type ===
|
|
4897
|
+
return attrs.type === import_types8.NodeType.WebSocketChannelNode;
|
|
4805
4898
|
}
|
|
4806
4899
|
function nodeIsSymbol(graph, nodeId) {
|
|
4807
4900
|
if (!graph.hasNode(nodeId)) return false;
|
|
4808
4901
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4809
|
-
return attrs.type ===
|
|
4902
|
+
return attrs.type === import_types8.NodeType.SymbolNode;
|
|
4810
4903
|
}
|
|
4811
4904
|
function clampConfidence(n) {
|
|
4812
4905
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -4826,14 +4919,14 @@ function gradedConfidence(edge) {
|
|
|
4826
4919
|
return clampConfidence(confidenceForEdge(edge));
|
|
4827
4920
|
}
|
|
4828
4921
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4922
|
+
import_types8.EdgeType.CALLS,
|
|
4923
|
+
import_types8.EdgeType.CONNECTS_TO,
|
|
4924
|
+
import_types8.EdgeType.PUBLISHES_TO,
|
|
4925
|
+
import_types8.EdgeType.CONSUMES_FROM
|
|
4833
4926
|
]);
|
|
4834
4927
|
function detectMissingDivergences(graph, bucket) {
|
|
4835
4928
|
const out = [];
|
|
4836
|
-
if (bucket.type ===
|
|
4929
|
+
if (bucket.type === import_types8.EdgeType.CONTAINS) return out;
|
|
4837
4930
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
4838
4931
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
4839
4932
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
@@ -4875,7 +4968,7 @@ function declaredHostFor(svc) {
|
|
|
4875
4968
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
4876
4969
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4877
4970
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4878
|
-
if (e.type ===
|
|
4971
|
+
if (e.type === import_types8.EdgeType.CONFIGURED_BY && e.provenance === import_types8.Provenance.EXTRACTED) {
|
|
4879
4972
|
return true;
|
|
4880
4973
|
}
|
|
4881
4974
|
}
|
|
@@ -4888,10 +4981,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
4888
4981
|
const out = [];
|
|
4889
4982
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4890
4983
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4891
|
-
if (edge.type !==
|
|
4892
|
-
if (edge.provenance !==
|
|
4984
|
+
if (edge.type !== import_types8.EdgeType.CONNECTS_TO) continue;
|
|
4985
|
+
if (edge.provenance !== import_types8.Provenance.OBSERVED) continue;
|
|
4893
4986
|
const target = graph.getNodeAttributes(edge.target);
|
|
4894
|
-
if (target.type !==
|
|
4987
|
+
if (target.type !== import_types8.NodeType.DatabaseNode) continue;
|
|
4895
4988
|
const observedHost = target.host?.trim();
|
|
4896
4989
|
if (!observedHost) continue;
|
|
4897
4990
|
if (observedHost === declaredHost) continue;
|
|
@@ -4913,10 +5006,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
4913
5006
|
const deps = svc.dependencies ?? {};
|
|
4914
5007
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4915
5008
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4916
|
-
if (edge.type !==
|
|
4917
|
-
if (edge.provenance !==
|
|
5009
|
+
if (edge.type !== import_types8.EdgeType.CONNECTS_TO) continue;
|
|
5010
|
+
if (edge.provenance !== import_types8.Provenance.OBSERVED) continue;
|
|
4918
5011
|
const target = graph.getNodeAttributes(edge.target);
|
|
4919
|
-
if (target.type !==
|
|
5012
|
+
if (target.type !== import_types8.NodeType.DatabaseNode) continue;
|
|
4920
5013
|
for (const pair of compatPairs()) {
|
|
4921
5014
|
if (pair.engine !== target.engine) continue;
|
|
4922
5015
|
const declared = deps[pair.driver];
|
|
@@ -4966,6 +5059,44 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
4966
5059
|
}
|
|
4967
5060
|
return out;
|
|
4968
5061
|
}
|
|
5062
|
+
var RECOMMENDATION_COLUMN_MISSING_OBSERVED = "Verify the column is exercised in production; a migration that renamed or dropped it may have left a writer declaring the old name.";
|
|
5063
|
+
var RECOMMENDATION_COLUMN_MISSING_EXTRACTED = "The schema or migration is likely behind the code \u2014 production writes a column the declared schema does not carry. Check for a field rename that updated the query but not the model.";
|
|
5064
|
+
function detectColumnDrift(node) {
|
|
5065
|
+
const columns = node.columns;
|
|
5066
|
+
if (!columns || columns.length === 0) return [];
|
|
5067
|
+
const anyDeclared = columns.some(columnIsDeclared);
|
|
5068
|
+
const anyObserved = columns.some(columnIsObserved);
|
|
5069
|
+
if (!anyDeclared || !anyObserved) return [];
|
|
5070
|
+
const out = [];
|
|
5071
|
+
for (const col of columns) {
|
|
5072
|
+
const declared = columnIsDeclared(col);
|
|
5073
|
+
const observed = columnIsObserved(col);
|
|
5074
|
+
if (declared && !observed) {
|
|
5075
|
+
out.push({
|
|
5076
|
+
type: "missing-observed",
|
|
5077
|
+
source: node.id,
|
|
5078
|
+
target: node.id,
|
|
5079
|
+
table: node.id,
|
|
5080
|
+
column: col.name,
|
|
5081
|
+
confidence: clampConfidence(col.confidence),
|
|
5082
|
+
reason: `Schema declares column ${node.name}.${col.name} but no production statement has touched it.`,
|
|
5083
|
+
recommendation: RECOMMENDATION_COLUMN_MISSING_OBSERVED
|
|
5084
|
+
});
|
|
5085
|
+
} else if (observed && !declared) {
|
|
5086
|
+
out.push({
|
|
5087
|
+
type: "missing-extracted",
|
|
5088
|
+
source: node.id,
|
|
5089
|
+
target: node.id,
|
|
5090
|
+
table: node.id,
|
|
5091
|
+
column: col.name,
|
|
5092
|
+
confidence: clampConfidence(col.confidence),
|
|
5093
|
+
reason: `Production touched column ${node.name}.${col.name} but the schema does not declare it.`,
|
|
5094
|
+
recommendation: RECOMMENDATION_COLUMN_MISSING_EXTRACTED
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
return out;
|
|
5099
|
+
}
|
|
4969
5100
|
function involvesNode(d, nodeId) {
|
|
4970
5101
|
return d.source === nodeId || d.target === nodeId;
|
|
4971
5102
|
}
|
|
@@ -4975,7 +5106,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
4975
5106
|
for (const d of all) {
|
|
4976
5107
|
if (d.type !== "host-mismatch") continue;
|
|
4977
5108
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
4978
|
-
declaredHalf.add((0,
|
|
5109
|
+
declaredHalf.add((0, import_types8.databaseId)(d.extractedHost));
|
|
4979
5110
|
}
|
|
4980
5111
|
if (observedHalf.size === 0) return all;
|
|
4981
5112
|
return all.filter((d) => {
|
|
@@ -4994,10 +5125,15 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4994
5125
|
}
|
|
4995
5126
|
graph.forEachNode((nodeId, attrs) => {
|
|
4996
5127
|
const n = attrs;
|
|
4997
|
-
if (n.type
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5128
|
+
if (n.type === import_types8.NodeType.ServiceNode) {
|
|
5129
|
+
const svc = n;
|
|
5130
|
+
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
5131
|
+
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
5132
|
+
return;
|
|
5133
|
+
}
|
|
5134
|
+
if (n.type === import_types8.NodeType.InfraNode && n.kind === "sql-table") {
|
|
5135
|
+
for (const d of detectColumnDrift(n)) all.push(d);
|
|
5136
|
+
}
|
|
5001
5137
|
});
|
|
5002
5138
|
const reconciled = suppressHostMismatchHalves(all);
|
|
5003
5139
|
let filtered = reconciled;
|
|
@@ -5026,9 +5162,12 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5026
5162
|
if (lead !== 0) return lead;
|
|
5027
5163
|
if (a.type !== b.type) return a.type.localeCompare(b.type);
|
|
5028
5164
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
5029
|
-
return a.target.localeCompare(b.target);
|
|
5165
|
+
if (a.target !== b.target) return a.target.localeCompare(b.target);
|
|
5166
|
+
const ac = "column" in a && a.column ? a.column : "";
|
|
5167
|
+
const bc = "column" in b && b.column ? b.column : "";
|
|
5168
|
+
return ac.localeCompare(bc);
|
|
5030
5169
|
});
|
|
5031
|
-
return
|
|
5170
|
+
return import_types8.DivergenceResultSchema.parse({
|
|
5032
5171
|
divergences: filtered,
|
|
5033
5172
|
totalAffected: filtered.length,
|
|
5034
5173
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5092,7 +5231,7 @@ var import_node_fs12 = require("fs");
|
|
|
5092
5231
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
5093
5232
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
5094
5233
|
var import_minimatch2 = require("minimatch");
|
|
5095
|
-
var
|
|
5234
|
+
var import_types10 = require("@neat.is/types");
|
|
5096
5235
|
|
|
5097
5236
|
// src/extract/python.ts
|
|
5098
5237
|
init_cjs_shims();
|
|
@@ -5165,7 +5304,7 @@ function pythonToPackage(service) {
|
|
|
5165
5304
|
init_cjs_shims();
|
|
5166
5305
|
var import_node_fs10 = require("fs");
|
|
5167
5306
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
5168
|
-
var
|
|
5307
|
+
var import_types9 = require("@neat.is/types");
|
|
5169
5308
|
function parseGoMod(source) {
|
|
5170
5309
|
const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
|
|
5171
5310
|
if (!module2) return null;
|
|
@@ -5193,8 +5332,8 @@ async function discoverGoService(scanPath, dir) {
|
|
|
5193
5332
|
const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
|
|
5194
5333
|
const pkg = { name, dependencies: mod.dependencies };
|
|
5195
5334
|
const node = {
|
|
5196
|
-
id: (0,
|
|
5197
|
-
type:
|
|
5335
|
+
id: (0, import_types9.serviceId)(name),
|
|
5336
|
+
type: import_types9.NodeType.ServiceNode,
|
|
5198
5337
|
name,
|
|
5199
5338
|
language: "go",
|
|
5200
5339
|
dependencies: mod.dependencies,
|
|
@@ -5380,8 +5519,8 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
5380
5519
|
const framework = detectJsFramework(pkg);
|
|
5381
5520
|
const language = await detectJsServiceLanguage(dir, pkg);
|
|
5382
5521
|
const node = {
|
|
5383
|
-
id: (0,
|
|
5384
|
-
type:
|
|
5522
|
+
id: (0, import_types10.serviceId)(pkg.name),
|
|
5523
|
+
type: import_types10.NodeType.ServiceNode,
|
|
5385
5524
|
name: pkg.name,
|
|
5386
5525
|
language,
|
|
5387
5526
|
version: pkg.version,
|
|
@@ -5397,8 +5536,8 @@ async function discoverPyService(scanPath, dir) {
|
|
|
5397
5536
|
if (!py) return null;
|
|
5398
5537
|
const pkg = pythonToPackage(py);
|
|
5399
5538
|
const node = {
|
|
5400
|
-
id: (0,
|
|
5401
|
-
type:
|
|
5539
|
+
id: (0, import_types10.serviceId)(py.name),
|
|
5540
|
+
type: import_types10.NodeType.ServiceNode,
|
|
5402
5541
|
name: py.name,
|
|
5403
5542
|
language: "python",
|
|
5404
5543
|
version: py.version,
|
|
@@ -5494,7 +5633,7 @@ init_cjs_shims();
|
|
|
5494
5633
|
var import_node_path14 = __toESM(require("path"), 1);
|
|
5495
5634
|
var import_node_fs13 = require("fs");
|
|
5496
5635
|
var import_yaml2 = require("yaml");
|
|
5497
|
-
var
|
|
5636
|
+
var import_types11 = require("@neat.is/types");
|
|
5498
5637
|
var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
5499
5638
|
"Service",
|
|
5500
5639
|
"Deployment",
|
|
@@ -5504,7 +5643,7 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
5504
5643
|
function addAliases(graph, serviceId7, candidates) {
|
|
5505
5644
|
if (!graph.hasNode(serviceId7)) return;
|
|
5506
5645
|
const node = graph.getNodeAttributes(serviceId7);
|
|
5507
|
-
if (node.type !==
|
|
5646
|
+
if (node.type !== import_types11.NodeType.ServiceNode) return;
|
|
5508
5647
|
const set = new Set(node.aliases ?? []);
|
|
5509
5648
|
for (const c of candidates) {
|
|
5510
5649
|
if (!c) continue;
|
|
@@ -5685,7 +5824,7 @@ var import_node_path16 = __toESM(require("path"), 1);
|
|
|
5685
5824
|
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
5686
5825
|
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5687
5826
|
var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
|
|
5688
|
-
var
|
|
5827
|
+
var import_types12 = require("@neat.is/types");
|
|
5689
5828
|
var PARSE_CHUNK2 = 16384;
|
|
5690
5829
|
var GRAMMAR_BY_EXT = {
|
|
5691
5830
|
".ts": import_tree_sitter_typescript.default.typescript,
|
|
@@ -5776,7 +5915,7 @@ function disambiguate(defs) {
|
|
|
5776
5915
|
}
|
|
5777
5916
|
async function addSymbols(graph, services) {
|
|
5778
5917
|
const parsers = /* @__PURE__ */ new Map();
|
|
5779
|
-
const
|
|
5918
|
+
const parserForExt2 = (ext) => {
|
|
5780
5919
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
5781
5920
|
if (!grammar) return null;
|
|
5782
5921
|
let parser = parsers.get(ext);
|
|
@@ -5792,7 +5931,7 @@ async function addSymbols(graph, services) {
|
|
|
5792
5931
|
for (const service of services) {
|
|
5793
5932
|
const files = await loadSourceFiles(service.dir);
|
|
5794
5933
|
for (const file of files) {
|
|
5795
|
-
const parser =
|
|
5934
|
+
const parser = parserForExt2(import_node_path16.default.extname(file.path));
|
|
5796
5935
|
if (!parser) continue;
|
|
5797
5936
|
const relPath = toPosix(import_node_path16.default.relative(service.dir, file.path));
|
|
5798
5937
|
let defs;
|
|
@@ -5813,11 +5952,11 @@ async function addSymbols(graph, services) {
|
|
|
5813
5952
|
nodesAdded += fn;
|
|
5814
5953
|
edgesAdded += fe;
|
|
5815
5954
|
for (const { def, disambiguator } of disambiguate(defs)) {
|
|
5816
|
-
const sid = (0,
|
|
5955
|
+
const sid = (0, import_types12.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
|
|
5817
5956
|
if (!graph.hasNode(sid)) {
|
|
5818
5957
|
const node = {
|
|
5819
5958
|
id: sid,
|
|
5820
|
-
type:
|
|
5959
|
+
type: import_types12.NodeType.SymbolNode,
|
|
5821
5960
|
kind: def.kind,
|
|
5822
5961
|
qualname: def.qualname,
|
|
5823
5962
|
span: { startLine: def.startLine, endLine: def.endLine },
|
|
@@ -5828,15 +5967,15 @@ async function addSymbols(graph, services) {
|
|
|
5828
5967
|
graph.addNode(sid, node);
|
|
5829
5968
|
nodesAdded++;
|
|
5830
5969
|
}
|
|
5831
|
-
const containsId = (0,
|
|
5970
|
+
const containsId = (0, import_types12.extractedEdgeId)(fileNodeId, sid, import_types12.EdgeType.CONTAINS);
|
|
5832
5971
|
if (!graph.hasEdge(containsId)) {
|
|
5833
5972
|
const edge = {
|
|
5834
5973
|
id: containsId,
|
|
5835
5974
|
source: fileNodeId,
|
|
5836
5975
|
target: sid,
|
|
5837
|
-
type:
|
|
5838
|
-
provenance:
|
|
5839
|
-
confidence: (0,
|
|
5976
|
+
type: import_types12.EdgeType.CONTAINS,
|
|
5977
|
+
provenance: import_types12.Provenance.EXTRACTED,
|
|
5978
|
+
confidence: (0, import_types12.confidenceForExtracted)("structural"),
|
|
5840
5979
|
evidence: {
|
|
5841
5980
|
file: relPath,
|
|
5842
5981
|
line: def.startLine,
|
|
@@ -5856,7 +5995,7 @@ async function addSymbols(graph, services) {
|
|
|
5856
5995
|
init_cjs_shims();
|
|
5857
5996
|
var import_node_path18 = __toESM(require("path"), 1);
|
|
5858
5997
|
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
5859
|
-
var
|
|
5998
|
+
var import_types14 = require("@neat.is/types");
|
|
5860
5999
|
|
|
5861
6000
|
// src/extract/imports.ts
|
|
5862
6001
|
init_cjs_shims();
|
|
@@ -5866,7 +6005,7 @@ var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
|
5866
6005
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5867
6006
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
5868
6007
|
var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
|
|
5869
|
-
var
|
|
6008
|
+
var import_types13 = require("@neat.is/types");
|
|
5870
6009
|
var PARSE_CHUNK3 = 16384;
|
|
5871
6010
|
function parseSource3(parser, source) {
|
|
5872
6011
|
return parser.parse(
|
|
@@ -6140,17 +6279,17 @@ async function resolveGoImport(specifier, modulePath, serviceDir) {
|
|
|
6140
6279
|
return toPosix(import_node_path17.default.relative(serviceDir, candidates[0]));
|
|
6141
6280
|
}
|
|
6142
6281
|
function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
|
|
6143
|
-
const importeeFileId = (0,
|
|
6282
|
+
const importeeFileId = (0, import_types13.fileId)(serviceName, importeeRelPath);
|
|
6144
6283
|
if (!graph.hasNode(importeeFileId)) return 0;
|
|
6145
|
-
const edgeId = (0,
|
|
6284
|
+
const edgeId = (0, import_types13.extractedEdgeId)(importerFileId, importeeFileId, import_types13.EdgeType.IMPORTS);
|
|
6146
6285
|
if (graph.hasEdge(edgeId)) return 0;
|
|
6147
6286
|
const edge = {
|
|
6148
6287
|
id: edgeId,
|
|
6149
6288
|
source: importerFileId,
|
|
6150
6289
|
target: importeeFileId,
|
|
6151
|
-
type:
|
|
6152
|
-
provenance:
|
|
6153
|
-
confidence: (0,
|
|
6290
|
+
type: import_types13.EdgeType.IMPORTS,
|
|
6291
|
+
provenance: import_types13.Provenance.EXTRACTED,
|
|
6292
|
+
confidence: (0, import_types13.confidenceForExtracted)("structural"),
|
|
6154
6293
|
evidence: { file: importerRelPath, line, snippet: snippet2 }
|
|
6155
6294
|
};
|
|
6156
6295
|
graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
|
|
@@ -6167,7 +6306,7 @@ async function addImports(graph, services) {
|
|
|
6167
6306
|
for (const file of files) {
|
|
6168
6307
|
if (isTestPath(file.path)) continue;
|
|
6169
6308
|
const relFile = toPosix(import_node_path17.default.relative(service.dir, file.path));
|
|
6170
|
-
const importerFileId = (0,
|
|
6309
|
+
const importerFileId = (0, import_types13.fileId)(service.pkg.name, relFile);
|
|
6171
6310
|
const isPython = import_node_path17.default.extname(file.path) === ".py";
|
|
6172
6311
|
const isGo = import_node_path17.default.extname(file.path) === ".go";
|
|
6173
6312
|
if (isGo) {
|
|
@@ -6328,7 +6467,7 @@ function stringInner(node) {
|
|
|
6328
6467
|
}
|
|
6329
6468
|
async function addSymbolEdges(graph, services) {
|
|
6330
6469
|
const parsers = /* @__PURE__ */ new Map();
|
|
6331
|
-
const
|
|
6470
|
+
const parserForExt2 = (ext) => {
|
|
6332
6471
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6333
6472
|
if (!grammar) return null;
|
|
6334
6473
|
let parser = parsers.get(ext);
|
|
@@ -6345,7 +6484,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6345
6484
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
6346
6485
|
const files = await loadSourceFiles(service.dir);
|
|
6347
6486
|
for (const file of files) {
|
|
6348
|
-
const parser =
|
|
6487
|
+
const parser = parserForExt2(import_node_path18.default.extname(file.path));
|
|
6349
6488
|
if (!parser) continue;
|
|
6350
6489
|
const relPath = toPosix(import_node_path18.default.relative(service.dir, file.path));
|
|
6351
6490
|
const fileDir = import_node_path18.default.dirname(file.path);
|
|
@@ -6358,7 +6497,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6358
6497
|
}
|
|
6359
6498
|
const disambiguated = disambiguate(collectSymbolDefs(root));
|
|
6360
6499
|
const locals = disambiguated.map(({ def, disambiguator }) => ({
|
|
6361
|
-
sid: (0,
|
|
6500
|
+
sid: (0, import_types14.symbolId)(serviceName, relPath, def.qualname, disambiguator),
|
|
6362
6501
|
qualname: def.qualname,
|
|
6363
6502
|
kind: def.kind,
|
|
6364
6503
|
startLine: def.startLine,
|
|
@@ -6387,10 +6526,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
6387
6526
|
if (local) return local.kind === wantKind ? local.sid : null;
|
|
6388
6527
|
const imp = resolvedImports.get(name);
|
|
6389
6528
|
if (imp) {
|
|
6390
|
-
const candidate = (0,
|
|
6529
|
+
const candidate = (0, import_types14.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
|
|
6391
6530
|
if (graph.hasNode(candidate)) {
|
|
6392
6531
|
const node = graph.getNodeAttributes(candidate);
|
|
6393
|
-
if (node.type ===
|
|
6532
|
+
if (node.type === import_types14.NodeType.SymbolNode && node.kind === wantKind) return candidate;
|
|
6394
6533
|
}
|
|
6395
6534
|
}
|
|
6396
6535
|
return null;
|
|
@@ -6417,7 +6556,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6417
6556
|
sourceSid: self.sid,
|
|
6418
6557
|
targetName: ext.name,
|
|
6419
6558
|
wantKind: "class",
|
|
6420
|
-
edgeType:
|
|
6559
|
+
edgeType: import_types14.EdgeType.INHERITS,
|
|
6421
6560
|
line: ext.line
|
|
6422
6561
|
});
|
|
6423
6562
|
}
|
|
@@ -6426,7 +6565,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6426
6565
|
sourceSid: self.sid,
|
|
6427
6566
|
targetName: impl.name,
|
|
6428
6567
|
wantKind: "class",
|
|
6429
|
-
edgeType:
|
|
6568
|
+
edgeType: import_types14.EdgeType.IMPLEMENTS,
|
|
6430
6569
|
line: impl.line
|
|
6431
6570
|
});
|
|
6432
6571
|
}
|
|
@@ -6442,7 +6581,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6442
6581
|
sourceSid: caller.sid,
|
|
6443
6582
|
targetName: fn.text,
|
|
6444
6583
|
wantKind: "function",
|
|
6445
|
-
edgeType:
|
|
6584
|
+
edgeType: import_types14.EdgeType.CALLS,
|
|
6446
6585
|
line
|
|
6447
6586
|
});
|
|
6448
6587
|
}
|
|
@@ -6458,15 +6597,15 @@ async function addSymbolEdges(graph, services) {
|
|
|
6458
6597
|
const targetSid = resolveTarget(req.targetName, req.wantKind);
|
|
6459
6598
|
if (!targetSid) continue;
|
|
6460
6599
|
if (targetSid === req.sourceSid) continue;
|
|
6461
|
-
const edgeId = (0,
|
|
6600
|
+
const edgeId = (0, import_types14.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
|
|
6462
6601
|
if (graph.hasEdge(edgeId)) continue;
|
|
6463
6602
|
const edge = {
|
|
6464
6603
|
id: edgeId,
|
|
6465
6604
|
source: req.sourceSid,
|
|
6466
6605
|
target: targetSid,
|
|
6467
6606
|
type: req.edgeType,
|
|
6468
|
-
provenance:
|
|
6469
|
-
confidence: (0,
|
|
6607
|
+
provenance: import_types14.Provenance.EXTRACTED,
|
|
6608
|
+
confidence: (0, import_types14.confidenceForExtracted)("structural"),
|
|
6470
6609
|
evidence: {
|
|
6471
6610
|
file: relPath,
|
|
6472
6611
|
line: req.line,
|
|
@@ -6484,7 +6623,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6484
6623
|
// src/extract/databases/index.ts
|
|
6485
6624
|
init_cjs_shims();
|
|
6486
6625
|
var import_node_path26 = __toESM(require("path"), 1);
|
|
6487
|
-
var
|
|
6626
|
+
var import_types15 = require("@neat.is/types");
|
|
6488
6627
|
|
|
6489
6628
|
// src/extract/databases/db-config-yaml.ts
|
|
6490
6629
|
init_cjs_shims();
|
|
@@ -6988,8 +7127,8 @@ function compatibleDriversFor(engine) {
|
|
|
6988
7127
|
}
|
|
6989
7128
|
function toDatabaseNode(config) {
|
|
6990
7129
|
return {
|
|
6991
|
-
id: (0,
|
|
6992
|
-
type:
|
|
7130
|
+
id: (0, import_types15.databaseId)(config.host),
|
|
7131
|
+
type: import_types15.NodeType.DatabaseNode,
|
|
6993
7132
|
name: config.database || config.host,
|
|
6994
7133
|
engine: config.engine,
|
|
6995
7134
|
engineVersion: config.engineVersion,
|
|
@@ -7130,12 +7269,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7130
7269
|
edgesAdded += fe;
|
|
7131
7270
|
const evidenceFile = toPosix(import_node_path26.default.relative(scanPath, config.sourceFile));
|
|
7132
7271
|
const edge = {
|
|
7133
|
-
id: (0,
|
|
7272
|
+
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types15.EdgeType.CONNECTS_TO),
|
|
7134
7273
|
source: fileNodeId,
|
|
7135
7274
|
target: dbNode.id,
|
|
7136
|
-
type:
|
|
7137
|
-
provenance:
|
|
7138
|
-
confidence: (0,
|
|
7275
|
+
type: import_types15.EdgeType.CONNECTS_TO,
|
|
7276
|
+
provenance: import_types15.Provenance.EXTRACTED,
|
|
7277
|
+
confidence: (0, import_types15.confidenceForExtracted)("structural"),
|
|
7139
7278
|
evidence: { file: evidenceFile }
|
|
7140
7279
|
};
|
|
7141
7280
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -7147,11 +7286,11 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7147
7286
|
const primary = allConfigs[0];
|
|
7148
7287
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
7149
7288
|
const relPath = import_node_path26.default.relative(scanPath, primary.sourceFile);
|
|
7150
|
-
const cfgId = (0,
|
|
7289
|
+
const cfgId = (0, import_types15.configId)(relPath);
|
|
7151
7290
|
if (!graph.hasNode(cfgId)) {
|
|
7152
7291
|
const cfgNode = {
|
|
7153
7292
|
id: cfgId,
|
|
7154
|
-
type:
|
|
7293
|
+
type: import_types15.NodeType.ConfigNode,
|
|
7155
7294
|
name: import_node_path26.default.basename(primary.sourceFile),
|
|
7156
7295
|
path: relPath,
|
|
7157
7296
|
fileType: isConfigFile(import_node_path26.default.basename(primary.sourceFile)).fileType || "config"
|
|
@@ -7160,12 +7299,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7160
7299
|
nodesAdded++;
|
|
7161
7300
|
}
|
|
7162
7301
|
const cfgEdge = {
|
|
7163
|
-
id: (0,
|
|
7302
|
+
id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types15.EdgeType.CONFIGURED_BY),
|
|
7164
7303
|
source: service.node.id,
|
|
7165
7304
|
target: cfgId,
|
|
7166
|
-
type:
|
|
7167
|
-
provenance:
|
|
7168
|
-
confidence: (0,
|
|
7305
|
+
type: import_types15.EdgeType.CONFIGURED_BY,
|
|
7306
|
+
provenance: import_types15.Provenance.EXTRACTED,
|
|
7307
|
+
confidence: (0, import_types15.confidenceForExtracted)("structural"),
|
|
7169
7308
|
evidence: { file: toPosix(relPath) }
|
|
7170
7309
|
};
|
|
7171
7310
|
if (!graph.hasEdge(cfgEdge.id)) {
|
|
@@ -7197,7 +7336,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
7197
7336
|
init_cjs_shims();
|
|
7198
7337
|
var import_node_fs17 = require("fs");
|
|
7199
7338
|
var import_node_path27 = __toESM(require("path"), 1);
|
|
7200
|
-
var
|
|
7339
|
+
var import_types16 = require("@neat.is/types");
|
|
7201
7340
|
async function walkConfigFiles(dir) {
|
|
7202
7341
|
const out = [];
|
|
7203
7342
|
async function walk6(current) {
|
|
@@ -7224,8 +7363,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
7224
7363
|
for (const file of configFiles) {
|
|
7225
7364
|
const relPath = import_node_path27.default.relative(scanPath, file);
|
|
7226
7365
|
const node = {
|
|
7227
|
-
id: (0,
|
|
7228
|
-
type:
|
|
7366
|
+
id: (0, import_types16.configId)(relPath),
|
|
7367
|
+
type: import_types16.NodeType.ConfigNode,
|
|
7229
7368
|
name: import_node_path27.default.basename(file),
|
|
7230
7369
|
path: relPath,
|
|
7231
7370
|
fileType: isConfigFile(import_node_path27.default.basename(file)).fileType
|
|
@@ -7244,12 +7383,12 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
7244
7383
|
nodesAdded += fn;
|
|
7245
7384
|
edgesAdded += fe;
|
|
7246
7385
|
const edge = {
|
|
7247
|
-
id: (0,
|
|
7386
|
+
id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types16.EdgeType.CONFIGURED_BY),
|
|
7248
7387
|
source: fileNodeId,
|
|
7249
7388
|
target: node.id,
|
|
7250
|
-
type:
|
|
7251
|
-
provenance:
|
|
7252
|
-
confidence: (0,
|
|
7389
|
+
type: import_types16.EdgeType.CONFIGURED_BY,
|
|
7390
|
+
provenance: import_types16.Provenance.EXTRACTED,
|
|
7391
|
+
confidence: (0, import_types16.confidenceForExtracted)("structural"),
|
|
7253
7392
|
evidence: { file: relPath.split(import_node_path27.default.sep).join("/") }
|
|
7254
7393
|
};
|
|
7255
7394
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -7265,7 +7404,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
7265
7404
|
init_cjs_shims();
|
|
7266
7405
|
var import_node_fs18 = require("fs");
|
|
7267
7406
|
var import_node_path28 = __toESM(require("path"), 1);
|
|
7268
|
-
var
|
|
7407
|
+
var import_types17 = require("@neat.is/types");
|
|
7269
7408
|
var PROTO_EXTENSION = ".proto";
|
|
7270
7409
|
function packageOf(content) {
|
|
7271
7410
|
const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
|
|
@@ -7343,11 +7482,11 @@ async function addGrpcMethods(graph, services) {
|
|
|
7343
7482
|
}
|
|
7344
7483
|
if (methods.length === 0) continue;
|
|
7345
7484
|
for (const method of methods) {
|
|
7346
|
-
const mid = (0,
|
|
7485
|
+
const mid = (0, import_types17.grpcMethodId)(method.rpcService, method.rpcMethod);
|
|
7347
7486
|
if (!graph.hasNode(mid)) {
|
|
7348
7487
|
const node = {
|
|
7349
7488
|
id: mid,
|
|
7350
|
-
type:
|
|
7489
|
+
type: import_types17.NodeType.GrpcMethodNode,
|
|
7351
7490
|
name: `${method.rpcService}/${method.rpcMethod}`,
|
|
7352
7491
|
rpcService: method.rpcService,
|
|
7353
7492
|
rpcMethod: method.rpcMethod,
|
|
@@ -7358,15 +7497,15 @@ async function addGrpcMethods(graph, services) {
|
|
|
7358
7497
|
graph.addNode(mid, node);
|
|
7359
7498
|
nodesAdded++;
|
|
7360
7499
|
}
|
|
7361
|
-
const containsId = (0,
|
|
7500
|
+
const containsId = (0, import_types17.extractedEdgeId)(service.node.id, mid, import_types17.EdgeType.CONTAINS);
|
|
7362
7501
|
if (!graph.hasEdge(containsId)) {
|
|
7363
7502
|
const edge = {
|
|
7364
7503
|
id: containsId,
|
|
7365
7504
|
source: service.node.id,
|
|
7366
7505
|
target: mid,
|
|
7367
|
-
type:
|
|
7368
|
-
provenance:
|
|
7369
|
-
confidence: (0,
|
|
7506
|
+
type: import_types17.EdgeType.CONTAINS,
|
|
7507
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
7508
|
+
confidence: (0, import_types17.confidenceForExtracted)("structural"),
|
|
7370
7509
|
evidence: {
|
|
7371
7510
|
file: relFile,
|
|
7372
7511
|
line: method.line,
|
|
@@ -7384,7 +7523,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
7384
7523
|
|
|
7385
7524
|
// src/extract/calls/index.ts
|
|
7386
7525
|
init_cjs_shims();
|
|
7387
|
-
var
|
|
7526
|
+
var import_types30 = require("@neat.is/types");
|
|
7388
7527
|
|
|
7389
7528
|
// src/extract/calls/http.ts
|
|
7390
7529
|
init_cjs_shims();
|
|
@@ -7392,7 +7531,7 @@ var import_node_path29 = __toESM(require("path"), 1);
|
|
|
7392
7531
|
var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
|
|
7393
7532
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
7394
7533
|
var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
|
|
7395
|
-
var
|
|
7534
|
+
var import_types18 = require("@neat.is/types");
|
|
7396
7535
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
7397
7536
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
7398
7537
|
function isInsideJsxExternalLink(node) {
|
|
@@ -7478,7 +7617,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
7478
7617
|
const dedupKey = `${relFile}|${targetId}`;
|
|
7479
7618
|
if (seen.has(dedupKey)) continue;
|
|
7480
7619
|
seen.add(dedupKey);
|
|
7481
|
-
const confidence = (0,
|
|
7620
|
+
const confidence = (0, import_types18.confidenceForExtracted)("url-literal-service-target");
|
|
7482
7621
|
const ev = {
|
|
7483
7622
|
file: relFile,
|
|
7484
7623
|
line: site.line,
|
|
@@ -7492,25 +7631,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
7492
7631
|
);
|
|
7493
7632
|
nodesAdded += n;
|
|
7494
7633
|
edgesAdded += e;
|
|
7495
|
-
if (!(0,
|
|
7634
|
+
if (!(0, import_types18.passesExtractedFloor)(confidence)) {
|
|
7496
7635
|
noteExtractedDropped({
|
|
7497
7636
|
source: fileNodeId,
|
|
7498
7637
|
target: targetId,
|
|
7499
|
-
type:
|
|
7638
|
+
type: import_types18.EdgeType.CALLS,
|
|
7500
7639
|
confidence,
|
|
7501
7640
|
confidenceKind: "url-literal-service-target",
|
|
7502
7641
|
evidence: ev
|
|
7503
7642
|
});
|
|
7504
7643
|
continue;
|
|
7505
7644
|
}
|
|
7506
|
-
const edgeId = (0,
|
|
7645
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types18.EdgeType.CALLS);
|
|
7507
7646
|
if (!graph.hasEdge(edgeId)) {
|
|
7508
7647
|
const edge = {
|
|
7509
7648
|
id: edgeId,
|
|
7510
7649
|
source: fileNodeId,
|
|
7511
7650
|
target: targetId,
|
|
7512
|
-
type:
|
|
7513
|
-
provenance:
|
|
7651
|
+
type: import_types18.EdgeType.CALLS,
|
|
7652
|
+
provenance: import_types18.Provenance.EXTRACTED,
|
|
7514
7653
|
confidence,
|
|
7515
7654
|
evidence: ev
|
|
7516
7655
|
};
|
|
@@ -7528,7 +7667,7 @@ init_cjs_shims();
|
|
|
7528
7667
|
var import_node_path30 = __toESM(require("path"), 1);
|
|
7529
7668
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
7530
7669
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
7531
|
-
var
|
|
7670
|
+
var import_types19 = require("@neat.is/types");
|
|
7532
7671
|
var PARSE_CHUNK5 = 16384;
|
|
7533
7672
|
function parseSource5(parser, source) {
|
|
7534
7673
|
return parser.parse(
|
|
@@ -7693,9 +7832,9 @@ function buildRouteIndex(graph) {
|
|
|
7693
7832
|
const index = /* @__PURE__ */ new Map();
|
|
7694
7833
|
graph.forEachNode((_id, attrs) => {
|
|
7695
7834
|
const node = attrs;
|
|
7696
|
-
if (node.type !==
|
|
7835
|
+
if (node.type !== import_types19.NodeType.RouteNode) return;
|
|
7697
7836
|
const route = attrs;
|
|
7698
|
-
const owner = (0,
|
|
7837
|
+
const owner = (0, import_types19.serviceId)(route.service);
|
|
7699
7838
|
const entry = {
|
|
7700
7839
|
method: route.method.toUpperCase(),
|
|
7701
7840
|
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
@@ -7753,7 +7892,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
7753
7892
|
);
|
|
7754
7893
|
nodesAdded += n;
|
|
7755
7894
|
edgesAdded += e;
|
|
7756
|
-
const confidence = (0,
|
|
7895
|
+
const confidence = (0, import_types19.confidenceForExtracted)("verified-call-site");
|
|
7757
7896
|
const ev = {
|
|
7758
7897
|
file: relFile,
|
|
7759
7898
|
line: site.line,
|
|
@@ -7761,25 +7900,25 @@ async function addRouteCallEdges(graph, services) {
|
|
|
7761
7900
|
method: site.method ?? match.method,
|
|
7762
7901
|
pathTemplate: site.pathTemplate
|
|
7763
7902
|
};
|
|
7764
|
-
if (!(0,
|
|
7903
|
+
if (!(0, import_types19.passesExtractedFloor)(confidence)) {
|
|
7765
7904
|
noteExtractedDropped({
|
|
7766
7905
|
source: fileNodeId,
|
|
7767
7906
|
target: match.routeNodeId,
|
|
7768
|
-
type:
|
|
7907
|
+
type: import_types19.EdgeType.CALLS,
|
|
7769
7908
|
confidence,
|
|
7770
7909
|
confidenceKind: "verified-call-site",
|
|
7771
7910
|
evidence: ev
|
|
7772
7911
|
});
|
|
7773
7912
|
continue;
|
|
7774
7913
|
}
|
|
7775
|
-
const edgeId = (0,
|
|
7914
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types19.EdgeType.CALLS);
|
|
7776
7915
|
if (!graph.hasEdge(edgeId)) {
|
|
7777
7916
|
const edge = {
|
|
7778
7917
|
id: edgeId,
|
|
7779
7918
|
source: fileNodeId,
|
|
7780
7919
|
target: match.routeNodeId,
|
|
7781
|
-
type:
|
|
7782
|
-
provenance:
|
|
7920
|
+
type: import_types19.EdgeType.CALLS,
|
|
7921
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
7783
7922
|
confidence,
|
|
7784
7923
|
evidence: ev
|
|
7785
7924
|
};
|
|
@@ -7795,7 +7934,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
7795
7934
|
// src/extract/calls/kafka.ts
|
|
7796
7935
|
init_cjs_shims();
|
|
7797
7936
|
var import_node_path31 = __toESM(require("path"), 1);
|
|
7798
|
-
var
|
|
7937
|
+
var import_types20 = require("@neat.is/types");
|
|
7799
7938
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
7800
7939
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
7801
7940
|
function findAll(re, text) {
|
|
@@ -7816,7 +7955,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
7816
7955
|
seen.add(key);
|
|
7817
7956
|
const line = lineOf(file.content, topic);
|
|
7818
7957
|
out.push({
|
|
7819
|
-
infraId: (0,
|
|
7958
|
+
infraId: (0, import_types20.infraId)("kafka-topic", topic),
|
|
7820
7959
|
name: topic,
|
|
7821
7960
|
kind: "kafka-topic",
|
|
7822
7961
|
edgeType,
|
|
@@ -7839,7 +7978,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
7839
7978
|
// src/extract/calls/redis.ts
|
|
7840
7979
|
init_cjs_shims();
|
|
7841
7980
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
7842
|
-
var
|
|
7981
|
+
var import_types21 = require("@neat.is/types");
|
|
7843
7982
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
7844
7983
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
7845
7984
|
const out = [];
|
|
@@ -7852,7 +7991,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
7852
7991
|
seen.add(host);
|
|
7853
7992
|
const line = lineOf(file.content, host);
|
|
7854
7993
|
out.push({
|
|
7855
|
-
infraId: (0,
|
|
7994
|
+
infraId: (0, import_types21.infraId)("redis", host),
|
|
7856
7995
|
name: host,
|
|
7857
7996
|
kind: "redis",
|
|
7858
7997
|
edgeType: "CALLS",
|
|
@@ -7873,7 +8012,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
7873
8012
|
// src/extract/calls/aws.ts
|
|
7874
8013
|
init_cjs_shims();
|
|
7875
8014
|
var import_node_path33 = __toESM(require("path"), 1);
|
|
7876
|
-
var
|
|
8015
|
+
var import_types22 = require("@neat.is/types");
|
|
7877
8016
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
7878
8017
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
7879
8018
|
function hasMarker(text, markers) {
|
|
@@ -7897,7 +8036,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
7897
8036
|
seen.add(key);
|
|
7898
8037
|
const line = lineOf(file.content, name);
|
|
7899
8038
|
out.push({
|
|
7900
|
-
infraId: (0,
|
|
8039
|
+
infraId: (0, import_types22.infraId)(kind, name),
|
|
7901
8040
|
name,
|
|
7902
8041
|
kind,
|
|
7903
8042
|
edgeType: "CALLS",
|
|
@@ -7932,7 +8071,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
7932
8071
|
// src/extract/calls/grpc.ts
|
|
7933
8072
|
init_cjs_shims();
|
|
7934
8073
|
var import_node_path34 = __toESM(require("path"), 1);
|
|
7935
|
-
var
|
|
8074
|
+
var import_types23 = require("@neat.is/types");
|
|
7936
8075
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
7937
8076
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
7938
8077
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -7981,7 +8120,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
7981
8120
|
const { kind } = classified;
|
|
7982
8121
|
const line = lineOf(file.content, m[0]);
|
|
7983
8122
|
out.push({
|
|
7984
|
-
infraId: (0,
|
|
8123
|
+
infraId: (0, import_types23.infraId)(kind, name),
|
|
7985
8124
|
name,
|
|
7986
8125
|
kind,
|
|
7987
8126
|
edgeType: "CALLS",
|
|
@@ -8002,7 +8141,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
8002
8141
|
// src/extract/calls/supabase.ts
|
|
8003
8142
|
init_cjs_shims();
|
|
8004
8143
|
var import_node_path35 = __toESM(require("path"), 1);
|
|
8005
|
-
var
|
|
8144
|
+
var import_types24 = require("@neat.is/types");
|
|
8006
8145
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
8007
8146
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
8008
8147
|
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
@@ -8050,7 +8189,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
8050
8189
|
seen.add(name);
|
|
8051
8190
|
const line = lineOf(file.content, m[0]);
|
|
8052
8191
|
out.push({
|
|
8053
|
-
infraId: (0,
|
|
8192
|
+
infraId: (0, import_types24.infraId)("supabase", name),
|
|
8054
8193
|
name,
|
|
8055
8194
|
kind: "supabase",
|
|
8056
8195
|
edgeType: "CALLS",
|
|
@@ -8081,7 +8220,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
8081
8220
|
seen.add(key);
|
|
8082
8221
|
const line = lineOf(file.content, am[0]);
|
|
8083
8222
|
out.push({
|
|
8084
|
-
infraId: (0,
|
|
8223
|
+
infraId: (0, import_types24.infraId)(kind, resource),
|
|
8085
8224
|
name: resource,
|
|
8086
8225
|
kind,
|
|
8087
8226
|
edgeType: "CALLS",
|
|
@@ -8100,7 +8239,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
8100
8239
|
// src/extract/calls/mongoose.ts
|
|
8101
8240
|
init_cjs_shims();
|
|
8102
8241
|
var import_node_path36 = __toESM(require("path"), 1);
|
|
8103
|
-
var
|
|
8242
|
+
var import_types25 = require("@neat.is/types");
|
|
8104
8243
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
8105
8244
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
8106
8245
|
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
@@ -8245,7 +8384,7 @@ function collectModelDefs(content, pluralizeOn) {
|
|
|
8245
8384
|
function endpoint(r, file, serviceDir, matchText) {
|
|
8246
8385
|
const line = lineOf(file.content, matchText);
|
|
8247
8386
|
return {
|
|
8248
|
-
infraId: (0,
|
|
8387
|
+
infraId: (0, import_types25.infraId)(r.kind, r.name),
|
|
8249
8388
|
name: r.name,
|
|
8250
8389
|
kind: r.kind,
|
|
8251
8390
|
edgeType: "CALLS",
|
|
@@ -8398,7 +8537,7 @@ init_cjs_shims();
|
|
|
8398
8537
|
var import_node_path37 = __toESM(require("path"), 1);
|
|
8399
8538
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
8400
8539
|
var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
8401
|
-
var
|
|
8540
|
+
var import_types26 = require("@neat.is/types");
|
|
8402
8541
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
8403
8542
|
var PARSE_CHUNK6 = 16384;
|
|
8404
8543
|
function makePyParser4() {
|
|
@@ -8458,20 +8597,57 @@ function walk3(node, visit) {
|
|
|
8458
8597
|
visit(node);
|
|
8459
8598
|
for (const c of namedChildren(node)) walk3(c, visit);
|
|
8460
8599
|
}
|
|
8600
|
+
var COLUMN_BUILDERS = /* @__PURE__ */ new Set(["Column", "mapped_column"]);
|
|
8601
|
+
function isColumnBuilder(call) {
|
|
8602
|
+
const fn = call.childForFieldName("function");
|
|
8603
|
+
const t = fn?.text;
|
|
8604
|
+
if (!t) return false;
|
|
8605
|
+
const base = t.includes(".") ? t.slice(t.lastIndexOf(".") + 1) : t;
|
|
8606
|
+
return COLUMN_BUILDERS.has(base);
|
|
8607
|
+
}
|
|
8608
|
+
function firstPositionalString(call) {
|
|
8609
|
+
const args = call.childForFieldName("arguments");
|
|
8610
|
+
if (!args) return null;
|
|
8611
|
+
for (const arg of namedChildren(args)) {
|
|
8612
|
+
if (arg.type === "keyword_argument") continue;
|
|
8613
|
+
return arg.type === "string" ? pyStaticStringText2(arg) : null;
|
|
8614
|
+
}
|
|
8615
|
+
return null;
|
|
8616
|
+
}
|
|
8617
|
+
function columnsFromClassBody(body) {
|
|
8618
|
+
const out = [];
|
|
8619
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8620
|
+
for (const stmt of namedChildren(body)) {
|
|
8621
|
+
if (stmt.type !== "expression_statement") continue;
|
|
8622
|
+
const assign = stmt.namedChild(0);
|
|
8623
|
+
if (assign?.type !== "assignment") continue;
|
|
8624
|
+
const left = assign.childForFieldName("left");
|
|
8625
|
+
if (left?.type !== "identifier") continue;
|
|
8626
|
+
const right = assign.childForFieldName("right");
|
|
8627
|
+
if (right?.type !== "call" || !isColumnBuilder(right)) continue;
|
|
8628
|
+
const name = firstPositionalString(right) ?? left.text;
|
|
8629
|
+
if (name && !seen.has(name)) {
|
|
8630
|
+
seen.add(name);
|
|
8631
|
+
out.push(name);
|
|
8632
|
+
}
|
|
8633
|
+
}
|
|
8634
|
+
return out;
|
|
8635
|
+
}
|
|
8461
8636
|
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
8462
8637
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
8463
8638
|
const tree = parseSource6(makePyParser4(), file.content);
|
|
8464
8639
|
const out = [];
|
|
8465
8640
|
const seen = /* @__PURE__ */ new Set();
|
|
8466
|
-
const push = (name, line) => {
|
|
8641
|
+
const push = (name, line, columns) => {
|
|
8467
8642
|
if (seen.has(name)) return;
|
|
8468
8643
|
seen.add(name);
|
|
8469
8644
|
out.push({
|
|
8470
|
-
infraId: (0,
|
|
8645
|
+
infraId: (0, import_types26.infraId)("sql-table", name),
|
|
8471
8646
|
name,
|
|
8472
8647
|
kind: "sql-table",
|
|
8473
8648
|
edgeType: "CALLS",
|
|
8474
8649
|
confidenceKind: "verified-call-site",
|
|
8650
|
+
...columns && columns.length > 0 ? { columns } : {},
|
|
8475
8651
|
evidence: {
|
|
8476
8652
|
file: import_node_path37.default.relative(serviceDir, file.path),
|
|
8477
8653
|
line,
|
|
@@ -8487,11 +8663,12 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
8487
8663
|
const line = node.startPosition.row + 1;
|
|
8488
8664
|
const explicit = explicitTablename(body);
|
|
8489
8665
|
if (explicit === "computed") return;
|
|
8666
|
+
const columns = columnsFromClassBody(body);
|
|
8490
8667
|
if (explicit) {
|
|
8491
|
-
push(explicit.name, line);
|
|
8668
|
+
push(explicit.name, line, columns);
|
|
8492
8669
|
return;
|
|
8493
8670
|
}
|
|
8494
|
-
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
|
|
8671
|
+
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line, columns);
|
|
8495
8672
|
return;
|
|
8496
8673
|
}
|
|
8497
8674
|
if (node.type === "call") {
|
|
@@ -8573,7 +8750,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
8573
8750
|
if (seen.has(key)) continue;
|
|
8574
8751
|
seen.add(key);
|
|
8575
8752
|
out.push({
|
|
8576
|
-
infraId: (0,
|
|
8753
|
+
infraId: (0, import_types26.infraId)("sql-table", t),
|
|
8577
8754
|
name: t,
|
|
8578
8755
|
kind: "sql-table",
|
|
8579
8756
|
edgeType: "CALLS",
|
|
@@ -8594,7 +8771,7 @@ init_cjs_shims();
|
|
|
8594
8771
|
var import_node_path38 = __toESM(require("path"), 1);
|
|
8595
8772
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
8596
8773
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
8597
|
-
var
|
|
8774
|
+
var import_types27 = require("@neat.is/types");
|
|
8598
8775
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
8599
8776
|
var PARSE_CHUNK7 = 16384;
|
|
8600
8777
|
function makePyParser5() {
|
|
@@ -8674,7 +8851,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
8674
8851
|
seen.add(table);
|
|
8675
8852
|
const line = node.startPosition.row + 1;
|
|
8676
8853
|
out.push({
|
|
8677
|
-
infraId: (0,
|
|
8854
|
+
infraId: (0, import_types27.infraId)("sql-table", table),
|
|
8678
8855
|
name: table,
|
|
8679
8856
|
kind: "sql-table",
|
|
8680
8857
|
edgeType: "CALLS",
|
|
@@ -8685,12 +8862,129 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
8685
8862
|
return out;
|
|
8686
8863
|
}
|
|
8687
8864
|
|
|
8688
|
-
// src/extract/calls/
|
|
8865
|
+
// src/extract/calls/drizzle.ts
|
|
8689
8866
|
init_cjs_shims();
|
|
8690
|
-
var
|
|
8867
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
8691
8868
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
8869
|
+
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
8870
|
+
var import_types28 = require("@neat.is/types");
|
|
8871
|
+
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
8872
|
+
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
8873
|
+
function parserForExt(ext) {
|
|
8874
|
+
const p = new import_tree_sitter9.default();
|
|
8875
|
+
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
|
|
8876
|
+
return p;
|
|
8877
|
+
}
|
|
8878
|
+
function namedChildren3(node) {
|
|
8879
|
+
const out = [];
|
|
8880
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8881
|
+
const c = node.namedChild(i);
|
|
8882
|
+
if (c) out.push(c);
|
|
8883
|
+
}
|
|
8884
|
+
return out;
|
|
8885
|
+
}
|
|
8886
|
+
function stringLiteralText2(node) {
|
|
8887
|
+
if (!node || node.type !== "string") return null;
|
|
8888
|
+
for (const child of namedChildren3(node)) {
|
|
8889
|
+
if (child.type === "string_fragment") return child.text;
|
|
8890
|
+
}
|
|
8891
|
+
return "";
|
|
8892
|
+
}
|
|
8893
|
+
function firstStringArg(call) {
|
|
8894
|
+
const args = call.childForFieldName("arguments");
|
|
8895
|
+
if (!args) return null;
|
|
8896
|
+
for (const arg of namedChildren3(args)) {
|
|
8897
|
+
if (arg.type === "string") return stringLiteralText2(arg);
|
|
8898
|
+
return null;
|
|
8899
|
+
}
|
|
8900
|
+
return null;
|
|
8901
|
+
}
|
|
8902
|
+
function builderColumnName(value) {
|
|
8903
|
+
let node = value;
|
|
8904
|
+
while (node) {
|
|
8905
|
+
if (node.type === "call_expression") {
|
|
8906
|
+
const fn = node.childForFieldName("function");
|
|
8907
|
+
if (fn?.type === "identifier") return firstStringArg(node);
|
|
8908
|
+
if (fn?.type === "member_expression") {
|
|
8909
|
+
node = fn.childForFieldName("object");
|
|
8910
|
+
continue;
|
|
8911
|
+
}
|
|
8912
|
+
return null;
|
|
8913
|
+
}
|
|
8914
|
+
if (node.type === "member_expression") {
|
|
8915
|
+
node = node.childForFieldName("object");
|
|
8916
|
+
continue;
|
|
8917
|
+
}
|
|
8918
|
+
return null;
|
|
8919
|
+
}
|
|
8920
|
+
return null;
|
|
8921
|
+
}
|
|
8922
|
+
function keyName(key) {
|
|
8923
|
+
if (!key) return null;
|
|
8924
|
+
if (key.type === "property_identifier") return key.text;
|
|
8925
|
+
if (key.type === "string") return stringLiteralText2(key);
|
|
8926
|
+
return null;
|
|
8927
|
+
}
|
|
8928
|
+
function columnsFromObject(obj) {
|
|
8929
|
+
const out = [];
|
|
8930
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8931
|
+
for (const child of namedChildren3(obj)) {
|
|
8932
|
+
if (child.type !== "pair") continue;
|
|
8933
|
+
const value = child.childForFieldName("value");
|
|
8934
|
+
const key = keyName(child.childForFieldName("key"));
|
|
8935
|
+
const name = (value ? builderColumnName(value) : null) ?? key;
|
|
8936
|
+
if (name && !seen.has(name)) {
|
|
8937
|
+
seen.add(name);
|
|
8938
|
+
out.push(name);
|
|
8939
|
+
}
|
|
8940
|
+
}
|
|
8941
|
+
return out;
|
|
8942
|
+
}
|
|
8943
|
+
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
8944
|
+
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
8945
|
+
const tree = parseSource2(parserForExt(import_node_path39.default.extname(file.path)), file.content);
|
|
8946
|
+
const out = [];
|
|
8947
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8948
|
+
const walk6 = (node) => {
|
|
8949
|
+
if (node.type === "call_expression") {
|
|
8950
|
+
const fn = node.childForFieldName("function");
|
|
8951
|
+
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
8952
|
+
const args = node.childForFieldName("arguments");
|
|
8953
|
+
const argNodes = args ? namedChildren3(args) : [];
|
|
8954
|
+
const tableName = stringLiteralText2(argNodes[0] ?? null);
|
|
8955
|
+
const obj = argNodes[1];
|
|
8956
|
+
if (tableName && obj?.type === "object" && !seen.has(tableName)) {
|
|
8957
|
+
seen.add(tableName);
|
|
8958
|
+
const columns = columnsFromObject(obj);
|
|
8959
|
+
const line = node.startPosition.row + 1;
|
|
8960
|
+
out.push({
|
|
8961
|
+
infraId: (0, import_types28.infraId)("sql-table", tableName),
|
|
8962
|
+
name: tableName,
|
|
8963
|
+
kind: "sql-table",
|
|
8964
|
+
edgeType: "CALLS",
|
|
8965
|
+
confidenceKind: "structural",
|
|
8966
|
+
columns,
|
|
8967
|
+
evidence: {
|
|
8968
|
+
file: import_node_path39.default.relative(serviceDir, file.path),
|
|
8969
|
+
line,
|
|
8970
|
+
snippet: snippet(file.content, line)
|
|
8971
|
+
}
|
|
8972
|
+
});
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
}
|
|
8976
|
+
for (const c of namedChildren3(node)) walk6(c);
|
|
8977
|
+
};
|
|
8978
|
+
walk6(tree.rootNode);
|
|
8979
|
+
return out;
|
|
8980
|
+
}
|
|
8981
|
+
|
|
8982
|
+
// src/extract/calls/go.ts
|
|
8983
|
+
init_cjs_shims();
|
|
8984
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
8985
|
+
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
8692
8986
|
var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
|
|
8693
|
-
var
|
|
8987
|
+
var import_types29 = require("@neat.is/types");
|
|
8694
8988
|
init_otel();
|
|
8695
8989
|
var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
|
|
8696
8990
|
var PARSE_CHUNK8 = 16384;
|
|
@@ -8702,8 +8996,8 @@ function walk5(node, visit) {
|
|
|
8702
8996
|
}
|
|
8703
8997
|
}
|
|
8704
8998
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
8705
|
-
if (
|
|
8706
|
-
const parser = new
|
|
8999
|
+
if (import_node_path42.default.extname(file.path) !== ".go") return [];
|
|
9000
|
+
const parser = new import_tree_sitter10.default();
|
|
8707
9001
|
parser.setLanguage(import_tree_sitter_go3.default);
|
|
8708
9002
|
const tree = parser.parse(
|
|
8709
9003
|
(index) => index >= file.content.length ? "" : file.content.slice(index, index + PARSE_CHUNK8)
|
|
@@ -8722,12 +9016,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
8722
9016
|
if (!table) return;
|
|
8723
9017
|
const line = node.startPosition.row + 1;
|
|
8724
9018
|
out.push({
|
|
8725
|
-
infraId: (0,
|
|
9019
|
+
infraId: (0, import_types29.infraId)("sql-table", table),
|
|
8726
9020
|
name: table,
|
|
8727
9021
|
kind: "sql-table",
|
|
8728
9022
|
edgeType: "CALLS",
|
|
8729
9023
|
confidenceKind: "verified-call-site",
|
|
8730
|
-
evidence: { file: toPosix(
|
|
9024
|
+
evidence: { file: toPosix(import_node_path42.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
|
|
8731
9025
|
});
|
|
8732
9026
|
});
|
|
8733
9027
|
return out;
|
|
@@ -8737,11 +9031,11 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
8737
9031
|
function edgeTypeFromEndpoint(ep) {
|
|
8738
9032
|
switch (ep.edgeType) {
|
|
8739
9033
|
case "PUBLISHES_TO":
|
|
8740
|
-
return
|
|
9034
|
+
return import_types30.EdgeType.PUBLISHES_TO;
|
|
8741
9035
|
case "CONSUMES_FROM":
|
|
8742
|
-
return
|
|
9036
|
+
return import_types30.EdgeType.CONSUMES_FROM;
|
|
8743
9037
|
default:
|
|
8744
|
-
return
|
|
9038
|
+
return import_types30.EdgeType.CALLS;
|
|
8745
9039
|
}
|
|
8746
9040
|
}
|
|
8747
9041
|
function isAwsKind(kind) {
|
|
@@ -8767,6 +9061,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8767
9061
|
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
8768
9062
|
endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
|
|
8769
9063
|
endpoints.push(...djangoOrmEndpointsFromFile(maskedFile, service.dir));
|
|
9064
|
+
endpoints.push(...drizzleEndpointsFromFile(maskedFile, service.dir));
|
|
8770
9065
|
try {
|
|
8771
9066
|
endpoints.push(...goSqlEndpointsFromFile(maskedFile, service.dir));
|
|
8772
9067
|
} catch (err) {
|
|
@@ -8781,7 +9076,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8781
9076
|
if (!graph.hasNode(ep.infraId)) {
|
|
8782
9077
|
const node = {
|
|
8783
9078
|
id: ep.infraId,
|
|
8784
|
-
type:
|
|
9079
|
+
type: import_types30.NodeType.InfraNode,
|
|
8785
9080
|
name: ep.name,
|
|
8786
9081
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
8787
9082
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -8792,8 +9087,22 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8792
9087
|
graph.addNode(node.id, node);
|
|
8793
9088
|
nodesAdded++;
|
|
8794
9089
|
}
|
|
9090
|
+
if (ep.columns && ep.columns.length > 0) {
|
|
9091
|
+
const node = graph.getNodeAttributes(ep.infraId);
|
|
9092
|
+
if (node.type === import_types30.NodeType.InfraNode) {
|
|
9093
|
+
graph.replaceNodeAttributes(ep.infraId, {
|
|
9094
|
+
...node,
|
|
9095
|
+
columns: foldColumns(
|
|
9096
|
+
node.columns,
|
|
9097
|
+
ep.columns,
|
|
9098
|
+
import_types30.Provenance.EXTRACTED,
|
|
9099
|
+
(0, import_types30.confidenceForExtracted)(ep.confidenceKind)
|
|
9100
|
+
)
|
|
9101
|
+
});
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
8795
9104
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
8796
|
-
const confidence = (0,
|
|
9105
|
+
const confidence = (0, import_types30.confidenceForExtracted)(ep.confidenceKind);
|
|
8797
9106
|
const relFile = toPosix(ep.evidence.file);
|
|
8798
9107
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
8799
9108
|
graph,
|
|
@@ -8803,7 +9112,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8803
9112
|
);
|
|
8804
9113
|
nodesAdded += n;
|
|
8805
9114
|
edgesAdded += e;
|
|
8806
|
-
if (!(0,
|
|
9115
|
+
if (!(0, import_types30.passesExtractedFloor)(confidence)) {
|
|
8807
9116
|
noteExtractedDropped({
|
|
8808
9117
|
source: fileNodeId,
|
|
8809
9118
|
target: ep.infraId,
|
|
@@ -8814,7 +9123,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8814
9123
|
});
|
|
8815
9124
|
continue;
|
|
8816
9125
|
}
|
|
8817
|
-
const edgeId = (0,
|
|
9126
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, ep.infraId, edgeType);
|
|
8818
9127
|
if (seenEdges.has(edgeId)) continue;
|
|
8819
9128
|
seenEdges.add(edgeId);
|
|
8820
9129
|
if (!graph.hasEdge(edgeId)) {
|
|
@@ -8823,7 +9132,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8823
9132
|
source: fileNodeId,
|
|
8824
9133
|
target: ep.infraId,
|
|
8825
9134
|
type: edgeType,
|
|
8826
|
-
provenance:
|
|
9135
|
+
provenance: import_types30.Provenance.EXTRACTED,
|
|
8827
9136
|
confidence,
|
|
8828
9137
|
evidence: ep.evidence
|
|
8829
9138
|
};
|
|
@@ -8849,16 +9158,16 @@ init_cjs_shims();
|
|
|
8849
9158
|
|
|
8850
9159
|
// src/extract/infra/docker-compose.ts
|
|
8851
9160
|
init_cjs_shims();
|
|
8852
|
-
var
|
|
8853
|
-
var
|
|
9161
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
9162
|
+
var import_types32 = require("@neat.is/types");
|
|
8854
9163
|
|
|
8855
9164
|
// src/extract/infra/shared.ts
|
|
8856
9165
|
init_cjs_shims();
|
|
8857
|
-
var
|
|
9166
|
+
var import_types31 = require("@neat.is/types");
|
|
8858
9167
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
8859
9168
|
return {
|
|
8860
|
-
id: (0,
|
|
8861
|
-
type:
|
|
9169
|
+
id: (0, import_types31.infraId)(kind, name),
|
|
9170
|
+
type: import_types31.NodeType.InfraNode,
|
|
8862
9171
|
name,
|
|
8863
9172
|
provider,
|
|
8864
9173
|
kind,
|
|
@@ -8895,15 +9204,15 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
8895
9204
|
nodesAdded++;
|
|
8896
9205
|
}
|
|
8897
9206
|
if (node.id === anchorId) return { nodesAdded, edgesAdded };
|
|
8898
|
-
const edgeId = (0,
|
|
9207
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, node.id, edgeType);
|
|
8899
9208
|
if (!graph.hasEdge(edgeId)) {
|
|
8900
9209
|
const edge = {
|
|
8901
9210
|
id: edgeId,
|
|
8902
9211
|
source: anchorId,
|
|
8903
9212
|
target: node.id,
|
|
8904
9213
|
type: edgeType,
|
|
8905
|
-
provenance:
|
|
8906
|
-
confidence: (0,
|
|
9214
|
+
provenance: import_types31.Provenance.EXTRACTED,
|
|
9215
|
+
confidence: (0, import_types31.confidenceForExtracted)("structural"),
|
|
8907
9216
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
8908
9217
|
};
|
|
8909
9218
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8920,7 +9229,7 @@ function dependsOnList(value) {
|
|
|
8920
9229
|
}
|
|
8921
9230
|
function serviceNameToServiceNode(name, services) {
|
|
8922
9231
|
for (const s of services) {
|
|
8923
|
-
if (s.node.name === name ||
|
|
9232
|
+
if (s.node.name === name || import_node_path43.default.basename(s.dir) === name) return s.node.id;
|
|
8924
9233
|
}
|
|
8925
9234
|
return null;
|
|
8926
9235
|
}
|
|
@@ -8929,7 +9238,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8929
9238
|
let edgesAdded = 0;
|
|
8930
9239
|
let composePath = null;
|
|
8931
9240
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
8932
|
-
const abs =
|
|
9241
|
+
const abs = import_node_path43.default.join(scanPath, name);
|
|
8933
9242
|
if (await exists2(abs)) {
|
|
8934
9243
|
composePath = abs;
|
|
8935
9244
|
break;
|
|
@@ -8942,13 +9251,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8942
9251
|
} catch (err) {
|
|
8943
9252
|
recordExtractionError(
|
|
8944
9253
|
"infra docker-compose",
|
|
8945
|
-
|
|
9254
|
+
import_node_path43.default.relative(scanPath, composePath),
|
|
8946
9255
|
err
|
|
8947
9256
|
);
|
|
8948
9257
|
return { nodesAdded, edgesAdded };
|
|
8949
9258
|
}
|
|
8950
9259
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
8951
|
-
const evidenceFile =
|
|
9260
|
+
const evidenceFile = import_node_path43.default.relative(scanPath, composePath).split(import_node_path43.default.sep).join("/");
|
|
8952
9261
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
8953
9262
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
8954
9263
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -8970,15 +9279,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8970
9279
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
8971
9280
|
const targetId = composeNameToNodeId.get(dep);
|
|
8972
9281
|
if (!targetId) continue;
|
|
8973
|
-
const edgeId = (0,
|
|
9282
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types32.EdgeType.DEPENDS_ON);
|
|
8974
9283
|
if (graph.hasEdge(edgeId)) continue;
|
|
8975
9284
|
const edge = {
|
|
8976
9285
|
id: edgeId,
|
|
8977
9286
|
source: sourceId,
|
|
8978
9287
|
target: targetId,
|
|
8979
|
-
type:
|
|
8980
|
-
provenance:
|
|
8981
|
-
confidence: (0,
|
|
9288
|
+
type: import_types32.EdgeType.DEPENDS_ON,
|
|
9289
|
+
provenance: import_types32.Provenance.EXTRACTED,
|
|
9290
|
+
confidence: (0, import_types32.confidenceForExtracted)("structural"),
|
|
8982
9291
|
evidence: { file: evidenceFile }
|
|
8983
9292
|
};
|
|
8984
9293
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8990,9 +9299,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8990
9299
|
|
|
8991
9300
|
// src/extract/infra/dockerfile.ts
|
|
8992
9301
|
init_cjs_shims();
|
|
8993
|
-
var
|
|
9302
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8994
9303
|
var import_node_fs19 = require("fs");
|
|
8995
|
-
var
|
|
9304
|
+
var import_types33 = require("@neat.is/types");
|
|
8996
9305
|
function readDockerfile(content) {
|
|
8997
9306
|
let image = null;
|
|
8998
9307
|
const ports = [];
|
|
@@ -9021,7 +9330,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
9021
9330
|
let nodesAdded = 0;
|
|
9022
9331
|
let edgesAdded = 0;
|
|
9023
9332
|
for (const service of services) {
|
|
9024
|
-
const dockerfilePath =
|
|
9333
|
+
const dockerfilePath = import_node_path44.default.join(service.dir, "Dockerfile");
|
|
9025
9334
|
if (!await exists2(dockerfilePath)) continue;
|
|
9026
9335
|
let content;
|
|
9027
9336
|
try {
|
|
@@ -9029,7 +9338,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
9029
9338
|
} catch (err) {
|
|
9030
9339
|
recordExtractionError(
|
|
9031
9340
|
"infra dockerfile",
|
|
9032
|
-
|
|
9341
|
+
import_node_path44.default.relative(scanPath, dockerfilePath),
|
|
9033
9342
|
err
|
|
9034
9343
|
);
|
|
9035
9344
|
continue;
|
|
@@ -9041,8 +9350,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
9041
9350
|
graph.addNode(node.id, node);
|
|
9042
9351
|
nodesAdded++;
|
|
9043
9352
|
}
|
|
9044
|
-
const relDockerfile = toPosix(
|
|
9045
|
-
const evidenceFile = toPosix(
|
|
9353
|
+
const relDockerfile = toPosix(import_node_path44.default.relative(service.dir, dockerfilePath));
|
|
9354
|
+
const evidenceFile = toPosix(import_node_path44.default.relative(scanPath, dockerfilePath));
|
|
9046
9355
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9047
9356
|
graph,
|
|
9048
9357
|
service.pkg.name,
|
|
@@ -9051,15 +9360,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
9051
9360
|
);
|
|
9052
9361
|
nodesAdded += fn;
|
|
9053
9362
|
edgesAdded += fe;
|
|
9054
|
-
const edgeId = (0,
|
|
9363
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types33.EdgeType.RUNS_ON);
|
|
9055
9364
|
if (!graph.hasEdge(edgeId)) {
|
|
9056
9365
|
const edge = {
|
|
9057
9366
|
id: edgeId,
|
|
9058
9367
|
source: fileNodeId,
|
|
9059
9368
|
target: node.id,
|
|
9060
|
-
type:
|
|
9061
|
-
provenance:
|
|
9062
|
-
confidence: (0,
|
|
9369
|
+
type: import_types33.EdgeType.RUNS_ON,
|
|
9370
|
+
provenance: import_types33.Provenance.EXTRACTED,
|
|
9371
|
+
confidence: (0, import_types33.confidenceForExtracted)("structural"),
|
|
9063
9372
|
evidence: {
|
|
9064
9373
|
file: evidenceFile,
|
|
9065
9374
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -9074,15 +9383,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
9074
9383
|
graph.addNode(portNode.id, portNode);
|
|
9075
9384
|
nodesAdded++;
|
|
9076
9385
|
}
|
|
9077
|
-
const portEdgeId = (0,
|
|
9386
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types33.EdgeType.CONNECTS_TO);
|
|
9078
9387
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
9079
9388
|
const portEdge = {
|
|
9080
9389
|
id: portEdgeId,
|
|
9081
9390
|
source: fileNodeId,
|
|
9082
9391
|
target: portNode.id,
|
|
9083
|
-
type:
|
|
9084
|
-
provenance:
|
|
9085
|
-
confidence: (0,
|
|
9392
|
+
type: import_types33.EdgeType.CONNECTS_TO,
|
|
9393
|
+
provenance: import_types33.Provenance.EXTRACTED,
|
|
9394
|
+
confidence: (0, import_types33.confidenceForExtracted)("structural"),
|
|
9086
9395
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
9087
9396
|
};
|
|
9088
9397
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -9095,8 +9404,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
9095
9404
|
// src/extract/infra/terraform.ts
|
|
9096
9405
|
init_cjs_shims();
|
|
9097
9406
|
var import_node_fs20 = require("fs");
|
|
9098
|
-
var
|
|
9099
|
-
var
|
|
9407
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
9408
|
+
var import_types34 = require("@neat.is/types");
|
|
9100
9409
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
9101
9410
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
9102
9411
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -9106,11 +9415,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
9106
9415
|
for (const entry of entries) {
|
|
9107
9416
|
if (entry.isDirectory()) {
|
|
9108
9417
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
9109
|
-
const child =
|
|
9418
|
+
const child = import_node_path45.default.join(start, entry.name);
|
|
9110
9419
|
if (await isPythonVenvDir(child)) continue;
|
|
9111
9420
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
9112
9421
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
9113
|
-
out.push(
|
|
9422
|
+
out.push(import_node_path45.default.join(start, entry.name));
|
|
9114
9423
|
}
|
|
9115
9424
|
}
|
|
9116
9425
|
return out;
|
|
@@ -9142,7 +9451,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
9142
9451
|
const files = await walkTfFiles(scanPath);
|
|
9143
9452
|
for (const file of files) {
|
|
9144
9453
|
const content = await import_node_fs20.promises.readFile(file, "utf8");
|
|
9145
|
-
const evidenceFile = toPosix(
|
|
9454
|
+
const evidenceFile = toPosix(import_node_path45.default.relative(scanPath, file));
|
|
9146
9455
|
const resources = [];
|
|
9147
9456
|
const byKey = /* @__PURE__ */ new Map();
|
|
9148
9457
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -9177,16 +9486,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
9177
9486
|
if (!target) continue;
|
|
9178
9487
|
if (seen.has(target.nodeId)) continue;
|
|
9179
9488
|
seen.add(target.nodeId);
|
|
9180
|
-
const edgeId = (0,
|
|
9489
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types34.EdgeType.DEPENDS_ON);
|
|
9181
9490
|
if (graph.hasEdge(edgeId)) continue;
|
|
9182
9491
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
9183
9492
|
const edge = {
|
|
9184
9493
|
id: edgeId,
|
|
9185
9494
|
source: resource.nodeId,
|
|
9186
9495
|
target: target.nodeId,
|
|
9187
|
-
type:
|
|
9188
|
-
provenance:
|
|
9189
|
-
confidence: (0,
|
|
9496
|
+
type: import_types34.EdgeType.DEPENDS_ON,
|
|
9497
|
+
provenance: import_types34.Provenance.EXTRACTED,
|
|
9498
|
+
confidence: (0, import_types34.confidenceForExtracted)("structural"),
|
|
9190
9499
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
9191
9500
|
};
|
|
9192
9501
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -9200,7 +9509,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
9200
9509
|
// src/extract/infra/k8s.ts
|
|
9201
9510
|
init_cjs_shims();
|
|
9202
9511
|
var import_node_fs21 = require("fs");
|
|
9203
|
-
var
|
|
9512
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
9204
9513
|
var import_yaml3 = require("yaml");
|
|
9205
9514
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
9206
9515
|
Service: "k8s-service",
|
|
@@ -9218,11 +9527,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
9218
9527
|
for (const entry of entries) {
|
|
9219
9528
|
if (entry.isDirectory()) {
|
|
9220
9529
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
9221
|
-
const child =
|
|
9530
|
+
const child = import_node_path46.default.join(start, entry.name);
|
|
9222
9531
|
if (await isPythonVenvDir(child)) continue;
|
|
9223
9532
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
9224
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
9225
|
-
out.push(
|
|
9533
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path46.default.extname(entry.name))) {
|
|
9534
|
+
out.push(import_node_path46.default.join(start, entry.name));
|
|
9226
9535
|
}
|
|
9227
9536
|
}
|
|
9228
9537
|
return out;
|
|
@@ -9256,13 +9565,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
9256
9565
|
// src/extract/infra/cloudflare.ts
|
|
9257
9566
|
init_cjs_shims();
|
|
9258
9567
|
var import_node_fs22 = require("fs");
|
|
9259
|
-
var
|
|
9568
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9260
9569
|
var import_smol_toml2 = require("smol-toml");
|
|
9261
|
-
var
|
|
9570
|
+
var import_types35 = require("@neat.is/types");
|
|
9262
9571
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
9263
9572
|
async function readWranglerConfig(dir) {
|
|
9264
9573
|
for (const filename of WRANGLER_FILENAMES) {
|
|
9265
|
-
const abs =
|
|
9574
|
+
const abs = import_node_path47.default.join(dir, filename);
|
|
9266
9575
|
if (!await exists2(abs)) continue;
|
|
9267
9576
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
9268
9577
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9299,15 +9608,15 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
9299
9608
|
nodesAdded++;
|
|
9300
9609
|
}
|
|
9301
9610
|
if (node.id === anchorId) return { nodesAdded, edgesAdded };
|
|
9302
|
-
const edgeId = (0,
|
|
9611
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, node.id, edgeType);
|
|
9303
9612
|
if (!graph.hasEdge(edgeId)) {
|
|
9304
9613
|
const edge = {
|
|
9305
9614
|
id: edgeId,
|
|
9306
9615
|
source: anchorId,
|
|
9307
9616
|
target: node.id,
|
|
9308
9617
|
type: edgeType,
|
|
9309
|
-
provenance:
|
|
9310
|
-
confidence: (0,
|
|
9618
|
+
provenance: import_types35.Provenance.EXTRACTED,
|
|
9619
|
+
confidence: (0, import_types35.confidenceForExtracted)("structural"),
|
|
9311
9620
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
9312
9621
|
};
|
|
9313
9622
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -9325,11 +9634,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9325
9634
|
try {
|
|
9326
9635
|
read = await readWranglerConfig(service.dir);
|
|
9327
9636
|
} catch (err) {
|
|
9328
|
-
recordExtractionError("infra cloudflare",
|
|
9637
|
+
recordExtractionError("infra cloudflare", import_node_path47.default.relative(scanPath, service.dir), err);
|
|
9329
9638
|
continue;
|
|
9330
9639
|
}
|
|
9331
9640
|
if (!read || !read.config.name) continue;
|
|
9332
|
-
const evidenceFile = toPosix(
|
|
9641
|
+
const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, read.relFile)));
|
|
9333
9642
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
9334
9643
|
}
|
|
9335
9644
|
for (const worker of discovered) {
|
|
@@ -9341,7 +9650,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9341
9650
|
}
|
|
9342
9651
|
let anchorId = service.node.id;
|
|
9343
9652
|
if (config.main) {
|
|
9344
|
-
const entryRelPath = toPosix(
|
|
9653
|
+
const entryRelPath = toPosix(import_node_path47.default.normalize(config.main));
|
|
9345
9654
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
9346
9655
|
graph,
|
|
9347
9656
|
service.pkg.name,
|
|
@@ -9368,15 +9677,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9368
9677
|
nodesAdded++;
|
|
9369
9678
|
}
|
|
9370
9679
|
if (runtimeNode.id !== anchorId) {
|
|
9371
|
-
const runsOnId = (0,
|
|
9680
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types35.EdgeType.RUNS_ON);
|
|
9372
9681
|
if (!graph.hasEdge(runsOnId)) {
|
|
9373
9682
|
const edge = {
|
|
9374
9683
|
id: runsOnId,
|
|
9375
9684
|
source: anchorId,
|
|
9376
9685
|
target: runtimeNode.id,
|
|
9377
|
-
type:
|
|
9378
|
-
provenance:
|
|
9379
|
-
confidence: (0,
|
|
9686
|
+
type: import_types35.EdgeType.RUNS_ON,
|
|
9687
|
+
provenance: import_types35.Provenance.EXTRACTED,
|
|
9688
|
+
confidence: (0, import_types35.confidenceForExtracted)("structural"),
|
|
9380
9689
|
evidence: {
|
|
9381
9690
|
file: evidenceFile,
|
|
9382
9691
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -9390,7 +9699,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9390
9699
|
const result = addResourceEdge(
|
|
9391
9700
|
graph,
|
|
9392
9701
|
anchorId,
|
|
9393
|
-
|
|
9702
|
+
import_types35.EdgeType.CONNECTS_TO,
|
|
9394
9703
|
"cloudflare-route",
|
|
9395
9704
|
route,
|
|
9396
9705
|
evidenceFile,
|
|
@@ -9414,7 +9723,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9414
9723
|
const result = addResourceEdge(
|
|
9415
9724
|
graph,
|
|
9416
9725
|
anchorId,
|
|
9417
|
-
|
|
9726
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9418
9727
|
group.kind,
|
|
9419
9728
|
name,
|
|
9420
9729
|
evidenceFile,
|
|
@@ -9428,7 +9737,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9428
9737
|
const result = addResourceEdge(
|
|
9429
9738
|
graph,
|
|
9430
9739
|
anchorId,
|
|
9431
|
-
|
|
9740
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9432
9741
|
"cloudflare-cron",
|
|
9433
9742
|
cron,
|
|
9434
9743
|
evidenceFile,
|
|
@@ -9441,7 +9750,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9441
9750
|
const result = addResourceEdge(
|
|
9442
9751
|
graph,
|
|
9443
9752
|
anchorId,
|
|
9444
|
-
|
|
9753
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9445
9754
|
"cloudflare-env-var",
|
|
9446
9755
|
varName,
|
|
9447
9756
|
evidenceFile,
|
|
@@ -9454,15 +9763,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9454
9763
|
if (!svc.service) continue;
|
|
9455
9764
|
const target = workerIndex.get(svc.service);
|
|
9456
9765
|
if (target && target.anchorId !== anchorId) {
|
|
9457
|
-
const edgeId = (0,
|
|
9766
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types35.EdgeType.CALLS);
|
|
9458
9767
|
if (!graph.hasEdge(edgeId)) {
|
|
9459
9768
|
const edge = {
|
|
9460
9769
|
id: edgeId,
|
|
9461
9770
|
source: anchorId,
|
|
9462
9771
|
target: target.anchorId,
|
|
9463
|
-
type:
|
|
9464
|
-
provenance:
|
|
9465
|
-
confidence: (0,
|
|
9772
|
+
type: import_types35.EdgeType.CALLS,
|
|
9773
|
+
provenance: import_types35.Provenance.EXTRACTED,
|
|
9774
|
+
confidence: (0, import_types35.confidenceForExtracted)("structural"),
|
|
9466
9775
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
9467
9776
|
};
|
|
9468
9777
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -9473,7 +9782,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9473
9782
|
const result = addResourceEdge(
|
|
9474
9783
|
graph,
|
|
9475
9784
|
anchorId,
|
|
9476
|
-
|
|
9785
|
+
import_types35.EdgeType.DEPENDS_ON,
|
|
9477
9786
|
"cloudflare-service-binding",
|
|
9478
9787
|
svc.service,
|
|
9479
9788
|
evidenceFile,
|
|
@@ -9489,12 +9798,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
9489
9798
|
// src/extract/infra/vercel.ts
|
|
9490
9799
|
init_cjs_shims();
|
|
9491
9800
|
var import_node_fs23 = require("fs");
|
|
9492
|
-
var
|
|
9493
|
-
var
|
|
9801
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9802
|
+
var import_types36 = require("@neat.is/types");
|
|
9494
9803
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
9495
9804
|
async function readVercelConfig(dir) {
|
|
9496
9805
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
9497
|
-
const abs =
|
|
9806
|
+
const abs = import_node_path48.default.join(dir, filename);
|
|
9498
9807
|
if (!await exists2(abs)) continue;
|
|
9499
9808
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
9500
9809
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9503,7 +9812,7 @@ async function readVercelConfig(dir) {
|
|
|
9503
9812
|
return null;
|
|
9504
9813
|
}
|
|
9505
9814
|
async function readLinkedProjectName(dir) {
|
|
9506
|
-
const abs =
|
|
9815
|
+
const abs = import_node_path48.default.join(dir, ".vercel", "project.json");
|
|
9507
9816
|
if (!await exists2(abs)) return void 0;
|
|
9508
9817
|
const parsed = JSON.parse(await import_node_fs23.promises.readFile(abs, "utf8"));
|
|
9509
9818
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -9521,7 +9830,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9521
9830
|
read = await readVercelConfig(service.dir);
|
|
9522
9831
|
projectName = await readLinkedProjectName(service.dir);
|
|
9523
9832
|
} catch (err) {
|
|
9524
|
-
recordExtractionError("infra vercel",
|
|
9833
|
+
recordExtractionError("infra vercel", import_node_path48.default.relative(scanPath, service.dir), err);
|
|
9525
9834
|
continue;
|
|
9526
9835
|
}
|
|
9527
9836
|
if (!read && !projectName) continue;
|
|
@@ -9537,7 +9846,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9537
9846
|
const anchorId = service.node.id;
|
|
9538
9847
|
if (!read) continue;
|
|
9539
9848
|
const { config, relFile, raw } = read;
|
|
9540
|
-
const evidenceFile = toPosix(
|
|
9849
|
+
const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
|
|
9541
9850
|
const add = (edgeType, kind, name) => {
|
|
9542
9851
|
if (!name) return;
|
|
9543
9852
|
const result = emitPlatformResourceEdge(
|
|
@@ -9553,12 +9862,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9553
9862
|
nodesAdded += result.nodesAdded;
|
|
9554
9863
|
edgesAdded += result.edgesAdded;
|
|
9555
9864
|
};
|
|
9556
|
-
add(
|
|
9557
|
-
for (const cron of config.crons ?? []) add(
|
|
9558
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
9559
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
9865
|
+
add(import_types36.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
9866
|
+
for (const cron of config.crons ?? []) add(import_types36.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
9867
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
9868
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types36.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
9560
9869
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
9561
|
-
add(
|
|
9870
|
+
add(import_types36.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
9562
9871
|
}
|
|
9563
9872
|
}
|
|
9564
9873
|
return { nodesAdded, edgesAdded };
|
|
@@ -9567,13 +9876,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
9567
9876
|
// src/extract/infra/railway.ts
|
|
9568
9877
|
init_cjs_shims();
|
|
9569
9878
|
var import_node_fs24 = require("fs");
|
|
9570
|
-
var
|
|
9879
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9571
9880
|
var import_smol_toml3 = require("smol-toml");
|
|
9572
|
-
var
|
|
9881
|
+
var import_types37 = require("@neat.is/types");
|
|
9573
9882
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
9574
9883
|
async function readRailwayConfig(dir) {
|
|
9575
9884
|
for (const filename of RAILWAY_FILENAMES) {
|
|
9576
|
-
const abs =
|
|
9885
|
+
const abs = import_node_path49.default.join(dir, filename);
|
|
9577
9886
|
if (!await exists2(abs)) continue;
|
|
9578
9887
|
const raw = await import_node_fs24.promises.readFile(abs, "utf8");
|
|
9579
9888
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9589,7 +9898,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9589
9898
|
try {
|
|
9590
9899
|
read = await readRailwayConfig(service.dir);
|
|
9591
9900
|
} catch (err) {
|
|
9592
|
-
recordExtractionError("infra railway",
|
|
9901
|
+
recordExtractionError("infra railway", import_node_path49.default.relative(scanPath, service.dir), err);
|
|
9593
9902
|
continue;
|
|
9594
9903
|
}
|
|
9595
9904
|
if (!read) continue;
|
|
@@ -9599,7 +9908,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9599
9908
|
}
|
|
9600
9909
|
const anchorId = service.node.id;
|
|
9601
9910
|
const { config, relFile, raw } = read;
|
|
9602
|
-
const evidenceFile = toPosix(
|
|
9911
|
+
const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, import_node_path49.default.join(service.dir, relFile)));
|
|
9603
9912
|
const add = (edgeType, kind, name) => {
|
|
9604
9913
|
if (!name) return;
|
|
9605
9914
|
const result = emitPlatformResourceEdge(
|
|
@@ -9615,9 +9924,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9615
9924
|
nodesAdded += result.nodesAdded;
|
|
9616
9925
|
edgesAdded += result.edgesAdded;
|
|
9617
9926
|
};
|
|
9618
|
-
add(
|
|
9619
|
-
add(
|
|
9620
|
-
add(
|
|
9927
|
+
add(import_types37.EdgeType.RUNS_ON, "railway", "railway");
|
|
9928
|
+
add(import_types37.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
9929
|
+
add(import_types37.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
9621
9930
|
}
|
|
9622
9931
|
return { nodesAdded, edgesAdded };
|
|
9623
9932
|
}
|
|
@@ -9625,12 +9934,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9625
9934
|
// src/extract/infra/supabase.ts
|
|
9626
9935
|
init_cjs_shims();
|
|
9627
9936
|
var import_node_fs25 = require("fs");
|
|
9628
|
-
var
|
|
9937
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9629
9938
|
var import_smol_toml4 = require("smol-toml");
|
|
9630
|
-
var
|
|
9939
|
+
var import_types38 = require("@neat.is/types");
|
|
9631
9940
|
async function readSupabaseConfig(dir) {
|
|
9632
|
-
const relFile =
|
|
9633
|
-
const abs =
|
|
9941
|
+
const relFile = import_node_path50.default.join("supabase", "config.toml");
|
|
9942
|
+
const abs = import_node_path50.default.join(dir, relFile);
|
|
9634
9943
|
if (!await exists2(abs)) return null;
|
|
9635
9944
|
const raw = await import_node_fs25.promises.readFile(abs, "utf8");
|
|
9636
9945
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -9644,7 +9953,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9644
9953
|
try {
|
|
9645
9954
|
read = await readSupabaseConfig(service.dir);
|
|
9646
9955
|
} catch (err) {
|
|
9647
|
-
recordExtractionError("infra supabase",
|
|
9956
|
+
recordExtractionError("infra supabase", import_node_path50.default.relative(scanPath, service.dir), err);
|
|
9648
9957
|
continue;
|
|
9649
9958
|
}
|
|
9650
9959
|
if (!read) continue;
|
|
@@ -9659,7 +9968,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9659
9968
|
});
|
|
9660
9969
|
}
|
|
9661
9970
|
const anchorId = service.node.id;
|
|
9662
|
-
const evidenceFile = toPosix(
|
|
9971
|
+
const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, import_node_path50.default.join(service.dir, relFile)));
|
|
9663
9972
|
const add = (edgeType, kind, name) => {
|
|
9664
9973
|
if (!name) return;
|
|
9665
9974
|
const result = emitPlatformResourceEdge(
|
|
@@ -9675,10 +9984,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9675
9984
|
nodesAdded += result.nodesAdded;
|
|
9676
9985
|
edgesAdded += result.edgesAdded;
|
|
9677
9986
|
};
|
|
9678
|
-
add(
|
|
9679
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
9680
|
-
if (config.storage) add(
|
|
9681
|
-
if (config.auth) add(
|
|
9987
|
+
add(import_types38.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
9988
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types38.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
9989
|
+
if (config.storage) add(import_types38.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
9990
|
+
if (config.auth) add(import_types38.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
9682
9991
|
}
|
|
9683
9992
|
return { nodesAdded, edgesAdded };
|
|
9684
9993
|
}
|
|
@@ -9700,17 +10009,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
9700
10009
|
}
|
|
9701
10010
|
|
|
9702
10011
|
// src/extract/index.ts
|
|
9703
|
-
var
|
|
10012
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
9704
10013
|
|
|
9705
10014
|
// src/extract/retire.ts
|
|
9706
10015
|
init_cjs_shims();
|
|
9707
10016
|
var import_node_fs26 = require("fs");
|
|
9708
|
-
var
|
|
9709
|
-
var
|
|
10017
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
10018
|
+
var import_types39 = require("@neat.is/types");
|
|
9710
10019
|
function dropOrphanedFileNodes(graph) {
|
|
9711
10020
|
const orphans = [];
|
|
9712
10021
|
graph.forEachNode((id, attrs) => {
|
|
9713
|
-
if (attrs.type !==
|
|
10022
|
+
if (attrs.type !== import_types39.NodeType.FileNode) return;
|
|
9714
10023
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
9715
10024
|
orphans.push(id);
|
|
9716
10025
|
}
|
|
@@ -9723,14 +10032,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
9723
10032
|
const bases = [scanPath, ...serviceDirs];
|
|
9724
10033
|
graph.forEachEdge((id, attrs) => {
|
|
9725
10034
|
const edge = attrs;
|
|
9726
|
-
if (edge.provenance !==
|
|
10035
|
+
if (edge.provenance !== import_types39.Provenance.EXTRACTED) return;
|
|
9727
10036
|
const evidenceFile = edge.evidence?.file;
|
|
9728
10037
|
if (!evidenceFile) return;
|
|
9729
|
-
if (
|
|
10038
|
+
if (import_node_path51.default.isAbsolute(evidenceFile)) {
|
|
9730
10039
|
if (!(0, import_node_fs26.existsSync)(evidenceFile)) toDrop.push(id);
|
|
9731
10040
|
return;
|
|
9732
10041
|
}
|
|
9733
|
-
const found = bases.some((base) => (0, import_node_fs26.existsSync)(
|
|
10042
|
+
const found = bases.some((base) => (0, import_node_fs26.existsSync)(import_node_path51.default.join(base, evidenceFile)));
|
|
9734
10043
|
if (!found) toDrop.push(id);
|
|
9735
10044
|
});
|
|
9736
10045
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -9783,7 +10092,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
9783
10092
|
}
|
|
9784
10093
|
const droppedEntries = drainDroppedExtracted();
|
|
9785
10094
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
9786
|
-
const rejectedPath =
|
|
10095
|
+
const rejectedPath = import_node_path52.default.join(import_node_path52.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
9787
10096
|
try {
|
|
9788
10097
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
9789
10098
|
} catch (err) {
|
|
@@ -9895,9 +10204,9 @@ function canonicalJson(value) {
|
|
|
9895
10204
|
// src/persist.ts
|
|
9896
10205
|
init_cjs_shims();
|
|
9897
10206
|
var import_node_fs28 = require("fs");
|
|
9898
|
-
var
|
|
9899
|
-
var
|
|
9900
|
-
var SCHEMA_VERSION =
|
|
10207
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
10208
|
+
var import_types40 = require("@neat.is/types");
|
|
10209
|
+
var SCHEMA_VERSION = 6;
|
|
9901
10210
|
function migrateV1ToV2(payload) {
|
|
9902
10211
|
const nodes = payload.graph.nodes;
|
|
9903
10212
|
if (Array.isArray(nodes)) {
|
|
@@ -9915,18 +10224,30 @@ function migrateV3ToV4(payload) {
|
|
|
9915
10224
|
function migrateV4ToV5(payload) {
|
|
9916
10225
|
return { ...payload, schemaVersion: 5 };
|
|
9917
10226
|
}
|
|
10227
|
+
function migrateV5ToV6(payload) {
|
|
10228
|
+
const nodes = payload.graph.nodes;
|
|
10229
|
+
if (Array.isArray(nodes)) {
|
|
10230
|
+
for (const node of nodes) {
|
|
10231
|
+
const attrs = node.attributes;
|
|
10232
|
+
if (!attrs || attrs.type !== import_types40.NodeType.InfraNode) continue;
|
|
10233
|
+
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
10234
|
+
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
10235
|
+
}
|
|
10236
|
+
}
|
|
10237
|
+
return { ...payload, schemaVersion: 6 };
|
|
10238
|
+
}
|
|
9918
10239
|
function migrateV2ToV3(payload) {
|
|
9919
10240
|
const edges = payload.graph.edges;
|
|
9920
10241
|
if (Array.isArray(edges)) {
|
|
9921
10242
|
for (const edge of edges) {
|
|
9922
10243
|
const attrs = edge.attributes;
|
|
9923
10244
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
9924
|
-
attrs.provenance =
|
|
10245
|
+
attrs.provenance = import_types40.Provenance.OBSERVED;
|
|
9925
10246
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
9926
10247
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
9927
10248
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
9928
10249
|
if (type && source && target) {
|
|
9929
|
-
const newId = (0,
|
|
10250
|
+
const newId = (0, import_types40.observedEdgeId)(source, target, type);
|
|
9930
10251
|
attrs.id = newId;
|
|
9931
10252
|
if (edge.key) edge.key = newId;
|
|
9932
10253
|
}
|
|
@@ -9935,7 +10256,7 @@ function migrateV2ToV3(payload) {
|
|
|
9935
10256
|
return { ...payload, schemaVersion: 3 };
|
|
9936
10257
|
}
|
|
9937
10258
|
async function ensureDir(filePath) {
|
|
9938
|
-
await import_node_fs28.promises.mkdir(
|
|
10259
|
+
await import_node_fs28.promises.mkdir(import_node_path53.default.dirname(filePath), { recursive: true });
|
|
9939
10260
|
}
|
|
9940
10261
|
async function saveGraphToDisk(graph, outPath) {
|
|
9941
10262
|
await ensureDir(outPath);
|
|
@@ -9969,6 +10290,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
9969
10290
|
if (payload.schemaVersion === 4) {
|
|
9970
10291
|
payload = migrateV4ToV5(payload);
|
|
9971
10292
|
}
|
|
10293
|
+
if (payload.schemaVersion === 5) {
|
|
10294
|
+
payload = migrateV5ToV6(payload);
|
|
10295
|
+
}
|
|
9972
10296
|
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
9973
10297
|
throw new Error(
|
|
9974
10298
|
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
@@ -10019,23 +10343,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
10019
10343
|
|
|
10020
10344
|
// src/projects.ts
|
|
10021
10345
|
init_cjs_shims();
|
|
10022
|
-
var
|
|
10346
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
10023
10347
|
function pathsForProject(project, baseDir) {
|
|
10024
10348
|
if (project === DEFAULT_PROJECT) {
|
|
10025
10349
|
return {
|
|
10026
|
-
snapshotPath:
|
|
10027
|
-
errorsPath:
|
|
10028
|
-
staleEventsPath:
|
|
10029
|
-
embeddingsCachePath:
|
|
10030
|
-
policyViolationsPath:
|
|
10350
|
+
snapshotPath: import_node_path54.default.join(baseDir, "graph.json"),
|
|
10351
|
+
errorsPath: import_node_path54.default.join(baseDir, "errors.ndjson"),
|
|
10352
|
+
staleEventsPath: import_node_path54.default.join(baseDir, "stale-events.ndjson"),
|
|
10353
|
+
embeddingsCachePath: import_node_path54.default.join(baseDir, "embeddings.json"),
|
|
10354
|
+
policyViolationsPath: import_node_path54.default.join(baseDir, "policy-violations.ndjson")
|
|
10031
10355
|
};
|
|
10032
10356
|
}
|
|
10033
10357
|
return {
|
|
10034
|
-
snapshotPath:
|
|
10035
|
-
errorsPath:
|
|
10036
|
-
staleEventsPath:
|
|
10037
|
-
embeddingsCachePath:
|
|
10038
|
-
policyViolationsPath:
|
|
10358
|
+
snapshotPath: import_node_path54.default.join(baseDir, `${project}.json`),
|
|
10359
|
+
errorsPath: import_node_path54.default.join(baseDir, `errors.${project}.ndjson`),
|
|
10360
|
+
staleEventsPath: import_node_path54.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
10361
|
+
embeddingsCachePath: import_node_path54.default.join(baseDir, `embeddings.${project}.json`),
|
|
10362
|
+
policyViolationsPath: import_node_path54.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
10039
10363
|
};
|
|
10040
10364
|
}
|
|
10041
10365
|
var Projects = class {
|
|
@@ -10077,18 +10401,18 @@ function parseExtraProjects(raw) {
|
|
|
10077
10401
|
init_cjs_shims();
|
|
10078
10402
|
var import_node_fs29 = require("fs");
|
|
10079
10403
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
10080
|
-
var
|
|
10081
|
-
var
|
|
10404
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
10405
|
+
var import_types41 = require("@neat.is/types");
|
|
10082
10406
|
function neatHome() {
|
|
10083
10407
|
const override = process.env.NEAT_HOME;
|
|
10084
|
-
if (override && override.length > 0) return
|
|
10085
|
-
return
|
|
10408
|
+
if (override && override.length > 0) return import_node_path55.default.resolve(override);
|
|
10409
|
+
return import_node_path55.default.join(import_node_os3.default.homedir(), ".neat");
|
|
10086
10410
|
}
|
|
10087
10411
|
function registryPath() {
|
|
10088
|
-
return
|
|
10412
|
+
return import_node_path55.default.join(neatHome(), "projects.json");
|
|
10089
10413
|
}
|
|
10090
10414
|
function daemonsDir() {
|
|
10091
|
-
return
|
|
10415
|
+
return import_node_path55.default.join(neatHome(), "daemons");
|
|
10092
10416
|
}
|
|
10093
10417
|
function isFiniteInt(v) {
|
|
10094
10418
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -10129,7 +10453,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
10129
10453
|
const out = [];
|
|
10130
10454
|
for (const name of names) {
|
|
10131
10455
|
if (!name.endsWith(".json")) continue;
|
|
10132
|
-
const file =
|
|
10456
|
+
const file = import_node_path55.default.join(dir, name);
|
|
10133
10457
|
let raw;
|
|
10134
10458
|
try {
|
|
10135
10459
|
raw = await import_node_fs29.promises.readFile(file, "utf8");
|
|
@@ -10168,7 +10492,7 @@ async function readRegistry() {
|
|
|
10168
10492
|
throw err;
|
|
10169
10493
|
}
|
|
10170
10494
|
const parsed = JSON.parse(raw);
|
|
10171
|
-
return
|
|
10495
|
+
return import_types41.RegistryFileSchema.parse(parsed);
|
|
10172
10496
|
}
|
|
10173
10497
|
async function getProject(name) {
|
|
10174
10498
|
const reg = await readRegistry();
|
|
@@ -10243,7 +10567,7 @@ init_auth();
|
|
|
10243
10567
|
// src/connectors-config.ts
|
|
10244
10568
|
init_cjs_shims();
|
|
10245
10569
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
10246
|
-
var
|
|
10570
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
10247
10571
|
var import_node_fs30 = require("fs");
|
|
10248
10572
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
10249
10573
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -10258,11 +10582,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
10258
10582
|
};
|
|
10259
10583
|
function neatHome2() {
|
|
10260
10584
|
const override = process.env.NEAT_HOME;
|
|
10261
|
-
if (override && override.length > 0) return
|
|
10262
|
-
return
|
|
10585
|
+
if (override && override.length > 0) return import_node_path56.default.resolve(override);
|
|
10586
|
+
return import_node_path56.default.join(import_node_os4.default.homedir(), ".neat");
|
|
10263
10587
|
}
|
|
10264
10588
|
function connectorsConfigPath(home = neatHome2()) {
|
|
10265
|
-
return
|
|
10589
|
+
return import_node_path56.default.join(home, "connectors.json");
|
|
10266
10590
|
}
|
|
10267
10591
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
10268
10592
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -10449,15 +10773,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
10449
10773
|
|
|
10450
10774
|
// src/connectors/index.ts
|
|
10451
10775
|
init_cjs_shims();
|
|
10452
|
-
var
|
|
10776
|
+
var import_types42 = require("@neat.is/types");
|
|
10453
10777
|
var NO_ENV = "unknown";
|
|
10454
10778
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
10455
10779
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10456
10780
|
const sites = [];
|
|
10457
10781
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
10458
10782
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
10459
|
-
if (edge.provenance !==
|
|
10460
|
-
const parsed = (0,
|
|
10783
|
+
if (edge.provenance !== import_types42.Provenance.EXTRACTED) continue;
|
|
10784
|
+
const parsed = (0, import_types42.parseFileId)(edge.source);
|
|
10461
10785
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
10462
10786
|
const site = { relPath: edge.evidence.file };
|
|
10463
10787
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -10468,7 +10792,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
10468
10792
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
10469
10793
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10470
10794
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
10471
|
-
if (attrs.type !==
|
|
10795
|
+
if (attrs.type !== import_types42.NodeType.RouteNode || !attrs.path) return void 0;
|
|
10472
10796
|
const site = { relPath: attrs.path };
|
|
10473
10797
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
10474
10798
|
return site;
|
|
@@ -10488,6 +10812,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
10488
10812
|
const { kind, name, provider } = resolved.ensureInfraNode;
|
|
10489
10813
|
ensureInfraNode(graph, kind, name, provider);
|
|
10490
10814
|
}
|
|
10815
|
+
mergeObservedColumns(graph, resolved.targetNodeId, signal.columns);
|
|
10491
10816
|
const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
10492
10817
|
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
|
|
10493
10818
|
const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
|
|
@@ -10877,6 +11202,7 @@ async function fetchSupabaseEdgeLogs(config, token, startIso, endIso, fetchImpl
|
|
|
10877
11202
|
|
|
10878
11203
|
// src/connectors/supabase/map.ts
|
|
10879
11204
|
init_cjs_shims();
|
|
11205
|
+
init_otel();
|
|
10880
11206
|
|
|
10881
11207
|
// src/connectors/supabase/types.ts
|
|
10882
11208
|
init_cjs_shims();
|
|
@@ -10902,10 +11228,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10902
11228
|
// src/connectors/supabase/map.ts
|
|
10903
11229
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10904
11230
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10905
|
-
function targetFromRestPath(
|
|
10906
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
11231
|
+
function targetFromRestPath(path59) {
|
|
11232
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path59);
|
|
10907
11233
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10908
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
11234
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path59);
|
|
10909
11235
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10910
11236
|
return null;
|
|
10911
11237
|
}
|
|
@@ -10969,12 +11295,14 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
|
|
|
10969
11295
|
if (delta <= 0) continue;
|
|
10970
11296
|
const table = tableNameFromQueryText(row.query);
|
|
10971
11297
|
if (!table) continue;
|
|
11298
|
+
const columns = columnsFromSqlStatement(row.query);
|
|
10972
11299
|
signals.push({
|
|
10973
11300
|
targetKind: SUPABASE_TABLE_TARGET_KIND,
|
|
10974
11301
|
targetName: table,
|
|
10975
11302
|
callCount: delta,
|
|
10976
11303
|
errorCount: 0,
|
|
10977
|
-
lastObservedIso: nowIso2
|
|
11304
|
+
lastObservedIso: nowIso2,
|
|
11305
|
+
...columns.length > 0 ? { columns } : {}
|
|
10978
11306
|
});
|
|
10979
11307
|
}
|
|
10980
11308
|
for (const queryid of [...previous.keys()]) {
|
|
@@ -11014,23 +11342,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
11014
11342
|
|
|
11015
11343
|
// src/connectors/supabase/resolve.ts
|
|
11016
11344
|
init_cjs_shims();
|
|
11017
|
-
var
|
|
11345
|
+
var import_types44 = require("@neat.is/types");
|
|
11018
11346
|
function createSupabaseResolveTarget(graph, config) {
|
|
11019
11347
|
return (signal, _ctx) => {
|
|
11020
11348
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
11021
11349
|
return null;
|
|
11022
11350
|
}
|
|
11023
|
-
const subResourceId = (0,
|
|
11351
|
+
const subResourceId = (0, import_types44.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
11024
11352
|
if (graph.hasNode(subResourceId)) {
|
|
11025
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
11353
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
|
|
11026
11354
|
}
|
|
11027
|
-
const bareResourceId = (0,
|
|
11355
|
+
const bareResourceId = (0, import_types44.infraId)(signal.targetKind, signal.targetName);
|
|
11028
11356
|
if (graph.hasNode(bareResourceId)) {
|
|
11029
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
11357
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
|
|
11030
11358
|
}
|
|
11031
|
-
const projectLevelId = (0,
|
|
11359
|
+
const projectLevelId = (0, import_types44.infraId)("supabase", config.nodeRef);
|
|
11032
11360
|
if (graph.hasNode(projectLevelId)) {
|
|
11033
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
11361
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
|
|
11034
11362
|
}
|
|
11035
11363
|
return null;
|
|
11036
11364
|
};
|
|
@@ -11123,7 +11451,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
11123
11451
|
|
|
11124
11452
|
// src/connectors/railway/index.ts
|
|
11125
11453
|
init_cjs_shims();
|
|
11126
|
-
var
|
|
11454
|
+
var import_types48 = require("@neat.is/types");
|
|
11127
11455
|
|
|
11128
11456
|
// src/connectors/railway/client.ts
|
|
11129
11457
|
init_cjs_shims();
|
|
@@ -11274,7 +11602,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
11274
11602
|
const out = [];
|
|
11275
11603
|
graph.forEachNode((_id, attrs) => {
|
|
11276
11604
|
const node = attrs;
|
|
11277
|
-
if (node.type !==
|
|
11605
|
+
if (node.type !== import_types48.NodeType.RouteNode) return;
|
|
11278
11606
|
const route = attrs;
|
|
11279
11607
|
if (route.service !== serviceName) return;
|
|
11280
11608
|
out.push({
|
|
@@ -11378,12 +11706,12 @@ function createRailwayResolveTarget(config) {
|
|
|
11378
11706
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
11379
11707
|
if (!serviceName) return null;
|
|
11380
11708
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
11381
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11709
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types48.EdgeType.CALLS };
|
|
11382
11710
|
}
|
|
11383
11711
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
11384
11712
|
const peerName = config.serviceNameById[signal.targetName];
|
|
11385
11713
|
if (!peerName) return null;
|
|
11386
|
-
return { targetNodeId: (0,
|
|
11714
|
+
return { targetNodeId: (0, import_types48.serviceId)(peerName), serviceName, edgeType: import_types48.EdgeType.CONNECTS_TO };
|
|
11387
11715
|
}
|
|
11388
11716
|
return null;
|
|
11389
11717
|
};
|
|
@@ -11507,9 +11835,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11507
11835
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11508
11836
|
if (secondSep === -1) return null;
|
|
11509
11837
|
const method = rest.slice(0, secondSep);
|
|
11510
|
-
const
|
|
11511
|
-
if (!resourceName || !method || !
|
|
11512
|
-
return { resourceName, method, path:
|
|
11838
|
+
const path59 = rest.slice(secondSep + 1);
|
|
11839
|
+
if (!resourceName || !method || !path59) return null;
|
|
11840
|
+
return { resourceName, method, path: path59 };
|
|
11513
11841
|
}
|
|
11514
11842
|
function resourceNameFor(type, labels) {
|
|
11515
11843
|
if (!labels) return null;
|
|
@@ -11547,14 +11875,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
11547
11875
|
if (!req) return null;
|
|
11548
11876
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11549
11877
|
const method = req.requestMethod.toUpperCase();
|
|
11550
|
-
const
|
|
11551
|
-
if (
|
|
11878
|
+
const path59 = pathFromRequestUrl(req.requestUrl);
|
|
11879
|
+
if (path59 === null) return null;
|
|
11552
11880
|
const timestamp = entry.timestamp;
|
|
11553
11881
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11554
11882
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11555
11883
|
return {
|
|
11556
11884
|
targetKind: resourceType,
|
|
11557
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11885
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path59 }),
|
|
11558
11886
|
callCount: 1,
|
|
11559
11887
|
errorCount: isError ? 1 : 0,
|
|
11560
11888
|
lastObservedIso: timestamp
|
|
@@ -11571,7 +11899,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11571
11899
|
|
|
11572
11900
|
// src/connectors/firebase/resolve.ts
|
|
11573
11901
|
init_cjs_shims();
|
|
11574
|
-
var
|
|
11902
|
+
var import_types49 = require("@neat.is/types");
|
|
11575
11903
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11576
11904
|
switch (resourceType) {
|
|
11577
11905
|
case "cloud_function":
|
|
@@ -11586,7 +11914,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11586
11914
|
const entries = [];
|
|
11587
11915
|
graph.forEachNode((_id, attrs) => {
|
|
11588
11916
|
const node = attrs;
|
|
11589
|
-
if (node.type !==
|
|
11917
|
+
if (node.type !== import_types49.NodeType.RouteNode) return;
|
|
11590
11918
|
const route = attrs;
|
|
11591
11919
|
if (route.service !== serviceName) return;
|
|
11592
11920
|
entries.push({
|
|
@@ -11618,7 +11946,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11618
11946
|
return {
|
|
11619
11947
|
targetNodeId: match.routeNodeId,
|
|
11620
11948
|
serviceName,
|
|
11621
|
-
edgeType:
|
|
11949
|
+
edgeType: import_types49.EdgeType.CALLS
|
|
11622
11950
|
};
|
|
11623
11951
|
};
|
|
11624
11952
|
}
|
|
@@ -11645,7 +11973,7 @@ init_cjs_shims();
|
|
|
11645
11973
|
|
|
11646
11974
|
// src/connectors/cloudflare/connector.ts
|
|
11647
11975
|
init_cjs_shims();
|
|
11648
|
-
var
|
|
11976
|
+
var import_types51 = require("@neat.is/types");
|
|
11649
11977
|
|
|
11650
11978
|
// src/connectors/cloudflare/client.ts
|
|
11651
11979
|
init_cjs_shims();
|
|
@@ -11761,7 +12089,7 @@ function mapEventToSignal(event) {
|
|
|
11761
12089
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11762
12090
|
const statusCode = metadata?.statusCode;
|
|
11763
12091
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11764
|
-
const
|
|
12092
|
+
const path59 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11765
12093
|
return {
|
|
11766
12094
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11767
12095
|
targetName: scriptName,
|
|
@@ -11769,7 +12097,7 @@ function mapEventToSignal(event) {
|
|
|
11769
12097
|
errorCount: isError ? 1 : 0,
|
|
11770
12098
|
lastObservedIso: observedAt.toISOString(),
|
|
11771
12099
|
method,
|
|
11772
|
-
...
|
|
12100
|
+
...path59 ? { path: path59 } : {},
|
|
11773
12101
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11774
12102
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11775
12103
|
};
|
|
@@ -11809,19 +12137,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11809
12137
|
graph.forEachNode((id, attrs) => {
|
|
11810
12138
|
if (found) return;
|
|
11811
12139
|
const a = attrs;
|
|
11812
|
-
if (a.type ===
|
|
12140
|
+
if (a.type === import_types51.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11813
12141
|
found = id;
|
|
11814
12142
|
}
|
|
11815
12143
|
});
|
|
11816
12144
|
return found;
|
|
11817
12145
|
}
|
|
11818
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11819
|
-
const normalizedPath = normalizePathTemplate(
|
|
12146
|
+
function findMatchingRouteNode(graph, serviceName, method, path59) {
|
|
12147
|
+
const normalizedPath = normalizePathTemplate(path59);
|
|
11820
12148
|
let found = null;
|
|
11821
12149
|
graph.forEachNode((id, attrs) => {
|
|
11822
12150
|
if (found) return;
|
|
11823
12151
|
const a = attrs;
|
|
11824
|
-
if (a.type !==
|
|
12152
|
+
if (a.type !== import_types51.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11825
12153
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11826
12154
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11827
12155
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11833,18 +12161,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11833
12161
|
return (signal) => {
|
|
11834
12162
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11835
12163
|
const scriptName = signal.targetName;
|
|
11836
|
-
const { method, path:
|
|
12164
|
+
const { method, path: path59 } = signal;
|
|
11837
12165
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11838
|
-
if (!method || !
|
|
11839
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
12166
|
+
if (!method || !path59) return wholeFileId;
|
|
12167
|
+
return findMatchingRouteNode(graph, serviceName, method, path59) ?? wholeFileId;
|
|
11840
12168
|
};
|
|
11841
12169
|
const mapping = config.workers?.[scriptName];
|
|
11842
12170
|
if (mapping) {
|
|
11843
|
-
const wholeFileId = (0,
|
|
12171
|
+
const wholeFileId = (0, import_types51.fileId)(mapping.service, mapping.entryFile);
|
|
11844
12172
|
return {
|
|
11845
12173
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11846
12174
|
serviceName: mapping.service,
|
|
11847
|
-
edgeType:
|
|
12175
|
+
edgeType: import_types51.EdgeType.CALLS
|
|
11848
12176
|
};
|
|
11849
12177
|
}
|
|
11850
12178
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11853,13 +12181,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11853
12181
|
return {
|
|
11854
12182
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11855
12183
|
serviceName: fileNode.service,
|
|
11856
|
-
edgeType:
|
|
12184
|
+
edgeType: import_types51.EdgeType.CALLS
|
|
11857
12185
|
};
|
|
11858
12186
|
}
|
|
11859
12187
|
return {
|
|
11860
|
-
targetNodeId: (0,
|
|
12188
|
+
targetNodeId: (0, import_types51.infraId)("cloudflare-worker", scriptName),
|
|
11861
12189
|
serviceName: scriptName,
|
|
11862
|
-
edgeType:
|
|
12190
|
+
edgeType: import_types51.EdgeType.CALLS,
|
|
11863
12191
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11864
12192
|
};
|
|
11865
12193
|
};
|
|
@@ -12037,12 +12365,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
12037
12365
|
if (!prior || calls <= prior.calls) continue;
|
|
12038
12366
|
const table = tableFromSqlStatement(row.query);
|
|
12039
12367
|
if (!table) continue;
|
|
12368
|
+
const columns = columnsFromSqlStatement(row.query);
|
|
12040
12369
|
signals.push({
|
|
12041
12370
|
targetKind: NEON_SQL_TABLE_TARGET_KIND,
|
|
12042
12371
|
targetName: table,
|
|
12043
12372
|
callCount: calls - prior.calls,
|
|
12044
12373
|
errorCount: 0,
|
|
12045
|
-
lastObservedIso: observedAtIso
|
|
12374
|
+
lastObservedIso: observedAtIso,
|
|
12375
|
+
...columns.length > 0 ? { columns } : {}
|
|
12046
12376
|
});
|
|
12047
12377
|
}
|
|
12048
12378
|
for (const queryid of previous.keys()) {
|
|
@@ -12053,14 +12383,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
12053
12383
|
|
|
12054
12384
|
// src/connectors/neon/resolve.ts
|
|
12055
12385
|
init_cjs_shims();
|
|
12056
|
-
var
|
|
12386
|
+
var import_types55 = require("@neat.is/types");
|
|
12057
12387
|
function createNeonResolveTarget(config) {
|
|
12058
12388
|
return (signal) => {
|
|
12059
12389
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
12060
12390
|
return {
|
|
12061
|
-
targetNodeId: (0,
|
|
12391
|
+
targetNodeId: (0, import_types55.infraId)("sql-table", signal.targetName),
|
|
12062
12392
|
serviceName: config.serviceName,
|
|
12063
|
-
edgeType:
|
|
12393
|
+
edgeType: import_types55.EdgeType.CALLS,
|
|
12064
12394
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
12065
12395
|
};
|
|
12066
12396
|
};
|
|
@@ -12582,11 +12912,11 @@ function registerRoutes(scope, ctx) {
|
|
|
12582
12912
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12583
12913
|
const parsed = [];
|
|
12584
12914
|
for (const c of candidates) {
|
|
12585
|
-
const r =
|
|
12915
|
+
const r = import_types58.DivergenceTypeSchema.safeParse(c);
|
|
12586
12916
|
if (!r.success) {
|
|
12587
12917
|
return reply.code(400).send({
|
|
12588
12918
|
error: `unknown divergence type "${c}"`,
|
|
12589
|
-
allowed:
|
|
12919
|
+
allowed: import_types58.DivergenceTypeSchema.options
|
|
12590
12920
|
});
|
|
12591
12921
|
}
|
|
12592
12922
|
parsed.push(r.data);
|
|
@@ -12895,7 +13225,7 @@ function registerRoutes(scope, ctx) {
|
|
|
12895
13225
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
12896
13226
|
let violations = await log.readAll();
|
|
12897
13227
|
if (req.query.severity) {
|
|
12898
|
-
const sev =
|
|
13228
|
+
const sev = import_types58.PolicySeveritySchema.safeParse(req.query.severity);
|
|
12899
13229
|
if (!sev.success) {
|
|
12900
13230
|
return reply.code(400).send({
|
|
12901
13231
|
error: "invalid severity",
|
|
@@ -12934,7 +13264,7 @@ function registerRoutes(scope, ctx) {
|
|
|
12934
13264
|
scope.post("/policies/check", async (req, reply) => {
|
|
12935
13265
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
12936
13266
|
if (!proj) return;
|
|
12937
|
-
const parsed =
|
|
13267
|
+
const parsed = import_types58.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
12938
13268
|
if (!parsed.success) {
|
|
12939
13269
|
return reply.code(400).send({
|
|
12940
13270
|
error: "invalid /policies/check body",
|
|
@@ -13256,7 +13586,7 @@ init_otel_grpc();
|
|
|
13256
13586
|
// src/search.ts
|
|
13257
13587
|
init_cjs_shims();
|
|
13258
13588
|
var import_node_fs31 = require("fs");
|
|
13259
|
-
var
|
|
13589
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
13260
13590
|
var import_node_crypto4 = require("crypto");
|
|
13261
13591
|
var DEFAULT_LIMIT = 10;
|
|
13262
13592
|
var NOMIC_DIM = 768;
|
|
@@ -13419,7 +13749,7 @@ async function readCache(cachePath) {
|
|
|
13419
13749
|
}
|
|
13420
13750
|
}
|
|
13421
13751
|
async function writeCache(cachePath, cache) {
|
|
13422
|
-
await import_node_fs31.promises.mkdir(
|
|
13752
|
+
await import_node_fs31.promises.mkdir(import_node_path57.default.dirname(cachePath), { recursive: true });
|
|
13423
13753
|
await import_node_fs31.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
13424
13754
|
}
|
|
13425
13755
|
var VectorIndex = class {
|
|
@@ -13600,14 +13930,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
13600
13930
|
async function main() {
|
|
13601
13931
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
13602
13932
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
13603
|
-
const baseDir = baseDirEnv ?
|
|
13604
|
-
const defaultScanPath =
|
|
13933
|
+
const baseDir = baseDirEnv ? import_node_path58.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path58.default.resolve(import_node_path58.default.dirname(legacyOutPath)) : import_node_path58.default.resolve("./neat-out");
|
|
13934
|
+
const defaultScanPath = import_node_path58.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
13605
13935
|
const registry = new Projects();
|
|
13606
13936
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
13607
13937
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
13608
13938
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
13609
13939
|
const projectScan = process.env[envKey];
|
|
13610
|
-
await bootProject(registry, name, projectScan ?
|
|
13940
|
+
await bootProject(registry, name, projectScan ? import_node_path58.default.resolve(projectScan) : void 0, baseDir);
|
|
13611
13941
|
}
|
|
13612
13942
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
13613
13943
|
const port = Number(process.env.PORT ?? 8080);
|