@neat.is/core 0.6.1 → 0.6.2-dev.20260723
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-BZ3AJVAC.js → chunk-2MAGU6RB.js} +11 -2
- package/dist/chunk-2MAGU6RB.js.map +1 -0
- package/dist/{chunk-3YUNROLT.js → chunk-64JJOXES.js} +3 -3
- package/dist/{chunk-MVINCLQM.js → chunk-OY7FGJAX.js} +2 -2
- package/dist/{chunk-7C6ZULNU.js → chunk-ZZ3VUCWL.js} +1492 -1346
- package/dist/chunk-ZZ3VUCWL.js.map +1 -0
- package/dist/cli.cjs +783 -468
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +170 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +466 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +475 -319
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-XS45HBET.js → otel-grpc-AHNPM5LX.js} +3 -3
- package/dist/server.cjs +2400 -2240
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-7C6ZULNU.js.map +0 -1
- package/dist/chunk-BZ3AJVAC.js.map +0 -1
- /package/dist/{chunk-3YUNROLT.js.map → chunk-64JJOXES.js.map} +0 -0
- /package/dist/{chunk-MVINCLQM.js.map → chunk-OY7FGJAX.js.map} +0 -0
- /package/dist/{otel-grpc-XS45HBET.js.map → otel-grpc-AHNPM5LX.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
|
|
|
60
60
|
]);
|
|
61
61
|
const publicRead = opts.publicRead === true;
|
|
62
62
|
app.addHook("preHandler", (req, reply, done) => {
|
|
63
|
-
const
|
|
64
|
-
if (exactUnauthPaths.has(
|
|
63
|
+
const path53 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
64
|
+
if (exactUnauthPaths.has(path53) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path53)) {
|
|
65
65
|
done();
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
function resolveProtoRoot() {
|
|
196
|
-
const here =
|
|
197
|
-
return
|
|
196
|
+
const here = import_node_path49.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
197
|
+
return import_node_path49.default.resolve(here, "..", "proto");
|
|
198
198
|
}
|
|
199
199
|
function loadTraceService() {
|
|
200
200
|
const protoRoot = resolveProtoRoot();
|
|
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
262
262
|
})
|
|
263
263
|
};
|
|
264
264
|
}
|
|
265
|
-
var import_node_url,
|
|
265
|
+
var import_node_url, import_node_path49, import_node_crypto2, grpc, protoLoader;
|
|
266
266
|
var init_otel_grpc = __esm({
|
|
267
267
|
"src/otel-grpc.ts"() {
|
|
268
268
|
"use strict";
|
|
269
269
|
init_cjs_shims();
|
|
270
270
|
import_node_url = require("url");
|
|
271
|
-
|
|
271
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
272
272
|
import_node_crypto2 = require("crypto");
|
|
273
273
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
274
274
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -346,6 +346,14 @@ function normalizeDbSystem(attrs) {
|
|
|
346
346
|
if (typeof raw !== "string") return void 0;
|
|
347
347
|
return raw === "mongoose" ? "mongodb" : raw;
|
|
348
348
|
}
|
|
349
|
+
function tableFromSqlStatement(sql) {
|
|
350
|
+
if (typeof sql !== "string" || sql.length === 0) return null;
|
|
351
|
+
if (/\bjoin\b/i.test(sql)) return null;
|
|
352
|
+
const froms = sql.match(/\bfrom\b/gi);
|
|
353
|
+
if (froms && froms.length > 1) return null;
|
|
354
|
+
const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
|
|
355
|
+
return m ? m[1] : null;
|
|
356
|
+
}
|
|
349
357
|
function messagingDestinationOf(attrs) {
|
|
350
358
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
351
359
|
const v = attrs[key];
|
|
@@ -366,8 +374,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
366
374
|
const v = attrs[key];
|
|
367
375
|
if (typeof v === "string" && v.length > 0) {
|
|
368
376
|
const q = v.indexOf("?");
|
|
369
|
-
const
|
|
370
|
-
if (
|
|
377
|
+
const path53 = q === -1 ? v : v.slice(0, q);
|
|
378
|
+
if (path53.length > 0) return path53;
|
|
371
379
|
}
|
|
372
380
|
}
|
|
373
381
|
return void 0;
|
|
@@ -403,6 +411,7 @@ function parseOtlpRequest(body) {
|
|
|
403
411
|
dbSystem: normalizeDbSystem(attrs),
|
|
404
412
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
405
413
|
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
414
|
+
dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
|
|
406
415
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
407
416
|
messagingDestination: messagingDestinationOf(attrs),
|
|
408
417
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -422,10 +431,10 @@ function parseOtlpRequest(body) {
|
|
|
422
431
|
return out;
|
|
423
432
|
}
|
|
424
433
|
function loadProtoRoot() {
|
|
425
|
-
const here =
|
|
426
|
-
const protoRoot =
|
|
434
|
+
const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
435
|
+
const protoRoot = import_node_path50.default.resolve(here, "..", "proto");
|
|
427
436
|
const root = new import_protobufjs.default.Root();
|
|
428
|
-
root.resolvePath = (_origin, target) =>
|
|
437
|
+
root.resolvePath = (_origin, target) => import_node_path50.default.resolve(protoRoot, target);
|
|
429
438
|
root.loadSync(
|
|
430
439
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
431
440
|
{ keepCase: true }
|
|
@@ -661,12 +670,12 @@ function logSpanHandler(span) {
|
|
|
661
670
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
662
671
|
);
|
|
663
672
|
}
|
|
664
|
-
var
|
|
673
|
+
var import_node_path50, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
665
674
|
var init_otel = __esm({
|
|
666
675
|
"src/otel.ts"() {
|
|
667
676
|
"use strict";
|
|
668
677
|
init_cjs_shims();
|
|
669
|
-
|
|
678
|
+
import_node_path50 = __toESM(require("path"), 1);
|
|
670
679
|
import_node_url2 = require("url");
|
|
671
680
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
672
681
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -1256,19 +1265,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1256
1265
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1257
1266
|
let best = { path: [start], edges: [] };
|
|
1258
1267
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1259
|
-
function step(node,
|
|
1260
|
-
if (
|
|
1261
|
-
best = { path: [...
|
|
1268
|
+
function step(node, path53, edges) {
|
|
1269
|
+
if (path53.length > best.path.length) {
|
|
1270
|
+
best = { path: [...path53], edges: [...edges] };
|
|
1262
1271
|
}
|
|
1263
|
-
if (
|
|
1272
|
+
if (path53.length - 1 >= maxDepth) return;
|
|
1264
1273
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1265
1274
|
for (const [srcId, edge] of incoming) {
|
|
1266
1275
|
if (visited.has(srcId)) continue;
|
|
1267
1276
|
visited.add(srcId);
|
|
1268
|
-
|
|
1277
|
+
path53.push(srcId);
|
|
1269
1278
|
edges.push(edge);
|
|
1270
|
-
step(srcId,
|
|
1271
|
-
|
|
1279
|
+
step(srcId, path53, edges);
|
|
1280
|
+
path53.pop();
|
|
1272
1281
|
edges.pop();
|
|
1273
1282
|
visited.delete(srcId);
|
|
1274
1283
|
}
|
|
@@ -1276,11 +1285,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1276
1285
|
step(start, [start], []);
|
|
1277
1286
|
return best;
|
|
1278
1287
|
}
|
|
1279
|
-
function databaseRootCauseShape(graph, origin,
|
|
1288
|
+
function databaseRootCauseShape(graph, origin, walk4) {
|
|
1280
1289
|
const targetDb = origin;
|
|
1281
1290
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1282
1291
|
if (candidatePairs.length === 0) return null;
|
|
1283
|
-
for (const id of
|
|
1292
|
+
for (const id of walk4.path) {
|
|
1284
1293
|
const owner = resolveOwningService(graph, id);
|
|
1285
1294
|
if (!owner) continue;
|
|
1286
1295
|
const { id: serviceId6, svc } = owner;
|
|
@@ -1307,8 +1316,8 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1307
1316
|
}
|
|
1308
1317
|
return null;
|
|
1309
1318
|
}
|
|
1310
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1311
|
-
for (const id of
|
|
1319
|
+
function serviceRootCauseShape(graph, _origin, walk4) {
|
|
1320
|
+
for (const id of walk4.path) {
|
|
1312
1321
|
const owner = resolveOwningService(graph, id);
|
|
1313
1322
|
if (!owner) continue;
|
|
1314
1323
|
const { id: serviceId6, svc } = owner;
|
|
@@ -1344,10 +1353,10 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1344
1353
|
}
|
|
1345
1354
|
return null;
|
|
1346
1355
|
}
|
|
1347
|
-
function fileRootCauseShape(graph, origin,
|
|
1356
|
+
function fileRootCauseShape(graph, origin, walk4) {
|
|
1348
1357
|
const owner = resolveOwningService(graph, origin.id);
|
|
1349
1358
|
if (!owner) return null;
|
|
1350
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1359
|
+
return serviceRootCauseShape(graph, owner.svc, walk4);
|
|
1351
1360
|
}
|
|
1352
1361
|
var rootCauseShapes = {
|
|
1353
1362
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1359,16 +1368,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1359
1368
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1360
1369
|
const shape = rootCauseShapes[origin.type];
|
|
1361
1370
|
if (shape) {
|
|
1362
|
-
const
|
|
1363
|
-
const match = shape(graph, origin,
|
|
1371
|
+
const walk4 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1372
|
+
const match = shape(graph, origin, walk4);
|
|
1364
1373
|
if (match) {
|
|
1365
1374
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1366
1375
|
return import_types.RootCauseResultSchema.parse({
|
|
1367
1376
|
rootCauseNode: match.rootCauseNode,
|
|
1368
1377
|
rootCauseReason: reason,
|
|
1369
|
-
traversalPath:
|
|
1370
|
-
edgeProvenances:
|
|
1371
|
-
confidence: confidenceFromMix(
|
|
1378
|
+
traversalPath: walk4.path,
|
|
1379
|
+
edgeProvenances: walk4.edges.map((e) => e.provenance),
|
|
1380
|
+
confidence: confidenceFromMix(walk4.edges),
|
|
1372
1381
|
fixRecommendation: match.fixRecommendation
|
|
1373
1382
|
});
|
|
1374
1383
|
}
|
|
@@ -1389,8 +1398,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1389
1398
|
if (relevant.length === 0) return null;
|
|
1390
1399
|
const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
1391
1400
|
const attrs = latest.attributes ?? {};
|
|
1392
|
-
const filepath =
|
|
1393
|
-
const lineno =
|
|
1401
|
+
const filepath = codeFilepathOf(attrs);
|
|
1402
|
+
const lineno = codeLinenoOf(attrs);
|
|
1394
1403
|
const route = typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0;
|
|
1395
1404
|
const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
|
|
1396
1405
|
const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage);
|
|
@@ -1462,26 +1471,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1462
1471
|
return best;
|
|
1463
1472
|
}
|
|
1464
1473
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1465
|
-
const
|
|
1474
|
+
const path53 = [originServiceId];
|
|
1466
1475
|
const edges = [];
|
|
1467
1476
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1468
1477
|
let current = originServiceId;
|
|
1469
1478
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1470
1479
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1471
1480
|
if (!hop) break;
|
|
1472
|
-
|
|
1481
|
+
path53.push(hop.nextService);
|
|
1473
1482
|
edges.push(hop.edge);
|
|
1474
1483
|
visited.add(hop.nextService);
|
|
1475
1484
|
current = hop.nextService;
|
|
1476
1485
|
}
|
|
1477
1486
|
if (edges.length === 0) return null;
|
|
1478
|
-
return { path:
|
|
1487
|
+
return { path: path53, edges, culprit: current };
|
|
1479
1488
|
}
|
|
1480
1489
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1481
1490
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1482
1491
|
if (!chain) return null;
|
|
1483
1492
|
const culprit = chain.culprit;
|
|
1484
|
-
const
|
|
1493
|
+
const path53 = [...chain.path];
|
|
1485
1494
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1486
1495
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1487
1496
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1489,14 +1498,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1489
1498
|
if (loc) {
|
|
1490
1499
|
let rootCauseNode = culprit;
|
|
1491
1500
|
if (loc.fileNode) {
|
|
1492
|
-
|
|
1501
|
+
path53.push(loc.fileNode);
|
|
1493
1502
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1494
1503
|
rootCauseNode = loc.fileNode;
|
|
1495
1504
|
}
|
|
1496
1505
|
return import_types.RootCauseResultSchema.parse({
|
|
1497
1506
|
rootCauseNode,
|
|
1498
1507
|
rootCauseReason: loc.rootCauseReason,
|
|
1499
|
-
traversalPath:
|
|
1508
|
+
traversalPath: path53,
|
|
1500
1509
|
edgeProvenances,
|
|
1501
1510
|
confidence,
|
|
1502
1511
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1508,7 +1517,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1508
1517
|
return import_types.RootCauseResultSchema.parse({
|
|
1509
1518
|
rootCauseNode: culprit,
|
|
1510
1519
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1511
|
-
traversalPath:
|
|
1520
|
+
traversalPath: path53,
|
|
1512
1521
|
edgeProvenances,
|
|
1513
1522
|
confidence,
|
|
1514
1523
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2266,9 +2275,30 @@ function isLoopbackHost(host) {
|
|
|
2266
2275
|
const h = host.toLowerCase();
|
|
2267
2276
|
return h === "localhost" || h === "ip6-localhost" || h === "::1" || h === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(h);
|
|
2268
2277
|
}
|
|
2278
|
+
var CODE_FILE_PATH_ATTR = "code.file.path";
|
|
2269
2279
|
var CODE_FILEPATH_ATTR = "code.filepath";
|
|
2280
|
+
var CODE_LINE_NUMBER_ATTR = "code.line.number";
|
|
2270
2281
|
var CODE_LINENO_ATTR = "code.lineno";
|
|
2282
|
+
var CODE_FUNCTION_NAME_ATTR = "code.function.name";
|
|
2271
2283
|
var CODE_FUNCTION_ATTR = "code.function";
|
|
2284
|
+
function codeFilepathOf(attrs) {
|
|
2285
|
+
const stable = attrs[CODE_FILE_PATH_ATTR];
|
|
2286
|
+
if (typeof stable === "string" && stable.length > 0) return stable;
|
|
2287
|
+
const prior = attrs[CODE_FILEPATH_ATTR];
|
|
2288
|
+
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
2289
|
+
}
|
|
2290
|
+
function codeLinenoOf(attrs) {
|
|
2291
|
+
const stable = attrs[CODE_LINE_NUMBER_ATTR];
|
|
2292
|
+
if (typeof stable === "number" && Number.isFinite(stable)) return stable;
|
|
2293
|
+
const prior = attrs[CODE_LINENO_ATTR];
|
|
2294
|
+
return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
|
|
2295
|
+
}
|
|
2296
|
+
function codeFunctionOf(attrs) {
|
|
2297
|
+
const stable = attrs[CODE_FUNCTION_NAME_ATTR];
|
|
2298
|
+
if (typeof stable === "string" && stable.length > 0) return stable;
|
|
2299
|
+
const prior = attrs[CODE_FUNCTION_ATTR];
|
|
2300
|
+
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
2301
|
+
}
|
|
2272
2302
|
function toPosix(p) {
|
|
2273
2303
|
return p.split("\\").join("/");
|
|
2274
2304
|
}
|
|
@@ -2346,10 +2376,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
2346
2376
|
}
|
|
2347
2377
|
}
|
|
2348
2378
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
2349
|
-
const filepath = span.attributes
|
|
2350
|
-
if (
|
|
2351
|
-
|
|
2352
|
-
let line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
|
|
2379
|
+
const filepath = codeFilepathOf(span.attributes);
|
|
2380
|
+
if (filepath === void 0) return null;
|
|
2381
|
+
let line = codeLinenoOf(span.attributes);
|
|
2353
2382
|
const abs = toPosix(filepath).replace(/^file:\/\//, "");
|
|
2354
2383
|
const resolved = resolveDistToSrc(abs, line);
|
|
2355
2384
|
let effectivePath = filepath;
|
|
@@ -2364,8 +2393,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
2364
2393
|
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
2365
2394
|
warnNoSourceMaps(serviceNode.name);
|
|
2366
2395
|
}
|
|
2367
|
-
const
|
|
2368
|
-
const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
|
|
2396
|
+
const fn = codeFunctionOf(span.attributes);
|
|
2369
2397
|
return {
|
|
2370
2398
|
relPath,
|
|
2371
2399
|
...line !== void 0 ? { line } : {},
|
|
@@ -2947,6 +2975,18 @@ async function handleSpan(ctx, span) {
|
|
|
2947
2975
|
callSiteEvidence
|
|
2948
2976
|
);
|
|
2949
2977
|
}
|
|
2978
|
+
if (span.dbTable) {
|
|
2979
|
+
const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
|
|
2980
|
+
upsertObservedEdge(
|
|
2981
|
+
ctx.graph,
|
|
2982
|
+
import_types3.EdgeType.CALLS,
|
|
2983
|
+
observedSource(),
|
|
2984
|
+
tableId,
|
|
2985
|
+
ts,
|
|
2986
|
+
isError,
|
|
2987
|
+
callSiteEvidence
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2950
2990
|
}
|
|
2951
2991
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2952
2992
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -4114,14 +4154,14 @@ function buildServiceHostIndex(services) {
|
|
|
4114
4154
|
}
|
|
4115
4155
|
async function walkSourceFiles(dir) {
|
|
4116
4156
|
const out = [];
|
|
4117
|
-
async function
|
|
4157
|
+
async function walk4(current) {
|
|
4118
4158
|
const entries = await import_node_fs10.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
4119
4159
|
for (const entry of entries) {
|
|
4120
4160
|
const full = import_node_path10.default.join(current, entry.name);
|
|
4121
4161
|
if (entry.isDirectory()) {
|
|
4122
4162
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4123
4163
|
if (await isPythonVenvDir(full)) continue;
|
|
4124
|
-
await
|
|
4164
|
+
await walk4(full);
|
|
4125
4165
|
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path10.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
4126
4166
|
// would attribute our instrumentation imports to the user's service.
|
|
4127
4167
|
!isNeatAuthoredSourceFile(entry.name)) {
|
|
@@ -4129,7 +4169,7 @@ async function walkSourceFiles(dir) {
|
|
|
4129
4169
|
}
|
|
4130
4170
|
}
|
|
4131
4171
|
}
|
|
4132
|
-
await
|
|
4172
|
+
await walk4(dir);
|
|
4133
4173
|
return out;
|
|
4134
4174
|
}
|
|
4135
4175
|
async function loadSourceFiles(dir) {
|
|
@@ -5279,20 +5319,20 @@ var import_node_path21 = __toESM(require("path"), 1);
|
|
|
5279
5319
|
var import_types10 = require("@neat.is/types");
|
|
5280
5320
|
async function walkConfigFiles(dir) {
|
|
5281
5321
|
const out = [];
|
|
5282
|
-
async function
|
|
5322
|
+
async function walk4(current) {
|
|
5283
5323
|
const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
|
|
5284
5324
|
for (const entry of entries) {
|
|
5285
5325
|
const full = import_node_path21.default.join(current, entry.name);
|
|
5286
5326
|
if (entry.isDirectory()) {
|
|
5287
5327
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
5288
5328
|
if (await isPythonVenvDir(full)) continue;
|
|
5289
|
-
await
|
|
5329
|
+
await walk4(full);
|
|
5290
5330
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
5291
5331
|
out.push(full);
|
|
5292
5332
|
}
|
|
5293
5333
|
}
|
|
5294
5334
|
}
|
|
5295
|
-
await
|
|
5335
|
+
await walk4(dir);
|
|
5296
5336
|
return out;
|
|
5297
5337
|
}
|
|
5298
5338
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -5819,20 +5859,20 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
5819
5859
|
}
|
|
5820
5860
|
async function walkProtoFiles(dir) {
|
|
5821
5861
|
const out = [];
|
|
5822
|
-
async function
|
|
5862
|
+
async function walk4(current) {
|
|
5823
5863
|
const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
5824
5864
|
for (const entry of entries) {
|
|
5825
5865
|
const full = import_node_path23.default.join(current, entry.name);
|
|
5826
5866
|
if (entry.isDirectory()) {
|
|
5827
5867
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
5828
5868
|
if (await isPythonVenvDir(full)) continue;
|
|
5829
|
-
await
|
|
5869
|
+
await walk4(full);
|
|
5830
5870
|
} else if (entry.isFile() && import_node_path23.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
5831
5871
|
out.push(full);
|
|
5832
5872
|
}
|
|
5833
5873
|
}
|
|
5834
5874
|
}
|
|
5835
|
-
await
|
|
5875
|
+
await walk4(dir);
|
|
5836
5876
|
return out;
|
|
5837
5877
|
}
|
|
5838
5878
|
async function addGrpcMethods(graph, services) {
|
|
@@ -5900,7 +5940,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5900
5940
|
|
|
5901
5941
|
// src/extract/calls/index.ts
|
|
5902
5942
|
init_cjs_shims();
|
|
5903
|
-
var
|
|
5943
|
+
var import_types22 = require("@neat.is/types");
|
|
5904
5944
|
|
|
5905
5945
|
// src/extract/calls/http.ts
|
|
5906
5946
|
init_cjs_shims();
|
|
@@ -6909,15 +6949,130 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
6909
6949
|
return out;
|
|
6910
6950
|
}
|
|
6911
6951
|
|
|
6952
|
+
// src/extract/calls/sqlalchemy.ts
|
|
6953
|
+
init_cjs_shims();
|
|
6954
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6955
|
+
var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
|
|
6956
|
+
var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
6957
|
+
var import_types21 = require("@neat.is/types");
|
|
6958
|
+
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
6959
|
+
var PARSE_CHUNK5 = 16384;
|
|
6960
|
+
function makePyParser4() {
|
|
6961
|
+
const p = new import_tree_sitter5.default();
|
|
6962
|
+
p.setLanguage(import_tree_sitter_python4.default);
|
|
6963
|
+
return p;
|
|
6964
|
+
}
|
|
6965
|
+
function parseSource5(parser, source) {
|
|
6966
|
+
return parser.parse(
|
|
6967
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
6968
|
+
);
|
|
6969
|
+
}
|
|
6970
|
+
function flaskSqlalchemyTableName(className) {
|
|
6971
|
+
return className.replace(/((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g, "_$1").toLowerCase().replace(/^_+/, "");
|
|
6972
|
+
}
|
|
6973
|
+
function namedChildren(node) {
|
|
6974
|
+
const out = [];
|
|
6975
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6976
|
+
const c = node.namedChild(i);
|
|
6977
|
+
if (c) out.push(c);
|
|
6978
|
+
}
|
|
6979
|
+
return out;
|
|
6980
|
+
}
|
|
6981
|
+
function pyStaticStringText2(node) {
|
|
6982
|
+
if (node.type !== "string") return null;
|
|
6983
|
+
for (const child of namedChildren(node)) {
|
|
6984
|
+
if (child.type === "interpolation") return null;
|
|
6985
|
+
if (child.type === "string_content") return child.text;
|
|
6986
|
+
}
|
|
6987
|
+
return "";
|
|
6988
|
+
}
|
|
6989
|
+
function explicitTablename(body) {
|
|
6990
|
+
for (const stmt of namedChildren(body)) {
|
|
6991
|
+
if (stmt.type !== "expression_statement") continue;
|
|
6992
|
+
const assign = stmt.namedChild(0);
|
|
6993
|
+
if (assign?.type !== "assignment") continue;
|
|
6994
|
+
if (assign.childForFieldName("left")?.text !== "__tablename__") continue;
|
|
6995
|
+
const right = assign.childForFieldName("right");
|
|
6996
|
+
if (right?.type === "string") {
|
|
6997
|
+
const s = pyStaticStringText2(right);
|
|
6998
|
+
return s ? { name: s } : "computed";
|
|
6999
|
+
}
|
|
7000
|
+
return "computed";
|
|
7001
|
+
}
|
|
7002
|
+
return null;
|
|
7003
|
+
}
|
|
7004
|
+
function extendsFlaskModel(cls) {
|
|
7005
|
+
const supers = cls.childForFieldName("superclasses");
|
|
7006
|
+
if (!supers) return false;
|
|
7007
|
+
for (const a of namedChildren(supers)) {
|
|
7008
|
+
const t = a.text;
|
|
7009
|
+
if (t === "db.Model" || t === "Model" || t.endsWith(".Model")) return true;
|
|
7010
|
+
}
|
|
7011
|
+
return false;
|
|
7012
|
+
}
|
|
7013
|
+
function walk3(node, visit) {
|
|
7014
|
+
visit(node);
|
|
7015
|
+
for (const c of namedChildren(node)) walk3(c, visit);
|
|
7016
|
+
}
|
|
7017
|
+
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
7018
|
+
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
7019
|
+
const tree = parseSource5(makePyParser4(), file.content);
|
|
7020
|
+
const out = [];
|
|
7021
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7022
|
+
const push = (name, line) => {
|
|
7023
|
+
if (seen.has(name)) return;
|
|
7024
|
+
seen.add(name);
|
|
7025
|
+
out.push({
|
|
7026
|
+
infraId: (0, import_types21.infraId)("sql-table", name),
|
|
7027
|
+
name,
|
|
7028
|
+
kind: "sql-table",
|
|
7029
|
+
edgeType: "CALLS",
|
|
7030
|
+
confidenceKind: "verified-call-site",
|
|
7031
|
+
evidence: {
|
|
7032
|
+
file: import_node_path32.default.relative(serviceDir, file.path),
|
|
7033
|
+
line,
|
|
7034
|
+
snippet: snippet(file.content, line)
|
|
7035
|
+
}
|
|
7036
|
+
});
|
|
7037
|
+
};
|
|
7038
|
+
walk3(tree.rootNode, (node) => {
|
|
7039
|
+
if (node.type === "class_definition") {
|
|
7040
|
+
const body = node.childForFieldName("body");
|
|
7041
|
+
const nameNode = node.childForFieldName("name");
|
|
7042
|
+
if (!body || !nameNode) return;
|
|
7043
|
+
const line = node.startPosition.row + 1;
|
|
7044
|
+
const explicit = explicitTablename(body);
|
|
7045
|
+
if (explicit === "computed") return;
|
|
7046
|
+
if (explicit) {
|
|
7047
|
+
push(explicit.name, line);
|
|
7048
|
+
return;
|
|
7049
|
+
}
|
|
7050
|
+
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
|
|
7051
|
+
return;
|
|
7052
|
+
}
|
|
7053
|
+
if (node.type === "call") {
|
|
7054
|
+
const fn = node.childForFieldName("function");
|
|
7055
|
+
const fnText = fn?.text;
|
|
7056
|
+
if (fnText !== "Table" && fnText !== "sa.Table" && fnText !== "sqlalchemy.Table") return;
|
|
7057
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
7058
|
+
if (first?.type === "string") {
|
|
7059
|
+
const s = pyStaticStringText2(first);
|
|
7060
|
+
if (s) push(s, node.startPosition.row + 1);
|
|
7061
|
+
}
|
|
7062
|
+
}
|
|
7063
|
+
});
|
|
7064
|
+
return out;
|
|
7065
|
+
}
|
|
7066
|
+
|
|
6912
7067
|
// src/extract/calls/index.ts
|
|
6913
7068
|
function edgeTypeFromEndpoint(ep) {
|
|
6914
7069
|
switch (ep.edgeType) {
|
|
6915
7070
|
case "PUBLISHES_TO":
|
|
6916
|
-
return
|
|
7071
|
+
return import_types22.EdgeType.PUBLISHES_TO;
|
|
6917
7072
|
case "CONSUMES_FROM":
|
|
6918
|
-
return
|
|
7073
|
+
return import_types22.EdgeType.CONSUMES_FROM;
|
|
6919
7074
|
default:
|
|
6920
|
-
return
|
|
7075
|
+
return import_types22.EdgeType.CALLS;
|
|
6921
7076
|
}
|
|
6922
7077
|
}
|
|
6923
7078
|
function isAwsKind(kind) {
|
|
@@ -6941,6 +7096,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6941
7096
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6942
7097
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6943
7098
|
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
7099
|
+
endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
|
|
6944
7100
|
}
|
|
6945
7101
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6946
7102
|
if (endpoints.length === 0) continue;
|
|
@@ -6949,7 +7105,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6949
7105
|
if (!graph.hasNode(ep.infraId)) {
|
|
6950
7106
|
const node = {
|
|
6951
7107
|
id: ep.infraId,
|
|
6952
|
-
type:
|
|
7108
|
+
type: import_types22.NodeType.InfraNode,
|
|
6953
7109
|
name: ep.name,
|
|
6954
7110
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6955
7111
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6961,7 +7117,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6961
7117
|
nodesAdded++;
|
|
6962
7118
|
}
|
|
6963
7119
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6964
|
-
const confidence = (0,
|
|
7120
|
+
const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
|
|
6965
7121
|
const relFile = toPosix2(ep.evidence.file);
|
|
6966
7122
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6967
7123
|
graph,
|
|
@@ -6971,7 +7127,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6971
7127
|
);
|
|
6972
7128
|
nodesAdded += n;
|
|
6973
7129
|
edgesAdded += e;
|
|
6974
|
-
if (!(0,
|
|
7130
|
+
if (!(0, import_types22.passesExtractedFloor)(confidence)) {
|
|
6975
7131
|
noteExtractedDropped({
|
|
6976
7132
|
source: fileNodeId,
|
|
6977
7133
|
target: ep.infraId,
|
|
@@ -6991,7 +7147,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6991
7147
|
source: fileNodeId,
|
|
6992
7148
|
target: ep.infraId,
|
|
6993
7149
|
type: edgeType,
|
|
6994
|
-
provenance:
|
|
7150
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6995
7151
|
confidence,
|
|
6996
7152
|
evidence: ep.evidence
|
|
6997
7153
|
};
|
|
@@ -7017,16 +7173,16 @@ init_cjs_shims();
|
|
|
7017
7173
|
|
|
7018
7174
|
// src/extract/infra/docker-compose.ts
|
|
7019
7175
|
init_cjs_shims();
|
|
7020
|
-
var
|
|
7021
|
-
var
|
|
7176
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
7177
|
+
var import_types24 = require("@neat.is/types");
|
|
7022
7178
|
|
|
7023
7179
|
// src/extract/infra/shared.ts
|
|
7024
7180
|
init_cjs_shims();
|
|
7025
|
-
var
|
|
7181
|
+
var import_types23 = require("@neat.is/types");
|
|
7026
7182
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
7027
7183
|
return {
|
|
7028
|
-
id: (0,
|
|
7029
|
-
type:
|
|
7184
|
+
id: (0, import_types23.infraId)(kind, name),
|
|
7185
|
+
type: import_types23.NodeType.InfraNode,
|
|
7030
7186
|
name,
|
|
7031
7187
|
provider,
|
|
7032
7188
|
kind,
|
|
@@ -7070,8 +7226,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
7070
7226
|
source: anchorId,
|
|
7071
7227
|
target: node.id,
|
|
7072
7228
|
type: edgeType,
|
|
7073
|
-
provenance:
|
|
7074
|
-
confidence: (0,
|
|
7229
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7230
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
7075
7231
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7076
7232
|
};
|
|
7077
7233
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7088,7 +7244,7 @@ function dependsOnList(value) {
|
|
|
7088
7244
|
}
|
|
7089
7245
|
function serviceNameToServiceNode(name, services) {
|
|
7090
7246
|
for (const s of services) {
|
|
7091
|
-
if (s.node.name === name ||
|
|
7247
|
+
if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
|
|
7092
7248
|
}
|
|
7093
7249
|
return null;
|
|
7094
7250
|
}
|
|
@@ -7097,7 +7253,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7097
7253
|
let edgesAdded = 0;
|
|
7098
7254
|
let composePath = null;
|
|
7099
7255
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
7100
|
-
const abs =
|
|
7256
|
+
const abs = import_node_path33.default.join(scanPath, name);
|
|
7101
7257
|
if (await exists(abs)) {
|
|
7102
7258
|
composePath = abs;
|
|
7103
7259
|
break;
|
|
@@ -7110,13 +7266,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7110
7266
|
} catch (err) {
|
|
7111
7267
|
recordExtractionError(
|
|
7112
7268
|
"infra docker-compose",
|
|
7113
|
-
|
|
7269
|
+
import_node_path33.default.relative(scanPath, composePath),
|
|
7114
7270
|
err
|
|
7115
7271
|
);
|
|
7116
7272
|
return { nodesAdded, edgesAdded };
|
|
7117
7273
|
}
|
|
7118
7274
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
7119
|
-
const evidenceFile =
|
|
7275
|
+
const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
|
|
7120
7276
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
7121
7277
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
7122
7278
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -7138,15 +7294,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7138
7294
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
7139
7295
|
const targetId = composeNameToNodeId.get(dep);
|
|
7140
7296
|
if (!targetId) continue;
|
|
7141
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7297
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
|
|
7142
7298
|
if (graph.hasEdge(edgeId)) continue;
|
|
7143
7299
|
const edge = {
|
|
7144
7300
|
id: edgeId,
|
|
7145
7301
|
source: sourceId,
|
|
7146
7302
|
target: targetId,
|
|
7147
|
-
type:
|
|
7148
|
-
provenance:
|
|
7149
|
-
confidence: (0,
|
|
7303
|
+
type: import_types24.EdgeType.DEPENDS_ON,
|
|
7304
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7305
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
7150
7306
|
evidence: { file: evidenceFile }
|
|
7151
7307
|
};
|
|
7152
7308
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7158,9 +7314,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7158
7314
|
|
|
7159
7315
|
// src/extract/infra/dockerfile.ts
|
|
7160
7316
|
init_cjs_shims();
|
|
7161
|
-
var
|
|
7317
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7162
7318
|
var import_node_fs16 = require("fs");
|
|
7163
|
-
var
|
|
7319
|
+
var import_types25 = require("@neat.is/types");
|
|
7164
7320
|
function readDockerfile(content) {
|
|
7165
7321
|
let image = null;
|
|
7166
7322
|
const ports = [];
|
|
@@ -7189,7 +7345,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7189
7345
|
let nodesAdded = 0;
|
|
7190
7346
|
let edgesAdded = 0;
|
|
7191
7347
|
for (const service of services) {
|
|
7192
|
-
const dockerfilePath =
|
|
7348
|
+
const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
|
|
7193
7349
|
if (!await exists(dockerfilePath)) continue;
|
|
7194
7350
|
let content;
|
|
7195
7351
|
try {
|
|
@@ -7197,7 +7353,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7197
7353
|
} catch (err) {
|
|
7198
7354
|
recordExtractionError(
|
|
7199
7355
|
"infra dockerfile",
|
|
7200
|
-
|
|
7356
|
+
import_node_path34.default.relative(scanPath, dockerfilePath),
|
|
7201
7357
|
err
|
|
7202
7358
|
);
|
|
7203
7359
|
continue;
|
|
@@ -7209,8 +7365,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7209
7365
|
graph.addNode(node.id, node);
|
|
7210
7366
|
nodesAdded++;
|
|
7211
7367
|
}
|
|
7212
|
-
const relDockerfile = toPosix2(
|
|
7213
|
-
const evidenceFile = toPosix2(
|
|
7368
|
+
const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
|
|
7369
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
|
|
7214
7370
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7215
7371
|
graph,
|
|
7216
7372
|
service.pkg.name,
|
|
@@ -7219,15 +7375,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7219
7375
|
);
|
|
7220
7376
|
nodesAdded += fn;
|
|
7221
7377
|
edgesAdded += fe;
|
|
7222
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7378
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
|
|
7223
7379
|
if (!graph.hasEdge(edgeId)) {
|
|
7224
7380
|
const edge = {
|
|
7225
7381
|
id: edgeId,
|
|
7226
7382
|
source: fileNodeId,
|
|
7227
7383
|
target: node.id,
|
|
7228
|
-
type:
|
|
7229
|
-
provenance:
|
|
7230
|
-
confidence: (0,
|
|
7384
|
+
type: import_types25.EdgeType.RUNS_ON,
|
|
7385
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7386
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7231
7387
|
evidence: {
|
|
7232
7388
|
file: evidenceFile,
|
|
7233
7389
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -7242,15 +7398,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7242
7398
|
graph.addNode(portNode.id, portNode);
|
|
7243
7399
|
nodesAdded++;
|
|
7244
7400
|
}
|
|
7245
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7401
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
|
|
7246
7402
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
7247
7403
|
const portEdge = {
|
|
7248
7404
|
id: portEdgeId,
|
|
7249
7405
|
source: fileNodeId,
|
|
7250
7406
|
target: portNode.id,
|
|
7251
|
-
type:
|
|
7252
|
-
provenance:
|
|
7253
|
-
confidence: (0,
|
|
7407
|
+
type: import_types25.EdgeType.CONNECTS_TO,
|
|
7408
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7409
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7254
7410
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
7255
7411
|
};
|
|
7256
7412
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -7263,8 +7419,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7263
7419
|
// src/extract/infra/terraform.ts
|
|
7264
7420
|
init_cjs_shims();
|
|
7265
7421
|
var import_node_fs17 = require("fs");
|
|
7266
|
-
var
|
|
7267
|
-
var
|
|
7422
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7423
|
+
var import_types26 = require("@neat.is/types");
|
|
7268
7424
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
7269
7425
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
7270
7426
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -7274,11 +7430,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
7274
7430
|
for (const entry of entries) {
|
|
7275
7431
|
if (entry.isDirectory()) {
|
|
7276
7432
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
7277
|
-
const child =
|
|
7433
|
+
const child = import_node_path35.default.join(start, entry.name);
|
|
7278
7434
|
if (await isPythonVenvDir(child)) continue;
|
|
7279
7435
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
7280
7436
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
7281
|
-
out.push(
|
|
7437
|
+
out.push(import_node_path35.default.join(start, entry.name));
|
|
7282
7438
|
}
|
|
7283
7439
|
}
|
|
7284
7440
|
return out;
|
|
@@ -7310,7 +7466,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7310
7466
|
const files = await walkTfFiles(scanPath);
|
|
7311
7467
|
for (const file of files) {
|
|
7312
7468
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
7313
|
-
const evidenceFile = toPosix2(
|
|
7469
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
|
|
7314
7470
|
const resources = [];
|
|
7315
7471
|
const byKey = /* @__PURE__ */ new Map();
|
|
7316
7472
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -7345,16 +7501,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7345
7501
|
if (!target) continue;
|
|
7346
7502
|
if (seen.has(target.nodeId)) continue;
|
|
7347
7503
|
seen.add(target.nodeId);
|
|
7348
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7504
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
|
|
7349
7505
|
if (graph.hasEdge(edgeId)) continue;
|
|
7350
7506
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
7351
7507
|
const edge = {
|
|
7352
7508
|
id: edgeId,
|
|
7353
7509
|
source: resource.nodeId,
|
|
7354
7510
|
target: target.nodeId,
|
|
7355
|
-
type:
|
|
7356
|
-
provenance:
|
|
7357
|
-
confidence: (0,
|
|
7511
|
+
type: import_types26.EdgeType.DEPENDS_ON,
|
|
7512
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7513
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7358
7514
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
7359
7515
|
};
|
|
7360
7516
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7368,7 +7524,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7368
7524
|
// src/extract/infra/k8s.ts
|
|
7369
7525
|
init_cjs_shims();
|
|
7370
7526
|
var import_node_fs18 = require("fs");
|
|
7371
|
-
var
|
|
7527
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
7372
7528
|
var import_yaml3 = require("yaml");
|
|
7373
7529
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
7374
7530
|
Service: "k8s-service",
|
|
@@ -7386,11 +7542,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
7386
7542
|
for (const entry of entries) {
|
|
7387
7543
|
if (entry.isDirectory()) {
|
|
7388
7544
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7389
|
-
const child =
|
|
7545
|
+
const child = import_node_path36.default.join(start, entry.name);
|
|
7390
7546
|
if (await isPythonVenvDir(child)) continue;
|
|
7391
7547
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
7392
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
7393
|
-
out.push(
|
|
7548
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry.name))) {
|
|
7549
|
+
out.push(import_node_path36.default.join(start, entry.name));
|
|
7394
7550
|
}
|
|
7395
7551
|
}
|
|
7396
7552
|
return out;
|
|
@@ -7424,13 +7580,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7424
7580
|
// src/extract/infra/cloudflare.ts
|
|
7425
7581
|
init_cjs_shims();
|
|
7426
7582
|
var import_node_fs19 = require("fs");
|
|
7427
|
-
var
|
|
7583
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7428
7584
|
var import_smol_toml2 = require("smol-toml");
|
|
7429
|
-
var
|
|
7585
|
+
var import_types27 = require("@neat.is/types");
|
|
7430
7586
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7431
7587
|
async function readWranglerConfig(dir) {
|
|
7432
7588
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7433
|
-
const abs =
|
|
7589
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7434
7590
|
if (!await exists(abs)) continue;
|
|
7435
7591
|
const raw = await import_node_fs19.promises.readFile(abs, "utf8");
|
|
7436
7592
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7474,8 +7630,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7474
7630
|
source: anchorId,
|
|
7475
7631
|
target: node.id,
|
|
7476
7632
|
type: edgeType,
|
|
7477
|
-
provenance:
|
|
7478
|
-
confidence: (0,
|
|
7633
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7634
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7479
7635
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7480
7636
|
};
|
|
7481
7637
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7493,11 +7649,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7493
7649
|
try {
|
|
7494
7650
|
read = await readWranglerConfig(service.dir);
|
|
7495
7651
|
} catch (err) {
|
|
7496
|
-
recordExtractionError("infra cloudflare",
|
|
7652
|
+
recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7497
7653
|
continue;
|
|
7498
7654
|
}
|
|
7499
7655
|
if (!read || !read.config.name) continue;
|
|
7500
|
-
const evidenceFile = toPosix2(
|
|
7656
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
|
|
7501
7657
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7502
7658
|
}
|
|
7503
7659
|
for (const worker of discovered) {
|
|
@@ -7509,7 +7665,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7509
7665
|
}
|
|
7510
7666
|
let anchorId = service.node.id;
|
|
7511
7667
|
if (config.main) {
|
|
7512
|
-
const entryRelPath = toPosix2(
|
|
7668
|
+
const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
|
|
7513
7669
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7514
7670
|
graph,
|
|
7515
7671
|
service.pkg.name,
|
|
@@ -7536,15 +7692,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7536
7692
|
nodesAdded++;
|
|
7537
7693
|
}
|
|
7538
7694
|
if (runtimeNode.id !== anchorId) {
|
|
7539
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7695
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
|
|
7540
7696
|
if (!graph.hasEdge(runsOnId)) {
|
|
7541
7697
|
const edge = {
|
|
7542
7698
|
id: runsOnId,
|
|
7543
7699
|
source: anchorId,
|
|
7544
7700
|
target: runtimeNode.id,
|
|
7545
|
-
type:
|
|
7546
|
-
provenance:
|
|
7547
|
-
confidence: (0,
|
|
7701
|
+
type: import_types27.EdgeType.RUNS_ON,
|
|
7702
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7703
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7548
7704
|
evidence: {
|
|
7549
7705
|
file: evidenceFile,
|
|
7550
7706
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7558,7 +7714,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7558
7714
|
const result = addResourceEdge(
|
|
7559
7715
|
graph,
|
|
7560
7716
|
anchorId,
|
|
7561
|
-
|
|
7717
|
+
import_types27.EdgeType.CONNECTS_TO,
|
|
7562
7718
|
"cloudflare-route",
|
|
7563
7719
|
route,
|
|
7564
7720
|
evidenceFile,
|
|
@@ -7582,7 +7738,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7582
7738
|
const result = addResourceEdge(
|
|
7583
7739
|
graph,
|
|
7584
7740
|
anchorId,
|
|
7585
|
-
|
|
7741
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7586
7742
|
group.kind,
|
|
7587
7743
|
name,
|
|
7588
7744
|
evidenceFile,
|
|
@@ -7596,7 +7752,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7596
7752
|
const result = addResourceEdge(
|
|
7597
7753
|
graph,
|
|
7598
7754
|
anchorId,
|
|
7599
|
-
|
|
7755
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7600
7756
|
"cloudflare-cron",
|
|
7601
7757
|
cron,
|
|
7602
7758
|
evidenceFile,
|
|
@@ -7609,7 +7765,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7609
7765
|
const result = addResourceEdge(
|
|
7610
7766
|
graph,
|
|
7611
7767
|
anchorId,
|
|
7612
|
-
|
|
7768
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7613
7769
|
"cloudflare-env-var",
|
|
7614
7770
|
varName,
|
|
7615
7771
|
evidenceFile,
|
|
@@ -7622,15 +7778,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7622
7778
|
if (!svc.service) continue;
|
|
7623
7779
|
const target = workerIndex.get(svc.service);
|
|
7624
7780
|
if (target && target.anchorId !== anchorId) {
|
|
7625
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7781
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
|
|
7626
7782
|
if (!graph.hasEdge(edgeId)) {
|
|
7627
7783
|
const edge = {
|
|
7628
7784
|
id: edgeId,
|
|
7629
7785
|
source: anchorId,
|
|
7630
7786
|
target: target.anchorId,
|
|
7631
|
-
type:
|
|
7632
|
-
provenance:
|
|
7633
|
-
confidence: (0,
|
|
7787
|
+
type: import_types27.EdgeType.CALLS,
|
|
7788
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7789
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7634
7790
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7635
7791
|
};
|
|
7636
7792
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7641,7 +7797,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7641
7797
|
const result = addResourceEdge(
|
|
7642
7798
|
graph,
|
|
7643
7799
|
anchorId,
|
|
7644
|
-
|
|
7800
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7645
7801
|
"cloudflare-service-binding",
|
|
7646
7802
|
svc.service,
|
|
7647
7803
|
evidenceFile,
|
|
@@ -7657,12 +7813,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7657
7813
|
// src/extract/infra/vercel.ts
|
|
7658
7814
|
init_cjs_shims();
|
|
7659
7815
|
var import_node_fs20 = require("fs");
|
|
7660
|
-
var
|
|
7661
|
-
var
|
|
7816
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7817
|
+
var import_types28 = require("@neat.is/types");
|
|
7662
7818
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7663
7819
|
async function readVercelConfig(dir) {
|
|
7664
7820
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7665
|
-
const abs =
|
|
7821
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7666
7822
|
if (!await exists(abs)) continue;
|
|
7667
7823
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7668
7824
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7671,7 +7827,7 @@ async function readVercelConfig(dir) {
|
|
|
7671
7827
|
return null;
|
|
7672
7828
|
}
|
|
7673
7829
|
async function readLinkedProjectName(dir) {
|
|
7674
|
-
const abs =
|
|
7830
|
+
const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
|
|
7675
7831
|
if (!await exists(abs)) return void 0;
|
|
7676
7832
|
const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
|
|
7677
7833
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7689,7 +7845,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7689
7845
|
read = await readVercelConfig(service.dir);
|
|
7690
7846
|
projectName = await readLinkedProjectName(service.dir);
|
|
7691
7847
|
} catch (err) {
|
|
7692
|
-
recordExtractionError("infra vercel",
|
|
7848
|
+
recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7693
7849
|
continue;
|
|
7694
7850
|
}
|
|
7695
7851
|
if (!read && !projectName) continue;
|
|
@@ -7705,7 +7861,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7705
7861
|
const anchorId = service.node.id;
|
|
7706
7862
|
if (!read) continue;
|
|
7707
7863
|
const { config, relFile, raw } = read;
|
|
7708
|
-
const evidenceFile = toPosix2(
|
|
7864
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7709
7865
|
const add = (edgeType, kind, name) => {
|
|
7710
7866
|
if (!name) return;
|
|
7711
7867
|
const result = emitPlatformResourceEdge(
|
|
@@ -7721,12 +7877,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7721
7877
|
nodesAdded += result.nodesAdded;
|
|
7722
7878
|
edgesAdded += result.edgesAdded;
|
|
7723
7879
|
};
|
|
7724
|
-
add(
|
|
7725
|
-
for (const cron of config.crons ?? []) add(
|
|
7726
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7727
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7880
|
+
add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7881
|
+
for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7882
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7883
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7728
7884
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7729
|
-
add(
|
|
7885
|
+
add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7730
7886
|
}
|
|
7731
7887
|
}
|
|
7732
7888
|
return { nodesAdded, edgesAdded };
|
|
@@ -7735,13 +7891,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7735
7891
|
// src/extract/infra/railway.ts
|
|
7736
7892
|
init_cjs_shims();
|
|
7737
7893
|
var import_node_fs21 = require("fs");
|
|
7738
|
-
var
|
|
7894
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7739
7895
|
var import_smol_toml3 = require("smol-toml");
|
|
7740
|
-
var
|
|
7896
|
+
var import_types29 = require("@neat.is/types");
|
|
7741
7897
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7742
7898
|
async function readRailwayConfig(dir) {
|
|
7743
7899
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7744
|
-
const abs =
|
|
7900
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7745
7901
|
if (!await exists(abs)) continue;
|
|
7746
7902
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7747
7903
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7757,7 +7913,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7757
7913
|
try {
|
|
7758
7914
|
read = await readRailwayConfig(service.dir);
|
|
7759
7915
|
} catch (err) {
|
|
7760
|
-
recordExtractionError("infra railway",
|
|
7916
|
+
recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7761
7917
|
continue;
|
|
7762
7918
|
}
|
|
7763
7919
|
if (!read) continue;
|
|
@@ -7767,7 +7923,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7767
7923
|
}
|
|
7768
7924
|
const anchorId = service.node.id;
|
|
7769
7925
|
const { config, relFile, raw } = read;
|
|
7770
|
-
const evidenceFile = toPosix2(
|
|
7926
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7771
7927
|
const add = (edgeType, kind, name) => {
|
|
7772
7928
|
if (!name) return;
|
|
7773
7929
|
const result = emitPlatformResourceEdge(
|
|
@@ -7783,9 +7939,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7783
7939
|
nodesAdded += result.nodesAdded;
|
|
7784
7940
|
edgesAdded += result.edgesAdded;
|
|
7785
7941
|
};
|
|
7786
|
-
add(
|
|
7787
|
-
add(
|
|
7788
|
-
add(
|
|
7942
|
+
add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
|
|
7943
|
+
add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7944
|
+
add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7789
7945
|
}
|
|
7790
7946
|
return { nodesAdded, edgesAdded };
|
|
7791
7947
|
}
|
|
@@ -7793,12 +7949,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7793
7949
|
// src/extract/infra/supabase.ts
|
|
7794
7950
|
init_cjs_shims();
|
|
7795
7951
|
var import_node_fs22 = require("fs");
|
|
7796
|
-
var
|
|
7952
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7797
7953
|
var import_smol_toml4 = require("smol-toml");
|
|
7798
|
-
var
|
|
7954
|
+
var import_types30 = require("@neat.is/types");
|
|
7799
7955
|
async function readSupabaseConfig(dir) {
|
|
7800
|
-
const relFile =
|
|
7801
|
-
const abs =
|
|
7956
|
+
const relFile = import_node_path40.default.join("supabase", "config.toml");
|
|
7957
|
+
const abs = import_node_path40.default.join(dir, relFile);
|
|
7802
7958
|
if (!await exists(abs)) return null;
|
|
7803
7959
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7804
7960
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7812,7 +7968,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7812
7968
|
try {
|
|
7813
7969
|
read = await readSupabaseConfig(service.dir);
|
|
7814
7970
|
} catch (err) {
|
|
7815
|
-
recordExtractionError("infra supabase",
|
|
7971
|
+
recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7816
7972
|
continue;
|
|
7817
7973
|
}
|
|
7818
7974
|
if (!read) continue;
|
|
@@ -7827,7 +7983,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7827
7983
|
});
|
|
7828
7984
|
}
|
|
7829
7985
|
const anchorId = service.node.id;
|
|
7830
|
-
const evidenceFile = toPosix2(
|
|
7986
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7831
7987
|
const add = (edgeType, kind, name) => {
|
|
7832
7988
|
if (!name) return;
|
|
7833
7989
|
const result = emitPlatformResourceEdge(
|
|
@@ -7843,10 +7999,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7843
7999
|
nodesAdded += result.nodesAdded;
|
|
7844
8000
|
edgesAdded += result.edgesAdded;
|
|
7845
8001
|
};
|
|
7846
|
-
add(
|
|
7847
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7848
|
-
if (config.storage) add(
|
|
7849
|
-
if (config.auth) add(
|
|
8002
|
+
add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
8003
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
8004
|
+
if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
8005
|
+
if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7850
8006
|
}
|
|
7851
8007
|
return { nodesAdded, edgesAdded };
|
|
7852
8008
|
}
|
|
@@ -7868,17 +8024,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7868
8024
|
}
|
|
7869
8025
|
|
|
7870
8026
|
// src/extract/index.ts
|
|
7871
|
-
var
|
|
8027
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7872
8028
|
|
|
7873
8029
|
// src/extract/retire.ts
|
|
7874
8030
|
init_cjs_shims();
|
|
7875
8031
|
var import_node_fs23 = require("fs");
|
|
7876
|
-
var
|
|
7877
|
-
var
|
|
8032
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
8033
|
+
var import_types31 = require("@neat.is/types");
|
|
7878
8034
|
function dropOrphanedFileNodes(graph) {
|
|
7879
8035
|
const orphans = [];
|
|
7880
8036
|
graph.forEachNode((id, attrs) => {
|
|
7881
|
-
if (attrs.type !==
|
|
8037
|
+
if (attrs.type !== import_types31.NodeType.FileNode) return;
|
|
7882
8038
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7883
8039
|
orphans.push(id);
|
|
7884
8040
|
}
|
|
@@ -7891,14 +8047,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7891
8047
|
const bases = [scanPath, ...serviceDirs];
|
|
7892
8048
|
graph.forEachEdge((id, attrs) => {
|
|
7893
8049
|
const edge = attrs;
|
|
7894
|
-
if (edge.provenance !==
|
|
8050
|
+
if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
|
|
7895
8051
|
const evidenceFile = edge.evidence?.file;
|
|
7896
8052
|
if (!evidenceFile) return;
|
|
7897
|
-
if (
|
|
8053
|
+
if (import_node_path41.default.isAbsolute(evidenceFile)) {
|
|
7898
8054
|
if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7899
8055
|
return;
|
|
7900
8056
|
}
|
|
7901
|
-
const found = bases.some((base) => (0, import_node_fs23.existsSync)(
|
|
8057
|
+
const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path41.default.join(base, evidenceFile)));
|
|
7902
8058
|
if (!found) toDrop.push(id);
|
|
7903
8059
|
});
|
|
7904
8060
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7940,7 +8096,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7940
8096
|
}
|
|
7941
8097
|
const droppedEntries = drainDroppedExtracted();
|
|
7942
8098
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7943
|
-
const rejectedPath =
|
|
8099
|
+
const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7944
8100
|
try {
|
|
7945
8101
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7946
8102
|
} catch (err) {
|
|
@@ -7975,8 +8131,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7975
8131
|
// src/persist.ts
|
|
7976
8132
|
init_cjs_shims();
|
|
7977
8133
|
var import_node_fs24 = require("fs");
|
|
7978
|
-
var
|
|
7979
|
-
var
|
|
8134
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8135
|
+
var import_types32 = require("@neat.is/types");
|
|
7980
8136
|
var SCHEMA_VERSION = 4;
|
|
7981
8137
|
function migrateV1ToV2(payload) {
|
|
7982
8138
|
const nodes = payload.graph.nodes;
|
|
@@ -7998,12 +8154,12 @@ function migrateV2ToV3(payload) {
|
|
|
7998
8154
|
for (const edge of edges) {
|
|
7999
8155
|
const attrs = edge.attributes;
|
|
8000
8156
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
8001
|
-
attrs.provenance =
|
|
8157
|
+
attrs.provenance = import_types32.Provenance.OBSERVED;
|
|
8002
8158
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
8003
8159
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
8004
8160
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
8005
8161
|
if (type && source && target) {
|
|
8006
|
-
const newId = (0,
|
|
8162
|
+
const newId = (0, import_types32.observedEdgeId)(source, target, type);
|
|
8007
8163
|
attrs.id = newId;
|
|
8008
8164
|
if (edge.key) edge.key = newId;
|
|
8009
8165
|
}
|
|
@@ -8012,7 +8168,7 @@ function migrateV2ToV3(payload) {
|
|
|
8012
8168
|
return { ...payload, schemaVersion: 3 };
|
|
8013
8169
|
}
|
|
8014
8170
|
async function ensureDir(filePath) {
|
|
8015
|
-
await import_node_fs24.promises.mkdir(
|
|
8171
|
+
await import_node_fs24.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
|
|
8016
8172
|
}
|
|
8017
8173
|
async function saveGraphToDisk(graph, outPath) {
|
|
8018
8174
|
await ensureDir(outPath);
|
|
@@ -8095,19 +8251,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
8095
8251
|
init_cjs_shims();
|
|
8096
8252
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
8097
8253
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
8098
|
-
var
|
|
8254
|
+
var import_types35 = require("@neat.is/types");
|
|
8099
8255
|
|
|
8100
8256
|
// src/extend/index.ts
|
|
8101
8257
|
init_cjs_shims();
|
|
8102
8258
|
var import_node_fs26 = require("fs");
|
|
8103
|
-
var
|
|
8259
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8104
8260
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
8105
8261
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
8106
8262
|
|
|
8107
8263
|
// src/installers/package-manager.ts
|
|
8108
8264
|
init_cjs_shims();
|
|
8109
8265
|
var import_node_fs25 = require("fs");
|
|
8110
|
-
var
|
|
8266
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8111
8267
|
var import_node_child_process = require("child_process");
|
|
8112
8268
|
var LOCKFILE_PRIORITY = [
|
|
8113
8269
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -8129,22 +8285,22 @@ async function exists2(p) {
|
|
|
8129
8285
|
}
|
|
8130
8286
|
}
|
|
8131
8287
|
async function detectPackageManager(serviceDir) {
|
|
8132
|
-
let dir =
|
|
8288
|
+
let dir = import_node_path44.default.resolve(serviceDir);
|
|
8133
8289
|
const stops = /* @__PURE__ */ new Set();
|
|
8134
8290
|
for (let i = 0; i < 64; i++) {
|
|
8135
8291
|
if (stops.has(dir)) break;
|
|
8136
8292
|
stops.add(dir);
|
|
8137
8293
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8138
|
-
const lockPath =
|
|
8294
|
+
const lockPath = import_node_path44.default.join(dir, candidate.lockfile);
|
|
8139
8295
|
if (await exists2(lockPath)) {
|
|
8140
8296
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8141
8297
|
}
|
|
8142
8298
|
}
|
|
8143
|
-
const parent =
|
|
8299
|
+
const parent = import_node_path44.default.dirname(dir);
|
|
8144
8300
|
if (parent === dir) break;
|
|
8145
8301
|
dir = parent;
|
|
8146
8302
|
}
|
|
8147
|
-
return { pm: "npm", cwd:
|
|
8303
|
+
return { pm: "npm", cwd: import_node_path44.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8148
8304
|
}
|
|
8149
8305
|
async function runPackageManagerInstall(cmd) {
|
|
8150
8306
|
return new Promise((resolve) => {
|
|
@@ -8193,7 +8349,7 @@ async function fileExists2(p) {
|
|
|
8193
8349
|
}
|
|
8194
8350
|
}
|
|
8195
8351
|
async function readPackageJson(scanPath) {
|
|
8196
|
-
const pkgPath =
|
|
8352
|
+
const pkgPath = import_node_path45.default.join(scanPath, "package.json");
|
|
8197
8353
|
const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
|
|
8198
8354
|
return JSON.parse(raw);
|
|
8199
8355
|
}
|
|
@@ -8207,27 +8363,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
8207
8363
|
]);
|
|
8208
8364
|
async function findHookFiles(scanPath) {
|
|
8209
8365
|
const found = [];
|
|
8210
|
-
const
|
|
8366
|
+
const walk4 = async (dir) => {
|
|
8211
8367
|
const entries = await import_node_fs26.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8212
8368
|
for (const entry of entries) {
|
|
8213
8369
|
if (entry.isDirectory()) {
|
|
8214
8370
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
8215
|
-
await
|
|
8371
|
+
await walk4(import_node_path45.default.join(dir, entry.name));
|
|
8216
8372
|
} else if (entry.isFile()) {
|
|
8217
8373
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
8218
|
-
const rel =
|
|
8219
|
-
found.push(rel.split(
|
|
8374
|
+
const rel = import_node_path45.default.relative(scanPath, import_node_path45.default.join(dir, entry.name));
|
|
8375
|
+
found.push(rel.split(import_node_path45.default.sep).join("/"));
|
|
8220
8376
|
}
|
|
8221
8377
|
}
|
|
8222
8378
|
}
|
|
8223
8379
|
};
|
|
8224
|
-
await
|
|
8380
|
+
await walk4(scanPath);
|
|
8225
8381
|
return found.sort();
|
|
8226
8382
|
}
|
|
8227
8383
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8228
8384
|
let fallback = null;
|
|
8229
8385
|
for (const file of hookFiles) {
|
|
8230
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8386
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(scanPath, file), "utf8");
|
|
8231
8387
|
const patched = splicedContent(content, snippet2);
|
|
8232
8388
|
if (patched !== null) return { file, content, patched };
|
|
8233
8389
|
if (fallback === null) fallback = { file, content };
|
|
@@ -8235,11 +8391,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
8235
8391
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
8236
8392
|
}
|
|
8237
8393
|
function extendLogPath() {
|
|
8238
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8394
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path45.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
8239
8395
|
}
|
|
8240
8396
|
async function appendExtendLog(entry) {
|
|
8241
8397
|
const logPath = extendLogPath();
|
|
8242
|
-
await import_node_fs26.promises.mkdir(
|
|
8398
|
+
await import_node_fs26.promises.mkdir(import_node_path45.default.dirname(logPath), { recursive: true });
|
|
8243
8399
|
await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
8244
8400
|
}
|
|
8245
8401
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -8298,7 +8454,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
8298
8454
|
}
|
|
8299
8455
|
async function describeProjectInstrumentation(ctx) {
|
|
8300
8456
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8301
|
-
const envNeat = await fileExists2(
|
|
8457
|
+
const envNeat = await fileExists2(import_node_path45.default.join(ctx.scanPath, ".env.neat"));
|
|
8302
8458
|
const registryInstrPackages = new Set(
|
|
8303
8459
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
8304
8460
|
);
|
|
@@ -8320,7 +8476,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
8320
8476
|
);
|
|
8321
8477
|
}
|
|
8322
8478
|
for (const file of hookFiles) {
|
|
8323
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8479
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
|
|
8324
8480
|
if (content.includes(args.registration_snippet)) {
|
|
8325
8481
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
8326
8482
|
}
|
|
@@ -8332,10 +8488,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
8332
8488
|
);
|
|
8333
8489
|
}
|
|
8334
8490
|
const primaryFile = primary.file;
|
|
8335
|
-
const primaryPath =
|
|
8491
|
+
const primaryPath = import_node_path45.default.join(ctx.scanPath, primaryFile);
|
|
8336
8492
|
const filesTouched = [];
|
|
8337
8493
|
const depsAdded = [];
|
|
8338
|
-
const pkgPath =
|
|
8494
|
+
const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
|
|
8339
8495
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8340
8496
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
8341
8497
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -8374,7 +8530,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8374
8530
|
};
|
|
8375
8531
|
}
|
|
8376
8532
|
for (const file of hookFiles) {
|
|
8377
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8533
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
|
|
8378
8534
|
if (content.includes(args.registration_snippet)) {
|
|
8379
8535
|
return {
|
|
8380
8536
|
library: args.library,
|
|
@@ -8415,7 +8571,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8415
8571
|
if (!match) {
|
|
8416
8572
|
return { undone: false, message: "no apply found for library" };
|
|
8417
8573
|
}
|
|
8418
|
-
const pkgPath =
|
|
8574
|
+
const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
|
|
8419
8575
|
if (await fileExists2(pkgPath)) {
|
|
8420
8576
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8421
8577
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8426,7 +8582,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8426
8582
|
}
|
|
8427
8583
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8428
8584
|
for (const file of hookFiles) {
|
|
8429
|
-
const filePath =
|
|
8585
|
+
const filePath = import_node_path45.default.join(ctx.scanPath, file);
|
|
8430
8586
|
const content = await import_node_fs26.promises.readFile(filePath, "utf8");
|
|
8431
8587
|
if (content.includes(match.registration_snippet)) {
|
|
8432
8588
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8442,39 +8598,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
8442
8598
|
|
|
8443
8599
|
// src/divergences.ts
|
|
8444
8600
|
init_cjs_shims();
|
|
8445
|
-
var
|
|
8601
|
+
var import_types33 = require("@neat.is/types");
|
|
8446
8602
|
function bucketKey(source, target, type) {
|
|
8447
8603
|
return `${type}|${source}|${target}`;
|
|
8448
8604
|
}
|
|
8449
8605
|
function bucketSourceFor(graph, edge) {
|
|
8450
|
-
if (edge.type !==
|
|
8451
|
-
const parsed = (0,
|
|
8606
|
+
if (edge.type !== import_types33.EdgeType.CONNECTS_TO) return edge.source;
|
|
8607
|
+
const parsed = (0, import_types33.parseFileId)(edge.source);
|
|
8452
8608
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8453
8609
|
const target = graph.getNodeAttributes(edge.target);
|
|
8454
|
-
if (target.type !==
|
|
8455
|
-
return (0,
|
|
8610
|
+
if (target.type !== import_types33.NodeType.DatabaseNode) return edge.source;
|
|
8611
|
+
return (0, import_types33.serviceId)(parsed.service);
|
|
8456
8612
|
}
|
|
8457
8613
|
function bucketEdges(graph) {
|
|
8458
8614
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8459
8615
|
graph.forEachEdge((id, attrs) => {
|
|
8460
8616
|
const e = attrs;
|
|
8461
|
-
const parsed = (0,
|
|
8617
|
+
const parsed = (0, import_types33.parseEdgeId)(id);
|
|
8462
8618
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8463
8619
|
const source = bucketSourceFor(graph, e);
|
|
8464
8620
|
const key = bucketKey(source, e.target, e.type);
|
|
8465
8621
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8466
8622
|
switch (provenance) {
|
|
8467
|
-
case
|
|
8623
|
+
case import_types33.Provenance.EXTRACTED:
|
|
8468
8624
|
cur.extracted = e;
|
|
8469
8625
|
break;
|
|
8470
|
-
case
|
|
8626
|
+
case import_types33.Provenance.OBSERVED:
|
|
8471
8627
|
cur.observed = e;
|
|
8472
8628
|
break;
|
|
8473
|
-
case
|
|
8629
|
+
case import_types33.Provenance.INFERRED:
|
|
8474
8630
|
cur.inferred = e;
|
|
8475
8631
|
break;
|
|
8476
8632
|
default:
|
|
8477
|
-
if (e.provenance ===
|
|
8633
|
+
if (e.provenance === import_types33.Provenance.STALE) cur.stale = e;
|
|
8478
8634
|
}
|
|
8479
8635
|
buckets2.set(key, cur);
|
|
8480
8636
|
});
|
|
@@ -8483,12 +8639,12 @@ function bucketEdges(graph) {
|
|
|
8483
8639
|
function nodeIsFrontier(graph, nodeId) {
|
|
8484
8640
|
if (!graph.hasNode(nodeId)) return false;
|
|
8485
8641
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8486
|
-
return attrs.type ===
|
|
8642
|
+
return attrs.type === import_types33.NodeType.FrontierNode;
|
|
8487
8643
|
}
|
|
8488
8644
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8489
8645
|
if (!graph.hasNode(nodeId)) return false;
|
|
8490
8646
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8491
|
-
return attrs.type ===
|
|
8647
|
+
return attrs.type === import_types33.NodeType.WebSocketChannelNode;
|
|
8492
8648
|
}
|
|
8493
8649
|
function clampConfidence(n) {
|
|
8494
8650
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8508,14 +8664,14 @@ function gradedConfidence(edge) {
|
|
|
8508
8664
|
return clampConfidence(confidenceForEdge(edge));
|
|
8509
8665
|
}
|
|
8510
8666
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8667
|
+
import_types33.EdgeType.CALLS,
|
|
8668
|
+
import_types33.EdgeType.CONNECTS_TO,
|
|
8669
|
+
import_types33.EdgeType.PUBLISHES_TO,
|
|
8670
|
+
import_types33.EdgeType.CONSUMES_FROM
|
|
8515
8671
|
]);
|
|
8516
8672
|
function detectMissingDivergences(graph, bucket) {
|
|
8517
8673
|
const out = [];
|
|
8518
|
-
if (bucket.type ===
|
|
8674
|
+
if (bucket.type === import_types33.EdgeType.CONTAINS) return out;
|
|
8519
8675
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8520
8676
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8521
8677
|
out.push({
|
|
@@ -8556,7 +8712,7 @@ function declaredHostFor(svc) {
|
|
|
8556
8712
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8557
8713
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8558
8714
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8559
|
-
if (e.type ===
|
|
8715
|
+
if (e.type === import_types33.EdgeType.CONFIGURED_BY && e.provenance === import_types33.Provenance.EXTRACTED) {
|
|
8560
8716
|
return true;
|
|
8561
8717
|
}
|
|
8562
8718
|
}
|
|
@@ -8569,10 +8725,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8569
8725
|
const out = [];
|
|
8570
8726
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8571
8727
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8572
|
-
if (edge.type !==
|
|
8573
|
-
if (edge.provenance !==
|
|
8728
|
+
if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
|
|
8729
|
+
if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
|
|
8574
8730
|
const target = graph.getNodeAttributes(edge.target);
|
|
8575
|
-
if (target.type !==
|
|
8731
|
+
if (target.type !== import_types33.NodeType.DatabaseNode) continue;
|
|
8576
8732
|
const observedHost = target.host?.trim();
|
|
8577
8733
|
if (!observedHost) continue;
|
|
8578
8734
|
if (observedHost === declaredHost) continue;
|
|
@@ -8594,10 +8750,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8594
8750
|
const deps = svc.dependencies ?? {};
|
|
8595
8751
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8596
8752
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8597
|
-
if (edge.type !==
|
|
8598
|
-
if (edge.provenance !==
|
|
8753
|
+
if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
|
|
8754
|
+
if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
|
|
8599
8755
|
const target = graph.getNodeAttributes(edge.target);
|
|
8600
|
-
if (target.type !==
|
|
8756
|
+
if (target.type !== import_types33.NodeType.DatabaseNode) continue;
|
|
8601
8757
|
for (const pair of compatPairs()) {
|
|
8602
8758
|
if (pair.engine !== target.engine) continue;
|
|
8603
8759
|
const declared = deps[pair.driver];
|
|
@@ -8656,7 +8812,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8656
8812
|
for (const d of all) {
|
|
8657
8813
|
if (d.type !== "host-mismatch") continue;
|
|
8658
8814
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8659
|
-
declaredHalf.add((0,
|
|
8815
|
+
declaredHalf.add((0, import_types33.databaseId)(d.extractedHost));
|
|
8660
8816
|
}
|
|
8661
8817
|
if (observedHalf.size === 0) return all;
|
|
8662
8818
|
return all.filter((d) => {
|
|
@@ -8675,7 +8831,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8675
8831
|
}
|
|
8676
8832
|
graph.forEachNode((nodeId, attrs) => {
|
|
8677
8833
|
const n = attrs;
|
|
8678
|
-
if (n.type !==
|
|
8834
|
+
if (n.type !== import_types33.NodeType.ServiceNode) return;
|
|
8679
8835
|
const svc = n;
|
|
8680
8836
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8681
8837
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8709,7 +8865,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8709
8865
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8710
8866
|
return a.target.localeCompare(b.target);
|
|
8711
8867
|
});
|
|
8712
|
-
return
|
|
8868
|
+
return import_types33.DivergenceResultSchema.parse({
|
|
8713
8869
|
divergences: filtered,
|
|
8714
8870
|
totalAffected: filtered.length,
|
|
8715
8871
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8840,23 +8996,23 @@ function canonicalJson(value) {
|
|
|
8840
8996
|
|
|
8841
8997
|
// src/projects.ts
|
|
8842
8998
|
init_cjs_shims();
|
|
8843
|
-
var
|
|
8999
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8844
9000
|
function pathsForProject(project, baseDir) {
|
|
8845
9001
|
if (project === DEFAULT_PROJECT) {
|
|
8846
9002
|
return {
|
|
8847
|
-
snapshotPath:
|
|
8848
|
-
errorsPath:
|
|
8849
|
-
staleEventsPath:
|
|
8850
|
-
embeddingsCachePath:
|
|
8851
|
-
policyViolationsPath:
|
|
9003
|
+
snapshotPath: import_node_path46.default.join(baseDir, "graph.json"),
|
|
9004
|
+
errorsPath: import_node_path46.default.join(baseDir, "errors.ndjson"),
|
|
9005
|
+
staleEventsPath: import_node_path46.default.join(baseDir, "stale-events.ndjson"),
|
|
9006
|
+
embeddingsCachePath: import_node_path46.default.join(baseDir, "embeddings.json"),
|
|
9007
|
+
policyViolationsPath: import_node_path46.default.join(baseDir, "policy-violations.ndjson")
|
|
8852
9008
|
};
|
|
8853
9009
|
}
|
|
8854
9010
|
return {
|
|
8855
|
-
snapshotPath:
|
|
8856
|
-
errorsPath:
|
|
8857
|
-
staleEventsPath:
|
|
8858
|
-
embeddingsCachePath:
|
|
8859
|
-
policyViolationsPath:
|
|
9011
|
+
snapshotPath: import_node_path46.default.join(baseDir, `${project}.json`),
|
|
9012
|
+
errorsPath: import_node_path46.default.join(baseDir, `errors.${project}.ndjson`),
|
|
9013
|
+
staleEventsPath: import_node_path46.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
9014
|
+
embeddingsCachePath: import_node_path46.default.join(baseDir, `embeddings.${project}.json`),
|
|
9015
|
+
policyViolationsPath: import_node_path46.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
8860
9016
|
};
|
|
8861
9017
|
}
|
|
8862
9018
|
var Projects = class {
|
|
@@ -8894,23 +9050,23 @@ var Projects = class {
|
|
|
8894
9050
|
init_cjs_shims();
|
|
8895
9051
|
var import_node_fs28 = require("fs");
|
|
8896
9052
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8897
|
-
var
|
|
8898
|
-
var
|
|
9053
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9054
|
+
var import_types34 = require("@neat.is/types");
|
|
8899
9055
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8900
9056
|
var LOCK_RETRY_MS = 50;
|
|
8901
9057
|
function neatHome() {
|
|
8902
9058
|
const override = process.env.NEAT_HOME;
|
|
8903
|
-
if (override && override.length > 0) return
|
|
8904
|
-
return
|
|
9059
|
+
if (override && override.length > 0) return import_node_path47.default.resolve(override);
|
|
9060
|
+
return import_node_path47.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8905
9061
|
}
|
|
8906
9062
|
function registryPath() {
|
|
8907
|
-
return
|
|
9063
|
+
return import_node_path47.default.join(neatHome(), "projects.json");
|
|
8908
9064
|
}
|
|
8909
9065
|
function registryLockPath() {
|
|
8910
|
-
return
|
|
9066
|
+
return import_node_path47.default.join(neatHome(), "projects.json.lock");
|
|
8911
9067
|
}
|
|
8912
9068
|
function daemonPidPath() {
|
|
8913
|
-
return
|
|
9069
|
+
return import_node_path47.default.join(neatHome(), "neatd.pid");
|
|
8914
9070
|
}
|
|
8915
9071
|
function isPidAliveDefault(pid) {
|
|
8916
9072
|
try {
|
|
@@ -8970,7 +9126,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8970
9126
|
}
|
|
8971
9127
|
}
|
|
8972
9128
|
async function normalizeProjectPath(input) {
|
|
8973
|
-
const resolved =
|
|
9129
|
+
const resolved = import_node_path47.default.resolve(input);
|
|
8974
9130
|
try {
|
|
8975
9131
|
return await import_node_fs28.promises.realpath(resolved);
|
|
8976
9132
|
} catch {
|
|
@@ -8978,7 +9134,7 @@ async function normalizeProjectPath(input) {
|
|
|
8978
9134
|
}
|
|
8979
9135
|
}
|
|
8980
9136
|
async function writeAtomically(target, contents) {
|
|
8981
|
-
await import_node_fs28.promises.mkdir(
|
|
9137
|
+
await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(target), { recursive: true });
|
|
8982
9138
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8983
9139
|
const fd = await import_node_fs28.promises.open(tmp, "w");
|
|
8984
9140
|
try {
|
|
@@ -8991,7 +9147,7 @@ async function writeAtomically(target, contents) {
|
|
|
8991
9147
|
}
|
|
8992
9148
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8993
9149
|
const deadline = Date.now() + timeoutMs;
|
|
8994
|
-
await import_node_fs28.promises.mkdir(
|
|
9150
|
+
await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(lockPath), { recursive: true });
|
|
8995
9151
|
let probedHolder = false;
|
|
8996
9152
|
while (true) {
|
|
8997
9153
|
try {
|
|
@@ -9044,10 +9200,10 @@ async function readRegistry() {
|
|
|
9044
9200
|
throw err;
|
|
9045
9201
|
}
|
|
9046
9202
|
const parsed = JSON.parse(raw);
|
|
9047
|
-
return
|
|
9203
|
+
return import_types34.RegistryFileSchema.parse(parsed);
|
|
9048
9204
|
}
|
|
9049
9205
|
async function writeRegistry(reg) {
|
|
9050
|
-
const validated =
|
|
9206
|
+
const validated = import_types34.RegistryFileSchema.parse(reg);
|
|
9051
9207
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
9052
9208
|
}
|
|
9053
9209
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -9242,7 +9398,7 @@ init_auth();
|
|
|
9242
9398
|
// src/connectors-config.ts
|
|
9243
9399
|
init_cjs_shims();
|
|
9244
9400
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
9245
|
-
var
|
|
9401
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9246
9402
|
var import_node_fs29 = require("fs");
|
|
9247
9403
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
9248
9404
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -9257,11 +9413,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
9257
9413
|
};
|
|
9258
9414
|
function neatHome2() {
|
|
9259
9415
|
const override = process.env.NEAT_HOME;
|
|
9260
|
-
if (override && override.length > 0) return
|
|
9261
|
-
return
|
|
9416
|
+
if (override && override.length > 0) return import_node_path48.default.resolve(override);
|
|
9417
|
+
return import_node_path48.default.join(import_node_os4.default.homedir(), ".neat");
|
|
9262
9418
|
}
|
|
9263
9419
|
function connectorsConfigPath(home = neatHome2()) {
|
|
9264
|
-
return
|
|
9420
|
+
return import_node_path48.default.join(home, "connectors.json");
|
|
9265
9421
|
}
|
|
9266
9422
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
9267
9423
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9597,11 +9753,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9597
9753
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9598
9754
|
const parsed = [];
|
|
9599
9755
|
for (const c of candidates) {
|
|
9600
|
-
const r =
|
|
9756
|
+
const r = import_types35.DivergenceTypeSchema.safeParse(c);
|
|
9601
9757
|
if (!r.success) {
|
|
9602
9758
|
return reply.code(400).send({
|
|
9603
9759
|
error: `unknown divergence type "${c}"`,
|
|
9604
|
-
allowed:
|
|
9760
|
+
allowed: import_types35.DivergenceTypeSchema.options
|
|
9605
9761
|
});
|
|
9606
9762
|
}
|
|
9607
9763
|
parsed.push(r.data);
|
|
@@ -9872,7 +10028,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9872
10028
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9873
10029
|
let violations = await log.readAll();
|
|
9874
10030
|
if (req.query.severity) {
|
|
9875
|
-
const sev =
|
|
10031
|
+
const sev = import_types35.PolicySeveritySchema.safeParse(req.query.severity);
|
|
9876
10032
|
if (!sev.success) {
|
|
9877
10033
|
return reply.code(400).send({
|
|
9878
10034
|
error: "invalid severity",
|
|
@@ -9911,7 +10067,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9911
10067
|
scope.post("/policies/check", async (req, reply) => {
|
|
9912
10068
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9913
10069
|
if (!proj) return;
|
|
9914
|
-
const parsed =
|
|
10070
|
+
const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9915
10071
|
if (!parsed.success) {
|
|
9916
10072
|
return reply.code(400).send({
|
|
9917
10073
|
error: "invalid /policies/check body",
|
|
@@ -10213,21 +10369,21 @@ init_otel_grpc();
|
|
|
10213
10369
|
// src/daemon.ts
|
|
10214
10370
|
init_cjs_shims();
|
|
10215
10371
|
var import_node_fs31 = require("fs");
|
|
10216
|
-
var
|
|
10372
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
10217
10373
|
var import_node_module = require("module");
|
|
10218
10374
|
init_otel();
|
|
10219
10375
|
|
|
10220
10376
|
// src/connectors/index.ts
|
|
10221
10377
|
init_cjs_shims();
|
|
10222
|
-
var
|
|
10378
|
+
var import_types36 = require("@neat.is/types");
|
|
10223
10379
|
var NO_ENV = "unknown";
|
|
10224
10380
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
10225
10381
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10226
10382
|
const sites = [];
|
|
10227
10383
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
10228
10384
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
10229
|
-
if (edge.provenance !==
|
|
10230
|
-
const parsed = (0,
|
|
10385
|
+
if (edge.provenance !== import_types36.Provenance.EXTRACTED) continue;
|
|
10386
|
+
const parsed = (0, import_types36.parseFileId)(edge.source);
|
|
10231
10387
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
10232
10388
|
const site = { relPath: edge.evidence.file };
|
|
10233
10389
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -10238,7 +10394,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
10238
10394
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
10239
10395
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10240
10396
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
10241
|
-
if (attrs.type !==
|
|
10397
|
+
if (attrs.type !== import_types36.NodeType.RouteNode || !attrs.path) return void 0;
|
|
10242
10398
|
const site = { relPath: attrs.path };
|
|
10243
10399
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
10244
10400
|
return site;
|
|
@@ -10709,10 +10865,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10709
10865
|
// src/connectors/supabase/map.ts
|
|
10710
10866
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10711
10867
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10712
|
-
function targetFromRestPath(
|
|
10713
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10868
|
+
function targetFromRestPath(path53) {
|
|
10869
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path53);
|
|
10714
10870
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10715
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10871
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path53);
|
|
10716
10872
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10717
10873
|
return null;
|
|
10718
10874
|
}
|
|
@@ -10821,23 +10977,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10821
10977
|
|
|
10822
10978
|
// src/connectors/supabase/resolve.ts
|
|
10823
10979
|
init_cjs_shims();
|
|
10824
|
-
var
|
|
10980
|
+
var import_types38 = require("@neat.is/types");
|
|
10825
10981
|
function createSupabaseResolveTarget(graph, config) {
|
|
10826
10982
|
return (signal, _ctx) => {
|
|
10827
10983
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10828
10984
|
return null;
|
|
10829
10985
|
}
|
|
10830
|
-
const subResourceId = (0,
|
|
10986
|
+
const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10831
10987
|
if (graph.hasNode(subResourceId)) {
|
|
10832
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10988
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10833
10989
|
}
|
|
10834
|
-
const bareResourceId = (0,
|
|
10990
|
+
const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
|
|
10835
10991
|
if (graph.hasNode(bareResourceId)) {
|
|
10836
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10992
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10837
10993
|
}
|
|
10838
|
-
const projectLevelId = (0,
|
|
10994
|
+
const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
|
|
10839
10995
|
if (graph.hasNode(projectLevelId)) {
|
|
10840
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10996
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10841
10997
|
}
|
|
10842
10998
|
return null;
|
|
10843
10999
|
};
|
|
@@ -10930,7 +11086,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10930
11086
|
|
|
10931
11087
|
// src/connectors/railway/index.ts
|
|
10932
11088
|
init_cjs_shims();
|
|
10933
|
-
var
|
|
11089
|
+
var import_types42 = require("@neat.is/types");
|
|
10934
11090
|
|
|
10935
11091
|
// src/connectors/railway/client.ts
|
|
10936
11092
|
init_cjs_shims();
|
|
@@ -11056,7 +11212,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
11056
11212
|
const out = [];
|
|
11057
11213
|
graph.forEachNode((_id, attrs) => {
|
|
11058
11214
|
const node = attrs;
|
|
11059
|
-
if (node.type !==
|
|
11215
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
11060
11216
|
const route = attrs;
|
|
11061
11217
|
if (route.service !== serviceName) return;
|
|
11062
11218
|
out.push({
|
|
@@ -11160,12 +11316,12 @@ function createRailwayResolveTarget(config) {
|
|
|
11160
11316
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
11161
11317
|
if (!serviceName) return null;
|
|
11162
11318
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
11163
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11319
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
|
|
11164
11320
|
}
|
|
11165
11321
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
11166
11322
|
const peerName = config.serviceNameById[signal.targetName];
|
|
11167
11323
|
if (!peerName) return null;
|
|
11168
|
-
return { targetNodeId: (0,
|
|
11324
|
+
return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
|
|
11169
11325
|
}
|
|
11170
11326
|
return null;
|
|
11171
11327
|
};
|
|
@@ -11289,9 +11445,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11289
11445
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11290
11446
|
if (secondSep === -1) return null;
|
|
11291
11447
|
const method = rest.slice(0, secondSep);
|
|
11292
|
-
const
|
|
11293
|
-
if (!resourceName || !method || !
|
|
11294
|
-
return { resourceName, method, path:
|
|
11448
|
+
const path53 = rest.slice(secondSep + 1);
|
|
11449
|
+
if (!resourceName || !method || !path53) return null;
|
|
11450
|
+
return { resourceName, method, path: path53 };
|
|
11295
11451
|
}
|
|
11296
11452
|
function resourceNameFor(type, labels) {
|
|
11297
11453
|
if (!labels) return null;
|
|
@@ -11329,14 +11485,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
11329
11485
|
if (!req) return null;
|
|
11330
11486
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11331
11487
|
const method = req.requestMethod.toUpperCase();
|
|
11332
|
-
const
|
|
11333
|
-
if (
|
|
11488
|
+
const path53 = pathFromRequestUrl(req.requestUrl);
|
|
11489
|
+
if (path53 === null) return null;
|
|
11334
11490
|
const timestamp = entry.timestamp;
|
|
11335
11491
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11336
11492
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11337
11493
|
return {
|
|
11338
11494
|
targetKind: resourceType,
|
|
11339
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11495
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path53 }),
|
|
11340
11496
|
callCount: 1,
|
|
11341
11497
|
errorCount: isError ? 1 : 0,
|
|
11342
11498
|
lastObservedIso: timestamp
|
|
@@ -11353,7 +11509,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11353
11509
|
|
|
11354
11510
|
// src/connectors/firebase/resolve.ts
|
|
11355
11511
|
init_cjs_shims();
|
|
11356
|
-
var
|
|
11512
|
+
var import_types43 = require("@neat.is/types");
|
|
11357
11513
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11358
11514
|
switch (resourceType) {
|
|
11359
11515
|
case "cloud_function":
|
|
@@ -11368,7 +11524,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11368
11524
|
const entries = [];
|
|
11369
11525
|
graph.forEachNode((_id, attrs) => {
|
|
11370
11526
|
const node = attrs;
|
|
11371
|
-
if (node.type !==
|
|
11527
|
+
if (node.type !== import_types43.NodeType.RouteNode) return;
|
|
11372
11528
|
const route = attrs;
|
|
11373
11529
|
if (route.service !== serviceName) return;
|
|
11374
11530
|
entries.push({
|
|
@@ -11400,7 +11556,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11400
11556
|
return {
|
|
11401
11557
|
targetNodeId: match.routeNodeId,
|
|
11402
11558
|
serviceName,
|
|
11403
|
-
edgeType:
|
|
11559
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
11404
11560
|
};
|
|
11405
11561
|
};
|
|
11406
11562
|
}
|
|
@@ -11427,7 +11583,7 @@ init_cjs_shims();
|
|
|
11427
11583
|
|
|
11428
11584
|
// src/connectors/cloudflare/connector.ts
|
|
11429
11585
|
init_cjs_shims();
|
|
11430
|
-
var
|
|
11586
|
+
var import_types45 = require("@neat.is/types");
|
|
11431
11587
|
|
|
11432
11588
|
// src/connectors/cloudflare/client.ts
|
|
11433
11589
|
init_cjs_shims();
|
|
@@ -11543,7 +11699,7 @@ function mapEventToSignal(event) {
|
|
|
11543
11699
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11544
11700
|
const statusCode = metadata?.statusCode;
|
|
11545
11701
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11546
|
-
const
|
|
11702
|
+
const path53 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11547
11703
|
return {
|
|
11548
11704
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11549
11705
|
targetName: scriptName,
|
|
@@ -11551,7 +11707,7 @@ function mapEventToSignal(event) {
|
|
|
11551
11707
|
errorCount: isError ? 1 : 0,
|
|
11552
11708
|
lastObservedIso: observedAt.toISOString(),
|
|
11553
11709
|
method,
|
|
11554
|
-
...
|
|
11710
|
+
...path53 ? { path: path53 } : {},
|
|
11555
11711
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11556
11712
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11557
11713
|
};
|
|
@@ -11591,19 +11747,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11591
11747
|
graph.forEachNode((id, attrs) => {
|
|
11592
11748
|
if (found) return;
|
|
11593
11749
|
const a = attrs;
|
|
11594
|
-
if (a.type ===
|
|
11750
|
+
if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11595
11751
|
found = id;
|
|
11596
11752
|
}
|
|
11597
11753
|
});
|
|
11598
11754
|
return found;
|
|
11599
11755
|
}
|
|
11600
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11601
|
-
const normalizedPath = normalizePathTemplate(
|
|
11756
|
+
function findMatchingRouteNode(graph, serviceName, method, path53) {
|
|
11757
|
+
const normalizedPath = normalizePathTemplate(path53);
|
|
11602
11758
|
let found = null;
|
|
11603
11759
|
graph.forEachNode((id, attrs) => {
|
|
11604
11760
|
if (found) return;
|
|
11605
11761
|
const a = attrs;
|
|
11606
|
-
if (a.type !==
|
|
11762
|
+
if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11607
11763
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11608
11764
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11609
11765
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11615,18 +11771,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11615
11771
|
return (signal) => {
|
|
11616
11772
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11617
11773
|
const scriptName = signal.targetName;
|
|
11618
|
-
const { method, path:
|
|
11774
|
+
const { method, path: path53 } = signal;
|
|
11619
11775
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11620
|
-
if (!method || !
|
|
11621
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11776
|
+
if (!method || !path53) return wholeFileId;
|
|
11777
|
+
return findMatchingRouteNode(graph, serviceName, method, path53) ?? wholeFileId;
|
|
11622
11778
|
};
|
|
11623
11779
|
const mapping = config.workers?.[scriptName];
|
|
11624
11780
|
if (mapping) {
|
|
11625
|
-
const wholeFileId = (0,
|
|
11781
|
+
const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
|
|
11626
11782
|
return {
|
|
11627
11783
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11628
11784
|
serviceName: mapping.service,
|
|
11629
|
-
edgeType:
|
|
11785
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11630
11786
|
};
|
|
11631
11787
|
}
|
|
11632
11788
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11635,13 +11791,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11635
11791
|
return {
|
|
11636
11792
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11637
11793
|
serviceName: fileNode.service,
|
|
11638
|
-
edgeType:
|
|
11794
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11639
11795
|
};
|
|
11640
11796
|
}
|
|
11641
11797
|
return {
|
|
11642
|
-
targetNodeId: (0,
|
|
11798
|
+
targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
|
|
11643
11799
|
serviceName: scriptName,
|
|
11644
|
-
edgeType:
|
|
11800
|
+
edgeType: import_types45.EdgeType.CALLS,
|
|
11645
11801
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11646
11802
|
};
|
|
11647
11803
|
};
|
|
@@ -12085,7 +12241,7 @@ init_auth();
|
|
|
12085
12241
|
// src/unrouted.ts
|
|
12086
12242
|
init_cjs_shims();
|
|
12087
12243
|
var import_node_fs30 = require("fs");
|
|
12088
|
-
var
|
|
12244
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12089
12245
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
12090
12246
|
return {
|
|
12091
12247
|
timestamp: now.toISOString(),
|
|
@@ -12095,34 +12251,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
12095
12251
|
};
|
|
12096
12252
|
}
|
|
12097
12253
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
12098
|
-
const target =
|
|
12254
|
+
const target = import_node_path51.default.join(neatHome3, "errors.ndjson");
|
|
12099
12255
|
await import_node_fs30.promises.mkdir(neatHome3, { recursive: true });
|
|
12100
12256
|
await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
12101
12257
|
}
|
|
12102
12258
|
function unroutedErrorsPath(neatHome3) {
|
|
12103
|
-
return
|
|
12259
|
+
return import_node_path51.default.join(neatHome3, "errors.ndjson");
|
|
12104
12260
|
}
|
|
12105
12261
|
|
|
12106
12262
|
// src/daemon.ts
|
|
12107
|
-
var
|
|
12263
|
+
var import_types48 = require("@neat.is/types");
|
|
12108
12264
|
function daemonJsonPath(scanPath) {
|
|
12109
|
-
return
|
|
12265
|
+
return import_node_path52.default.join(scanPath, "neat-out", "daemon.json");
|
|
12110
12266
|
}
|
|
12111
12267
|
function daemonsDiscoveryDir(home) {
|
|
12112
12268
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
12113
|
-
return
|
|
12269
|
+
return import_node_path52.default.join(base, "daemons");
|
|
12114
12270
|
}
|
|
12115
12271
|
function daemonDiscoveryPath(project, home) {
|
|
12116
|
-
return
|
|
12272
|
+
return import_node_path52.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
12117
12273
|
}
|
|
12118
12274
|
function sanitizeDiscoveryName(project) {
|
|
12119
12275
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
12120
12276
|
}
|
|
12121
12277
|
function neatHomeFromEnv() {
|
|
12122
12278
|
const env = process.env.NEAT_HOME;
|
|
12123
|
-
if (env && env.length > 0) return
|
|
12279
|
+
if (env && env.length > 0) return import_node_path52.default.resolve(env);
|
|
12124
12280
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12125
|
-
return
|
|
12281
|
+
return import_node_path52.default.join(home, ".neat");
|
|
12126
12282
|
}
|
|
12127
12283
|
function resolveNeatVersion() {
|
|
12128
12284
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -12177,11 +12333,11 @@ function teardownSlot(slot) {
|
|
|
12177
12333
|
}
|
|
12178
12334
|
}
|
|
12179
12335
|
function neatHomeFor(opts) {
|
|
12180
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
12336
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path52.default.resolve(opts.neatHome);
|
|
12181
12337
|
const env = process.env.NEAT_HOME;
|
|
12182
|
-
if (env && env.length > 0) return
|
|
12338
|
+
if (env && env.length > 0) return import_node_path52.default.resolve(env);
|
|
12183
12339
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12184
|
-
return
|
|
12340
|
+
return import_node_path52.default.join(home, ".neat");
|
|
12185
12341
|
}
|
|
12186
12342
|
function routeSpanToProject(serviceName, projects) {
|
|
12187
12343
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -12225,11 +12381,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
12225
12381
|
if (!serviceName) return true;
|
|
12226
12382
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
12227
12383
|
return graph.someNode(
|
|
12228
|
-
(_id, attrs) => attrs.type ===
|
|
12384
|
+
(_id, attrs) => attrs.type === import_types48.NodeType.ServiceNode && attrs.name === serviceName
|
|
12229
12385
|
);
|
|
12230
12386
|
}
|
|
12231
12387
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
12232
|
-
const paths = pathsForProject(entry.name,
|
|
12388
|
+
const paths = pathsForProject(entry.name, import_node_path52.default.join(entry.path, "neat-out"));
|
|
12233
12389
|
try {
|
|
12234
12390
|
const stat = await import_node_fs31.promises.stat(entry.path);
|
|
12235
12391
|
if (!stat.isDirectory()) {
|
|
@@ -12360,7 +12516,7 @@ async function startDaemon(opts = {}) {
|
|
|
12360
12516
|
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;
|
|
12361
12517
|
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;
|
|
12362
12518
|
const singleProject = projectArg;
|
|
12363
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
12519
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path52.default.resolve(projectPathArg) : null;
|
|
12364
12520
|
if (singleProject && !singleProjectPath) {
|
|
12365
12521
|
throw new Error(
|
|
12366
12522
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -12375,7 +12531,7 @@ async function startDaemon(opts = {}) {
|
|
|
12375
12531
|
);
|
|
12376
12532
|
}
|
|
12377
12533
|
}
|
|
12378
|
-
const pidPath =
|
|
12534
|
+
const pidPath = import_node_path52.default.join(home, "neatd.pid");
|
|
12379
12535
|
await writeAtomically(pidPath, `${process.pid}
|
|
12380
12536
|
`);
|
|
12381
12537
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12782,8 +12938,8 @@ async function startDaemon(opts = {}) {
|
|
|
12782
12938
|
let registryWatcher = null;
|
|
12783
12939
|
let reloadTimer = null;
|
|
12784
12940
|
if (!singleProject) try {
|
|
12785
|
-
const regDir =
|
|
12786
|
-
const regBase =
|
|
12941
|
+
const regDir = import_node_path52.default.dirname(regPath);
|
|
12942
|
+
const regBase = import_node_path52.default.basename(regPath);
|
|
12787
12943
|
registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
|
|
12788
12944
|
if (filename !== null && filename !== regBase) return;
|
|
12789
12945
|
if (reloadTimer) clearTimeout(reloadTimer);
|