@neat.is/core 0.6.0 → 0.6.2-dev.20260722
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-5PVQJLPR.js → chunk-64JJOXES.js} +3 -3
- package/dist/{chunk-MVINCLQM.js → chunk-OY7FGJAX.js} +2 -2
- package/dist/{chunk-CS4GHQO3.js → chunk-ZZ3VUCWL.js} +1581 -1329
- package/dist/chunk-ZZ3VUCWL.js.map +1 -0
- package/dist/cli.cjs +730 -468
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +579 -317
- 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 +588 -326
- 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 +2683 -2417
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-BZ3AJVAC.js.map +0 -1
- package/dist/chunk-CS4GHQO3.js.map +0 -1
- /package/dist/{chunk-5PVQJLPR.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/neatd.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path55 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path55) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path55)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path49.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path49.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url,
|
|
266
|
+
var import_node_url, import_node_path49, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -347,6 +347,14 @@ function normalizeDbSystem(attrs) {
|
|
|
347
347
|
if (typeof raw !== "string") return void 0;
|
|
348
348
|
return raw === "mongoose" ? "mongodb" : raw;
|
|
349
349
|
}
|
|
350
|
+
function tableFromSqlStatement(sql) {
|
|
351
|
+
if (typeof sql !== "string" || sql.length === 0) return null;
|
|
352
|
+
if (/\bjoin\b/i.test(sql)) return null;
|
|
353
|
+
const froms = sql.match(/\bfrom\b/gi);
|
|
354
|
+
if (froms && froms.length > 1) return null;
|
|
355
|
+
const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
|
|
356
|
+
return m ? m[1] : null;
|
|
357
|
+
}
|
|
350
358
|
function messagingDestinationOf(attrs) {
|
|
351
359
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
352
360
|
const v = attrs[key];
|
|
@@ -367,8 +375,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
367
375
|
const v = attrs[key];
|
|
368
376
|
if (typeof v === "string" && v.length > 0) {
|
|
369
377
|
const q = v.indexOf("?");
|
|
370
|
-
const
|
|
371
|
-
if (
|
|
378
|
+
const path55 = q === -1 ? v : v.slice(0, q);
|
|
379
|
+
if (path55.length > 0) return path55;
|
|
372
380
|
}
|
|
373
381
|
}
|
|
374
382
|
return void 0;
|
|
@@ -404,6 +412,7 @@ function parseOtlpRequest(body) {
|
|
|
404
412
|
dbSystem: normalizeDbSystem(attrs),
|
|
405
413
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
406
414
|
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
415
|
+
dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
|
|
407
416
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
408
417
|
messagingDestination: messagingDestinationOf(attrs),
|
|
409
418
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -423,10 +432,10 @@ function parseOtlpRequest(body) {
|
|
|
423
432
|
return out;
|
|
424
433
|
}
|
|
425
434
|
function loadProtoRoot() {
|
|
426
|
-
const here =
|
|
427
|
-
const protoRoot =
|
|
435
|
+
const here = import_node_path50.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
436
|
+
const protoRoot = import_node_path50.default.resolve(here, "..", "proto");
|
|
428
437
|
const root = new import_protobufjs.default.Root();
|
|
429
|
-
root.resolvePath = (_origin, target) =>
|
|
438
|
+
root.resolvePath = (_origin, target) => import_node_path50.default.resolve(protoRoot, target);
|
|
430
439
|
root.loadSync(
|
|
431
440
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
432
441
|
{ keepCase: true }
|
|
@@ -654,12 +663,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
654
663
|
}
|
|
655
664
|
}
|
|
656
665
|
}
|
|
657
|
-
var
|
|
666
|
+
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;
|
|
658
667
|
var init_otel = __esm({
|
|
659
668
|
"src/otel.ts"() {
|
|
660
669
|
"use strict";
|
|
661
670
|
init_cjs_shims();
|
|
662
|
-
|
|
671
|
+
import_node_path50 = __toESM(require("path"), 1);
|
|
663
672
|
import_node_url2 = require("url");
|
|
664
673
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
665
674
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -683,13 +692,13 @@ __export(neatd_exports, {
|
|
|
683
692
|
module.exports = __toCommonJS(neatd_exports);
|
|
684
693
|
init_cjs_shims();
|
|
685
694
|
var import_node_fs33 = require("fs");
|
|
686
|
-
var
|
|
695
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
687
696
|
var import_node_module2 = require("module");
|
|
688
697
|
|
|
689
698
|
// src/daemon.ts
|
|
690
699
|
init_cjs_shims();
|
|
691
700
|
var import_node_fs31 = require("fs");
|
|
692
|
-
var
|
|
701
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
693
702
|
var import_node_module = require("module");
|
|
694
703
|
|
|
695
704
|
// src/graph.ts
|
|
@@ -1219,19 +1228,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1219
1228
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1220
1229
|
let best = { path: [start], edges: [] };
|
|
1221
1230
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1222
|
-
function step(node,
|
|
1223
|
-
if (
|
|
1224
|
-
best = { path: [...
|
|
1231
|
+
function step(node, path55, edges) {
|
|
1232
|
+
if (path55.length > best.path.length) {
|
|
1233
|
+
best = { path: [...path55], edges: [...edges] };
|
|
1225
1234
|
}
|
|
1226
|
-
if (
|
|
1235
|
+
if (path55.length - 1 >= maxDepth) return;
|
|
1227
1236
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1228
1237
|
for (const [srcId, edge] of incoming) {
|
|
1229
1238
|
if (visited.has(srcId)) continue;
|
|
1230
1239
|
visited.add(srcId);
|
|
1231
|
-
|
|
1240
|
+
path55.push(srcId);
|
|
1232
1241
|
edges.push(edge);
|
|
1233
|
-
step(srcId,
|
|
1234
|
-
|
|
1242
|
+
step(srcId, path55, edges);
|
|
1243
|
+
path55.pop();
|
|
1235
1244
|
edges.pop();
|
|
1236
1245
|
visited.delete(srcId);
|
|
1237
1246
|
}
|
|
@@ -1239,11 +1248,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1239
1248
|
step(start, [start], []);
|
|
1240
1249
|
return best;
|
|
1241
1250
|
}
|
|
1242
|
-
function databaseRootCauseShape(graph, origin,
|
|
1251
|
+
function databaseRootCauseShape(graph, origin, walk4) {
|
|
1243
1252
|
const targetDb = origin;
|
|
1244
1253
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1245
1254
|
if (candidatePairs.length === 0) return null;
|
|
1246
|
-
for (const id of
|
|
1255
|
+
for (const id of walk4.path) {
|
|
1247
1256
|
const owner = resolveOwningService(graph, id);
|
|
1248
1257
|
if (!owner) continue;
|
|
1249
1258
|
const { id: serviceId6, svc } = owner;
|
|
@@ -1270,8 +1279,8 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1270
1279
|
}
|
|
1271
1280
|
return null;
|
|
1272
1281
|
}
|
|
1273
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1274
|
-
for (const id of
|
|
1282
|
+
function serviceRootCauseShape(graph, _origin, walk4) {
|
|
1283
|
+
for (const id of walk4.path) {
|
|
1275
1284
|
const owner = resolveOwningService(graph, id);
|
|
1276
1285
|
if (!owner) continue;
|
|
1277
1286
|
const { id: serviceId6, svc } = owner;
|
|
@@ -1307,10 +1316,10 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1307
1316
|
}
|
|
1308
1317
|
return null;
|
|
1309
1318
|
}
|
|
1310
|
-
function fileRootCauseShape(graph, origin,
|
|
1319
|
+
function fileRootCauseShape(graph, origin, walk4) {
|
|
1311
1320
|
const owner = resolveOwningService(graph, origin.id);
|
|
1312
1321
|
if (!owner) return null;
|
|
1313
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1322
|
+
return serviceRootCauseShape(graph, owner.svc, walk4);
|
|
1314
1323
|
}
|
|
1315
1324
|
var rootCauseShapes = {
|
|
1316
1325
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1322,16 +1331,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1322
1331
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1323
1332
|
const shape = rootCauseShapes[origin.type];
|
|
1324
1333
|
if (shape) {
|
|
1325
|
-
const
|
|
1326
|
-
const match = shape(graph, origin,
|
|
1334
|
+
const walk4 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1335
|
+
const match = shape(graph, origin, walk4);
|
|
1327
1336
|
if (match) {
|
|
1328
1337
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1329
1338
|
return import_types.RootCauseResultSchema.parse({
|
|
1330
1339
|
rootCauseNode: match.rootCauseNode,
|
|
1331
1340
|
rootCauseReason: reason,
|
|
1332
|
-
traversalPath:
|
|
1333
|
-
edgeProvenances:
|
|
1334
|
-
confidence: confidenceFromMix(
|
|
1341
|
+
traversalPath: walk4.path,
|
|
1342
|
+
edgeProvenances: walk4.edges.map((e) => e.provenance),
|
|
1343
|
+
confidence: confidenceFromMix(walk4.edges),
|
|
1335
1344
|
fixRecommendation: match.fixRecommendation
|
|
1336
1345
|
});
|
|
1337
1346
|
}
|
|
@@ -1352,8 +1361,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1352
1361
|
if (relevant.length === 0) return null;
|
|
1353
1362
|
const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
1354
1363
|
const attrs = latest.attributes ?? {};
|
|
1355
|
-
const filepath =
|
|
1356
|
-
const lineno =
|
|
1364
|
+
const filepath = codeFilepathOf(attrs);
|
|
1365
|
+
const lineno = codeLinenoOf(attrs);
|
|
1357
1366
|
const route = typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0;
|
|
1358
1367
|
const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
|
|
1359
1368
|
const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage);
|
|
@@ -1425,26 +1434,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1425
1434
|
return best;
|
|
1426
1435
|
}
|
|
1427
1436
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1428
|
-
const
|
|
1437
|
+
const path55 = [originServiceId];
|
|
1429
1438
|
const edges = [];
|
|
1430
1439
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1431
1440
|
let current = originServiceId;
|
|
1432
1441
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1433
1442
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1434
1443
|
if (!hop) break;
|
|
1435
|
-
|
|
1444
|
+
path55.push(hop.nextService);
|
|
1436
1445
|
edges.push(hop.edge);
|
|
1437
1446
|
visited.add(hop.nextService);
|
|
1438
1447
|
current = hop.nextService;
|
|
1439
1448
|
}
|
|
1440
1449
|
if (edges.length === 0) return null;
|
|
1441
|
-
return { path:
|
|
1450
|
+
return { path: path55, edges, culprit: current };
|
|
1442
1451
|
}
|
|
1443
1452
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1444
1453
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1445
1454
|
if (!chain) return null;
|
|
1446
1455
|
const culprit = chain.culprit;
|
|
1447
|
-
const
|
|
1456
|
+
const path55 = [...chain.path];
|
|
1448
1457
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1449
1458
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1450
1459
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1452,14 +1461,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1452
1461
|
if (loc) {
|
|
1453
1462
|
let rootCauseNode = culprit;
|
|
1454
1463
|
if (loc.fileNode) {
|
|
1455
|
-
|
|
1464
|
+
path55.push(loc.fileNode);
|
|
1456
1465
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1457
1466
|
rootCauseNode = loc.fileNode;
|
|
1458
1467
|
}
|
|
1459
1468
|
return import_types.RootCauseResultSchema.parse({
|
|
1460
1469
|
rootCauseNode,
|
|
1461
1470
|
rootCauseReason: loc.rootCauseReason,
|
|
1462
|
-
traversalPath:
|
|
1471
|
+
traversalPath: path55,
|
|
1463
1472
|
edgeProvenances,
|
|
1464
1473
|
confidence,
|
|
1465
1474
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1471,7 +1480,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1471
1480
|
return import_types.RootCauseResultSchema.parse({
|
|
1472
1481
|
rootCauseNode: culprit,
|
|
1473
1482
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1474
|
-
traversalPath:
|
|
1483
|
+
traversalPath: path55,
|
|
1475
1484
|
edgeProvenances,
|
|
1476
1485
|
confidence,
|
|
1477
1486
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2229,9 +2238,30 @@ function isLoopbackHost(host) {
|
|
|
2229
2238
|
const h = host.toLowerCase();
|
|
2230
2239
|
return h === "localhost" || h === "ip6-localhost" || h === "::1" || h === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(h);
|
|
2231
2240
|
}
|
|
2241
|
+
var CODE_FILE_PATH_ATTR = "code.file.path";
|
|
2232
2242
|
var CODE_FILEPATH_ATTR = "code.filepath";
|
|
2243
|
+
var CODE_LINE_NUMBER_ATTR = "code.line.number";
|
|
2233
2244
|
var CODE_LINENO_ATTR = "code.lineno";
|
|
2245
|
+
var CODE_FUNCTION_NAME_ATTR = "code.function.name";
|
|
2234
2246
|
var CODE_FUNCTION_ATTR = "code.function";
|
|
2247
|
+
function codeFilepathOf(attrs) {
|
|
2248
|
+
const stable = attrs[CODE_FILE_PATH_ATTR];
|
|
2249
|
+
if (typeof stable === "string" && stable.length > 0) return stable;
|
|
2250
|
+
const prior = attrs[CODE_FILEPATH_ATTR];
|
|
2251
|
+
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
2252
|
+
}
|
|
2253
|
+
function codeLinenoOf(attrs) {
|
|
2254
|
+
const stable = attrs[CODE_LINE_NUMBER_ATTR];
|
|
2255
|
+
if (typeof stable === "number" && Number.isFinite(stable)) return stable;
|
|
2256
|
+
const prior = attrs[CODE_LINENO_ATTR];
|
|
2257
|
+
return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
|
|
2258
|
+
}
|
|
2259
|
+
function codeFunctionOf(attrs) {
|
|
2260
|
+
const stable = attrs[CODE_FUNCTION_NAME_ATTR];
|
|
2261
|
+
if (typeof stable === "string" && stable.length > 0) return stable;
|
|
2262
|
+
const prior = attrs[CODE_FUNCTION_ATTR];
|
|
2263
|
+
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
2264
|
+
}
|
|
2235
2265
|
function toPosix(p) {
|
|
2236
2266
|
return p.split("\\").join("/");
|
|
2237
2267
|
}
|
|
@@ -2309,10 +2339,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
2309
2339
|
}
|
|
2310
2340
|
}
|
|
2311
2341
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
2312
|
-
const filepath = span.attributes
|
|
2313
|
-
if (
|
|
2314
|
-
|
|
2315
|
-
let line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
|
|
2342
|
+
const filepath = codeFilepathOf(span.attributes);
|
|
2343
|
+
if (filepath === void 0) return null;
|
|
2344
|
+
let line = codeLinenoOf(span.attributes);
|
|
2316
2345
|
const abs = toPosix(filepath).replace(/^file:\/\//, "");
|
|
2317
2346
|
const resolved = resolveDistToSrc(abs, line);
|
|
2318
2347
|
let effectivePath = filepath;
|
|
@@ -2327,8 +2356,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
2327
2356
|
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
2328
2357
|
warnNoSourceMaps(serviceNode.name);
|
|
2329
2358
|
}
|
|
2330
|
-
const
|
|
2331
|
-
const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
|
|
2359
|
+
const fn = codeFunctionOf(span.attributes);
|
|
2332
2360
|
return {
|
|
2333
2361
|
relPath,
|
|
2334
2362
|
...line !== void 0 ? { line } : {},
|
|
@@ -2910,6 +2938,18 @@ async function handleSpan(ctx, span) {
|
|
|
2910
2938
|
callSiteEvidence
|
|
2911
2939
|
);
|
|
2912
2940
|
}
|
|
2941
|
+
if (span.dbTable) {
|
|
2942
|
+
const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
|
|
2943
|
+
upsertObservedEdge(
|
|
2944
|
+
ctx.graph,
|
|
2945
|
+
import_types3.EdgeType.CALLS,
|
|
2946
|
+
observedSource(),
|
|
2947
|
+
tableId,
|
|
2948
|
+
ts,
|
|
2949
|
+
isError,
|
|
2950
|
+
callSiteEvidence
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2913
2953
|
}
|
|
2914
2954
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2915
2955
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -4074,14 +4114,14 @@ function buildServiceHostIndex(services) {
|
|
|
4074
4114
|
}
|
|
4075
4115
|
async function walkSourceFiles(dir) {
|
|
4076
4116
|
const out = [];
|
|
4077
|
-
async function
|
|
4117
|
+
async function walk4(current) {
|
|
4078
4118
|
const entries = await import_node_fs10.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
4079
4119
|
for (const entry2 of entries) {
|
|
4080
4120
|
const full = import_node_path10.default.join(current, entry2.name);
|
|
4081
4121
|
if (entry2.isDirectory()) {
|
|
4082
4122
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4083
4123
|
if (await isPythonVenvDir(full)) continue;
|
|
4084
|
-
await
|
|
4124
|
+
await walk4(full);
|
|
4085
4125
|
} else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path10.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
4086
4126
|
// would attribute our instrumentation imports to the user's service.
|
|
4087
4127
|
!isNeatAuthoredSourceFile(entry2.name)) {
|
|
@@ -4089,7 +4129,7 @@ async function walkSourceFiles(dir) {
|
|
|
4089
4129
|
}
|
|
4090
4130
|
}
|
|
4091
4131
|
}
|
|
4092
|
-
await
|
|
4132
|
+
await walk4(dir);
|
|
4093
4133
|
return out;
|
|
4094
4134
|
}
|
|
4095
4135
|
async function loadSourceFiles(dir) {
|
|
@@ -5239,20 +5279,20 @@ var import_node_path21 = __toESM(require("path"), 1);
|
|
|
5239
5279
|
var import_types10 = require("@neat.is/types");
|
|
5240
5280
|
async function walkConfigFiles(dir) {
|
|
5241
5281
|
const out = [];
|
|
5242
|
-
async function
|
|
5282
|
+
async function walk4(current) {
|
|
5243
5283
|
const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true });
|
|
5244
5284
|
for (const entry2 of entries) {
|
|
5245
5285
|
const full = import_node_path21.default.join(current, entry2.name);
|
|
5246
5286
|
if (entry2.isDirectory()) {
|
|
5247
5287
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
5248
5288
|
if (await isPythonVenvDir(full)) continue;
|
|
5249
|
-
await
|
|
5289
|
+
await walk4(full);
|
|
5250
5290
|
} else if (entry2.isFile() && isConfigFile(entry2.name).match) {
|
|
5251
5291
|
out.push(full);
|
|
5252
5292
|
}
|
|
5253
5293
|
}
|
|
5254
5294
|
}
|
|
5255
|
-
await
|
|
5295
|
+
await walk4(dir);
|
|
5256
5296
|
return out;
|
|
5257
5297
|
}
|
|
5258
5298
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -5305,6 +5345,7 @@ init_cjs_shims();
|
|
|
5305
5345
|
var import_node_path22 = __toESM(require("path"), 1);
|
|
5306
5346
|
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
5307
5347
|
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5348
|
+
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
5308
5349
|
var import_types11 = require("@neat.is/types");
|
|
5309
5350
|
var PARSE_CHUNK2 = 16384;
|
|
5310
5351
|
function parseSource2(parser, source) {
|
|
@@ -5317,6 +5358,11 @@ function makeJsParser2() {
|
|
|
5317
5358
|
p.setLanguage(import_tree_sitter_javascript2.default);
|
|
5318
5359
|
return p;
|
|
5319
5360
|
}
|
|
5361
|
+
function makePyParser2() {
|
|
5362
|
+
const p = new import_tree_sitter2.default();
|
|
5363
|
+
p.setLanguage(import_tree_sitter_python2.default);
|
|
5364
|
+
return p;
|
|
5365
|
+
}
|
|
5320
5366
|
var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
5321
5367
|
"get",
|
|
5322
5368
|
"post",
|
|
@@ -5329,6 +5375,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
|
5329
5375
|
]);
|
|
5330
5376
|
var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
5331
5377
|
var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
5378
|
+
var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
|
|
5332
5379
|
function canonicalizeTemplate(raw) {
|
|
5333
5380
|
let p = raw.split("?")[0].split("#")[0];
|
|
5334
5381
|
if (!p.startsWith("/")) p = "/" + p;
|
|
@@ -5556,8 +5603,102 @@ function nextRoutesFromFile(source, relFile, parser) {
|
|
|
5556
5603
|
}
|
|
5557
5604
|
return [];
|
|
5558
5605
|
}
|
|
5606
|
+
function pyStaticStringText(node) {
|
|
5607
|
+
if (node.type !== "string") return null;
|
|
5608
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5609
|
+
const child = node.namedChild(i);
|
|
5610
|
+
if (child?.type === "interpolation") return null;
|
|
5611
|
+
if (child?.type === "string_content") return child.text;
|
|
5612
|
+
}
|
|
5613
|
+
return "";
|
|
5614
|
+
}
|
|
5615
|
+
function keywordArrayStrings(argsNode, key) {
|
|
5616
|
+
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
5617
|
+
const arg = argsNode.namedChild(i);
|
|
5618
|
+
if (arg?.type !== "keyword_argument") continue;
|
|
5619
|
+
if (arg.childForFieldName("name")?.text !== key) continue;
|
|
5620
|
+
const val = arg.childForFieldName("value");
|
|
5621
|
+
if (!val || val.type !== "list") return [];
|
|
5622
|
+
const out = [];
|
|
5623
|
+
for (let j = 0; j < val.namedChildCount; j++) {
|
|
5624
|
+
const el = val.namedChild(j);
|
|
5625
|
+
if (el?.type === "string") {
|
|
5626
|
+
const s = pyStaticStringText(el);
|
|
5627
|
+
if (s) out.push(s);
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
return out;
|
|
5631
|
+
}
|
|
5632
|
+
return [];
|
|
5633
|
+
}
|
|
5634
|
+
function collectApiRouterPrefixes(root) {
|
|
5635
|
+
const prefixes = /* @__PURE__ */ new Map();
|
|
5636
|
+
walk(root, (node) => {
|
|
5637
|
+
if (node.type !== "assignment") return;
|
|
5638
|
+
const right = node.childForFieldName("right");
|
|
5639
|
+
if (!right || right.type !== "call") return;
|
|
5640
|
+
const fn = right.childForFieldName("function");
|
|
5641
|
+
if (!fn) return;
|
|
5642
|
+
const ctor = fn.type === "attribute" ? fn.childForFieldName("attribute")?.text : fn.text;
|
|
5643
|
+
if (ctor !== "APIRouter") return;
|
|
5644
|
+
const left = node.childForFieldName("left");
|
|
5645
|
+
if (!left || left.type !== "identifier") return;
|
|
5646
|
+
const args = right.childForFieldName("arguments");
|
|
5647
|
+
if (!args) return;
|
|
5648
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
5649
|
+
const arg = args.namedChild(i);
|
|
5650
|
+
if (arg?.type !== "keyword_argument") continue;
|
|
5651
|
+
if (arg.childForFieldName("name")?.text !== "prefix") continue;
|
|
5652
|
+
const val = arg.childForFieldName("value");
|
|
5653
|
+
const p = val ? pyStaticStringText(val) : null;
|
|
5654
|
+
if (p !== null) prefixes.set(left.text, p);
|
|
5655
|
+
}
|
|
5656
|
+
});
|
|
5657
|
+
return prefixes;
|
|
5658
|
+
}
|
|
5659
|
+
function fastapiRoutesFromSource(source, parser) {
|
|
5660
|
+
const tree = parseSource2(parser, source);
|
|
5661
|
+
const prefixes = collectApiRouterPrefixes(tree.rootNode);
|
|
5662
|
+
const out = [];
|
|
5663
|
+
walk(tree.rootNode, (node) => {
|
|
5664
|
+
if (node.type !== "decorator") return;
|
|
5665
|
+
const call = node.namedChild(0);
|
|
5666
|
+
if (!call || call.type !== "call") return;
|
|
5667
|
+
const fn = call.childForFieldName("function");
|
|
5668
|
+
if (!fn || fn.type !== "attribute") return;
|
|
5669
|
+
const method = fn.childForFieldName("attribute")?.text?.toLowerCase();
|
|
5670
|
+
if (!method) return;
|
|
5671
|
+
const isVerb = FASTAPI_METHODS.has(method);
|
|
5672
|
+
if (!isVerb && method !== "api_route") return;
|
|
5673
|
+
const args = call.childForFieldName("arguments");
|
|
5674
|
+
const first = args?.namedChild(0);
|
|
5675
|
+
if (!first || first.type !== "string") return;
|
|
5676
|
+
const rawPath = pyStaticStringText(first);
|
|
5677
|
+
if (rawPath === null || !rawPath.startsWith("/")) return;
|
|
5678
|
+
const obj = fn.childForFieldName("object")?.text;
|
|
5679
|
+
const prefix = obj ? prefixes.get(obj) ?? "" : "";
|
|
5680
|
+
const pathTemplate = canonicalizeTemplate(prefix + rawPath);
|
|
5681
|
+
const line = node.startPosition.row + 1;
|
|
5682
|
+
if (isVerb) {
|
|
5683
|
+
out.push({ method: method.toUpperCase(), pathTemplate, line, framework: "fastapi" });
|
|
5684
|
+
return;
|
|
5685
|
+
}
|
|
5686
|
+
const methods = keywordArrayStrings(args, "methods");
|
|
5687
|
+
const list = methods.length > 0 ? methods : ["ALL"];
|
|
5688
|
+
for (const m of list) {
|
|
5689
|
+
out.push({
|
|
5690
|
+
method: m === "ALL" ? "ALL" : m.toUpperCase(),
|
|
5691
|
+
pathTemplate,
|
|
5692
|
+
line,
|
|
5693
|
+
framework: "fastapi"
|
|
5694
|
+
});
|
|
5695
|
+
}
|
|
5696
|
+
});
|
|
5697
|
+
return out;
|
|
5698
|
+
}
|
|
5559
5699
|
async function addRoutes(graph, services) {
|
|
5560
5700
|
const jsParser = makeJsParser2();
|
|
5701
|
+
const pyParser = makePyParser2();
|
|
5561
5702
|
let nodesAdded = 0;
|
|
5562
5703
|
let edgesAdded = 0;
|
|
5563
5704
|
for (const service of services) {
|
|
@@ -5569,15 +5710,20 @@ async function addRoutes(graph, services) {
|
|
|
5569
5710
|
const hasFastify = deps["fastify"] !== void 0;
|
|
5570
5711
|
const hasHono = deps["hono"] !== void 0;
|
|
5571
5712
|
const hasNext = deps["next"] !== void 0;
|
|
5572
|
-
|
|
5713
|
+
const hasFastapi = deps["fastapi"] !== void 0;
|
|
5714
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasFastapi) continue;
|
|
5573
5715
|
const files = await loadSourceFiles(service.dir);
|
|
5574
5716
|
for (const file of files) {
|
|
5575
5717
|
if (isTestPath(file.path)) continue;
|
|
5576
|
-
|
|
5718
|
+
const ext = import_node_path22.default.extname(file.path);
|
|
5719
|
+
const isPy = ext === ".py";
|
|
5720
|
+
if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy) continue;
|
|
5577
5721
|
const relFile = toPosix2(import_node_path22.default.relative(service.dir, file.path));
|
|
5578
5722
|
let routes;
|
|
5579
5723
|
try {
|
|
5580
|
-
if (
|
|
5724
|
+
if (isPy) {
|
|
5725
|
+
routes = hasFastapi ? fastapiRoutesFromSource(file.content, pyParser) : [];
|
|
5726
|
+
} else if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
|
|
5581
5727
|
routes = nextRoutesFromFile(file.content, relFile, jsParser);
|
|
5582
5728
|
} else if (hasExpress || hasFastify || hasHono) {
|
|
5583
5729
|
routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify, hasHono);
|
|
@@ -5673,20 +5819,20 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
5673
5819
|
}
|
|
5674
5820
|
async function walkProtoFiles(dir) {
|
|
5675
5821
|
const out = [];
|
|
5676
|
-
async function
|
|
5822
|
+
async function walk4(current) {
|
|
5677
5823
|
const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
5678
5824
|
for (const entry2 of entries) {
|
|
5679
5825
|
const full = import_node_path23.default.join(current, entry2.name);
|
|
5680
5826
|
if (entry2.isDirectory()) {
|
|
5681
5827
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
5682
5828
|
if (await isPythonVenvDir(full)) continue;
|
|
5683
|
-
await
|
|
5829
|
+
await walk4(full);
|
|
5684
5830
|
} else if (entry2.isFile() && import_node_path23.default.extname(entry2.name) === PROTO_EXTENSION) {
|
|
5685
5831
|
out.push(full);
|
|
5686
5832
|
}
|
|
5687
5833
|
}
|
|
5688
5834
|
}
|
|
5689
|
-
await
|
|
5835
|
+
await walk4(dir);
|
|
5690
5836
|
return out;
|
|
5691
5837
|
}
|
|
5692
5838
|
async function addGrpcMethods(graph, services) {
|
|
@@ -5754,14 +5900,14 @@ async function addGrpcMethods(graph, services) {
|
|
|
5754
5900
|
|
|
5755
5901
|
// src/extract/calls/index.ts
|
|
5756
5902
|
init_cjs_shims();
|
|
5757
|
-
var
|
|
5903
|
+
var import_types22 = require("@neat.is/types");
|
|
5758
5904
|
|
|
5759
5905
|
// src/extract/calls/http.ts
|
|
5760
5906
|
init_cjs_shims();
|
|
5761
5907
|
var import_node_path24 = __toESM(require("path"), 1);
|
|
5762
5908
|
var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
5763
5909
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5764
|
-
var
|
|
5910
|
+
var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
|
|
5765
5911
|
var import_types13 = require("@neat.is/types");
|
|
5766
5912
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
5767
5913
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
@@ -5816,14 +5962,14 @@ function makeJsParser3() {
|
|
|
5816
5962
|
p.setLanguage(import_tree_sitter_javascript3.default);
|
|
5817
5963
|
return p;
|
|
5818
5964
|
}
|
|
5819
|
-
function
|
|
5965
|
+
function makePyParser3() {
|
|
5820
5966
|
const p = new import_tree_sitter3.default();
|
|
5821
|
-
p.setLanguage(
|
|
5967
|
+
p.setLanguage(import_tree_sitter_python3.default);
|
|
5822
5968
|
return p;
|
|
5823
5969
|
}
|
|
5824
5970
|
async function addHttpCallEdges(graph, services) {
|
|
5825
5971
|
const jsParser = makeJsParser3();
|
|
5826
|
-
const pyParser =
|
|
5972
|
+
const pyParser = makePyParser3();
|
|
5827
5973
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
5828
5974
|
let nodesAdded = 0;
|
|
5829
5975
|
let edgesAdded = 0;
|
|
@@ -6763,15 +6909,130 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
6763
6909
|
return out;
|
|
6764
6910
|
}
|
|
6765
6911
|
|
|
6912
|
+
// src/extract/calls/sqlalchemy.ts
|
|
6913
|
+
init_cjs_shims();
|
|
6914
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6915
|
+
var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
|
|
6916
|
+
var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
6917
|
+
var import_types21 = require("@neat.is/types");
|
|
6918
|
+
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
6919
|
+
var PARSE_CHUNK5 = 16384;
|
|
6920
|
+
function makePyParser4() {
|
|
6921
|
+
const p = new import_tree_sitter5.default();
|
|
6922
|
+
p.setLanguage(import_tree_sitter_python4.default);
|
|
6923
|
+
return p;
|
|
6924
|
+
}
|
|
6925
|
+
function parseSource5(parser, source) {
|
|
6926
|
+
return parser.parse(
|
|
6927
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
6928
|
+
);
|
|
6929
|
+
}
|
|
6930
|
+
function flaskSqlalchemyTableName(className) {
|
|
6931
|
+
return className.replace(/((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g, "_$1").toLowerCase().replace(/^_+/, "");
|
|
6932
|
+
}
|
|
6933
|
+
function namedChildren(node) {
|
|
6934
|
+
const out = [];
|
|
6935
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6936
|
+
const c = node.namedChild(i);
|
|
6937
|
+
if (c) out.push(c);
|
|
6938
|
+
}
|
|
6939
|
+
return out;
|
|
6940
|
+
}
|
|
6941
|
+
function pyStaticStringText2(node) {
|
|
6942
|
+
if (node.type !== "string") return null;
|
|
6943
|
+
for (const child of namedChildren(node)) {
|
|
6944
|
+
if (child.type === "interpolation") return null;
|
|
6945
|
+
if (child.type === "string_content") return child.text;
|
|
6946
|
+
}
|
|
6947
|
+
return "";
|
|
6948
|
+
}
|
|
6949
|
+
function explicitTablename(body) {
|
|
6950
|
+
for (const stmt of namedChildren(body)) {
|
|
6951
|
+
if (stmt.type !== "expression_statement") continue;
|
|
6952
|
+
const assign = stmt.namedChild(0);
|
|
6953
|
+
if (assign?.type !== "assignment") continue;
|
|
6954
|
+
if (assign.childForFieldName("left")?.text !== "__tablename__") continue;
|
|
6955
|
+
const right = assign.childForFieldName("right");
|
|
6956
|
+
if (right?.type === "string") {
|
|
6957
|
+
const s = pyStaticStringText2(right);
|
|
6958
|
+
return s ? { name: s } : "computed";
|
|
6959
|
+
}
|
|
6960
|
+
return "computed";
|
|
6961
|
+
}
|
|
6962
|
+
return null;
|
|
6963
|
+
}
|
|
6964
|
+
function extendsFlaskModel(cls) {
|
|
6965
|
+
const supers = cls.childForFieldName("superclasses");
|
|
6966
|
+
if (!supers) return false;
|
|
6967
|
+
for (const a of namedChildren(supers)) {
|
|
6968
|
+
const t = a.text;
|
|
6969
|
+
if (t === "db.Model" || t === "Model" || t.endsWith(".Model")) return true;
|
|
6970
|
+
}
|
|
6971
|
+
return false;
|
|
6972
|
+
}
|
|
6973
|
+
function walk3(node, visit) {
|
|
6974
|
+
visit(node);
|
|
6975
|
+
for (const c of namedChildren(node)) walk3(c, visit);
|
|
6976
|
+
}
|
|
6977
|
+
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
6978
|
+
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
6979
|
+
const tree = parseSource5(makePyParser4(), file.content);
|
|
6980
|
+
const out = [];
|
|
6981
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6982
|
+
const push = (name, line) => {
|
|
6983
|
+
if (seen.has(name)) return;
|
|
6984
|
+
seen.add(name);
|
|
6985
|
+
out.push({
|
|
6986
|
+
infraId: (0, import_types21.infraId)("sql-table", name),
|
|
6987
|
+
name,
|
|
6988
|
+
kind: "sql-table",
|
|
6989
|
+
edgeType: "CALLS",
|
|
6990
|
+
confidenceKind: "verified-call-site",
|
|
6991
|
+
evidence: {
|
|
6992
|
+
file: import_node_path32.default.relative(serviceDir, file.path),
|
|
6993
|
+
line,
|
|
6994
|
+
snippet: snippet(file.content, line)
|
|
6995
|
+
}
|
|
6996
|
+
});
|
|
6997
|
+
};
|
|
6998
|
+
walk3(tree.rootNode, (node) => {
|
|
6999
|
+
if (node.type === "class_definition") {
|
|
7000
|
+
const body = node.childForFieldName("body");
|
|
7001
|
+
const nameNode = node.childForFieldName("name");
|
|
7002
|
+
if (!body || !nameNode) return;
|
|
7003
|
+
const line = node.startPosition.row + 1;
|
|
7004
|
+
const explicit = explicitTablename(body);
|
|
7005
|
+
if (explicit === "computed") return;
|
|
7006
|
+
if (explicit) {
|
|
7007
|
+
push(explicit.name, line);
|
|
7008
|
+
return;
|
|
7009
|
+
}
|
|
7010
|
+
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
|
|
7011
|
+
return;
|
|
7012
|
+
}
|
|
7013
|
+
if (node.type === "call") {
|
|
7014
|
+
const fn = node.childForFieldName("function");
|
|
7015
|
+
const fnText = fn?.text;
|
|
7016
|
+
if (fnText !== "Table" && fnText !== "sa.Table" && fnText !== "sqlalchemy.Table") return;
|
|
7017
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
7018
|
+
if (first?.type === "string") {
|
|
7019
|
+
const s = pyStaticStringText2(first);
|
|
7020
|
+
if (s) push(s, node.startPosition.row + 1);
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
});
|
|
7024
|
+
return out;
|
|
7025
|
+
}
|
|
7026
|
+
|
|
6766
7027
|
// src/extract/calls/index.ts
|
|
6767
7028
|
function edgeTypeFromEndpoint(ep) {
|
|
6768
7029
|
switch (ep.edgeType) {
|
|
6769
7030
|
case "PUBLISHES_TO":
|
|
6770
|
-
return
|
|
7031
|
+
return import_types22.EdgeType.PUBLISHES_TO;
|
|
6771
7032
|
case "CONSUMES_FROM":
|
|
6772
|
-
return
|
|
7033
|
+
return import_types22.EdgeType.CONSUMES_FROM;
|
|
6773
7034
|
default:
|
|
6774
|
-
return
|
|
7035
|
+
return import_types22.EdgeType.CALLS;
|
|
6775
7036
|
}
|
|
6776
7037
|
}
|
|
6777
7038
|
function isAwsKind(kind) {
|
|
@@ -6795,6 +7056,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6795
7056
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6796
7057
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6797
7058
|
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
7059
|
+
endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
|
|
6798
7060
|
}
|
|
6799
7061
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6800
7062
|
if (endpoints.length === 0) continue;
|
|
@@ -6803,7 +7065,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6803
7065
|
if (!graph.hasNode(ep.infraId)) {
|
|
6804
7066
|
const node = {
|
|
6805
7067
|
id: ep.infraId,
|
|
6806
|
-
type:
|
|
7068
|
+
type: import_types22.NodeType.InfraNode,
|
|
6807
7069
|
name: ep.name,
|
|
6808
7070
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6809
7071
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6815,7 +7077,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6815
7077
|
nodesAdded++;
|
|
6816
7078
|
}
|
|
6817
7079
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6818
|
-
const confidence = (0,
|
|
7080
|
+
const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
|
|
6819
7081
|
const relFile = toPosix2(ep.evidence.file);
|
|
6820
7082
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6821
7083
|
graph,
|
|
@@ -6825,7 +7087,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6825
7087
|
);
|
|
6826
7088
|
nodesAdded += n;
|
|
6827
7089
|
edgesAdded += e;
|
|
6828
|
-
if (!(0,
|
|
7090
|
+
if (!(0, import_types22.passesExtractedFloor)(confidence)) {
|
|
6829
7091
|
noteExtractedDropped({
|
|
6830
7092
|
source: fileNodeId,
|
|
6831
7093
|
target: ep.infraId,
|
|
@@ -6845,7 +7107,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6845
7107
|
source: fileNodeId,
|
|
6846
7108
|
target: ep.infraId,
|
|
6847
7109
|
type: edgeType,
|
|
6848
|
-
provenance:
|
|
7110
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6849
7111
|
confidence,
|
|
6850
7112
|
evidence: ep.evidence
|
|
6851
7113
|
};
|
|
@@ -6871,16 +7133,16 @@ init_cjs_shims();
|
|
|
6871
7133
|
|
|
6872
7134
|
// src/extract/infra/docker-compose.ts
|
|
6873
7135
|
init_cjs_shims();
|
|
6874
|
-
var
|
|
6875
|
-
var
|
|
7136
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
7137
|
+
var import_types24 = require("@neat.is/types");
|
|
6876
7138
|
|
|
6877
7139
|
// src/extract/infra/shared.ts
|
|
6878
7140
|
init_cjs_shims();
|
|
6879
|
-
var
|
|
7141
|
+
var import_types23 = require("@neat.is/types");
|
|
6880
7142
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6881
7143
|
return {
|
|
6882
|
-
id: (0,
|
|
6883
|
-
type:
|
|
7144
|
+
id: (0, import_types23.infraId)(kind, name),
|
|
7145
|
+
type: import_types23.NodeType.InfraNode,
|
|
6884
7146
|
name,
|
|
6885
7147
|
provider,
|
|
6886
7148
|
kind,
|
|
@@ -6924,8 +7186,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6924
7186
|
source: anchorId,
|
|
6925
7187
|
target: node.id,
|
|
6926
7188
|
type: edgeType,
|
|
6927
|
-
provenance:
|
|
6928
|
-
confidence: (0,
|
|
7189
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7190
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6929
7191
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6930
7192
|
};
|
|
6931
7193
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6942,7 +7204,7 @@ function dependsOnList(value) {
|
|
|
6942
7204
|
}
|
|
6943
7205
|
function serviceNameToServiceNode(name, services) {
|
|
6944
7206
|
for (const s of services) {
|
|
6945
|
-
if (s.node.name === name ||
|
|
7207
|
+
if (s.node.name === name || import_node_path33.default.basename(s.dir) === name) return s.node.id;
|
|
6946
7208
|
}
|
|
6947
7209
|
return null;
|
|
6948
7210
|
}
|
|
@@ -6951,7 +7213,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6951
7213
|
let edgesAdded = 0;
|
|
6952
7214
|
let composePath = null;
|
|
6953
7215
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6954
|
-
const abs =
|
|
7216
|
+
const abs = import_node_path33.default.join(scanPath, name);
|
|
6955
7217
|
if (await exists(abs)) {
|
|
6956
7218
|
composePath = abs;
|
|
6957
7219
|
break;
|
|
@@ -6964,13 +7226,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6964
7226
|
} catch (err) {
|
|
6965
7227
|
recordExtractionError(
|
|
6966
7228
|
"infra docker-compose",
|
|
6967
|
-
|
|
7229
|
+
import_node_path33.default.relative(scanPath, composePath),
|
|
6968
7230
|
err
|
|
6969
7231
|
);
|
|
6970
7232
|
return { nodesAdded, edgesAdded };
|
|
6971
7233
|
}
|
|
6972
7234
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6973
|
-
const evidenceFile =
|
|
7235
|
+
const evidenceFile = import_node_path33.default.relative(scanPath, composePath).split(import_node_path33.default.sep).join("/");
|
|
6974
7236
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6975
7237
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6976
7238
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6992,15 +7254,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6992
7254
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6993
7255
|
const targetId = composeNameToNodeId.get(dep);
|
|
6994
7256
|
if (!targetId) continue;
|
|
6995
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7257
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
|
|
6996
7258
|
if (graph.hasEdge(edgeId)) continue;
|
|
6997
7259
|
const edge = {
|
|
6998
7260
|
id: edgeId,
|
|
6999
7261
|
source: sourceId,
|
|
7000
7262
|
target: targetId,
|
|
7001
|
-
type:
|
|
7002
|
-
provenance:
|
|
7003
|
-
confidence: (0,
|
|
7263
|
+
type: import_types24.EdgeType.DEPENDS_ON,
|
|
7264
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7265
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
7004
7266
|
evidence: { file: evidenceFile }
|
|
7005
7267
|
};
|
|
7006
7268
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7012,9 +7274,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7012
7274
|
|
|
7013
7275
|
// src/extract/infra/dockerfile.ts
|
|
7014
7276
|
init_cjs_shims();
|
|
7015
|
-
var
|
|
7277
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7016
7278
|
var import_node_fs16 = require("fs");
|
|
7017
|
-
var
|
|
7279
|
+
var import_types25 = require("@neat.is/types");
|
|
7018
7280
|
function readDockerfile(content) {
|
|
7019
7281
|
let image = null;
|
|
7020
7282
|
const ports = [];
|
|
@@ -7043,7 +7305,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7043
7305
|
let nodesAdded = 0;
|
|
7044
7306
|
let edgesAdded = 0;
|
|
7045
7307
|
for (const service of services) {
|
|
7046
|
-
const dockerfilePath =
|
|
7308
|
+
const dockerfilePath = import_node_path34.default.join(service.dir, "Dockerfile");
|
|
7047
7309
|
if (!await exists(dockerfilePath)) continue;
|
|
7048
7310
|
let content;
|
|
7049
7311
|
try {
|
|
@@ -7051,7 +7313,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7051
7313
|
} catch (err) {
|
|
7052
7314
|
recordExtractionError(
|
|
7053
7315
|
"infra dockerfile",
|
|
7054
|
-
|
|
7316
|
+
import_node_path34.default.relative(scanPath, dockerfilePath),
|
|
7055
7317
|
err
|
|
7056
7318
|
);
|
|
7057
7319
|
continue;
|
|
@@ -7063,8 +7325,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7063
7325
|
graph.addNode(node.id, node);
|
|
7064
7326
|
nodesAdded++;
|
|
7065
7327
|
}
|
|
7066
|
-
const relDockerfile = toPosix2(
|
|
7067
|
-
const evidenceFile = toPosix2(
|
|
7328
|
+
const relDockerfile = toPosix2(import_node_path34.default.relative(service.dir, dockerfilePath));
|
|
7329
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, dockerfilePath));
|
|
7068
7330
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7069
7331
|
graph,
|
|
7070
7332
|
service.pkg.name,
|
|
@@ -7073,15 +7335,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7073
7335
|
);
|
|
7074
7336
|
nodesAdded += fn;
|
|
7075
7337
|
edgesAdded += fe;
|
|
7076
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7338
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
|
|
7077
7339
|
if (!graph.hasEdge(edgeId)) {
|
|
7078
7340
|
const edge = {
|
|
7079
7341
|
id: edgeId,
|
|
7080
7342
|
source: fileNodeId,
|
|
7081
7343
|
target: node.id,
|
|
7082
|
-
type:
|
|
7083
|
-
provenance:
|
|
7084
|
-
confidence: (0,
|
|
7344
|
+
type: import_types25.EdgeType.RUNS_ON,
|
|
7345
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7346
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7085
7347
|
evidence: {
|
|
7086
7348
|
file: evidenceFile,
|
|
7087
7349
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -7096,15 +7358,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7096
7358
|
graph.addNode(portNode.id, portNode);
|
|
7097
7359
|
nodesAdded++;
|
|
7098
7360
|
}
|
|
7099
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7361
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
|
|
7100
7362
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
7101
7363
|
const portEdge = {
|
|
7102
7364
|
id: portEdgeId,
|
|
7103
7365
|
source: fileNodeId,
|
|
7104
7366
|
target: portNode.id,
|
|
7105
|
-
type:
|
|
7106
|
-
provenance:
|
|
7107
|
-
confidence: (0,
|
|
7367
|
+
type: import_types25.EdgeType.CONNECTS_TO,
|
|
7368
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7369
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7108
7370
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
7109
7371
|
};
|
|
7110
7372
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -7117,8 +7379,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7117
7379
|
// src/extract/infra/terraform.ts
|
|
7118
7380
|
init_cjs_shims();
|
|
7119
7381
|
var import_node_fs17 = require("fs");
|
|
7120
|
-
var
|
|
7121
|
-
var
|
|
7382
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7383
|
+
var import_types26 = require("@neat.is/types");
|
|
7122
7384
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
7123
7385
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
7124
7386
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -7128,11 +7390,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
7128
7390
|
for (const entry2 of entries) {
|
|
7129
7391
|
if (entry2.isDirectory()) {
|
|
7130
7392
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
7131
|
-
const child =
|
|
7393
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
7132
7394
|
if (await isPythonVenvDir(child)) continue;
|
|
7133
7395
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
7134
7396
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
7135
|
-
out.push(
|
|
7397
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
7136
7398
|
}
|
|
7137
7399
|
}
|
|
7138
7400
|
return out;
|
|
@@ -7164,7 +7426,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7164
7426
|
const files = await walkTfFiles(scanPath);
|
|
7165
7427
|
for (const file of files) {
|
|
7166
7428
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
7167
|
-
const evidenceFile = toPosix2(
|
|
7429
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, file));
|
|
7168
7430
|
const resources = [];
|
|
7169
7431
|
const byKey = /* @__PURE__ */ new Map();
|
|
7170
7432
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -7199,16 +7461,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7199
7461
|
if (!target) continue;
|
|
7200
7462
|
if (seen.has(target.nodeId)) continue;
|
|
7201
7463
|
seen.add(target.nodeId);
|
|
7202
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7464
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
|
|
7203
7465
|
if (graph.hasEdge(edgeId)) continue;
|
|
7204
7466
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
7205
7467
|
const edge = {
|
|
7206
7468
|
id: edgeId,
|
|
7207
7469
|
source: resource.nodeId,
|
|
7208
7470
|
target: target.nodeId,
|
|
7209
|
-
type:
|
|
7210
|
-
provenance:
|
|
7211
|
-
confidence: (0,
|
|
7471
|
+
type: import_types26.EdgeType.DEPENDS_ON,
|
|
7472
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7473
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7212
7474
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
7213
7475
|
};
|
|
7214
7476
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7222,7 +7484,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7222
7484
|
// src/extract/infra/k8s.ts
|
|
7223
7485
|
init_cjs_shims();
|
|
7224
7486
|
var import_node_fs18 = require("fs");
|
|
7225
|
-
var
|
|
7487
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
7226
7488
|
var import_yaml3 = require("yaml");
|
|
7227
7489
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
7228
7490
|
Service: "k8s-service",
|
|
@@ -7240,11 +7502,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
7240
7502
|
for (const entry2 of entries) {
|
|
7241
7503
|
if (entry2.isDirectory()) {
|
|
7242
7504
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
7243
|
-
const child =
|
|
7505
|
+
const child = import_node_path36.default.join(start, entry2.name);
|
|
7244
7506
|
if (await isPythonVenvDir(child)) continue;
|
|
7245
7507
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
7246
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
7247
|
-
out.push(
|
|
7508
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path36.default.extname(entry2.name))) {
|
|
7509
|
+
out.push(import_node_path36.default.join(start, entry2.name));
|
|
7248
7510
|
}
|
|
7249
7511
|
}
|
|
7250
7512
|
return out;
|
|
@@ -7278,13 +7540,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7278
7540
|
// src/extract/infra/cloudflare.ts
|
|
7279
7541
|
init_cjs_shims();
|
|
7280
7542
|
var import_node_fs19 = require("fs");
|
|
7281
|
-
var
|
|
7543
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7282
7544
|
var import_smol_toml2 = require("smol-toml");
|
|
7283
|
-
var
|
|
7545
|
+
var import_types27 = require("@neat.is/types");
|
|
7284
7546
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7285
7547
|
async function readWranglerConfig(dir) {
|
|
7286
7548
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7287
|
-
const abs =
|
|
7549
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7288
7550
|
if (!await exists(abs)) continue;
|
|
7289
7551
|
const raw = await import_node_fs19.promises.readFile(abs, "utf8");
|
|
7290
7552
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7328,8 +7590,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7328
7590
|
source: anchorId,
|
|
7329
7591
|
target: node.id,
|
|
7330
7592
|
type: edgeType,
|
|
7331
|
-
provenance:
|
|
7332
|
-
confidence: (0,
|
|
7593
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7594
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7333
7595
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7334
7596
|
};
|
|
7335
7597
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7347,11 +7609,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7347
7609
|
try {
|
|
7348
7610
|
read = await readWranglerConfig(service.dir);
|
|
7349
7611
|
} catch (err) {
|
|
7350
|
-
recordExtractionError("infra cloudflare",
|
|
7612
|
+
recordExtractionError("infra cloudflare", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7351
7613
|
continue;
|
|
7352
7614
|
}
|
|
7353
7615
|
if (!read || !read.config.name) continue;
|
|
7354
|
-
const evidenceFile = toPosix2(
|
|
7616
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, read.relFile)));
|
|
7355
7617
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7356
7618
|
}
|
|
7357
7619
|
for (const worker of discovered) {
|
|
@@ -7363,7 +7625,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7363
7625
|
}
|
|
7364
7626
|
let anchorId = service.node.id;
|
|
7365
7627
|
if (config.main) {
|
|
7366
|
-
const entryRelPath = toPosix2(
|
|
7628
|
+
const entryRelPath = toPosix2(import_node_path37.default.normalize(config.main));
|
|
7367
7629
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7368
7630
|
graph,
|
|
7369
7631
|
service.pkg.name,
|
|
@@ -7390,15 +7652,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7390
7652
|
nodesAdded++;
|
|
7391
7653
|
}
|
|
7392
7654
|
if (runtimeNode.id !== anchorId) {
|
|
7393
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7655
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
|
|
7394
7656
|
if (!graph.hasEdge(runsOnId)) {
|
|
7395
7657
|
const edge = {
|
|
7396
7658
|
id: runsOnId,
|
|
7397
7659
|
source: anchorId,
|
|
7398
7660
|
target: runtimeNode.id,
|
|
7399
|
-
type:
|
|
7400
|
-
provenance:
|
|
7401
|
-
confidence: (0,
|
|
7661
|
+
type: import_types27.EdgeType.RUNS_ON,
|
|
7662
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7663
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7402
7664
|
evidence: {
|
|
7403
7665
|
file: evidenceFile,
|
|
7404
7666
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7412,7 +7674,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7412
7674
|
const result = addResourceEdge(
|
|
7413
7675
|
graph,
|
|
7414
7676
|
anchorId,
|
|
7415
|
-
|
|
7677
|
+
import_types27.EdgeType.CONNECTS_TO,
|
|
7416
7678
|
"cloudflare-route",
|
|
7417
7679
|
route,
|
|
7418
7680
|
evidenceFile,
|
|
@@ -7436,7 +7698,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7436
7698
|
const result = addResourceEdge(
|
|
7437
7699
|
graph,
|
|
7438
7700
|
anchorId,
|
|
7439
|
-
|
|
7701
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7440
7702
|
group.kind,
|
|
7441
7703
|
name,
|
|
7442
7704
|
evidenceFile,
|
|
@@ -7450,7 +7712,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7450
7712
|
const result = addResourceEdge(
|
|
7451
7713
|
graph,
|
|
7452
7714
|
anchorId,
|
|
7453
|
-
|
|
7715
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7454
7716
|
"cloudflare-cron",
|
|
7455
7717
|
cron,
|
|
7456
7718
|
evidenceFile,
|
|
@@ -7463,7 +7725,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7463
7725
|
const result = addResourceEdge(
|
|
7464
7726
|
graph,
|
|
7465
7727
|
anchorId,
|
|
7466
|
-
|
|
7728
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7467
7729
|
"cloudflare-env-var",
|
|
7468
7730
|
varName,
|
|
7469
7731
|
evidenceFile,
|
|
@@ -7476,15 +7738,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7476
7738
|
if (!svc.service) continue;
|
|
7477
7739
|
const target = workerIndex.get(svc.service);
|
|
7478
7740
|
if (target && target.anchorId !== anchorId) {
|
|
7479
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7741
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
|
|
7480
7742
|
if (!graph.hasEdge(edgeId)) {
|
|
7481
7743
|
const edge = {
|
|
7482
7744
|
id: edgeId,
|
|
7483
7745
|
source: anchorId,
|
|
7484
7746
|
target: target.anchorId,
|
|
7485
|
-
type:
|
|
7486
|
-
provenance:
|
|
7487
|
-
confidence: (0,
|
|
7747
|
+
type: import_types27.EdgeType.CALLS,
|
|
7748
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7749
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7488
7750
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7489
7751
|
};
|
|
7490
7752
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7495,7 +7757,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7495
7757
|
const result = addResourceEdge(
|
|
7496
7758
|
graph,
|
|
7497
7759
|
anchorId,
|
|
7498
|
-
|
|
7760
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7499
7761
|
"cloudflare-service-binding",
|
|
7500
7762
|
svc.service,
|
|
7501
7763
|
evidenceFile,
|
|
@@ -7511,12 +7773,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7511
7773
|
// src/extract/infra/vercel.ts
|
|
7512
7774
|
init_cjs_shims();
|
|
7513
7775
|
var import_node_fs20 = require("fs");
|
|
7514
|
-
var
|
|
7515
|
-
var
|
|
7776
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7777
|
+
var import_types28 = require("@neat.is/types");
|
|
7516
7778
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7517
7779
|
async function readVercelConfig(dir) {
|
|
7518
7780
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7519
|
-
const abs =
|
|
7781
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7520
7782
|
if (!await exists(abs)) continue;
|
|
7521
7783
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7522
7784
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7525,7 +7787,7 @@ async function readVercelConfig(dir) {
|
|
|
7525
7787
|
return null;
|
|
7526
7788
|
}
|
|
7527
7789
|
async function readLinkedProjectName(dir) {
|
|
7528
|
-
const abs =
|
|
7790
|
+
const abs = import_node_path38.default.join(dir, ".vercel", "project.json");
|
|
7529
7791
|
if (!await exists(abs)) return void 0;
|
|
7530
7792
|
const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
|
|
7531
7793
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7543,7 +7805,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7543
7805
|
read = await readVercelConfig(service.dir);
|
|
7544
7806
|
projectName = await readLinkedProjectName(service.dir);
|
|
7545
7807
|
} catch (err) {
|
|
7546
|
-
recordExtractionError("infra vercel",
|
|
7808
|
+
recordExtractionError("infra vercel", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7547
7809
|
continue;
|
|
7548
7810
|
}
|
|
7549
7811
|
if (!read && !projectName) continue;
|
|
@@ -7559,7 +7821,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7559
7821
|
const anchorId = service.node.id;
|
|
7560
7822
|
if (!read) continue;
|
|
7561
7823
|
const { config, relFile, raw } = read;
|
|
7562
|
-
const evidenceFile = toPosix2(
|
|
7824
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7563
7825
|
const add = (edgeType, kind, name) => {
|
|
7564
7826
|
if (!name) return;
|
|
7565
7827
|
const result = emitPlatformResourceEdge(
|
|
@@ -7575,12 +7837,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7575
7837
|
nodesAdded += result.nodesAdded;
|
|
7576
7838
|
edgesAdded += result.edgesAdded;
|
|
7577
7839
|
};
|
|
7578
|
-
add(
|
|
7579
|
-
for (const cron of config.crons ?? []) add(
|
|
7580
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7581
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7840
|
+
add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7841
|
+
for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7842
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7843
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7582
7844
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7583
|
-
add(
|
|
7845
|
+
add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7584
7846
|
}
|
|
7585
7847
|
}
|
|
7586
7848
|
return { nodesAdded, edgesAdded };
|
|
@@ -7589,13 +7851,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7589
7851
|
// src/extract/infra/railway.ts
|
|
7590
7852
|
init_cjs_shims();
|
|
7591
7853
|
var import_node_fs21 = require("fs");
|
|
7592
|
-
var
|
|
7854
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7593
7855
|
var import_smol_toml3 = require("smol-toml");
|
|
7594
|
-
var
|
|
7856
|
+
var import_types29 = require("@neat.is/types");
|
|
7595
7857
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7596
7858
|
async function readRailwayConfig(dir) {
|
|
7597
7859
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7598
|
-
const abs =
|
|
7860
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7599
7861
|
if (!await exists(abs)) continue;
|
|
7600
7862
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7601
7863
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7611,7 +7873,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7611
7873
|
try {
|
|
7612
7874
|
read = await readRailwayConfig(service.dir);
|
|
7613
7875
|
} catch (err) {
|
|
7614
|
-
recordExtractionError("infra railway",
|
|
7876
|
+
recordExtractionError("infra railway", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7615
7877
|
continue;
|
|
7616
7878
|
}
|
|
7617
7879
|
if (!read) continue;
|
|
@@ -7621,7 +7883,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7621
7883
|
}
|
|
7622
7884
|
const anchorId = service.node.id;
|
|
7623
7885
|
const { config, relFile, raw } = read;
|
|
7624
|
-
const evidenceFile = toPosix2(
|
|
7886
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7625
7887
|
const add = (edgeType, kind, name) => {
|
|
7626
7888
|
if (!name) return;
|
|
7627
7889
|
const result = emitPlatformResourceEdge(
|
|
@@ -7637,9 +7899,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7637
7899
|
nodesAdded += result.nodesAdded;
|
|
7638
7900
|
edgesAdded += result.edgesAdded;
|
|
7639
7901
|
};
|
|
7640
|
-
add(
|
|
7641
|
-
add(
|
|
7642
|
-
add(
|
|
7902
|
+
add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
|
|
7903
|
+
add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7904
|
+
add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7643
7905
|
}
|
|
7644
7906
|
return { nodesAdded, edgesAdded };
|
|
7645
7907
|
}
|
|
@@ -7647,12 +7909,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7647
7909
|
// src/extract/infra/supabase.ts
|
|
7648
7910
|
init_cjs_shims();
|
|
7649
7911
|
var import_node_fs22 = require("fs");
|
|
7650
|
-
var
|
|
7912
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7651
7913
|
var import_smol_toml4 = require("smol-toml");
|
|
7652
|
-
var
|
|
7914
|
+
var import_types30 = require("@neat.is/types");
|
|
7653
7915
|
async function readSupabaseConfig(dir) {
|
|
7654
|
-
const relFile =
|
|
7655
|
-
const abs =
|
|
7916
|
+
const relFile = import_node_path40.default.join("supabase", "config.toml");
|
|
7917
|
+
const abs = import_node_path40.default.join(dir, relFile);
|
|
7656
7918
|
if (!await exists(abs)) return null;
|
|
7657
7919
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7658
7920
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7666,7 +7928,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7666
7928
|
try {
|
|
7667
7929
|
read = await readSupabaseConfig(service.dir);
|
|
7668
7930
|
} catch (err) {
|
|
7669
|
-
recordExtractionError("infra supabase",
|
|
7931
|
+
recordExtractionError("infra supabase", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7670
7932
|
continue;
|
|
7671
7933
|
}
|
|
7672
7934
|
if (!read) continue;
|
|
@@ -7681,7 +7943,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7681
7943
|
});
|
|
7682
7944
|
}
|
|
7683
7945
|
const anchorId = service.node.id;
|
|
7684
|
-
const evidenceFile = toPosix2(
|
|
7946
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7685
7947
|
const add = (edgeType, kind, name) => {
|
|
7686
7948
|
if (!name) return;
|
|
7687
7949
|
const result = emitPlatformResourceEdge(
|
|
@@ -7697,10 +7959,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7697
7959
|
nodesAdded += result.nodesAdded;
|
|
7698
7960
|
edgesAdded += result.edgesAdded;
|
|
7699
7961
|
};
|
|
7700
|
-
add(
|
|
7701
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7702
|
-
if (config.storage) add(
|
|
7703
|
-
if (config.auth) add(
|
|
7962
|
+
add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7963
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7964
|
+
if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7965
|
+
if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7704
7966
|
}
|
|
7705
7967
|
return { nodesAdded, edgesAdded };
|
|
7706
7968
|
}
|
|
@@ -7722,17 +7984,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7722
7984
|
}
|
|
7723
7985
|
|
|
7724
7986
|
// src/extract/index.ts
|
|
7725
|
-
var
|
|
7987
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7726
7988
|
|
|
7727
7989
|
// src/extract/retire.ts
|
|
7728
7990
|
init_cjs_shims();
|
|
7729
7991
|
var import_node_fs23 = require("fs");
|
|
7730
|
-
var
|
|
7731
|
-
var
|
|
7992
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7993
|
+
var import_types31 = require("@neat.is/types");
|
|
7732
7994
|
function dropOrphanedFileNodes(graph) {
|
|
7733
7995
|
const orphans = [];
|
|
7734
7996
|
graph.forEachNode((id, attrs) => {
|
|
7735
|
-
if (attrs.type !==
|
|
7997
|
+
if (attrs.type !== import_types31.NodeType.FileNode) return;
|
|
7736
7998
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7737
7999
|
orphans.push(id);
|
|
7738
8000
|
}
|
|
@@ -7745,14 +8007,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7745
8007
|
const bases = [scanPath, ...serviceDirs];
|
|
7746
8008
|
graph.forEachEdge((id, attrs) => {
|
|
7747
8009
|
const edge = attrs;
|
|
7748
|
-
if (edge.provenance !==
|
|
8010
|
+
if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
|
|
7749
8011
|
const evidenceFile = edge.evidence?.file;
|
|
7750
8012
|
if (!evidenceFile) return;
|
|
7751
|
-
if (
|
|
8013
|
+
if (import_node_path41.default.isAbsolute(evidenceFile)) {
|
|
7752
8014
|
if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7753
8015
|
return;
|
|
7754
8016
|
}
|
|
7755
|
-
const found = bases.some((base) => (0, import_node_fs23.existsSync)(
|
|
8017
|
+
const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path41.default.join(base, evidenceFile)));
|
|
7756
8018
|
if (!found) toDrop.push(id);
|
|
7757
8019
|
});
|
|
7758
8020
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7794,7 +8056,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7794
8056
|
}
|
|
7795
8057
|
const droppedEntries = drainDroppedExtracted();
|
|
7796
8058
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7797
|
-
const rejectedPath =
|
|
8059
|
+
const rejectedPath = import_node_path42.default.join(import_node_path42.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7798
8060
|
try {
|
|
7799
8061
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7800
8062
|
} catch (err) {
|
|
@@ -7829,8 +8091,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7829
8091
|
// src/persist.ts
|
|
7830
8092
|
init_cjs_shims();
|
|
7831
8093
|
var import_node_fs24 = require("fs");
|
|
7832
|
-
var
|
|
7833
|
-
var
|
|
8094
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
8095
|
+
var import_types32 = require("@neat.is/types");
|
|
7834
8096
|
var SCHEMA_VERSION = 4;
|
|
7835
8097
|
function migrateV1ToV2(payload) {
|
|
7836
8098
|
const nodes = payload.graph.nodes;
|
|
@@ -7852,12 +8114,12 @@ function migrateV2ToV3(payload) {
|
|
|
7852
8114
|
for (const edge of edges) {
|
|
7853
8115
|
const attrs = edge.attributes;
|
|
7854
8116
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7855
|
-
attrs.provenance =
|
|
8117
|
+
attrs.provenance = import_types32.Provenance.OBSERVED;
|
|
7856
8118
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7857
8119
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7858
8120
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7859
8121
|
if (type && source && target) {
|
|
7860
|
-
const newId = (0,
|
|
8122
|
+
const newId = (0, import_types32.observedEdgeId)(source, target, type);
|
|
7861
8123
|
attrs.id = newId;
|
|
7862
8124
|
if (edge.key) edge.key = newId;
|
|
7863
8125
|
}
|
|
@@ -7866,7 +8128,7 @@ function migrateV2ToV3(payload) {
|
|
|
7866
8128
|
return { ...payload, schemaVersion: 3 };
|
|
7867
8129
|
}
|
|
7868
8130
|
async function ensureDir(filePath) {
|
|
7869
|
-
await import_node_fs24.promises.mkdir(
|
|
8131
|
+
await import_node_fs24.promises.mkdir(import_node_path43.default.dirname(filePath), { recursive: true });
|
|
7870
8132
|
}
|
|
7871
8133
|
async function saveGraphToDisk(graph, outPath) {
|
|
7872
8134
|
await ensureDir(outPath);
|
|
@@ -7947,23 +8209,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7947
8209
|
|
|
7948
8210
|
// src/projects.ts
|
|
7949
8211
|
init_cjs_shims();
|
|
7950
|
-
var
|
|
8212
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7951
8213
|
function pathsForProject(project, baseDir) {
|
|
7952
8214
|
if (project === DEFAULT_PROJECT) {
|
|
7953
8215
|
return {
|
|
7954
|
-
snapshotPath:
|
|
7955
|
-
errorsPath:
|
|
7956
|
-
staleEventsPath:
|
|
7957
|
-
embeddingsCachePath:
|
|
7958
|
-
policyViolationsPath:
|
|
8216
|
+
snapshotPath: import_node_path44.default.join(baseDir, "graph.json"),
|
|
8217
|
+
errorsPath: import_node_path44.default.join(baseDir, "errors.ndjson"),
|
|
8218
|
+
staleEventsPath: import_node_path44.default.join(baseDir, "stale-events.ndjson"),
|
|
8219
|
+
embeddingsCachePath: import_node_path44.default.join(baseDir, "embeddings.json"),
|
|
8220
|
+
policyViolationsPath: import_node_path44.default.join(baseDir, "policy-violations.ndjson")
|
|
7959
8221
|
};
|
|
7960
8222
|
}
|
|
7961
8223
|
return {
|
|
7962
|
-
snapshotPath:
|
|
7963
|
-
errorsPath:
|
|
7964
|
-
staleEventsPath:
|
|
7965
|
-
embeddingsCachePath:
|
|
7966
|
-
policyViolationsPath:
|
|
8224
|
+
snapshotPath: import_node_path44.default.join(baseDir, `${project}.json`),
|
|
8225
|
+
errorsPath: import_node_path44.default.join(baseDir, `errors.${project}.ndjson`),
|
|
8226
|
+
staleEventsPath: import_node_path44.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
8227
|
+
embeddingsCachePath: import_node_path44.default.join(baseDir, `embeddings.${project}.json`),
|
|
8228
|
+
policyViolationsPath: import_node_path44.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
7967
8229
|
};
|
|
7968
8230
|
}
|
|
7969
8231
|
var Projects = class {
|
|
@@ -8001,19 +8263,19 @@ var Projects = class {
|
|
|
8001
8263
|
init_cjs_shims();
|
|
8002
8264
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
8003
8265
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
8004
|
-
var
|
|
8266
|
+
var import_types35 = require("@neat.is/types");
|
|
8005
8267
|
|
|
8006
8268
|
// src/extend/index.ts
|
|
8007
8269
|
init_cjs_shims();
|
|
8008
8270
|
var import_node_fs26 = require("fs");
|
|
8009
|
-
var
|
|
8271
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8010
8272
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
8011
8273
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
8012
8274
|
|
|
8013
8275
|
// src/installers/package-manager.ts
|
|
8014
8276
|
init_cjs_shims();
|
|
8015
8277
|
var import_node_fs25 = require("fs");
|
|
8016
|
-
var
|
|
8278
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8017
8279
|
var import_node_child_process = require("child_process");
|
|
8018
8280
|
var LOCKFILE_PRIORITY = [
|
|
8019
8281
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -8035,22 +8297,22 @@ async function exists2(p) {
|
|
|
8035
8297
|
}
|
|
8036
8298
|
}
|
|
8037
8299
|
async function detectPackageManager(serviceDir) {
|
|
8038
|
-
let dir =
|
|
8300
|
+
let dir = import_node_path45.default.resolve(serviceDir);
|
|
8039
8301
|
const stops = /* @__PURE__ */ new Set();
|
|
8040
8302
|
for (let i = 0; i < 64; i++) {
|
|
8041
8303
|
if (stops.has(dir)) break;
|
|
8042
8304
|
stops.add(dir);
|
|
8043
8305
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8044
|
-
const lockPath =
|
|
8306
|
+
const lockPath = import_node_path45.default.join(dir, candidate.lockfile);
|
|
8045
8307
|
if (await exists2(lockPath)) {
|
|
8046
8308
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8047
8309
|
}
|
|
8048
8310
|
}
|
|
8049
|
-
const parent =
|
|
8311
|
+
const parent = import_node_path45.default.dirname(dir);
|
|
8050
8312
|
if (parent === dir) break;
|
|
8051
8313
|
dir = parent;
|
|
8052
8314
|
}
|
|
8053
|
-
return { pm: "npm", cwd:
|
|
8315
|
+
return { pm: "npm", cwd: import_node_path45.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8054
8316
|
}
|
|
8055
8317
|
async function runPackageManagerInstall(cmd) {
|
|
8056
8318
|
return new Promise((resolve) => {
|
|
@@ -8099,7 +8361,7 @@ async function fileExists2(p) {
|
|
|
8099
8361
|
}
|
|
8100
8362
|
}
|
|
8101
8363
|
async function readPackageJson(scanPath) {
|
|
8102
|
-
const pkgPath =
|
|
8364
|
+
const pkgPath = import_node_path46.default.join(scanPath, "package.json");
|
|
8103
8365
|
const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
|
|
8104
8366
|
return JSON.parse(raw);
|
|
8105
8367
|
}
|
|
@@ -8113,27 +8375,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
8113
8375
|
]);
|
|
8114
8376
|
async function findHookFiles(scanPath) {
|
|
8115
8377
|
const found = [];
|
|
8116
|
-
const
|
|
8378
|
+
const walk4 = async (dir) => {
|
|
8117
8379
|
const entries = await import_node_fs26.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8118
8380
|
for (const entry2 of entries) {
|
|
8119
8381
|
if (entry2.isDirectory()) {
|
|
8120
8382
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
8121
|
-
await
|
|
8383
|
+
await walk4(import_node_path46.default.join(dir, entry2.name));
|
|
8122
8384
|
} else if (entry2.isFile()) {
|
|
8123
8385
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
8124
|
-
const rel =
|
|
8125
|
-
found.push(rel.split(
|
|
8386
|
+
const rel = import_node_path46.default.relative(scanPath, import_node_path46.default.join(dir, entry2.name));
|
|
8387
|
+
found.push(rel.split(import_node_path46.default.sep).join("/"));
|
|
8126
8388
|
}
|
|
8127
8389
|
}
|
|
8128
8390
|
}
|
|
8129
8391
|
};
|
|
8130
|
-
await
|
|
8392
|
+
await walk4(scanPath);
|
|
8131
8393
|
return found.sort();
|
|
8132
8394
|
}
|
|
8133
8395
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8134
8396
|
let fallback = null;
|
|
8135
8397
|
for (const file of hookFiles) {
|
|
8136
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8398
|
+
const content = await import_node_fs26.promises.readFile(import_node_path46.default.join(scanPath, file), "utf8");
|
|
8137
8399
|
const patched = splicedContent(content, snippet2);
|
|
8138
8400
|
if (patched !== null) return { file, content, patched };
|
|
8139
8401
|
if (fallback === null) fallback = { file, content };
|
|
@@ -8141,11 +8403,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
8141
8403
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
8142
8404
|
}
|
|
8143
8405
|
function extendLogPath() {
|
|
8144
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8406
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path46.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
8145
8407
|
}
|
|
8146
8408
|
async function appendExtendLog(entry2) {
|
|
8147
8409
|
const logPath = extendLogPath();
|
|
8148
|
-
await import_node_fs26.promises.mkdir(
|
|
8410
|
+
await import_node_fs26.promises.mkdir(import_node_path46.default.dirname(logPath), { recursive: true });
|
|
8149
8411
|
await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
8150
8412
|
}
|
|
8151
8413
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -8204,7 +8466,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
8204
8466
|
}
|
|
8205
8467
|
async function describeProjectInstrumentation(ctx) {
|
|
8206
8468
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8207
|
-
const envNeat = await fileExists2(
|
|
8469
|
+
const envNeat = await fileExists2(import_node_path46.default.join(ctx.scanPath, ".env.neat"));
|
|
8208
8470
|
const registryInstrPackages = new Set(
|
|
8209
8471
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
8210
8472
|
);
|
|
@@ -8226,7 +8488,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
8226
8488
|
);
|
|
8227
8489
|
}
|
|
8228
8490
|
for (const file of hookFiles) {
|
|
8229
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8491
|
+
const content = await import_node_fs26.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8230
8492
|
if (content.includes(args.registration_snippet)) {
|
|
8231
8493
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
8232
8494
|
}
|
|
@@ -8238,10 +8500,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
8238
8500
|
);
|
|
8239
8501
|
}
|
|
8240
8502
|
const primaryFile = primary.file;
|
|
8241
|
-
const primaryPath =
|
|
8503
|
+
const primaryPath = import_node_path46.default.join(ctx.scanPath, primaryFile);
|
|
8242
8504
|
const filesTouched = [];
|
|
8243
8505
|
const depsAdded = [];
|
|
8244
|
-
const pkgPath =
|
|
8506
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8245
8507
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8246
8508
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
8247
8509
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -8280,7 +8542,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8280
8542
|
};
|
|
8281
8543
|
}
|
|
8282
8544
|
for (const file of hookFiles) {
|
|
8283
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8545
|
+
const content = await import_node_fs26.promises.readFile(import_node_path46.default.join(ctx.scanPath, file), "utf8");
|
|
8284
8546
|
if (content.includes(args.registration_snippet)) {
|
|
8285
8547
|
return {
|
|
8286
8548
|
library: args.library,
|
|
@@ -8321,7 +8583,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8321
8583
|
if (!match) {
|
|
8322
8584
|
return { undone: false, message: "no apply found for library" };
|
|
8323
8585
|
}
|
|
8324
|
-
const pkgPath =
|
|
8586
|
+
const pkgPath = import_node_path46.default.join(ctx.scanPath, "package.json");
|
|
8325
8587
|
if (await fileExists2(pkgPath)) {
|
|
8326
8588
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8327
8589
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8332,7 +8594,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8332
8594
|
}
|
|
8333
8595
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8334
8596
|
for (const file of hookFiles) {
|
|
8335
|
-
const filePath =
|
|
8597
|
+
const filePath = import_node_path46.default.join(ctx.scanPath, file);
|
|
8336
8598
|
const content = await import_node_fs26.promises.readFile(filePath, "utf8");
|
|
8337
8599
|
if (content.includes(match.registration_snippet)) {
|
|
8338
8600
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8348,39 +8610,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
8348
8610
|
|
|
8349
8611
|
// src/divergences.ts
|
|
8350
8612
|
init_cjs_shims();
|
|
8351
|
-
var
|
|
8613
|
+
var import_types33 = require("@neat.is/types");
|
|
8352
8614
|
function bucketKey(source, target, type) {
|
|
8353
8615
|
return `${type}|${source}|${target}`;
|
|
8354
8616
|
}
|
|
8355
8617
|
function bucketSourceFor(graph, edge) {
|
|
8356
|
-
if (edge.type !==
|
|
8357
|
-
const parsed = (0,
|
|
8618
|
+
if (edge.type !== import_types33.EdgeType.CONNECTS_TO) return edge.source;
|
|
8619
|
+
const parsed = (0, import_types33.parseFileId)(edge.source);
|
|
8358
8620
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8359
8621
|
const target = graph.getNodeAttributes(edge.target);
|
|
8360
|
-
if (target.type !==
|
|
8361
|
-
return (0,
|
|
8622
|
+
if (target.type !== import_types33.NodeType.DatabaseNode) return edge.source;
|
|
8623
|
+
return (0, import_types33.serviceId)(parsed.service);
|
|
8362
8624
|
}
|
|
8363
8625
|
function bucketEdges(graph) {
|
|
8364
8626
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8365
8627
|
graph.forEachEdge((id, attrs) => {
|
|
8366
8628
|
const e = attrs;
|
|
8367
|
-
const parsed = (0,
|
|
8629
|
+
const parsed = (0, import_types33.parseEdgeId)(id);
|
|
8368
8630
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8369
8631
|
const source = bucketSourceFor(graph, e);
|
|
8370
8632
|
const key = bucketKey(source, e.target, e.type);
|
|
8371
8633
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8372
8634
|
switch (provenance) {
|
|
8373
|
-
case
|
|
8635
|
+
case import_types33.Provenance.EXTRACTED:
|
|
8374
8636
|
cur.extracted = e;
|
|
8375
8637
|
break;
|
|
8376
|
-
case
|
|
8638
|
+
case import_types33.Provenance.OBSERVED:
|
|
8377
8639
|
cur.observed = e;
|
|
8378
8640
|
break;
|
|
8379
|
-
case
|
|
8641
|
+
case import_types33.Provenance.INFERRED:
|
|
8380
8642
|
cur.inferred = e;
|
|
8381
8643
|
break;
|
|
8382
8644
|
default:
|
|
8383
|
-
if (e.provenance ===
|
|
8645
|
+
if (e.provenance === import_types33.Provenance.STALE) cur.stale = e;
|
|
8384
8646
|
}
|
|
8385
8647
|
buckets2.set(key, cur);
|
|
8386
8648
|
});
|
|
@@ -8389,12 +8651,12 @@ function bucketEdges(graph) {
|
|
|
8389
8651
|
function nodeIsFrontier(graph, nodeId) {
|
|
8390
8652
|
if (!graph.hasNode(nodeId)) return false;
|
|
8391
8653
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8392
|
-
return attrs.type ===
|
|
8654
|
+
return attrs.type === import_types33.NodeType.FrontierNode;
|
|
8393
8655
|
}
|
|
8394
8656
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8395
8657
|
if (!graph.hasNode(nodeId)) return false;
|
|
8396
8658
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8397
|
-
return attrs.type ===
|
|
8659
|
+
return attrs.type === import_types33.NodeType.WebSocketChannelNode;
|
|
8398
8660
|
}
|
|
8399
8661
|
function clampConfidence(n) {
|
|
8400
8662
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8414,14 +8676,14 @@ function gradedConfidence(edge) {
|
|
|
8414
8676
|
return clampConfidence(confidenceForEdge(edge));
|
|
8415
8677
|
}
|
|
8416
8678
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8679
|
+
import_types33.EdgeType.CALLS,
|
|
8680
|
+
import_types33.EdgeType.CONNECTS_TO,
|
|
8681
|
+
import_types33.EdgeType.PUBLISHES_TO,
|
|
8682
|
+
import_types33.EdgeType.CONSUMES_FROM
|
|
8421
8683
|
]);
|
|
8422
8684
|
function detectMissingDivergences(graph, bucket) {
|
|
8423
8685
|
const out = [];
|
|
8424
|
-
if (bucket.type ===
|
|
8686
|
+
if (bucket.type === import_types33.EdgeType.CONTAINS) return out;
|
|
8425
8687
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8426
8688
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8427
8689
|
out.push({
|
|
@@ -8462,7 +8724,7 @@ function declaredHostFor(svc) {
|
|
|
8462
8724
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8463
8725
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8464
8726
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8465
|
-
if (e.type ===
|
|
8727
|
+
if (e.type === import_types33.EdgeType.CONFIGURED_BY && e.provenance === import_types33.Provenance.EXTRACTED) {
|
|
8466
8728
|
return true;
|
|
8467
8729
|
}
|
|
8468
8730
|
}
|
|
@@ -8475,10 +8737,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8475
8737
|
const out = [];
|
|
8476
8738
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8477
8739
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8478
|
-
if (edge.type !==
|
|
8479
|
-
if (edge.provenance !==
|
|
8740
|
+
if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
|
|
8741
|
+
if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
|
|
8480
8742
|
const target = graph.getNodeAttributes(edge.target);
|
|
8481
|
-
if (target.type !==
|
|
8743
|
+
if (target.type !== import_types33.NodeType.DatabaseNode) continue;
|
|
8482
8744
|
const observedHost = target.host?.trim();
|
|
8483
8745
|
if (!observedHost) continue;
|
|
8484
8746
|
if (observedHost === declaredHost) continue;
|
|
@@ -8500,10 +8762,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8500
8762
|
const deps = svc.dependencies ?? {};
|
|
8501
8763
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8502
8764
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8503
|
-
if (edge.type !==
|
|
8504
|
-
if (edge.provenance !==
|
|
8765
|
+
if (edge.type !== import_types33.EdgeType.CONNECTS_TO) continue;
|
|
8766
|
+
if (edge.provenance !== import_types33.Provenance.OBSERVED) continue;
|
|
8505
8767
|
const target = graph.getNodeAttributes(edge.target);
|
|
8506
|
-
if (target.type !==
|
|
8768
|
+
if (target.type !== import_types33.NodeType.DatabaseNode) continue;
|
|
8507
8769
|
for (const pair of compatPairs()) {
|
|
8508
8770
|
if (pair.engine !== target.engine) continue;
|
|
8509
8771
|
const declared = deps[pair.driver];
|
|
@@ -8562,7 +8824,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8562
8824
|
for (const d of all) {
|
|
8563
8825
|
if (d.type !== "host-mismatch") continue;
|
|
8564
8826
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8565
|
-
declaredHalf.add((0,
|
|
8827
|
+
declaredHalf.add((0, import_types33.databaseId)(d.extractedHost));
|
|
8566
8828
|
}
|
|
8567
8829
|
if (observedHalf.size === 0) return all;
|
|
8568
8830
|
return all.filter((d) => {
|
|
@@ -8581,7 +8843,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8581
8843
|
}
|
|
8582
8844
|
graph.forEachNode((nodeId, attrs) => {
|
|
8583
8845
|
const n = attrs;
|
|
8584
|
-
if (n.type !==
|
|
8846
|
+
if (n.type !== import_types33.NodeType.ServiceNode) return;
|
|
8585
8847
|
const svc = n;
|
|
8586
8848
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8587
8849
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8615,7 +8877,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8615
8877
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8616
8878
|
return a.target.localeCompare(b.target);
|
|
8617
8879
|
});
|
|
8618
|
-
return
|
|
8880
|
+
return import_types33.DivergenceResultSchema.parse({
|
|
8619
8881
|
divergences: filtered,
|
|
8620
8882
|
totalAffected: filtered.length,
|
|
8621
8883
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8748,23 +9010,23 @@ function canonicalJson(value) {
|
|
|
8748
9010
|
init_cjs_shims();
|
|
8749
9011
|
var import_node_fs28 = require("fs");
|
|
8750
9012
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8751
|
-
var
|
|
8752
|
-
var
|
|
9013
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
9014
|
+
var import_types34 = require("@neat.is/types");
|
|
8753
9015
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8754
9016
|
var LOCK_RETRY_MS = 50;
|
|
8755
9017
|
function neatHome() {
|
|
8756
9018
|
const override = process.env.NEAT_HOME;
|
|
8757
|
-
if (override && override.length > 0) return
|
|
8758
|
-
return
|
|
9019
|
+
if (override && override.length > 0) return import_node_path47.default.resolve(override);
|
|
9020
|
+
return import_node_path47.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8759
9021
|
}
|
|
8760
9022
|
function registryPath() {
|
|
8761
|
-
return
|
|
9023
|
+
return import_node_path47.default.join(neatHome(), "projects.json");
|
|
8762
9024
|
}
|
|
8763
9025
|
function registryLockPath() {
|
|
8764
|
-
return
|
|
9026
|
+
return import_node_path47.default.join(neatHome(), "projects.json.lock");
|
|
8765
9027
|
}
|
|
8766
9028
|
function daemonPidPath() {
|
|
8767
|
-
return
|
|
9029
|
+
return import_node_path47.default.join(neatHome(), "neatd.pid");
|
|
8768
9030
|
}
|
|
8769
9031
|
function isPidAliveDefault(pid) {
|
|
8770
9032
|
try {
|
|
@@ -8824,7 +9086,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8824
9086
|
}
|
|
8825
9087
|
}
|
|
8826
9088
|
async function writeAtomically(target, contents) {
|
|
8827
|
-
await import_node_fs28.promises.mkdir(
|
|
9089
|
+
await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(target), { recursive: true });
|
|
8828
9090
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8829
9091
|
const fd = await import_node_fs28.promises.open(tmp, "w");
|
|
8830
9092
|
try {
|
|
@@ -8837,7 +9099,7 @@ async function writeAtomically(target, contents) {
|
|
|
8837
9099
|
}
|
|
8838
9100
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8839
9101
|
const deadline = Date.now() + timeoutMs;
|
|
8840
|
-
await import_node_fs28.promises.mkdir(
|
|
9102
|
+
await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(lockPath), { recursive: true });
|
|
8841
9103
|
let probedHolder = false;
|
|
8842
9104
|
while (true) {
|
|
8843
9105
|
try {
|
|
@@ -8890,10 +9152,10 @@ async function readRegistry() {
|
|
|
8890
9152
|
throw err;
|
|
8891
9153
|
}
|
|
8892
9154
|
const parsed = JSON.parse(raw);
|
|
8893
|
-
return
|
|
9155
|
+
return import_types34.RegistryFileSchema.parse(parsed);
|
|
8894
9156
|
}
|
|
8895
9157
|
async function writeRegistry(reg) {
|
|
8896
|
-
const validated =
|
|
9158
|
+
const validated = import_types34.RegistryFileSchema.parse(reg);
|
|
8897
9159
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8898
9160
|
}
|
|
8899
9161
|
async function getProject(name) {
|
|
@@ -9038,7 +9300,7 @@ init_auth();
|
|
|
9038
9300
|
// src/connectors-config.ts
|
|
9039
9301
|
init_cjs_shims();
|
|
9040
9302
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
9041
|
-
var
|
|
9303
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9042
9304
|
var import_node_fs29 = require("fs");
|
|
9043
9305
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
9044
9306
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -9053,11 +9315,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
9053
9315
|
};
|
|
9054
9316
|
function neatHome2() {
|
|
9055
9317
|
const override = process.env.NEAT_HOME;
|
|
9056
|
-
if (override && override.length > 0) return
|
|
9057
|
-
return
|
|
9318
|
+
if (override && override.length > 0) return import_node_path48.default.resolve(override);
|
|
9319
|
+
return import_node_path48.default.join(import_node_os4.default.homedir(), ".neat");
|
|
9058
9320
|
}
|
|
9059
9321
|
function connectorsConfigPath(home = neatHome2()) {
|
|
9060
|
-
return
|
|
9322
|
+
return import_node_path48.default.join(home, "connectors.json");
|
|
9061
9323
|
}
|
|
9062
9324
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
9063
9325
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9393,11 +9655,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9393
9655
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9394
9656
|
const parsed = [];
|
|
9395
9657
|
for (const c of candidates) {
|
|
9396
|
-
const r =
|
|
9658
|
+
const r = import_types35.DivergenceTypeSchema.safeParse(c);
|
|
9397
9659
|
if (!r.success) {
|
|
9398
9660
|
return reply.code(400).send({
|
|
9399
9661
|
error: `unknown divergence type "${c}"`,
|
|
9400
|
-
allowed:
|
|
9662
|
+
allowed: import_types35.DivergenceTypeSchema.options
|
|
9401
9663
|
});
|
|
9402
9664
|
}
|
|
9403
9665
|
parsed.push(r.data);
|
|
@@ -9668,7 +9930,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9668
9930
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9669
9931
|
let violations = await log.readAll();
|
|
9670
9932
|
if (req2.query.severity) {
|
|
9671
|
-
const sev =
|
|
9933
|
+
const sev = import_types35.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
9672
9934
|
if (!sev.success) {
|
|
9673
9935
|
return reply.code(400).send({
|
|
9674
9936
|
error: "invalid severity",
|
|
@@ -9707,7 +9969,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9707
9969
|
scope.post("/policies/check", async (req2, reply) => {
|
|
9708
9970
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
9709
9971
|
if (!proj) return;
|
|
9710
|
-
const parsed =
|
|
9972
|
+
const parsed = import_types35.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
9711
9973
|
if (!parsed.success) {
|
|
9712
9974
|
return reply.code(400).send({
|
|
9713
9975
|
error: "invalid /policies/check body",
|
|
@@ -10007,15 +10269,15 @@ init_otel();
|
|
|
10007
10269
|
|
|
10008
10270
|
// src/connectors/index.ts
|
|
10009
10271
|
init_cjs_shims();
|
|
10010
|
-
var
|
|
10272
|
+
var import_types36 = require("@neat.is/types");
|
|
10011
10273
|
var NO_ENV = "unknown";
|
|
10012
10274
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
10013
10275
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10014
10276
|
const sites = [];
|
|
10015
10277
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
10016
10278
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
10017
|
-
if (edge.provenance !==
|
|
10018
|
-
const parsed = (0,
|
|
10279
|
+
if (edge.provenance !== import_types36.Provenance.EXTRACTED) continue;
|
|
10280
|
+
const parsed = (0, import_types36.parseFileId)(edge.source);
|
|
10019
10281
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
10020
10282
|
const site = { relPath: edge.evidence.file };
|
|
10021
10283
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -10026,7 +10288,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
10026
10288
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
10027
10289
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
10028
10290
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
10029
|
-
if (attrs.type !==
|
|
10291
|
+
if (attrs.type !== import_types36.NodeType.RouteNode || !attrs.path) return void 0;
|
|
10030
10292
|
const site = { relPath: attrs.path };
|
|
10031
10293
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
10032
10294
|
return site;
|
|
@@ -10497,10 +10759,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10497
10759
|
// src/connectors/supabase/map.ts
|
|
10498
10760
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10499
10761
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10500
|
-
function targetFromRestPath(
|
|
10501
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10762
|
+
function targetFromRestPath(path55) {
|
|
10763
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path55);
|
|
10502
10764
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10503
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10765
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path55);
|
|
10504
10766
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10505
10767
|
return null;
|
|
10506
10768
|
}
|
|
@@ -10609,23 +10871,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10609
10871
|
|
|
10610
10872
|
// src/connectors/supabase/resolve.ts
|
|
10611
10873
|
init_cjs_shims();
|
|
10612
|
-
var
|
|
10874
|
+
var import_types38 = require("@neat.is/types");
|
|
10613
10875
|
function createSupabaseResolveTarget(graph, config) {
|
|
10614
10876
|
return (signal, _ctx) => {
|
|
10615
10877
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10616
10878
|
return null;
|
|
10617
10879
|
}
|
|
10618
|
-
const subResourceId = (0,
|
|
10880
|
+
const subResourceId = (0, import_types38.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10619
10881
|
if (graph.hasNode(subResourceId)) {
|
|
10620
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10882
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10621
10883
|
}
|
|
10622
|
-
const bareResourceId = (0,
|
|
10884
|
+
const bareResourceId = (0, import_types38.infraId)(signal.targetKind, signal.targetName);
|
|
10623
10885
|
if (graph.hasNode(bareResourceId)) {
|
|
10624
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10886
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10625
10887
|
}
|
|
10626
|
-
const projectLevelId = (0,
|
|
10888
|
+
const projectLevelId = (0, import_types38.infraId)("supabase", config.nodeRef);
|
|
10627
10889
|
if (graph.hasNode(projectLevelId)) {
|
|
10628
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10890
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types38.EdgeType.CALLS };
|
|
10629
10891
|
}
|
|
10630
10892
|
return null;
|
|
10631
10893
|
};
|
|
@@ -10718,7 +10980,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10718
10980
|
|
|
10719
10981
|
// src/connectors/railway/index.ts
|
|
10720
10982
|
init_cjs_shims();
|
|
10721
|
-
var
|
|
10983
|
+
var import_types42 = require("@neat.is/types");
|
|
10722
10984
|
|
|
10723
10985
|
// src/connectors/railway/client.ts
|
|
10724
10986
|
init_cjs_shims();
|
|
@@ -10844,7 +11106,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10844
11106
|
const out = [];
|
|
10845
11107
|
graph.forEachNode((_id, attrs) => {
|
|
10846
11108
|
const node = attrs;
|
|
10847
|
-
if (node.type !==
|
|
11109
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10848
11110
|
const route = attrs;
|
|
10849
11111
|
if (route.service !== serviceName) return;
|
|
10850
11112
|
out.push({
|
|
@@ -10948,12 +11210,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10948
11210
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10949
11211
|
if (!serviceName) return null;
|
|
10950
11212
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10951
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11213
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types42.EdgeType.CALLS };
|
|
10952
11214
|
}
|
|
10953
11215
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10954
11216
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10955
11217
|
if (!peerName) return null;
|
|
10956
|
-
return { targetNodeId: (0,
|
|
11218
|
+
return { targetNodeId: (0, import_types42.serviceId)(peerName), serviceName, edgeType: import_types42.EdgeType.CONNECTS_TO };
|
|
10957
11219
|
}
|
|
10958
11220
|
return null;
|
|
10959
11221
|
};
|
|
@@ -11077,9 +11339,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11077
11339
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11078
11340
|
if (secondSep === -1) return null;
|
|
11079
11341
|
const method = rest.slice(0, secondSep);
|
|
11080
|
-
const
|
|
11081
|
-
if (!resourceName || !method || !
|
|
11082
|
-
return { resourceName, method, path:
|
|
11342
|
+
const path55 = rest.slice(secondSep + 1);
|
|
11343
|
+
if (!resourceName || !method || !path55) return null;
|
|
11344
|
+
return { resourceName, method, path: path55 };
|
|
11083
11345
|
}
|
|
11084
11346
|
function resourceNameFor(type, labels) {
|
|
11085
11347
|
if (!labels) return null;
|
|
@@ -11117,14 +11379,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
11117
11379
|
if (!req2) return null;
|
|
11118
11380
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
11119
11381
|
const method = req2.requestMethod.toUpperCase();
|
|
11120
|
-
const
|
|
11121
|
-
if (
|
|
11382
|
+
const path55 = pathFromRequestUrl(req2.requestUrl);
|
|
11383
|
+
if (path55 === null) return null;
|
|
11122
11384
|
const timestamp = entry2.timestamp;
|
|
11123
11385
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11124
11386
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
11125
11387
|
return {
|
|
11126
11388
|
targetKind: resourceType,
|
|
11127
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11389
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path55 }),
|
|
11128
11390
|
callCount: 1,
|
|
11129
11391
|
errorCount: isError ? 1 : 0,
|
|
11130
11392
|
lastObservedIso: timestamp
|
|
@@ -11141,7 +11403,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11141
11403
|
|
|
11142
11404
|
// src/connectors/firebase/resolve.ts
|
|
11143
11405
|
init_cjs_shims();
|
|
11144
|
-
var
|
|
11406
|
+
var import_types43 = require("@neat.is/types");
|
|
11145
11407
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11146
11408
|
switch (resourceType) {
|
|
11147
11409
|
case "cloud_function":
|
|
@@ -11156,7 +11418,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11156
11418
|
const entries = [];
|
|
11157
11419
|
graph.forEachNode((_id, attrs) => {
|
|
11158
11420
|
const node = attrs;
|
|
11159
|
-
if (node.type !==
|
|
11421
|
+
if (node.type !== import_types43.NodeType.RouteNode) return;
|
|
11160
11422
|
const route = attrs;
|
|
11161
11423
|
if (route.service !== serviceName) return;
|
|
11162
11424
|
entries.push({
|
|
@@ -11188,7 +11450,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11188
11450
|
return {
|
|
11189
11451
|
targetNodeId: match.routeNodeId,
|
|
11190
11452
|
serviceName,
|
|
11191
|
-
edgeType:
|
|
11453
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
11192
11454
|
};
|
|
11193
11455
|
};
|
|
11194
11456
|
}
|
|
@@ -11215,7 +11477,7 @@ init_cjs_shims();
|
|
|
11215
11477
|
|
|
11216
11478
|
// src/connectors/cloudflare/connector.ts
|
|
11217
11479
|
init_cjs_shims();
|
|
11218
|
-
var
|
|
11480
|
+
var import_types45 = require("@neat.is/types");
|
|
11219
11481
|
|
|
11220
11482
|
// src/connectors/cloudflare/client.ts
|
|
11221
11483
|
init_cjs_shims();
|
|
@@ -11331,7 +11593,7 @@ function mapEventToSignal(event) {
|
|
|
11331
11593
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11332
11594
|
const statusCode = metadata?.statusCode;
|
|
11333
11595
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11334
|
-
const
|
|
11596
|
+
const path55 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11335
11597
|
return {
|
|
11336
11598
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11337
11599
|
targetName: scriptName,
|
|
@@ -11339,7 +11601,7 @@ function mapEventToSignal(event) {
|
|
|
11339
11601
|
errorCount: isError ? 1 : 0,
|
|
11340
11602
|
lastObservedIso: observedAt.toISOString(),
|
|
11341
11603
|
method,
|
|
11342
|
-
...
|
|
11604
|
+
...path55 ? { path: path55 } : {},
|
|
11343
11605
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11344
11606
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11345
11607
|
};
|
|
@@ -11379,19 +11641,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11379
11641
|
graph.forEachNode((id, attrs) => {
|
|
11380
11642
|
if (found) return;
|
|
11381
11643
|
const a = attrs;
|
|
11382
|
-
if (a.type ===
|
|
11644
|
+
if (a.type === import_types45.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11383
11645
|
found = id;
|
|
11384
11646
|
}
|
|
11385
11647
|
});
|
|
11386
11648
|
return found;
|
|
11387
11649
|
}
|
|
11388
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11389
|
-
const normalizedPath = normalizePathTemplate(
|
|
11650
|
+
function findMatchingRouteNode(graph, serviceName, method, path55) {
|
|
11651
|
+
const normalizedPath = normalizePathTemplate(path55);
|
|
11390
11652
|
let found = null;
|
|
11391
11653
|
graph.forEachNode((id, attrs) => {
|
|
11392
11654
|
if (found) return;
|
|
11393
11655
|
const a = attrs;
|
|
11394
|
-
if (a.type !==
|
|
11656
|
+
if (a.type !== import_types45.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11395
11657
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11396
11658
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11397
11659
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11403,18 +11665,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11403
11665
|
return (signal) => {
|
|
11404
11666
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11405
11667
|
const scriptName = signal.targetName;
|
|
11406
|
-
const { method, path:
|
|
11668
|
+
const { method, path: path55 } = signal;
|
|
11407
11669
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11408
|
-
if (!method || !
|
|
11409
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11670
|
+
if (!method || !path55) return wholeFileId;
|
|
11671
|
+
return findMatchingRouteNode(graph, serviceName, method, path55) ?? wholeFileId;
|
|
11410
11672
|
};
|
|
11411
11673
|
const mapping = config.workers?.[scriptName];
|
|
11412
11674
|
if (mapping) {
|
|
11413
|
-
const wholeFileId = (0,
|
|
11675
|
+
const wholeFileId = (0, import_types45.fileId)(mapping.service, mapping.entryFile);
|
|
11414
11676
|
return {
|
|
11415
11677
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11416
11678
|
serviceName: mapping.service,
|
|
11417
|
-
edgeType:
|
|
11679
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11418
11680
|
};
|
|
11419
11681
|
}
|
|
11420
11682
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11423,13 +11685,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11423
11685
|
return {
|
|
11424
11686
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11425
11687
|
serviceName: fileNode.service,
|
|
11426
|
-
edgeType:
|
|
11688
|
+
edgeType: import_types45.EdgeType.CALLS
|
|
11427
11689
|
};
|
|
11428
11690
|
}
|
|
11429
11691
|
return {
|
|
11430
|
-
targetNodeId: (0,
|
|
11692
|
+
targetNodeId: (0, import_types45.infraId)("cloudflare-worker", scriptName),
|
|
11431
11693
|
serviceName: scriptName,
|
|
11432
|
-
edgeType:
|
|
11694
|
+
edgeType: import_types45.EdgeType.CALLS,
|
|
11433
11695
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11434
11696
|
};
|
|
11435
11697
|
};
|
|
@@ -11873,7 +12135,7 @@ init_auth();
|
|
|
11873
12135
|
// src/unrouted.ts
|
|
11874
12136
|
init_cjs_shims();
|
|
11875
12137
|
var import_node_fs30 = require("fs");
|
|
11876
|
-
var
|
|
12138
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
11877
12139
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
11878
12140
|
return {
|
|
11879
12141
|
timestamp: now.toISOString(),
|
|
@@ -11883,34 +12145,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
11883
12145
|
};
|
|
11884
12146
|
}
|
|
11885
12147
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
11886
|
-
const target =
|
|
12148
|
+
const target = import_node_path51.default.join(neatHome4, "errors.ndjson");
|
|
11887
12149
|
await import_node_fs30.promises.mkdir(neatHome4, { recursive: true });
|
|
11888
12150
|
await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
11889
12151
|
}
|
|
11890
12152
|
function unroutedErrorsPath(neatHome4) {
|
|
11891
|
-
return
|
|
12153
|
+
return import_node_path51.default.join(neatHome4, "errors.ndjson");
|
|
11892
12154
|
}
|
|
11893
12155
|
|
|
11894
12156
|
// src/daemon.ts
|
|
11895
|
-
var
|
|
12157
|
+
var import_types48 = require("@neat.is/types");
|
|
11896
12158
|
function daemonJsonPath(scanPath) {
|
|
11897
|
-
return
|
|
12159
|
+
return import_node_path52.default.join(scanPath, "neat-out", "daemon.json");
|
|
11898
12160
|
}
|
|
11899
12161
|
function daemonsDiscoveryDir(home) {
|
|
11900
12162
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11901
|
-
return
|
|
12163
|
+
return import_node_path52.default.join(base, "daemons");
|
|
11902
12164
|
}
|
|
11903
12165
|
function daemonDiscoveryPath(project, home) {
|
|
11904
|
-
return
|
|
12166
|
+
return import_node_path52.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11905
12167
|
}
|
|
11906
12168
|
function sanitizeDiscoveryName(project) {
|
|
11907
12169
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11908
12170
|
}
|
|
11909
12171
|
function neatHomeFromEnv() {
|
|
11910
12172
|
const env = process.env.NEAT_HOME;
|
|
11911
|
-
if (env && env.length > 0) return
|
|
12173
|
+
if (env && env.length > 0) return import_node_path52.default.resolve(env);
|
|
11912
12174
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11913
|
-
return
|
|
12175
|
+
return import_node_path52.default.join(home, ".neat");
|
|
11914
12176
|
}
|
|
11915
12177
|
function resolveNeatVersion() {
|
|
11916
12178
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -11979,11 +12241,11 @@ function teardownSlot(slot) {
|
|
|
11979
12241
|
}
|
|
11980
12242
|
}
|
|
11981
12243
|
function neatHomeFor(opts) {
|
|
11982
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
12244
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path52.default.resolve(opts.neatHome);
|
|
11983
12245
|
const env = process.env.NEAT_HOME;
|
|
11984
|
-
if (env && env.length > 0) return
|
|
12246
|
+
if (env && env.length > 0) return import_node_path52.default.resolve(env);
|
|
11985
12247
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11986
|
-
return
|
|
12248
|
+
return import_node_path52.default.join(home, ".neat");
|
|
11987
12249
|
}
|
|
11988
12250
|
function routeSpanToProject(serviceName, projects) {
|
|
11989
12251
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -12027,11 +12289,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
12027
12289
|
if (!serviceName) return true;
|
|
12028
12290
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
12029
12291
|
return graph.someNode(
|
|
12030
|
-
(_id, attrs) => attrs.type ===
|
|
12292
|
+
(_id, attrs) => attrs.type === import_types48.NodeType.ServiceNode && attrs.name === serviceName
|
|
12031
12293
|
);
|
|
12032
12294
|
}
|
|
12033
12295
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
12034
|
-
const paths = pathsForProject(entry2.name,
|
|
12296
|
+
const paths = pathsForProject(entry2.name, import_node_path52.default.join(entry2.path, "neat-out"));
|
|
12035
12297
|
try {
|
|
12036
12298
|
const stat = await import_node_fs31.promises.stat(entry2.path);
|
|
12037
12299
|
if (!stat.isDirectory()) {
|
|
@@ -12162,7 +12424,7 @@ async function startDaemon(opts = {}) {
|
|
|
12162
12424
|
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;
|
|
12163
12425
|
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;
|
|
12164
12426
|
const singleProject = projectArg;
|
|
12165
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
12427
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path52.default.resolve(projectPathArg) : null;
|
|
12166
12428
|
if (singleProject && !singleProjectPath) {
|
|
12167
12429
|
throw new Error(
|
|
12168
12430
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -12177,7 +12439,7 @@ async function startDaemon(opts = {}) {
|
|
|
12177
12439
|
);
|
|
12178
12440
|
}
|
|
12179
12441
|
}
|
|
12180
|
-
const pidPath =
|
|
12442
|
+
const pidPath = import_node_path52.default.join(home, "neatd.pid");
|
|
12181
12443
|
await writeAtomically(pidPath, `${process.pid}
|
|
12182
12444
|
`);
|
|
12183
12445
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12584,8 +12846,8 @@ async function startDaemon(opts = {}) {
|
|
|
12584
12846
|
let registryWatcher = null;
|
|
12585
12847
|
let reloadTimer = null;
|
|
12586
12848
|
if (!singleProject) try {
|
|
12587
|
-
const regDir =
|
|
12588
|
-
const regBase =
|
|
12849
|
+
const regDir = import_node_path52.default.dirname(regPath);
|
|
12850
|
+
const regBase = import_node_path52.default.basename(regPath);
|
|
12589
12851
|
registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
|
|
12590
12852
|
if (filename !== null && filename !== regBase) return;
|
|
12591
12853
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
@@ -12660,7 +12922,7 @@ init_cjs_shims();
|
|
|
12660
12922
|
var import_node_child_process2 = require("child_process");
|
|
12661
12923
|
var import_node_fs32 = require("fs");
|
|
12662
12924
|
var import_node_net = __toESM(require("net"), 1);
|
|
12663
|
-
var
|
|
12925
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
12664
12926
|
var DEFAULT_WEB_PORT = 6328;
|
|
12665
12927
|
var DEFAULT_REST_PORT = 8080;
|
|
12666
12928
|
function asValidPort(value) {
|
|
@@ -12669,11 +12931,11 @@ function asValidPort(value) {
|
|
|
12669
12931
|
}
|
|
12670
12932
|
function projectRoot() {
|
|
12671
12933
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
12672
|
-
return
|
|
12934
|
+
return import_node_path53.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
12673
12935
|
}
|
|
12674
12936
|
async function readDaemonPorts(root) {
|
|
12675
12937
|
try {
|
|
12676
|
-
const raw = await import_node_fs32.promises.readFile(
|
|
12938
|
+
const raw = await import_node_fs32.promises.readFile(import_node_path53.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
12677
12939
|
const parsed = JSON.parse(raw);
|
|
12678
12940
|
const ports = parsed?.ports ?? {};
|
|
12679
12941
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -12718,10 +12980,10 @@ function resolveWebPackageDir() {
|
|
|
12718
12980
|
eval("require")
|
|
12719
12981
|
);
|
|
12720
12982
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
12721
|
-
return
|
|
12983
|
+
return import_node_path53.default.dirname(pkgJsonPath);
|
|
12722
12984
|
}
|
|
12723
12985
|
function resolveStandaloneServerEntry(webDir) {
|
|
12724
|
-
return
|
|
12986
|
+
return import_node_path53.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
12725
12987
|
}
|
|
12726
12988
|
async function pickInternalPort() {
|
|
12727
12989
|
return new Promise((resolve, reject) => {
|
|
@@ -12774,7 +13036,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
12774
13036
|
NEAT_API_URL: apiUrl
|
|
12775
13037
|
};
|
|
12776
13038
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
12777
|
-
cwd:
|
|
13039
|
+
cwd: import_node_path53.default.dirname(serverEntry),
|
|
12778
13040
|
env,
|
|
12779
13041
|
stdio: ["ignore", "inherit", "inherit"],
|
|
12780
13042
|
detached: false
|
|
@@ -12950,14 +13212,14 @@ function localVersion() {
|
|
|
12950
13212
|
}
|
|
12951
13213
|
function neatHome3() {
|
|
12952
13214
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
12953
|
-
return
|
|
13215
|
+
return import_node_path54.default.resolve(process.env.NEAT_HOME);
|
|
12954
13216
|
}
|
|
12955
13217
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12956
|
-
return
|
|
13218
|
+
return import_node_path54.default.join(home, ".neat");
|
|
12957
13219
|
}
|
|
12958
13220
|
async function readPid() {
|
|
12959
13221
|
try {
|
|
12960
|
-
const raw = await import_node_fs33.promises.readFile(
|
|
13222
|
+
const raw = await import_node_fs33.promises.readFile(import_node_path54.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
12961
13223
|
const n = Number.parseInt(raw.trim(), 10);
|
|
12962
13224
|
return Number.isFinite(n) ? n : null;
|
|
12963
13225
|
} catch {
|