@neat.is/core 0.7.0 → 0.7.1
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 +997 -649
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +25 -7
- 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/neatd.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path61 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path61) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path61)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path38.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path38.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url,
|
|
266
|
+
var import_node_url, import_node_path38, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path38 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -355,6 +355,46 @@ function tableFromSqlStatement(sql) {
|
|
|
355
355
|
const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
|
|
356
356
|
return m ? m[1] : null;
|
|
357
357
|
}
|
|
358
|
+
function columnsFromSqlStatement(sql) {
|
|
359
|
+
if (typeof sql !== "string" || sql.length === 0) return [];
|
|
360
|
+
const s = sql.replace(/\s+/g, " ").trim();
|
|
361
|
+
if (/\bjoin\b/i.test(s)) return [];
|
|
362
|
+
if ((s.match(/\bfrom\b/gi) ?? []).length > 1) return [];
|
|
363
|
+
const bare = (raw) => {
|
|
364
|
+
let t = raw.trim().replace(/"/g, "");
|
|
365
|
+
if (/[()*]/.test(t)) return null;
|
|
366
|
+
t = t.split(/\s+as\s+/i)[0].trim();
|
|
367
|
+
t = t.split(".").pop();
|
|
368
|
+
return /^[a-z_][\w$]*$/i.test(t) ? t.toLowerCase() : null;
|
|
369
|
+
};
|
|
370
|
+
const isCol = (c) => c !== null;
|
|
371
|
+
const cols = (list) => [...new Set(list.split(",").map(bare).filter(isCol))];
|
|
372
|
+
const whereCols = (w) => w ? [
|
|
373
|
+
...new Set(
|
|
374
|
+
[
|
|
375
|
+
...w.matchAll(
|
|
376
|
+
/(?:"?[\w$]+"?\.)?"?([a-z_][\w$]*)"?\s*(?:=|<|>|<=|>=|<>|!=|\bis\b|\bin\b|\blike\b)/gi
|
|
377
|
+
)
|
|
378
|
+
].map((match) => match[1].toLowerCase()).filter((c) => !/^(and|or|not|null)$/i.test(c))
|
|
379
|
+
)
|
|
380
|
+
] : [];
|
|
381
|
+
let m;
|
|
382
|
+
if (m = /\binsert\s+into\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s*\(([^)]*)\)/i.exec(s)) {
|
|
383
|
+
return cols(m[1]);
|
|
384
|
+
}
|
|
385
|
+
if (m = /\bupdate\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?\s+set\s+(.+?)(?:\bwhere\b(.+))?$/i.exec(s)) {
|
|
386
|
+
const set = m[1].split(",").map((p) => bare(p.split("=")[0])).filter(isCol);
|
|
387
|
+
return [.../* @__PURE__ */ new Set([...set, ...whereCols(m[2] ?? void 0)])];
|
|
388
|
+
}
|
|
389
|
+
if (m = /\bdelete\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
|
|
390
|
+
return whereCols(m[1] ?? void 0);
|
|
391
|
+
}
|
|
392
|
+
if (m = /\bselect\s+(.+?)\s+from\s+(?:"?[\w$]+"?\.)?"?[\w$]+"?(?:\s+where\b(.+))?$/i.exec(s)) {
|
|
393
|
+
if (m[1].trim() === "*") return [];
|
|
394
|
+
return [.../* @__PURE__ */ new Set([...cols(m[1]), ...whereCols(m[2] ?? void 0)])];
|
|
395
|
+
}
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
358
398
|
function messagingDestinationOf(attrs) {
|
|
359
399
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
360
400
|
const v = attrs[key];
|
|
@@ -375,8 +415,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
375
415
|
const v = attrs[key];
|
|
376
416
|
if (typeof v === "string" && v.length > 0) {
|
|
377
417
|
const q = v.indexOf("?");
|
|
378
|
-
const
|
|
379
|
-
if (
|
|
418
|
+
const path61 = q === -1 ? v : v.slice(0, q);
|
|
419
|
+
if (path61.length > 0) return path61;
|
|
380
420
|
}
|
|
381
421
|
}
|
|
382
422
|
return void 0;
|
|
@@ -413,6 +453,7 @@ function parseOtlpRequest(body) {
|
|
|
413
453
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
414
454
|
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
415
455
|
dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
|
|
456
|
+
dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
|
|
416
457
|
httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
|
|
417
458
|
httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
|
|
418
459
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
@@ -434,10 +475,10 @@ function parseOtlpRequest(body) {
|
|
|
434
475
|
return out;
|
|
435
476
|
}
|
|
436
477
|
function loadProtoRoot() {
|
|
437
|
-
const here =
|
|
438
|
-
const protoRoot =
|
|
478
|
+
const here = import_node_path39.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
479
|
+
const protoRoot = import_node_path39.default.resolve(here, "..", "proto");
|
|
439
480
|
const root = new import_protobufjs.default.Root();
|
|
440
|
-
root.resolvePath = (_origin, target) =>
|
|
481
|
+
root.resolvePath = (_origin, target) => import_node_path39.default.resolve(protoRoot, target);
|
|
441
482
|
root.loadSync(
|
|
442
483
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
443
484
|
{ keepCase: true }
|
|
@@ -668,12 +709,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
668
709
|
}
|
|
669
710
|
}
|
|
670
711
|
}
|
|
671
|
-
var
|
|
712
|
+
var import_node_path39, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
672
713
|
var init_otel = __esm({
|
|
673
714
|
"src/otel.ts"() {
|
|
674
715
|
"use strict";
|
|
675
716
|
init_cjs_shims();
|
|
676
|
-
|
|
717
|
+
import_node_path39 = __toESM(require("path"), 1);
|
|
677
718
|
import_node_url2 = require("url");
|
|
678
719
|
import_fastify = __toESM(require("fastify"), 1);
|
|
679
720
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -697,13 +738,13 @@ __export(neatd_exports, {
|
|
|
697
738
|
module.exports = __toCommonJS(neatd_exports);
|
|
698
739
|
init_cjs_shims();
|
|
699
740
|
var import_node_fs34 = require("fs");
|
|
700
|
-
var
|
|
741
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
701
742
|
var import_node_module2 = require("module");
|
|
702
743
|
|
|
703
744
|
// src/daemon.ts
|
|
704
745
|
init_cjs_shims();
|
|
705
746
|
var import_node_fs32 = require("fs");
|
|
706
|
-
var
|
|
747
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
707
748
|
var import_node_module = require("module");
|
|
708
749
|
|
|
709
750
|
// src/graph.ts
|
|
@@ -1236,19 +1277,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1236
1277
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1237
1278
|
let best = { path: [start], edges: [] };
|
|
1238
1279
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1239
|
-
function step(node,
|
|
1240
|
-
if (
|
|
1241
|
-
best = { path: [...
|
|
1280
|
+
function step(node, path61, edges) {
|
|
1281
|
+
if (path61.length > best.path.length) {
|
|
1282
|
+
best = { path: [...path61], edges: [...edges] };
|
|
1242
1283
|
}
|
|
1243
|
-
if (
|
|
1284
|
+
if (path61.length - 1 >= maxDepth) return;
|
|
1244
1285
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1245
1286
|
for (const [srcId, edge] of incoming) {
|
|
1246
1287
|
if (visited.has(srcId)) continue;
|
|
1247
1288
|
visited.add(srcId);
|
|
1248
|
-
|
|
1289
|
+
path61.push(srcId);
|
|
1249
1290
|
edges.push(edge);
|
|
1250
|
-
step(srcId,
|
|
1251
|
-
|
|
1291
|
+
step(srcId, path61, edges);
|
|
1292
|
+
path61.pop();
|
|
1252
1293
|
edges.pop();
|
|
1253
1294
|
visited.delete(srcId);
|
|
1254
1295
|
}
|
|
@@ -1455,26 +1496,26 @@ function dominantFailingCall(graph, serviceId7, visited) {
|
|
|
1455
1496
|
return best;
|
|
1456
1497
|
}
|
|
1457
1498
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1458
|
-
const
|
|
1499
|
+
const path61 = [originServiceId];
|
|
1459
1500
|
const edges = [];
|
|
1460
1501
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1461
1502
|
let current = originServiceId;
|
|
1462
1503
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1463
1504
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1464
1505
|
if (!hop) break;
|
|
1465
|
-
|
|
1506
|
+
path61.push(hop.nextService);
|
|
1466
1507
|
edges.push(hop.edge);
|
|
1467
1508
|
visited.add(hop.nextService);
|
|
1468
1509
|
current = hop.nextService;
|
|
1469
1510
|
}
|
|
1470
1511
|
if (edges.length === 0) return null;
|
|
1471
|
-
return { path:
|
|
1512
|
+
return { path: path61, edges, culprit: current };
|
|
1472
1513
|
}
|
|
1473
1514
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1474
1515
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1475
1516
|
if (!chain) return null;
|
|
1476
1517
|
const culprit = chain.culprit;
|
|
1477
|
-
const
|
|
1518
|
+
const path61 = [...chain.path];
|
|
1478
1519
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1479
1520
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1480
1521
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1482,14 +1523,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1482
1523
|
if (loc) {
|
|
1483
1524
|
let rootCauseNode = culprit;
|
|
1484
1525
|
if (loc.fileNode) {
|
|
1485
|
-
|
|
1526
|
+
path61.push(loc.fileNode);
|
|
1486
1527
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1487
1528
|
rootCauseNode = loc.fileNode;
|
|
1488
1529
|
}
|
|
1489
1530
|
return import_types.RootCauseResultSchema.parse({
|
|
1490
1531
|
rootCauseNode,
|
|
1491
1532
|
rootCauseReason: loc.rootCauseReason,
|
|
1492
|
-
traversalPath:
|
|
1533
|
+
traversalPath: path61,
|
|
1493
1534
|
edgeProvenances,
|
|
1494
1535
|
confidence,
|
|
1495
1536
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1501,7 +1542,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1501
1542
|
return import_types.RootCauseResultSchema.parse({
|
|
1502
1543
|
rootCauseNode: culprit,
|
|
1503
1544
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1504
|
-
traversalPath:
|
|
1545
|
+
traversalPath: path61,
|
|
1505
1546
|
edgeProvenances,
|
|
1506
1547
|
confidence,
|
|
1507
1548
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2089,7 +2130,7 @@ var PolicyViolationsLog = class {
|
|
|
2089
2130
|
};
|
|
2090
2131
|
|
|
2091
2132
|
// src/ingest.ts
|
|
2092
|
-
var
|
|
2133
|
+
var import_types7 = require("@neat.is/types");
|
|
2093
2134
|
|
|
2094
2135
|
// src/extract/routes.ts
|
|
2095
2136
|
init_cjs_shims();
|
|
@@ -3135,6 +3176,42 @@ async function addRoutes(graph, services) {
|
|
|
3135
3176
|
return { nodesAdded, edgesAdded };
|
|
3136
3177
|
}
|
|
3137
3178
|
|
|
3179
|
+
// src/columns.ts
|
|
3180
|
+
init_cjs_shims();
|
|
3181
|
+
var import_types6 = require("@neat.is/types");
|
|
3182
|
+
var OBSERVED_COLUMN_CONFIDENCE = 0.9;
|
|
3183
|
+
function normalizeProvenances(provenances) {
|
|
3184
|
+
return [...new Set(provenances)].sort();
|
|
3185
|
+
}
|
|
3186
|
+
function foldColumns(existing, names, provenance, confidence) {
|
|
3187
|
+
const out = (existing ?? []).map((c) => ({
|
|
3188
|
+
...c,
|
|
3189
|
+
provenances: [...c.provenances]
|
|
3190
|
+
}));
|
|
3191
|
+
const byName = new Map(out.map((c) => [c.name, c]));
|
|
3192
|
+
for (const raw of names) {
|
|
3193
|
+
const name = raw.toLowerCase();
|
|
3194
|
+
const prior = byName.get(name);
|
|
3195
|
+
if (!prior) {
|
|
3196
|
+
const col = { name, provenances: [provenance], confidence };
|
|
3197
|
+
byName.set(name, col);
|
|
3198
|
+
out.push(col);
|
|
3199
|
+
continue;
|
|
3200
|
+
}
|
|
3201
|
+
if (!prior.provenances.includes(provenance)) {
|
|
3202
|
+
prior.provenances = normalizeProvenances([...prior.provenances, provenance]);
|
|
3203
|
+
}
|
|
3204
|
+
if (confidence > prior.confidence) prior.confidence = confidence;
|
|
3205
|
+
}
|
|
3206
|
+
return out;
|
|
3207
|
+
}
|
|
3208
|
+
function columnIsDeclared(col) {
|
|
3209
|
+
return col.provenances.includes(import_types6.Provenance.EXTRACTED);
|
|
3210
|
+
}
|
|
3211
|
+
function columnIsObserved(col) {
|
|
3212
|
+
return col.provenances.includes(import_types6.Provenance.OBSERVED);
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3138
3215
|
// src/ingest.ts
|
|
3139
3216
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
3140
3217
|
var DAY_MS = 24 * HOUR_MS;
|
|
@@ -3434,11 +3511,11 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
3434
3511
|
};
|
|
3435
3512
|
}
|
|
3436
3513
|
function reconcileObservedRelPath(graph, serviceName, relPath) {
|
|
3437
|
-
if (graph.hasNode((0,
|
|
3514
|
+
if (graph.hasNode((0, import_types7.fileId)(serviceName, relPath))) return relPath;
|
|
3438
3515
|
let best = null;
|
|
3439
3516
|
graph.forEachNode((_id, attrs) => {
|
|
3440
3517
|
const a = attrs;
|
|
3441
|
-
if (a.type !==
|
|
3518
|
+
if (a.type !== import_types7.NodeType.FileNode || a.service !== serviceName) return;
|
|
3442
3519
|
if (a.discoveredVia === "otel") return;
|
|
3443
3520
|
const p = a.path;
|
|
3444
3521
|
if (!p) return;
|
|
@@ -3450,14 +3527,14 @@ function reconcileObservedRelPath(graph, serviceName, relPath) {
|
|
|
3450
3527
|
}
|
|
3451
3528
|
function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
|
|
3452
3529
|
const svcAttrs = graph.hasNode(serviceNodeId) ? graph.getNodeAttributes(serviceNodeId) : void 0;
|
|
3453
|
-
const canonicalService = svcAttrs && svcAttrs.type ===
|
|
3530
|
+
const canonicalService = svcAttrs && svcAttrs.type === import_types7.NodeType.ServiceNode && typeof svcAttrs.name === "string" ? svcAttrs.name : serviceName;
|
|
3454
3531
|
const relPath = reconcileObservedRelPath(graph, canonicalService, callSite.relPath);
|
|
3455
|
-
const fileNodeId = (0,
|
|
3532
|
+
const fileNodeId = (0, import_types7.fileId)(canonicalService, relPath);
|
|
3456
3533
|
if (!graph.hasNode(fileNodeId)) {
|
|
3457
3534
|
const language = languageForExt(relPath);
|
|
3458
3535
|
const node = {
|
|
3459
3536
|
id: fileNodeId,
|
|
3460
|
-
type:
|
|
3537
|
+
type: import_types7.NodeType.FileNode,
|
|
3461
3538
|
service: canonicalService,
|
|
3462
3539
|
path: relPath,
|
|
3463
3540
|
...language ? { language } : {},
|
|
@@ -3466,14 +3543,14 @@ function ensureObservedFileNode(graph, serviceName, serviceNodeId, callSite) {
|
|
|
3466
3543
|
};
|
|
3467
3544
|
graph.addNode(fileNodeId, node);
|
|
3468
3545
|
}
|
|
3469
|
-
const containsId = makeObservedEdgeId(
|
|
3546
|
+
const containsId = makeObservedEdgeId(import_types7.EdgeType.CONTAINS, serviceNodeId, fileNodeId);
|
|
3470
3547
|
if (!graph.hasEdge(containsId)) {
|
|
3471
3548
|
const edge = {
|
|
3472
3549
|
id: containsId,
|
|
3473
3550
|
source: serviceNodeId,
|
|
3474
3551
|
target: fileNodeId,
|
|
3475
|
-
type:
|
|
3476
|
-
provenance:
|
|
3552
|
+
type: import_types7.EdgeType.CONTAINS,
|
|
3553
|
+
provenance: import_types7.Provenance.OBSERVED
|
|
3477
3554
|
};
|
|
3478
3555
|
graph.addEdgeWithKey(containsId, serviceNodeId, fileNodeId, edge);
|
|
3479
3556
|
}
|
|
@@ -3497,11 +3574,11 @@ function pickContainingSymbol(candidates, fn) {
|
|
|
3497
3574
|
return [...candidates].sort(bySpan)[0].id;
|
|
3498
3575
|
}
|
|
3499
3576
|
function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line) {
|
|
3500
|
-
const sid = (0,
|
|
3577
|
+
const sid = (0, import_types7.symbolId)(service, relPath, fn);
|
|
3501
3578
|
if (!graph.hasNode(sid)) {
|
|
3502
3579
|
const node = {
|
|
3503
3580
|
id: sid,
|
|
3504
|
-
type:
|
|
3581
|
+
type: import_types7.NodeType.SymbolNode,
|
|
3505
3582
|
kind: "function",
|
|
3506
3583
|
qualname: fn,
|
|
3507
3584
|
span: { startLine: line, endLine: line },
|
|
@@ -3511,14 +3588,14 @@ function ensureObservedSymbolNode(graph, fileNodeId, service, relPath, fn, line)
|
|
|
3511
3588
|
};
|
|
3512
3589
|
graph.addNode(sid, node);
|
|
3513
3590
|
}
|
|
3514
|
-
const containsId = makeObservedEdgeId(
|
|
3591
|
+
const containsId = makeObservedEdgeId(import_types7.EdgeType.CONTAINS, fileNodeId, sid);
|
|
3515
3592
|
if (!graph.hasEdge(containsId)) {
|
|
3516
3593
|
const edge = {
|
|
3517
3594
|
id: containsId,
|
|
3518
3595
|
source: fileNodeId,
|
|
3519
3596
|
target: sid,
|
|
3520
|
-
type:
|
|
3521
|
-
provenance:
|
|
3597
|
+
type: import_types7.EdgeType.CONTAINS,
|
|
3598
|
+
provenance: import_types7.Provenance.OBSERVED
|
|
3522
3599
|
};
|
|
3523
3600
|
graph.addEdgeWithKey(containsId, fileNodeId, sid, edge);
|
|
3524
3601
|
}
|
|
@@ -3530,9 +3607,9 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
|
|
|
3530
3607
|
let sawSymbol = false;
|
|
3531
3608
|
const candidates = [];
|
|
3532
3609
|
graph.forEachOutboundEdge(fileNodeId, (_edge, edgeAttrs, _source, target) => {
|
|
3533
|
-
if (edgeAttrs.type !==
|
|
3610
|
+
if (edgeAttrs.type !== import_types7.EdgeType.CONTAINS) return;
|
|
3534
3611
|
const t = graph.getNodeAttributes(target);
|
|
3535
|
-
if (t.type !==
|
|
3612
|
+
if (t.type !== import_types7.NodeType.SymbolNode) return;
|
|
3536
3613
|
sawSymbol = true;
|
|
3537
3614
|
if (line >= t.span.startLine && line <= t.span.endLine) {
|
|
3538
3615
|
candidates.push({ id: target, symbol: t });
|
|
@@ -3545,17 +3622,17 @@ function landObservedSymbol(graph, fileNodeId, service, relPath, callSite) {
|
|
|
3545
3622
|
return fileNodeId;
|
|
3546
3623
|
}
|
|
3547
3624
|
function makeObservedEdgeId(type, source, target) {
|
|
3548
|
-
return (0,
|
|
3625
|
+
return (0, import_types7.observedEdgeId)(source, target, type);
|
|
3549
3626
|
}
|
|
3550
3627
|
function makeInferredEdgeId(type, source, target) {
|
|
3551
|
-
return (0,
|
|
3628
|
+
return (0, import_types7.inferredEdgeId)(source, target, type);
|
|
3552
3629
|
}
|
|
3553
3630
|
var INFERRED_CONFIDENCE = 0.6;
|
|
3554
3631
|
var STITCH_MAX_DEPTH = 2;
|
|
3555
3632
|
var STITCH_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3633
|
+
import_types7.EdgeType.CALLS,
|
|
3634
|
+
import_types7.EdgeType.CONNECTS_TO,
|
|
3635
|
+
import_types7.EdgeType.DEPENDS_ON
|
|
3559
3636
|
]);
|
|
3560
3637
|
var WIRE_SPAN_KIND_CLIENT = 3;
|
|
3561
3638
|
var WIRE_SPAN_KIND_PRODUCER = 4;
|
|
@@ -3571,11 +3648,11 @@ function spanServesGraphqlOperation(kind) {
|
|
|
3571
3648
|
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
3572
3649
|
}
|
|
3573
3650
|
function ensureGraphqlOperationNode(graph, serviceName, operationType, operationName) {
|
|
3574
|
-
const id = (0,
|
|
3651
|
+
const id = (0, import_types7.graphqlOperationId)(serviceName, operationType, operationName);
|
|
3575
3652
|
if (graph.hasNode(id)) return id;
|
|
3576
3653
|
const node = {
|
|
3577
3654
|
id,
|
|
3578
|
-
type:
|
|
3655
|
+
type: import_types7.NodeType.GraphQLOperationNode,
|
|
3579
3656
|
name: operationName,
|
|
3580
3657
|
service: serviceName,
|
|
3581
3658
|
operationType: operationType.toLowerCase(),
|
|
@@ -3589,11 +3666,11 @@ function spanServesGrpcMethod(kind) {
|
|
|
3589
3666
|
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
3590
3667
|
}
|
|
3591
3668
|
function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
|
|
3592
|
-
const id = (0,
|
|
3669
|
+
const id = (0, import_types7.grpcMethodId)(rpcService, rpcMethod);
|
|
3593
3670
|
if (graph.hasNode(id)) return id;
|
|
3594
3671
|
const node = {
|
|
3595
3672
|
id,
|
|
3596
|
-
type:
|
|
3673
|
+
type: import_types7.NodeType.GrpcMethodNode,
|
|
3597
3674
|
name: `${rpcService}/${rpcMethod}`,
|
|
3598
3675
|
rpcService,
|
|
3599
3676
|
rpcMethod,
|
|
@@ -3606,11 +3683,11 @@ function spanServesWebsocketChannel(kind) {
|
|
|
3606
3683
|
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
3607
3684
|
}
|
|
3608
3685
|
function ensureWebsocketChannelNode(graph, serviceName, channel) {
|
|
3609
|
-
const id = (0,
|
|
3686
|
+
const id = (0, import_types7.websocketChannelId)(serviceName, channel);
|
|
3610
3687
|
if (graph.hasNode(id)) return id;
|
|
3611
3688
|
const node = {
|
|
3612
3689
|
id,
|
|
3613
|
-
type:
|
|
3690
|
+
type: import_types7.NodeType.WebSocketChannelNode,
|
|
3614
3691
|
name: channel,
|
|
3615
3692
|
service: serviceName,
|
|
3616
3693
|
channel,
|
|
@@ -3623,11 +3700,11 @@ function messagingDestinationKind(system) {
|
|
|
3623
3700
|
return `${system}-topic`;
|
|
3624
3701
|
}
|
|
3625
3702
|
function ensureMessagingDestinationNode(graph, system, destination) {
|
|
3626
|
-
const id = (0,
|
|
3703
|
+
const id = (0, import_types7.infraId)(messagingDestinationKind(system), destination);
|
|
3627
3704
|
if (graph.hasNode(id)) return id;
|
|
3628
3705
|
const node = {
|
|
3629
3706
|
id,
|
|
3630
|
-
type:
|
|
3707
|
+
type: import_types7.NodeType.InfraNode,
|
|
3631
3708
|
name: destination,
|
|
3632
3709
|
provider: "self",
|
|
3633
3710
|
kind: messagingDestinationKind(system)
|
|
@@ -3671,9 +3748,9 @@ function lookupParentSpan(traceId, parentSpanId, now) {
|
|
|
3671
3748
|
};
|
|
3672
3749
|
}
|
|
3673
3750
|
function resolveServiceId(graph, host, env) {
|
|
3674
|
-
const envTagged = (0,
|
|
3751
|
+
const envTagged = (0, import_types7.serviceId)(host, env);
|
|
3675
3752
|
if (graph.hasNode(envTagged)) return envTagged;
|
|
3676
|
-
const envLess = (0,
|
|
3753
|
+
const envLess = (0, import_types7.serviceId)(host);
|
|
3677
3754
|
if (envLess !== envTagged && graph.hasNode(envLess)) return envLess;
|
|
3678
3755
|
let sameEnv = null;
|
|
3679
3756
|
let envLessMatch = null;
|
|
@@ -3681,7 +3758,7 @@ function resolveServiceId(graph, host, env) {
|
|
|
3681
3758
|
graph.forEachNode((id, attrs) => {
|
|
3682
3759
|
if (sameEnv) return;
|
|
3683
3760
|
const a = attrs;
|
|
3684
|
-
if (a.type !==
|
|
3761
|
+
if (a.type !== import_types7.NodeType.ServiceNode) return;
|
|
3685
3762
|
const matchesByName = a.name === host;
|
|
3686
3763
|
const matchesByAlias = a.aliases ? a.aliases.includes(host) : false;
|
|
3687
3764
|
if (!matchesByName && !matchesByAlias) return;
|
|
@@ -3696,14 +3773,14 @@ function resolveServiceId(graph, host, env) {
|
|
|
3696
3773
|
return sameEnv ?? envLessMatch ?? anyMatch;
|
|
3697
3774
|
}
|
|
3698
3775
|
function frontierIdFor(host) {
|
|
3699
|
-
return (0,
|
|
3776
|
+
return (0, import_types7.frontierId)(host);
|
|
3700
3777
|
}
|
|
3701
3778
|
function ensureServiceNode(graph, serviceName, env) {
|
|
3702
|
-
const id = (0,
|
|
3779
|
+
const id = (0, import_types7.serviceId)(serviceName, env);
|
|
3703
3780
|
if (graph.hasNode(id)) return id;
|
|
3704
3781
|
const wanted = serviceName.toLowerCase();
|
|
3705
3782
|
const extractedId = graph.findNode((_nid, attrs) => {
|
|
3706
|
-
if (attrs.type !==
|
|
3783
|
+
if (attrs.type !== import_types7.NodeType.ServiceNode) return false;
|
|
3707
3784
|
const svc = attrs;
|
|
3708
3785
|
if (svc.discoveredVia === "otel") return false;
|
|
3709
3786
|
return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
|
|
@@ -3711,7 +3788,7 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
3711
3788
|
if (extractedId) return extractedId;
|
|
3712
3789
|
const node = {
|
|
3713
3790
|
id,
|
|
3714
|
-
type:
|
|
3791
|
+
type: import_types7.NodeType.ServiceNode,
|
|
3715
3792
|
name: serviceName,
|
|
3716
3793
|
language: "unknown",
|
|
3717
3794
|
discoveredVia: "otel",
|
|
@@ -3721,11 +3798,11 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
3721
3798
|
return id;
|
|
3722
3799
|
}
|
|
3723
3800
|
function ensureInfraNode(graph, kind, name, provider) {
|
|
3724
|
-
const id = (0,
|
|
3801
|
+
const id = (0, import_types7.infraId)(kind, name);
|
|
3725
3802
|
if (graph.hasNode(id)) return id;
|
|
3726
3803
|
const node = {
|
|
3727
3804
|
id,
|
|
3728
|
-
type:
|
|
3805
|
+
type: import_types7.NodeType.InfraNode,
|
|
3729
3806
|
name,
|
|
3730
3807
|
provider,
|
|
3731
3808
|
kind
|
|
@@ -3733,12 +3810,27 @@ function ensureInfraNode(graph, kind, name, provider) {
|
|
|
3733
3810
|
graph.addNode(id, node);
|
|
3734
3811
|
return id;
|
|
3735
3812
|
}
|
|
3813
|
+
var COLUMN_BEARING_INFRA_KINDS = /* @__PURE__ */ new Set(["sql-table", "supabase-table"]);
|
|
3814
|
+
function mergeColumnsAt(graph, tableNodeId, columns, provenance, confidence) {
|
|
3815
|
+
if (!columns || columns.length === 0 || !graph.hasNode(tableNodeId)) return;
|
|
3816
|
+
const node = graph.getNodeAttributes(tableNodeId);
|
|
3817
|
+
if (node.type !== import_types7.NodeType.InfraNode || !node.kind || !COLUMN_BEARING_INFRA_KINDS.has(node.kind)) {
|
|
3818
|
+
return;
|
|
3819
|
+
}
|
|
3820
|
+
graph.replaceNodeAttributes(tableNodeId, {
|
|
3821
|
+
...node,
|
|
3822
|
+
columns: foldColumns(node.columns, columns, provenance, confidence)
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
function mergeObservedColumns(graph, tableNodeId, columns) {
|
|
3826
|
+
mergeColumnsAt(graph, tableNodeId, columns, import_types7.Provenance.OBSERVED, OBSERVED_COLUMN_CONFIDENCE);
|
|
3827
|
+
}
|
|
3736
3828
|
function ensureDatabaseNode(graph, host, engine) {
|
|
3737
|
-
const id = (0,
|
|
3829
|
+
const id = (0, import_types7.databaseId)(host);
|
|
3738
3830
|
if (graph.hasNode(id)) return id;
|
|
3739
3831
|
const node = {
|
|
3740
3832
|
id,
|
|
3741
|
-
type:
|
|
3833
|
+
type: import_types7.NodeType.DatabaseNode,
|
|
3742
3834
|
name: host,
|
|
3743
3835
|
engine,
|
|
3744
3836
|
engineVersion: "unknown",
|
|
@@ -3750,11 +3842,11 @@ function ensureDatabaseNode(graph, host, engine) {
|
|
|
3750
3842
|
return id;
|
|
3751
3843
|
}
|
|
3752
3844
|
function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
3753
|
-
const id = (0,
|
|
3845
|
+
const id = (0, import_types7.localDatabaseId)(serviceName, name);
|
|
3754
3846
|
if (graph.hasNode(id)) return id;
|
|
3755
3847
|
const node = {
|
|
3756
3848
|
id,
|
|
3757
|
-
type:
|
|
3849
|
+
type: import_types7.NodeType.DatabaseNode,
|
|
3758
3850
|
name,
|
|
3759
3851
|
engine,
|
|
3760
3852
|
engineVersion: "unknown",
|
|
@@ -3769,17 +3861,17 @@ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
|
|
|
3769
3861
|
const sources = [serviceNodeId];
|
|
3770
3862
|
for (const edgeId of graph.outboundEdges(serviceNodeId)) {
|
|
3771
3863
|
const e = graph.getEdgeAttributes(edgeId);
|
|
3772
|
-
if (e.type ===
|
|
3864
|
+
if (e.type === import_types7.EdgeType.CONTAINS) sources.push(e.target);
|
|
3773
3865
|
}
|
|
3774
3866
|
const matches = /* @__PURE__ */ new Set();
|
|
3775
3867
|
for (const src of sources) {
|
|
3776
3868
|
if (!graph.hasNode(src)) continue;
|
|
3777
3869
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
3778
3870
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3779
|
-
if (edge.type !==
|
|
3871
|
+
if (edge.type !== import_types7.EdgeType.CONNECTS_TO || edge.provenance !== import_types7.Provenance.EXTRACTED) continue;
|
|
3780
3872
|
if (!graph.hasNode(edge.target)) continue;
|
|
3781
3873
|
const target = graph.getNodeAttributes(edge.target);
|
|
3782
|
-
if (target.type !==
|
|
3874
|
+
if (target.type !== import_types7.NodeType.DatabaseNode || target.engine !== engine) continue;
|
|
3783
3875
|
matches.add(edge.target);
|
|
3784
3876
|
}
|
|
3785
3877
|
}
|
|
@@ -3794,7 +3886,7 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
3794
3886
|
}
|
|
3795
3887
|
const node = {
|
|
3796
3888
|
id,
|
|
3797
|
-
type:
|
|
3889
|
+
type: import_types7.NodeType.FrontierNode,
|
|
3798
3890
|
name: host,
|
|
3799
3891
|
host,
|
|
3800
3892
|
firstObserved: ts,
|
|
@@ -3818,11 +3910,11 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
3818
3910
|
};
|
|
3819
3911
|
const updated = {
|
|
3820
3912
|
...existing,
|
|
3821
|
-
provenance:
|
|
3913
|
+
provenance: import_types7.Provenance.OBSERVED,
|
|
3822
3914
|
lastObserved: ts,
|
|
3823
3915
|
callCount: newSpanCount,
|
|
3824
3916
|
signal: newSignal,
|
|
3825
|
-
confidence: (0,
|
|
3917
|
+
confidence: (0, import_types7.confidenceForObservedSignal)(newSignal),
|
|
3826
3918
|
grain
|
|
3827
3919
|
// backfills legacy edges that predate ADR-142
|
|
3828
3920
|
};
|
|
@@ -3839,8 +3931,8 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
3839
3931
|
source,
|
|
3840
3932
|
target,
|
|
3841
3933
|
type,
|
|
3842
|
-
provenance:
|
|
3843
|
-
confidence: (0,
|
|
3934
|
+
provenance: import_types7.Provenance.OBSERVED,
|
|
3935
|
+
confidence: (0, import_types7.confidenceForObservedSignal)(signal),
|
|
3844
3936
|
lastObserved: ts,
|
|
3845
3937
|
callCount: 1,
|
|
3846
3938
|
signal,
|
|
@@ -3862,9 +3954,9 @@ function stitchTrace(graph, sourceServiceId, ts) {
|
|
|
3862
3954
|
const outbound = graph.outboundEdges(nodeId);
|
|
3863
3955
|
for (const edgeId of outbound) {
|
|
3864
3956
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3865
|
-
if (edge.provenance !==
|
|
3957
|
+
if (edge.provenance !== import_types7.Provenance.EXTRACTED) continue;
|
|
3866
3958
|
if (!STITCH_EDGE_TYPES.has(edge.type)) continue;
|
|
3867
|
-
if (graph.hasEdge((0,
|
|
3959
|
+
if (graph.hasEdge((0, import_types7.observedEdgeId)(edge.source, edge.target, edge.type))) continue;
|
|
3868
3960
|
upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts);
|
|
3869
3961
|
if (!visited.has(edge.target)) {
|
|
3870
3962
|
visited.add(edge.target);
|
|
@@ -3886,7 +3978,7 @@ function upsertInferredEdge(graph, type, source, target, ts) {
|
|
|
3886
3978
|
source,
|
|
3887
3979
|
target,
|
|
3888
3980
|
type,
|
|
3889
|
-
provenance:
|
|
3981
|
+
provenance: import_types7.Provenance.INFERRED,
|
|
3890
3982
|
confidence: INFERRED_CONFIDENCE,
|
|
3891
3983
|
lastObserved: ts
|
|
3892
3984
|
};
|
|
@@ -3897,12 +3989,12 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
3897
3989
|
await import_node_fs6.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
3898
3990
|
}
|
|
3899
3991
|
function incidentAffectedNode(span, graph, scanPath) {
|
|
3900
|
-
const sid = (0,
|
|
3992
|
+
const sid = (0, import_types7.serviceId)(span.service, span.env);
|
|
3901
3993
|
const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
|
|
3902
3994
|
const callSite = callSiteFromSpan(span, serviceNode, scanPath);
|
|
3903
3995
|
if (callSite) {
|
|
3904
3996
|
const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
|
|
3905
|
-
return (0,
|
|
3997
|
+
return (0, import_types7.fileId)(span.service, relPath);
|
|
3906
3998
|
}
|
|
3907
3999
|
return sid;
|
|
3908
4000
|
}
|
|
@@ -4027,7 +4119,7 @@ function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
|
|
|
4027
4119
|
graph.forEachNode((id, attrs) => {
|
|
4028
4120
|
if (found) return;
|
|
4029
4121
|
const a = attrs;
|
|
4030
|
-
if (a.type !==
|
|
4122
|
+
if (a.type !== import_types7.NodeType.RouteNode || a.service !== serviceName) return;
|
|
4031
4123
|
if (m && a.method !== "ALL" && a.method !== m) return;
|
|
4032
4124
|
if (normalizePathTemplate(a.pathTemplate) === target) found = id;
|
|
4033
4125
|
});
|
|
@@ -4058,7 +4150,7 @@ async function handleSpan(ctx, span) {
|
|
|
4058
4150
|
let targetId;
|
|
4059
4151
|
if (host) {
|
|
4060
4152
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
4061
|
-
targetId = (0,
|
|
4153
|
+
targetId = (0, import_types7.databaseId)(host);
|
|
4062
4154
|
} else {
|
|
4063
4155
|
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
4064
4156
|
if (declared) {
|
|
@@ -4075,7 +4167,7 @@ async function handleSpan(ctx, span) {
|
|
|
4075
4167
|
}
|
|
4076
4168
|
const result = upsertObservedEdge(
|
|
4077
4169
|
ctx.graph,
|
|
4078
|
-
|
|
4170
|
+
import_types7.EdgeType.CONNECTS_TO,
|
|
4079
4171
|
observedSource(),
|
|
4080
4172
|
targetId,
|
|
4081
4173
|
ts,
|
|
@@ -4087,7 +4179,7 @@ async function handleSpan(ctx, span) {
|
|
|
4087
4179
|
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
4088
4180
|
upsertObservedEdge(
|
|
4089
4181
|
ctx.graph,
|
|
4090
|
-
|
|
4182
|
+
import_types7.EdgeType.CALLS,
|
|
4091
4183
|
observedSource(),
|
|
4092
4184
|
collectionId,
|
|
4093
4185
|
ts,
|
|
@@ -4099,13 +4191,14 @@ async function handleSpan(ctx, span) {
|
|
|
4099
4191
|
const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
|
|
4100
4192
|
upsertObservedEdge(
|
|
4101
4193
|
ctx.graph,
|
|
4102
|
-
|
|
4194
|
+
import_types7.EdgeType.CALLS,
|
|
4103
4195
|
observedSource(),
|
|
4104
4196
|
tableId,
|
|
4105
4197
|
ts,
|
|
4106
4198
|
isError,
|
|
4107
4199
|
callSiteEvidence
|
|
4108
4200
|
);
|
|
4201
|
+
mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
|
|
4109
4202
|
}
|
|
4110
4203
|
}
|
|
4111
4204
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
@@ -4114,7 +4207,7 @@ async function handleSpan(ctx, span) {
|
|
|
4114
4207
|
span.messagingSystem,
|
|
4115
4208
|
span.messagingDestination
|
|
4116
4209
|
);
|
|
4117
|
-
const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ?
|
|
4210
|
+
const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types7.EdgeType.CONSUMES_FROM : import_types7.EdgeType.PUBLISHES_TO;
|
|
4118
4211
|
const result = upsertObservedEdge(
|
|
4119
4212
|
ctx.graph,
|
|
4120
4213
|
edgeType,
|
|
@@ -4134,7 +4227,7 @@ async function handleSpan(ctx, span) {
|
|
|
4134
4227
|
);
|
|
4135
4228
|
const result = upsertObservedEdge(
|
|
4136
4229
|
ctx.graph,
|
|
4137
|
-
|
|
4230
|
+
import_types7.EdgeType.CONTAINS,
|
|
4138
4231
|
observedSource(),
|
|
4139
4232
|
targetId,
|
|
4140
4233
|
ts,
|
|
@@ -4146,7 +4239,7 @@ async function handleSpan(ctx, span) {
|
|
|
4146
4239
|
const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
|
|
4147
4240
|
const result = upsertObservedEdge(
|
|
4148
4241
|
ctx.graph,
|
|
4149
|
-
|
|
4242
|
+
import_types7.EdgeType.CONTAINS,
|
|
4150
4243
|
observedSource(),
|
|
4151
4244
|
targetId,
|
|
4152
4245
|
ts,
|
|
@@ -4162,7 +4255,7 @@ async function handleSpan(ctx, span) {
|
|
|
4162
4255
|
);
|
|
4163
4256
|
const result = upsertObservedEdge(
|
|
4164
4257
|
ctx.graph,
|
|
4165
|
-
|
|
4258
|
+
import_types7.EdgeType.CONNECTS_TO,
|
|
4166
4259
|
observedSource(),
|
|
4167
4260
|
targetId,
|
|
4168
4261
|
ts,
|
|
@@ -4178,7 +4271,7 @@ async function handleSpan(ctx, span) {
|
|
|
4178
4271
|
if (targetId && targetId !== sourceId) {
|
|
4179
4272
|
upsertObservedEdge(
|
|
4180
4273
|
ctx.graph,
|
|
4181
|
-
|
|
4274
|
+
import_types7.EdgeType.CALLS,
|
|
4182
4275
|
observedSource(),
|
|
4183
4276
|
targetId,
|
|
4184
4277
|
ts,
|
|
@@ -4191,7 +4284,7 @@ async function handleSpan(ctx, span) {
|
|
|
4191
4284
|
const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
|
|
4192
4285
|
upsertObservedEdge(
|
|
4193
4286
|
ctx.graph,
|
|
4194
|
-
|
|
4287
|
+
import_types7.EdgeType.CALLS,
|
|
4195
4288
|
observedSource(),
|
|
4196
4289
|
frontierNodeId,
|
|
4197
4290
|
ts,
|
|
@@ -4217,7 +4310,7 @@ async function handleSpan(ctx, span) {
|
|
|
4217
4310
|
} : void 0;
|
|
4218
4311
|
upsertObservedEdge(
|
|
4219
4312
|
ctx.graph,
|
|
4220
|
-
|
|
4313
|
+
import_types7.EdgeType.CALLS,
|
|
4221
4314
|
fallbackSource,
|
|
4222
4315
|
sourceId,
|
|
4223
4316
|
ts,
|
|
@@ -4227,7 +4320,7 @@ async function handleSpan(ctx, span) {
|
|
|
4227
4320
|
}
|
|
4228
4321
|
}
|
|
4229
4322
|
}
|
|
4230
|
-
if (span.httpRoute && (span.kind === 2 || span.kind === 0 || span.kind === void 0)) {
|
|
4323
|
+
if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
|
|
4231
4324
|
const routeNodeId = findRouteNodeByHttpRoute(
|
|
4232
4325
|
ctx.graph,
|
|
4233
4326
|
span.service,
|
|
@@ -4236,7 +4329,7 @@ async function handleSpan(ctx, span) {
|
|
|
4236
4329
|
);
|
|
4237
4330
|
if (routeNodeId) {
|
|
4238
4331
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
4239
|
-
upsertObservedEdge(ctx.graph,
|
|
4332
|
+
upsertObservedEdge(ctx.graph, import_types7.EdgeType.CONTAINS, (0, import_types7.serviceId)(routeSvc), routeNodeId, ts, isError);
|
|
4240
4333
|
}
|
|
4241
4334
|
}
|
|
4242
4335
|
if (span.statusCode === 2) {
|
|
@@ -4275,7 +4368,7 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
4275
4368
|
const aliasIndex = /* @__PURE__ */ new Map();
|
|
4276
4369
|
graph.forEachNode((id, attrs) => {
|
|
4277
4370
|
const a = attrs;
|
|
4278
|
-
if (a.type !==
|
|
4371
|
+
if (a.type !== import_types7.NodeType.ServiceNode) return;
|
|
4279
4372
|
aliasIndex.set(a.name, id);
|
|
4280
4373
|
if (a.aliases) {
|
|
4281
4374
|
for (const alias of a.aliases) aliasIndex.set(alias, id);
|
|
@@ -4284,7 +4377,7 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
4284
4377
|
const toPromote = [];
|
|
4285
4378
|
graph.forEachNode((id, attrs) => {
|
|
4286
4379
|
const a = attrs;
|
|
4287
|
-
if (a.type !==
|
|
4380
|
+
if (a.type !== import_types7.NodeType.FrontierNode) return;
|
|
4288
4381
|
const target = aliasIndex.get(a.host);
|
|
4289
4382
|
if (!target) return;
|
|
4290
4383
|
if (target === id) return;
|
|
@@ -4318,7 +4411,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId7) {
|
|
|
4318
4411
|
}
|
|
4319
4412
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
4320
4413
|
graph.dropEdge(oldEdgeId);
|
|
4321
|
-
const newId = edge.provenance ===
|
|
4414
|
+
const newId = edge.provenance === import_types7.Provenance.OBSERVED ? (0, import_types7.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types7.Provenance.INFERRED ? (0, import_types7.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types7.extractedEdgeId)(newSource, newTarget, edge.type);
|
|
4322
4415
|
if (graph.hasEdge(newId)) {
|
|
4323
4416
|
const existing = graph.getEdgeAttributes(newId);
|
|
4324
4417
|
const merged = {
|
|
@@ -4349,12 +4442,12 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
4349
4442
|
const project = options.project ?? DEFAULT_PROJECT;
|
|
4350
4443
|
graph.forEachEdge((id, attrs) => {
|
|
4351
4444
|
const e = attrs;
|
|
4352
|
-
if (e.provenance !==
|
|
4445
|
+
if (e.provenance !== import_types7.Provenance.OBSERVED) return;
|
|
4353
4446
|
if (!e.lastObserved) return;
|
|
4354
4447
|
const threshold = thresholdForEdgeType(e.type, thresholds);
|
|
4355
4448
|
const age = now - new Date(e.lastObserved).getTime();
|
|
4356
4449
|
if (age > threshold) {
|
|
4357
|
-
const updated = { ...e, provenance:
|
|
4450
|
+
const updated = { ...e, provenance: import_types7.Provenance.STALE, confidence: 0.3 };
|
|
4358
4451
|
graph.replaceEdgeAttributes(id, updated);
|
|
4359
4452
|
events.push({
|
|
4360
4453
|
edgeId: id,
|
|
@@ -4371,8 +4464,8 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
4371
4464
|
project,
|
|
4372
4465
|
payload: {
|
|
4373
4466
|
edgeId: id,
|
|
4374
|
-
from:
|
|
4375
|
-
to:
|
|
4467
|
+
from: import_types7.Provenance.OBSERVED,
|
|
4468
|
+
to: import_types7.Provenance.STALE
|
|
4376
4469
|
}
|
|
4377
4470
|
});
|
|
4378
4471
|
}
|
|
@@ -4481,7 +4574,7 @@ function mergeSnapshot(graph, snapshot) {
|
|
|
4481
4574
|
const validEdges = [];
|
|
4482
4575
|
for (const node of incomingNodes) {
|
|
4483
4576
|
if (node.attributes === void 0) continue;
|
|
4484
|
-
const parsed =
|
|
4577
|
+
const parsed = import_types7.GraphNodeSchema.safeParse(node.attributes);
|
|
4485
4578
|
if (!parsed.success) {
|
|
4486
4579
|
issues.push(`node "${node.key}": ${describeZodIssues(parsed.error)}`);
|
|
4487
4580
|
continue;
|
|
@@ -4490,7 +4583,7 @@ function mergeSnapshot(graph, snapshot) {
|
|
|
4490
4583
|
}
|
|
4491
4584
|
for (const edge of incomingEdges) {
|
|
4492
4585
|
if (edge.attributes === void 0) continue;
|
|
4493
|
-
const parsed =
|
|
4586
|
+
const parsed = import_types7.GraphEdgeSchema.safeParse(edge.attributes);
|
|
4494
4587
|
if (!parsed.success) {
|
|
4495
4588
|
const label = edge.key ?? `${edge.source}->${edge.target}`;
|
|
4496
4589
|
issues.push(`edge "${label}": ${describeZodIssues(parsed.error)}`);
|
|
@@ -4524,7 +4617,7 @@ var import_node_fs10 = require("fs");
|
|
|
4524
4617
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
4525
4618
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
4526
4619
|
var import_minimatch2 = require("minimatch");
|
|
4527
|
-
var
|
|
4620
|
+
var import_types9 = require("@neat.is/types");
|
|
4528
4621
|
|
|
4529
4622
|
// src/extract/python.ts
|
|
4530
4623
|
init_cjs_shims();
|
|
@@ -4597,7 +4690,7 @@ function pythonToPackage(service) {
|
|
|
4597
4690
|
init_cjs_shims();
|
|
4598
4691
|
var import_node_fs8 = require("fs");
|
|
4599
4692
|
var import_node_path9 = __toESM(require("path"), 1);
|
|
4600
|
-
var
|
|
4693
|
+
var import_types8 = require("@neat.is/types");
|
|
4601
4694
|
function parseGoMod(source) {
|
|
4602
4695
|
const module2 = source.match(/^\s*module\s+(\S+)\s*$/m)?.[1];
|
|
4603
4696
|
if (!module2) return null;
|
|
@@ -4625,8 +4718,8 @@ async function discoverGoService(scanPath, dir) {
|
|
|
4625
4718
|
const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
|
|
4626
4719
|
const pkg = { name, dependencies: mod.dependencies };
|
|
4627
4720
|
const node = {
|
|
4628
|
-
id: (0,
|
|
4629
|
-
type:
|
|
4721
|
+
id: (0, import_types8.serviceId)(name),
|
|
4722
|
+
type: import_types8.NodeType.ServiceNode,
|
|
4630
4723
|
name,
|
|
4631
4724
|
language: "go",
|
|
4632
4725
|
dependencies: mod.dependencies,
|
|
@@ -4812,8 +4905,8 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
4812
4905
|
const framework = detectJsFramework(pkg);
|
|
4813
4906
|
const language = await detectJsServiceLanguage(dir, pkg);
|
|
4814
4907
|
const node = {
|
|
4815
|
-
id: (0,
|
|
4816
|
-
type:
|
|
4908
|
+
id: (0, import_types9.serviceId)(pkg.name),
|
|
4909
|
+
type: import_types9.NodeType.ServiceNode,
|
|
4817
4910
|
name: pkg.name,
|
|
4818
4911
|
language,
|
|
4819
4912
|
version: pkg.version,
|
|
@@ -4829,8 +4922,8 @@ async function discoverPyService(scanPath, dir) {
|
|
|
4829
4922
|
if (!py) return null;
|
|
4830
4923
|
const pkg = pythonToPackage(py);
|
|
4831
4924
|
const node = {
|
|
4832
|
-
id: (0,
|
|
4833
|
-
type:
|
|
4925
|
+
id: (0, import_types9.serviceId)(py.name),
|
|
4926
|
+
type: import_types9.NodeType.ServiceNode,
|
|
4834
4927
|
name: py.name,
|
|
4835
4928
|
language: "python",
|
|
4836
4929
|
version: py.version,
|
|
@@ -4926,7 +5019,7 @@ init_cjs_shims();
|
|
|
4926
5019
|
var import_node_path12 = __toESM(require("path"), 1);
|
|
4927
5020
|
var import_node_fs11 = require("fs");
|
|
4928
5021
|
var import_yaml2 = require("yaml");
|
|
4929
|
-
var
|
|
5022
|
+
var import_types10 = require("@neat.is/types");
|
|
4930
5023
|
var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
4931
5024
|
"Service",
|
|
4932
5025
|
"Deployment",
|
|
@@ -4936,7 +5029,7 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
4936
5029
|
function addAliases(graph, serviceId7, candidates) {
|
|
4937
5030
|
if (!graph.hasNode(serviceId7)) return;
|
|
4938
5031
|
const node = graph.getNodeAttributes(serviceId7);
|
|
4939
|
-
if (node.type !==
|
|
5032
|
+
if (node.type !== import_types10.NodeType.ServiceNode) return;
|
|
4940
5033
|
const set = new Set(node.aliases ?? []);
|
|
4941
5034
|
for (const c of candidates) {
|
|
4942
5035
|
if (!c) continue;
|
|
@@ -5117,7 +5210,7 @@ var import_node_path14 = __toESM(require("path"), 1);
|
|
|
5117
5210
|
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
5118
5211
|
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5119
5212
|
var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
|
|
5120
|
-
var
|
|
5213
|
+
var import_types11 = require("@neat.is/types");
|
|
5121
5214
|
var PARSE_CHUNK2 = 16384;
|
|
5122
5215
|
var GRAMMAR_BY_EXT = {
|
|
5123
5216
|
".ts": import_tree_sitter_typescript.default.typescript,
|
|
@@ -5208,7 +5301,7 @@ function disambiguate(defs) {
|
|
|
5208
5301
|
}
|
|
5209
5302
|
async function addSymbols(graph, services) {
|
|
5210
5303
|
const parsers = /* @__PURE__ */ new Map();
|
|
5211
|
-
const
|
|
5304
|
+
const parserForExt2 = (ext) => {
|
|
5212
5305
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
5213
5306
|
if (!grammar) return null;
|
|
5214
5307
|
let parser = parsers.get(ext);
|
|
@@ -5224,7 +5317,7 @@ async function addSymbols(graph, services) {
|
|
|
5224
5317
|
for (const service of services) {
|
|
5225
5318
|
const files = await loadSourceFiles(service.dir);
|
|
5226
5319
|
for (const file of files) {
|
|
5227
|
-
const parser =
|
|
5320
|
+
const parser = parserForExt2(import_node_path14.default.extname(file.path));
|
|
5228
5321
|
if (!parser) continue;
|
|
5229
5322
|
const relPath = toPosix(import_node_path14.default.relative(service.dir, file.path));
|
|
5230
5323
|
let defs;
|
|
@@ -5245,11 +5338,11 @@ async function addSymbols(graph, services) {
|
|
|
5245
5338
|
nodesAdded += fn;
|
|
5246
5339
|
edgesAdded += fe;
|
|
5247
5340
|
for (const { def, disambiguator } of disambiguate(defs)) {
|
|
5248
|
-
const sid = (0,
|
|
5341
|
+
const sid = (0, import_types11.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
|
|
5249
5342
|
if (!graph.hasNode(sid)) {
|
|
5250
5343
|
const node = {
|
|
5251
5344
|
id: sid,
|
|
5252
|
-
type:
|
|
5345
|
+
type: import_types11.NodeType.SymbolNode,
|
|
5253
5346
|
kind: def.kind,
|
|
5254
5347
|
qualname: def.qualname,
|
|
5255
5348
|
span: { startLine: def.startLine, endLine: def.endLine },
|
|
@@ -5260,15 +5353,15 @@ async function addSymbols(graph, services) {
|
|
|
5260
5353
|
graph.addNode(sid, node);
|
|
5261
5354
|
nodesAdded++;
|
|
5262
5355
|
}
|
|
5263
|
-
const containsId = (0,
|
|
5356
|
+
const containsId = (0, import_types11.extractedEdgeId)(fileNodeId, sid, import_types11.EdgeType.CONTAINS);
|
|
5264
5357
|
if (!graph.hasEdge(containsId)) {
|
|
5265
5358
|
const edge = {
|
|
5266
5359
|
id: containsId,
|
|
5267
5360
|
source: fileNodeId,
|
|
5268
5361
|
target: sid,
|
|
5269
|
-
type:
|
|
5270
|
-
provenance:
|
|
5271
|
-
confidence: (0,
|
|
5362
|
+
type: import_types11.EdgeType.CONTAINS,
|
|
5363
|
+
provenance: import_types11.Provenance.EXTRACTED,
|
|
5364
|
+
confidence: (0, import_types11.confidenceForExtracted)("structural"),
|
|
5272
5365
|
evidence: {
|
|
5273
5366
|
file: relPath,
|
|
5274
5367
|
line: def.startLine,
|
|
@@ -5288,7 +5381,7 @@ async function addSymbols(graph, services) {
|
|
|
5288
5381
|
init_cjs_shims();
|
|
5289
5382
|
var import_node_path16 = __toESM(require("path"), 1);
|
|
5290
5383
|
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
5291
|
-
var
|
|
5384
|
+
var import_types13 = require("@neat.is/types");
|
|
5292
5385
|
|
|
5293
5386
|
// src/extract/imports.ts
|
|
5294
5387
|
init_cjs_shims();
|
|
@@ -5298,7 +5391,7 @@ var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
|
5298
5391
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5299
5392
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
5300
5393
|
var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
|
|
5301
|
-
var
|
|
5394
|
+
var import_types12 = require("@neat.is/types");
|
|
5302
5395
|
var PARSE_CHUNK3 = 16384;
|
|
5303
5396
|
function parseSource3(parser, source) {
|
|
5304
5397
|
return parser.parse(
|
|
@@ -5572,17 +5665,17 @@ async function resolveGoImport(specifier, modulePath, serviceDir) {
|
|
|
5572
5665
|
return toPosix(import_node_path15.default.relative(serviceDir, candidates[0]));
|
|
5573
5666
|
}
|
|
5574
5667
|
function emitImportEdge(graph, serviceName, importerFileId, importerRelPath, importeeRelPath, line, snippet2) {
|
|
5575
|
-
const importeeFileId = (0,
|
|
5668
|
+
const importeeFileId = (0, import_types12.fileId)(serviceName, importeeRelPath);
|
|
5576
5669
|
if (!graph.hasNode(importeeFileId)) return 0;
|
|
5577
|
-
const edgeId = (0,
|
|
5670
|
+
const edgeId = (0, import_types12.extractedEdgeId)(importerFileId, importeeFileId, import_types12.EdgeType.IMPORTS);
|
|
5578
5671
|
if (graph.hasEdge(edgeId)) return 0;
|
|
5579
5672
|
const edge = {
|
|
5580
5673
|
id: edgeId,
|
|
5581
5674
|
source: importerFileId,
|
|
5582
5675
|
target: importeeFileId,
|
|
5583
|
-
type:
|
|
5584
|
-
provenance:
|
|
5585
|
-
confidence: (0,
|
|
5676
|
+
type: import_types12.EdgeType.IMPORTS,
|
|
5677
|
+
provenance: import_types12.Provenance.EXTRACTED,
|
|
5678
|
+
confidence: (0, import_types12.confidenceForExtracted)("structural"),
|
|
5586
5679
|
evidence: { file: importerRelPath, line, snippet: snippet2 }
|
|
5587
5680
|
};
|
|
5588
5681
|
graph.addEdgeWithKey(edgeId, importerFileId, importeeFileId, edge);
|
|
@@ -5599,7 +5692,7 @@ async function addImports(graph, services) {
|
|
|
5599
5692
|
for (const file of files) {
|
|
5600
5693
|
if (isTestPath(file.path)) continue;
|
|
5601
5694
|
const relFile = toPosix(import_node_path15.default.relative(service.dir, file.path));
|
|
5602
|
-
const importerFileId = (0,
|
|
5695
|
+
const importerFileId = (0, import_types12.fileId)(service.pkg.name, relFile);
|
|
5603
5696
|
const isPython = import_node_path15.default.extname(file.path) === ".py";
|
|
5604
5697
|
const isGo = import_node_path15.default.extname(file.path) === ".go";
|
|
5605
5698
|
if (isGo) {
|
|
@@ -5760,7 +5853,7 @@ function stringInner(node) {
|
|
|
5760
5853
|
}
|
|
5761
5854
|
async function addSymbolEdges(graph, services) {
|
|
5762
5855
|
const parsers = /* @__PURE__ */ new Map();
|
|
5763
|
-
const
|
|
5856
|
+
const parserForExt2 = (ext) => {
|
|
5764
5857
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
5765
5858
|
if (!grammar) return null;
|
|
5766
5859
|
let parser = parsers.get(ext);
|
|
@@ -5777,7 +5870,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
5777
5870
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
5778
5871
|
const files = await loadSourceFiles(service.dir);
|
|
5779
5872
|
for (const file of files) {
|
|
5780
|
-
const parser =
|
|
5873
|
+
const parser = parserForExt2(import_node_path16.default.extname(file.path));
|
|
5781
5874
|
if (!parser) continue;
|
|
5782
5875
|
const relPath = toPosix(import_node_path16.default.relative(service.dir, file.path));
|
|
5783
5876
|
const fileDir = import_node_path16.default.dirname(file.path);
|
|
@@ -5790,7 +5883,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
5790
5883
|
}
|
|
5791
5884
|
const disambiguated = disambiguate(collectSymbolDefs(root));
|
|
5792
5885
|
const locals = disambiguated.map(({ def, disambiguator }) => ({
|
|
5793
|
-
sid: (0,
|
|
5886
|
+
sid: (0, import_types13.symbolId)(serviceName, relPath, def.qualname, disambiguator),
|
|
5794
5887
|
qualname: def.qualname,
|
|
5795
5888
|
kind: def.kind,
|
|
5796
5889
|
startLine: def.startLine,
|
|
@@ -5819,10 +5912,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
5819
5912
|
if (local) return local.kind === wantKind ? local.sid : null;
|
|
5820
5913
|
const imp = resolvedImports.get(name);
|
|
5821
5914
|
if (imp) {
|
|
5822
|
-
const candidate = (0,
|
|
5915
|
+
const candidate = (0, import_types13.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
|
|
5823
5916
|
if (graph.hasNode(candidate)) {
|
|
5824
5917
|
const node = graph.getNodeAttributes(candidate);
|
|
5825
|
-
if (node.type ===
|
|
5918
|
+
if (node.type === import_types13.NodeType.SymbolNode && node.kind === wantKind) return candidate;
|
|
5826
5919
|
}
|
|
5827
5920
|
}
|
|
5828
5921
|
return null;
|
|
@@ -5849,7 +5942,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
5849
5942
|
sourceSid: self.sid,
|
|
5850
5943
|
targetName: ext.name,
|
|
5851
5944
|
wantKind: "class",
|
|
5852
|
-
edgeType:
|
|
5945
|
+
edgeType: import_types13.EdgeType.INHERITS,
|
|
5853
5946
|
line: ext.line
|
|
5854
5947
|
});
|
|
5855
5948
|
}
|
|
@@ -5858,7 +5951,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
5858
5951
|
sourceSid: self.sid,
|
|
5859
5952
|
targetName: impl.name,
|
|
5860
5953
|
wantKind: "class",
|
|
5861
|
-
edgeType:
|
|
5954
|
+
edgeType: import_types13.EdgeType.IMPLEMENTS,
|
|
5862
5955
|
line: impl.line
|
|
5863
5956
|
});
|
|
5864
5957
|
}
|
|
@@ -5874,7 +5967,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
5874
5967
|
sourceSid: caller.sid,
|
|
5875
5968
|
targetName: fn.text,
|
|
5876
5969
|
wantKind: "function",
|
|
5877
|
-
edgeType:
|
|
5970
|
+
edgeType: import_types13.EdgeType.CALLS,
|
|
5878
5971
|
line
|
|
5879
5972
|
});
|
|
5880
5973
|
}
|
|
@@ -5890,15 +5983,15 @@ async function addSymbolEdges(graph, services) {
|
|
|
5890
5983
|
const targetSid = resolveTarget(req2.targetName, req2.wantKind);
|
|
5891
5984
|
if (!targetSid) continue;
|
|
5892
5985
|
if (targetSid === req2.sourceSid) continue;
|
|
5893
|
-
const edgeId = (0,
|
|
5986
|
+
const edgeId = (0, import_types13.extractedEdgeId)(req2.sourceSid, targetSid, req2.edgeType);
|
|
5894
5987
|
if (graph.hasEdge(edgeId)) continue;
|
|
5895
5988
|
const edge = {
|
|
5896
5989
|
id: edgeId,
|
|
5897
5990
|
source: req2.sourceSid,
|
|
5898
5991
|
target: targetSid,
|
|
5899
5992
|
type: req2.edgeType,
|
|
5900
|
-
provenance:
|
|
5901
|
-
confidence: (0,
|
|
5993
|
+
provenance: import_types13.Provenance.EXTRACTED,
|
|
5994
|
+
confidence: (0, import_types13.confidenceForExtracted)("structural"),
|
|
5902
5995
|
evidence: {
|
|
5903
5996
|
file: relPath,
|
|
5904
5997
|
line: req2.line,
|
|
@@ -5916,7 +6009,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
5916
6009
|
// src/extract/databases/index.ts
|
|
5917
6010
|
init_cjs_shims();
|
|
5918
6011
|
var import_node_path24 = __toESM(require("path"), 1);
|
|
5919
|
-
var
|
|
6012
|
+
var import_types14 = require("@neat.is/types");
|
|
5920
6013
|
|
|
5921
6014
|
// src/extract/databases/db-config-yaml.ts
|
|
5922
6015
|
init_cjs_shims();
|
|
@@ -6420,8 +6513,8 @@ function compatibleDriversFor(engine) {
|
|
|
6420
6513
|
}
|
|
6421
6514
|
function toDatabaseNode(config) {
|
|
6422
6515
|
return {
|
|
6423
|
-
id: (0,
|
|
6424
|
-
type:
|
|
6516
|
+
id: (0, import_types14.databaseId)(config.host),
|
|
6517
|
+
type: import_types14.NodeType.DatabaseNode,
|
|
6425
6518
|
name: config.database || config.host,
|
|
6426
6519
|
engine: config.engine,
|
|
6427
6520
|
engineVersion: config.engineVersion,
|
|
@@ -6562,12 +6655,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
6562
6655
|
edgesAdded += fe;
|
|
6563
6656
|
const evidenceFile = toPosix(import_node_path24.default.relative(scanPath, config.sourceFile));
|
|
6564
6657
|
const edge = {
|
|
6565
|
-
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id,
|
|
6658
|
+
id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types14.EdgeType.CONNECTS_TO),
|
|
6566
6659
|
source: fileNodeId,
|
|
6567
6660
|
target: dbNode.id,
|
|
6568
|
-
type:
|
|
6569
|
-
provenance:
|
|
6570
|
-
confidence: (0,
|
|
6661
|
+
type: import_types14.EdgeType.CONNECTS_TO,
|
|
6662
|
+
provenance: import_types14.Provenance.EXTRACTED,
|
|
6663
|
+
confidence: (0, import_types14.confidenceForExtracted)("structural"),
|
|
6571
6664
|
evidence: { file: evidenceFile }
|
|
6572
6665
|
};
|
|
6573
6666
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -6579,11 +6672,11 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
6579
6672
|
const primary = allConfigs[0];
|
|
6580
6673
|
service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
|
|
6581
6674
|
const relPath = import_node_path24.default.relative(scanPath, primary.sourceFile);
|
|
6582
|
-
const cfgId = (0,
|
|
6675
|
+
const cfgId = (0, import_types14.configId)(relPath);
|
|
6583
6676
|
if (!graph.hasNode(cfgId)) {
|
|
6584
6677
|
const cfgNode = {
|
|
6585
6678
|
id: cfgId,
|
|
6586
|
-
type:
|
|
6679
|
+
type: import_types14.NodeType.ConfigNode,
|
|
6587
6680
|
name: import_node_path24.default.basename(primary.sourceFile),
|
|
6588
6681
|
path: relPath,
|
|
6589
6682
|
fileType: isConfigFile(import_node_path24.default.basename(primary.sourceFile)).fileType || "config"
|
|
@@ -6592,12 +6685,12 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
6592
6685
|
nodesAdded++;
|
|
6593
6686
|
}
|
|
6594
6687
|
const cfgEdge = {
|
|
6595
|
-
id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId,
|
|
6688
|
+
id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types14.EdgeType.CONFIGURED_BY),
|
|
6596
6689
|
source: service.node.id,
|
|
6597
6690
|
target: cfgId,
|
|
6598
|
-
type:
|
|
6599
|
-
provenance:
|
|
6600
|
-
confidence: (0,
|
|
6691
|
+
type: import_types14.EdgeType.CONFIGURED_BY,
|
|
6692
|
+
provenance: import_types14.Provenance.EXTRACTED,
|
|
6693
|
+
confidence: (0, import_types14.confidenceForExtracted)("structural"),
|
|
6601
6694
|
evidence: { file: toPosix(relPath) }
|
|
6602
6695
|
};
|
|
6603
6696
|
if (!graph.hasEdge(cfgEdge.id)) {
|
|
@@ -6629,7 +6722,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
6629
6722
|
init_cjs_shims();
|
|
6630
6723
|
var import_node_fs15 = require("fs");
|
|
6631
6724
|
var import_node_path25 = __toESM(require("path"), 1);
|
|
6632
|
-
var
|
|
6725
|
+
var import_types15 = require("@neat.is/types");
|
|
6633
6726
|
async function walkConfigFiles(dir) {
|
|
6634
6727
|
const out = [];
|
|
6635
6728
|
async function walk6(current) {
|
|
@@ -6656,8 +6749,8 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
6656
6749
|
for (const file of configFiles) {
|
|
6657
6750
|
const relPath = import_node_path25.default.relative(scanPath, file);
|
|
6658
6751
|
const node = {
|
|
6659
|
-
id: (0,
|
|
6660
|
-
type:
|
|
6752
|
+
id: (0, import_types15.configId)(relPath),
|
|
6753
|
+
type: import_types15.NodeType.ConfigNode,
|
|
6661
6754
|
name: import_node_path25.default.basename(file),
|
|
6662
6755
|
path: relPath,
|
|
6663
6756
|
fileType: isConfigFile(import_node_path25.default.basename(file)).fileType
|
|
@@ -6676,12 +6769,12 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
6676
6769
|
nodesAdded += fn;
|
|
6677
6770
|
edgesAdded += fe;
|
|
6678
6771
|
const edge = {
|
|
6679
|
-
id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
6772
|
+
id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types15.EdgeType.CONFIGURED_BY),
|
|
6680
6773
|
source: fileNodeId,
|
|
6681
6774
|
target: node.id,
|
|
6682
|
-
type:
|
|
6683
|
-
provenance:
|
|
6684
|
-
confidence: (0,
|
|
6775
|
+
type: import_types15.EdgeType.CONFIGURED_BY,
|
|
6776
|
+
provenance: import_types15.Provenance.EXTRACTED,
|
|
6777
|
+
confidence: (0, import_types15.confidenceForExtracted)("structural"),
|
|
6685
6778
|
evidence: { file: relPath.split(import_node_path25.default.sep).join("/") }
|
|
6686
6779
|
};
|
|
6687
6780
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -6697,7 +6790,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
6697
6790
|
init_cjs_shims();
|
|
6698
6791
|
var import_node_fs16 = require("fs");
|
|
6699
6792
|
var import_node_path26 = __toESM(require("path"), 1);
|
|
6700
|
-
var
|
|
6793
|
+
var import_types16 = require("@neat.is/types");
|
|
6701
6794
|
var PROTO_EXTENSION = ".proto";
|
|
6702
6795
|
function packageOf(content) {
|
|
6703
6796
|
const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
|
|
@@ -6775,11 +6868,11 @@ async function addGrpcMethods(graph, services) {
|
|
|
6775
6868
|
}
|
|
6776
6869
|
if (methods.length === 0) continue;
|
|
6777
6870
|
for (const method of methods) {
|
|
6778
|
-
const mid = (0,
|
|
6871
|
+
const mid = (0, import_types16.grpcMethodId)(method.rpcService, method.rpcMethod);
|
|
6779
6872
|
if (!graph.hasNode(mid)) {
|
|
6780
6873
|
const node = {
|
|
6781
6874
|
id: mid,
|
|
6782
|
-
type:
|
|
6875
|
+
type: import_types16.NodeType.GrpcMethodNode,
|
|
6783
6876
|
name: `${method.rpcService}/${method.rpcMethod}`,
|
|
6784
6877
|
rpcService: method.rpcService,
|
|
6785
6878
|
rpcMethod: method.rpcMethod,
|
|
@@ -6790,15 +6883,15 @@ async function addGrpcMethods(graph, services) {
|
|
|
6790
6883
|
graph.addNode(mid, node);
|
|
6791
6884
|
nodesAdded++;
|
|
6792
6885
|
}
|
|
6793
|
-
const containsId = (0,
|
|
6886
|
+
const containsId = (0, import_types16.extractedEdgeId)(service.node.id, mid, import_types16.EdgeType.CONTAINS);
|
|
6794
6887
|
if (!graph.hasEdge(containsId)) {
|
|
6795
6888
|
const edge = {
|
|
6796
6889
|
id: containsId,
|
|
6797
6890
|
source: service.node.id,
|
|
6798
6891
|
target: mid,
|
|
6799
|
-
type:
|
|
6800
|
-
provenance:
|
|
6801
|
-
confidence: (0,
|
|
6892
|
+
type: import_types16.EdgeType.CONTAINS,
|
|
6893
|
+
provenance: import_types16.Provenance.EXTRACTED,
|
|
6894
|
+
confidence: (0, import_types16.confidenceForExtracted)("structural"),
|
|
6802
6895
|
evidence: {
|
|
6803
6896
|
file: relFile,
|
|
6804
6897
|
line: method.line,
|
|
@@ -6816,7 +6909,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
6816
6909
|
|
|
6817
6910
|
// src/extract/calls/index.ts
|
|
6818
6911
|
init_cjs_shims();
|
|
6819
|
-
var
|
|
6912
|
+
var import_types29 = require("@neat.is/types");
|
|
6820
6913
|
|
|
6821
6914
|
// src/extract/calls/http.ts
|
|
6822
6915
|
init_cjs_shims();
|
|
@@ -6824,7 +6917,7 @@ var import_node_path27 = __toESM(require("path"), 1);
|
|
|
6824
6917
|
var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
|
|
6825
6918
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
6826
6919
|
var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
|
|
6827
|
-
var
|
|
6920
|
+
var import_types17 = require("@neat.is/types");
|
|
6828
6921
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
6829
6922
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
6830
6923
|
function isInsideJsxExternalLink(node) {
|
|
@@ -6910,7 +7003,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
6910
7003
|
const dedupKey = `${relFile}|${targetId}`;
|
|
6911
7004
|
if (seen.has(dedupKey)) continue;
|
|
6912
7005
|
seen.add(dedupKey);
|
|
6913
|
-
const confidence = (0,
|
|
7006
|
+
const confidence = (0, import_types17.confidenceForExtracted)("url-literal-service-target");
|
|
6914
7007
|
const ev = {
|
|
6915
7008
|
file: relFile,
|
|
6916
7009
|
line: site.line,
|
|
@@ -6924,25 +7017,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
6924
7017
|
);
|
|
6925
7018
|
nodesAdded += n;
|
|
6926
7019
|
edgesAdded += e;
|
|
6927
|
-
if (!(0,
|
|
7020
|
+
if (!(0, import_types17.passesExtractedFloor)(confidence)) {
|
|
6928
7021
|
noteExtractedDropped({
|
|
6929
7022
|
source: fileNodeId,
|
|
6930
7023
|
target: targetId,
|
|
6931
|
-
type:
|
|
7024
|
+
type: import_types17.EdgeType.CALLS,
|
|
6932
7025
|
confidence,
|
|
6933
7026
|
confidenceKind: "url-literal-service-target",
|
|
6934
7027
|
evidence: ev
|
|
6935
7028
|
});
|
|
6936
7029
|
continue;
|
|
6937
7030
|
}
|
|
6938
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId,
|
|
7031
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types17.EdgeType.CALLS);
|
|
6939
7032
|
if (!graph.hasEdge(edgeId)) {
|
|
6940
7033
|
const edge = {
|
|
6941
7034
|
id: edgeId,
|
|
6942
7035
|
source: fileNodeId,
|
|
6943
7036
|
target: targetId,
|
|
6944
|
-
type:
|
|
6945
|
-
provenance:
|
|
7037
|
+
type: import_types17.EdgeType.CALLS,
|
|
7038
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
6946
7039
|
confidence,
|
|
6947
7040
|
evidence: ev
|
|
6948
7041
|
};
|
|
@@ -6960,7 +7053,7 @@ init_cjs_shims();
|
|
|
6960
7053
|
var import_node_path28 = __toESM(require("path"), 1);
|
|
6961
7054
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
6962
7055
|
var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
|
|
6963
|
-
var
|
|
7056
|
+
var import_types18 = require("@neat.is/types");
|
|
6964
7057
|
var PARSE_CHUNK5 = 16384;
|
|
6965
7058
|
function parseSource5(parser, source) {
|
|
6966
7059
|
return parser.parse(
|
|
@@ -7125,9 +7218,9 @@ function buildRouteIndex(graph) {
|
|
|
7125
7218
|
const index = /* @__PURE__ */ new Map();
|
|
7126
7219
|
graph.forEachNode((_id, attrs) => {
|
|
7127
7220
|
const node = attrs;
|
|
7128
|
-
if (node.type !==
|
|
7221
|
+
if (node.type !== import_types18.NodeType.RouteNode) return;
|
|
7129
7222
|
const route = attrs;
|
|
7130
|
-
const owner = (0,
|
|
7223
|
+
const owner = (0, import_types18.serviceId)(route.service);
|
|
7131
7224
|
const entry2 = {
|
|
7132
7225
|
method: route.method.toUpperCase(),
|
|
7133
7226
|
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
@@ -7185,7 +7278,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
7185
7278
|
);
|
|
7186
7279
|
nodesAdded += n;
|
|
7187
7280
|
edgesAdded += e;
|
|
7188
|
-
const confidence = (0,
|
|
7281
|
+
const confidence = (0, import_types18.confidenceForExtracted)("verified-call-site");
|
|
7189
7282
|
const ev = {
|
|
7190
7283
|
file: relFile,
|
|
7191
7284
|
line: site.line,
|
|
@@ -7193,25 +7286,25 @@ async function addRouteCallEdges(graph, services) {
|
|
|
7193
7286
|
method: site.method ?? match.method,
|
|
7194
7287
|
pathTemplate: site.pathTemplate
|
|
7195
7288
|
};
|
|
7196
|
-
if (!(0,
|
|
7289
|
+
if (!(0, import_types18.passesExtractedFloor)(confidence)) {
|
|
7197
7290
|
noteExtractedDropped({
|
|
7198
7291
|
source: fileNodeId,
|
|
7199
7292
|
target: match.routeNodeId,
|
|
7200
|
-
type:
|
|
7293
|
+
type: import_types18.EdgeType.CALLS,
|
|
7201
7294
|
confidence,
|
|
7202
7295
|
confidenceKind: "verified-call-site",
|
|
7203
7296
|
evidence: ev
|
|
7204
7297
|
});
|
|
7205
7298
|
continue;
|
|
7206
7299
|
}
|
|
7207
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId,
|
|
7300
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types18.EdgeType.CALLS);
|
|
7208
7301
|
if (!graph.hasEdge(edgeId)) {
|
|
7209
7302
|
const edge = {
|
|
7210
7303
|
id: edgeId,
|
|
7211
7304
|
source: fileNodeId,
|
|
7212
7305
|
target: match.routeNodeId,
|
|
7213
|
-
type:
|
|
7214
|
-
provenance:
|
|
7306
|
+
type: import_types18.EdgeType.CALLS,
|
|
7307
|
+
provenance: import_types18.Provenance.EXTRACTED,
|
|
7215
7308
|
confidence,
|
|
7216
7309
|
evidence: ev
|
|
7217
7310
|
};
|
|
@@ -7227,7 +7320,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
7227
7320
|
// src/extract/calls/kafka.ts
|
|
7228
7321
|
init_cjs_shims();
|
|
7229
7322
|
var import_node_path29 = __toESM(require("path"), 1);
|
|
7230
|
-
var
|
|
7323
|
+
var import_types19 = require("@neat.is/types");
|
|
7231
7324
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
7232
7325
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
7233
7326
|
function findAll(re, text) {
|
|
@@ -7248,7 +7341,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
7248
7341
|
seen.add(key);
|
|
7249
7342
|
const line = lineOf(file.content, topic);
|
|
7250
7343
|
out.push({
|
|
7251
|
-
infraId: (0,
|
|
7344
|
+
infraId: (0, import_types19.infraId)("kafka-topic", topic),
|
|
7252
7345
|
name: topic,
|
|
7253
7346
|
kind: "kafka-topic",
|
|
7254
7347
|
edgeType,
|
|
@@ -7271,7 +7364,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
7271
7364
|
// src/extract/calls/redis.ts
|
|
7272
7365
|
init_cjs_shims();
|
|
7273
7366
|
var import_node_path30 = __toESM(require("path"), 1);
|
|
7274
|
-
var
|
|
7367
|
+
var import_types20 = require("@neat.is/types");
|
|
7275
7368
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
7276
7369
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
7277
7370
|
const out = [];
|
|
@@ -7284,7 +7377,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
7284
7377
|
seen.add(host);
|
|
7285
7378
|
const line = lineOf(file.content, host);
|
|
7286
7379
|
out.push({
|
|
7287
|
-
infraId: (0,
|
|
7380
|
+
infraId: (0, import_types20.infraId)("redis", host),
|
|
7288
7381
|
name: host,
|
|
7289
7382
|
kind: "redis",
|
|
7290
7383
|
edgeType: "CALLS",
|
|
@@ -7305,7 +7398,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
7305
7398
|
// src/extract/calls/aws.ts
|
|
7306
7399
|
init_cjs_shims();
|
|
7307
7400
|
var import_node_path31 = __toESM(require("path"), 1);
|
|
7308
|
-
var
|
|
7401
|
+
var import_types21 = require("@neat.is/types");
|
|
7309
7402
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
7310
7403
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
7311
7404
|
function hasMarker(text, markers) {
|
|
@@ -7329,7 +7422,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
7329
7422
|
seen.add(key);
|
|
7330
7423
|
const line = lineOf(file.content, name);
|
|
7331
7424
|
out.push({
|
|
7332
|
-
infraId: (0,
|
|
7425
|
+
infraId: (0, import_types21.infraId)(kind, name),
|
|
7333
7426
|
name,
|
|
7334
7427
|
kind,
|
|
7335
7428
|
edgeType: "CALLS",
|
|
@@ -7364,7 +7457,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
7364
7457
|
// src/extract/calls/grpc.ts
|
|
7365
7458
|
init_cjs_shims();
|
|
7366
7459
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
7367
|
-
var
|
|
7460
|
+
var import_types22 = require("@neat.is/types");
|
|
7368
7461
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
7369
7462
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
7370
7463
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -7413,7 +7506,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
7413
7506
|
const { kind } = classified;
|
|
7414
7507
|
const line = lineOf(file.content, m[0]);
|
|
7415
7508
|
out.push({
|
|
7416
|
-
infraId: (0,
|
|
7509
|
+
infraId: (0, import_types22.infraId)(kind, name),
|
|
7417
7510
|
name,
|
|
7418
7511
|
kind,
|
|
7419
7512
|
edgeType: "CALLS",
|
|
@@ -7434,7 +7527,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
7434
7527
|
// src/extract/calls/supabase.ts
|
|
7435
7528
|
init_cjs_shims();
|
|
7436
7529
|
var import_node_path33 = __toESM(require("path"), 1);
|
|
7437
|
-
var
|
|
7530
|
+
var import_types23 = require("@neat.is/types");
|
|
7438
7531
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
7439
7532
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
7440
7533
|
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
@@ -7482,7 +7575,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
7482
7575
|
seen.add(name);
|
|
7483
7576
|
const line = lineOf(file.content, m[0]);
|
|
7484
7577
|
out.push({
|
|
7485
|
-
infraId: (0,
|
|
7578
|
+
infraId: (0, import_types23.infraId)("supabase", name),
|
|
7486
7579
|
name,
|
|
7487
7580
|
kind: "supabase",
|
|
7488
7581
|
edgeType: "CALLS",
|
|
@@ -7513,7 +7606,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
7513
7606
|
seen.add(key);
|
|
7514
7607
|
const line = lineOf(file.content, am[0]);
|
|
7515
7608
|
out.push({
|
|
7516
|
-
infraId: (0,
|
|
7609
|
+
infraId: (0, import_types23.infraId)(kind, resource),
|
|
7517
7610
|
name: resource,
|
|
7518
7611
|
kind,
|
|
7519
7612
|
edgeType: "CALLS",
|
|
@@ -7532,7 +7625,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
7532
7625
|
// src/extract/calls/mongoose.ts
|
|
7533
7626
|
init_cjs_shims();
|
|
7534
7627
|
var import_node_path34 = __toESM(require("path"), 1);
|
|
7535
|
-
var
|
|
7628
|
+
var import_types24 = require("@neat.is/types");
|
|
7536
7629
|
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
7537
7630
|
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
7538
7631
|
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
@@ -7677,7 +7770,7 @@ function collectModelDefs(content, pluralizeOn) {
|
|
|
7677
7770
|
function endpoint(r, file, serviceDir, matchText) {
|
|
7678
7771
|
const line = lineOf(file.content, matchText);
|
|
7679
7772
|
return {
|
|
7680
|
-
infraId: (0,
|
|
7773
|
+
infraId: (0, import_types24.infraId)(r.kind, r.name),
|
|
7681
7774
|
name: r.name,
|
|
7682
7775
|
kind: r.kind,
|
|
7683
7776
|
edgeType: "CALLS",
|
|
@@ -7830,7 +7923,7 @@ init_cjs_shims();
|
|
|
7830
7923
|
var import_node_path35 = __toESM(require("path"), 1);
|
|
7831
7924
|
var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
|
|
7832
7925
|
var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
7833
|
-
var
|
|
7926
|
+
var import_types25 = require("@neat.is/types");
|
|
7834
7927
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
7835
7928
|
var PARSE_CHUNK6 = 16384;
|
|
7836
7929
|
function makePyParser4() {
|
|
@@ -7890,20 +7983,57 @@ function walk3(node, visit) {
|
|
|
7890
7983
|
visit(node);
|
|
7891
7984
|
for (const c of namedChildren(node)) walk3(c, visit);
|
|
7892
7985
|
}
|
|
7986
|
+
var COLUMN_BUILDERS = /* @__PURE__ */ new Set(["Column", "mapped_column"]);
|
|
7987
|
+
function isColumnBuilder(call) {
|
|
7988
|
+
const fn = call.childForFieldName("function");
|
|
7989
|
+
const t = fn?.text;
|
|
7990
|
+
if (!t) return false;
|
|
7991
|
+
const base = t.includes(".") ? t.slice(t.lastIndexOf(".") + 1) : t;
|
|
7992
|
+
return COLUMN_BUILDERS.has(base);
|
|
7993
|
+
}
|
|
7994
|
+
function firstPositionalString(call) {
|
|
7995
|
+
const args = call.childForFieldName("arguments");
|
|
7996
|
+
if (!args) return null;
|
|
7997
|
+
for (const arg of namedChildren(args)) {
|
|
7998
|
+
if (arg.type === "keyword_argument") continue;
|
|
7999
|
+
return arg.type === "string" ? pyStaticStringText2(arg) : null;
|
|
8000
|
+
}
|
|
8001
|
+
return null;
|
|
8002
|
+
}
|
|
8003
|
+
function columnsFromClassBody(body) {
|
|
8004
|
+
const out = [];
|
|
8005
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8006
|
+
for (const stmt of namedChildren(body)) {
|
|
8007
|
+
if (stmt.type !== "expression_statement") continue;
|
|
8008
|
+
const assign = stmt.namedChild(0);
|
|
8009
|
+
if (assign?.type !== "assignment") continue;
|
|
8010
|
+
const left = assign.childForFieldName("left");
|
|
8011
|
+
if (left?.type !== "identifier") continue;
|
|
8012
|
+
const right = assign.childForFieldName("right");
|
|
8013
|
+
if (right?.type !== "call" || !isColumnBuilder(right)) continue;
|
|
8014
|
+
const name = firstPositionalString(right) ?? left.text;
|
|
8015
|
+
if (name && !seen.has(name)) {
|
|
8016
|
+
seen.add(name);
|
|
8017
|
+
out.push(name);
|
|
8018
|
+
}
|
|
8019
|
+
}
|
|
8020
|
+
return out;
|
|
8021
|
+
}
|
|
7893
8022
|
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
7894
8023
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
7895
8024
|
const tree = parseSource6(makePyParser4(), file.content);
|
|
7896
8025
|
const out = [];
|
|
7897
8026
|
const seen = /* @__PURE__ */ new Set();
|
|
7898
|
-
const push = (name, line) => {
|
|
8027
|
+
const push = (name, line, columns) => {
|
|
7899
8028
|
if (seen.has(name)) return;
|
|
7900
8029
|
seen.add(name);
|
|
7901
8030
|
out.push({
|
|
7902
|
-
infraId: (0,
|
|
8031
|
+
infraId: (0, import_types25.infraId)("sql-table", name),
|
|
7903
8032
|
name,
|
|
7904
8033
|
kind: "sql-table",
|
|
7905
8034
|
edgeType: "CALLS",
|
|
7906
8035
|
confidenceKind: "verified-call-site",
|
|
8036
|
+
...columns && columns.length > 0 ? { columns } : {},
|
|
7907
8037
|
evidence: {
|
|
7908
8038
|
file: import_node_path35.default.relative(serviceDir, file.path),
|
|
7909
8039
|
line,
|
|
@@ -7919,11 +8049,12 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
7919
8049
|
const line = node.startPosition.row + 1;
|
|
7920
8050
|
const explicit = explicitTablename(body);
|
|
7921
8051
|
if (explicit === "computed") return;
|
|
8052
|
+
const columns = columnsFromClassBody(body);
|
|
7922
8053
|
if (explicit) {
|
|
7923
|
-
push(explicit.name, line);
|
|
8054
|
+
push(explicit.name, line, columns);
|
|
7924
8055
|
return;
|
|
7925
8056
|
}
|
|
7926
|
-
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
|
|
8057
|
+
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line, columns);
|
|
7927
8058
|
return;
|
|
7928
8059
|
}
|
|
7929
8060
|
if (node.type === "call") {
|
|
@@ -8005,7 +8136,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
|
8005
8136
|
if (seen.has(key)) continue;
|
|
8006
8137
|
seen.add(key);
|
|
8007
8138
|
out.push({
|
|
8008
|
-
infraId: (0,
|
|
8139
|
+
infraId: (0, import_types25.infraId)("sql-table", t),
|
|
8009
8140
|
name: t,
|
|
8010
8141
|
kind: "sql-table",
|
|
8011
8142
|
edgeType: "CALLS",
|
|
@@ -8026,7 +8157,7 @@ init_cjs_shims();
|
|
|
8026
8157
|
var import_node_path36 = __toESM(require("path"), 1);
|
|
8027
8158
|
var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
|
|
8028
8159
|
var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
8029
|
-
var
|
|
8160
|
+
var import_types26 = require("@neat.is/types");
|
|
8030
8161
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
8031
8162
|
var PARSE_CHUNK7 = 16384;
|
|
8032
8163
|
function makePyParser5() {
|
|
@@ -8106,7 +8237,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
8106
8237
|
seen.add(table);
|
|
8107
8238
|
const line = node.startPosition.row + 1;
|
|
8108
8239
|
out.push({
|
|
8109
|
-
infraId: (0,
|
|
8240
|
+
infraId: (0, import_types26.infraId)("sql-table", table),
|
|
8110
8241
|
name: table,
|
|
8111
8242
|
kind: "sql-table",
|
|
8112
8243
|
edgeType: "CALLS",
|
|
@@ -8117,12 +8248,129 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
|
8117
8248
|
return out;
|
|
8118
8249
|
}
|
|
8119
8250
|
|
|
8120
|
-
// src/extract/calls/
|
|
8251
|
+
// src/extract/calls/drizzle.ts
|
|
8121
8252
|
init_cjs_shims();
|
|
8122
|
-
var
|
|
8253
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
8123
8254
|
var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
|
|
8255
|
+
var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
|
|
8256
|
+
var import_types27 = require("@neat.is/types");
|
|
8257
|
+
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
8258
|
+
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
8259
|
+
function parserForExt(ext) {
|
|
8260
|
+
const p = new import_tree_sitter9.default();
|
|
8261
|
+
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
|
|
8262
|
+
return p;
|
|
8263
|
+
}
|
|
8264
|
+
function namedChildren3(node) {
|
|
8265
|
+
const out = [];
|
|
8266
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
8267
|
+
const c = node.namedChild(i);
|
|
8268
|
+
if (c) out.push(c);
|
|
8269
|
+
}
|
|
8270
|
+
return out;
|
|
8271
|
+
}
|
|
8272
|
+
function stringLiteralText2(node) {
|
|
8273
|
+
if (!node || node.type !== "string") return null;
|
|
8274
|
+
for (const child of namedChildren3(node)) {
|
|
8275
|
+
if (child.type === "string_fragment") return child.text;
|
|
8276
|
+
}
|
|
8277
|
+
return "";
|
|
8278
|
+
}
|
|
8279
|
+
function firstStringArg(call) {
|
|
8280
|
+
const args = call.childForFieldName("arguments");
|
|
8281
|
+
if (!args) return null;
|
|
8282
|
+
for (const arg of namedChildren3(args)) {
|
|
8283
|
+
if (arg.type === "string") return stringLiteralText2(arg);
|
|
8284
|
+
return null;
|
|
8285
|
+
}
|
|
8286
|
+
return null;
|
|
8287
|
+
}
|
|
8288
|
+
function builderColumnName(value) {
|
|
8289
|
+
let node = value;
|
|
8290
|
+
while (node) {
|
|
8291
|
+
if (node.type === "call_expression") {
|
|
8292
|
+
const fn = node.childForFieldName("function");
|
|
8293
|
+
if (fn?.type === "identifier") return firstStringArg(node);
|
|
8294
|
+
if (fn?.type === "member_expression") {
|
|
8295
|
+
node = fn.childForFieldName("object");
|
|
8296
|
+
continue;
|
|
8297
|
+
}
|
|
8298
|
+
return null;
|
|
8299
|
+
}
|
|
8300
|
+
if (node.type === "member_expression") {
|
|
8301
|
+
node = node.childForFieldName("object");
|
|
8302
|
+
continue;
|
|
8303
|
+
}
|
|
8304
|
+
return null;
|
|
8305
|
+
}
|
|
8306
|
+
return null;
|
|
8307
|
+
}
|
|
8308
|
+
function keyName(key) {
|
|
8309
|
+
if (!key) return null;
|
|
8310
|
+
if (key.type === "property_identifier") return key.text;
|
|
8311
|
+
if (key.type === "string") return stringLiteralText2(key);
|
|
8312
|
+
return null;
|
|
8313
|
+
}
|
|
8314
|
+
function columnsFromObject(obj) {
|
|
8315
|
+
const out = [];
|
|
8316
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8317
|
+
for (const child of namedChildren3(obj)) {
|
|
8318
|
+
if (child.type !== "pair") continue;
|
|
8319
|
+
const value = child.childForFieldName("value");
|
|
8320
|
+
const key = keyName(child.childForFieldName("key"));
|
|
8321
|
+
const name = (value ? builderColumnName(value) : null) ?? key;
|
|
8322
|
+
if (name && !seen.has(name)) {
|
|
8323
|
+
seen.add(name);
|
|
8324
|
+
out.push(name);
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
return out;
|
|
8328
|
+
}
|
|
8329
|
+
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
8330
|
+
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
8331
|
+
const tree = parseSource2(parserForExt(import_node_path37.default.extname(file.path)), file.content);
|
|
8332
|
+
const out = [];
|
|
8333
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8334
|
+
const walk6 = (node) => {
|
|
8335
|
+
if (node.type === "call_expression") {
|
|
8336
|
+
const fn = node.childForFieldName("function");
|
|
8337
|
+
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
8338
|
+
const args = node.childForFieldName("arguments");
|
|
8339
|
+
const argNodes = args ? namedChildren3(args) : [];
|
|
8340
|
+
const tableName = stringLiteralText2(argNodes[0] ?? null);
|
|
8341
|
+
const obj = argNodes[1];
|
|
8342
|
+
if (tableName && obj?.type === "object" && !seen.has(tableName)) {
|
|
8343
|
+
seen.add(tableName);
|
|
8344
|
+
const columns = columnsFromObject(obj);
|
|
8345
|
+
const line = node.startPosition.row + 1;
|
|
8346
|
+
out.push({
|
|
8347
|
+
infraId: (0, import_types27.infraId)("sql-table", tableName),
|
|
8348
|
+
name: tableName,
|
|
8349
|
+
kind: "sql-table",
|
|
8350
|
+
edgeType: "CALLS",
|
|
8351
|
+
confidenceKind: "structural",
|
|
8352
|
+
columns,
|
|
8353
|
+
evidence: {
|
|
8354
|
+
file: import_node_path37.default.relative(serviceDir, file.path),
|
|
8355
|
+
line,
|
|
8356
|
+
snippet: snippet(file.content, line)
|
|
8357
|
+
}
|
|
8358
|
+
});
|
|
8359
|
+
}
|
|
8360
|
+
}
|
|
8361
|
+
}
|
|
8362
|
+
for (const c of namedChildren3(node)) walk6(c);
|
|
8363
|
+
};
|
|
8364
|
+
walk6(tree.rootNode);
|
|
8365
|
+
return out;
|
|
8366
|
+
}
|
|
8367
|
+
|
|
8368
|
+
// src/extract/calls/go.ts
|
|
8369
|
+
init_cjs_shims();
|
|
8370
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
8371
|
+
var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
|
|
8124
8372
|
var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
|
|
8125
|
-
var
|
|
8373
|
+
var import_types28 = require("@neat.is/types");
|
|
8126
8374
|
init_otel();
|
|
8127
8375
|
var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
|
|
8128
8376
|
var PARSE_CHUNK8 = 16384;
|
|
@@ -8134,8 +8382,8 @@ function walk5(node, visit) {
|
|
|
8134
8382
|
}
|
|
8135
8383
|
}
|
|
8136
8384
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
8137
|
-
if (
|
|
8138
|
-
const parser = new
|
|
8385
|
+
if (import_node_path40.default.extname(file.path) !== ".go") return [];
|
|
8386
|
+
const parser = new import_tree_sitter10.default();
|
|
8139
8387
|
parser.setLanguage(import_tree_sitter_go3.default);
|
|
8140
8388
|
const tree = parser.parse(
|
|
8141
8389
|
(index) => index >= file.content.length ? "" : file.content.slice(index, index + PARSE_CHUNK8)
|
|
@@ -8154,12 +8402,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
8154
8402
|
if (!table) return;
|
|
8155
8403
|
const line = node.startPosition.row + 1;
|
|
8156
8404
|
out.push({
|
|
8157
|
-
infraId: (0,
|
|
8405
|
+
infraId: (0, import_types28.infraId)("sql-table", table),
|
|
8158
8406
|
name: table,
|
|
8159
8407
|
kind: "sql-table",
|
|
8160
8408
|
edgeType: "CALLS",
|
|
8161
8409
|
confidenceKind: "verified-call-site",
|
|
8162
|
-
evidence: { file: toPosix(
|
|
8410
|
+
evidence: { file: toPosix(import_node_path40.default.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
|
|
8163
8411
|
});
|
|
8164
8412
|
});
|
|
8165
8413
|
return out;
|
|
@@ -8169,11 +8417,11 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
8169
8417
|
function edgeTypeFromEndpoint(ep) {
|
|
8170
8418
|
switch (ep.edgeType) {
|
|
8171
8419
|
case "PUBLISHES_TO":
|
|
8172
|
-
return
|
|
8420
|
+
return import_types29.EdgeType.PUBLISHES_TO;
|
|
8173
8421
|
case "CONSUMES_FROM":
|
|
8174
|
-
return
|
|
8422
|
+
return import_types29.EdgeType.CONSUMES_FROM;
|
|
8175
8423
|
default:
|
|
8176
|
-
return
|
|
8424
|
+
return import_types29.EdgeType.CALLS;
|
|
8177
8425
|
}
|
|
8178
8426
|
}
|
|
8179
8427
|
function isAwsKind(kind) {
|
|
@@ -8199,6 +8447,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8199
8447
|
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
8200
8448
|
endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
|
|
8201
8449
|
endpoints.push(...djangoOrmEndpointsFromFile(maskedFile, service.dir));
|
|
8450
|
+
endpoints.push(...drizzleEndpointsFromFile(maskedFile, service.dir));
|
|
8202
8451
|
try {
|
|
8203
8452
|
endpoints.push(...goSqlEndpointsFromFile(maskedFile, service.dir));
|
|
8204
8453
|
} catch (err) {
|
|
@@ -8213,7 +8462,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8213
8462
|
if (!graph.hasNode(ep.infraId)) {
|
|
8214
8463
|
const node = {
|
|
8215
8464
|
id: ep.infraId,
|
|
8216
|
-
type:
|
|
8465
|
+
type: import_types29.NodeType.InfraNode,
|
|
8217
8466
|
name: ep.name,
|
|
8218
8467
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
8219
8468
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -8224,8 +8473,22 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8224
8473
|
graph.addNode(node.id, node);
|
|
8225
8474
|
nodesAdded++;
|
|
8226
8475
|
}
|
|
8476
|
+
if (ep.columns && ep.columns.length > 0) {
|
|
8477
|
+
const node = graph.getNodeAttributes(ep.infraId);
|
|
8478
|
+
if (node.type === import_types29.NodeType.InfraNode) {
|
|
8479
|
+
graph.replaceNodeAttributes(ep.infraId, {
|
|
8480
|
+
...node,
|
|
8481
|
+
columns: foldColumns(
|
|
8482
|
+
node.columns,
|
|
8483
|
+
ep.columns,
|
|
8484
|
+
import_types29.Provenance.EXTRACTED,
|
|
8485
|
+
(0, import_types29.confidenceForExtracted)(ep.confidenceKind)
|
|
8486
|
+
)
|
|
8487
|
+
});
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8227
8490
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
8228
|
-
const confidence = (0,
|
|
8491
|
+
const confidence = (0, import_types29.confidenceForExtracted)(ep.confidenceKind);
|
|
8229
8492
|
const relFile = toPosix(ep.evidence.file);
|
|
8230
8493
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
8231
8494
|
graph,
|
|
@@ -8235,7 +8498,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8235
8498
|
);
|
|
8236
8499
|
nodesAdded += n;
|
|
8237
8500
|
edgesAdded += e;
|
|
8238
|
-
if (!(0,
|
|
8501
|
+
if (!(0, import_types29.passesExtractedFloor)(confidence)) {
|
|
8239
8502
|
noteExtractedDropped({
|
|
8240
8503
|
source: fileNodeId,
|
|
8241
8504
|
target: ep.infraId,
|
|
@@ -8255,7 +8518,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
8255
8518
|
source: fileNodeId,
|
|
8256
8519
|
target: ep.infraId,
|
|
8257
8520
|
type: edgeType,
|
|
8258
|
-
provenance:
|
|
8521
|
+
provenance: import_types29.Provenance.EXTRACTED,
|
|
8259
8522
|
confidence,
|
|
8260
8523
|
evidence: ep.evidence
|
|
8261
8524
|
};
|
|
@@ -8281,16 +8544,16 @@ init_cjs_shims();
|
|
|
8281
8544
|
|
|
8282
8545
|
// src/extract/infra/docker-compose.ts
|
|
8283
8546
|
init_cjs_shims();
|
|
8284
|
-
var
|
|
8285
|
-
var
|
|
8547
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
8548
|
+
var import_types31 = require("@neat.is/types");
|
|
8286
8549
|
|
|
8287
8550
|
// src/extract/infra/shared.ts
|
|
8288
8551
|
init_cjs_shims();
|
|
8289
|
-
var
|
|
8552
|
+
var import_types30 = require("@neat.is/types");
|
|
8290
8553
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
8291
8554
|
return {
|
|
8292
|
-
id: (0,
|
|
8293
|
-
type:
|
|
8555
|
+
id: (0, import_types30.infraId)(kind, name),
|
|
8556
|
+
type: import_types30.NodeType.InfraNode,
|
|
8294
8557
|
name,
|
|
8295
8558
|
provider,
|
|
8296
8559
|
kind,
|
|
@@ -8334,8 +8597,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
8334
8597
|
source: anchorId,
|
|
8335
8598
|
target: node.id,
|
|
8336
8599
|
type: edgeType,
|
|
8337
|
-
provenance:
|
|
8338
|
-
confidence: (0,
|
|
8600
|
+
provenance: import_types30.Provenance.EXTRACTED,
|
|
8601
|
+
confidence: (0, import_types30.confidenceForExtracted)("structural"),
|
|
8339
8602
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
8340
8603
|
};
|
|
8341
8604
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8352,7 +8615,7 @@ function dependsOnList(value) {
|
|
|
8352
8615
|
}
|
|
8353
8616
|
function serviceNameToServiceNode(name, services) {
|
|
8354
8617
|
for (const s of services) {
|
|
8355
|
-
if (s.node.name === name ||
|
|
8618
|
+
if (s.node.name === name || import_node_path41.default.basename(s.dir) === name) return s.node.id;
|
|
8356
8619
|
}
|
|
8357
8620
|
return null;
|
|
8358
8621
|
}
|
|
@@ -8361,7 +8624,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8361
8624
|
let edgesAdded = 0;
|
|
8362
8625
|
let composePath = null;
|
|
8363
8626
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
8364
|
-
const abs =
|
|
8627
|
+
const abs = import_node_path41.default.join(scanPath, name);
|
|
8365
8628
|
if (await exists(abs)) {
|
|
8366
8629
|
composePath = abs;
|
|
8367
8630
|
break;
|
|
@@ -8374,13 +8637,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8374
8637
|
} catch (err) {
|
|
8375
8638
|
recordExtractionError(
|
|
8376
8639
|
"infra docker-compose",
|
|
8377
|
-
|
|
8640
|
+
import_node_path41.default.relative(scanPath, composePath),
|
|
8378
8641
|
err
|
|
8379
8642
|
);
|
|
8380
8643
|
return { nodesAdded, edgesAdded };
|
|
8381
8644
|
}
|
|
8382
8645
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
8383
|
-
const evidenceFile =
|
|
8646
|
+
const evidenceFile = import_node_path41.default.relative(scanPath, composePath).split(import_node_path41.default.sep).join("/");
|
|
8384
8647
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
8385
8648
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
8386
8649
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -8402,15 +8665,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8402
8665
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
8403
8666
|
const targetId = composeNameToNodeId.get(dep);
|
|
8404
8667
|
if (!targetId) continue;
|
|
8405
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
8668
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types31.EdgeType.DEPENDS_ON);
|
|
8406
8669
|
if (graph.hasEdge(edgeId)) continue;
|
|
8407
8670
|
const edge = {
|
|
8408
8671
|
id: edgeId,
|
|
8409
8672
|
source: sourceId,
|
|
8410
8673
|
target: targetId,
|
|
8411
|
-
type:
|
|
8412
|
-
provenance:
|
|
8413
|
-
confidence: (0,
|
|
8674
|
+
type: import_types31.EdgeType.DEPENDS_ON,
|
|
8675
|
+
provenance: import_types31.Provenance.EXTRACTED,
|
|
8676
|
+
confidence: (0, import_types31.confidenceForExtracted)("structural"),
|
|
8414
8677
|
evidence: { file: evidenceFile }
|
|
8415
8678
|
};
|
|
8416
8679
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8422,9 +8685,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8422
8685
|
|
|
8423
8686
|
// src/extract/infra/dockerfile.ts
|
|
8424
8687
|
init_cjs_shims();
|
|
8425
|
-
var
|
|
8688
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
8426
8689
|
var import_node_fs17 = require("fs");
|
|
8427
|
-
var
|
|
8690
|
+
var import_types32 = require("@neat.is/types");
|
|
8428
8691
|
function readDockerfile(content) {
|
|
8429
8692
|
let image = null;
|
|
8430
8693
|
const ports = [];
|
|
@@ -8453,7 +8716,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8453
8716
|
let nodesAdded = 0;
|
|
8454
8717
|
let edgesAdded = 0;
|
|
8455
8718
|
for (const service of services) {
|
|
8456
|
-
const dockerfilePath =
|
|
8719
|
+
const dockerfilePath = import_node_path42.default.join(service.dir, "Dockerfile");
|
|
8457
8720
|
if (!await exists(dockerfilePath)) continue;
|
|
8458
8721
|
let content;
|
|
8459
8722
|
try {
|
|
@@ -8461,7 +8724,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8461
8724
|
} catch (err) {
|
|
8462
8725
|
recordExtractionError(
|
|
8463
8726
|
"infra dockerfile",
|
|
8464
|
-
|
|
8727
|
+
import_node_path42.default.relative(scanPath, dockerfilePath),
|
|
8465
8728
|
err
|
|
8466
8729
|
);
|
|
8467
8730
|
continue;
|
|
@@ -8473,8 +8736,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8473
8736
|
graph.addNode(node.id, node);
|
|
8474
8737
|
nodesAdded++;
|
|
8475
8738
|
}
|
|
8476
|
-
const relDockerfile = toPosix(
|
|
8477
|
-
const evidenceFile = toPosix(
|
|
8739
|
+
const relDockerfile = toPosix(import_node_path42.default.relative(service.dir, dockerfilePath));
|
|
8740
|
+
const evidenceFile = toPosix(import_node_path42.default.relative(scanPath, dockerfilePath));
|
|
8478
8741
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
8479
8742
|
graph,
|
|
8480
8743
|
service.pkg.name,
|
|
@@ -8483,15 +8746,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8483
8746
|
);
|
|
8484
8747
|
nodesAdded += fn;
|
|
8485
8748
|
edgesAdded += fe;
|
|
8486
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
8749
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types32.EdgeType.RUNS_ON);
|
|
8487
8750
|
if (!graph.hasEdge(edgeId)) {
|
|
8488
8751
|
const edge = {
|
|
8489
8752
|
id: edgeId,
|
|
8490
8753
|
source: fileNodeId,
|
|
8491
8754
|
target: node.id,
|
|
8492
|
-
type:
|
|
8493
|
-
provenance:
|
|
8494
|
-
confidence: (0,
|
|
8755
|
+
type: import_types32.EdgeType.RUNS_ON,
|
|
8756
|
+
provenance: import_types32.Provenance.EXTRACTED,
|
|
8757
|
+
confidence: (0, import_types32.confidenceForExtracted)("structural"),
|
|
8495
8758
|
evidence: {
|
|
8496
8759
|
file: evidenceFile,
|
|
8497
8760
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -8506,15 +8769,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8506
8769
|
graph.addNode(portNode.id, portNode);
|
|
8507
8770
|
nodesAdded++;
|
|
8508
8771
|
}
|
|
8509
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
8772
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types32.EdgeType.CONNECTS_TO);
|
|
8510
8773
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
8511
8774
|
const portEdge = {
|
|
8512
8775
|
id: portEdgeId,
|
|
8513
8776
|
source: fileNodeId,
|
|
8514
8777
|
target: portNode.id,
|
|
8515
|
-
type:
|
|
8516
|
-
provenance:
|
|
8517
|
-
confidence: (0,
|
|
8778
|
+
type: import_types32.EdgeType.CONNECTS_TO,
|
|
8779
|
+
provenance: import_types32.Provenance.EXTRACTED,
|
|
8780
|
+
confidence: (0, import_types32.confidenceForExtracted)("structural"),
|
|
8518
8781
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
8519
8782
|
};
|
|
8520
8783
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -8527,8 +8790,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8527
8790
|
// src/extract/infra/terraform.ts
|
|
8528
8791
|
init_cjs_shims();
|
|
8529
8792
|
var import_node_fs18 = require("fs");
|
|
8530
|
-
var
|
|
8531
|
-
var
|
|
8793
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8794
|
+
var import_types33 = require("@neat.is/types");
|
|
8532
8795
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
8533
8796
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
8534
8797
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -8538,11 +8801,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
8538
8801
|
for (const entry2 of entries) {
|
|
8539
8802
|
if (entry2.isDirectory()) {
|
|
8540
8803
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
8541
|
-
const child =
|
|
8804
|
+
const child = import_node_path43.default.join(start, entry2.name);
|
|
8542
8805
|
if (await isPythonVenvDir(child)) continue;
|
|
8543
8806
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
8544
8807
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
8545
|
-
out.push(
|
|
8808
|
+
out.push(import_node_path43.default.join(start, entry2.name));
|
|
8546
8809
|
}
|
|
8547
8810
|
}
|
|
8548
8811
|
return out;
|
|
@@ -8574,7 +8837,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8574
8837
|
const files = await walkTfFiles(scanPath);
|
|
8575
8838
|
for (const file of files) {
|
|
8576
8839
|
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
8577
|
-
const evidenceFile = toPosix(
|
|
8840
|
+
const evidenceFile = toPosix(import_node_path43.default.relative(scanPath, file));
|
|
8578
8841
|
const resources = [];
|
|
8579
8842
|
const byKey = /* @__PURE__ */ new Map();
|
|
8580
8843
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -8609,16 +8872,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8609
8872
|
if (!target) continue;
|
|
8610
8873
|
if (seen.has(target.nodeId)) continue;
|
|
8611
8874
|
seen.add(target.nodeId);
|
|
8612
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
8875
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types33.EdgeType.DEPENDS_ON);
|
|
8613
8876
|
if (graph.hasEdge(edgeId)) continue;
|
|
8614
8877
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
8615
8878
|
const edge = {
|
|
8616
8879
|
id: edgeId,
|
|
8617
8880
|
source: resource.nodeId,
|
|
8618
8881
|
target: target.nodeId,
|
|
8619
|
-
type:
|
|
8620
|
-
provenance:
|
|
8621
|
-
confidence: (0,
|
|
8882
|
+
type: import_types33.EdgeType.DEPENDS_ON,
|
|
8883
|
+
provenance: import_types33.Provenance.EXTRACTED,
|
|
8884
|
+
confidence: (0, import_types33.confidenceForExtracted)("structural"),
|
|
8622
8885
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
8623
8886
|
};
|
|
8624
8887
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8632,7 +8895,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8632
8895
|
// src/extract/infra/k8s.ts
|
|
8633
8896
|
init_cjs_shims();
|
|
8634
8897
|
var import_node_fs19 = require("fs");
|
|
8635
|
-
var
|
|
8898
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8636
8899
|
var import_yaml3 = require("yaml");
|
|
8637
8900
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
8638
8901
|
Service: "k8s-service",
|
|
@@ -8650,11 +8913,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
8650
8913
|
for (const entry2 of entries) {
|
|
8651
8914
|
if (entry2.isDirectory()) {
|
|
8652
8915
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
8653
|
-
const child =
|
|
8916
|
+
const child = import_node_path44.default.join(start, entry2.name);
|
|
8654
8917
|
if (await isPythonVenvDir(child)) continue;
|
|
8655
8918
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
8656
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
8657
|
-
out.push(
|
|
8919
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path44.default.extname(entry2.name))) {
|
|
8920
|
+
out.push(import_node_path44.default.join(start, entry2.name));
|
|
8658
8921
|
}
|
|
8659
8922
|
}
|
|
8660
8923
|
return out;
|
|
@@ -8688,13 +8951,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
8688
8951
|
// src/extract/infra/cloudflare.ts
|
|
8689
8952
|
init_cjs_shims();
|
|
8690
8953
|
var import_node_fs20 = require("fs");
|
|
8691
|
-
var
|
|
8954
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8692
8955
|
var import_smol_toml2 = require("smol-toml");
|
|
8693
|
-
var
|
|
8956
|
+
var import_types34 = require("@neat.is/types");
|
|
8694
8957
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
8695
8958
|
async function readWranglerConfig(dir) {
|
|
8696
8959
|
for (const filename of WRANGLER_FILENAMES) {
|
|
8697
|
-
const abs =
|
|
8960
|
+
const abs = import_node_path45.default.join(dir, filename);
|
|
8698
8961
|
if (!await exists(abs)) continue;
|
|
8699
8962
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
8700
8963
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -8738,8 +9001,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
8738
9001
|
source: anchorId,
|
|
8739
9002
|
target: node.id,
|
|
8740
9003
|
type: edgeType,
|
|
8741
|
-
provenance:
|
|
8742
|
-
confidence: (0,
|
|
9004
|
+
provenance: import_types34.Provenance.EXTRACTED,
|
|
9005
|
+
confidence: (0, import_types34.confidenceForExtracted)("structural"),
|
|
8743
9006
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
8744
9007
|
};
|
|
8745
9008
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8757,11 +9020,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8757
9020
|
try {
|
|
8758
9021
|
read = await readWranglerConfig(service.dir);
|
|
8759
9022
|
} catch (err) {
|
|
8760
|
-
recordExtractionError("infra cloudflare",
|
|
9023
|
+
recordExtractionError("infra cloudflare", import_node_path45.default.relative(scanPath, service.dir), err);
|
|
8761
9024
|
continue;
|
|
8762
9025
|
}
|
|
8763
9026
|
if (!read || !read.config.name) continue;
|
|
8764
|
-
const evidenceFile = toPosix(
|
|
9027
|
+
const evidenceFile = toPosix(import_node_path45.default.relative(scanPath, import_node_path45.default.join(service.dir, read.relFile)));
|
|
8765
9028
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
8766
9029
|
}
|
|
8767
9030
|
for (const worker of discovered) {
|
|
@@ -8773,7 +9036,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8773
9036
|
}
|
|
8774
9037
|
let anchorId = service.node.id;
|
|
8775
9038
|
if (config.main) {
|
|
8776
|
-
const entryRelPath = toPosix(
|
|
9039
|
+
const entryRelPath = toPosix(import_node_path45.default.normalize(config.main));
|
|
8777
9040
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
8778
9041
|
graph,
|
|
8779
9042
|
service.pkg.name,
|
|
@@ -8800,15 +9063,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8800
9063
|
nodesAdded++;
|
|
8801
9064
|
}
|
|
8802
9065
|
if (runtimeNode.id !== anchorId) {
|
|
8803
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
9066
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types34.EdgeType.RUNS_ON);
|
|
8804
9067
|
if (!graph.hasEdge(runsOnId)) {
|
|
8805
9068
|
const edge = {
|
|
8806
9069
|
id: runsOnId,
|
|
8807
9070
|
source: anchorId,
|
|
8808
9071
|
target: runtimeNode.id,
|
|
8809
|
-
type:
|
|
8810
|
-
provenance:
|
|
8811
|
-
confidence: (0,
|
|
9072
|
+
type: import_types34.EdgeType.RUNS_ON,
|
|
9073
|
+
provenance: import_types34.Provenance.EXTRACTED,
|
|
9074
|
+
confidence: (0, import_types34.confidenceForExtracted)("structural"),
|
|
8812
9075
|
evidence: {
|
|
8813
9076
|
file: evidenceFile,
|
|
8814
9077
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -8822,7 +9085,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8822
9085
|
const result = addResourceEdge(
|
|
8823
9086
|
graph,
|
|
8824
9087
|
anchorId,
|
|
8825
|
-
|
|
9088
|
+
import_types34.EdgeType.CONNECTS_TO,
|
|
8826
9089
|
"cloudflare-route",
|
|
8827
9090
|
route,
|
|
8828
9091
|
evidenceFile,
|
|
@@ -8846,7 +9109,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8846
9109
|
const result = addResourceEdge(
|
|
8847
9110
|
graph,
|
|
8848
9111
|
anchorId,
|
|
8849
|
-
|
|
9112
|
+
import_types34.EdgeType.DEPENDS_ON,
|
|
8850
9113
|
group.kind,
|
|
8851
9114
|
name,
|
|
8852
9115
|
evidenceFile,
|
|
@@ -8860,7 +9123,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8860
9123
|
const result = addResourceEdge(
|
|
8861
9124
|
graph,
|
|
8862
9125
|
anchorId,
|
|
8863
|
-
|
|
9126
|
+
import_types34.EdgeType.DEPENDS_ON,
|
|
8864
9127
|
"cloudflare-cron",
|
|
8865
9128
|
cron,
|
|
8866
9129
|
evidenceFile,
|
|
@@ -8873,7 +9136,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8873
9136
|
const result = addResourceEdge(
|
|
8874
9137
|
graph,
|
|
8875
9138
|
anchorId,
|
|
8876
|
-
|
|
9139
|
+
import_types34.EdgeType.DEPENDS_ON,
|
|
8877
9140
|
"cloudflare-env-var",
|
|
8878
9141
|
varName,
|
|
8879
9142
|
evidenceFile,
|
|
@@ -8886,15 +9149,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8886
9149
|
if (!svc.service) continue;
|
|
8887
9150
|
const target = workerIndex.get(svc.service);
|
|
8888
9151
|
if (target && target.anchorId !== anchorId) {
|
|
8889
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
9152
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types34.EdgeType.CALLS);
|
|
8890
9153
|
if (!graph.hasEdge(edgeId)) {
|
|
8891
9154
|
const edge = {
|
|
8892
9155
|
id: edgeId,
|
|
8893
9156
|
source: anchorId,
|
|
8894
9157
|
target: target.anchorId,
|
|
8895
|
-
type:
|
|
8896
|
-
provenance:
|
|
8897
|
-
confidence: (0,
|
|
9158
|
+
type: import_types34.EdgeType.CALLS,
|
|
9159
|
+
provenance: import_types34.Provenance.EXTRACTED,
|
|
9160
|
+
confidence: (0, import_types34.confidenceForExtracted)("structural"),
|
|
8898
9161
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
8899
9162
|
};
|
|
8900
9163
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -8905,7 +9168,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8905
9168
|
const result = addResourceEdge(
|
|
8906
9169
|
graph,
|
|
8907
9170
|
anchorId,
|
|
8908
|
-
|
|
9171
|
+
import_types34.EdgeType.DEPENDS_ON,
|
|
8909
9172
|
"cloudflare-service-binding",
|
|
8910
9173
|
svc.service,
|
|
8911
9174
|
evidenceFile,
|
|
@@ -8921,12 +9184,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8921
9184
|
// src/extract/infra/vercel.ts
|
|
8922
9185
|
init_cjs_shims();
|
|
8923
9186
|
var import_node_fs21 = require("fs");
|
|
8924
|
-
var
|
|
8925
|
-
var
|
|
9187
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
9188
|
+
var import_types35 = require("@neat.is/types");
|
|
8926
9189
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
8927
9190
|
async function readVercelConfig(dir) {
|
|
8928
9191
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
8929
|
-
const abs =
|
|
9192
|
+
const abs = import_node_path46.default.join(dir, filename);
|
|
8930
9193
|
if (!await exists(abs)) continue;
|
|
8931
9194
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
8932
9195
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -8935,7 +9198,7 @@ async function readVercelConfig(dir) {
|
|
|
8935
9198
|
return null;
|
|
8936
9199
|
}
|
|
8937
9200
|
async function readLinkedProjectName(dir) {
|
|
8938
|
-
const abs =
|
|
9201
|
+
const abs = import_node_path46.default.join(dir, ".vercel", "project.json");
|
|
8939
9202
|
if (!await exists(abs)) return void 0;
|
|
8940
9203
|
const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
|
|
8941
9204
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -8953,7 +9216,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8953
9216
|
read = await readVercelConfig(service.dir);
|
|
8954
9217
|
projectName = await readLinkedProjectName(service.dir);
|
|
8955
9218
|
} catch (err) {
|
|
8956
|
-
recordExtractionError("infra vercel",
|
|
9219
|
+
recordExtractionError("infra vercel", import_node_path46.default.relative(scanPath, service.dir), err);
|
|
8957
9220
|
continue;
|
|
8958
9221
|
}
|
|
8959
9222
|
if (!read && !projectName) continue;
|
|
@@ -8969,7 +9232,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8969
9232
|
const anchorId = service.node.id;
|
|
8970
9233
|
if (!read) continue;
|
|
8971
9234
|
const { config, relFile, raw } = read;
|
|
8972
|
-
const evidenceFile = toPosix(
|
|
9235
|
+
const evidenceFile = toPosix(import_node_path46.default.relative(scanPath, import_node_path46.default.join(service.dir, relFile)));
|
|
8973
9236
|
const add = (edgeType, kind, name) => {
|
|
8974
9237
|
if (!name) return;
|
|
8975
9238
|
const result = emitPlatformResourceEdge(
|
|
@@ -8985,12 +9248,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8985
9248
|
nodesAdded += result.nodesAdded;
|
|
8986
9249
|
edgesAdded += result.edgesAdded;
|
|
8987
9250
|
};
|
|
8988
|
-
add(
|
|
8989
|
-
for (const cron of config.crons ?? []) add(
|
|
8990
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
8991
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
9251
|
+
add(import_types35.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
9252
|
+
for (const cron of config.crons ?? []) add(import_types35.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
9253
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
9254
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types35.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
8992
9255
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
8993
|
-
add(
|
|
9256
|
+
add(import_types35.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
8994
9257
|
}
|
|
8995
9258
|
}
|
|
8996
9259
|
return { nodesAdded, edgesAdded };
|
|
@@ -8999,13 +9262,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8999
9262
|
// src/extract/infra/railway.ts
|
|
9000
9263
|
init_cjs_shims();
|
|
9001
9264
|
var import_node_fs22 = require("fs");
|
|
9002
|
-
var
|
|
9265
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9003
9266
|
var import_smol_toml3 = require("smol-toml");
|
|
9004
|
-
var
|
|
9267
|
+
var import_types36 = require("@neat.is/types");
|
|
9005
9268
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
9006
9269
|
async function readRailwayConfig(dir) {
|
|
9007
9270
|
for (const filename of RAILWAY_FILENAMES) {
|
|
9008
|
-
const abs =
|
|
9271
|
+
const abs = import_node_path47.default.join(dir, filename);
|
|
9009
9272
|
if (!await exists(abs)) continue;
|
|
9010
9273
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
9011
9274
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -9021,7 +9284,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9021
9284
|
try {
|
|
9022
9285
|
read = await readRailwayConfig(service.dir);
|
|
9023
9286
|
} catch (err) {
|
|
9024
|
-
recordExtractionError("infra railway",
|
|
9287
|
+
recordExtractionError("infra railway", import_node_path47.default.relative(scanPath, service.dir), err);
|
|
9025
9288
|
continue;
|
|
9026
9289
|
}
|
|
9027
9290
|
if (!read) continue;
|
|
@@ -9031,7 +9294,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9031
9294
|
}
|
|
9032
9295
|
const anchorId = service.node.id;
|
|
9033
9296
|
const { config, relFile, raw } = read;
|
|
9034
|
-
const evidenceFile = toPosix(
|
|
9297
|
+
const evidenceFile = toPosix(import_node_path47.default.relative(scanPath, import_node_path47.default.join(service.dir, relFile)));
|
|
9035
9298
|
const add = (edgeType, kind, name) => {
|
|
9036
9299
|
if (!name) return;
|
|
9037
9300
|
const result = emitPlatformResourceEdge(
|
|
@@ -9047,9 +9310,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9047
9310
|
nodesAdded += result.nodesAdded;
|
|
9048
9311
|
edgesAdded += result.edgesAdded;
|
|
9049
9312
|
};
|
|
9050
|
-
add(
|
|
9051
|
-
add(
|
|
9052
|
-
add(
|
|
9313
|
+
add(import_types36.EdgeType.RUNS_ON, "railway", "railway");
|
|
9314
|
+
add(import_types36.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
9315
|
+
add(import_types36.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
9053
9316
|
}
|
|
9054
9317
|
return { nodesAdded, edgesAdded };
|
|
9055
9318
|
}
|
|
@@ -9057,12 +9320,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
9057
9320
|
// src/extract/infra/supabase.ts
|
|
9058
9321
|
init_cjs_shims();
|
|
9059
9322
|
var import_node_fs23 = require("fs");
|
|
9060
|
-
var
|
|
9323
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9061
9324
|
var import_smol_toml4 = require("smol-toml");
|
|
9062
|
-
var
|
|
9325
|
+
var import_types37 = require("@neat.is/types");
|
|
9063
9326
|
async function readSupabaseConfig(dir) {
|
|
9064
|
-
const relFile =
|
|
9065
|
-
const abs =
|
|
9327
|
+
const relFile = import_node_path48.default.join("supabase", "config.toml");
|
|
9328
|
+
const abs = import_node_path48.default.join(dir, relFile);
|
|
9066
9329
|
if (!await exists(abs)) return null;
|
|
9067
9330
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
9068
9331
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -9076,7 +9339,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9076
9339
|
try {
|
|
9077
9340
|
read = await readSupabaseConfig(service.dir);
|
|
9078
9341
|
} catch (err) {
|
|
9079
|
-
recordExtractionError("infra supabase",
|
|
9342
|
+
recordExtractionError("infra supabase", import_node_path48.default.relative(scanPath, service.dir), err);
|
|
9080
9343
|
continue;
|
|
9081
9344
|
}
|
|
9082
9345
|
if (!read) continue;
|
|
@@ -9091,7 +9354,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9091
9354
|
});
|
|
9092
9355
|
}
|
|
9093
9356
|
const anchorId = service.node.id;
|
|
9094
|
-
const evidenceFile = toPosix(
|
|
9357
|
+
const evidenceFile = toPosix(import_node_path48.default.relative(scanPath, import_node_path48.default.join(service.dir, relFile)));
|
|
9095
9358
|
const add = (edgeType, kind, name) => {
|
|
9096
9359
|
if (!name) return;
|
|
9097
9360
|
const result = emitPlatformResourceEdge(
|
|
@@ -9107,10 +9370,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
9107
9370
|
nodesAdded += result.nodesAdded;
|
|
9108
9371
|
edgesAdded += result.edgesAdded;
|
|
9109
9372
|
};
|
|
9110
|
-
add(
|
|
9111
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
9112
|
-
if (config.storage) add(
|
|
9113
|
-
if (config.auth) add(
|
|
9373
|
+
add(import_types37.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
9374
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types37.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
9375
|
+
if (config.storage) add(import_types37.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
9376
|
+
if (config.auth) add(import_types37.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
9114
9377
|
}
|
|
9115
9378
|
return { nodesAdded, edgesAdded };
|
|
9116
9379
|
}
|
|
@@ -9132,17 +9395,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
9132
9395
|
}
|
|
9133
9396
|
|
|
9134
9397
|
// src/extract/index.ts
|
|
9135
|
-
var
|
|
9398
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9136
9399
|
|
|
9137
9400
|
// src/extract/retire.ts
|
|
9138
9401
|
init_cjs_shims();
|
|
9139
9402
|
var import_node_fs24 = require("fs");
|
|
9140
|
-
var
|
|
9141
|
-
var
|
|
9403
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9404
|
+
var import_types38 = require("@neat.is/types");
|
|
9142
9405
|
function dropOrphanedFileNodes(graph) {
|
|
9143
9406
|
const orphans = [];
|
|
9144
9407
|
graph.forEachNode((id, attrs) => {
|
|
9145
|
-
if (attrs.type !==
|
|
9408
|
+
if (attrs.type !== import_types38.NodeType.FileNode) return;
|
|
9146
9409
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
9147
9410
|
orphans.push(id);
|
|
9148
9411
|
}
|
|
@@ -9155,14 +9418,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
9155
9418
|
const bases = [scanPath, ...serviceDirs];
|
|
9156
9419
|
graph.forEachEdge((id, attrs) => {
|
|
9157
9420
|
const edge = attrs;
|
|
9158
|
-
if (edge.provenance !==
|
|
9421
|
+
if (edge.provenance !== import_types38.Provenance.EXTRACTED) return;
|
|
9159
9422
|
const evidenceFile = edge.evidence?.file;
|
|
9160
9423
|
if (!evidenceFile) return;
|
|
9161
|
-
if (
|
|
9424
|
+
if (import_node_path49.default.isAbsolute(evidenceFile)) {
|
|
9162
9425
|
if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
|
|
9163
9426
|
return;
|
|
9164
9427
|
}
|
|
9165
|
-
const found = bases.some((base) => (0, import_node_fs24.existsSync)(
|
|
9428
|
+
const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path49.default.join(base, evidenceFile)));
|
|
9166
9429
|
if (!found) toDrop.push(id);
|
|
9167
9430
|
});
|
|
9168
9431
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -9215,7 +9478,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
9215
9478
|
}
|
|
9216
9479
|
const droppedEntries = drainDroppedExtracted();
|
|
9217
9480
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
9218
|
-
const rejectedPath =
|
|
9481
|
+
const rejectedPath = import_node_path50.default.join(import_node_path50.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
9219
9482
|
try {
|
|
9220
9483
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
9221
9484
|
} catch (err) {
|
|
@@ -9250,9 +9513,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
9250
9513
|
// src/persist.ts
|
|
9251
9514
|
init_cjs_shims();
|
|
9252
9515
|
var import_node_fs25 = require("fs");
|
|
9253
|
-
var
|
|
9254
|
-
var
|
|
9255
|
-
var SCHEMA_VERSION =
|
|
9516
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
9517
|
+
var import_types39 = require("@neat.is/types");
|
|
9518
|
+
var SCHEMA_VERSION = 6;
|
|
9256
9519
|
function migrateV1ToV2(payload) {
|
|
9257
9520
|
const nodes = payload.graph.nodes;
|
|
9258
9521
|
if (Array.isArray(nodes)) {
|
|
@@ -9270,18 +9533,30 @@ function migrateV3ToV4(payload) {
|
|
|
9270
9533
|
function migrateV4ToV5(payload) {
|
|
9271
9534
|
return { ...payload, schemaVersion: 5 };
|
|
9272
9535
|
}
|
|
9536
|
+
function migrateV5ToV6(payload) {
|
|
9537
|
+
const nodes = payload.graph.nodes;
|
|
9538
|
+
if (Array.isArray(nodes)) {
|
|
9539
|
+
for (const node of nodes) {
|
|
9540
|
+
const attrs = node.attributes;
|
|
9541
|
+
if (!attrs || attrs.type !== import_types39.NodeType.InfraNode) continue;
|
|
9542
|
+
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
9543
|
+
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
return { ...payload, schemaVersion: 6 };
|
|
9547
|
+
}
|
|
9273
9548
|
function migrateV2ToV3(payload) {
|
|
9274
9549
|
const edges = payload.graph.edges;
|
|
9275
9550
|
if (Array.isArray(edges)) {
|
|
9276
9551
|
for (const edge of edges) {
|
|
9277
9552
|
const attrs = edge.attributes;
|
|
9278
9553
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
9279
|
-
attrs.provenance =
|
|
9554
|
+
attrs.provenance = import_types39.Provenance.OBSERVED;
|
|
9280
9555
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
9281
9556
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
9282
9557
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
9283
9558
|
if (type && source && target) {
|
|
9284
|
-
const newId = (0,
|
|
9559
|
+
const newId = (0, import_types39.observedEdgeId)(source, target, type);
|
|
9285
9560
|
attrs.id = newId;
|
|
9286
9561
|
if (edge.key) edge.key = newId;
|
|
9287
9562
|
}
|
|
@@ -9290,7 +9565,7 @@ function migrateV2ToV3(payload) {
|
|
|
9290
9565
|
return { ...payload, schemaVersion: 3 };
|
|
9291
9566
|
}
|
|
9292
9567
|
async function ensureDir(filePath) {
|
|
9293
|
-
await import_node_fs25.promises.mkdir(
|
|
9568
|
+
await import_node_fs25.promises.mkdir(import_node_path51.default.dirname(filePath), { recursive: true });
|
|
9294
9569
|
}
|
|
9295
9570
|
async function saveGraphToDisk(graph, outPath) {
|
|
9296
9571
|
await ensureDir(outPath);
|
|
@@ -9324,6 +9599,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
9324
9599
|
if (payload.schemaVersion === 4) {
|
|
9325
9600
|
payload = migrateV4ToV5(payload);
|
|
9326
9601
|
}
|
|
9602
|
+
if (payload.schemaVersion === 5) {
|
|
9603
|
+
payload = migrateV5ToV6(payload);
|
|
9604
|
+
}
|
|
9327
9605
|
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
9328
9606
|
throw new Error(
|
|
9329
9607
|
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
@@ -9374,23 +9652,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
9374
9652
|
|
|
9375
9653
|
// src/projects.ts
|
|
9376
9654
|
init_cjs_shims();
|
|
9377
|
-
var
|
|
9655
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
9378
9656
|
function pathsForProject(project, baseDir) {
|
|
9379
9657
|
if (project === DEFAULT_PROJECT) {
|
|
9380
9658
|
return {
|
|
9381
|
-
snapshotPath:
|
|
9382
|
-
errorsPath:
|
|
9383
|
-
staleEventsPath:
|
|
9384
|
-
embeddingsCachePath:
|
|
9385
|
-
policyViolationsPath:
|
|
9659
|
+
snapshotPath: import_node_path52.default.join(baseDir, "graph.json"),
|
|
9660
|
+
errorsPath: import_node_path52.default.join(baseDir, "errors.ndjson"),
|
|
9661
|
+
staleEventsPath: import_node_path52.default.join(baseDir, "stale-events.ndjson"),
|
|
9662
|
+
embeddingsCachePath: import_node_path52.default.join(baseDir, "embeddings.json"),
|
|
9663
|
+
policyViolationsPath: import_node_path52.default.join(baseDir, "policy-violations.ndjson")
|
|
9386
9664
|
};
|
|
9387
9665
|
}
|
|
9388
9666
|
return {
|
|
9389
|
-
snapshotPath:
|
|
9390
|
-
errorsPath:
|
|
9391
|
-
staleEventsPath:
|
|
9392
|
-
embeddingsCachePath:
|
|
9393
|
-
policyViolationsPath:
|
|
9667
|
+
snapshotPath: import_node_path52.default.join(baseDir, `${project}.json`),
|
|
9668
|
+
errorsPath: import_node_path52.default.join(baseDir, `errors.${project}.ndjson`),
|
|
9669
|
+
staleEventsPath: import_node_path52.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
9670
|
+
embeddingsCachePath: import_node_path52.default.join(baseDir, `embeddings.${project}.json`),
|
|
9671
|
+
policyViolationsPath: import_node_path52.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
9394
9672
|
};
|
|
9395
9673
|
}
|
|
9396
9674
|
var Projects = class {
|
|
@@ -9428,19 +9706,19 @@ var Projects = class {
|
|
|
9428
9706
|
init_cjs_shims();
|
|
9429
9707
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
9430
9708
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
9431
|
-
var
|
|
9709
|
+
var import_types58 = require("@neat.is/types");
|
|
9432
9710
|
|
|
9433
9711
|
// src/extend/index.ts
|
|
9434
9712
|
init_cjs_shims();
|
|
9435
9713
|
var import_node_fs27 = require("fs");
|
|
9436
|
-
var
|
|
9714
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
9437
9715
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
9438
9716
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
9439
9717
|
|
|
9440
9718
|
// src/installers/package-manager.ts
|
|
9441
9719
|
init_cjs_shims();
|
|
9442
9720
|
var import_node_fs26 = require("fs");
|
|
9443
|
-
var
|
|
9721
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
9444
9722
|
var import_node_child_process = require("child_process");
|
|
9445
9723
|
var LOCKFILE_PRIORITY = [
|
|
9446
9724
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -9462,22 +9740,22 @@ async function exists2(p) {
|
|
|
9462
9740
|
}
|
|
9463
9741
|
}
|
|
9464
9742
|
async function detectPackageManager(serviceDir) {
|
|
9465
|
-
let dir =
|
|
9743
|
+
let dir = import_node_path53.default.resolve(serviceDir);
|
|
9466
9744
|
const stops = /* @__PURE__ */ new Set();
|
|
9467
9745
|
for (let i = 0; i < 64; i++) {
|
|
9468
9746
|
if (stops.has(dir)) break;
|
|
9469
9747
|
stops.add(dir);
|
|
9470
9748
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
9471
|
-
const lockPath =
|
|
9749
|
+
const lockPath = import_node_path53.default.join(dir, candidate.lockfile);
|
|
9472
9750
|
if (await exists2(lockPath)) {
|
|
9473
9751
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
9474
9752
|
}
|
|
9475
9753
|
}
|
|
9476
|
-
const parent =
|
|
9754
|
+
const parent = import_node_path53.default.dirname(dir);
|
|
9477
9755
|
if (parent === dir) break;
|
|
9478
9756
|
dir = parent;
|
|
9479
9757
|
}
|
|
9480
|
-
return { pm: "npm", cwd:
|
|
9758
|
+
return { pm: "npm", cwd: import_node_path53.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
9481
9759
|
}
|
|
9482
9760
|
async function runPackageManagerInstall(cmd) {
|
|
9483
9761
|
return new Promise((resolve) => {
|
|
@@ -9526,7 +9804,7 @@ async function fileExists2(p) {
|
|
|
9526
9804
|
}
|
|
9527
9805
|
}
|
|
9528
9806
|
async function readPackageJson(scanPath) {
|
|
9529
|
-
const pkgPath =
|
|
9807
|
+
const pkgPath = import_node_path54.default.join(scanPath, "package.json");
|
|
9530
9808
|
const raw = await import_node_fs27.promises.readFile(pkgPath, "utf8");
|
|
9531
9809
|
return JSON.parse(raw);
|
|
9532
9810
|
}
|
|
@@ -9545,11 +9823,11 @@ async function findHookFiles(scanPath) {
|
|
|
9545
9823
|
for (const entry2 of entries) {
|
|
9546
9824
|
if (entry2.isDirectory()) {
|
|
9547
9825
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
9548
|
-
await walk6(
|
|
9826
|
+
await walk6(import_node_path54.default.join(dir, entry2.name));
|
|
9549
9827
|
} else if (entry2.isFile()) {
|
|
9550
9828
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
9551
|
-
const rel =
|
|
9552
|
-
found.push(rel.split(
|
|
9829
|
+
const rel = import_node_path54.default.relative(scanPath, import_node_path54.default.join(dir, entry2.name));
|
|
9830
|
+
found.push(rel.split(import_node_path54.default.sep).join("/"));
|
|
9553
9831
|
}
|
|
9554
9832
|
}
|
|
9555
9833
|
}
|
|
@@ -9560,7 +9838,7 @@ async function findHookFiles(scanPath) {
|
|
|
9560
9838
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
9561
9839
|
let fallback = null;
|
|
9562
9840
|
for (const file of hookFiles) {
|
|
9563
|
-
const content = await import_node_fs27.promises.readFile(
|
|
9841
|
+
const content = await import_node_fs27.promises.readFile(import_node_path54.default.join(scanPath, file), "utf8");
|
|
9564
9842
|
const patched = splicedContent(content, snippet2);
|
|
9565
9843
|
if (patched !== null) return { file, content, patched };
|
|
9566
9844
|
if (fallback === null) fallback = { file, content };
|
|
@@ -9568,11 +9846,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
9568
9846
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
9569
9847
|
}
|
|
9570
9848
|
function extendLogPath() {
|
|
9571
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
9849
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path54.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
9572
9850
|
}
|
|
9573
9851
|
async function appendExtendLog(entry2) {
|
|
9574
9852
|
const logPath = extendLogPath();
|
|
9575
|
-
await import_node_fs27.promises.mkdir(
|
|
9853
|
+
await import_node_fs27.promises.mkdir(import_node_path54.default.dirname(logPath), { recursive: true });
|
|
9576
9854
|
await import_node_fs27.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
9577
9855
|
}
|
|
9578
9856
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -9631,7 +9909,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
9631
9909
|
}
|
|
9632
9910
|
async function describeProjectInstrumentation(ctx) {
|
|
9633
9911
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
9634
|
-
const envNeat = await fileExists2(
|
|
9912
|
+
const envNeat = await fileExists2(import_node_path54.default.join(ctx.scanPath, ".env.neat"));
|
|
9635
9913
|
const registryInstrPackages = new Set(
|
|
9636
9914
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
9637
9915
|
);
|
|
@@ -9653,7 +9931,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
9653
9931
|
);
|
|
9654
9932
|
}
|
|
9655
9933
|
for (const file of hookFiles) {
|
|
9656
|
-
const content = await import_node_fs27.promises.readFile(
|
|
9934
|
+
const content = await import_node_fs27.promises.readFile(import_node_path54.default.join(ctx.scanPath, file), "utf8");
|
|
9657
9935
|
if (content.includes(args.registration_snippet)) {
|
|
9658
9936
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
9659
9937
|
}
|
|
@@ -9665,10 +9943,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
9665
9943
|
);
|
|
9666
9944
|
}
|
|
9667
9945
|
const primaryFile = primary.file;
|
|
9668
|
-
const primaryPath =
|
|
9946
|
+
const primaryPath = import_node_path54.default.join(ctx.scanPath, primaryFile);
|
|
9669
9947
|
const filesTouched = [];
|
|
9670
9948
|
const depsAdded = [];
|
|
9671
|
-
const pkgPath =
|
|
9949
|
+
const pkgPath = import_node_path54.default.join(ctx.scanPath, "package.json");
|
|
9672
9950
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
9673
9951
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
9674
9952
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -9707,7 +9985,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
9707
9985
|
};
|
|
9708
9986
|
}
|
|
9709
9987
|
for (const file of hookFiles) {
|
|
9710
|
-
const content = await import_node_fs27.promises.readFile(
|
|
9988
|
+
const content = await import_node_fs27.promises.readFile(import_node_path54.default.join(ctx.scanPath, file), "utf8");
|
|
9711
9989
|
if (content.includes(args.registration_snippet)) {
|
|
9712
9990
|
return {
|
|
9713
9991
|
library: args.library,
|
|
@@ -9748,7 +10026,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
9748
10026
|
if (!match) {
|
|
9749
10027
|
return { undone: false, message: "no apply found for library" };
|
|
9750
10028
|
}
|
|
9751
|
-
const pkgPath =
|
|
10029
|
+
const pkgPath = import_node_path54.default.join(ctx.scanPath, "package.json");
|
|
9752
10030
|
if (await fileExists2(pkgPath)) {
|
|
9753
10031
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
9754
10032
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -9759,7 +10037,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
9759
10037
|
}
|
|
9760
10038
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
9761
10039
|
for (const file of hookFiles) {
|
|
9762
|
-
const filePath =
|
|
10040
|
+
const filePath = import_node_path54.default.join(ctx.scanPath, file);
|
|
9763
10041
|
const content = await import_node_fs27.promises.readFile(filePath, "utf8");
|
|
9764
10042
|
if (content.includes(match.registration_snippet)) {
|
|
9765
10043
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -9775,39 +10053,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
9775
10053
|
|
|
9776
10054
|
// src/divergences.ts
|
|
9777
10055
|
init_cjs_shims();
|
|
9778
|
-
var
|
|
10056
|
+
var import_types40 = require("@neat.is/types");
|
|
9779
10057
|
function bucketKey(source, target, type) {
|
|
9780
10058
|
return `${type}|${source}|${target}`;
|
|
9781
10059
|
}
|
|
9782
10060
|
function bucketSourceFor(graph, edge) {
|
|
9783
|
-
if (edge.type !==
|
|
9784
|
-
const parsed = (0,
|
|
10061
|
+
if (edge.type !== import_types40.EdgeType.CONNECTS_TO) return edge.source;
|
|
10062
|
+
const parsed = (0, import_types40.parseFileId)(edge.source);
|
|
9785
10063
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
9786
10064
|
const target = graph.getNodeAttributes(edge.target);
|
|
9787
|
-
if (target.type !==
|
|
9788
|
-
return (0,
|
|
10065
|
+
if (target.type !== import_types40.NodeType.DatabaseNode) return edge.source;
|
|
10066
|
+
return (0, import_types40.serviceId)(parsed.service);
|
|
9789
10067
|
}
|
|
9790
10068
|
function bucketEdges(graph) {
|
|
9791
10069
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
9792
10070
|
graph.forEachEdge((id, attrs) => {
|
|
9793
10071
|
const e = attrs;
|
|
9794
|
-
const parsed = (0,
|
|
10072
|
+
const parsed = (0, import_types40.parseEdgeId)(id);
|
|
9795
10073
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
9796
10074
|
const source = bucketSourceFor(graph, e);
|
|
9797
10075
|
const key = bucketKey(source, e.target, e.type);
|
|
9798
10076
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
9799
10077
|
switch (provenance) {
|
|
9800
|
-
case
|
|
10078
|
+
case import_types40.Provenance.EXTRACTED:
|
|
9801
10079
|
cur.extracted = e;
|
|
9802
10080
|
break;
|
|
9803
|
-
case
|
|
10081
|
+
case import_types40.Provenance.OBSERVED:
|
|
9804
10082
|
cur.observed = e;
|
|
9805
10083
|
break;
|
|
9806
|
-
case
|
|
10084
|
+
case import_types40.Provenance.INFERRED:
|
|
9807
10085
|
cur.inferred = e;
|
|
9808
10086
|
break;
|
|
9809
10087
|
default:
|
|
9810
|
-
if (e.provenance ===
|
|
10088
|
+
if (e.provenance === import_types40.Provenance.STALE) cur.stale = e;
|
|
9811
10089
|
}
|
|
9812
10090
|
buckets2.set(key, cur);
|
|
9813
10091
|
});
|
|
@@ -9816,17 +10094,17 @@ function bucketEdges(graph) {
|
|
|
9816
10094
|
function nodeIsFrontier(graph, nodeId) {
|
|
9817
10095
|
if (!graph.hasNode(nodeId)) return false;
|
|
9818
10096
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
9819
|
-
return attrs.type ===
|
|
10097
|
+
return attrs.type === import_types40.NodeType.FrontierNode;
|
|
9820
10098
|
}
|
|
9821
10099
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
9822
10100
|
if (!graph.hasNode(nodeId)) return false;
|
|
9823
10101
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
9824
|
-
return attrs.type ===
|
|
10102
|
+
return attrs.type === import_types40.NodeType.WebSocketChannelNode;
|
|
9825
10103
|
}
|
|
9826
10104
|
function nodeIsSymbol(graph, nodeId) {
|
|
9827
10105
|
if (!graph.hasNode(nodeId)) return false;
|
|
9828
10106
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
9829
|
-
return attrs.type ===
|
|
10107
|
+
return attrs.type === import_types40.NodeType.SymbolNode;
|
|
9830
10108
|
}
|
|
9831
10109
|
function clampConfidence(n) {
|
|
9832
10110
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -9846,14 +10124,14 @@ function gradedConfidence(edge) {
|
|
|
9846
10124
|
return clampConfidence(confidenceForEdge(edge));
|
|
9847
10125
|
}
|
|
9848
10126
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
10127
|
+
import_types40.EdgeType.CALLS,
|
|
10128
|
+
import_types40.EdgeType.CONNECTS_TO,
|
|
10129
|
+
import_types40.EdgeType.PUBLISHES_TO,
|
|
10130
|
+
import_types40.EdgeType.CONSUMES_FROM
|
|
9853
10131
|
]);
|
|
9854
10132
|
function detectMissingDivergences(graph, bucket) {
|
|
9855
10133
|
const out = [];
|
|
9856
|
-
if (bucket.type ===
|
|
10134
|
+
if (bucket.type === import_types40.EdgeType.CONTAINS) return out;
|
|
9857
10135
|
if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
|
|
9858
10136
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
9859
10137
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
@@ -9895,7 +10173,7 @@ function declaredHostFor(svc) {
|
|
|
9895
10173
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
9896
10174
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
9897
10175
|
const e = graph.getEdgeAttributes(edgeId);
|
|
9898
|
-
if (e.type ===
|
|
10176
|
+
if (e.type === import_types40.EdgeType.CONFIGURED_BY && e.provenance === import_types40.Provenance.EXTRACTED) {
|
|
9899
10177
|
return true;
|
|
9900
10178
|
}
|
|
9901
10179
|
}
|
|
@@ -9908,10 +10186,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
9908
10186
|
const out = [];
|
|
9909
10187
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
9910
10188
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9911
|
-
if (edge.type !==
|
|
9912
|
-
if (edge.provenance !==
|
|
10189
|
+
if (edge.type !== import_types40.EdgeType.CONNECTS_TO) continue;
|
|
10190
|
+
if (edge.provenance !== import_types40.Provenance.OBSERVED) continue;
|
|
9913
10191
|
const target = graph.getNodeAttributes(edge.target);
|
|
9914
|
-
if (target.type !==
|
|
10192
|
+
if (target.type !== import_types40.NodeType.DatabaseNode) continue;
|
|
9915
10193
|
const observedHost = target.host?.trim();
|
|
9916
10194
|
if (!observedHost) continue;
|
|
9917
10195
|
if (observedHost === declaredHost) continue;
|
|
@@ -9933,10 +10211,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
9933
10211
|
const deps = svc.dependencies ?? {};
|
|
9934
10212
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
9935
10213
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9936
|
-
if (edge.type !==
|
|
9937
|
-
if (edge.provenance !==
|
|
10214
|
+
if (edge.type !== import_types40.EdgeType.CONNECTS_TO) continue;
|
|
10215
|
+
if (edge.provenance !== import_types40.Provenance.OBSERVED) continue;
|
|
9938
10216
|
const target = graph.getNodeAttributes(edge.target);
|
|
9939
|
-
if (target.type !==
|
|
10217
|
+
if (target.type !== import_types40.NodeType.DatabaseNode) continue;
|
|
9940
10218
|
for (const pair of compatPairs()) {
|
|
9941
10219
|
if (pair.engine !== target.engine) continue;
|
|
9942
10220
|
const declared = deps[pair.driver];
|
|
@@ -9986,6 +10264,44 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
9986
10264
|
}
|
|
9987
10265
|
return out;
|
|
9988
10266
|
}
|
|
10267
|
+
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.";
|
|
10268
|
+
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.";
|
|
10269
|
+
function detectColumnDrift(node) {
|
|
10270
|
+
const columns = node.columns;
|
|
10271
|
+
if (!columns || columns.length === 0) return [];
|
|
10272
|
+
const anyDeclared = columns.some(columnIsDeclared);
|
|
10273
|
+
const anyObserved = columns.some(columnIsObserved);
|
|
10274
|
+
if (!anyDeclared || !anyObserved) return [];
|
|
10275
|
+
const out = [];
|
|
10276
|
+
for (const col of columns) {
|
|
10277
|
+
const declared = columnIsDeclared(col);
|
|
10278
|
+
const observed = columnIsObserved(col);
|
|
10279
|
+
if (declared && !observed) {
|
|
10280
|
+
out.push({
|
|
10281
|
+
type: "missing-observed",
|
|
10282
|
+
source: node.id,
|
|
10283
|
+
target: node.id,
|
|
10284
|
+
table: node.id,
|
|
10285
|
+
column: col.name,
|
|
10286
|
+
confidence: clampConfidence(col.confidence),
|
|
10287
|
+
reason: `Schema declares column ${node.name}.${col.name} but no production statement has touched it.`,
|
|
10288
|
+
recommendation: RECOMMENDATION_COLUMN_MISSING_OBSERVED
|
|
10289
|
+
});
|
|
10290
|
+
} else if (observed && !declared) {
|
|
10291
|
+
out.push({
|
|
10292
|
+
type: "missing-extracted",
|
|
10293
|
+
source: node.id,
|
|
10294
|
+
target: node.id,
|
|
10295
|
+
table: node.id,
|
|
10296
|
+
column: col.name,
|
|
10297
|
+
confidence: clampConfidence(col.confidence),
|
|
10298
|
+
reason: `Production touched column ${node.name}.${col.name} but the schema does not declare it.`,
|
|
10299
|
+
recommendation: RECOMMENDATION_COLUMN_MISSING_EXTRACTED
|
|
10300
|
+
});
|
|
10301
|
+
}
|
|
10302
|
+
}
|
|
10303
|
+
return out;
|
|
10304
|
+
}
|
|
9989
10305
|
function involvesNode(d, nodeId) {
|
|
9990
10306
|
return d.source === nodeId || d.target === nodeId;
|
|
9991
10307
|
}
|
|
@@ -9995,7 +10311,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
9995
10311
|
for (const d of all) {
|
|
9996
10312
|
if (d.type !== "host-mismatch") continue;
|
|
9997
10313
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
9998
|
-
declaredHalf.add((0,
|
|
10314
|
+
declaredHalf.add((0, import_types40.databaseId)(d.extractedHost));
|
|
9999
10315
|
}
|
|
10000
10316
|
if (observedHalf.size === 0) return all;
|
|
10001
10317
|
return all.filter((d) => {
|
|
@@ -10014,10 +10330,15 @@ function computeDivergences(graph, opts = {}) {
|
|
|
10014
10330
|
}
|
|
10015
10331
|
graph.forEachNode((nodeId, attrs) => {
|
|
10016
10332
|
const n = attrs;
|
|
10017
|
-
if (n.type
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10333
|
+
if (n.type === import_types40.NodeType.ServiceNode) {
|
|
10334
|
+
const svc = n;
|
|
10335
|
+
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
10336
|
+
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
10337
|
+
return;
|
|
10338
|
+
}
|
|
10339
|
+
if (n.type === import_types40.NodeType.InfraNode && n.kind === "sql-table") {
|
|
10340
|
+
for (const d of detectColumnDrift(n)) all.push(d);
|
|
10341
|
+
}
|
|
10021
10342
|
});
|
|
10022
10343
|
const reconciled = suppressHostMismatchHalves(all);
|
|
10023
10344
|
let filtered = reconciled;
|
|
@@ -10046,9 +10367,12 @@ function computeDivergences(graph, opts = {}) {
|
|
|
10046
10367
|
if (lead !== 0) return lead;
|
|
10047
10368
|
if (a.type !== b.type) return a.type.localeCompare(b.type);
|
|
10048
10369
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
10049
|
-
return a.target.localeCompare(b.target);
|
|
10370
|
+
if (a.target !== b.target) return a.target.localeCompare(b.target);
|
|
10371
|
+
const ac = "column" in a && a.column ? a.column : "";
|
|
10372
|
+
const bc = "column" in b && b.column ? b.column : "";
|
|
10373
|
+
return ac.localeCompare(bc);
|
|
10050
10374
|
});
|
|
10051
|
-
return
|
|
10375
|
+
return import_types40.DivergenceResultSchema.parse({
|
|
10052
10376
|
divergences: filtered,
|
|
10053
10377
|
totalAffected: filtered.length,
|
|
10054
10378
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -10181,26 +10505,26 @@ function canonicalJson(value) {
|
|
|
10181
10505
|
init_cjs_shims();
|
|
10182
10506
|
var import_node_fs29 = require("fs");
|
|
10183
10507
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
10184
|
-
var
|
|
10185
|
-
var
|
|
10508
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
10509
|
+
var import_types41 = require("@neat.is/types");
|
|
10186
10510
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
10187
10511
|
var LOCK_RETRY_MS = 50;
|
|
10188
10512
|
function neatHome() {
|
|
10189
10513
|
const override = process.env.NEAT_HOME;
|
|
10190
|
-
if (override && override.length > 0) return
|
|
10191
|
-
return
|
|
10514
|
+
if (override && override.length > 0) return import_node_path55.default.resolve(override);
|
|
10515
|
+
return import_node_path55.default.join(import_node_os3.default.homedir(), ".neat");
|
|
10192
10516
|
}
|
|
10193
10517
|
function registryPath() {
|
|
10194
|
-
return
|
|
10518
|
+
return import_node_path55.default.join(neatHome(), "projects.json");
|
|
10195
10519
|
}
|
|
10196
10520
|
function registryLockPath() {
|
|
10197
|
-
return
|
|
10521
|
+
return import_node_path55.default.join(neatHome(), "projects.json.lock");
|
|
10198
10522
|
}
|
|
10199
10523
|
function daemonPidPath() {
|
|
10200
|
-
return
|
|
10524
|
+
return import_node_path55.default.join(neatHome(), "neatd.pid");
|
|
10201
10525
|
}
|
|
10202
10526
|
function daemonsDir() {
|
|
10203
|
-
return
|
|
10527
|
+
return import_node_path55.default.join(neatHome(), "daemons");
|
|
10204
10528
|
}
|
|
10205
10529
|
function isFiniteInt(v) {
|
|
10206
10530
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -10241,7 +10565,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
10241
10565
|
const out = [];
|
|
10242
10566
|
for (const name of names) {
|
|
10243
10567
|
if (!name.endsWith(".json")) continue;
|
|
10244
|
-
const file =
|
|
10568
|
+
const file = import_node_path55.default.join(dir, name);
|
|
10245
10569
|
let raw;
|
|
10246
10570
|
try {
|
|
10247
10571
|
raw = await import_node_fs29.promises.readFile(file, "utf8");
|
|
@@ -10318,7 +10642,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
10318
10642
|
}
|
|
10319
10643
|
}
|
|
10320
10644
|
async function writeAtomically(target, contents) {
|
|
10321
|
-
await import_node_fs29.promises.mkdir(
|
|
10645
|
+
await import_node_fs29.promises.mkdir(import_node_path55.default.dirname(target), { recursive: true });
|
|
10322
10646
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
10323
10647
|
const fd = await import_node_fs29.promises.open(tmp, "w");
|
|
10324
10648
|
try {
|
|
@@ -10331,7 +10655,7 @@ async function writeAtomically(target, contents) {
|
|
|
10331
10655
|
}
|
|
10332
10656
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
10333
10657
|
const deadline = Date.now() + timeoutMs;
|
|
10334
|
-
await import_node_fs29.promises.mkdir(
|
|
10658
|
+
await import_node_fs29.promises.mkdir(import_node_path55.default.dirname(lockPath), { recursive: true });
|
|
10335
10659
|
let probedHolder = false;
|
|
10336
10660
|
while (true) {
|
|
10337
10661
|
try {
|
|
@@ -10384,10 +10708,10 @@ async function readRegistry() {
|
|
|
10384
10708
|
throw err;
|
|
10385
10709
|
}
|
|
10386
10710
|
const parsed = JSON.parse(raw);
|
|
10387
|
-
return
|
|
10711
|
+
return import_types41.RegistryFileSchema.parse(parsed);
|
|
10388
10712
|
}
|
|
10389
10713
|
async function writeRegistry(reg) {
|
|
10390
|
-
const validated =
|
|
10714
|
+
const validated = import_types41.RegistryFileSchema.parse(reg);
|
|
10391
10715
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
10392
10716
|
}
|
|
10393
10717
|
async function getProject(name) {
|
|
@@ -10532,7 +10856,7 @@ init_auth();
|
|
|
10532
10856
|
// src/connectors-config.ts
|
|
10533
10857
|
init_cjs_shims();
|
|
10534
10858
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
10535
|
-
var
|
|
10859
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
10536
10860
|
var import_node_fs30 = require("fs");
|
|
10537
10861
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
10538
10862
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -10547,11 +10871,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
10547
10871
|
};
|
|
10548
10872
|
function neatHome2() {
|
|
10549
10873
|
const override = process.env.NEAT_HOME;
|
|
10550
|
-
if (override && override.length > 0) return
|
|
10551
|
-
return
|
|
10874
|
+
if (override && override.length > 0) return import_node_path56.default.resolve(override);
|
|
10875
|
+
return import_node_path56.default.join(import_node_os4.default.homedir(), ".neat");
|
|
10552
10876
|
}
|
|
10553
10877
|
function connectorsConfigPath(home = neatHome2()) {
|
|
10554
|
-
return
|
|
10878
|
+
return import_node_path56.default.join(home, "connectors.json");
|
|
10555
10879
|
}
|
|
10556
10880
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
10557
10881
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -10738,15 +11062,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
10738
11062
|
|
|
10739
11063
|
// src/connectors/index.ts
|
|
10740
11064
|
init_cjs_shims();
|
|
10741
|
-
var
|
|
11065
|
+
var import_types42 = require("@neat.is/types");
|
|
10742
11066
|
var NO_ENV = "unknown";
|
|
10743
11067
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
10744
11068
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10745
11069
|
const sites = [];
|
|
10746
11070
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
10747
11071
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
10748
|
-
if (edge.provenance !==
|
|
10749
|
-
const parsed = (0,
|
|
11072
|
+
if (edge.provenance !== import_types42.Provenance.EXTRACTED) continue;
|
|
11073
|
+
const parsed = (0, import_types42.parseFileId)(edge.source);
|
|
10750
11074
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
10751
11075
|
const site = { relPath: edge.evidence.file };
|
|
10752
11076
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -10757,7 +11081,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
10757
11081
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
10758
11082
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10759
11083
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
10760
|
-
if (attrs.type !==
|
|
11084
|
+
if (attrs.type !== import_types42.NodeType.RouteNode || !attrs.path) return void 0;
|
|
10761
11085
|
const site = { relPath: attrs.path };
|
|
10762
11086
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
10763
11087
|
return site;
|
|
@@ -10777,6 +11101,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
10777
11101
|
const { kind, name, provider } = resolved.ensureInfraNode;
|
|
10778
11102
|
ensureInfraNode(graph, kind, name, provider);
|
|
10779
11103
|
}
|
|
11104
|
+
mergeObservedColumns(graph, resolved.targetNodeId, signal.columns);
|
|
10780
11105
|
const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
10781
11106
|
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
|
|
10782
11107
|
const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
|
|
@@ -11207,6 +11532,7 @@ async function fetchSupabaseEdgeLogs(config, token, startIso, endIso, fetchImpl
|
|
|
11207
11532
|
|
|
11208
11533
|
// src/connectors/supabase/map.ts
|
|
11209
11534
|
init_cjs_shims();
|
|
11535
|
+
init_otel();
|
|
11210
11536
|
|
|
11211
11537
|
// src/connectors/supabase/types.ts
|
|
11212
11538
|
init_cjs_shims();
|
|
@@ -11232,10 +11558,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
11232
11558
|
// src/connectors/supabase/map.ts
|
|
11233
11559
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
11234
11560
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
11235
|
-
function targetFromRestPath(
|
|
11236
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
11561
|
+
function targetFromRestPath(path61) {
|
|
11562
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path61);
|
|
11237
11563
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
11238
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
11564
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path61);
|
|
11239
11565
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
11240
11566
|
return null;
|
|
11241
11567
|
}
|
|
@@ -11299,12 +11625,14 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
|
|
|
11299
11625
|
if (delta <= 0) continue;
|
|
11300
11626
|
const table = tableNameFromQueryText(row.query);
|
|
11301
11627
|
if (!table) continue;
|
|
11628
|
+
const columns = columnsFromSqlStatement(row.query);
|
|
11302
11629
|
signals.push({
|
|
11303
11630
|
targetKind: SUPABASE_TABLE_TARGET_KIND,
|
|
11304
11631
|
targetName: table,
|
|
11305
11632
|
callCount: delta,
|
|
11306
11633
|
errorCount: 0,
|
|
11307
|
-
lastObservedIso: nowIso2
|
|
11634
|
+
lastObservedIso: nowIso2,
|
|
11635
|
+
...columns.length > 0 ? { columns } : {}
|
|
11308
11636
|
});
|
|
11309
11637
|
}
|
|
11310
11638
|
for (const queryid of [...previous.keys()]) {
|
|
@@ -11344,23 +11672,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
11344
11672
|
|
|
11345
11673
|
// src/connectors/supabase/resolve.ts
|
|
11346
11674
|
init_cjs_shims();
|
|
11347
|
-
var
|
|
11675
|
+
var import_types44 = require("@neat.is/types");
|
|
11348
11676
|
function createSupabaseResolveTarget(graph, config) {
|
|
11349
11677
|
return (signal, _ctx) => {
|
|
11350
11678
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
11351
11679
|
return null;
|
|
11352
11680
|
}
|
|
11353
|
-
const subResourceId = (0,
|
|
11681
|
+
const subResourceId = (0, import_types44.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
11354
11682
|
if (graph.hasNode(subResourceId)) {
|
|
11355
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
11683
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
|
|
11356
11684
|
}
|
|
11357
|
-
const bareResourceId = (0,
|
|
11685
|
+
const bareResourceId = (0, import_types44.infraId)(signal.targetKind, signal.targetName);
|
|
11358
11686
|
if (graph.hasNode(bareResourceId)) {
|
|
11359
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
11687
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
|
|
11360
11688
|
}
|
|
11361
|
-
const projectLevelId = (0,
|
|
11689
|
+
const projectLevelId = (0, import_types44.infraId)("supabase", config.nodeRef);
|
|
11362
11690
|
if (graph.hasNode(projectLevelId)) {
|
|
11363
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
11691
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types44.EdgeType.CALLS };
|
|
11364
11692
|
}
|
|
11365
11693
|
return null;
|
|
11366
11694
|
};
|
|
@@ -11453,7 +11781,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
11453
11781
|
|
|
11454
11782
|
// src/connectors/railway/index.ts
|
|
11455
11783
|
init_cjs_shims();
|
|
11456
|
-
var
|
|
11784
|
+
var import_types48 = require("@neat.is/types");
|
|
11457
11785
|
|
|
11458
11786
|
// src/connectors/railway/client.ts
|
|
11459
11787
|
init_cjs_shims();
|
|
@@ -11604,7 +11932,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
11604
11932
|
const out = [];
|
|
11605
11933
|
graph.forEachNode((_id, attrs) => {
|
|
11606
11934
|
const node = attrs;
|
|
11607
|
-
if (node.type !==
|
|
11935
|
+
if (node.type !== import_types48.NodeType.RouteNode) return;
|
|
11608
11936
|
const route = attrs;
|
|
11609
11937
|
if (route.service !== serviceName) return;
|
|
11610
11938
|
out.push({
|
|
@@ -11708,12 +12036,12 @@ function createRailwayResolveTarget(config) {
|
|
|
11708
12036
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
11709
12037
|
if (!serviceName) return null;
|
|
11710
12038
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
11711
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
12039
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types48.EdgeType.CALLS };
|
|
11712
12040
|
}
|
|
11713
12041
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
11714
12042
|
const peerName = config.serviceNameById[signal.targetName];
|
|
11715
12043
|
if (!peerName) return null;
|
|
11716
|
-
return { targetNodeId: (0,
|
|
12044
|
+
return { targetNodeId: (0, import_types48.serviceId)(peerName), serviceName, edgeType: import_types48.EdgeType.CONNECTS_TO };
|
|
11717
12045
|
}
|
|
11718
12046
|
return null;
|
|
11719
12047
|
};
|
|
@@ -11837,9 +12165,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11837
12165
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11838
12166
|
if (secondSep === -1) return null;
|
|
11839
12167
|
const method = rest.slice(0, secondSep);
|
|
11840
|
-
const
|
|
11841
|
-
if (!resourceName || !method || !
|
|
11842
|
-
return { resourceName, method, path:
|
|
12168
|
+
const path61 = rest.slice(secondSep + 1);
|
|
12169
|
+
if (!resourceName || !method || !path61) return null;
|
|
12170
|
+
return { resourceName, method, path: path61 };
|
|
11843
12171
|
}
|
|
11844
12172
|
function resourceNameFor(type, labels) {
|
|
11845
12173
|
if (!labels) return null;
|
|
@@ -11877,14 +12205,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
11877
12205
|
if (!req2) return null;
|
|
11878
12206
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
11879
12207
|
const method = req2.requestMethod.toUpperCase();
|
|
11880
|
-
const
|
|
11881
|
-
if (
|
|
12208
|
+
const path61 = pathFromRequestUrl(req2.requestUrl);
|
|
12209
|
+
if (path61 === null) return null;
|
|
11882
12210
|
const timestamp = entry2.timestamp;
|
|
11883
12211
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11884
12212
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
11885
12213
|
return {
|
|
11886
12214
|
targetKind: resourceType,
|
|
11887
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
12215
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path61 }),
|
|
11888
12216
|
callCount: 1,
|
|
11889
12217
|
errorCount: isError ? 1 : 0,
|
|
11890
12218
|
lastObservedIso: timestamp
|
|
@@ -11901,7 +12229,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11901
12229
|
|
|
11902
12230
|
// src/connectors/firebase/resolve.ts
|
|
11903
12231
|
init_cjs_shims();
|
|
11904
|
-
var
|
|
12232
|
+
var import_types49 = require("@neat.is/types");
|
|
11905
12233
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11906
12234
|
switch (resourceType) {
|
|
11907
12235
|
case "cloud_function":
|
|
@@ -11916,7 +12244,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11916
12244
|
const entries = [];
|
|
11917
12245
|
graph.forEachNode((_id, attrs) => {
|
|
11918
12246
|
const node = attrs;
|
|
11919
|
-
if (node.type !==
|
|
12247
|
+
if (node.type !== import_types49.NodeType.RouteNode) return;
|
|
11920
12248
|
const route = attrs;
|
|
11921
12249
|
if (route.service !== serviceName) return;
|
|
11922
12250
|
entries.push({
|
|
@@ -11948,7 +12276,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11948
12276
|
return {
|
|
11949
12277
|
targetNodeId: match.routeNodeId,
|
|
11950
12278
|
serviceName,
|
|
11951
|
-
edgeType:
|
|
12279
|
+
edgeType: import_types49.EdgeType.CALLS
|
|
11952
12280
|
};
|
|
11953
12281
|
};
|
|
11954
12282
|
}
|
|
@@ -11975,7 +12303,7 @@ init_cjs_shims();
|
|
|
11975
12303
|
|
|
11976
12304
|
// src/connectors/cloudflare/connector.ts
|
|
11977
12305
|
init_cjs_shims();
|
|
11978
|
-
var
|
|
12306
|
+
var import_types51 = require("@neat.is/types");
|
|
11979
12307
|
|
|
11980
12308
|
// src/connectors/cloudflare/client.ts
|
|
11981
12309
|
init_cjs_shims();
|
|
@@ -12091,7 +12419,7 @@ function mapEventToSignal(event) {
|
|
|
12091
12419
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
12092
12420
|
const statusCode = metadata?.statusCode;
|
|
12093
12421
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
12094
|
-
const
|
|
12422
|
+
const path61 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
12095
12423
|
return {
|
|
12096
12424
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
12097
12425
|
targetName: scriptName,
|
|
@@ -12099,7 +12427,7 @@ function mapEventToSignal(event) {
|
|
|
12099
12427
|
errorCount: isError ? 1 : 0,
|
|
12100
12428
|
lastObservedIso: observedAt.toISOString(),
|
|
12101
12429
|
method,
|
|
12102
|
-
...
|
|
12430
|
+
...path61 ? { path: path61 } : {},
|
|
12103
12431
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
12104
12432
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
12105
12433
|
};
|
|
@@ -12139,19 +12467,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
12139
12467
|
graph.forEachNode((id, attrs) => {
|
|
12140
12468
|
if (found) return;
|
|
12141
12469
|
const a = attrs;
|
|
12142
|
-
if (a.type ===
|
|
12470
|
+
if (a.type === import_types51.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
12143
12471
|
found = id;
|
|
12144
12472
|
}
|
|
12145
12473
|
});
|
|
12146
12474
|
return found;
|
|
12147
12475
|
}
|
|
12148
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
12149
|
-
const normalizedPath = normalizePathTemplate(
|
|
12476
|
+
function findMatchingRouteNode(graph, serviceName, method, path61) {
|
|
12477
|
+
const normalizedPath = normalizePathTemplate(path61);
|
|
12150
12478
|
let found = null;
|
|
12151
12479
|
graph.forEachNode((id, attrs) => {
|
|
12152
12480
|
if (found) return;
|
|
12153
12481
|
const a = attrs;
|
|
12154
|
-
if (a.type !==
|
|
12482
|
+
if (a.type !== import_types51.NodeType.RouteNode || a.service !== serviceName) return;
|
|
12155
12483
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
12156
12484
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
12157
12485
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -12163,18 +12491,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
12163
12491
|
return (signal) => {
|
|
12164
12492
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
12165
12493
|
const scriptName = signal.targetName;
|
|
12166
|
-
const { method, path:
|
|
12494
|
+
const { method, path: path61 } = signal;
|
|
12167
12495
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
12168
|
-
if (!method || !
|
|
12169
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
12496
|
+
if (!method || !path61) return wholeFileId;
|
|
12497
|
+
return findMatchingRouteNode(graph, serviceName, method, path61) ?? wholeFileId;
|
|
12170
12498
|
};
|
|
12171
12499
|
const mapping = config.workers?.[scriptName];
|
|
12172
12500
|
if (mapping) {
|
|
12173
|
-
const wholeFileId = (0,
|
|
12501
|
+
const wholeFileId = (0, import_types51.fileId)(mapping.service, mapping.entryFile);
|
|
12174
12502
|
return {
|
|
12175
12503
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
12176
12504
|
serviceName: mapping.service,
|
|
12177
|
-
edgeType:
|
|
12505
|
+
edgeType: import_types51.EdgeType.CALLS
|
|
12178
12506
|
};
|
|
12179
12507
|
}
|
|
12180
12508
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -12183,13 +12511,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
12183
12511
|
return {
|
|
12184
12512
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
12185
12513
|
serviceName: fileNode.service,
|
|
12186
|
-
edgeType:
|
|
12514
|
+
edgeType: import_types51.EdgeType.CALLS
|
|
12187
12515
|
};
|
|
12188
12516
|
}
|
|
12189
12517
|
return {
|
|
12190
|
-
targetNodeId: (0,
|
|
12518
|
+
targetNodeId: (0, import_types51.infraId)("cloudflare-worker", scriptName),
|
|
12191
12519
|
serviceName: scriptName,
|
|
12192
|
-
edgeType:
|
|
12520
|
+
edgeType: import_types51.EdgeType.CALLS,
|
|
12193
12521
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
12194
12522
|
};
|
|
12195
12523
|
};
|
|
@@ -12367,12 +12695,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
12367
12695
|
if (!prior || calls <= prior.calls) continue;
|
|
12368
12696
|
const table = tableFromSqlStatement(row.query);
|
|
12369
12697
|
if (!table) continue;
|
|
12698
|
+
const columns = columnsFromSqlStatement(row.query);
|
|
12370
12699
|
signals.push({
|
|
12371
12700
|
targetKind: NEON_SQL_TABLE_TARGET_KIND,
|
|
12372
12701
|
targetName: table,
|
|
12373
12702
|
callCount: calls - prior.calls,
|
|
12374
12703
|
errorCount: 0,
|
|
12375
|
-
lastObservedIso: observedAtIso
|
|
12704
|
+
lastObservedIso: observedAtIso,
|
|
12705
|
+
...columns.length > 0 ? { columns } : {}
|
|
12376
12706
|
});
|
|
12377
12707
|
}
|
|
12378
12708
|
for (const queryid of previous.keys()) {
|
|
@@ -12383,14 +12713,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
12383
12713
|
|
|
12384
12714
|
// src/connectors/neon/resolve.ts
|
|
12385
12715
|
init_cjs_shims();
|
|
12386
|
-
var
|
|
12716
|
+
var import_types55 = require("@neat.is/types");
|
|
12387
12717
|
function createNeonResolveTarget(config) {
|
|
12388
12718
|
return (signal) => {
|
|
12389
12719
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
12390
12720
|
return {
|
|
12391
|
-
targetNodeId: (0,
|
|
12721
|
+
targetNodeId: (0, import_types55.infraId)("sql-table", signal.targetName),
|
|
12392
12722
|
serviceName: config.serviceName,
|
|
12393
|
-
edgeType:
|
|
12723
|
+
edgeType: import_types55.EdgeType.CALLS,
|
|
12394
12724
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
12395
12725
|
};
|
|
12396
12726
|
};
|
|
@@ -12954,11 +13284,11 @@ function registerRoutes(scope, ctx) {
|
|
|
12954
13284
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12955
13285
|
const parsed = [];
|
|
12956
13286
|
for (const c of candidates) {
|
|
12957
|
-
const r =
|
|
13287
|
+
const r = import_types58.DivergenceTypeSchema.safeParse(c);
|
|
12958
13288
|
if (!r.success) {
|
|
12959
13289
|
return reply.code(400).send({
|
|
12960
13290
|
error: `unknown divergence type "${c}"`,
|
|
12961
|
-
allowed:
|
|
13291
|
+
allowed: import_types58.DivergenceTypeSchema.options
|
|
12962
13292
|
});
|
|
12963
13293
|
}
|
|
12964
13294
|
parsed.push(r.data);
|
|
@@ -13267,7 +13597,7 @@ function registerRoutes(scope, ctx) {
|
|
|
13267
13597
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
13268
13598
|
let violations = await log.readAll();
|
|
13269
13599
|
if (req2.query.severity) {
|
|
13270
|
-
const sev =
|
|
13600
|
+
const sev = import_types58.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
13271
13601
|
if (!sev.success) {
|
|
13272
13602
|
return reply.code(400).send({
|
|
13273
13603
|
error: "invalid severity",
|
|
@@ -13306,7 +13636,7 @@ function registerRoutes(scope, ctx) {
|
|
|
13306
13636
|
scope.post("/policies/check", async (req2, reply) => {
|
|
13307
13637
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
13308
13638
|
if (!proj) return;
|
|
13309
|
-
const parsed =
|
|
13639
|
+
const parsed = import_types58.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
13310
13640
|
if (!parsed.success) {
|
|
13311
13641
|
return reply.code(400).send({
|
|
13312
13642
|
error: "invalid /policies/check body",
|
|
@@ -13628,7 +13958,7 @@ init_auth();
|
|
|
13628
13958
|
// src/unrouted.ts
|
|
13629
13959
|
init_cjs_shims();
|
|
13630
13960
|
var import_node_fs31 = require("fs");
|
|
13631
|
-
var
|
|
13961
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
13632
13962
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
13633
13963
|
return {
|
|
13634
13964
|
timestamp: now.toISOString(),
|
|
@@ -13638,34 +13968,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
13638
13968
|
};
|
|
13639
13969
|
}
|
|
13640
13970
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
13641
|
-
const target =
|
|
13971
|
+
const target = import_node_path57.default.join(neatHome4, "errors.ndjson");
|
|
13642
13972
|
await import_node_fs31.promises.mkdir(neatHome4, { recursive: true });
|
|
13643
13973
|
await import_node_fs31.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
13644
13974
|
}
|
|
13645
13975
|
function unroutedErrorsPath(neatHome4) {
|
|
13646
|
-
return
|
|
13976
|
+
return import_node_path57.default.join(neatHome4, "errors.ndjson");
|
|
13647
13977
|
}
|
|
13648
13978
|
|
|
13649
13979
|
// src/daemon.ts
|
|
13650
|
-
var
|
|
13980
|
+
var import_types59 = require("@neat.is/types");
|
|
13651
13981
|
function daemonJsonPath(scanPath) {
|
|
13652
|
-
return
|
|
13982
|
+
return import_node_path58.default.join(scanPath, "neat-out", "daemon.json");
|
|
13653
13983
|
}
|
|
13654
13984
|
function daemonsDiscoveryDir(home) {
|
|
13655
13985
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
13656
|
-
return
|
|
13986
|
+
return import_node_path58.default.join(base, "daemons");
|
|
13657
13987
|
}
|
|
13658
13988
|
function daemonDiscoveryPath(project, home) {
|
|
13659
|
-
return
|
|
13989
|
+
return import_node_path58.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
13660
13990
|
}
|
|
13661
13991
|
function sanitizeDiscoveryName(project) {
|
|
13662
13992
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
13663
13993
|
}
|
|
13664
13994
|
function neatHomeFromEnv() {
|
|
13665
13995
|
const env = process.env.NEAT_HOME;
|
|
13666
|
-
if (env && env.length > 0) return
|
|
13996
|
+
if (env && env.length > 0) return import_node_path58.default.resolve(env);
|
|
13667
13997
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
13668
|
-
return
|
|
13998
|
+
return import_node_path58.default.join(home, ".neat");
|
|
13669
13999
|
}
|
|
13670
14000
|
function resolveNeatVersion() {
|
|
13671
14001
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -13734,11 +14064,11 @@ function teardownSlot(slot) {
|
|
|
13734
14064
|
}
|
|
13735
14065
|
}
|
|
13736
14066
|
function neatHomeFor(opts) {
|
|
13737
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
14067
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path58.default.resolve(opts.neatHome);
|
|
13738
14068
|
const env = process.env.NEAT_HOME;
|
|
13739
|
-
if (env && env.length > 0) return
|
|
14069
|
+
if (env && env.length > 0) return import_node_path58.default.resolve(env);
|
|
13740
14070
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
13741
|
-
return
|
|
14071
|
+
return import_node_path58.default.join(home, ".neat");
|
|
13742
14072
|
}
|
|
13743
14073
|
function routeSpanToProject(serviceName, projects) {
|
|
13744
14074
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -13786,11 +14116,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
13786
14116
|
if (!serviceName) return true;
|
|
13787
14117
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
13788
14118
|
return graph.someNode(
|
|
13789
|
-
(_id, attrs) => attrs.type ===
|
|
14119
|
+
(_id, attrs) => attrs.type === import_types59.NodeType.ServiceNode && attrs.name === serviceName
|
|
13790
14120
|
);
|
|
13791
14121
|
}
|
|
13792
14122
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
13793
|
-
const paths = pathsForProject(entry2.name,
|
|
14123
|
+
const paths = pathsForProject(entry2.name, import_node_path58.default.join(entry2.path, "neat-out"));
|
|
13794
14124
|
try {
|
|
13795
14125
|
const stat = await import_node_fs32.promises.stat(entry2.path);
|
|
13796
14126
|
if (!stat.isDirectory()) {
|
|
@@ -13906,7 +14236,7 @@ async function startDaemon(opts = {}) {
|
|
|
13906
14236
|
const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
|
|
13907
14237
|
const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
|
|
13908
14238
|
const singleProject = projectArg;
|
|
13909
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
14239
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path58.default.resolve(projectPathArg) : null;
|
|
13910
14240
|
if (singleProject && !singleProjectPath) {
|
|
13911
14241
|
throw new Error(
|
|
13912
14242
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -13921,7 +14251,7 @@ async function startDaemon(opts = {}) {
|
|
|
13921
14251
|
);
|
|
13922
14252
|
}
|
|
13923
14253
|
}
|
|
13924
|
-
const pidPath =
|
|
14254
|
+
const pidPath = import_node_path58.default.join(home, "neatd.pid");
|
|
13925
14255
|
await writeAtomically(pidPath, `${process.pid}
|
|
13926
14256
|
`);
|
|
13927
14257
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -14333,8 +14663,8 @@ async function startDaemon(opts = {}) {
|
|
|
14333
14663
|
let registryWatcher = null;
|
|
14334
14664
|
let reloadTimer = null;
|
|
14335
14665
|
if (!singleProject) try {
|
|
14336
|
-
const regDir =
|
|
14337
|
-
const regBase =
|
|
14666
|
+
const regDir = import_node_path58.default.dirname(regPath);
|
|
14667
|
+
const regBase = import_node_path58.default.basename(regPath);
|
|
14338
14668
|
registryWatcher = (0, import_node_fs32.watch)(regDir, (_eventType, filename) => {
|
|
14339
14669
|
if (filename !== null && filename !== regBase) return;
|
|
14340
14670
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
@@ -14409,7 +14739,7 @@ init_cjs_shims();
|
|
|
14409
14739
|
var import_node_child_process2 = require("child_process");
|
|
14410
14740
|
var import_node_fs33 = require("fs");
|
|
14411
14741
|
var import_node_net = __toESM(require("net"), 1);
|
|
14412
|
-
var
|
|
14742
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
14413
14743
|
var DEFAULT_WEB_PORT = 6328;
|
|
14414
14744
|
var DEFAULT_REST_PORT = 8080;
|
|
14415
14745
|
function asValidPort(value) {
|
|
@@ -14418,11 +14748,11 @@ function asValidPort(value) {
|
|
|
14418
14748
|
}
|
|
14419
14749
|
function projectRoot() {
|
|
14420
14750
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
14421
|
-
return
|
|
14751
|
+
return import_node_path59.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
14422
14752
|
}
|
|
14423
14753
|
async function readDaemonPorts(root) {
|
|
14424
14754
|
try {
|
|
14425
|
-
const raw = await import_node_fs33.promises.readFile(
|
|
14755
|
+
const raw = await import_node_fs33.promises.readFile(import_node_path59.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
14426
14756
|
const parsed = JSON.parse(raw);
|
|
14427
14757
|
const ports = parsed?.ports ?? {};
|
|
14428
14758
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -14467,10 +14797,10 @@ function resolveWebPackageDir() {
|
|
|
14467
14797
|
eval("require")
|
|
14468
14798
|
);
|
|
14469
14799
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
14470
|
-
return
|
|
14800
|
+
return import_node_path59.default.dirname(pkgJsonPath);
|
|
14471
14801
|
}
|
|
14472
14802
|
function resolveStandaloneServerEntry(webDir) {
|
|
14473
|
-
return
|
|
14803
|
+
return import_node_path59.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
14474
14804
|
}
|
|
14475
14805
|
async function pickInternalPort() {
|
|
14476
14806
|
return new Promise((resolve, reject) => {
|
|
@@ -14523,7 +14853,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
14523
14853
|
NEAT_API_URL: apiUrl
|
|
14524
14854
|
};
|
|
14525
14855
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
14526
|
-
cwd:
|
|
14856
|
+
cwd: import_node_path59.default.dirname(serverEntry),
|
|
14527
14857
|
env,
|
|
14528
14858
|
stdio: ["ignore", "inherit", "inherit"],
|
|
14529
14859
|
detached: false
|
|
@@ -14699,14 +15029,14 @@ function localVersion() {
|
|
|
14699
15029
|
}
|
|
14700
15030
|
function neatHome3() {
|
|
14701
15031
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
14702
|
-
return
|
|
15032
|
+
return import_node_path60.default.resolve(process.env.NEAT_HOME);
|
|
14703
15033
|
}
|
|
14704
15034
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
14705
|
-
return
|
|
15035
|
+
return import_node_path60.default.join(home, ".neat");
|
|
14706
15036
|
}
|
|
14707
15037
|
async function readPid() {
|
|
14708
15038
|
try {
|
|
14709
|
-
const raw = await import_node_fs34.promises.readFile(
|
|
15039
|
+
const raw = await import_node_fs34.promises.readFile(import_node_path60.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
14710
15040
|
const n = Number.parseInt(raw.trim(), 10);
|
|
14711
15041
|
return Number.isFinite(n) ? n : null;
|
|
14712
15042
|
} catch {
|