@neat.is/core 0.3.3 → 0.3.4
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-VABXPLDT.js → chunk-33ZZ2ZID.js} +181 -66
- package/dist/chunk-33ZZ2ZID.js.map +1 -0
- package/dist/{chunk-ICFH326H.js → chunk-T3A2GK3X.js} +2 -2
- package/dist/cli.cjs +215 -124
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +160 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +167 -81
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +156 -70
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-VABXPLDT.js.map +0 -1
- /package/dist/{chunk-ICFH326H.js.map → chunk-T3A2GK3X.js.map} +0 -0
|
@@ -427,19 +427,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
427
427
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
428
428
|
let best = { path: [start], edges: [] };
|
|
429
429
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
430
|
-
function step(node,
|
|
431
|
-
if (
|
|
432
|
-
best = { path: [...
|
|
430
|
+
function step(node, path34, edges) {
|
|
431
|
+
if (path34.length > best.path.length) {
|
|
432
|
+
best = { path: [...path34], edges: [...edges] };
|
|
433
433
|
}
|
|
434
|
-
if (
|
|
434
|
+
if (path34.length - 1 >= maxDepth) return;
|
|
435
435
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
436
436
|
for (const [srcId, edge] of incoming) {
|
|
437
437
|
if (visited.has(srcId)) continue;
|
|
438
438
|
visited.add(srcId);
|
|
439
|
-
|
|
439
|
+
path34.push(srcId);
|
|
440
440
|
edges.push(edge);
|
|
441
|
-
step(srcId,
|
|
442
|
-
|
|
441
|
+
step(srcId, path34, edges);
|
|
442
|
+
path34.pop();
|
|
443
443
|
edges.pop();
|
|
444
444
|
visited.delete(srcId);
|
|
445
445
|
}
|
|
@@ -1018,6 +1018,7 @@ import {
|
|
|
1018
1018
|
EdgeType as EdgeType2,
|
|
1019
1019
|
NodeType as NodeType3,
|
|
1020
1020
|
Provenance as Provenance3,
|
|
1021
|
+
confidenceForObservedSignal,
|
|
1021
1022
|
databaseId,
|
|
1022
1023
|
extractedEdgeId,
|
|
1023
1024
|
frontierEdgeId,
|
|
@@ -1214,35 +1215,37 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
|
1214
1215
|
const existing = graph.getEdgeAttributes(id);
|
|
1215
1216
|
const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1;
|
|
1216
1217
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
1218
|
+
const newSignal = {
|
|
1219
|
+
spanCount: newSpanCount,
|
|
1220
|
+
errorCount: newErrorCount,
|
|
1221
|
+
lastObservedAgeMs: 0
|
|
1222
|
+
};
|
|
1217
1223
|
const updated = {
|
|
1218
1224
|
...existing,
|
|
1219
1225
|
provenance: Provenance3.OBSERVED,
|
|
1220
1226
|
lastObserved: ts,
|
|
1221
1227
|
callCount: newSpanCount,
|
|
1222
|
-
signal:
|
|
1223
|
-
|
|
1224
|
-
errorCount: newErrorCount,
|
|
1225
|
-
lastObservedAgeMs: 0
|
|
1226
|
-
},
|
|
1227
|
-
confidence: 1
|
|
1228
|
+
signal: newSignal,
|
|
1229
|
+
confidence: confidenceForObservedSignal(newSignal)
|
|
1228
1230
|
};
|
|
1229
1231
|
graph.replaceEdgeAttributes(id, updated);
|
|
1230
1232
|
return { edge: updated, created: false };
|
|
1231
1233
|
}
|
|
1234
|
+
const signal = {
|
|
1235
|
+
spanCount: 1,
|
|
1236
|
+
errorCount: isError ? 1 : 0,
|
|
1237
|
+
lastObservedAgeMs: 0
|
|
1238
|
+
};
|
|
1232
1239
|
const edge = {
|
|
1233
1240
|
id,
|
|
1234
1241
|
source,
|
|
1235
1242
|
target,
|
|
1236
1243
|
type,
|
|
1237
1244
|
provenance: Provenance3.OBSERVED,
|
|
1238
|
-
confidence:
|
|
1245
|
+
confidence: confidenceForObservedSignal(signal),
|
|
1239
1246
|
lastObserved: ts,
|
|
1240
1247
|
callCount: 1,
|
|
1241
|
-
signal
|
|
1242
|
-
spanCount: 1,
|
|
1243
|
-
errorCount: isError ? 1 : 0,
|
|
1244
|
-
lastObservedAgeMs: 0
|
|
1245
|
-
}
|
|
1248
|
+
signal
|
|
1246
1249
|
};
|
|
1247
1250
|
graph.addEdgeWithKey(id, source, target, edge);
|
|
1248
1251
|
return { edge, created: true };
|
|
@@ -1593,6 +1596,27 @@ function formatExtractionBanner(count) {
|
|
|
1593
1596
|
if (count === 1) return `[neat] 1 file skipped due to parse errors`;
|
|
1594
1597
|
return `[neat] ${count} files skipped due to parse errors`;
|
|
1595
1598
|
}
|
|
1599
|
+
var droppedSink = [];
|
|
1600
|
+
function noteExtractedDropped(edge) {
|
|
1601
|
+
droppedSink.push(edge);
|
|
1602
|
+
}
|
|
1603
|
+
function drainDroppedExtracted() {
|
|
1604
|
+
return droppedSink.splice(0, droppedSink.length);
|
|
1605
|
+
}
|
|
1606
|
+
function isRejectedLogEnabled() {
|
|
1607
|
+
const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
|
|
1608
|
+
return raw === "1" || raw === "true";
|
|
1609
|
+
}
|
|
1610
|
+
async function writeRejectedExtracted(drops, rejectedPath) {
|
|
1611
|
+
if (drops.length === 0) return;
|
|
1612
|
+
await fs4.mkdir(path4.dirname(rejectedPath), { recursive: true });
|
|
1613
|
+
const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
|
|
1614
|
+
await fs4.appendFile(rejectedPath, lines, "utf8");
|
|
1615
|
+
}
|
|
1616
|
+
function formatPrecisionFloorBanner(count) {
|
|
1617
|
+
if (count === 1) return `[neat] 1 extracted edge dropped below precision floor`;
|
|
1618
|
+
return `[neat] ${count} extracted edges dropped below precision floor`;
|
|
1619
|
+
}
|
|
1596
1620
|
|
|
1597
1621
|
// src/extract/services.ts
|
|
1598
1622
|
import { promises as fs8 } from "fs";
|
|
@@ -2223,7 +2247,13 @@ async function addServiceAliases(graph, scanPath, services) {
|
|
|
2223
2247
|
|
|
2224
2248
|
// src/extract/databases/index.ts
|
|
2225
2249
|
import path17 from "path";
|
|
2226
|
-
import {
|
|
2250
|
+
import {
|
|
2251
|
+
EdgeType as EdgeType3,
|
|
2252
|
+
NodeType as NodeType6,
|
|
2253
|
+
Provenance as Provenance4,
|
|
2254
|
+
databaseId as databaseId2,
|
|
2255
|
+
confidenceForExtracted
|
|
2256
|
+
} from "@neat.is/types";
|
|
2227
2257
|
|
|
2228
2258
|
// src/extract/databases/db-config-yaml.ts
|
|
2229
2259
|
import path10 from "path";
|
|
@@ -2798,6 +2828,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
2798
2828
|
target: dbNode.id,
|
|
2799
2829
|
type: EdgeType3.CONNECTS_TO,
|
|
2800
2830
|
provenance: Provenance4.EXTRACTED,
|
|
2831
|
+
confidence: confidenceForExtracted("structural"),
|
|
2801
2832
|
// ADR-032 / #140 — every EXTRACTED edge carries evidence.file.
|
|
2802
2833
|
// Ghost-edge cleanup keys retirement on this; the conditional
|
|
2803
2834
|
// sourceFile spread that used to live here was a v0.1.x leftover.
|
|
@@ -2830,7 +2861,13 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
2830
2861
|
// src/extract/configs.ts
|
|
2831
2862
|
import { promises as fs12 } from "fs";
|
|
2832
2863
|
import path18 from "path";
|
|
2833
|
-
import {
|
|
2864
|
+
import {
|
|
2865
|
+
EdgeType as EdgeType4,
|
|
2866
|
+
NodeType as NodeType7,
|
|
2867
|
+
Provenance as Provenance5,
|
|
2868
|
+
configId,
|
|
2869
|
+
confidenceForExtracted as confidenceForExtracted2
|
|
2870
|
+
} from "@neat.is/types";
|
|
2834
2871
|
async function walkConfigFiles(dir) {
|
|
2835
2872
|
const out = [];
|
|
2836
2873
|
async function walk(current) {
|
|
@@ -2871,6 +2908,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
2871
2908
|
target: node.id,
|
|
2872
2909
|
type: EdgeType4.CONFIGURED_BY,
|
|
2873
2910
|
provenance: Provenance5.EXTRACTED,
|
|
2911
|
+
confidence: confidenceForExtracted2("structural"),
|
|
2874
2912
|
evidence: { file: relPath.split(path18.sep).join("/") }
|
|
2875
2913
|
};
|
|
2876
2914
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -2883,14 +2921,25 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
2883
2921
|
}
|
|
2884
2922
|
|
|
2885
2923
|
// src/extract/calls/index.ts
|
|
2886
|
-
import {
|
|
2924
|
+
import {
|
|
2925
|
+
EdgeType as EdgeType6,
|
|
2926
|
+
NodeType as NodeType8,
|
|
2927
|
+
Provenance as Provenance7,
|
|
2928
|
+
confidenceForExtracted as confidenceForExtracted4,
|
|
2929
|
+
passesExtractedFloor as passesExtractedFloor2
|
|
2930
|
+
} from "@neat.is/types";
|
|
2887
2931
|
|
|
2888
2932
|
// src/extract/calls/http.ts
|
|
2889
2933
|
import path20 from "path";
|
|
2890
2934
|
import Parser from "tree-sitter";
|
|
2891
2935
|
import JavaScript from "tree-sitter-javascript";
|
|
2892
2936
|
import Python from "tree-sitter-python";
|
|
2893
|
-
import {
|
|
2937
|
+
import {
|
|
2938
|
+
EdgeType as EdgeType5,
|
|
2939
|
+
Provenance as Provenance6,
|
|
2940
|
+
confidenceForExtracted as confidenceForExtracted3,
|
|
2941
|
+
passesExtractedFloor
|
|
2942
|
+
} from "@neat.is/types";
|
|
2894
2943
|
|
|
2895
2944
|
// src/extract/calls/shared.ts
|
|
2896
2945
|
import { promises as fs13 } from "fs";
|
|
@@ -3022,17 +3071,32 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3022
3071
|
for (const [targetId, evidenceFile] of seenTargets) {
|
|
3023
3072
|
const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? "";
|
|
3024
3073
|
const line = lineOf(fileContent, `//${evidenceFile.host}`);
|
|
3074
|
+
const confidence = confidenceForExtracted3("hostname-shape-match");
|
|
3075
|
+
const ev = {
|
|
3076
|
+
file: path20.relative(service.dir, evidenceFile.file),
|
|
3077
|
+
line,
|
|
3078
|
+
snippet: snippet(fileContent, line)
|
|
3079
|
+
};
|
|
3080
|
+
const edgeId = extractedEdgeId2(service.node.id, targetId, EdgeType5.CALLS);
|
|
3081
|
+
if (!passesExtractedFloor(confidence)) {
|
|
3082
|
+
noteExtractedDropped({
|
|
3083
|
+
source: service.node.id,
|
|
3084
|
+
target: targetId,
|
|
3085
|
+
type: EdgeType5.CALLS,
|
|
3086
|
+
confidence,
|
|
3087
|
+
confidenceKind: "hostname-shape-match",
|
|
3088
|
+
evidence: ev
|
|
3089
|
+
});
|
|
3090
|
+
continue;
|
|
3091
|
+
}
|
|
3025
3092
|
const edge = {
|
|
3026
|
-
id:
|
|
3093
|
+
id: edgeId,
|
|
3027
3094
|
source: service.node.id,
|
|
3028
3095
|
target: targetId,
|
|
3029
3096
|
type: EdgeType5.CALLS,
|
|
3030
3097
|
provenance: Provenance6.EXTRACTED,
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
line,
|
|
3034
|
-
snippet: snippet(fileContent, line)
|
|
3035
|
-
}
|
|
3098
|
+
confidence,
|
|
3099
|
+
evidence: ev
|
|
3036
3100
|
};
|
|
3037
3101
|
if (!graph.hasEdge(edge.id)) {
|
|
3038
3102
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -3070,6 +3134,10 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3070
3134
|
name: topic,
|
|
3071
3135
|
kind: "kafka-topic",
|
|
3072
3136
|
edgeType,
|
|
3137
|
+
// `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})` —
|
|
3138
|
+
// framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
|
|
3139
|
+
// tier (ADR-066).
|
|
3140
|
+
confidenceKind: "verified-call-site",
|
|
3073
3141
|
evidence: {
|
|
3074
3142
|
file: path21.relative(serviceDir, file.path),
|
|
3075
3143
|
line,
|
|
@@ -3101,6 +3169,10 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3101
3169
|
name: host,
|
|
3102
3170
|
kind: "redis",
|
|
3103
3171
|
edgeType: "CALLS",
|
|
3172
|
+
// `redis://host` URL literal — the scheme is structural support, but no
|
|
3173
|
+
// call expression is verified to wire it through. URL-with-structural-
|
|
3174
|
+
// support tier (ADR-066).
|
|
3175
|
+
confidenceKind: "url-with-structural-support",
|
|
3104
3176
|
evidence: {
|
|
3105
3177
|
file: path22.relative(serviceDir, file.path),
|
|
3106
3178
|
line,
|
|
@@ -3141,6 +3213,10 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3141
3213
|
name,
|
|
3142
3214
|
kind,
|
|
3143
3215
|
edgeType: "CALLS",
|
|
3216
|
+
// SDK marker (S3Client, GetCommand, etc.) plus a Bucket/TableName
|
|
3217
|
+
// literal — framework-aware recognizer, verified-call-site tier
|
|
3218
|
+
// (ADR-066).
|
|
3219
|
+
confidenceKind: "verified-call-site",
|
|
3144
3220
|
evidence: {
|
|
3145
3221
|
file: path23.relative(serviceDir, file.path),
|
|
3146
3222
|
line,
|
|
@@ -3219,6 +3295,10 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3219
3295
|
name,
|
|
3220
3296
|
kind,
|
|
3221
3297
|
edgeType: "CALLS",
|
|
3298
|
+
// `new <Name>Client(...)` with @aws-sdk/* or @grpc/grpc-js import
|
|
3299
|
+
// context — import-aware classification per #238. Verified-call-site
|
|
3300
|
+
// tier (ADR-066).
|
|
3301
|
+
confidenceKind: "verified-call-site",
|
|
3222
3302
|
evidence: {
|
|
3223
3303
|
file: path24.relative(serviceDir, file.path),
|
|
3224
3304
|
line,
|
|
@@ -3279,6 +3359,18 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3279
3359
|
const edgeId = extractedEdgeId2(service.node.id, ep.infraId, edgeType);
|
|
3280
3360
|
if (seenEdges.has(edgeId)) continue;
|
|
3281
3361
|
seenEdges.add(edgeId);
|
|
3362
|
+
const confidence = confidenceForExtracted4(ep.confidenceKind);
|
|
3363
|
+
if (!passesExtractedFloor2(confidence)) {
|
|
3364
|
+
noteExtractedDropped({
|
|
3365
|
+
source: service.node.id,
|
|
3366
|
+
target: ep.infraId,
|
|
3367
|
+
type: edgeType,
|
|
3368
|
+
confidence,
|
|
3369
|
+
confidenceKind: ep.confidenceKind,
|
|
3370
|
+
evidence: ep.evidence
|
|
3371
|
+
});
|
|
3372
|
+
continue;
|
|
3373
|
+
}
|
|
3282
3374
|
if (!graph.hasEdge(edgeId)) {
|
|
3283
3375
|
const edge = {
|
|
3284
3376
|
id: edgeId,
|
|
@@ -3286,6 +3378,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3286
3378
|
target: ep.infraId,
|
|
3287
3379
|
type: edgeType,
|
|
3288
3380
|
provenance: Provenance7.EXTRACTED,
|
|
3381
|
+
confidence,
|
|
3289
3382
|
evidence: ep.evidence
|
|
3290
3383
|
};
|
|
3291
3384
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -3306,7 +3399,7 @@ async function addCallEdges(graph, services) {
|
|
|
3306
3399
|
|
|
3307
3400
|
// src/extract/infra/docker-compose.ts
|
|
3308
3401
|
import path25 from "path";
|
|
3309
|
-
import { EdgeType as EdgeType7, Provenance as Provenance8 } from "@neat.is/types";
|
|
3402
|
+
import { EdgeType as EdgeType7, Provenance as Provenance8, confidenceForExtracted as confidenceForExtracted5 } from "@neat.is/types";
|
|
3310
3403
|
|
|
3311
3404
|
// src/extract/infra/shared.ts
|
|
3312
3405
|
import { NodeType as NodeType9, infraId as infraId5 } from "@neat.is/types";
|
|
@@ -3400,6 +3493,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3400
3493
|
target: targetId,
|
|
3401
3494
|
type: EdgeType7.DEPENDS_ON,
|
|
3402
3495
|
provenance: Provenance8.EXTRACTED,
|
|
3496
|
+
confidence: confidenceForExtracted5("structural"),
|
|
3403
3497
|
evidence: { file: evidenceFile }
|
|
3404
3498
|
};
|
|
3405
3499
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -3412,7 +3506,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3412
3506
|
// src/extract/infra/dockerfile.ts
|
|
3413
3507
|
import path26 from "path";
|
|
3414
3508
|
import { promises as fs14 } from "fs";
|
|
3415
|
-
import { EdgeType as EdgeType8, Provenance as Provenance9 } from "@neat.is/types";
|
|
3509
|
+
import { EdgeType as EdgeType8, Provenance as Provenance9, confidenceForExtracted as confidenceForExtracted6 } from "@neat.is/types";
|
|
3416
3510
|
function runtimeImage(content) {
|
|
3417
3511
|
const lines = content.split("\n");
|
|
3418
3512
|
let last = null;
|
|
@@ -3459,6 +3553,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
3459
3553
|
target: node.id,
|
|
3460
3554
|
type: EdgeType8.RUNS_ON,
|
|
3461
3555
|
provenance: Provenance9.EXTRACTED,
|
|
3556
|
+
confidence: confidenceForExtracted6("structural"),
|
|
3462
3557
|
evidence: {
|
|
3463
3558
|
file: path26.relative(scanPath, dockerfilePath).split(path26.sep).join("/")
|
|
3464
3559
|
}
|
|
@@ -3573,6 +3668,9 @@ async function addInfra(graph, scanPath, services) {
|
|
|
3573
3668
|
};
|
|
3574
3669
|
}
|
|
3575
3670
|
|
|
3671
|
+
// src/extract/index.ts
|
|
3672
|
+
import path30 from "path";
|
|
3673
|
+
|
|
3576
3674
|
// src/extract/retire.ts
|
|
3577
3675
|
import { existsSync } from "fs";
|
|
3578
3676
|
import path29 from "path";
|
|
@@ -3636,13 +3734,26 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
3636
3734
|
);
|
|
3637
3735
|
}
|
|
3638
3736
|
}
|
|
3737
|
+
const droppedEntries = drainDroppedExtracted();
|
|
3738
|
+
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
3739
|
+
const rejectedPath = path30.join(path30.dirname(opts.errorsPath), "rejected.ndjson");
|
|
3740
|
+
try {
|
|
3741
|
+
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
3742
|
+
} catch (err) {
|
|
3743
|
+
console.warn(
|
|
3744
|
+
`[neat] failed to write rejected extracted edges to ${rejectedPath}: ${err.message}`
|
|
3745
|
+
);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3639
3748
|
const result = {
|
|
3640
3749
|
nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
3641
3750
|
edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
3642
3751
|
frontiersPromoted,
|
|
3643
3752
|
extractionErrors: errorEntries.length,
|
|
3644
3753
|
errorEntries,
|
|
3645
|
-
ghostsRetired
|
|
3754
|
+
ghostsRetired,
|
|
3755
|
+
extractedDropped: droppedEntries.length,
|
|
3756
|
+
droppedEntries
|
|
3646
3757
|
};
|
|
3647
3758
|
emitNeatEvent({
|
|
3648
3759
|
type: "extraction-complete",
|
|
@@ -3659,7 +3770,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
3659
3770
|
|
|
3660
3771
|
// src/persist.ts
|
|
3661
3772
|
import { promises as fs17 } from "fs";
|
|
3662
|
-
import
|
|
3773
|
+
import path31 from "path";
|
|
3663
3774
|
var SCHEMA_VERSION = 2;
|
|
3664
3775
|
function migrateV1ToV2(payload) {
|
|
3665
3776
|
const nodes = payload.graph.nodes;
|
|
@@ -3673,7 +3784,7 @@ function migrateV1ToV2(payload) {
|
|
|
3673
3784
|
return { ...payload, schemaVersion: 2 };
|
|
3674
3785
|
}
|
|
3675
3786
|
async function ensureDir(filePath) {
|
|
3676
|
-
await fs17.mkdir(
|
|
3787
|
+
await fs17.mkdir(path31.dirname(filePath), { recursive: true });
|
|
3677
3788
|
}
|
|
3678
3789
|
async function saveGraphToDisk(graph, outPath) {
|
|
3679
3790
|
await ensureDir(outPath);
|
|
@@ -3817,23 +3928,23 @@ function canonicalJson(value) {
|
|
|
3817
3928
|
}
|
|
3818
3929
|
|
|
3819
3930
|
// src/projects.ts
|
|
3820
|
-
import
|
|
3931
|
+
import path32 from "path";
|
|
3821
3932
|
function pathsForProject(project, baseDir) {
|
|
3822
3933
|
if (project === DEFAULT_PROJECT) {
|
|
3823
3934
|
return {
|
|
3824
|
-
snapshotPath:
|
|
3825
|
-
errorsPath:
|
|
3826
|
-
staleEventsPath:
|
|
3827
|
-
embeddingsCachePath:
|
|
3828
|
-
policyViolationsPath:
|
|
3935
|
+
snapshotPath: path32.join(baseDir, "graph.json"),
|
|
3936
|
+
errorsPath: path32.join(baseDir, "errors.ndjson"),
|
|
3937
|
+
staleEventsPath: path32.join(baseDir, "stale-events.ndjson"),
|
|
3938
|
+
embeddingsCachePath: path32.join(baseDir, "embeddings.json"),
|
|
3939
|
+
policyViolationsPath: path32.join(baseDir, "policy-violations.ndjson")
|
|
3829
3940
|
};
|
|
3830
3941
|
}
|
|
3831
3942
|
return {
|
|
3832
|
-
snapshotPath:
|
|
3833
|
-
errorsPath:
|
|
3834
|
-
staleEventsPath:
|
|
3835
|
-
embeddingsCachePath:
|
|
3836
|
-
policyViolationsPath:
|
|
3943
|
+
snapshotPath: path32.join(baseDir, `${project}.json`),
|
|
3944
|
+
errorsPath: path32.join(baseDir, `errors.${project}.ndjson`),
|
|
3945
|
+
staleEventsPath: path32.join(baseDir, `stale-events.${project}.ndjson`),
|
|
3946
|
+
embeddingsCachePath: path32.join(baseDir, `embeddings.${project}.json`),
|
|
3947
|
+
policyViolationsPath: path32.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
3837
3948
|
};
|
|
3838
3949
|
}
|
|
3839
3950
|
var Projects = class {
|
|
@@ -3874,7 +3985,7 @@ function parseExtraProjects(raw) {
|
|
|
3874
3985
|
// src/registry.ts
|
|
3875
3986
|
import { promises as fs19 } from "fs";
|
|
3876
3987
|
import os2 from "os";
|
|
3877
|
-
import
|
|
3988
|
+
import path33 from "path";
|
|
3878
3989
|
import {
|
|
3879
3990
|
RegistryFileSchema
|
|
3880
3991
|
} from "@neat.is/types";
|
|
@@ -3882,17 +3993,17 @@ var LOCK_TIMEOUT_MS = 5e3;
|
|
|
3882
3993
|
var LOCK_RETRY_MS = 50;
|
|
3883
3994
|
function neatHome() {
|
|
3884
3995
|
const override = process.env.NEAT_HOME;
|
|
3885
|
-
if (override && override.length > 0) return
|
|
3886
|
-
return
|
|
3996
|
+
if (override && override.length > 0) return path33.resolve(override);
|
|
3997
|
+
return path33.join(os2.homedir(), ".neat");
|
|
3887
3998
|
}
|
|
3888
3999
|
function registryPath() {
|
|
3889
|
-
return
|
|
4000
|
+
return path33.join(neatHome(), "projects.json");
|
|
3890
4001
|
}
|
|
3891
4002
|
function registryLockPath() {
|
|
3892
|
-
return
|
|
4003
|
+
return path33.join(neatHome(), "projects.json.lock");
|
|
3893
4004
|
}
|
|
3894
4005
|
async function normalizeProjectPath(input) {
|
|
3895
|
-
const resolved =
|
|
4006
|
+
const resolved = path33.resolve(input);
|
|
3896
4007
|
try {
|
|
3897
4008
|
return await fs19.realpath(resolved);
|
|
3898
4009
|
} catch {
|
|
@@ -3900,7 +4011,7 @@ async function normalizeProjectPath(input) {
|
|
|
3900
4011
|
}
|
|
3901
4012
|
}
|
|
3902
4013
|
async function writeAtomically(target, contents) {
|
|
3903
|
-
await fs19.mkdir(
|
|
4014
|
+
await fs19.mkdir(path33.dirname(target), { recursive: true });
|
|
3904
4015
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
3905
4016
|
const fd = await fs19.open(tmp, "w");
|
|
3906
4017
|
try {
|
|
@@ -3913,7 +4024,7 @@ async function writeAtomically(target, contents) {
|
|
|
3913
4024
|
}
|
|
3914
4025
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS) {
|
|
3915
4026
|
const deadline = Date.now() + timeoutMs;
|
|
3916
|
-
await fs19.mkdir(
|
|
4027
|
+
await fs19.mkdir(path33.dirname(lockPath), { recursive: true });
|
|
3917
4028
|
while (true) {
|
|
3918
4029
|
try {
|
|
3919
4030
|
const fd = await fs19.open(lockPath, "wx");
|
|
@@ -4089,14 +4200,6 @@ function nodeIsFrontier(graph, nodeId) {
|
|
|
4089
4200
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
4090
4201
|
return attrs.type === NodeType10.FrontierNode;
|
|
4091
4202
|
}
|
|
4092
|
-
function hasAnyObservedFromSource(graph, sourceId) {
|
|
4093
|
-
if (!graph.hasNode(sourceId)) return false;
|
|
4094
|
-
for (const edgeId of graph.outboundEdges(sourceId)) {
|
|
4095
|
-
const e = graph.getEdgeAttributes(edgeId);
|
|
4096
|
-
if (e.provenance === Provenance11.OBSERVED) return true;
|
|
4097
|
-
}
|
|
4098
|
-
return false;
|
|
4099
|
-
}
|
|
4100
4203
|
function clampConfidence(n) {
|
|
4101
4204
|
if (!Number.isFinite(n)) return 0;
|
|
4102
4205
|
return Math.max(0, Math.min(1, n));
|
|
@@ -4110,32 +4213,34 @@ function reasonForMissingExtracted(source, target, type) {
|
|
|
4110
4213
|
var RECOMMENDATION_MISSING_OBSERVED = "Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.";
|
|
4111
4214
|
var RECOMMENDATION_MISSING_EXTRACTED = "Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.";
|
|
4112
4215
|
var RECOMMENDATION_HOST_MISMATCH = "Check environment-specific config overrides \u2014 the runtime host differs from what static configuration declares.";
|
|
4216
|
+
function gradedConfidence(edge) {
|
|
4217
|
+
if (typeof edge.confidence === "number") return clampConfidence(edge.confidence);
|
|
4218
|
+
return clampConfidence(confidenceForEdge(edge));
|
|
4219
|
+
}
|
|
4113
4220
|
function detectMissingDivergences(graph, bucket) {
|
|
4114
4221
|
const out = [];
|
|
4115
4222
|
if (bucket.extracted && !bucket.observed) {
|
|
4116
4223
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
4117
|
-
const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source);
|
|
4118
4224
|
out.push({
|
|
4119
4225
|
type: "missing-observed",
|
|
4120
4226
|
source: bucket.source,
|
|
4121
4227
|
target: bucket.target,
|
|
4122
4228
|
edgeType: bucket.type,
|
|
4123
4229
|
extracted: bucket.extracted,
|
|
4124
|
-
confidence:
|
|
4230
|
+
confidence: gradedConfidence(bucket.extracted),
|
|
4125
4231
|
reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),
|
|
4126
4232
|
recommendation: RECOMMENDATION_MISSING_OBSERVED
|
|
4127
4233
|
});
|
|
4128
4234
|
}
|
|
4129
4235
|
}
|
|
4130
4236
|
if (bucket.observed && !bucket.extracted) {
|
|
4131
|
-
const cascaded = clampConfidence(confidenceForEdge(bucket.observed));
|
|
4132
4237
|
out.push({
|
|
4133
4238
|
type: "missing-extracted",
|
|
4134
4239
|
source: bucket.source,
|
|
4135
4240
|
target: bucket.target,
|
|
4136
4241
|
edgeType: bucket.type,
|
|
4137
4242
|
observed: bucket.observed,
|
|
4138
|
-
confidence:
|
|
4243
|
+
confidence: gradedConfidence(bucket.observed),
|
|
4139
4244
|
reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),
|
|
4140
4245
|
recommendation: RECOMMENDATION_MISSING_EXTRACTED
|
|
4141
4246
|
});
|
|
@@ -4274,8 +4379,17 @@ function computeDivergences(graph, opts = {}) {
|
|
|
4274
4379
|
const target = opts.node;
|
|
4275
4380
|
filtered = filtered.filter((d) => involvesNode(d, target));
|
|
4276
4381
|
}
|
|
4382
|
+
const TYPE_LEADERSHIP = {
|
|
4383
|
+
"missing-extracted": 0,
|
|
4384
|
+
"missing-observed": 1,
|
|
4385
|
+
"version-mismatch": 2,
|
|
4386
|
+
"host-mismatch": 3,
|
|
4387
|
+
"compat-violation": 4
|
|
4388
|
+
};
|
|
4277
4389
|
filtered.sort((a, b) => {
|
|
4278
4390
|
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
4391
|
+
const lead = TYPE_LEADERSHIP[a.type] - TYPE_LEADERSHIP[b.type];
|
|
4392
|
+
if (lead !== 0) return lead;
|
|
4279
4393
|
if (a.type !== b.type) return a.type.localeCompare(b.type);
|
|
4280
4394
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
4281
4395
|
return a.target.localeCompare(b.target);
|
|
@@ -4795,6 +4909,7 @@ export {
|
|
|
4795
4909
|
readErrorEvents,
|
|
4796
4910
|
isStrictExtractionEnabled,
|
|
4797
4911
|
formatExtractionBanner,
|
|
4912
|
+
formatPrecisionFloorBanner,
|
|
4798
4913
|
discoverServices,
|
|
4799
4914
|
addServiceNodes,
|
|
4800
4915
|
addServiceAliases,
|
|
@@ -4826,4 +4941,4 @@ export {
|
|
|
4826
4941
|
removeProject,
|
|
4827
4942
|
buildApi
|
|
4828
4943
|
};
|
|
4829
|
-
//# sourceMappingURL=chunk-
|
|
4944
|
+
//# sourceMappingURL=chunk-33ZZ2ZID.js.map
|