@neat.is/core 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-33ZZ2ZID.js → chunk-BUB3ASD5.js} +94 -83
- package/dist/chunk-BUB3ASD5.js.map +1 -0
- package/dist/{chunk-T3A2GK3X.js → chunk-KCEZSFU2.js} +2 -2
- package/dist/cli.cjs +94 -80
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +84 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +84 -70
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +57 -45
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-33ZZ2ZID.js.map +0 -1
- /package/dist/{chunk-T3A2GK3X.js.map → chunk-KCEZSFU2.js.map} +0 -0
|
@@ -339,17 +339,21 @@ import {
|
|
|
339
339
|
BlastRadiusResultSchema,
|
|
340
340
|
NodeType,
|
|
341
341
|
PROV_RANK,
|
|
342
|
-
Provenance,
|
|
343
342
|
RootCauseResultSchema,
|
|
344
343
|
TransitiveDependenciesResultSchema
|
|
345
344
|
} from "@neat.is/types";
|
|
346
345
|
var ROOT_CAUSE_MAX_DEPTH = 5;
|
|
347
346
|
var BLAST_RADIUS_DEFAULT_DEPTH = 10;
|
|
347
|
+
function isFrontierNode(graph, nodeId) {
|
|
348
|
+
if (!graph.hasNode(nodeId)) return false;
|
|
349
|
+
const attrs = graph.getNodeAttributes(nodeId);
|
|
350
|
+
return attrs.type === NodeType.FrontierNode;
|
|
351
|
+
}
|
|
348
352
|
function bestEdgeBySource(graph, edgeIds) {
|
|
349
353
|
const best = /* @__PURE__ */ new Map();
|
|
350
354
|
for (const id of edgeIds) {
|
|
351
355
|
const e = graph.getEdgeAttributes(id);
|
|
352
|
-
if (e.
|
|
356
|
+
if (isFrontierNode(graph, e.source)) continue;
|
|
353
357
|
const cur = best.get(e.source);
|
|
354
358
|
if (!cur || PROV_RANK[e.provenance] > PROV_RANK[cur.provenance]) {
|
|
355
359
|
best.set(e.source, e);
|
|
@@ -361,7 +365,7 @@ function bestEdgeByTarget(graph, edgeIds) {
|
|
|
361
365
|
const best = /* @__PURE__ */ new Map();
|
|
362
366
|
for (const id of edgeIds) {
|
|
363
367
|
const e = graph.getEdgeAttributes(id);
|
|
364
|
-
if (e.
|
|
368
|
+
if (isFrontierNode(graph, e.target)) continue;
|
|
365
369
|
const cur = best.get(e.target);
|
|
366
370
|
if (!cur || PROV_RANK[e.provenance] > PROV_RANK[cur.provenance]) {
|
|
367
371
|
best.set(e.target, e);
|
|
@@ -373,8 +377,7 @@ var PROVENANCE_CEILING = {
|
|
|
373
377
|
OBSERVED: 1,
|
|
374
378
|
INFERRED: 0.7,
|
|
375
379
|
EXTRACTED: 0.5,
|
|
376
|
-
STALE: 0.3
|
|
377
|
-
FRONTIER: 0.3
|
|
380
|
+
STALE: 0.3
|
|
378
381
|
};
|
|
379
382
|
function volumeWeight(spanCount) {
|
|
380
383
|
if (!spanCount || spanCount <= 0) return 0.5;
|
|
@@ -631,8 +634,7 @@ import path2 from "path";
|
|
|
631
634
|
import {
|
|
632
635
|
EdgeType,
|
|
633
636
|
NodeType as NodeType2,
|
|
634
|
-
PolicyFileSchema
|
|
635
|
-
Provenance as Provenance2
|
|
637
|
+
PolicyFileSchema
|
|
636
638
|
} from "@neat.is/types";
|
|
637
639
|
|
|
638
640
|
// src/events.ts
|
|
@@ -733,8 +735,8 @@ var evaluateStructural = ({
|
|
|
733
735
|
for (const edgeId of graph.outboundEdges(id)) {
|
|
734
736
|
const e = graph.getEdgeAttributes(edgeId);
|
|
735
737
|
if (e.type !== rule.edgeType) continue;
|
|
736
|
-
if (e.provenance === Provenance2.FRONTIER) continue;
|
|
737
738
|
const target = graph.getNodeAttributes(e.target);
|
|
739
|
+
if (target.type === NodeType2.FrontierNode) continue;
|
|
738
740
|
if (target.type === rule.toNodeType) {
|
|
739
741
|
satisfied = true;
|
|
740
742
|
break;
|
|
@@ -853,8 +855,8 @@ var evaluateCompatibility = ({
|
|
|
853
855
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
854
856
|
const e = graph.getEdgeAttributes(edgeId);
|
|
855
857
|
if (e.type !== EdgeType.CONNECTS_TO) continue;
|
|
856
|
-
if (e.provenance === Provenance2.FRONTIER) continue;
|
|
857
858
|
const dbAttrs = graph.getNodeAttributes(e.target);
|
|
859
|
+
if (dbAttrs.type === NodeType2.FrontierNode) continue;
|
|
858
860
|
if (dbAttrs.type !== NodeType2.DatabaseNode) continue;
|
|
859
861
|
const db = dbAttrs;
|
|
860
862
|
for (const pair of compatPairs()) {
|
|
@@ -1017,11 +1019,10 @@ var PolicyViolationsLog = class {
|
|
|
1017
1019
|
import {
|
|
1018
1020
|
EdgeType as EdgeType2,
|
|
1019
1021
|
NodeType as NodeType3,
|
|
1020
|
-
Provenance
|
|
1022
|
+
Provenance,
|
|
1021
1023
|
confidenceForObservedSignal,
|
|
1022
1024
|
databaseId,
|
|
1023
1025
|
extractedEdgeId,
|
|
1024
|
-
frontierEdgeId,
|
|
1025
1026
|
frontierId,
|
|
1026
1027
|
inferredEdgeId,
|
|
1027
1028
|
observedEdgeId,
|
|
@@ -1183,31 +1184,6 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
1183
1184
|
graph.addNode(id, node);
|
|
1184
1185
|
return id;
|
|
1185
1186
|
}
|
|
1186
|
-
function upsertFrontierEdge(graph, type, source, target, ts) {
|
|
1187
|
-
const id = frontierEdgeId(source, target, type);
|
|
1188
|
-
if (graph.hasEdge(id)) {
|
|
1189
|
-
const existing = graph.getEdgeAttributes(id);
|
|
1190
|
-
const updated = {
|
|
1191
|
-
...existing,
|
|
1192
|
-
provenance: Provenance3.FRONTIER,
|
|
1193
|
-
lastObserved: ts,
|
|
1194
|
-
callCount: (existing.callCount ?? 0) + 1
|
|
1195
|
-
};
|
|
1196
|
-
graph.replaceEdgeAttributes(id, updated);
|
|
1197
|
-
return;
|
|
1198
|
-
}
|
|
1199
|
-
const edge = {
|
|
1200
|
-
id,
|
|
1201
|
-
source,
|
|
1202
|
-
target,
|
|
1203
|
-
type,
|
|
1204
|
-
provenance: Provenance3.FRONTIER,
|
|
1205
|
-
confidence: 1,
|
|
1206
|
-
lastObserved: ts,
|
|
1207
|
-
callCount: 1
|
|
1208
|
-
};
|
|
1209
|
-
graph.addEdgeWithKey(id, source, target, edge);
|
|
1210
|
-
}
|
|
1211
1187
|
function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
1212
1188
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
1213
1189
|
const id = makeObservedEdgeId(type, source, target);
|
|
@@ -1222,7 +1198,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
|
1222
1198
|
};
|
|
1223
1199
|
const updated = {
|
|
1224
1200
|
...existing,
|
|
1225
|
-
provenance:
|
|
1201
|
+
provenance: Provenance.OBSERVED,
|
|
1226
1202
|
lastObserved: ts,
|
|
1227
1203
|
callCount: newSpanCount,
|
|
1228
1204
|
signal: newSignal,
|
|
@@ -1241,7 +1217,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
|
1241
1217
|
source,
|
|
1242
1218
|
target,
|
|
1243
1219
|
type,
|
|
1244
|
-
provenance:
|
|
1220
|
+
provenance: Provenance.OBSERVED,
|
|
1245
1221
|
confidence: confidenceForObservedSignal(signal),
|
|
1246
1222
|
lastObserved: ts,
|
|
1247
1223
|
callCount: 1,
|
|
@@ -1260,7 +1236,7 @@ function stitchTrace(graph, sourceServiceId, ts) {
|
|
|
1260
1236
|
const outbound = graph.outboundEdges(nodeId);
|
|
1261
1237
|
for (const edgeId of outbound) {
|
|
1262
1238
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
1263
|
-
if (edge.provenance !==
|
|
1239
|
+
if (edge.provenance !== Provenance.EXTRACTED) continue;
|
|
1264
1240
|
if (graph.hasEdge(observedEdgeId(edge.source, edge.target, edge.type))) continue;
|
|
1265
1241
|
upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts);
|
|
1266
1242
|
if (!visited.has(edge.target)) {
|
|
@@ -1283,7 +1259,7 @@ function upsertInferredEdge(graph, type, source, target, ts) {
|
|
|
1283
1259
|
source,
|
|
1284
1260
|
target,
|
|
1285
1261
|
type,
|
|
1286
|
-
provenance:
|
|
1262
|
+
provenance: Provenance.INFERRED,
|
|
1287
1263
|
confidence: INFERRED_CONFIDENCE,
|
|
1288
1264
|
lastObserved: ts
|
|
1289
1265
|
};
|
|
@@ -1293,18 +1269,28 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
1293
1269
|
await fs3.mkdir(path3.dirname(ctx.errorsPath), { recursive: true });
|
|
1294
1270
|
await fs3.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
1295
1271
|
}
|
|
1272
|
+
function sanitizeAttributes(attrs) {
|
|
1273
|
+
const out = {};
|
|
1274
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
1275
|
+
if (typeof v === "bigint") out[k] = v.toString();
|
|
1276
|
+
else out[k] = v;
|
|
1277
|
+
}
|
|
1278
|
+
return out;
|
|
1279
|
+
}
|
|
1296
1280
|
function buildErrorEventForReceiver(span) {
|
|
1297
1281
|
if (span.statusCode !== 2) return null;
|
|
1298
1282
|
const ts = span.startTimeIso ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1283
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
1299
1284
|
return {
|
|
1300
1285
|
id: `${span.traceId}:${span.spanId}`,
|
|
1301
1286
|
timestamp: ts,
|
|
1302
1287
|
service: span.service,
|
|
1303
1288
|
traceId: span.traceId,
|
|
1304
1289
|
spanId: span.spanId,
|
|
1305
|
-
errorMessage: span.exception?.message ?? span.
|
|
1290
|
+
errorMessage: span.exception?.message ?? span.name ?? "unknown error",
|
|
1306
1291
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1307
1292
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1293
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
1308
1294
|
affectedNode: serviceId(span.service)
|
|
1309
1295
|
};
|
|
1310
1296
|
}
|
|
@@ -1355,11 +1341,16 @@ async function handleSpan(ctx, span) {
|
|
|
1355
1341
|
affectedNode = targetId;
|
|
1356
1342
|
resolvedViaAddress = true;
|
|
1357
1343
|
} else if (!targetId) {
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1344
|
+
const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
|
|
1345
|
+
upsertObservedEdge(
|
|
1346
|
+
ctx.graph,
|
|
1347
|
+
EdgeType2.CALLS,
|
|
1348
|
+
sourceId,
|
|
1349
|
+
frontierNodeId,
|
|
1350
|
+
ts,
|
|
1351
|
+
isError
|
|
1352
|
+
);
|
|
1353
|
+
affectedNode = frontierNodeId;
|
|
1363
1354
|
resolvedViaAddress = true;
|
|
1364
1355
|
}
|
|
1365
1356
|
}
|
|
@@ -1381,15 +1372,17 @@ async function handleSpan(ctx, span) {
|
|
|
1381
1372
|
if (span.statusCode === 2) {
|
|
1382
1373
|
stitchTrace(ctx.graph, sourceId, ts);
|
|
1383
1374
|
if (ctx.writeErrorEventInline !== false) {
|
|
1375
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
1384
1376
|
const ev = {
|
|
1385
1377
|
id: `${span.traceId}:${span.spanId}`,
|
|
1386
1378
|
timestamp: ts,
|
|
1387
1379
|
service: span.service,
|
|
1388
1380
|
traceId: span.traceId,
|
|
1389
1381
|
spanId: span.spanId,
|
|
1390
|
-
errorMessage: span.exception?.message ?? span.
|
|
1382
|
+
errorMessage: span.exception?.message ?? span.name ?? "unknown error",
|
|
1391
1383
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1392
1384
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1385
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
1393
1386
|
affectedNode
|
|
1394
1387
|
};
|
|
1395
1388
|
await appendErrorEvent(ctx, ev);
|
|
@@ -1445,8 +1438,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId3) {
|
|
|
1445
1438
|
}
|
|
1446
1439
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
1447
1440
|
graph.dropEdge(oldEdgeId);
|
|
1448
|
-
const
|
|
1449
|
-
const newId = promotedProvenance === Provenance3.OBSERVED ? observedEdgeId(newSource, newTarget, edge.type) : promotedProvenance === Provenance3.INFERRED ? inferredEdgeId(newSource, newTarget, edge.type) : promotedProvenance === Provenance3.EXTRACTED ? extractedEdgeId(newSource, newTarget, edge.type) : frontierEdgeId(newSource, newTarget, edge.type);
|
|
1441
|
+
const newId = edge.provenance === Provenance.OBSERVED ? observedEdgeId(newSource, newTarget, edge.type) : edge.provenance === Provenance.INFERRED ? inferredEdgeId(newSource, newTarget, edge.type) : extractedEdgeId(newSource, newTarget, edge.type);
|
|
1450
1442
|
if (graph.hasEdge(newId)) {
|
|
1451
1443
|
const existing = graph.getEdgeAttributes(newId);
|
|
1452
1444
|
const merged = {
|
|
@@ -1461,8 +1453,7 @@ function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
|
1461
1453
|
...edge,
|
|
1462
1454
|
id: newId,
|
|
1463
1455
|
source: newSource,
|
|
1464
|
-
target: newTarget
|
|
1465
|
-
provenance: promotedProvenance
|
|
1456
|
+
target: newTarget
|
|
1466
1457
|
};
|
|
1467
1458
|
graph.addEdgeWithKey(newId, newSource, newTarget, rebuilt);
|
|
1468
1459
|
}
|
|
@@ -1481,12 +1472,12 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
1481
1472
|
const project = options.project ?? DEFAULT_PROJECT;
|
|
1482
1473
|
graph.forEachEdge((id, attrs) => {
|
|
1483
1474
|
const e = attrs;
|
|
1484
|
-
if (e.provenance !==
|
|
1475
|
+
if (e.provenance !== Provenance.OBSERVED) return;
|
|
1485
1476
|
if (!e.lastObserved) return;
|
|
1486
1477
|
const threshold = thresholdForEdgeType(e.type, thresholds);
|
|
1487
1478
|
const age = now - new Date(e.lastObserved).getTime();
|
|
1488
1479
|
if (age > threshold) {
|
|
1489
|
-
const updated = { ...e, provenance:
|
|
1480
|
+
const updated = { ...e, provenance: Provenance.STALE, confidence: 0.3 };
|
|
1490
1481
|
graph.replaceEdgeAttributes(id, updated);
|
|
1491
1482
|
events.push({
|
|
1492
1483
|
edgeId: id,
|
|
@@ -1503,8 +1494,8 @@ async function markStaleEdges(graph, options = {}) {
|
|
|
1503
1494
|
project,
|
|
1504
1495
|
payload: {
|
|
1505
1496
|
edgeId: id,
|
|
1506
|
-
from:
|
|
1507
|
-
to:
|
|
1497
|
+
from: Provenance.OBSERVED,
|
|
1498
|
+
to: Provenance.STALE
|
|
1508
1499
|
}
|
|
1509
1500
|
});
|
|
1510
1501
|
}
|
|
@@ -2250,7 +2241,7 @@ import path17 from "path";
|
|
|
2250
2241
|
import {
|
|
2251
2242
|
EdgeType as EdgeType3,
|
|
2252
2243
|
NodeType as NodeType6,
|
|
2253
|
-
Provenance as
|
|
2244
|
+
Provenance as Provenance2,
|
|
2254
2245
|
databaseId as databaseId2,
|
|
2255
2246
|
confidenceForExtracted
|
|
2256
2247
|
} from "@neat.is/types";
|
|
@@ -2827,7 +2818,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
2827
2818
|
source: service.node.id,
|
|
2828
2819
|
target: dbNode.id,
|
|
2829
2820
|
type: EdgeType3.CONNECTS_TO,
|
|
2830
|
-
provenance:
|
|
2821
|
+
provenance: Provenance2.EXTRACTED,
|
|
2831
2822
|
confidence: confidenceForExtracted("structural"),
|
|
2832
2823
|
// ADR-032 / #140 — every EXTRACTED edge carries evidence.file.
|
|
2833
2824
|
// Ghost-edge cleanup keys retirement on this; the conditional
|
|
@@ -2864,7 +2855,7 @@ import path18 from "path";
|
|
|
2864
2855
|
import {
|
|
2865
2856
|
EdgeType as EdgeType4,
|
|
2866
2857
|
NodeType as NodeType7,
|
|
2867
|
-
Provenance as
|
|
2858
|
+
Provenance as Provenance3,
|
|
2868
2859
|
configId,
|
|
2869
2860
|
confidenceForExtracted as confidenceForExtracted2
|
|
2870
2861
|
} from "@neat.is/types";
|
|
@@ -2907,7 +2898,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
2907
2898
|
source: service.node.id,
|
|
2908
2899
|
target: node.id,
|
|
2909
2900
|
type: EdgeType4.CONFIGURED_BY,
|
|
2910
|
-
provenance:
|
|
2901
|
+
provenance: Provenance3.EXTRACTED,
|
|
2911
2902
|
confidence: confidenceForExtracted2("structural"),
|
|
2912
2903
|
evidence: { file: relPath.split(path18.sep).join("/") }
|
|
2913
2904
|
};
|
|
@@ -2924,7 +2915,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
2924
2915
|
import {
|
|
2925
2916
|
EdgeType as EdgeType6,
|
|
2926
2917
|
NodeType as NodeType8,
|
|
2927
|
-
Provenance as
|
|
2918
|
+
Provenance as Provenance5,
|
|
2928
2919
|
confidenceForExtracted as confidenceForExtracted4,
|
|
2929
2920
|
passesExtractedFloor as passesExtractedFloor2
|
|
2930
2921
|
} from "@neat.is/types";
|
|
@@ -2936,7 +2927,7 @@ import JavaScript from "tree-sitter-javascript";
|
|
|
2936
2927
|
import Python from "tree-sitter-python";
|
|
2937
2928
|
import {
|
|
2938
2929
|
EdgeType as EdgeType5,
|
|
2939
|
-
Provenance as
|
|
2930
|
+
Provenance as Provenance4,
|
|
2940
2931
|
confidenceForExtracted as confidenceForExtracted3,
|
|
2941
2932
|
passesExtractedFloor
|
|
2942
2933
|
} from "@neat.is/types";
|
|
@@ -3094,7 +3085,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3094
3085
|
source: service.node.id,
|
|
3095
3086
|
target: targetId,
|
|
3096
3087
|
type: EdgeType5.CALLS,
|
|
3097
|
-
provenance:
|
|
3088
|
+
provenance: Provenance4.EXTRACTED,
|
|
3098
3089
|
confidence,
|
|
3099
3090
|
evidence: ev
|
|
3100
3091
|
};
|
|
@@ -3377,7 +3368,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3377
3368
|
source: service.node.id,
|
|
3378
3369
|
target: ep.infraId,
|
|
3379
3370
|
type: edgeType,
|
|
3380
|
-
provenance:
|
|
3371
|
+
provenance: Provenance5.EXTRACTED,
|
|
3381
3372
|
confidence,
|
|
3382
3373
|
evidence: ep.evidence
|
|
3383
3374
|
};
|
|
@@ -3399,7 +3390,7 @@ async function addCallEdges(graph, services) {
|
|
|
3399
3390
|
|
|
3400
3391
|
// src/extract/infra/docker-compose.ts
|
|
3401
3392
|
import path25 from "path";
|
|
3402
|
-
import { EdgeType as EdgeType7, Provenance as
|
|
3393
|
+
import { EdgeType as EdgeType7, Provenance as Provenance6, confidenceForExtracted as confidenceForExtracted5 } from "@neat.is/types";
|
|
3403
3394
|
|
|
3404
3395
|
// src/extract/infra/shared.ts
|
|
3405
3396
|
import { NodeType as NodeType9, infraId as infraId5 } from "@neat.is/types";
|
|
@@ -3492,7 +3483,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3492
3483
|
source: sourceId,
|
|
3493
3484
|
target: targetId,
|
|
3494
3485
|
type: EdgeType7.DEPENDS_ON,
|
|
3495
|
-
provenance:
|
|
3486
|
+
provenance: Provenance6.EXTRACTED,
|
|
3496
3487
|
confidence: confidenceForExtracted5("structural"),
|
|
3497
3488
|
evidence: { file: evidenceFile }
|
|
3498
3489
|
};
|
|
@@ -3506,7 +3497,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3506
3497
|
// src/extract/infra/dockerfile.ts
|
|
3507
3498
|
import path26 from "path";
|
|
3508
3499
|
import { promises as fs14 } from "fs";
|
|
3509
|
-
import { EdgeType as EdgeType8, Provenance as
|
|
3500
|
+
import { EdgeType as EdgeType8, Provenance as Provenance7, confidenceForExtracted as confidenceForExtracted6 } from "@neat.is/types";
|
|
3510
3501
|
function runtimeImage(content) {
|
|
3511
3502
|
const lines = content.split("\n");
|
|
3512
3503
|
let last = null;
|
|
@@ -3552,7 +3543,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
3552
3543
|
source: service.node.id,
|
|
3553
3544
|
target: node.id,
|
|
3554
3545
|
type: EdgeType8.RUNS_ON,
|
|
3555
|
-
provenance:
|
|
3546
|
+
provenance: Provenance7.EXTRACTED,
|
|
3556
3547
|
confidence: confidenceForExtracted6("structural"),
|
|
3557
3548
|
evidence: {
|
|
3558
3549
|
file: path26.relative(scanPath, dockerfilePath).split(path26.sep).join("/")
|
|
@@ -3674,13 +3665,13 @@ import path30 from "path";
|
|
|
3674
3665
|
// src/extract/retire.ts
|
|
3675
3666
|
import { existsSync } from "fs";
|
|
3676
3667
|
import path29 from "path";
|
|
3677
|
-
import { Provenance as
|
|
3668
|
+
import { Provenance as Provenance8 } from "@neat.is/types";
|
|
3678
3669
|
function retireEdgesByFile(graph, file) {
|
|
3679
3670
|
const normalized = file.split("\\").join("/");
|
|
3680
3671
|
const toDrop = [];
|
|
3681
3672
|
graph.forEachEdge((id, attrs) => {
|
|
3682
3673
|
const edge = attrs;
|
|
3683
|
-
if (edge.provenance !==
|
|
3674
|
+
if (edge.provenance !== Provenance8.EXTRACTED) return;
|
|
3684
3675
|
if (!edge.evidence?.file) return;
|
|
3685
3676
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
3686
3677
|
});
|
|
@@ -3692,7 +3683,7 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
3692
3683
|
const bases = [scanPath, ...serviceDirs];
|
|
3693
3684
|
graph.forEachEdge((id, attrs) => {
|
|
3694
3685
|
const edge = attrs;
|
|
3695
|
-
if (edge.provenance !==
|
|
3686
|
+
if (edge.provenance !== Provenance8.EXTRACTED) return;
|
|
3696
3687
|
const evidenceFile = edge.evidence?.file;
|
|
3697
3688
|
if (!evidenceFile) return;
|
|
3698
3689
|
if (path29.isAbsolute(evidenceFile)) {
|
|
@@ -3771,7 +3762,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
3771
3762
|
// src/persist.ts
|
|
3772
3763
|
import { promises as fs17 } from "fs";
|
|
3773
3764
|
import path31 from "path";
|
|
3774
|
-
|
|
3765
|
+
import { Provenance as Provenance9, observedEdgeId as observedEdgeId2 } from "@neat.is/types";
|
|
3766
|
+
var SCHEMA_VERSION = 3;
|
|
3775
3767
|
function migrateV1ToV2(payload) {
|
|
3776
3768
|
const nodes = payload.graph.nodes;
|
|
3777
3769
|
if (Array.isArray(nodes)) {
|
|
@@ -3783,6 +3775,25 @@ function migrateV1ToV2(payload) {
|
|
|
3783
3775
|
}
|
|
3784
3776
|
return { ...payload, schemaVersion: 2 };
|
|
3785
3777
|
}
|
|
3778
|
+
function migrateV2ToV3(payload) {
|
|
3779
|
+
const edges = payload.graph.edges;
|
|
3780
|
+
if (Array.isArray(edges)) {
|
|
3781
|
+
for (const edge of edges) {
|
|
3782
|
+
const attrs = edge.attributes;
|
|
3783
|
+
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
3784
|
+
attrs.provenance = Provenance9.OBSERVED;
|
|
3785
|
+
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
3786
|
+
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
3787
|
+
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
3788
|
+
if (type && source && target) {
|
|
3789
|
+
const newId = observedEdgeId2(source, target, type);
|
|
3790
|
+
attrs.id = newId;
|
|
3791
|
+
if (edge.key) edge.key = newId;
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
return { ...payload, schemaVersion: 3 };
|
|
3796
|
+
}
|
|
3786
3797
|
async function ensureDir(filePath) {
|
|
3787
3798
|
await fs17.mkdir(path31.dirname(filePath), { recursive: true });
|
|
3788
3799
|
}
|
|
@@ -3809,6 +3820,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
3809
3820
|
if (payload.schemaVersion === 1) {
|
|
3810
3821
|
payload = migrateV1ToV2(payload);
|
|
3811
3822
|
}
|
|
3823
|
+
if (payload.schemaVersion === 2) {
|
|
3824
|
+
payload = migrateV2ToV3(payload);
|
|
3825
|
+
}
|
|
3812
3826
|
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
3813
3827
|
throw new Error(
|
|
3814
3828
|
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
@@ -4162,7 +4176,7 @@ import {
|
|
|
4162
4176
|
EdgeType as EdgeType9,
|
|
4163
4177
|
NodeType as NodeType10,
|
|
4164
4178
|
parseEdgeId,
|
|
4165
|
-
Provenance as
|
|
4179
|
+
Provenance as Provenance10
|
|
4166
4180
|
} from "@neat.is/types";
|
|
4167
4181
|
function bucketKey(source, target, type) {
|
|
4168
4182
|
return `${type}|${source}|${target}`;
|
|
@@ -4176,20 +4190,17 @@ function bucketEdges(graph) {
|
|
|
4176
4190
|
const key = bucketKey(e.source, e.target, e.type);
|
|
4177
4191
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
4178
4192
|
switch (provenance) {
|
|
4179
|
-
case
|
|
4193
|
+
case Provenance10.EXTRACTED:
|
|
4180
4194
|
cur.extracted = e;
|
|
4181
4195
|
break;
|
|
4182
|
-
case
|
|
4196
|
+
case Provenance10.OBSERVED:
|
|
4183
4197
|
cur.observed = e;
|
|
4184
4198
|
break;
|
|
4185
|
-
case
|
|
4199
|
+
case Provenance10.INFERRED:
|
|
4186
4200
|
cur.inferred = e;
|
|
4187
4201
|
break;
|
|
4188
|
-
case Provenance11.FRONTIER:
|
|
4189
|
-
cur.frontier = e;
|
|
4190
|
-
break;
|
|
4191
4202
|
default:
|
|
4192
|
-
if (e.provenance ===
|
|
4203
|
+
if (e.provenance === Provenance10.STALE) cur.stale = e;
|
|
4193
4204
|
}
|
|
4194
4205
|
buckets.set(key, cur);
|
|
4195
4206
|
});
|
|
@@ -4259,7 +4270,7 @@ function declaredHostFor(svc) {
|
|
|
4259
4270
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
4260
4271
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4261
4272
|
const e = graph.getEdgeAttributes(edgeId);
|
|
4262
|
-
if (e.type === EdgeType9.CONFIGURED_BY && e.provenance ===
|
|
4273
|
+
if (e.type === EdgeType9.CONFIGURED_BY && e.provenance === Provenance10.EXTRACTED) {
|
|
4263
4274
|
return true;
|
|
4264
4275
|
}
|
|
4265
4276
|
}
|
|
@@ -4273,7 +4284,7 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
4273
4284
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4274
4285
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4275
4286
|
if (edge.type !== EdgeType9.CONNECTS_TO) continue;
|
|
4276
|
-
if (edge.provenance !==
|
|
4287
|
+
if (edge.provenance !== Provenance10.OBSERVED) continue;
|
|
4277
4288
|
const target = graph.getNodeAttributes(edge.target);
|
|
4278
4289
|
if (target.type !== NodeType10.DatabaseNode) continue;
|
|
4279
4290
|
const observedHost = target.host?.trim();
|
|
@@ -4298,7 +4309,7 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
4298
4309
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
4299
4310
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
4300
4311
|
if (edge.type !== EdgeType9.CONNECTS_TO) continue;
|
|
4301
|
-
if (edge.provenance !==
|
|
4312
|
+
if (edge.provenance !== Provenance10.OBSERVED) continue;
|
|
4302
4313
|
const target = graph.getNodeAttributes(edge.target);
|
|
4303
4314
|
if (target.type !== NodeType10.DatabaseNode) continue;
|
|
4304
4315
|
for (const pair of compatPairs()) {
|
|
@@ -4941,4 +4952,4 @@ export {
|
|
|
4941
4952
|
removeProject,
|
|
4942
4953
|
buildApi
|
|
4943
4954
|
};
|
|
4944
|
-
//# sourceMappingURL=chunk-
|
|
4955
|
+
//# sourceMappingURL=chunk-BUB3ASD5.js.map
|