@neat.is/core 0.4.26-dev.20260702 → 0.4.26-dev.20260703
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-ZX7PCMGZ.js → chunk-2LNICOVU.js} +30 -3
- package/dist/chunk-2LNICOVU.js.map +1 -0
- package/dist/{chunk-XOOCA5T7.js → chunk-4OR4RQEO.js} +13 -1
- package/dist/chunk-4OR4RQEO.js.map +1 -0
- package/dist/{chunk-GAFTW2OX.js → chunk-C5NCCKPZ.js} +2 -2
- package/dist/{chunk-IABNGQT2.js → chunk-S7TDPQDD.js} +15 -3
- package/dist/chunk-S7TDPQDD.js.map +1 -0
- package/dist/{chunk-O25KZNZK.js → chunk-WZYH5DVG.js} +957 -238
- package/dist/chunk-WZYH5DVG.js.map +1 -0
- package/dist/cli.cjs +1149 -406
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5 -5
- package/dist/index.cjs +1011 -269
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +1112 -278
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-QYHUJ4OY.js → otel-grpc-LFYZDSK3.js} +3 -3
- package/dist/server.cjs +954 -211
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-IABNGQT2.js.map +0 -1
- package/dist/chunk-O25KZNZK.js.map +0 -1
- package/dist/chunk-XOOCA5T7.js.map +0 -1
- package/dist/chunk-ZX7PCMGZ.js.map +0 -1
- /package/dist/{chunk-GAFTW2OX.js.map → chunk-C5NCCKPZ.js.map} +0 -0
- /package/dist/{otel-grpc-QYHUJ4OY.js.map → otel-grpc-LFYZDSK3.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
|
|
|
58
58
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
59
59
|
const publicRead = opts.publicRead === true;
|
|
60
60
|
app.addHook("preHandler", (req, reply, done) => {
|
|
61
|
-
const
|
|
61
|
+
const path55 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
62
62
|
for (const suffix of suffixes) {
|
|
63
|
-
if (
|
|
63
|
+
if (path55 === suffix || path55.endsWith(suffix)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -71,11 +71,13 @@ function mountBearerAuth(app, opts) {
|
|
|
71
71
|
}
|
|
72
72
|
const header = req.headers.authorization;
|
|
73
73
|
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
74
|
+
opts.onReject?.();
|
|
74
75
|
void reply.code(401).send({ error: "unauthorized" });
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
const provided = Buffer.from(header.slice("Bearer ".length).trim(), "utf8");
|
|
78
79
|
if (provided.length !== expected.length || !(0, import_node_crypto.timingSafeEqual)(provided, expected)) {
|
|
80
|
+
opts.onReject?.();
|
|
79
81
|
void reply.code(401).send({ error: "unauthorized" });
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
@@ -188,8 +190,8 @@ function reshapeGrpcRequest(req) {
|
|
|
188
190
|
};
|
|
189
191
|
}
|
|
190
192
|
function resolveProtoRoot() {
|
|
191
|
-
const here =
|
|
192
|
-
return
|
|
193
|
+
const here = import_node_path43.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
194
|
+
return import_node_path43.default.resolve(here, "..", "proto");
|
|
193
195
|
}
|
|
194
196
|
function loadTraceService() {
|
|
195
197
|
const protoRoot = resolveProtoRoot();
|
|
@@ -257,13 +259,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
257
259
|
})
|
|
258
260
|
};
|
|
259
261
|
}
|
|
260
|
-
var import_node_url2,
|
|
262
|
+
var import_node_url2, import_node_path43, import_node_crypto2, grpc, protoLoader;
|
|
261
263
|
var init_otel_grpc = __esm({
|
|
262
264
|
"src/otel-grpc.ts"() {
|
|
263
265
|
"use strict";
|
|
264
266
|
init_cjs_shims();
|
|
265
267
|
import_node_url2 = require("url");
|
|
266
|
-
|
|
268
|
+
import_node_path43 = __toESM(require("path"), 1);
|
|
267
269
|
import_node_crypto2 = require("crypto");
|
|
268
270
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
269
271
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -336,6 +338,13 @@ function pickEnv(spanAttrs, resourceAttrs) {
|
|
|
336
338
|
}
|
|
337
339
|
return ENV_FALLBACK;
|
|
338
340
|
}
|
|
341
|
+
function messagingDestinationOf(attrs) {
|
|
342
|
+
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
343
|
+
const v = attrs[key];
|
|
344
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
345
|
+
}
|
|
346
|
+
return void 0;
|
|
347
|
+
}
|
|
339
348
|
function parseOtlpRequest(body) {
|
|
340
349
|
const out = [];
|
|
341
350
|
for (const rs of body.resourceSpans ?? []) {
|
|
@@ -362,6 +371,10 @@ function parseOtlpRequest(body) {
|
|
|
362
371
|
attributes: attrs,
|
|
363
372
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
364
373
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
374
|
+
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
375
|
+
messagingDestination: messagingDestinationOf(attrs),
|
|
376
|
+
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
377
|
+
graphqlOperationType: typeof attrs["graphql.operation.type"] === "string" && attrs["graphql.operation.type"].length > 0 ? attrs["graphql.operation.type"] : void 0,
|
|
365
378
|
statusCode: span.status?.code,
|
|
366
379
|
errorMessage: span.status?.message,
|
|
367
380
|
exception: extractExceptionFromEvents(span.events)
|
|
@@ -373,10 +386,10 @@ function parseOtlpRequest(body) {
|
|
|
373
386
|
return out;
|
|
374
387
|
}
|
|
375
388
|
function loadProtoRoot() {
|
|
376
|
-
const here =
|
|
377
|
-
const protoRoot =
|
|
389
|
+
const here = import_node_path44.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
390
|
+
const protoRoot = import_node_path44.default.resolve(here, "..", "proto");
|
|
378
391
|
const root = new import_protobufjs.default.Root();
|
|
379
|
-
root.resolvePath = (_origin, target) =>
|
|
392
|
+
root.resolvePath = (_origin, target) => import_node_path44.default.resolve(protoRoot, target);
|
|
380
393
|
root.loadSync(
|
|
381
394
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
382
395
|
{ keepCase: true }
|
|
@@ -421,7 +434,21 @@ async function buildOtelReceiver(opts) {
|
|
|
421
434
|
logger: false,
|
|
422
435
|
bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
|
|
423
436
|
});
|
|
424
|
-
|
|
437
|
+
const REJECT_WARN_INTERVAL_MS = 6e4;
|
|
438
|
+
let lastRejectWarnAt = 0;
|
|
439
|
+
const warnRejectedOtlp = () => {
|
|
440
|
+
const now = Date.now();
|
|
441
|
+
if (now - lastRejectWarnAt < REJECT_WARN_INTERVAL_MS) return;
|
|
442
|
+
lastRejectWarnAt = now;
|
|
443
|
+
console.warn(
|
|
444
|
+
"[neatd] rejecting OTLP spans on /v1/traces \u2014 missing or invalid bearer token (set NEAT_OTEL_TOKEN on the instrumented app)"
|
|
445
|
+
);
|
|
446
|
+
};
|
|
447
|
+
mountBearerAuth(app, {
|
|
448
|
+
token: opts.authToken,
|
|
449
|
+
trustProxy: opts.trustProxy,
|
|
450
|
+
onReject: warnRejectedOtlp
|
|
451
|
+
});
|
|
425
452
|
const queue = [];
|
|
426
453
|
let draining = false;
|
|
427
454
|
let drainPromise = Promise.resolve();
|
|
@@ -590,12 +617,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
590
617
|
}
|
|
591
618
|
}
|
|
592
619
|
}
|
|
593
|
-
var
|
|
620
|
+
var import_node_path44, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
594
621
|
var init_otel = __esm({
|
|
595
622
|
"src/otel.ts"() {
|
|
596
623
|
"use strict";
|
|
597
624
|
init_cjs_shims();
|
|
598
|
-
|
|
625
|
+
import_node_path44 = __toESM(require("path"), 1);
|
|
599
626
|
import_node_url3 = require("url");
|
|
600
627
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
601
628
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -632,7 +659,7 @@ __export(cli_exports, {
|
|
|
632
659
|
});
|
|
633
660
|
module.exports = __toCommonJS(cli_exports);
|
|
634
661
|
init_cjs_shims();
|
|
635
|
-
var
|
|
662
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
636
663
|
var import_node_fs34 = require("fs");
|
|
637
664
|
|
|
638
665
|
// src/banner.ts
|
|
@@ -1198,19 +1225,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1198
1225
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1199
1226
|
let best = { path: [start], edges: [] };
|
|
1200
1227
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1201
|
-
function step(node,
|
|
1202
|
-
if (
|
|
1203
|
-
best = { path: [...
|
|
1228
|
+
function step(node, path55, edges) {
|
|
1229
|
+
if (path55.length > best.path.length) {
|
|
1230
|
+
best = { path: [...path55], edges: [...edges] };
|
|
1204
1231
|
}
|
|
1205
|
-
if (
|
|
1232
|
+
if (path55.length - 1 >= maxDepth) return;
|
|
1206
1233
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1207
1234
|
for (const [srcId, edge] of incoming) {
|
|
1208
1235
|
if (visited.has(srcId)) continue;
|
|
1209
1236
|
visited.add(srcId);
|
|
1210
|
-
|
|
1237
|
+
path55.push(srcId);
|
|
1211
1238
|
edges.push(edge);
|
|
1212
|
-
step(srcId,
|
|
1213
|
-
|
|
1239
|
+
step(srcId, path55, edges);
|
|
1240
|
+
path55.pop();
|
|
1214
1241
|
edges.pop();
|
|
1215
1242
|
visited.delete(srcId);
|
|
1216
1243
|
}
|
|
@@ -1218,14 +1245,14 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1218
1245
|
step(start, [start], []);
|
|
1219
1246
|
return best;
|
|
1220
1247
|
}
|
|
1221
|
-
function databaseRootCauseShape(graph, origin,
|
|
1248
|
+
function databaseRootCauseShape(graph, origin, walk3) {
|
|
1222
1249
|
const targetDb = origin;
|
|
1223
1250
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1224
1251
|
if (candidatePairs.length === 0) return null;
|
|
1225
|
-
for (const id of
|
|
1252
|
+
for (const id of walk3.path) {
|
|
1226
1253
|
const owner = resolveOwningService(graph, id);
|
|
1227
1254
|
if (!owner) continue;
|
|
1228
|
-
const { id:
|
|
1255
|
+
const { id: serviceId4, svc } = owner;
|
|
1229
1256
|
const deps = svc.dependencies ?? {};
|
|
1230
1257
|
for (const pair of candidatePairs) {
|
|
1231
1258
|
const declared = deps[pair.driver];
|
|
@@ -1238,7 +1265,7 @@ function databaseRootCauseShape(graph, origin, walk) {
|
|
|
1238
1265
|
);
|
|
1239
1266
|
if (!result.compatible) {
|
|
1240
1267
|
return {
|
|
1241
|
-
rootCauseNode:
|
|
1268
|
+
rootCauseNode: serviceId4,
|
|
1242
1269
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1243
1270
|
...result.minDriverVersion ? {
|
|
1244
1271
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1249,11 +1276,11 @@ function databaseRootCauseShape(graph, origin, walk) {
|
|
|
1249
1276
|
}
|
|
1250
1277
|
return null;
|
|
1251
1278
|
}
|
|
1252
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1253
|
-
for (const id of
|
|
1279
|
+
function serviceRootCauseShape(graph, _origin, walk3) {
|
|
1280
|
+
for (const id of walk3.path) {
|
|
1254
1281
|
const owner = resolveOwningService(graph, id);
|
|
1255
1282
|
if (!owner) continue;
|
|
1256
|
-
const { id:
|
|
1283
|
+
const { id: serviceId4, svc } = owner;
|
|
1257
1284
|
const deps = svc.dependencies ?? {};
|
|
1258
1285
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1259
1286
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1262,7 +1289,7 @@ function serviceRootCauseShape(graph, _origin, walk) {
|
|
|
1262
1289
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1263
1290
|
if (!result.compatible && result.reason) {
|
|
1264
1291
|
return {
|
|
1265
|
-
rootCauseNode:
|
|
1292
|
+
rootCauseNode: serviceId4,
|
|
1266
1293
|
rootCauseReason: result.reason,
|
|
1267
1294
|
...result.requiredNodeVersion ? {
|
|
1268
1295
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1277,7 +1304,7 @@ function serviceRootCauseShape(graph, _origin, walk) {
|
|
|
1277
1304
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1278
1305
|
if (!result.compatible && result.reason) {
|
|
1279
1306
|
return {
|
|
1280
|
-
rootCauseNode:
|
|
1307
|
+
rootCauseNode: serviceId4,
|
|
1281
1308
|
rootCauseReason: result.reason,
|
|
1282
1309
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1283
1310
|
};
|
|
@@ -1286,10 +1313,10 @@ function serviceRootCauseShape(graph, _origin, walk) {
|
|
|
1286
1313
|
}
|
|
1287
1314
|
return null;
|
|
1288
1315
|
}
|
|
1289
|
-
function fileRootCauseShape(graph, origin,
|
|
1316
|
+
function fileRootCauseShape(graph, origin, walk3) {
|
|
1290
1317
|
const owner = resolveOwningService(graph, origin.id);
|
|
1291
1318
|
if (!owner) return null;
|
|
1292
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1319
|
+
return serviceRootCauseShape(graph, owner.svc, walk3);
|
|
1293
1320
|
}
|
|
1294
1321
|
var rootCauseShapes = {
|
|
1295
1322
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1301,16 +1328,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1301
1328
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1302
1329
|
const shape = rootCauseShapes[origin.type];
|
|
1303
1330
|
if (shape) {
|
|
1304
|
-
const
|
|
1305
|
-
const match = shape(graph, origin,
|
|
1331
|
+
const walk3 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1332
|
+
const match = shape(graph, origin, walk3);
|
|
1306
1333
|
if (match) {
|
|
1307
1334
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1308
1335
|
return import_types.RootCauseResultSchema.parse({
|
|
1309
1336
|
rootCauseNode: match.rootCauseNode,
|
|
1310
1337
|
rootCauseReason: reason,
|
|
1311
|
-
traversalPath:
|
|
1312
|
-
edgeProvenances:
|
|
1313
|
-
confidence: confidenceFromMix(
|
|
1338
|
+
traversalPath: walk3.path,
|
|
1339
|
+
edgeProvenances: walk3.edges.map((e) => e.provenance),
|
|
1340
|
+
confidence: confidenceFromMix(walk3.edges),
|
|
1314
1341
|
fixRecommendation: match.fixRecommendation
|
|
1315
1342
|
});
|
|
1316
1343
|
}
|
|
@@ -1368,9 +1395,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1368
1395
|
function isFailingCallEdge(e) {
|
|
1369
1396
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1370
1397
|
}
|
|
1371
|
-
function callSourcesForService(graph,
|
|
1372
|
-
const ids = [
|
|
1373
|
-
for (const edgeId of graph.outboundEdges(
|
|
1398
|
+
function callSourcesForService(graph, serviceId4) {
|
|
1399
|
+
const ids = [serviceId4];
|
|
1400
|
+
for (const edgeId of graph.outboundEdges(serviceId4)) {
|
|
1374
1401
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1375
1402
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1376
1403
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1387,9 +1414,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1387
1414
|
}
|
|
1388
1415
|
return id < curId;
|
|
1389
1416
|
}
|
|
1390
|
-
function dominantFailingCall(graph,
|
|
1417
|
+
function dominantFailingCall(graph, serviceId4, visited) {
|
|
1391
1418
|
let best = null;
|
|
1392
|
-
for (const src of callSourcesForService(graph,
|
|
1419
|
+
for (const src of callSourcesForService(graph, serviceId4)) {
|
|
1393
1420
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1394
1421
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1395
1422
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -1404,26 +1431,26 @@ function dominantFailingCall(graph, serviceId3, visited) {
|
|
|
1404
1431
|
return best;
|
|
1405
1432
|
}
|
|
1406
1433
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1407
|
-
const
|
|
1434
|
+
const path55 = [originServiceId];
|
|
1408
1435
|
const edges = [];
|
|
1409
1436
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1410
1437
|
let current = originServiceId;
|
|
1411
1438
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1412
1439
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1413
1440
|
if (!hop) break;
|
|
1414
|
-
|
|
1441
|
+
path55.push(hop.nextService);
|
|
1415
1442
|
edges.push(hop.edge);
|
|
1416
1443
|
visited.add(hop.nextService);
|
|
1417
1444
|
current = hop.nextService;
|
|
1418
1445
|
}
|
|
1419
1446
|
if (edges.length === 0) return null;
|
|
1420
|
-
return { path:
|
|
1447
|
+
return { path: path55, edges, culprit: current };
|
|
1421
1448
|
}
|
|
1422
1449
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1423
1450
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1424
1451
|
if (!chain) return null;
|
|
1425
1452
|
const culprit = chain.culprit;
|
|
1426
|
-
const
|
|
1453
|
+
const path55 = [...chain.path];
|
|
1427
1454
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1428
1455
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1429
1456
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1431,14 +1458,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1431
1458
|
if (loc) {
|
|
1432
1459
|
let rootCauseNode = culprit;
|
|
1433
1460
|
if (loc.fileNode) {
|
|
1434
|
-
|
|
1461
|
+
path55.push(loc.fileNode);
|
|
1435
1462
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1436
1463
|
rootCauseNode = loc.fileNode;
|
|
1437
1464
|
}
|
|
1438
1465
|
return import_types.RootCauseResultSchema.parse({
|
|
1439
1466
|
rootCauseNode,
|
|
1440
1467
|
rootCauseReason: loc.rootCauseReason,
|
|
1441
|
-
traversalPath:
|
|
1468
|
+
traversalPath: path55,
|
|
1442
1469
|
edgeProvenances,
|
|
1443
1470
|
confidence,
|
|
1444
1471
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1450,7 +1477,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1450
1477
|
return import_types.RootCauseResultSchema.parse({
|
|
1451
1478
|
rootCauseNode: culprit,
|
|
1452
1479
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1453
|
-
traversalPath:
|
|
1480
|
+
traversalPath: path55,
|
|
1454
1481
|
edgeProvenances,
|
|
1455
1482
|
confidence,
|
|
1456
1483
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2374,10 +2401,48 @@ var STITCH_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
|
2374
2401
|
]);
|
|
2375
2402
|
var WIRE_SPAN_KIND_CLIENT = 3;
|
|
2376
2403
|
var WIRE_SPAN_KIND_PRODUCER = 4;
|
|
2404
|
+
var WIRE_SPAN_KIND_CONSUMER = 5;
|
|
2377
2405
|
function spanMintsObservedEdge(kind) {
|
|
2378
2406
|
if (kind === void 0 || kind === 0) return true;
|
|
2379
2407
|
return kind === WIRE_SPAN_KIND_CLIENT || kind === WIRE_SPAN_KIND_PRODUCER;
|
|
2380
2408
|
}
|
|
2409
|
+
function spanMintsMessagingEdge(kind) {
|
|
2410
|
+
return kind === WIRE_SPAN_KIND_PRODUCER || kind === WIRE_SPAN_KIND_CONSUMER;
|
|
2411
|
+
}
|
|
2412
|
+
function spanServesGraphqlOperation(kind) {
|
|
2413
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2414
|
+
}
|
|
2415
|
+
function ensureGraphqlOperationNode(graph, serviceName, operationType, operationName) {
|
|
2416
|
+
const id = (0, import_types3.graphqlOperationId)(serviceName, operationType, operationName);
|
|
2417
|
+
if (graph.hasNode(id)) return id;
|
|
2418
|
+
const node = {
|
|
2419
|
+
id,
|
|
2420
|
+
type: import_types3.NodeType.GraphQLOperationNode,
|
|
2421
|
+
name: operationName,
|
|
2422
|
+
service: serviceName,
|
|
2423
|
+
operationType: operationType.toLowerCase(),
|
|
2424
|
+
operationName,
|
|
2425
|
+
discoveredVia: "otel"
|
|
2426
|
+
};
|
|
2427
|
+
graph.addNode(id, node);
|
|
2428
|
+
return id;
|
|
2429
|
+
}
|
|
2430
|
+
function messagingDestinationKind(system) {
|
|
2431
|
+
return `${system}-topic`;
|
|
2432
|
+
}
|
|
2433
|
+
function ensureMessagingDestinationNode(graph, system, destination) {
|
|
2434
|
+
const id = (0, import_types3.infraId)(messagingDestinationKind(system), destination);
|
|
2435
|
+
if (graph.hasNode(id)) return id;
|
|
2436
|
+
const node = {
|
|
2437
|
+
id,
|
|
2438
|
+
type: import_types3.NodeType.InfraNode,
|
|
2439
|
+
name: destination,
|
|
2440
|
+
provider: "self",
|
|
2441
|
+
kind: messagingDestinationKind(system)
|
|
2442
|
+
};
|
|
2443
|
+
graph.addNode(id, node);
|
|
2444
|
+
return id;
|
|
2445
|
+
}
|
|
2381
2446
|
var PARENT_SPAN_CACHE_SIZE = 1e4;
|
|
2382
2447
|
var PARENT_SPAN_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
2383
2448
|
var parentSpanCache = /* @__PURE__ */ new Map();
|
|
@@ -2471,6 +2536,21 @@ function ensureDatabaseNode(graph, host, engine) {
|
|
|
2471
2536
|
graph.addNode(id, node);
|
|
2472
2537
|
return id;
|
|
2473
2538
|
}
|
|
2539
|
+
function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
2540
|
+
const id = (0, import_types3.localDatabaseId)(serviceName, name);
|
|
2541
|
+
if (graph.hasNode(id)) return id;
|
|
2542
|
+
const node = {
|
|
2543
|
+
id,
|
|
2544
|
+
type: import_types3.NodeType.DatabaseNode,
|
|
2545
|
+
name,
|
|
2546
|
+
engine,
|
|
2547
|
+
engineVersion: "unknown",
|
|
2548
|
+
compatibleDrivers: [],
|
|
2549
|
+
discoveredVia: "otel"
|
|
2550
|
+
};
|
|
2551
|
+
graph.addNode(id, node);
|
|
2552
|
+
return id;
|
|
2553
|
+
}
|
|
2474
2554
|
function ensureFrontierNode(graph, host, ts) {
|
|
2475
2555
|
const id = frontierIdFor(host);
|
|
2476
2556
|
if (graph.hasNode(id)) {
|
|
@@ -2722,10 +2802,21 @@ async function handleSpan(ctx, span) {
|
|
|
2722
2802
|
let affectedNode = sourceId;
|
|
2723
2803
|
const mintsFromCallerSide = spanMintsObservedEdge(span.kind);
|
|
2724
2804
|
if (span.dbSystem) {
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2805
|
+
if (mintsFromCallerSide) {
|
|
2806
|
+
const host = pickAddress(span);
|
|
2807
|
+
let targetId;
|
|
2808
|
+
if (host) {
|
|
2809
|
+
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
2810
|
+
targetId = (0, import_types3.databaseId)(host);
|
|
2811
|
+
} else {
|
|
2812
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
2813
|
+
targetId = ensureLocalDatabaseNode(
|
|
2814
|
+
ctx.graph,
|
|
2815
|
+
span.service,
|
|
2816
|
+
localName,
|
|
2817
|
+
span.dbSystem
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2729
2820
|
const result = upsertObservedEdge(
|
|
2730
2821
|
ctx.graph,
|
|
2731
2822
|
import_types3.EdgeType.CONNECTS_TO,
|
|
@@ -2737,6 +2828,40 @@ async function handleSpan(ctx, span) {
|
|
|
2737
2828
|
);
|
|
2738
2829
|
if (result) affectedNode = targetId;
|
|
2739
2830
|
}
|
|
2831
|
+
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2832
|
+
const targetId = ensureMessagingDestinationNode(
|
|
2833
|
+
ctx.graph,
|
|
2834
|
+
span.messagingSystem,
|
|
2835
|
+
span.messagingDestination
|
|
2836
|
+
);
|
|
2837
|
+
const edgeType = span.kind === WIRE_SPAN_KIND_CONSUMER ? import_types3.EdgeType.CONSUMES_FROM : import_types3.EdgeType.PUBLISHES_TO;
|
|
2838
|
+
const result = upsertObservedEdge(
|
|
2839
|
+
ctx.graph,
|
|
2840
|
+
edgeType,
|
|
2841
|
+
observedSource(),
|
|
2842
|
+
targetId,
|
|
2843
|
+
ts,
|
|
2844
|
+
isError,
|
|
2845
|
+
callSiteEvidence
|
|
2846
|
+
);
|
|
2847
|
+
if (result) affectedNode = targetId;
|
|
2848
|
+
} else if (span.graphqlOperationName && span.graphqlOperationType && spanServesGraphqlOperation(span.kind)) {
|
|
2849
|
+
const targetId = ensureGraphqlOperationNode(
|
|
2850
|
+
ctx.graph,
|
|
2851
|
+
span.service,
|
|
2852
|
+
span.graphqlOperationType,
|
|
2853
|
+
span.graphqlOperationName
|
|
2854
|
+
);
|
|
2855
|
+
const result = upsertObservedEdge(
|
|
2856
|
+
ctx.graph,
|
|
2857
|
+
import_types3.EdgeType.CONTAINS,
|
|
2858
|
+
observedSource(),
|
|
2859
|
+
targetId,
|
|
2860
|
+
ts,
|
|
2861
|
+
isError,
|
|
2862
|
+
callSiteEvidence
|
|
2863
|
+
);
|
|
2864
|
+
if (result) affectedNode = targetId;
|
|
2740
2865
|
} else {
|
|
2741
2866
|
const host = pickAddress(span);
|
|
2742
2867
|
let resolvedViaAddress = false;
|
|
@@ -2846,29 +2971,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
2846
2971
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
2847
2972
|
});
|
|
2848
2973
|
let promoted = 0;
|
|
2849
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
2974
|
+
for (const { frontierId: frontierId2, serviceId: serviceId4 } of toPromote) {
|
|
2850
2975
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
2851
2976
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
2852
2977
|
if (!gate.allowed) {
|
|
2853
2978
|
continue;
|
|
2854
2979
|
}
|
|
2855
2980
|
}
|
|
2856
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
2981
|
+
rewireFrontierEdges(graph, frontierId2, serviceId4);
|
|
2857
2982
|
graph.dropNode(frontierId2);
|
|
2858
2983
|
promoted++;
|
|
2859
2984
|
}
|
|
2860
2985
|
return promoted;
|
|
2861
2986
|
}
|
|
2862
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
2987
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId4) {
|
|
2863
2988
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
2864
2989
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
2865
2990
|
for (const edgeId of inbound) {
|
|
2866
2991
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
2867
|
-
rebuildEdge(graph, edge, edge.source,
|
|
2992
|
+
rebuildEdge(graph, edge, edge.source, serviceId4, edgeId);
|
|
2868
2993
|
}
|
|
2869
2994
|
for (const edgeId of outbound) {
|
|
2870
2995
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
2871
|
-
rebuildEdge(graph, edge,
|
|
2996
|
+
rebuildEdge(graph, edge, serviceId4, edge.target, edgeId);
|
|
2872
2997
|
}
|
|
2873
2998
|
}
|
|
2874
2999
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -3642,9 +3767,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
3642
3767
|
"StatefulSet",
|
|
3643
3768
|
"DaemonSet"
|
|
3644
3769
|
]);
|
|
3645
|
-
function addAliases(graph,
|
|
3646
|
-
if (!graph.hasNode(
|
|
3647
|
-
const node = graph.getNodeAttributes(
|
|
3770
|
+
function addAliases(graph, serviceId4, candidates) {
|
|
3771
|
+
if (!graph.hasNode(serviceId4)) return;
|
|
3772
|
+
const node = graph.getNodeAttributes(serviceId4);
|
|
3648
3773
|
if (node.type !== import_types6.NodeType.ServiceNode) return;
|
|
3649
3774
|
const set = new Set(node.aliases ?? []);
|
|
3650
3775
|
for (const c of candidates) {
|
|
@@ -3654,7 +3779,7 @@ function addAliases(graph, serviceId3, candidates) {
|
|
|
3654
3779
|
}
|
|
3655
3780
|
if (set.size === 0) return;
|
|
3656
3781
|
const updated = { ...node, aliases: [...set].sort() };
|
|
3657
|
-
graph.replaceNodeAttributes(
|
|
3782
|
+
graph.replaceNodeAttributes(serviceId4, updated);
|
|
3658
3783
|
}
|
|
3659
3784
|
function indexServicesByName(services) {
|
|
3660
3785
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3687,12 +3812,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
3687
3812
|
}
|
|
3688
3813
|
if (!compose?.services) return;
|
|
3689
3814
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3690
|
-
const
|
|
3691
|
-
if (!
|
|
3815
|
+
const serviceId4 = serviceIndex.get(composeName);
|
|
3816
|
+
if (!serviceId4) continue;
|
|
3692
3817
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
3693
3818
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
3694
3819
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
3695
|
-
addAliases(graph,
|
|
3820
|
+
addAliases(graph, serviceId4, aliases);
|
|
3696
3821
|
}
|
|
3697
3822
|
}
|
|
3698
3823
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -3805,16 +3930,28 @@ init_cjs_shims();
|
|
|
3805
3930
|
var import_node_fs11 = require("fs");
|
|
3806
3931
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
3807
3932
|
var import_types7 = require("@neat.is/types");
|
|
3933
|
+
function buildServiceHostIndex(services) {
|
|
3934
|
+
const knownHosts = /* @__PURE__ */ new Set();
|
|
3935
|
+
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
3936
|
+
for (const service of services) {
|
|
3937
|
+
const base = import_node_path11.default.basename(service.dir);
|
|
3938
|
+
knownHosts.add(base);
|
|
3939
|
+
knownHosts.add(service.pkg.name);
|
|
3940
|
+
hostToNodeId.set(base, service.node.id);
|
|
3941
|
+
hostToNodeId.set(service.pkg.name, service.node.id);
|
|
3942
|
+
}
|
|
3943
|
+
return { knownHosts, hostToNodeId };
|
|
3944
|
+
}
|
|
3808
3945
|
async function walkSourceFiles(dir) {
|
|
3809
3946
|
const out = [];
|
|
3810
|
-
async function
|
|
3947
|
+
async function walk3(current) {
|
|
3811
3948
|
const entries = await import_node_fs11.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
3812
3949
|
for (const entry2 of entries) {
|
|
3813
3950
|
const full = import_node_path11.default.join(current, entry2.name);
|
|
3814
3951
|
if (entry2.isDirectory()) {
|
|
3815
3952
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
3816
3953
|
if (await isPythonVenvDir(full)) continue;
|
|
3817
|
-
await
|
|
3954
|
+
await walk3(full);
|
|
3818
3955
|
} else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path11.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
3819
3956
|
// would attribute our instrumentation imports to the user's service.
|
|
3820
3957
|
!isNeatAuthoredSourceFile(entry2.name)) {
|
|
@@ -3822,7 +3959,7 @@ async function walkSourceFiles(dir) {
|
|
|
3822
3959
|
}
|
|
3823
3960
|
}
|
|
3824
3961
|
}
|
|
3825
|
-
await
|
|
3962
|
+
await walk3(dir);
|
|
3826
3963
|
return out;
|
|
3827
3964
|
}
|
|
3828
3965
|
async function loadSourceFiles(dir) {
|
|
@@ -4916,20 +5053,20 @@ var import_node_path22 = __toESM(require("path"), 1);
|
|
|
4916
5053
|
var import_types10 = require("@neat.is/types");
|
|
4917
5054
|
async function walkConfigFiles(dir) {
|
|
4918
5055
|
const out = [];
|
|
4919
|
-
async function
|
|
5056
|
+
async function walk3(current) {
|
|
4920
5057
|
const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true });
|
|
4921
5058
|
for (const entry2 of entries) {
|
|
4922
5059
|
const full = import_node_path22.default.join(current, entry2.name);
|
|
4923
5060
|
if (entry2.isDirectory()) {
|
|
4924
5061
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4925
5062
|
if (await isPythonVenvDir(full)) continue;
|
|
4926
|
-
await
|
|
5063
|
+
await walk3(full);
|
|
4927
5064
|
} else if (entry2.isFile() && isConfigFile(entry2.name).match) {
|
|
4928
5065
|
out.push(full);
|
|
4929
5066
|
}
|
|
4930
5067
|
}
|
|
4931
5068
|
}
|
|
4932
|
-
await
|
|
5069
|
+
await walk3(dir);
|
|
4933
5070
|
return out;
|
|
4934
5071
|
}
|
|
4935
5072
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -4977,17 +5114,347 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4977
5114
|
return { nodesAdded, edgesAdded };
|
|
4978
5115
|
}
|
|
4979
5116
|
|
|
5117
|
+
// src/extract/routes.ts
|
|
5118
|
+
init_cjs_shims();
|
|
5119
|
+
var import_node_path23 = __toESM(require("path"), 1);
|
|
5120
|
+
var import_tree_sitter2 = __toESM(require("tree-sitter"), 1);
|
|
5121
|
+
var import_tree_sitter_javascript2 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5122
|
+
var import_types11 = require("@neat.is/types");
|
|
5123
|
+
var PARSE_CHUNK2 = 16384;
|
|
5124
|
+
function parseSource2(parser, source) {
|
|
5125
|
+
return parser.parse(
|
|
5126
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK2)
|
|
5127
|
+
);
|
|
5128
|
+
}
|
|
5129
|
+
function makeJsParser2() {
|
|
5130
|
+
const p = new import_tree_sitter2.default();
|
|
5131
|
+
p.setLanguage(import_tree_sitter_javascript2.default);
|
|
5132
|
+
return p;
|
|
5133
|
+
}
|
|
5134
|
+
var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
5135
|
+
"get",
|
|
5136
|
+
"post",
|
|
5137
|
+
"put",
|
|
5138
|
+
"patch",
|
|
5139
|
+
"delete",
|
|
5140
|
+
"options",
|
|
5141
|
+
"head",
|
|
5142
|
+
"all"
|
|
5143
|
+
]);
|
|
5144
|
+
var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
5145
|
+
var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
5146
|
+
function canonicalizeTemplate(raw) {
|
|
5147
|
+
let p = raw.split("?")[0].split("#")[0];
|
|
5148
|
+
if (!p.startsWith("/")) p = "/" + p;
|
|
5149
|
+
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1);
|
|
5150
|
+
return p;
|
|
5151
|
+
}
|
|
5152
|
+
function isDynamicSegment(seg) {
|
|
5153
|
+
if (seg.length === 0) return false;
|
|
5154
|
+
if (seg.includes(":")) return true;
|
|
5155
|
+
if (seg.startsWith("{") || seg.startsWith("[")) return true;
|
|
5156
|
+
if (/^\d+$/.test(seg)) return true;
|
|
5157
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seg)) return true;
|
|
5158
|
+
if (/^[0-9a-f]{24,}$/i.test(seg)) return true;
|
|
5159
|
+
return false;
|
|
5160
|
+
}
|
|
5161
|
+
function normalizePathTemplate(raw) {
|
|
5162
|
+
const canonical = canonicalizeTemplate(raw);
|
|
5163
|
+
const segments = canonical.split("/").filter((s) => s.length > 0);
|
|
5164
|
+
const normalised = segments.map((seg) => isDynamicSegment(seg) ? ":param" : seg.toLowerCase());
|
|
5165
|
+
return "/" + normalised.join("/");
|
|
5166
|
+
}
|
|
5167
|
+
function walk(node, visit) {
|
|
5168
|
+
visit(node);
|
|
5169
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5170
|
+
const child = node.namedChild(i);
|
|
5171
|
+
if (child) walk(child, visit);
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
function staticStringText(node) {
|
|
5175
|
+
if (node.type === "string") {
|
|
5176
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5177
|
+
const child = node.namedChild(i);
|
|
5178
|
+
if (child?.type === "string_fragment") return child.text;
|
|
5179
|
+
}
|
|
5180
|
+
return "";
|
|
5181
|
+
}
|
|
5182
|
+
if (node.type === "template_string") {
|
|
5183
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5184
|
+
if (node.namedChild(i)?.type === "template_substitution") return null;
|
|
5185
|
+
}
|
|
5186
|
+
const raw = node.text;
|
|
5187
|
+
return raw.length >= 2 ? raw.slice(1, -1) : "";
|
|
5188
|
+
}
|
|
5189
|
+
return null;
|
|
5190
|
+
}
|
|
5191
|
+
function objectStringProp(objNode, key) {
|
|
5192
|
+
for (let i = 0; i < objNode.namedChildCount; i++) {
|
|
5193
|
+
const pair = objNode.namedChild(i);
|
|
5194
|
+
if (!pair || pair.type !== "pair") continue;
|
|
5195
|
+
const k = pair.childForFieldName("key");
|
|
5196
|
+
if (!k) continue;
|
|
5197
|
+
const kText = k.type === "string" ? staticStringText(k) : k.text;
|
|
5198
|
+
if (kText !== key) continue;
|
|
5199
|
+
const v = pair.childForFieldName("value");
|
|
5200
|
+
if (v) return staticStringText(v);
|
|
5201
|
+
}
|
|
5202
|
+
return null;
|
|
5203
|
+
}
|
|
5204
|
+
function fastifyRouteMethods(objNode) {
|
|
5205
|
+
for (let i = 0; i < objNode.namedChildCount; i++) {
|
|
5206
|
+
const pair = objNode.namedChild(i);
|
|
5207
|
+
if (!pair || pair.type !== "pair") continue;
|
|
5208
|
+
const k = pair.childForFieldName("key");
|
|
5209
|
+
const kText = k ? k.type === "string" ? staticStringText(k) : k.text : null;
|
|
5210
|
+
if (kText !== "method") continue;
|
|
5211
|
+
const v = pair.childForFieldName("value");
|
|
5212
|
+
if (!v) return [];
|
|
5213
|
+
if (v.type === "string" || v.type === "template_string") {
|
|
5214
|
+
const s = staticStringText(v);
|
|
5215
|
+
return s ? [s.toUpperCase()] : [];
|
|
5216
|
+
}
|
|
5217
|
+
if (v.type === "array") {
|
|
5218
|
+
const out = [];
|
|
5219
|
+
for (let j = 0; j < v.namedChildCount; j++) {
|
|
5220
|
+
const el = v.namedChild(j);
|
|
5221
|
+
if (el && (el.type === "string" || el.type === "template_string")) {
|
|
5222
|
+
const s = staticStringText(el);
|
|
5223
|
+
if (s) out.push(s.toUpperCase());
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
return out;
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
return [];
|
|
5230
|
+
}
|
|
5231
|
+
function serverRoutesFromSource(source, parser, hasExpress, hasFastify) {
|
|
5232
|
+
const tree = parseSource2(parser, source);
|
|
5233
|
+
const out = [];
|
|
5234
|
+
const framework = hasExpress ? "express" : "fastify";
|
|
5235
|
+
walk(tree.rootNode, (node) => {
|
|
5236
|
+
if (node.type !== "call_expression") return;
|
|
5237
|
+
const fn = node.childForFieldName("function");
|
|
5238
|
+
if (!fn || fn.type !== "member_expression") return;
|
|
5239
|
+
const prop = fn.childForFieldName("property");
|
|
5240
|
+
if (!prop) return;
|
|
5241
|
+
const method = prop.text.toLowerCase();
|
|
5242
|
+
const args = node.childForFieldName("arguments");
|
|
5243
|
+
const first = args?.namedChild(0);
|
|
5244
|
+
if (!first) return;
|
|
5245
|
+
const line = node.startPosition.row + 1;
|
|
5246
|
+
if (ROUTER_METHODS.has(method)) {
|
|
5247
|
+
const p = staticStringText(first);
|
|
5248
|
+
if (p && p.startsWith("/")) {
|
|
5249
|
+
out.push({
|
|
5250
|
+
method: method === "all" ? "ALL" : method.toUpperCase(),
|
|
5251
|
+
pathTemplate: canonicalizeTemplate(p),
|
|
5252
|
+
line,
|
|
5253
|
+
framework
|
|
5254
|
+
});
|
|
5255
|
+
}
|
|
5256
|
+
return;
|
|
5257
|
+
}
|
|
5258
|
+
if (method === "route" && hasFastify && first.type === "object") {
|
|
5259
|
+
const url = objectStringProp(first, "url");
|
|
5260
|
+
if (!url || !url.startsWith("/")) return;
|
|
5261
|
+
const methods = fastifyRouteMethods(first);
|
|
5262
|
+
const list = methods.length > 0 ? methods : ["ALL"];
|
|
5263
|
+
for (const m of list) {
|
|
5264
|
+
out.push({
|
|
5265
|
+
method: m === "ALL" ? "ALL" : m.toUpperCase(),
|
|
5266
|
+
pathTemplate: canonicalizeTemplate(url),
|
|
5267
|
+
line,
|
|
5268
|
+
framework: "fastify"
|
|
5269
|
+
});
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
});
|
|
5273
|
+
return out;
|
|
5274
|
+
}
|
|
5275
|
+
function segmentsOf(relFile) {
|
|
5276
|
+
return toPosix2(relFile).split("/").filter((s) => s.length > 0);
|
|
5277
|
+
}
|
|
5278
|
+
function isNextAppRouteFile(relFile) {
|
|
5279
|
+
const segs = segmentsOf(relFile);
|
|
5280
|
+
if (!segs.includes("app")) return false;
|
|
5281
|
+
const base = segs[segs.length - 1] ?? "";
|
|
5282
|
+
return /^route\.(?:js|jsx|mjs|cjs|ts|tsx)$/.test(base);
|
|
5283
|
+
}
|
|
5284
|
+
function isNextPagesApiFile(relFile) {
|
|
5285
|
+
const segs = segmentsOf(relFile);
|
|
5286
|
+
const pagesIdx = segs.indexOf("pages");
|
|
5287
|
+
if (pagesIdx === -1 || segs[pagesIdx + 1] !== "api") return false;
|
|
5288
|
+
const base = segs[segs.length - 1] ?? "";
|
|
5289
|
+
if (/^_(app|document|middleware)\./.test(base)) return false;
|
|
5290
|
+
return JS_ROUTE_EXTENSIONS.has(import_node_path23.default.extname(base));
|
|
5291
|
+
}
|
|
5292
|
+
function nextSegment(seg) {
|
|
5293
|
+
if (seg.startsWith("(") && seg.endsWith(")")) return null;
|
|
5294
|
+
const catchAll = seg.match(/^\[\[?\.\.\.(.+?)\]?\]$/);
|
|
5295
|
+
if (catchAll) return ":" + catchAll[1];
|
|
5296
|
+
const dynamic = seg.match(/^\[(.+?)\]$/);
|
|
5297
|
+
if (dynamic) return ":" + dynamic[1];
|
|
5298
|
+
return seg;
|
|
5299
|
+
}
|
|
5300
|
+
function nextAppPathTemplate(relFile) {
|
|
5301
|
+
const segs = segmentsOf(relFile);
|
|
5302
|
+
const appIdx = segs.lastIndexOf("app");
|
|
5303
|
+
const between = segs.slice(appIdx + 1, segs.length - 1);
|
|
5304
|
+
const parts = [];
|
|
5305
|
+
for (const seg of between) {
|
|
5306
|
+
const mapped = nextSegment(seg);
|
|
5307
|
+
if (mapped !== null) parts.push(mapped);
|
|
5308
|
+
}
|
|
5309
|
+
return "/" + parts.join("/");
|
|
5310
|
+
}
|
|
5311
|
+
function nextPagesApiPathTemplate(relFile) {
|
|
5312
|
+
const segs = segmentsOf(relFile);
|
|
5313
|
+
const pagesIdx = segs.indexOf("pages");
|
|
5314
|
+
const rest = segs.slice(pagesIdx + 1);
|
|
5315
|
+
const parts = [];
|
|
5316
|
+
for (let i = 0; i < rest.length; i++) {
|
|
5317
|
+
let seg = rest[i];
|
|
5318
|
+
if (i === rest.length - 1) {
|
|
5319
|
+
seg = seg.replace(/\.(?:js|jsx|mjs|cjs|ts|tsx)$/, "");
|
|
5320
|
+
if (seg === "index") continue;
|
|
5321
|
+
}
|
|
5322
|
+
const mapped = nextSegment(seg);
|
|
5323
|
+
if (mapped !== null) parts.push(mapped);
|
|
5324
|
+
}
|
|
5325
|
+
return "/" + parts.join("/");
|
|
5326
|
+
}
|
|
5327
|
+
function nextAppMethods(root) {
|
|
5328
|
+
const out = [];
|
|
5329
|
+
walk(root, (node) => {
|
|
5330
|
+
if (node.type !== "export_statement") return;
|
|
5331
|
+
const decl = node.childForFieldName("declaration");
|
|
5332
|
+
if (!decl) return;
|
|
5333
|
+
const line = node.startPosition.row + 1;
|
|
5334
|
+
if (decl.type === "function_declaration") {
|
|
5335
|
+
const name = decl.childForFieldName("name")?.text;
|
|
5336
|
+
if (name && NEXT_APP_METHODS.has(name)) out.push({ method: name, line });
|
|
5337
|
+
return;
|
|
5338
|
+
}
|
|
5339
|
+
if (decl.type === "lexical_declaration" || decl.type === "variable_declaration") {
|
|
5340
|
+
for (let i = 0; i < decl.namedChildCount; i++) {
|
|
5341
|
+
const d = decl.namedChild(i);
|
|
5342
|
+
if (d?.type !== "variable_declarator") continue;
|
|
5343
|
+
const name = d.childForFieldName("name")?.text;
|
|
5344
|
+
if (name && NEXT_APP_METHODS.has(name)) out.push({ method: name, line });
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
});
|
|
5348
|
+
return out;
|
|
5349
|
+
}
|
|
5350
|
+
function nextRoutesFromFile(source, relFile, parser) {
|
|
5351
|
+
if (isNextAppRouteFile(relFile)) {
|
|
5352
|
+
const tree = parseSource2(parser, source);
|
|
5353
|
+
const template = nextAppPathTemplate(relFile);
|
|
5354
|
+
return nextAppMethods(tree.rootNode).map(({ method, line }) => ({
|
|
5355
|
+
method,
|
|
5356
|
+
pathTemplate: canonicalizeTemplate(template),
|
|
5357
|
+
line,
|
|
5358
|
+
framework: "next"
|
|
5359
|
+
}));
|
|
5360
|
+
}
|
|
5361
|
+
if (isNextPagesApiFile(relFile)) {
|
|
5362
|
+
return [
|
|
5363
|
+
{
|
|
5364
|
+
method: "ALL",
|
|
5365
|
+
pathTemplate: canonicalizeTemplate(nextPagesApiPathTemplate(relFile)),
|
|
5366
|
+
line: 1,
|
|
5367
|
+
framework: "next"
|
|
5368
|
+
}
|
|
5369
|
+
];
|
|
5370
|
+
}
|
|
5371
|
+
return [];
|
|
5372
|
+
}
|
|
5373
|
+
async function addRoutes(graph, services) {
|
|
5374
|
+
const jsParser = makeJsParser2();
|
|
5375
|
+
let nodesAdded = 0;
|
|
5376
|
+
let edgesAdded = 0;
|
|
5377
|
+
for (const service of services) {
|
|
5378
|
+
const deps = {
|
|
5379
|
+
...service.pkg.dependencies ?? {},
|
|
5380
|
+
...service.pkg.devDependencies ?? {}
|
|
5381
|
+
};
|
|
5382
|
+
const hasExpress = deps["express"] !== void 0;
|
|
5383
|
+
const hasFastify = deps["fastify"] !== void 0;
|
|
5384
|
+
const hasNext = deps["next"] !== void 0;
|
|
5385
|
+
if (!hasExpress && !hasFastify && !hasNext) continue;
|
|
5386
|
+
const files = await loadSourceFiles(service.dir);
|
|
5387
|
+
for (const file of files) {
|
|
5388
|
+
if (isTestPath(file.path)) continue;
|
|
5389
|
+
if (!JS_ROUTE_EXTENSIONS.has(import_node_path23.default.extname(file.path))) continue;
|
|
5390
|
+
const relFile = toPosix2(import_node_path23.default.relative(service.dir, file.path));
|
|
5391
|
+
let routes;
|
|
5392
|
+
try {
|
|
5393
|
+
if (hasNext && (isNextAppRouteFile(relFile) || isNextPagesApiFile(relFile))) {
|
|
5394
|
+
routes = nextRoutesFromFile(file.content, relFile, jsParser);
|
|
5395
|
+
} else if (hasExpress || hasFastify) {
|
|
5396
|
+
routes = serverRoutesFromSource(file.content, jsParser, hasExpress, hasFastify);
|
|
5397
|
+
} else {
|
|
5398
|
+
routes = [];
|
|
5399
|
+
}
|
|
5400
|
+
} catch (err) {
|
|
5401
|
+
recordExtractionError("route extraction", file.path, err);
|
|
5402
|
+
continue;
|
|
5403
|
+
}
|
|
5404
|
+
if (routes.length === 0) continue;
|
|
5405
|
+
for (const route of routes) {
|
|
5406
|
+
const rid = (0, import_types11.routeId)(service.pkg.name, route.method, route.pathTemplate);
|
|
5407
|
+
if (!graph.hasNode(rid)) {
|
|
5408
|
+
const node = {
|
|
5409
|
+
id: rid,
|
|
5410
|
+
type: import_types11.NodeType.RouteNode,
|
|
5411
|
+
name: `${route.method} ${route.pathTemplate}`,
|
|
5412
|
+
service: service.pkg.name,
|
|
5413
|
+
method: route.method,
|
|
5414
|
+
pathTemplate: route.pathTemplate,
|
|
5415
|
+
path: relFile,
|
|
5416
|
+
line: route.line,
|
|
5417
|
+
framework: route.framework,
|
|
5418
|
+
discoveredVia: "static"
|
|
5419
|
+
};
|
|
5420
|
+
graph.addNode(rid, node);
|
|
5421
|
+
nodesAdded++;
|
|
5422
|
+
}
|
|
5423
|
+
const containsId = (0, import_types11.extractedEdgeId)(service.node.id, rid, import_types11.EdgeType.CONTAINS);
|
|
5424
|
+
if (!graph.hasEdge(containsId)) {
|
|
5425
|
+
const edge = {
|
|
5426
|
+
id: containsId,
|
|
5427
|
+
source: service.node.id,
|
|
5428
|
+
target: rid,
|
|
5429
|
+
type: import_types11.EdgeType.CONTAINS,
|
|
5430
|
+
provenance: import_types11.Provenance.EXTRACTED,
|
|
5431
|
+
confidence: (0, import_types11.confidenceForExtracted)("structural"),
|
|
5432
|
+
evidence: {
|
|
5433
|
+
file: relFile,
|
|
5434
|
+
line: route.line,
|
|
5435
|
+
snippet: snippet(file.content, route.line)
|
|
5436
|
+
}
|
|
5437
|
+
};
|
|
5438
|
+
graph.addEdgeWithKey(containsId, service.node.id, rid, edge);
|
|
5439
|
+
edgesAdded++;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
return { nodesAdded, edgesAdded };
|
|
5445
|
+
}
|
|
5446
|
+
|
|
4980
5447
|
// src/extract/calls/index.ts
|
|
4981
5448
|
init_cjs_shims();
|
|
4982
|
-
var
|
|
5449
|
+
var import_types19 = require("@neat.is/types");
|
|
4983
5450
|
|
|
4984
5451
|
// src/extract/calls/http.ts
|
|
4985
5452
|
init_cjs_shims();
|
|
4986
|
-
var
|
|
4987
|
-
var
|
|
4988
|
-
var
|
|
5453
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
5454
|
+
var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
5455
|
+
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
4989
5456
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
4990
|
-
var
|
|
5457
|
+
var import_types12 = require("@neat.is/types");
|
|
4991
5458
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
4992
5459
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
4993
5460
|
function isInsideJsxExternalLink(node) {
|
|
@@ -5015,14 +5482,14 @@ function collectStringLiterals(node, out) {
|
|
|
5015
5482
|
if (child) collectStringLiterals(child, out);
|
|
5016
5483
|
}
|
|
5017
5484
|
}
|
|
5018
|
-
var
|
|
5019
|
-
function
|
|
5485
|
+
var PARSE_CHUNK3 = 16384;
|
|
5486
|
+
function parseSource3(parser, source) {
|
|
5020
5487
|
return parser.parse(
|
|
5021
|
-
(index) => index >= source.length ? "" : source.slice(index, index +
|
|
5488
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK3)
|
|
5022
5489
|
);
|
|
5023
5490
|
}
|
|
5024
5491
|
function callsFromSource(source, parser, knownHosts) {
|
|
5025
|
-
const tree =
|
|
5492
|
+
const tree = parseSource3(parser, source);
|
|
5026
5493
|
const literals = [];
|
|
5027
5494
|
collectStringLiterals(tree.rootNode, literals);
|
|
5028
5495
|
const out = [];
|
|
@@ -5036,27 +5503,20 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
5036
5503
|
}
|
|
5037
5504
|
return out;
|
|
5038
5505
|
}
|
|
5039
|
-
function
|
|
5040
|
-
const p = new
|
|
5041
|
-
p.setLanguage(
|
|
5506
|
+
function makeJsParser3() {
|
|
5507
|
+
const p = new import_tree_sitter3.default();
|
|
5508
|
+
p.setLanguage(import_tree_sitter_javascript3.default);
|
|
5042
5509
|
return p;
|
|
5043
5510
|
}
|
|
5044
5511
|
function makePyParser2() {
|
|
5045
|
-
const p = new
|
|
5512
|
+
const p = new import_tree_sitter3.default();
|
|
5046
5513
|
p.setLanguage(import_tree_sitter_python2.default);
|
|
5047
5514
|
return p;
|
|
5048
5515
|
}
|
|
5049
5516
|
async function addHttpCallEdges(graph, services) {
|
|
5050
|
-
const jsParser =
|
|
5517
|
+
const jsParser = makeJsParser3();
|
|
5051
5518
|
const pyParser = makePyParser2();
|
|
5052
|
-
const knownHosts
|
|
5053
|
-
const hostToNodeId = /* @__PURE__ */ new Map();
|
|
5054
|
-
for (const service of services) {
|
|
5055
|
-
knownHosts.add(import_node_path23.default.basename(service.dir));
|
|
5056
|
-
knownHosts.add(service.pkg.name);
|
|
5057
|
-
hostToNodeId.set(import_node_path23.default.basename(service.dir), service.node.id);
|
|
5058
|
-
hostToNodeId.set(service.pkg.name, service.node.id);
|
|
5059
|
-
}
|
|
5519
|
+
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
5060
5520
|
let nodesAdded = 0;
|
|
5061
5521
|
let edgesAdded = 0;
|
|
5062
5522
|
for (const service of services) {
|
|
@@ -5064,7 +5524,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5064
5524
|
const seen = /* @__PURE__ */ new Set();
|
|
5065
5525
|
for (const file of files) {
|
|
5066
5526
|
if (isTestPath(file.path)) continue;
|
|
5067
|
-
const parser =
|
|
5527
|
+
const parser = import_node_path24.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
5068
5528
|
let sites;
|
|
5069
5529
|
try {
|
|
5070
5530
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -5073,14 +5533,14 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5073
5533
|
continue;
|
|
5074
5534
|
}
|
|
5075
5535
|
if (sites.length === 0) continue;
|
|
5076
|
-
const relFile = toPosix2(
|
|
5536
|
+
const relFile = toPosix2(import_node_path24.default.relative(service.dir, file.path));
|
|
5077
5537
|
for (const site of sites) {
|
|
5078
5538
|
const targetId = hostToNodeId.get(site.host);
|
|
5079
5539
|
if (!targetId || targetId === service.node.id) continue;
|
|
5080
5540
|
const dedupKey = `${relFile}|${targetId}`;
|
|
5081
5541
|
if (seen.has(dedupKey)) continue;
|
|
5082
5542
|
seen.add(dedupKey);
|
|
5083
|
-
const confidence = (0,
|
|
5543
|
+
const confidence = (0, import_types12.confidenceForExtracted)("url-literal-service-target");
|
|
5084
5544
|
const ev = {
|
|
5085
5545
|
file: relFile,
|
|
5086
5546
|
line: site.line,
|
|
@@ -5094,25 +5554,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5094
5554
|
);
|
|
5095
5555
|
nodesAdded += n;
|
|
5096
5556
|
edgesAdded += e;
|
|
5097
|
-
if (!(0,
|
|
5557
|
+
if (!(0, import_types12.passesExtractedFloor)(confidence)) {
|
|
5098
5558
|
noteExtractedDropped({
|
|
5099
5559
|
source: fileNodeId,
|
|
5100
5560
|
target: targetId,
|
|
5101
|
-
type:
|
|
5561
|
+
type: import_types12.EdgeType.CALLS,
|
|
5102
5562
|
confidence,
|
|
5103
5563
|
confidenceKind: "url-literal-service-target",
|
|
5104
5564
|
evidence: ev
|
|
5105
5565
|
});
|
|
5106
5566
|
continue;
|
|
5107
5567
|
}
|
|
5108
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId,
|
|
5568
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types12.EdgeType.CALLS);
|
|
5109
5569
|
if (!graph.hasEdge(edgeId)) {
|
|
5110
5570
|
const edge = {
|
|
5111
5571
|
id: edgeId,
|
|
5112
5572
|
source: fileNodeId,
|
|
5113
5573
|
target: targetId,
|
|
5114
|
-
type:
|
|
5115
|
-
provenance:
|
|
5574
|
+
type: import_types12.EdgeType.CALLS,
|
|
5575
|
+
provenance: import_types12.Provenance.EXTRACTED,
|
|
5116
5576
|
confidence,
|
|
5117
5577
|
evidence: ev
|
|
5118
5578
|
};
|
|
@@ -5125,10 +5585,279 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5125
5585
|
return { nodesAdded, edgesAdded };
|
|
5126
5586
|
}
|
|
5127
5587
|
|
|
5588
|
+
// src/extract/calls/route-match.ts
|
|
5589
|
+
init_cjs_shims();
|
|
5590
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
5591
|
+
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
5592
|
+
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5593
|
+
var import_types13 = require("@neat.is/types");
|
|
5594
|
+
var PARSE_CHUNK4 = 16384;
|
|
5595
|
+
function parseSource4(parser, source) {
|
|
5596
|
+
return parser.parse(
|
|
5597
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK4)
|
|
5598
|
+
);
|
|
5599
|
+
}
|
|
5600
|
+
function makeJsParser4() {
|
|
5601
|
+
const p = new import_tree_sitter4.default();
|
|
5602
|
+
p.setLanguage(import_tree_sitter_javascript4.default);
|
|
5603
|
+
return p;
|
|
5604
|
+
}
|
|
5605
|
+
var JS_CLIENT_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
5606
|
+
var AXIOS_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "request"]);
|
|
5607
|
+
function walk2(node, visit) {
|
|
5608
|
+
visit(node);
|
|
5609
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5610
|
+
const child = node.namedChild(i);
|
|
5611
|
+
if (child) walk2(child, visit);
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
function reconstructUrl(node) {
|
|
5615
|
+
if (node.type === "string") {
|
|
5616
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5617
|
+
const child = node.namedChild(i);
|
|
5618
|
+
if (child?.type === "string_fragment") return child.text;
|
|
5619
|
+
}
|
|
5620
|
+
return "";
|
|
5621
|
+
}
|
|
5622
|
+
if (node.type === "template_string") {
|
|
5623
|
+
let out = "";
|
|
5624
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5625
|
+
const child = node.namedChild(i);
|
|
5626
|
+
if (!child) continue;
|
|
5627
|
+
if (child.type === "string_fragment") out += child.text;
|
|
5628
|
+
else if (child.type === "template_substitution") out += ":param";
|
|
5629
|
+
}
|
|
5630
|
+
if (out.length === 0) {
|
|
5631
|
+
const raw = node.text;
|
|
5632
|
+
return raw.length >= 2 ? raw.slice(1, -1) : "";
|
|
5633
|
+
}
|
|
5634
|
+
return out;
|
|
5635
|
+
}
|
|
5636
|
+
return null;
|
|
5637
|
+
}
|
|
5638
|
+
function methodFromOptions(objNode) {
|
|
5639
|
+
for (let i = 0; i < objNode.namedChildCount; i++) {
|
|
5640
|
+
const pair = objNode.namedChild(i);
|
|
5641
|
+
if (!pair || pair.type !== "pair") continue;
|
|
5642
|
+
const k = pair.childForFieldName("key");
|
|
5643
|
+
const kText = k ? k.type === "string" ? stringText(k) : k.text : null;
|
|
5644
|
+
if (kText !== "method") continue;
|
|
5645
|
+
const v = pair.childForFieldName("value");
|
|
5646
|
+
if (v && (v.type === "string" || v.type === "template_string")) {
|
|
5647
|
+
const s = stringText(v);
|
|
5648
|
+
return s ? s.toUpperCase() : void 0;
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
return void 0;
|
|
5652
|
+
}
|
|
5653
|
+
function stringText(node) {
|
|
5654
|
+
if (node.type === "string") {
|
|
5655
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
5656
|
+
const child = node.namedChild(i);
|
|
5657
|
+
if (child?.type === "string_fragment") return child.text;
|
|
5658
|
+
}
|
|
5659
|
+
return "";
|
|
5660
|
+
}
|
|
5661
|
+
return null;
|
|
5662
|
+
}
|
|
5663
|
+
function urlNodeFromConfig(objNode) {
|
|
5664
|
+
for (let i = 0; i < objNode.namedChildCount; i++) {
|
|
5665
|
+
const pair = objNode.namedChild(i);
|
|
5666
|
+
if (!pair || pair.type !== "pair") continue;
|
|
5667
|
+
const k = pair.childForFieldName("key");
|
|
5668
|
+
const kText = k ? k.type === "string" ? stringText(k) : k.text : null;
|
|
5669
|
+
if (kText === "url") return pair.childForFieldName("value");
|
|
5670
|
+
}
|
|
5671
|
+
return null;
|
|
5672
|
+
}
|
|
5673
|
+
function pathOf(urlStr) {
|
|
5674
|
+
try {
|
|
5675
|
+
const candidate = urlStr.startsWith("//") ? `http:${urlStr}` : urlStr;
|
|
5676
|
+
const parsed = new URL(candidate);
|
|
5677
|
+
return parsed.pathname || "/";
|
|
5678
|
+
} catch {
|
|
5679
|
+
return null;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
function matchHost(urlStr, knownHosts) {
|
|
5683
|
+
for (const host of knownHosts) {
|
|
5684
|
+
if (urlMatchesHost(urlStr, host)) return host;
|
|
5685
|
+
}
|
|
5686
|
+
return null;
|
|
5687
|
+
}
|
|
5688
|
+
function clientCallSitesFromSource(source, parser, knownHosts) {
|
|
5689
|
+
const tree = parseSource4(parser, source);
|
|
5690
|
+
const out = [];
|
|
5691
|
+
const push = (urlNode, method, callNode) => {
|
|
5692
|
+
const urlStr = reconstructUrl(urlNode);
|
|
5693
|
+
if (!urlStr) return;
|
|
5694
|
+
const host = matchHost(urlStr, knownHosts);
|
|
5695
|
+
if (!host) return;
|
|
5696
|
+
const p = pathOf(urlStr);
|
|
5697
|
+
if (p === null) return;
|
|
5698
|
+
const line = callNode.startPosition.row + 1;
|
|
5699
|
+
out.push({
|
|
5700
|
+
host,
|
|
5701
|
+
method,
|
|
5702
|
+
pathTemplate: p,
|
|
5703
|
+
line,
|
|
5704
|
+
snippet: snippet(source, line)
|
|
5705
|
+
});
|
|
5706
|
+
};
|
|
5707
|
+
walk2(tree.rootNode, (node) => {
|
|
5708
|
+
if (node.type !== "call_expression") return;
|
|
5709
|
+
const fn = node.childForFieldName("function");
|
|
5710
|
+
if (!fn) return;
|
|
5711
|
+
const args = node.childForFieldName("arguments");
|
|
5712
|
+
const first = args?.namedChild(0);
|
|
5713
|
+
if (!first) return;
|
|
5714
|
+
const fnName = fn.type === "identifier" ? fn.text : fn.type === "member_expression" ? fn.childForFieldName("property")?.text ?? "" : "";
|
|
5715
|
+
if (fn.type === "identifier" && fnName === "fetch") {
|
|
5716
|
+
const opts = args?.namedChild(1);
|
|
5717
|
+
const method = opts && opts.type === "object" ? methodFromOptions(opts) ?? "GET" : "GET";
|
|
5718
|
+
push(first, method, node);
|
|
5719
|
+
return;
|
|
5720
|
+
}
|
|
5721
|
+
if (fn.type === "identifier" && fnName === "axios") {
|
|
5722
|
+
if (first.type === "object") {
|
|
5723
|
+
const urlNode = urlNodeFromConfig(first);
|
|
5724
|
+
if (urlNode) push(urlNode, methodFromOptions(first) ?? "GET", node);
|
|
5725
|
+
} else {
|
|
5726
|
+
const opts = args?.namedChild(1);
|
|
5727
|
+
const method = opts && opts.type === "object" ? methodFromOptions(opts) ?? "GET" : "GET";
|
|
5728
|
+
push(first, method, node);
|
|
5729
|
+
}
|
|
5730
|
+
return;
|
|
5731
|
+
}
|
|
5732
|
+
if (fn.type === "member_expression") {
|
|
5733
|
+
const obj = fn.childForFieldName("object");
|
|
5734
|
+
const objName = obj?.text ?? "";
|
|
5735
|
+
if (objName === "axios" && AXIOS_METHODS.has(fnName)) {
|
|
5736
|
+
if (fnName === "request" && first.type === "object") {
|
|
5737
|
+
const urlNode = urlNodeFromConfig(first);
|
|
5738
|
+
if (urlNode) push(urlNode, methodFromOptions(first) ?? "GET", node);
|
|
5739
|
+
} else {
|
|
5740
|
+
push(first, fnName.toUpperCase(), node);
|
|
5741
|
+
}
|
|
5742
|
+
return;
|
|
5743
|
+
}
|
|
5744
|
+
if ((objName === "http" || objName === "https") && (fnName === "request" || fnName === "get")) {
|
|
5745
|
+
const opts = args?.namedChild(1);
|
|
5746
|
+
const method = opts && opts.type === "object" ? methodFromOptions(opts) ?? (fnName === "get" ? "GET" : "GET") : "GET";
|
|
5747
|
+
push(first, method, node);
|
|
5748
|
+
return;
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
});
|
|
5752
|
+
return out;
|
|
5753
|
+
}
|
|
5754
|
+
function buildRouteIndex(graph) {
|
|
5755
|
+
const index = /* @__PURE__ */ new Map();
|
|
5756
|
+
graph.forEachNode((_id, attrs) => {
|
|
5757
|
+
const node = attrs;
|
|
5758
|
+
if (node.type !== import_types13.NodeType.RouteNode) return;
|
|
5759
|
+
const route = attrs;
|
|
5760
|
+
const owner = (0, import_types13.serviceId)(route.service);
|
|
5761
|
+
const entry2 = {
|
|
5762
|
+
method: route.method.toUpperCase(),
|
|
5763
|
+
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
5764
|
+
routeNodeId: route.id
|
|
5765
|
+
};
|
|
5766
|
+
const list = index.get(owner);
|
|
5767
|
+
if (list) list.push(entry2);
|
|
5768
|
+
else index.set(owner, [entry2]);
|
|
5769
|
+
});
|
|
5770
|
+
return index;
|
|
5771
|
+
}
|
|
5772
|
+
function findRoute(entries, method, normalizedPath) {
|
|
5773
|
+
return entries.find(
|
|
5774
|
+
(e) => e.normalizedPath === normalizedPath && (e.method === "ALL" || method === void 0 || e.method === method)
|
|
5775
|
+
);
|
|
5776
|
+
}
|
|
5777
|
+
async function addRouteCallEdges(graph, services) {
|
|
5778
|
+
const jsParser = makeJsParser4();
|
|
5779
|
+
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
5780
|
+
const routeIndex = buildRouteIndex(graph);
|
|
5781
|
+
if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
5782
|
+
let nodesAdded = 0;
|
|
5783
|
+
let edgesAdded = 0;
|
|
5784
|
+
for (const service of services) {
|
|
5785
|
+
const files = await loadSourceFiles(service.dir);
|
|
5786
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5787
|
+
for (const file of files) {
|
|
5788
|
+
if (isTestPath(file.path)) continue;
|
|
5789
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path25.default.extname(file.path))) continue;
|
|
5790
|
+
let sites;
|
|
5791
|
+
try {
|
|
5792
|
+
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
5793
|
+
} catch (err) {
|
|
5794
|
+
recordExtractionError("route-match call extraction", file.path, err);
|
|
5795
|
+
continue;
|
|
5796
|
+
}
|
|
5797
|
+
if (sites.length === 0) continue;
|
|
5798
|
+
const relFile = toPosix2(import_node_path25.default.relative(service.dir, file.path));
|
|
5799
|
+
for (const site of sites) {
|
|
5800
|
+
const serverServiceId = hostToNodeId.get(site.host);
|
|
5801
|
+
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
5802
|
+
const entries = routeIndex.get(serverServiceId);
|
|
5803
|
+
if (!entries) continue;
|
|
5804
|
+
const normalizedPath = normalizePathTemplate(site.pathTemplate);
|
|
5805
|
+
const match = findRoute(entries, site.method, normalizedPath);
|
|
5806
|
+
if (!match) continue;
|
|
5807
|
+
const dedupKey = `${relFile}|${match.routeNodeId}`;
|
|
5808
|
+
if (seen.has(dedupKey)) continue;
|
|
5809
|
+
seen.add(dedupKey);
|
|
5810
|
+
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
5811
|
+
graph,
|
|
5812
|
+
service.pkg.name,
|
|
5813
|
+
service.node.id,
|
|
5814
|
+
relFile
|
|
5815
|
+
);
|
|
5816
|
+
nodesAdded += n;
|
|
5817
|
+
edgesAdded += e;
|
|
5818
|
+
const confidence = (0, import_types13.confidenceForExtracted)("verified-call-site");
|
|
5819
|
+
const ev = {
|
|
5820
|
+
file: relFile,
|
|
5821
|
+
line: site.line,
|
|
5822
|
+
snippet: site.snippet,
|
|
5823
|
+
method: site.method ?? match.method,
|
|
5824
|
+
pathTemplate: site.pathTemplate
|
|
5825
|
+
};
|
|
5826
|
+
if (!(0, import_types13.passesExtractedFloor)(confidence)) {
|
|
5827
|
+
noteExtractedDropped({
|
|
5828
|
+
source: fileNodeId,
|
|
5829
|
+
target: match.routeNodeId,
|
|
5830
|
+
type: import_types13.EdgeType.CALLS,
|
|
5831
|
+
confidence,
|
|
5832
|
+
confidenceKind: "verified-call-site",
|
|
5833
|
+
evidence: ev
|
|
5834
|
+
});
|
|
5835
|
+
continue;
|
|
5836
|
+
}
|
|
5837
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types13.EdgeType.CALLS);
|
|
5838
|
+
if (!graph.hasEdge(edgeId)) {
|
|
5839
|
+
const edge = {
|
|
5840
|
+
id: edgeId,
|
|
5841
|
+
source: fileNodeId,
|
|
5842
|
+
target: match.routeNodeId,
|
|
5843
|
+
type: import_types13.EdgeType.CALLS,
|
|
5844
|
+
provenance: import_types13.Provenance.EXTRACTED,
|
|
5845
|
+
confidence,
|
|
5846
|
+
evidence: ev
|
|
5847
|
+
};
|
|
5848
|
+
graph.addEdgeWithKey(edgeId, fileNodeId, match.routeNodeId, edge);
|
|
5849
|
+
edgesAdded++;
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
return { nodesAdded, edgesAdded };
|
|
5855
|
+
}
|
|
5856
|
+
|
|
5128
5857
|
// src/extract/calls/kafka.ts
|
|
5129
5858
|
init_cjs_shims();
|
|
5130
|
-
var
|
|
5131
|
-
var
|
|
5859
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
5860
|
+
var import_types14 = require("@neat.is/types");
|
|
5132
5861
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5133
5862
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
5134
5863
|
function findAll(re, text) {
|
|
@@ -5149,7 +5878,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5149
5878
|
seen.add(key);
|
|
5150
5879
|
const line = lineOf(file.content, topic);
|
|
5151
5880
|
out.push({
|
|
5152
|
-
infraId: (0,
|
|
5881
|
+
infraId: (0, import_types14.infraId)("kafka-topic", topic),
|
|
5153
5882
|
name: topic,
|
|
5154
5883
|
kind: "kafka-topic",
|
|
5155
5884
|
edgeType,
|
|
@@ -5158,7 +5887,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5158
5887
|
// tier (ADR-066).
|
|
5159
5888
|
confidenceKind: "verified-call-site",
|
|
5160
5889
|
evidence: {
|
|
5161
|
-
file:
|
|
5890
|
+
file: import_node_path26.default.relative(serviceDir, file.path),
|
|
5162
5891
|
line,
|
|
5163
5892
|
snippet: snippet(file.content, line)
|
|
5164
5893
|
}
|
|
@@ -5171,8 +5900,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5171
5900
|
|
|
5172
5901
|
// src/extract/calls/redis.ts
|
|
5173
5902
|
init_cjs_shims();
|
|
5174
|
-
var
|
|
5175
|
-
var
|
|
5903
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
5904
|
+
var import_types15 = require("@neat.is/types");
|
|
5176
5905
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
5177
5906
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
5178
5907
|
const out = [];
|
|
@@ -5185,7 +5914,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5185
5914
|
seen.add(host);
|
|
5186
5915
|
const line = lineOf(file.content, host);
|
|
5187
5916
|
out.push({
|
|
5188
|
-
infraId: (0,
|
|
5917
|
+
infraId: (0, import_types15.infraId)("redis", host),
|
|
5189
5918
|
name: host,
|
|
5190
5919
|
kind: "redis",
|
|
5191
5920
|
edgeType: "CALLS",
|
|
@@ -5194,7 +5923,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5194
5923
|
// support tier (ADR-066).
|
|
5195
5924
|
confidenceKind: "url-with-structural-support",
|
|
5196
5925
|
evidence: {
|
|
5197
|
-
file:
|
|
5926
|
+
file: import_node_path27.default.relative(serviceDir, file.path),
|
|
5198
5927
|
line,
|
|
5199
5928
|
snippet: snippet(file.content, line)
|
|
5200
5929
|
}
|
|
@@ -5205,8 +5934,8 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5205
5934
|
|
|
5206
5935
|
// src/extract/calls/aws.ts
|
|
5207
5936
|
init_cjs_shims();
|
|
5208
|
-
var
|
|
5209
|
-
var
|
|
5937
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
5938
|
+
var import_types16 = require("@neat.is/types");
|
|
5210
5939
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5211
5940
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5212
5941
|
function hasMarker(text, markers) {
|
|
@@ -5230,7 +5959,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5230
5959
|
seen.add(key);
|
|
5231
5960
|
const line = lineOf(file.content, name);
|
|
5232
5961
|
out.push({
|
|
5233
|
-
infraId: (0,
|
|
5962
|
+
infraId: (0, import_types16.infraId)(kind, name),
|
|
5234
5963
|
name,
|
|
5235
5964
|
kind,
|
|
5236
5965
|
edgeType: "CALLS",
|
|
@@ -5239,7 +5968,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5239
5968
|
// (ADR-066).
|
|
5240
5969
|
confidenceKind: "verified-call-site",
|
|
5241
5970
|
evidence: {
|
|
5242
|
-
file:
|
|
5971
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
5243
5972
|
line,
|
|
5244
5973
|
snippet: snippet(file.content, line)
|
|
5245
5974
|
}
|
|
@@ -5264,8 +5993,8 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5264
5993
|
|
|
5265
5994
|
// src/extract/calls/grpc.ts
|
|
5266
5995
|
init_cjs_shims();
|
|
5267
|
-
var
|
|
5268
|
-
var
|
|
5996
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
5997
|
+
var import_types17 = require("@neat.is/types");
|
|
5269
5998
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
5270
5999
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
5271
6000
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -5314,7 +6043,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
5314
6043
|
const { kind } = classified;
|
|
5315
6044
|
const line = lineOf(file.content, m[0]);
|
|
5316
6045
|
out.push({
|
|
5317
|
-
infraId: (0,
|
|
6046
|
+
infraId: (0, import_types17.infraId)(kind, name),
|
|
5318
6047
|
name,
|
|
5319
6048
|
kind,
|
|
5320
6049
|
edgeType: "CALLS",
|
|
@@ -5323,7 +6052,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
5323
6052
|
// tier (ADR-066).
|
|
5324
6053
|
confidenceKind: "verified-call-site",
|
|
5325
6054
|
evidence: {
|
|
5326
|
-
file:
|
|
6055
|
+
file: import_node_path29.default.relative(serviceDir, file.path),
|
|
5327
6056
|
line,
|
|
5328
6057
|
snippet: snippet(file.content, line)
|
|
5329
6058
|
}
|
|
@@ -5334,8 +6063,8 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
5334
6063
|
|
|
5335
6064
|
// src/extract/calls/supabase.ts
|
|
5336
6065
|
init_cjs_shims();
|
|
5337
|
-
var
|
|
5338
|
-
var
|
|
6066
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
6067
|
+
var import_types18 = require("@neat.is/types");
|
|
5339
6068
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
5340
6069
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
5341
6070
|
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
@@ -5373,7 +6102,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
5373
6102
|
seen.add(name);
|
|
5374
6103
|
const line = lineOf(file.content, m[0]);
|
|
5375
6104
|
out.push({
|
|
5376
|
-
infraId: (0,
|
|
6105
|
+
infraId: (0, import_types18.infraId)("supabase", name),
|
|
5377
6106
|
name,
|
|
5378
6107
|
kind: "supabase",
|
|
5379
6108
|
edgeType: "CALLS",
|
|
@@ -5383,7 +6112,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
5383
6112
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
5384
6113
|
confidenceKind: "verified-call-site",
|
|
5385
6114
|
evidence: {
|
|
5386
|
-
file:
|
|
6115
|
+
file: import_node_path30.default.relative(serviceDir, file.path),
|
|
5387
6116
|
line,
|
|
5388
6117
|
snippet: snippet(file.content, line)
|
|
5389
6118
|
}
|
|
@@ -5396,11 +6125,11 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
5396
6125
|
function edgeTypeFromEndpoint(ep) {
|
|
5397
6126
|
switch (ep.edgeType) {
|
|
5398
6127
|
case "PUBLISHES_TO":
|
|
5399
|
-
return
|
|
6128
|
+
return import_types19.EdgeType.PUBLISHES_TO;
|
|
5400
6129
|
case "CONSUMES_FROM":
|
|
5401
|
-
return
|
|
6130
|
+
return import_types19.EdgeType.CONSUMES_FROM;
|
|
5402
6131
|
default:
|
|
5403
|
-
return
|
|
6132
|
+
return import_types19.EdgeType.CALLS;
|
|
5404
6133
|
}
|
|
5405
6134
|
}
|
|
5406
6135
|
function isAwsKind(kind) {
|
|
@@ -5428,7 +6157,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
5428
6157
|
if (!graph.hasNode(ep.infraId)) {
|
|
5429
6158
|
const node = {
|
|
5430
6159
|
id: ep.infraId,
|
|
5431
|
-
type:
|
|
6160
|
+
type: import_types19.NodeType.InfraNode,
|
|
5432
6161
|
name: ep.name,
|
|
5433
6162
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
5434
6163
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -5440,7 +6169,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
5440
6169
|
nodesAdded++;
|
|
5441
6170
|
}
|
|
5442
6171
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
5443
|
-
const confidence = (0,
|
|
6172
|
+
const confidence = (0, import_types19.confidenceForExtracted)(ep.confidenceKind);
|
|
5444
6173
|
const relFile = toPosix2(ep.evidence.file);
|
|
5445
6174
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
5446
6175
|
graph,
|
|
@@ -5450,7 +6179,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
5450
6179
|
);
|
|
5451
6180
|
nodesAdded += n;
|
|
5452
6181
|
edgesAdded += e;
|
|
5453
|
-
if (!(0,
|
|
6182
|
+
if (!(0, import_types19.passesExtractedFloor)(confidence)) {
|
|
5454
6183
|
noteExtractedDropped({
|
|
5455
6184
|
source: fileNodeId,
|
|
5456
6185
|
target: ep.infraId,
|
|
@@ -5470,7 +6199,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
5470
6199
|
source: fileNodeId,
|
|
5471
6200
|
target: ep.infraId,
|
|
5472
6201
|
type: edgeType,
|
|
5473
|
-
provenance:
|
|
6202
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
5474
6203
|
confidence,
|
|
5475
6204
|
evidence: ep.evidence
|
|
5476
6205
|
};
|
|
@@ -5484,9 +6213,10 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
5484
6213
|
async function addCallEdges(graph, services) {
|
|
5485
6214
|
const http2 = await addHttpCallEdges(graph, services);
|
|
5486
6215
|
const ext = await addExternalEndpointEdges(graph, services);
|
|
6216
|
+
const routes = await addRouteCallEdges(graph, services);
|
|
5487
6217
|
return {
|
|
5488
|
-
nodesAdded: http2.nodesAdded + ext.nodesAdded,
|
|
5489
|
-
edgesAdded: http2.edgesAdded + ext.edgesAdded
|
|
6218
|
+
nodesAdded: http2.nodesAdded + ext.nodesAdded + routes.nodesAdded,
|
|
6219
|
+
edgesAdded: http2.edgesAdded + ext.edgesAdded + routes.edgesAdded
|
|
5490
6220
|
};
|
|
5491
6221
|
}
|
|
5492
6222
|
|
|
@@ -5495,16 +6225,16 @@ init_cjs_shims();
|
|
|
5495
6225
|
|
|
5496
6226
|
// src/extract/infra/docker-compose.ts
|
|
5497
6227
|
init_cjs_shims();
|
|
5498
|
-
var
|
|
5499
|
-
var
|
|
6228
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6229
|
+
var import_types21 = require("@neat.is/types");
|
|
5500
6230
|
|
|
5501
6231
|
// src/extract/infra/shared.ts
|
|
5502
6232
|
init_cjs_shims();
|
|
5503
|
-
var
|
|
6233
|
+
var import_types20 = require("@neat.is/types");
|
|
5504
6234
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
5505
6235
|
return {
|
|
5506
|
-
id: (0,
|
|
5507
|
-
type:
|
|
6236
|
+
id: (0, import_types20.infraId)(kind, name),
|
|
6237
|
+
type: import_types20.NodeType.InfraNode,
|
|
5508
6238
|
name,
|
|
5509
6239
|
provider,
|
|
5510
6240
|
kind,
|
|
@@ -5533,7 +6263,7 @@ function dependsOnList(value) {
|
|
|
5533
6263
|
}
|
|
5534
6264
|
function serviceNameToServiceNode(name, services) {
|
|
5535
6265
|
for (const s of services) {
|
|
5536
|
-
if (s.node.name === name ||
|
|
6266
|
+
if (s.node.name === name || import_node_path31.default.basename(s.dir) === name) return s.node.id;
|
|
5537
6267
|
}
|
|
5538
6268
|
return null;
|
|
5539
6269
|
}
|
|
@@ -5542,7 +6272,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
5542
6272
|
let edgesAdded = 0;
|
|
5543
6273
|
let composePath = null;
|
|
5544
6274
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
5545
|
-
const abs =
|
|
6275
|
+
const abs = import_node_path31.default.join(scanPath, name);
|
|
5546
6276
|
if (await exists(abs)) {
|
|
5547
6277
|
composePath = abs;
|
|
5548
6278
|
break;
|
|
@@ -5555,13 +6285,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
5555
6285
|
} catch (err) {
|
|
5556
6286
|
recordExtractionError(
|
|
5557
6287
|
"infra docker-compose",
|
|
5558
|
-
|
|
6288
|
+
import_node_path31.default.relative(scanPath, composePath),
|
|
5559
6289
|
err
|
|
5560
6290
|
);
|
|
5561
6291
|
return { nodesAdded, edgesAdded };
|
|
5562
6292
|
}
|
|
5563
6293
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
5564
|
-
const evidenceFile =
|
|
6294
|
+
const evidenceFile = import_node_path31.default.relative(scanPath, composePath).split(import_node_path31.default.sep).join("/");
|
|
5565
6295
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
5566
6296
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
5567
6297
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -5583,15 +6313,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
5583
6313
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
5584
6314
|
const targetId = composeNameToNodeId.get(dep);
|
|
5585
6315
|
if (!targetId) continue;
|
|
5586
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
6316
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types21.EdgeType.DEPENDS_ON);
|
|
5587
6317
|
if (graph.hasEdge(edgeId)) continue;
|
|
5588
6318
|
const edge = {
|
|
5589
6319
|
id: edgeId,
|
|
5590
6320
|
source: sourceId,
|
|
5591
6321
|
target: targetId,
|
|
5592
|
-
type:
|
|
5593
|
-
provenance:
|
|
5594
|
-
confidence: (0,
|
|
6322
|
+
type: import_types21.EdgeType.DEPENDS_ON,
|
|
6323
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6324
|
+
confidence: (0, import_types21.confidenceForExtracted)("structural"),
|
|
5595
6325
|
evidence: { file: evidenceFile }
|
|
5596
6326
|
};
|
|
5597
6327
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -5603,9 +6333,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
5603
6333
|
|
|
5604
6334
|
// src/extract/infra/dockerfile.ts
|
|
5605
6335
|
init_cjs_shims();
|
|
5606
|
-
var
|
|
6336
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
5607
6337
|
var import_node_fs16 = require("fs");
|
|
5608
|
-
var
|
|
6338
|
+
var import_types22 = require("@neat.is/types");
|
|
5609
6339
|
function readDockerfile(content) {
|
|
5610
6340
|
let image = null;
|
|
5611
6341
|
const ports = [];
|
|
@@ -5634,7 +6364,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
5634
6364
|
let nodesAdded = 0;
|
|
5635
6365
|
let edgesAdded = 0;
|
|
5636
6366
|
for (const service of services) {
|
|
5637
|
-
const dockerfilePath =
|
|
6367
|
+
const dockerfilePath = import_node_path32.default.join(service.dir, "Dockerfile");
|
|
5638
6368
|
if (!await exists(dockerfilePath)) continue;
|
|
5639
6369
|
let content;
|
|
5640
6370
|
try {
|
|
@@ -5642,7 +6372,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
5642
6372
|
} catch (err) {
|
|
5643
6373
|
recordExtractionError(
|
|
5644
6374
|
"infra dockerfile",
|
|
5645
|
-
|
|
6375
|
+
import_node_path32.default.relative(scanPath, dockerfilePath),
|
|
5646
6376
|
err
|
|
5647
6377
|
);
|
|
5648
6378
|
continue;
|
|
@@ -5654,8 +6384,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
5654
6384
|
graph.addNode(node.id, node);
|
|
5655
6385
|
nodesAdded++;
|
|
5656
6386
|
}
|
|
5657
|
-
const relDockerfile = toPosix2(
|
|
5658
|
-
const evidenceFile = toPosix2(
|
|
6387
|
+
const relDockerfile = toPosix2(import_node_path32.default.relative(service.dir, dockerfilePath));
|
|
6388
|
+
const evidenceFile = toPosix2(import_node_path32.default.relative(scanPath, dockerfilePath));
|
|
5659
6389
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
5660
6390
|
graph,
|
|
5661
6391
|
service.pkg.name,
|
|
@@ -5664,15 +6394,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
5664
6394
|
);
|
|
5665
6395
|
nodesAdded += fn;
|
|
5666
6396
|
edgesAdded += fe;
|
|
5667
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
6397
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types22.EdgeType.RUNS_ON);
|
|
5668
6398
|
if (!graph.hasEdge(edgeId)) {
|
|
5669
6399
|
const edge = {
|
|
5670
6400
|
id: edgeId,
|
|
5671
6401
|
source: fileNodeId,
|
|
5672
6402
|
target: node.id,
|
|
5673
|
-
type:
|
|
5674
|
-
provenance:
|
|
5675
|
-
confidence: (0,
|
|
6403
|
+
type: import_types22.EdgeType.RUNS_ON,
|
|
6404
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6405
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
5676
6406
|
evidence: {
|
|
5677
6407
|
file: evidenceFile,
|
|
5678
6408
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -5687,15 +6417,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
5687
6417
|
graph.addNode(portNode.id, portNode);
|
|
5688
6418
|
nodesAdded++;
|
|
5689
6419
|
}
|
|
5690
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
6420
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types22.EdgeType.CONNECTS_TO);
|
|
5691
6421
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
5692
6422
|
const portEdge = {
|
|
5693
6423
|
id: portEdgeId,
|
|
5694
6424
|
source: fileNodeId,
|
|
5695
6425
|
target: portNode.id,
|
|
5696
|
-
type:
|
|
5697
|
-
provenance:
|
|
5698
|
-
confidence: (0,
|
|
6426
|
+
type: import_types22.EdgeType.CONNECTS_TO,
|
|
6427
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6428
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
5699
6429
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
5700
6430
|
};
|
|
5701
6431
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -5708,8 +6438,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
5708
6438
|
// src/extract/infra/terraform.ts
|
|
5709
6439
|
init_cjs_shims();
|
|
5710
6440
|
var import_node_fs17 = require("fs");
|
|
5711
|
-
var
|
|
5712
|
-
var
|
|
6441
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6442
|
+
var import_types23 = require("@neat.is/types");
|
|
5713
6443
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
5714
6444
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
5715
6445
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -5719,11 +6449,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
5719
6449
|
for (const entry2 of entries) {
|
|
5720
6450
|
if (entry2.isDirectory()) {
|
|
5721
6451
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
5722
|
-
const child =
|
|
6452
|
+
const child = import_node_path33.default.join(start, entry2.name);
|
|
5723
6453
|
if (await isPythonVenvDir(child)) continue;
|
|
5724
6454
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
5725
6455
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
5726
|
-
out.push(
|
|
6456
|
+
out.push(import_node_path33.default.join(start, entry2.name));
|
|
5727
6457
|
}
|
|
5728
6458
|
}
|
|
5729
6459
|
return out;
|
|
@@ -5755,7 +6485,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
5755
6485
|
const files = await walkTfFiles(scanPath);
|
|
5756
6486
|
for (const file of files) {
|
|
5757
6487
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
5758
|
-
const evidenceFile = toPosix2(
|
|
6488
|
+
const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, file));
|
|
5759
6489
|
const resources = [];
|
|
5760
6490
|
const byKey = /* @__PURE__ */ new Map();
|
|
5761
6491
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -5790,16 +6520,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
5790
6520
|
if (!target) continue;
|
|
5791
6521
|
if (seen.has(target.nodeId)) continue;
|
|
5792
6522
|
seen.add(target.nodeId);
|
|
5793
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
6523
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types23.EdgeType.DEPENDS_ON);
|
|
5794
6524
|
if (graph.hasEdge(edgeId)) continue;
|
|
5795
6525
|
const line = lineAt(content, resource.bodyOffset + ref.index);
|
|
5796
6526
|
const edge = {
|
|
5797
6527
|
id: edgeId,
|
|
5798
6528
|
source: resource.nodeId,
|
|
5799
6529
|
target: target.nodeId,
|
|
5800
|
-
type:
|
|
5801
|
-
provenance:
|
|
5802
|
-
confidence: (0,
|
|
6530
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
6531
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6532
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
5803
6533
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
5804
6534
|
};
|
|
5805
6535
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -5813,7 +6543,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
5813
6543
|
// src/extract/infra/k8s.ts
|
|
5814
6544
|
init_cjs_shims();
|
|
5815
6545
|
var import_node_fs18 = require("fs");
|
|
5816
|
-
var
|
|
6546
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5817
6547
|
var import_yaml3 = require("yaml");
|
|
5818
6548
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
5819
6549
|
Service: "k8s-service",
|
|
@@ -5831,11 +6561,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
5831
6561
|
for (const entry2 of entries) {
|
|
5832
6562
|
if (entry2.isDirectory()) {
|
|
5833
6563
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
5834
|
-
const child =
|
|
6564
|
+
const child = import_node_path34.default.join(start, entry2.name);
|
|
5835
6565
|
if (await isPythonVenvDir(child)) continue;
|
|
5836
6566
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
5837
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
5838
|
-
out.push(
|
|
6567
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path34.default.extname(entry2.name))) {
|
|
6568
|
+
out.push(import_node_path34.default.join(start, entry2.name));
|
|
5839
6569
|
}
|
|
5840
6570
|
}
|
|
5841
6571
|
return out;
|
|
@@ -5879,17 +6609,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
5879
6609
|
}
|
|
5880
6610
|
|
|
5881
6611
|
// src/extract/index.ts
|
|
5882
|
-
var
|
|
6612
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5883
6613
|
|
|
5884
6614
|
// src/extract/retire.ts
|
|
5885
6615
|
init_cjs_shims();
|
|
5886
6616
|
var import_node_fs19 = require("fs");
|
|
5887
|
-
var
|
|
5888
|
-
var
|
|
6617
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6618
|
+
var import_types24 = require("@neat.is/types");
|
|
5889
6619
|
function dropOrphanedFileNodes(graph) {
|
|
5890
6620
|
const orphans = [];
|
|
5891
6621
|
graph.forEachNode((id, attrs) => {
|
|
5892
|
-
if (attrs.type !==
|
|
6622
|
+
if (attrs.type !== import_types24.NodeType.FileNode) return;
|
|
5893
6623
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
5894
6624
|
orphans.push(id);
|
|
5895
6625
|
}
|
|
@@ -5902,7 +6632,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
5902
6632
|
const toDrop = [];
|
|
5903
6633
|
graph.forEachEdge((id, attrs) => {
|
|
5904
6634
|
const edge = attrs;
|
|
5905
|
-
if (edge.provenance !==
|
|
6635
|
+
if (edge.provenance !== import_types24.Provenance.EXTRACTED) return;
|
|
5906
6636
|
if (!edge.evidence?.file) return;
|
|
5907
6637
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
5908
6638
|
});
|
|
@@ -5915,14 +6645,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
5915
6645
|
const bases = [scanPath, ...serviceDirs];
|
|
5916
6646
|
graph.forEachEdge((id, attrs) => {
|
|
5917
6647
|
const edge = attrs;
|
|
5918
|
-
if (edge.provenance !==
|
|
6648
|
+
if (edge.provenance !== import_types24.Provenance.EXTRACTED) return;
|
|
5919
6649
|
const evidenceFile = edge.evidence?.file;
|
|
5920
6650
|
if (!evidenceFile) return;
|
|
5921
|
-
if (
|
|
6651
|
+
if (import_node_path35.default.isAbsolute(evidenceFile)) {
|
|
5922
6652
|
if (!(0, import_node_fs19.existsSync)(evidenceFile)) toDrop.push(id);
|
|
5923
6653
|
return;
|
|
5924
6654
|
}
|
|
5925
|
-
const found = bases.some((base) => (0, import_node_fs19.existsSync)(
|
|
6655
|
+
const found = bases.some((base) => (0, import_node_fs19.existsSync)(import_node_path35.default.join(base, evidenceFile)));
|
|
5926
6656
|
if (!found) toDrop.push(id);
|
|
5927
6657
|
});
|
|
5928
6658
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -5941,6 +6671,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5941
6671
|
const importGraph = await addImports(graph, services);
|
|
5942
6672
|
const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
|
|
5943
6673
|
const phase3 = await addConfigNodes(graph, services, scanPath);
|
|
6674
|
+
const routePhase = await addRoutes(graph, services);
|
|
5944
6675
|
const phase4 = await addCallEdges(graph, services);
|
|
5945
6676
|
const phase5 = await addInfra(graph, scanPath, services);
|
|
5946
6677
|
const ghostsRetired = retireExtractedEdgesByMissingFile(
|
|
@@ -5962,7 +6693,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5962
6693
|
}
|
|
5963
6694
|
const droppedEntries = drainDroppedExtracted();
|
|
5964
6695
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
5965
|
-
const rejectedPath =
|
|
6696
|
+
const rejectedPath = import_node_path36.default.join(import_node_path36.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
5966
6697
|
try {
|
|
5967
6698
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
5968
6699
|
} catch (err) {
|
|
@@ -5972,8 +6703,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5972
6703
|
}
|
|
5973
6704
|
}
|
|
5974
6705
|
const result = {
|
|
5975
|
-
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
5976
|
-
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
6706
|
+
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
6707
|
+
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
5977
6708
|
frontiersPromoted,
|
|
5978
6709
|
extractionErrors: errorEntries.length,
|
|
5979
6710
|
errorEntries,
|
|
@@ -5996,7 +6727,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5996
6727
|
|
|
5997
6728
|
// src/divergences.ts
|
|
5998
6729
|
init_cjs_shims();
|
|
5999
|
-
var
|
|
6730
|
+
var import_types25 = require("@neat.is/types");
|
|
6000
6731
|
function bucketKey(source, target, type) {
|
|
6001
6732
|
return `${type}|${source}|${target}`;
|
|
6002
6733
|
}
|
|
@@ -6004,22 +6735,22 @@ function bucketEdges(graph) {
|
|
|
6004
6735
|
const buckets = /* @__PURE__ */ new Map();
|
|
6005
6736
|
graph.forEachEdge((id, attrs) => {
|
|
6006
6737
|
const e = attrs;
|
|
6007
|
-
const parsed = (0,
|
|
6738
|
+
const parsed = (0, import_types25.parseEdgeId)(id);
|
|
6008
6739
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
6009
6740
|
const key = bucketKey(e.source, e.target, e.type);
|
|
6010
6741
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
6011
6742
|
switch (provenance) {
|
|
6012
|
-
case
|
|
6743
|
+
case import_types25.Provenance.EXTRACTED:
|
|
6013
6744
|
cur.extracted = e;
|
|
6014
6745
|
break;
|
|
6015
|
-
case
|
|
6746
|
+
case import_types25.Provenance.OBSERVED:
|
|
6016
6747
|
cur.observed = e;
|
|
6017
6748
|
break;
|
|
6018
|
-
case
|
|
6749
|
+
case import_types25.Provenance.INFERRED:
|
|
6019
6750
|
cur.inferred = e;
|
|
6020
6751
|
break;
|
|
6021
6752
|
default:
|
|
6022
|
-
if (e.provenance ===
|
|
6753
|
+
if (e.provenance === import_types25.Provenance.STALE) cur.stale = e;
|
|
6023
6754
|
}
|
|
6024
6755
|
buckets.set(key, cur);
|
|
6025
6756
|
});
|
|
@@ -6028,7 +6759,7 @@ function bucketEdges(graph) {
|
|
|
6028
6759
|
function nodeIsFrontier(graph, nodeId) {
|
|
6029
6760
|
if (!graph.hasNode(nodeId)) return false;
|
|
6030
6761
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
6031
|
-
return attrs.type ===
|
|
6762
|
+
return attrs.type === import_types25.NodeType.FrontierNode;
|
|
6032
6763
|
}
|
|
6033
6764
|
function clampConfidence(n) {
|
|
6034
6765
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -6048,14 +6779,14 @@ function gradedConfidence(edge) {
|
|
|
6048
6779
|
return clampConfidence(confidenceForEdge(edge));
|
|
6049
6780
|
}
|
|
6050
6781
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6782
|
+
import_types25.EdgeType.CALLS,
|
|
6783
|
+
import_types25.EdgeType.CONNECTS_TO,
|
|
6784
|
+
import_types25.EdgeType.PUBLISHES_TO,
|
|
6785
|
+
import_types25.EdgeType.CONSUMES_FROM
|
|
6055
6786
|
]);
|
|
6056
6787
|
function detectMissingDivergences(graph, bucket) {
|
|
6057
6788
|
const out = [];
|
|
6058
|
-
if (bucket.type ===
|
|
6789
|
+
if (bucket.type === import_types25.EdgeType.CONTAINS) return out;
|
|
6059
6790
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
6060
6791
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
6061
6792
|
out.push({
|
|
@@ -6096,7 +6827,7 @@ function declaredHostFor(svc) {
|
|
|
6096
6827
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
6097
6828
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
6098
6829
|
const e = graph.getEdgeAttributes(edgeId);
|
|
6099
|
-
if (e.type ===
|
|
6830
|
+
if (e.type === import_types25.EdgeType.CONFIGURED_BY && e.provenance === import_types25.Provenance.EXTRACTED) {
|
|
6100
6831
|
return true;
|
|
6101
6832
|
}
|
|
6102
6833
|
}
|
|
@@ -6109,10 +6840,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
6109
6840
|
const out = [];
|
|
6110
6841
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
6111
6842
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6112
|
-
if (edge.type !==
|
|
6113
|
-
if (edge.provenance !==
|
|
6843
|
+
if (edge.type !== import_types25.EdgeType.CONNECTS_TO) continue;
|
|
6844
|
+
if (edge.provenance !== import_types25.Provenance.OBSERVED) continue;
|
|
6114
6845
|
const target = graph.getNodeAttributes(edge.target);
|
|
6115
|
-
if (target.type !==
|
|
6846
|
+
if (target.type !== import_types25.NodeType.DatabaseNode) continue;
|
|
6116
6847
|
const observedHost = target.host?.trim();
|
|
6117
6848
|
if (!observedHost) continue;
|
|
6118
6849
|
if (observedHost === declaredHost) continue;
|
|
@@ -6134,10 +6865,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
6134
6865
|
const deps = svc.dependencies ?? {};
|
|
6135
6866
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
6136
6867
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6137
|
-
if (edge.type !==
|
|
6138
|
-
if (edge.provenance !==
|
|
6868
|
+
if (edge.type !== import_types25.EdgeType.CONNECTS_TO) continue;
|
|
6869
|
+
if (edge.provenance !== import_types25.Provenance.OBSERVED) continue;
|
|
6139
6870
|
const target = graph.getNodeAttributes(edge.target);
|
|
6140
|
-
if (target.type !==
|
|
6871
|
+
if (target.type !== import_types25.NodeType.DatabaseNode) continue;
|
|
6141
6872
|
for (const pair of compatPairs()) {
|
|
6142
6873
|
if (pair.engine !== target.engine) continue;
|
|
6143
6874
|
const declared = deps[pair.driver];
|
|
@@ -6196,7 +6927,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
6196
6927
|
for (const d of all) {
|
|
6197
6928
|
if (d.type !== "host-mismatch") continue;
|
|
6198
6929
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
6199
|
-
declaredHalf.add((0,
|
|
6930
|
+
declaredHalf.add((0, import_types25.databaseId)(d.extractedHost));
|
|
6200
6931
|
}
|
|
6201
6932
|
if (observedHalf.size === 0) return all;
|
|
6202
6933
|
return all.filter((d) => {
|
|
@@ -6215,7 +6946,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
6215
6946
|
}
|
|
6216
6947
|
graph.forEachNode((nodeId, attrs) => {
|
|
6217
6948
|
const n = attrs;
|
|
6218
|
-
if (n.type !==
|
|
6949
|
+
if (n.type !== import_types25.NodeType.ServiceNode) return;
|
|
6219
6950
|
const svc = n;
|
|
6220
6951
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
6221
6952
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -6249,7 +6980,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
6249
6980
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
6250
6981
|
return a.target.localeCompare(b.target);
|
|
6251
6982
|
});
|
|
6252
|
-
return
|
|
6983
|
+
return import_types25.DivergenceResultSchema.parse({
|
|
6253
6984
|
divergences: filtered,
|
|
6254
6985
|
totalAffected: filtered.length,
|
|
6255
6986
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6259,8 +6990,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
6259
6990
|
// src/persist.ts
|
|
6260
6991
|
init_cjs_shims();
|
|
6261
6992
|
var import_node_fs20 = require("fs");
|
|
6262
|
-
var
|
|
6263
|
-
var
|
|
6993
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
6994
|
+
var import_types26 = require("@neat.is/types");
|
|
6264
6995
|
var SCHEMA_VERSION = 4;
|
|
6265
6996
|
function migrateV1ToV2(payload) {
|
|
6266
6997
|
const nodes = payload.graph.nodes;
|
|
@@ -6282,12 +7013,12 @@ function migrateV2ToV3(payload) {
|
|
|
6282
7013
|
for (const edge of edges) {
|
|
6283
7014
|
const attrs = edge.attributes;
|
|
6284
7015
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
6285
|
-
attrs.provenance =
|
|
7016
|
+
attrs.provenance = import_types26.Provenance.OBSERVED;
|
|
6286
7017
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
6287
7018
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
6288
7019
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
6289
7020
|
if (type && source && target) {
|
|
6290
|
-
const newId = (0,
|
|
7021
|
+
const newId = (0, import_types26.observedEdgeId)(source, target, type);
|
|
6291
7022
|
attrs.id = newId;
|
|
6292
7023
|
if (edge.key) edge.key = newId;
|
|
6293
7024
|
}
|
|
@@ -6296,7 +7027,7 @@ function migrateV2ToV3(payload) {
|
|
|
6296
7027
|
return { ...payload, schemaVersion: 3 };
|
|
6297
7028
|
}
|
|
6298
7029
|
async function ensureDir(filePath) {
|
|
6299
|
-
await import_node_fs20.promises.mkdir(
|
|
7030
|
+
await import_node_fs20.promises.mkdir(import_node_path37.default.dirname(filePath), { recursive: true });
|
|
6300
7031
|
}
|
|
6301
7032
|
async function saveGraphToDisk(graph, outPath) {
|
|
6302
7033
|
await ensureDir(outPath);
|
|
@@ -6378,7 +7109,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
6378
7109
|
// src/gitignore.ts
|
|
6379
7110
|
init_cjs_shims();
|
|
6380
7111
|
var import_node_fs21 = require("fs");
|
|
6381
|
-
var
|
|
7112
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
6382
7113
|
var NEAT_OUT_LINE = "neat-out/";
|
|
6383
7114
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
6384
7115
|
function isNeatOutLine(line) {
|
|
@@ -6386,7 +7117,7 @@ function isNeatOutLine(line) {
|
|
|
6386
7117
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
6387
7118
|
}
|
|
6388
7119
|
async function ensureNeatOutIgnored(projectDir) {
|
|
6389
|
-
const file =
|
|
7120
|
+
const file = import_node_path38.default.join(projectDir, ".gitignore");
|
|
6390
7121
|
let existing = null;
|
|
6391
7122
|
try {
|
|
6392
7123
|
existing = await import_node_fs21.promises.readFile(file, "utf8");
|
|
@@ -6413,7 +7144,7 @@ ${NEAT_OUT_LINE}
|
|
|
6413
7144
|
|
|
6414
7145
|
// src/summary.ts
|
|
6415
7146
|
init_cjs_shims();
|
|
6416
|
-
var
|
|
7147
|
+
var import_types27 = require("@neat.is/types");
|
|
6417
7148
|
function renderOtelEnvBlock() {
|
|
6418
7149
|
return [
|
|
6419
7150
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -6423,19 +7154,19 @@ function renderOtelEnvBlock() {
|
|
|
6423
7154
|
}
|
|
6424
7155
|
function findIncompatServices(nodes) {
|
|
6425
7156
|
return nodes.filter(
|
|
6426
|
-
(n) => n.type ===
|
|
7157
|
+
(n) => n.type === import_types27.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
6427
7158
|
);
|
|
6428
7159
|
}
|
|
6429
7160
|
function servicesWithoutObserved(nodes, edges) {
|
|
6430
7161
|
const seen = /* @__PURE__ */ new Set();
|
|
6431
7162
|
for (const e of edges) {
|
|
6432
|
-
if (e.provenance ===
|
|
7163
|
+
if (e.provenance === import_types27.Provenance.OBSERVED) {
|
|
6433
7164
|
seen.add(e.source);
|
|
6434
7165
|
seen.add(e.target);
|
|
6435
7166
|
}
|
|
6436
7167
|
}
|
|
6437
7168
|
return nodes.filter(
|
|
6438
|
-
(n) => n.type ===
|
|
7169
|
+
(n) => n.type === import_types27.NodeType.ServiceNode && !seen.has(n.id)
|
|
6439
7170
|
);
|
|
6440
7171
|
}
|
|
6441
7172
|
function formatDivergence(d) {
|
|
@@ -6510,26 +7241,26 @@ function formatIncompat(inc) {
|
|
|
6510
7241
|
// src/watch.ts
|
|
6511
7242
|
init_cjs_shims();
|
|
6512
7243
|
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
6513
|
-
var
|
|
7244
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
6514
7245
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
6515
7246
|
|
|
6516
7247
|
// src/api.ts
|
|
6517
7248
|
init_cjs_shims();
|
|
6518
7249
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
6519
7250
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
6520
|
-
var
|
|
7251
|
+
var import_types29 = require("@neat.is/types");
|
|
6521
7252
|
|
|
6522
7253
|
// src/extend/index.ts
|
|
6523
7254
|
init_cjs_shims();
|
|
6524
7255
|
var import_node_fs23 = require("fs");
|
|
6525
|
-
var
|
|
7256
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
6526
7257
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
6527
7258
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
6528
7259
|
|
|
6529
7260
|
// src/installers/package-manager.ts
|
|
6530
7261
|
init_cjs_shims();
|
|
6531
7262
|
var import_node_fs22 = require("fs");
|
|
6532
|
-
var
|
|
7263
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
6533
7264
|
var import_node_child_process = require("child_process");
|
|
6534
7265
|
var LOCKFILE_PRIORITY = [
|
|
6535
7266
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -6551,22 +7282,22 @@ async function exists2(p) {
|
|
|
6551
7282
|
}
|
|
6552
7283
|
}
|
|
6553
7284
|
async function detectPackageManager(serviceDir) {
|
|
6554
|
-
let dir =
|
|
7285
|
+
let dir = import_node_path39.default.resolve(serviceDir);
|
|
6555
7286
|
const stops = /* @__PURE__ */ new Set();
|
|
6556
7287
|
for (let i = 0; i < 64; i++) {
|
|
6557
7288
|
if (stops.has(dir)) break;
|
|
6558
7289
|
stops.add(dir);
|
|
6559
7290
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
6560
|
-
const lockPath =
|
|
7291
|
+
const lockPath = import_node_path39.default.join(dir, candidate.lockfile);
|
|
6561
7292
|
if (await exists2(lockPath)) {
|
|
6562
7293
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
6563
7294
|
}
|
|
6564
7295
|
}
|
|
6565
|
-
const parent =
|
|
7296
|
+
const parent = import_node_path39.default.dirname(dir);
|
|
6566
7297
|
if (parent === dir) break;
|
|
6567
7298
|
dir = parent;
|
|
6568
7299
|
}
|
|
6569
|
-
return { pm: "npm", cwd:
|
|
7300
|
+
return { pm: "npm", cwd: import_node_path39.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
6570
7301
|
}
|
|
6571
7302
|
async function runPackageManagerInstall(cmd) {
|
|
6572
7303
|
return new Promise((resolve) => {
|
|
@@ -6615,7 +7346,7 @@ async function fileExists2(p) {
|
|
|
6615
7346
|
}
|
|
6616
7347
|
}
|
|
6617
7348
|
async function readPackageJson(scanPath) {
|
|
6618
|
-
const pkgPath =
|
|
7349
|
+
const pkgPath = import_node_path40.default.join(scanPath, "package.json");
|
|
6619
7350
|
const raw = await import_node_fs23.promises.readFile(pkgPath, "utf8");
|
|
6620
7351
|
return JSON.parse(raw);
|
|
6621
7352
|
}
|
|
@@ -6626,11 +7357,11 @@ async function findHookFiles(scanPath) {
|
|
|
6626
7357
|
).sort();
|
|
6627
7358
|
}
|
|
6628
7359
|
function extendLogPath() {
|
|
6629
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
7360
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path40.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
6630
7361
|
}
|
|
6631
7362
|
async function appendExtendLog(entry2) {
|
|
6632
7363
|
const logPath = extendLogPath();
|
|
6633
|
-
await import_node_fs23.promises.mkdir(
|
|
7364
|
+
await import_node_fs23.promises.mkdir(import_node_path40.default.dirname(logPath), { recursive: true });
|
|
6634
7365
|
await import_node_fs23.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
6635
7366
|
}
|
|
6636
7367
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -6689,7 +7420,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
6689
7420
|
}
|
|
6690
7421
|
async function describeProjectInstrumentation(ctx) {
|
|
6691
7422
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
6692
|
-
const envNeat = await fileExists2(
|
|
7423
|
+
const envNeat = await fileExists2(import_node_path40.default.join(ctx.scanPath, ".env.neat"));
|
|
6693
7424
|
const registryInstrPackages = new Set(
|
|
6694
7425
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
6695
7426
|
);
|
|
@@ -6711,16 +7442,16 @@ async function applyExtension(ctx, args, options) {
|
|
|
6711
7442
|
);
|
|
6712
7443
|
}
|
|
6713
7444
|
for (const file of hookFiles) {
|
|
6714
|
-
const content = await import_node_fs23.promises.readFile(
|
|
7445
|
+
const content = await import_node_fs23.promises.readFile(import_node_path40.default.join(ctx.scanPath, file), "utf8");
|
|
6715
7446
|
if (content.includes(args.registration_snippet)) {
|
|
6716
7447
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
6717
7448
|
}
|
|
6718
7449
|
}
|
|
6719
7450
|
const primaryFile = hookFiles[0];
|
|
6720
|
-
const primaryPath =
|
|
7451
|
+
const primaryPath = import_node_path40.default.join(ctx.scanPath, primaryFile);
|
|
6721
7452
|
const filesTouched = [];
|
|
6722
7453
|
const depsAdded = [];
|
|
6723
|
-
const pkgPath =
|
|
7454
|
+
const pkgPath = import_node_path40.default.join(ctx.scanPath, "package.json");
|
|
6724
7455
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
6725
7456
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
6726
7457
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -6766,7 +7497,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
6766
7497
|
};
|
|
6767
7498
|
}
|
|
6768
7499
|
for (const file of hookFiles) {
|
|
6769
|
-
const content = await import_node_fs23.promises.readFile(
|
|
7500
|
+
const content = await import_node_fs23.promises.readFile(import_node_path40.default.join(ctx.scanPath, file), "utf8");
|
|
6770
7501
|
if (content.includes(args.registration_snippet)) {
|
|
6771
7502
|
return {
|
|
6772
7503
|
library: args.library,
|
|
@@ -6788,7 +7519,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
6788
7519
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
6789
7520
|
filesTouched.push("package.json");
|
|
6790
7521
|
}
|
|
6791
|
-
const hookContent = await import_node_fs23.promises.readFile(
|
|
7522
|
+
const hookContent = await import_node_fs23.promises.readFile(import_node_path40.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
6792
7523
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
6793
7524
|
if (patched) {
|
|
6794
7525
|
filesTouched.push(primaryFile);
|
|
@@ -6809,7 +7540,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
6809
7540
|
if (!match) {
|
|
6810
7541
|
return { undone: false, message: "no apply found for library" };
|
|
6811
7542
|
}
|
|
6812
|
-
const pkgPath =
|
|
7543
|
+
const pkgPath = import_node_path40.default.join(ctx.scanPath, "package.json");
|
|
6813
7544
|
if (await fileExists2(pkgPath)) {
|
|
6814
7545
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
6815
7546
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -6820,7 +7551,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
6820
7551
|
}
|
|
6821
7552
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
6822
7553
|
for (const file of hookFiles) {
|
|
6823
|
-
const filePath =
|
|
7554
|
+
const filePath = import_node_path40.default.join(ctx.scanPath, file);
|
|
6824
7555
|
const content = await import_node_fs23.promises.readFile(filePath, "utf8");
|
|
6825
7556
|
if (content.includes(match.registration_snippet)) {
|
|
6826
7557
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -6913,23 +7644,23 @@ function canonicalJson(value) {
|
|
|
6913
7644
|
|
|
6914
7645
|
// src/projects.ts
|
|
6915
7646
|
init_cjs_shims();
|
|
6916
|
-
var
|
|
7647
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
6917
7648
|
function pathsForProject(project, baseDir) {
|
|
6918
7649
|
if (project === DEFAULT_PROJECT) {
|
|
6919
7650
|
return {
|
|
6920
|
-
snapshotPath:
|
|
6921
|
-
errorsPath:
|
|
6922
|
-
staleEventsPath:
|
|
6923
|
-
embeddingsCachePath:
|
|
6924
|
-
policyViolationsPath:
|
|
7651
|
+
snapshotPath: import_node_path41.default.join(baseDir, "graph.json"),
|
|
7652
|
+
errorsPath: import_node_path41.default.join(baseDir, "errors.ndjson"),
|
|
7653
|
+
staleEventsPath: import_node_path41.default.join(baseDir, "stale-events.ndjson"),
|
|
7654
|
+
embeddingsCachePath: import_node_path41.default.join(baseDir, "embeddings.json"),
|
|
7655
|
+
policyViolationsPath: import_node_path41.default.join(baseDir, "policy-violations.ndjson")
|
|
6925
7656
|
};
|
|
6926
7657
|
}
|
|
6927
7658
|
return {
|
|
6928
|
-
snapshotPath:
|
|
6929
|
-
errorsPath:
|
|
6930
|
-
staleEventsPath:
|
|
6931
|
-
embeddingsCachePath:
|
|
6932
|
-
policyViolationsPath:
|
|
7659
|
+
snapshotPath: import_node_path41.default.join(baseDir, `${project}.json`),
|
|
7660
|
+
errorsPath: import_node_path41.default.join(baseDir, `errors.${project}.ndjson`),
|
|
7661
|
+
staleEventsPath: import_node_path41.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
7662
|
+
embeddingsCachePath: import_node_path41.default.join(baseDir, `embeddings.${project}.json`),
|
|
7663
|
+
policyViolationsPath: import_node_path41.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
6933
7664
|
};
|
|
6934
7665
|
}
|
|
6935
7666
|
var Projects = class {
|
|
@@ -6967,26 +7698,26 @@ var Projects = class {
|
|
|
6967
7698
|
init_cjs_shims();
|
|
6968
7699
|
var import_node_fs25 = require("fs");
|
|
6969
7700
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
6970
|
-
var
|
|
6971
|
-
var
|
|
7701
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7702
|
+
var import_types28 = require("@neat.is/types");
|
|
6972
7703
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
6973
7704
|
var LOCK_RETRY_MS = 50;
|
|
6974
7705
|
function neatHome() {
|
|
6975
7706
|
const override = process.env.NEAT_HOME;
|
|
6976
|
-
if (override && override.length > 0) return
|
|
6977
|
-
return
|
|
7707
|
+
if (override && override.length > 0) return import_node_path42.default.resolve(override);
|
|
7708
|
+
return import_node_path42.default.join(import_node_os3.default.homedir(), ".neat");
|
|
6978
7709
|
}
|
|
6979
7710
|
function registryPath() {
|
|
6980
|
-
return
|
|
7711
|
+
return import_node_path42.default.join(neatHome(), "projects.json");
|
|
6981
7712
|
}
|
|
6982
7713
|
function registryLockPath() {
|
|
6983
|
-
return
|
|
7714
|
+
return import_node_path42.default.join(neatHome(), "projects.json.lock");
|
|
6984
7715
|
}
|
|
6985
7716
|
function daemonPidPath() {
|
|
6986
|
-
return
|
|
7717
|
+
return import_node_path42.default.join(neatHome(), "neatd.pid");
|
|
6987
7718
|
}
|
|
6988
7719
|
function daemonsDir() {
|
|
6989
|
-
return
|
|
7720
|
+
return import_node_path42.default.join(neatHome(), "daemons");
|
|
6990
7721
|
}
|
|
6991
7722
|
function isFiniteInt(v) {
|
|
6992
7723
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -7027,7 +7758,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
7027
7758
|
const out = [];
|
|
7028
7759
|
for (const name of names) {
|
|
7029
7760
|
if (!name.endsWith(".json")) continue;
|
|
7030
|
-
const file =
|
|
7761
|
+
const file = import_node_path42.default.join(dir, name);
|
|
7031
7762
|
let raw;
|
|
7032
7763
|
try {
|
|
7033
7764
|
raw = await import_node_fs25.promises.readFile(file, "utf8");
|
|
@@ -7148,7 +7879,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
7148
7879
|
}
|
|
7149
7880
|
}
|
|
7150
7881
|
async function normalizeProjectPath(input) {
|
|
7151
|
-
const resolved =
|
|
7882
|
+
const resolved = import_node_path42.default.resolve(input);
|
|
7152
7883
|
try {
|
|
7153
7884
|
return await import_node_fs25.promises.realpath(resolved);
|
|
7154
7885
|
} catch {
|
|
@@ -7156,7 +7887,7 @@ async function normalizeProjectPath(input) {
|
|
|
7156
7887
|
}
|
|
7157
7888
|
}
|
|
7158
7889
|
async function writeAtomically(target, contents) {
|
|
7159
|
-
await import_node_fs25.promises.mkdir(
|
|
7890
|
+
await import_node_fs25.promises.mkdir(import_node_path42.default.dirname(target), { recursive: true });
|
|
7160
7891
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
7161
7892
|
const fd = await import_node_fs25.promises.open(tmp, "w");
|
|
7162
7893
|
try {
|
|
@@ -7169,7 +7900,7 @@ async function writeAtomically(target, contents) {
|
|
|
7169
7900
|
}
|
|
7170
7901
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
7171
7902
|
const deadline = Date.now() + timeoutMs;
|
|
7172
|
-
await import_node_fs25.promises.mkdir(
|
|
7903
|
+
await import_node_fs25.promises.mkdir(import_node_path42.default.dirname(lockPath), { recursive: true });
|
|
7173
7904
|
let probedHolder = false;
|
|
7174
7905
|
while (true) {
|
|
7175
7906
|
try {
|
|
@@ -7222,10 +7953,10 @@ async function readRegistry() {
|
|
|
7222
7953
|
throw err;
|
|
7223
7954
|
}
|
|
7224
7955
|
const parsed = JSON.parse(raw);
|
|
7225
|
-
return
|
|
7956
|
+
return import_types28.RegistryFileSchema.parse(parsed);
|
|
7226
7957
|
}
|
|
7227
7958
|
async function writeRegistry(reg) {
|
|
7228
|
-
const validated =
|
|
7959
|
+
const validated = import_types28.RegistryFileSchema.parse(reg);
|
|
7229
7960
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
7230
7961
|
}
|
|
7231
7962
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -7557,11 +8288,11 @@ function registerRoutes(scope, ctx) {
|
|
|
7557
8288
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7558
8289
|
const parsed = [];
|
|
7559
8290
|
for (const c of candidates) {
|
|
7560
|
-
const r =
|
|
8291
|
+
const r = import_types29.DivergenceTypeSchema.safeParse(c);
|
|
7561
8292
|
if (!r.success) {
|
|
7562
8293
|
return reply.code(400).send({
|
|
7563
8294
|
error: `unknown divergence type "${c}"`,
|
|
7564
|
-
allowed:
|
|
8295
|
+
allowed: import_types29.DivergenceTypeSchema.options
|
|
7565
8296
|
});
|
|
7566
8297
|
}
|
|
7567
8298
|
parsed.push(r.data);
|
|
@@ -7780,7 +8511,7 @@ function registerRoutes(scope, ctx) {
|
|
|
7780
8511
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
7781
8512
|
let violations = await log.readAll();
|
|
7782
8513
|
if (req.query.severity) {
|
|
7783
|
-
const sev =
|
|
8514
|
+
const sev = import_types29.PolicySeveritySchema.safeParse(req.query.severity);
|
|
7784
8515
|
if (!sev.success) {
|
|
7785
8516
|
return reply.code(400).send({
|
|
7786
8517
|
error: "invalid severity",
|
|
@@ -7819,7 +8550,7 @@ function registerRoutes(scope, ctx) {
|
|
|
7819
8550
|
scope.post("/policies/check", async (req, reply) => {
|
|
7820
8551
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
7821
8552
|
if (!proj) return;
|
|
7822
|
-
const parsed =
|
|
8553
|
+
const parsed = import_types29.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
7823
8554
|
if (!parsed.success) {
|
|
7824
8555
|
return reply.code(400).send({
|
|
7825
8556
|
error: "invalid /policies/check body",
|
|
@@ -8082,7 +8813,7 @@ init_otel();
|
|
|
8082
8813
|
// src/daemon.ts
|
|
8083
8814
|
init_cjs_shims();
|
|
8084
8815
|
var import_node_fs27 = require("fs");
|
|
8085
|
-
var
|
|
8816
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8086
8817
|
var import_node_module = require("module");
|
|
8087
8818
|
init_otel();
|
|
8088
8819
|
init_auth();
|
|
@@ -8090,28 +8821,28 @@ init_auth();
|
|
|
8090
8821
|
// src/unrouted.ts
|
|
8091
8822
|
init_cjs_shims();
|
|
8092
8823
|
var import_node_fs26 = require("fs");
|
|
8093
|
-
var
|
|
8824
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8094
8825
|
|
|
8095
8826
|
// src/daemon.ts
|
|
8096
|
-
var
|
|
8827
|
+
var import_types30 = require("@neat.is/types");
|
|
8097
8828
|
function daemonJsonPath(scanPath) {
|
|
8098
|
-
return
|
|
8829
|
+
return import_node_path46.default.join(scanPath, "neat-out", "daemon.json");
|
|
8099
8830
|
}
|
|
8100
8831
|
function daemonsDiscoveryDir(home) {
|
|
8101
8832
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
8102
|
-
return
|
|
8833
|
+
return import_node_path46.default.join(base, "daemons");
|
|
8103
8834
|
}
|
|
8104
8835
|
function daemonDiscoveryPath(project, home) {
|
|
8105
|
-
return
|
|
8836
|
+
return import_node_path46.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
8106
8837
|
}
|
|
8107
8838
|
function sanitizeDiscoveryName(project) {
|
|
8108
8839
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
8109
8840
|
}
|
|
8110
8841
|
function neatHomeFromEnv() {
|
|
8111
8842
|
const env = process.env.NEAT_HOME;
|
|
8112
|
-
if (env && env.length > 0) return
|
|
8843
|
+
if (env && env.length > 0) return import_node_path46.default.resolve(env);
|
|
8113
8844
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
8114
|
-
return
|
|
8845
|
+
return import_node_path46.default.join(home, ".neat");
|
|
8115
8846
|
}
|
|
8116
8847
|
async function readDaemonRecord(scanPath) {
|
|
8117
8848
|
try {
|
|
@@ -8182,7 +8913,7 @@ init_otel_grpc();
|
|
|
8182
8913
|
// src/search.ts
|
|
8183
8914
|
init_cjs_shims();
|
|
8184
8915
|
var import_node_fs28 = require("fs");
|
|
8185
|
-
var
|
|
8916
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8186
8917
|
var import_node_crypto3 = require("crypto");
|
|
8187
8918
|
var DEFAULT_LIMIT = 10;
|
|
8188
8919
|
var NOMIC_DIM = 768;
|
|
@@ -8217,6 +8948,18 @@ function embedText(node) {
|
|
|
8217
8948
|
if (filePath) parts.push(`path=${filePath}`);
|
|
8218
8949
|
break;
|
|
8219
8950
|
}
|
|
8951
|
+
case "RouteNode": {
|
|
8952
|
+
const method = node.method;
|
|
8953
|
+
const tmpl = node.pathTemplate;
|
|
8954
|
+
if (method) parts.push(`method=${method}`);
|
|
8955
|
+
if (tmpl) parts.push(`path=${tmpl}`);
|
|
8956
|
+
break;
|
|
8957
|
+
}
|
|
8958
|
+
case "GraphQLOperationNode": {
|
|
8959
|
+
const opType = node.operationType;
|
|
8960
|
+
if (opType) parts.push(`operationType=${opType}`);
|
|
8961
|
+
break;
|
|
8962
|
+
}
|
|
8220
8963
|
default:
|
|
8221
8964
|
break;
|
|
8222
8965
|
}
|
|
@@ -8321,7 +9064,7 @@ async function readCache(cachePath) {
|
|
|
8321
9064
|
}
|
|
8322
9065
|
}
|
|
8323
9066
|
async function writeCache(cachePath, cache) {
|
|
8324
|
-
await import_node_fs28.promises.mkdir(
|
|
9067
|
+
await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(cachePath), { recursive: true });
|
|
8325
9068
|
await import_node_fs28.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
8326
9069
|
}
|
|
8327
9070
|
var VectorIndex = class {
|
|
@@ -8479,8 +9222,8 @@ var ALL_PHASES = [
|
|
|
8479
9222
|
];
|
|
8480
9223
|
function classifyChange(relPath) {
|
|
8481
9224
|
const phases = /* @__PURE__ */ new Set();
|
|
8482
|
-
const base =
|
|
8483
|
-
const segments = relPath.split(
|
|
9225
|
+
const base = import_node_path48.default.basename(relPath).toLowerCase();
|
|
9226
|
+
const segments = relPath.split(import_node_path48.default.sep).map((s) => s.toLowerCase());
|
|
8484
9227
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
8485
9228
|
phases.add("services");
|
|
8486
9229
|
phases.add("aliases");
|
|
@@ -8608,9 +9351,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
8608
9351
|
for (const e of entries) {
|
|
8609
9352
|
if (count >= limit) return;
|
|
8610
9353
|
if (!e.isDirectory()) continue;
|
|
8611
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
9354
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path48.default.join(dir, e.name) + import_node_path48.default.sep))) continue;
|
|
8612
9355
|
count++;
|
|
8613
|
-
if (depth < 2) visit(
|
|
9356
|
+
if (depth < 2) visit(import_node_path48.default.join(dir, e.name), depth + 1);
|
|
8614
9357
|
}
|
|
8615
9358
|
};
|
|
8616
9359
|
visit(scanPath, 0);
|
|
@@ -8628,8 +9371,8 @@ async function startWatch(graph, opts) {
|
|
|
8628
9371
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
8629
9372
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
8630
9373
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
8631
|
-
const policyFilePath =
|
|
8632
|
-
const policyViolationsPath =
|
|
9374
|
+
const policyFilePath = import_node_path48.default.join(opts.scanPath, "policy.json");
|
|
9375
|
+
const policyViolationsPath = import_node_path48.default.join(import_node_path48.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
8633
9376
|
let policies = [];
|
|
8634
9377
|
try {
|
|
8635
9378
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -8680,7 +9423,7 @@ async function startWatch(graph, opts) {
|
|
|
8680
9423
|
assertBindAuthority(host, auth.authToken);
|
|
8681
9424
|
const port = opts.port ?? 8080;
|
|
8682
9425
|
const otelPort = opts.otelPort ?? 4318;
|
|
8683
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
9426
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path48.default.join(import_node_path48.default.dirname(opts.outPath), "embeddings.json");
|
|
8684
9427
|
let searchIndex;
|
|
8685
9428
|
try {
|
|
8686
9429
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -8698,7 +9441,7 @@ async function startWatch(graph, opts) {
|
|
|
8698
9441
|
// Paths are derived from the explicit options the watch caller passes
|
|
8699
9442
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
8700
9443
|
// fields so the registry shape is complete.
|
|
8701
|
-
...pathsForProject(projectName,
|
|
9444
|
+
...pathsForProject(projectName, import_node_path48.default.dirname(opts.outPath)),
|
|
8702
9445
|
snapshotPath: opts.outPath,
|
|
8703
9446
|
errorsPath: opts.errorsPath,
|
|
8704
9447
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -8816,9 +9559,9 @@ async function startWatch(graph, opts) {
|
|
|
8816
9559
|
};
|
|
8817
9560
|
const onPath = (absPath) => {
|
|
8818
9561
|
if (shouldIgnore(absPath)) return;
|
|
8819
|
-
const rel =
|
|
9562
|
+
const rel = import_node_path48.default.relative(opts.scanPath, absPath);
|
|
8820
9563
|
if (!rel || rel.startsWith("..")) return;
|
|
8821
|
-
pendingPaths.add(rel.split(
|
|
9564
|
+
pendingPaths.add(rel.split(import_node_path48.default.sep).join("/"));
|
|
8822
9565
|
const phases = classifyChange(rel);
|
|
8823
9566
|
if (phases.size === 0) {
|
|
8824
9567
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -8875,7 +9618,7 @@ async function startWatch(graph, opts) {
|
|
|
8875
9618
|
// src/deploy/detect.ts
|
|
8876
9619
|
init_cjs_shims();
|
|
8877
9620
|
var import_node_fs30 = require("fs");
|
|
8878
|
-
var
|
|
9621
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
8879
9622
|
var import_node_child_process2 = require("child_process");
|
|
8880
9623
|
var import_node_crypto4 = require("crypto");
|
|
8881
9624
|
function generateToken() {
|
|
@@ -8975,7 +9718,7 @@ async function runDeploy(opts = {}) {
|
|
|
8975
9718
|
const token = generateToken();
|
|
8976
9719
|
switch (substrate) {
|
|
8977
9720
|
case "docker-compose": {
|
|
8978
|
-
const artifactPath =
|
|
9721
|
+
const artifactPath = import_node_path49.default.join(cwd, "docker-compose.neat.yml");
|
|
8979
9722
|
const contents = emitDockerCompose(cwd);
|
|
8980
9723
|
await import_node_fs30.promises.writeFile(artifactPath, contents, "utf8");
|
|
8981
9724
|
return {
|
|
@@ -8983,11 +9726,11 @@ async function runDeploy(opts = {}) {
|
|
|
8983
9726
|
artifactPath,
|
|
8984
9727
|
token,
|
|
8985
9728
|
contents,
|
|
8986
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
9729
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path49.default.basename(artifactPath)} up -d`
|
|
8987
9730
|
};
|
|
8988
9731
|
}
|
|
8989
9732
|
case "systemd": {
|
|
8990
|
-
const artifactPath =
|
|
9733
|
+
const artifactPath = import_node_path49.default.join(cwd, "neat.service");
|
|
8991
9734
|
const contents = emitSystemdUnit(cwd);
|
|
8992
9735
|
await import_node_fs30.promises.writeFile(artifactPath, contents, "utf8");
|
|
8993
9736
|
return {
|
|
@@ -9023,7 +9766,7 @@ init_cjs_shims();
|
|
|
9023
9766
|
// src/installers/javascript.ts
|
|
9024
9767
|
init_cjs_shims();
|
|
9025
9768
|
var import_node_fs31 = require("fs");
|
|
9026
|
-
var
|
|
9769
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9027
9770
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
9028
9771
|
|
|
9029
9772
|
// src/installers/templates.ts
|
|
@@ -9619,11 +10362,11 @@ var OTEL_ENV = {
|
|
|
9619
10362
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
9620
10363
|
};
|
|
9621
10364
|
function serviceNodeName(pkg, serviceDir) {
|
|
9622
|
-
return pkg.name ??
|
|
10365
|
+
return pkg.name ?? import_node_path50.default.basename(serviceDir);
|
|
9623
10366
|
}
|
|
9624
10367
|
function projectToken(pkg, serviceDir, project) {
|
|
9625
10368
|
if (project && project.length > 0) return project;
|
|
9626
|
-
return pkg.name ??
|
|
10369
|
+
return pkg.name ?? import_node_path50.default.basename(serviceDir);
|
|
9627
10370
|
}
|
|
9628
10371
|
async function readJsonFile(p) {
|
|
9629
10372
|
try {
|
|
@@ -9636,16 +10379,16 @@ async function readJsonFile(p) {
|
|
|
9636
10379
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
9637
10380
|
const deps = allDeps(pkg);
|
|
9638
10381
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
9639
|
-
const appJson = await readJsonFile(
|
|
10382
|
+
const appJson = await readJsonFile(import_node_path50.default.join(pkgRoot, "app.json"));
|
|
9640
10383
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
9641
10384
|
return "react-native";
|
|
9642
10385
|
}
|
|
9643
|
-
if (await exists3(
|
|
10386
|
+
if (await exists3(import_node_path50.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path50.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path50.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
9644
10387
|
return "browser-bundle";
|
|
9645
10388
|
}
|
|
9646
|
-
if (await exists3(
|
|
9647
|
-
if (await exists3(
|
|
9648
|
-
if (await exists3(
|
|
10389
|
+
if (await exists3(import_node_path50.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
10390
|
+
if (await exists3(import_node_path50.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
10391
|
+
if (await exists3(import_node_path50.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path50.default.join(pkgRoot, "deno.lock"))) {
|
|
9649
10392
|
return "deno";
|
|
9650
10393
|
}
|
|
9651
10394
|
const engines = pkg.engines ?? {};
|
|
@@ -9654,7 +10397,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
9654
10397
|
}
|
|
9655
10398
|
async function readPackageJson2(serviceDir) {
|
|
9656
10399
|
try {
|
|
9657
|
-
const raw = await import_node_fs31.promises.readFile(
|
|
10400
|
+
const raw = await import_node_fs31.promises.readFile(import_node_path50.default.join(serviceDir, "package.json"), "utf8");
|
|
9658
10401
|
return JSON.parse(raw);
|
|
9659
10402
|
} catch {
|
|
9660
10403
|
return null;
|
|
@@ -9698,7 +10441,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
9698
10441
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
9699
10442
|
async function findNextConfig(serviceDir) {
|
|
9700
10443
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
9701
|
-
const candidate =
|
|
10444
|
+
const candidate = import_node_path50.default.join(serviceDir, name);
|
|
9702
10445
|
if (await exists3(candidate)) return candidate;
|
|
9703
10446
|
}
|
|
9704
10447
|
return null;
|
|
@@ -9767,7 +10510,7 @@ function hasRemixDependency(pkg) {
|
|
|
9767
10510
|
}
|
|
9768
10511
|
async function findRemixEntry(serviceDir) {
|
|
9769
10512
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
9770
|
-
const candidate =
|
|
10513
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
9771
10514
|
if (await exists3(candidate)) return candidate;
|
|
9772
10515
|
}
|
|
9773
10516
|
return null;
|
|
@@ -9779,14 +10522,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
9779
10522
|
}
|
|
9780
10523
|
async function findSvelteKitHooks(serviceDir) {
|
|
9781
10524
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
9782
|
-
const candidate =
|
|
10525
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
9783
10526
|
if (await exists3(candidate)) return candidate;
|
|
9784
10527
|
}
|
|
9785
10528
|
return null;
|
|
9786
10529
|
}
|
|
9787
10530
|
async function findSvelteKitConfig(serviceDir) {
|
|
9788
10531
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
9789
|
-
const candidate =
|
|
10532
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
9790
10533
|
if (await exists3(candidate)) return candidate;
|
|
9791
10534
|
}
|
|
9792
10535
|
return null;
|
|
@@ -9797,7 +10540,7 @@ function hasNuxtDependency(pkg) {
|
|
|
9797
10540
|
}
|
|
9798
10541
|
async function findNuxtConfig(serviceDir) {
|
|
9799
10542
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
9800
|
-
const candidate =
|
|
10543
|
+
const candidate = import_node_path50.default.join(serviceDir, name);
|
|
9801
10544
|
if (await exists3(candidate)) return candidate;
|
|
9802
10545
|
}
|
|
9803
10546
|
return null;
|
|
@@ -9808,7 +10551,7 @@ function hasAstroDependency(pkg) {
|
|
|
9808
10551
|
}
|
|
9809
10552
|
async function findAstroConfig(serviceDir) {
|
|
9810
10553
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
9811
|
-
const candidate =
|
|
10554
|
+
const candidate = import_node_path50.default.join(serviceDir, name);
|
|
9812
10555
|
if (await exists3(candidate)) return candidate;
|
|
9813
10556
|
}
|
|
9814
10557
|
return null;
|
|
@@ -9822,7 +10565,7 @@ function parseNextMajor(range) {
|
|
|
9822
10565
|
return Number.isFinite(n) ? n : null;
|
|
9823
10566
|
}
|
|
9824
10567
|
async function isTypeScriptProject(serviceDir) {
|
|
9825
|
-
return exists3(
|
|
10568
|
+
return exists3(import_node_path50.default.join(serviceDir, "tsconfig.json"));
|
|
9826
10569
|
}
|
|
9827
10570
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
9828
10571
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -9871,7 +10614,7 @@ function entryFromScript(script) {
|
|
|
9871
10614
|
}
|
|
9872
10615
|
async function resolveEntry(serviceDir, pkg) {
|
|
9873
10616
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
9874
|
-
const candidate =
|
|
10617
|
+
const candidate = import_node_path50.default.resolve(serviceDir, pkg.main);
|
|
9875
10618
|
if (await exists3(candidate)) return candidate;
|
|
9876
10619
|
}
|
|
9877
10620
|
if (pkg.bin) {
|
|
@@ -9885,40 +10628,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
9885
10628
|
if (typeof first === "string") binEntry = first;
|
|
9886
10629
|
}
|
|
9887
10630
|
if (binEntry) {
|
|
9888
|
-
const candidate =
|
|
10631
|
+
const candidate = import_node_path50.default.resolve(serviceDir, binEntry);
|
|
9889
10632
|
if (await exists3(candidate)) return candidate;
|
|
9890
10633
|
}
|
|
9891
10634
|
}
|
|
9892
10635
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
9893
10636
|
if (startEntry) {
|
|
9894
|
-
const candidate =
|
|
10637
|
+
const candidate = import_node_path50.default.resolve(serviceDir, startEntry);
|
|
9895
10638
|
if (await exists3(candidate)) return candidate;
|
|
9896
10639
|
}
|
|
9897
10640
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
9898
10641
|
if (devEntry) {
|
|
9899
|
-
const candidate =
|
|
10642
|
+
const candidate = import_node_path50.default.resolve(serviceDir, devEntry);
|
|
9900
10643
|
if (await exists3(candidate)) return candidate;
|
|
9901
10644
|
}
|
|
9902
10645
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
9903
|
-
const candidate =
|
|
10646
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
9904
10647
|
if (await exists3(candidate)) return candidate;
|
|
9905
10648
|
}
|
|
9906
10649
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
9907
|
-
const candidate =
|
|
10650
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
9908
10651
|
if (await exists3(candidate)) return candidate;
|
|
9909
10652
|
}
|
|
9910
10653
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
9911
|
-
const candidate =
|
|
10654
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
9912
10655
|
if (await exists3(candidate)) return candidate;
|
|
9913
10656
|
}
|
|
9914
10657
|
for (const name of INDEX_CANDIDATES) {
|
|
9915
|
-
const candidate =
|
|
10658
|
+
const candidate = import_node_path50.default.join(serviceDir, name);
|
|
9916
10659
|
if (await exists3(candidate)) return candidate;
|
|
9917
10660
|
}
|
|
9918
10661
|
return null;
|
|
9919
10662
|
}
|
|
9920
10663
|
function dispatchEntry(entryFile, pkg) {
|
|
9921
|
-
const ext =
|
|
10664
|
+
const ext = import_node_path50.default.extname(entryFile).toLowerCase();
|
|
9922
10665
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
9923
10666
|
if (ext === ".mjs") return "esm";
|
|
9924
10667
|
if (ext === ".cjs") return "cjs";
|
|
@@ -9935,9 +10678,9 @@ function otelInitContents(flavor) {
|
|
|
9935
10678
|
return OTEL_INIT_CJS;
|
|
9936
10679
|
}
|
|
9937
10680
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
9938
|
-
let rel =
|
|
10681
|
+
let rel = import_node_path50.default.relative(import_node_path50.default.dirname(entryFile), otelInitFile);
|
|
9939
10682
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
9940
|
-
rel = rel.split(
|
|
10683
|
+
rel = rel.split(import_node_path50.default.sep).join("/");
|
|
9941
10684
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
9942
10685
|
if (flavor === "esm") return `import '${rel}'`;
|
|
9943
10686
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -9950,23 +10693,23 @@ function lineIsOtelInjection(line) {
|
|
|
9950
10693
|
}
|
|
9951
10694
|
async function detectsSrcLayout(serviceDir) {
|
|
9952
10695
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
9953
|
-
exists3(
|
|
9954
|
-
exists3(
|
|
9955
|
-
exists3(
|
|
9956
|
-
exists3(
|
|
10696
|
+
exists3(import_node_path50.default.join(serviceDir, "src", "app")),
|
|
10697
|
+
exists3(import_node_path50.default.join(serviceDir, "src", "pages")),
|
|
10698
|
+
exists3(import_node_path50.default.join(serviceDir, "app")),
|
|
10699
|
+
exists3(import_node_path50.default.join(serviceDir, "pages"))
|
|
9957
10700
|
]);
|
|
9958
10701
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
9959
10702
|
}
|
|
9960
10703
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
9961
10704
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
9962
10705
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
9963
|
-
const baseDir = srcLayout ?
|
|
9964
|
-
const instrumentationFile =
|
|
9965
|
-
const instrumentationNodeFile =
|
|
10706
|
+
const baseDir = srcLayout ? import_node_path50.default.join(serviceDir, "src") : serviceDir;
|
|
10707
|
+
const instrumentationFile = import_node_path50.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
10708
|
+
const instrumentationNodeFile = import_node_path50.default.join(
|
|
9966
10709
|
baseDir,
|
|
9967
10710
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
9968
10711
|
);
|
|
9969
|
-
const envNeatFile =
|
|
10712
|
+
const envNeatFile = import_node_path50.default.join(baseDir, ".env.neat");
|
|
9970
10713
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
9971
10714
|
const dependencyEdits = [];
|
|
9972
10715
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -10071,7 +10814,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
10071
10814
|
return edits;
|
|
10072
10815
|
}
|
|
10073
10816
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
10074
|
-
const envNeatFile =
|
|
10817
|
+
const envNeatFile = import_node_path50.default.join(serviceDir, ".env.neat");
|
|
10075
10818
|
if (!await exists3(envNeatFile)) {
|
|
10076
10819
|
generatedFiles.push({
|
|
10077
10820
|
file: envNeatFile,
|
|
@@ -10106,7 +10849,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
10106
10849
|
}
|
|
10107
10850
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
10108
10851
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10109
|
-
const otelServerFile =
|
|
10852
|
+
const otelServerFile = import_node_path50.default.join(
|
|
10110
10853
|
serviceDir,
|
|
10111
10854
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
10112
10855
|
);
|
|
@@ -10163,11 +10906,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
10163
10906
|
}
|
|
10164
10907
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
10165
10908
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10166
|
-
const otelInitFile =
|
|
10909
|
+
const otelInitFile = import_node_path50.default.join(
|
|
10167
10910
|
serviceDir,
|
|
10168
10911
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
10169
10912
|
);
|
|
10170
|
-
const resolvedHooksFile = hooksFile ??
|
|
10913
|
+
const resolvedHooksFile = hooksFile ?? import_node_path50.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
10171
10914
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
10172
10915
|
const generatedFiles = [];
|
|
10173
10916
|
const entrypointEdits = [];
|
|
@@ -10229,11 +10972,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
10229
10972
|
}
|
|
10230
10973
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
10231
10974
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10232
|
-
const otelPluginFile =
|
|
10975
|
+
const otelPluginFile = import_node_path50.default.join(
|
|
10233
10976
|
serviceDir,
|
|
10234
10977
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
10235
10978
|
);
|
|
10236
|
-
const otelInitFile =
|
|
10979
|
+
const otelInitFile = import_node_path50.default.join(
|
|
10237
10980
|
serviceDir,
|
|
10238
10981
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
10239
10982
|
);
|
|
@@ -10284,19 +11027,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
10284
11027
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
10285
11028
|
async function findAstroMiddleware(serviceDir) {
|
|
10286
11029
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
10287
|
-
const candidate =
|
|
11030
|
+
const candidate = import_node_path50.default.join(serviceDir, rel);
|
|
10288
11031
|
if (await exists3(candidate)) return candidate;
|
|
10289
11032
|
}
|
|
10290
11033
|
return null;
|
|
10291
11034
|
}
|
|
10292
11035
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
10293
11036
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10294
|
-
const otelInitFile =
|
|
11037
|
+
const otelInitFile = import_node_path50.default.join(
|
|
10295
11038
|
serviceDir,
|
|
10296
11039
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
10297
11040
|
);
|
|
10298
11041
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
10299
|
-
const middlewareFile = existingMiddleware ??
|
|
11042
|
+
const middlewareFile = existingMiddleware ?? import_node_path50.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
10300
11043
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
10301
11044
|
const generatedFiles = [];
|
|
10302
11045
|
const entrypointEdits = [];
|
|
@@ -10392,7 +11135,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
10392
11135
|
}
|
|
10393
11136
|
async function plan(serviceDir, opts) {
|
|
10394
11137
|
const pkg = await readPackageJson2(serviceDir);
|
|
10395
|
-
const manifestPath =
|
|
11138
|
+
const manifestPath = import_node_path50.default.join(serviceDir, "package.json");
|
|
10396
11139
|
const project = opts?.project;
|
|
10397
11140
|
const empty = {
|
|
10398
11141
|
language: "javascript",
|
|
@@ -10427,8 +11170,8 @@ async function plan(serviceDir, opts) {
|
|
|
10427
11170
|
return { ...empty, libOnly: true };
|
|
10428
11171
|
}
|
|
10429
11172
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
10430
|
-
const otelInitFile =
|
|
10431
|
-
const envNeatFile =
|
|
11173
|
+
const otelInitFile = import_node_path50.default.join(import_node_path50.default.dirname(entryFile), otelInitFilename(flavor));
|
|
11174
|
+
const envNeatFile = import_node_path50.default.join(serviceDir, ".env.neat");
|
|
10432
11175
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
10433
11176
|
const dependencyEdits = [];
|
|
10434
11177
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -10497,13 +11240,13 @@ async function plan(serviceDir, opts) {
|
|
|
10497
11240
|
};
|
|
10498
11241
|
}
|
|
10499
11242
|
function isAllowedWritePath(serviceDir, target) {
|
|
10500
|
-
const rel =
|
|
11243
|
+
const rel = import_node_path50.default.relative(serviceDir, target);
|
|
10501
11244
|
if (rel.startsWith("..")) return false;
|
|
10502
|
-
const base =
|
|
11245
|
+
const base = import_node_path50.default.basename(target);
|
|
10503
11246
|
if (base === "package.json") return true;
|
|
10504
11247
|
if (base === ".env.neat") return true;
|
|
10505
11248
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
10506
|
-
const relPosix = rel.split(
|
|
11249
|
+
const relPosix = rel.split(import_node_path50.default.sep).join("/");
|
|
10507
11250
|
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
10508
11251
|
if (relPosix === base) return true;
|
|
10509
11252
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -10520,7 +11263,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
10520
11263
|
return false;
|
|
10521
11264
|
}
|
|
10522
11265
|
async function writeAtomic(file, contents) {
|
|
10523
|
-
await import_node_fs31.promises.mkdir(
|
|
11266
|
+
await import_node_fs31.promises.mkdir(import_node_path50.default.dirname(file), { recursive: true });
|
|
10524
11267
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
10525
11268
|
await import_node_fs31.promises.writeFile(tmp, contents, "utf8");
|
|
10526
11269
|
await import_node_fs31.promises.rename(tmp, file);
|
|
@@ -10704,7 +11447,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
10704
11447
|
...removed.map((f) => `removed: ${f}`),
|
|
10705
11448
|
""
|
|
10706
11449
|
];
|
|
10707
|
-
const rollbackPath =
|
|
11450
|
+
const rollbackPath = import_node_path50.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
10708
11451
|
await import_node_fs31.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
10709
11452
|
}
|
|
10710
11453
|
function injectInstrumentationHook(raw) {
|
|
@@ -10735,7 +11478,7 @@ var javascriptInstaller = {
|
|
|
10735
11478
|
// src/installers/python.ts
|
|
10736
11479
|
init_cjs_shims();
|
|
10737
11480
|
var import_node_fs32 = require("fs");
|
|
10738
|
-
var
|
|
11481
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
10739
11482
|
var SDK_PACKAGES2 = [
|
|
10740
11483
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
10741
11484
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -10756,7 +11499,7 @@ async function exists4(p) {
|
|
|
10756
11499
|
async function detect2(serviceDir) {
|
|
10757
11500
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
10758
11501
|
for (const m of markers) {
|
|
10759
|
-
if (await exists4(
|
|
11502
|
+
if (await exists4(import_node_path51.default.join(serviceDir, m))) return true;
|
|
10760
11503
|
}
|
|
10761
11504
|
return false;
|
|
10762
11505
|
}
|
|
@@ -10766,7 +11509,7 @@ function reqPackageName(line) {
|
|
|
10766
11509
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
10767
11510
|
}
|
|
10768
11511
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
10769
|
-
const file =
|
|
11512
|
+
const file = import_node_path51.default.join(serviceDir, "requirements.txt");
|
|
10770
11513
|
if (!await exists4(file)) return null;
|
|
10771
11514
|
const raw = await import_node_fs32.promises.readFile(file, "utf8");
|
|
10772
11515
|
const presentNames = new Set(
|
|
@@ -10776,7 +11519,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
10776
11519
|
return { manifest: file, missing: [...missing] };
|
|
10777
11520
|
}
|
|
10778
11521
|
async function planProcfileEdits(serviceDir) {
|
|
10779
|
-
const procfile =
|
|
11522
|
+
const procfile = import_node_path51.default.join(serviceDir, "Procfile");
|
|
10780
11523
|
if (!await exists4(procfile)) return [];
|
|
10781
11524
|
const raw = await import_node_fs32.promises.readFile(procfile, "utf8");
|
|
10782
11525
|
const edits = [];
|
|
@@ -10865,7 +11608,7 @@ async function apply2(installPlan) {
|
|
|
10865
11608
|
if (raw === void 0) {
|
|
10866
11609
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
10867
11610
|
}
|
|
10868
|
-
const base =
|
|
11611
|
+
const base = import_node_path51.default.basename(file);
|
|
10869
11612
|
if (base === "requirements.txt") {
|
|
10870
11613
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
10871
11614
|
if (edits.length > 0) {
|
|
@@ -10904,7 +11647,7 @@ async function rollback2(installPlan, originals) {
|
|
|
10904
11647
|
...restored.map((f) => `restored: ${f}`),
|
|
10905
11648
|
""
|
|
10906
11649
|
];
|
|
10907
|
-
const rollbackPath =
|
|
11650
|
+
const rollbackPath = import_node_path51.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
10908
11651
|
await import_node_fs32.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
10909
11652
|
}
|
|
10910
11653
|
var pythonInstaller = {
|
|
@@ -11031,7 +11774,7 @@ init_cjs_shims();
|
|
|
11031
11774
|
var import_node_fs33 = require("fs");
|
|
11032
11775
|
var import_node_http = __toESM(require("http"), 1);
|
|
11033
11776
|
var import_node_net = __toESM(require("net"), 1);
|
|
11034
|
-
var
|
|
11777
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
11035
11778
|
var import_node_child_process3 = require("child_process");
|
|
11036
11779
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
11037
11780
|
async function extractAndPersist(opts) {
|
|
@@ -11040,7 +11783,7 @@ async function extractAndPersist(opts) {
|
|
|
11040
11783
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
11041
11784
|
resetGraph(graphKey);
|
|
11042
11785
|
const graph = getGraph(graphKey);
|
|
11043
|
-
const projectPaths = pathsForProject(graphKey,
|
|
11786
|
+
const projectPaths = pathsForProject(graphKey, import_node_path52.default.join(opts.scanPath, "neat-out"));
|
|
11044
11787
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
11045
11788
|
errorsPath: projectPaths.errorsPath
|
|
11046
11789
|
});
|
|
@@ -11092,7 +11835,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11092
11835
|
libOnly++;
|
|
11093
11836
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
11094
11837
|
if (appDeps.length > 0) {
|
|
11095
|
-
const svcName =
|
|
11838
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11096
11839
|
const list = appDeps.join(", ");
|
|
11097
11840
|
console.warn(
|
|
11098
11841
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -11105,7 +11848,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11105
11848
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
11106
11849
|
} else if (outcome.outcome === "react-native") {
|
|
11107
11850
|
reactNative++;
|
|
11108
|
-
const svcName =
|
|
11851
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11109
11852
|
console.log(
|
|
11110
11853
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
11111
11854
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11116,7 +11859,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11116
11859
|
);
|
|
11117
11860
|
} else if (outcome.outcome === "bun") {
|
|
11118
11861
|
bun++;
|
|
11119
|
-
const svcName =
|
|
11862
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11120
11863
|
console.log(
|
|
11121
11864
|
`neat: ${svc.dir} detected as Bun
|
|
11122
11865
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11127,7 +11870,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11127
11870
|
);
|
|
11128
11871
|
} else if (outcome.outcome === "deno") {
|
|
11129
11872
|
deno++;
|
|
11130
|
-
const svcName =
|
|
11873
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11131
11874
|
console.log(
|
|
11132
11875
|
`neat: ${svc.dir} detected as Deno
|
|
11133
11876
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11138,7 +11881,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11138
11881
|
);
|
|
11139
11882
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
11140
11883
|
cloudflareWorkers++;
|
|
11141
|
-
const svcName =
|
|
11884
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11142
11885
|
console.log(
|
|
11143
11886
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
11144
11887
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11149,7 +11892,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11149
11892
|
);
|
|
11150
11893
|
} else if (outcome.outcome === "electron") {
|
|
11151
11894
|
electron++;
|
|
11152
|
-
const svcName =
|
|
11895
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11153
11896
|
console.log(
|
|
11154
11897
|
`neat: ${svc.dir} detected as Electron
|
|
11155
11898
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11162,7 +11905,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11162
11905
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
11163
11906
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
11164
11907
|
if (gaps.length > 0) {
|
|
11165
|
-
const svcName =
|
|
11908
|
+
const svcName = import_node_path52.default.basename(svc.dir);
|
|
11166
11909
|
const list = gaps.join(", ");
|
|
11167
11910
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
11168
11911
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -11368,8 +12111,8 @@ async function persistedPortsFor(scanPath) {
|
|
|
11368
12111
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
11369
12112
|
}
|
|
11370
12113
|
async function acquireSpawnLock(scanPath) {
|
|
11371
|
-
const lockPath =
|
|
11372
|
-
await import_node_fs33.promises.mkdir(
|
|
12114
|
+
const lockPath = import_node_path52.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
12115
|
+
await import_node_fs33.promises.mkdir(import_node_path52.default.dirname(lockPath), { recursive: true });
|
|
11373
12116
|
const STALE_LOCK_MS = 6e4;
|
|
11374
12117
|
try {
|
|
11375
12118
|
const fd = await import_node_fs33.promises.open(lockPath, "wx");
|
|
@@ -11414,13 +12157,13 @@ async function healthIsForProject(restPort, project) {
|
|
|
11414
12157
|
return false;
|
|
11415
12158
|
}
|
|
11416
12159
|
function daemonLogPath(projectPath2) {
|
|
11417
|
-
return
|
|
12160
|
+
return import_node_path52.default.join(projectPath2, "neat-out", "daemon.log");
|
|
11418
12161
|
}
|
|
11419
12162
|
function spawnDaemonDetached(spec) {
|
|
11420
|
-
const here =
|
|
12163
|
+
const here = import_node_path52.default.dirname(new URL(importMetaUrl).pathname);
|
|
11421
12164
|
const candidates = [
|
|
11422
|
-
|
|
11423
|
-
|
|
12165
|
+
import_node_path52.default.join(here, "neatd.cjs"),
|
|
12166
|
+
import_node_path52.default.join(here, "neatd.js")
|
|
11424
12167
|
];
|
|
11425
12168
|
let entry2 = null;
|
|
11426
12169
|
const fsSync = require("fs");
|
|
@@ -11450,7 +12193,7 @@ function spawnDaemonDetached(spec) {
|
|
|
11450
12193
|
let logFd = null;
|
|
11451
12194
|
if (spec) {
|
|
11452
12195
|
const logPath = daemonLogPath(spec.projectPath);
|
|
11453
|
-
fsSync.mkdirSync(
|
|
12196
|
+
fsSync.mkdirSync(import_node_path52.default.dirname(logPath), { recursive: true });
|
|
11454
12197
|
logFd = fsSync.openSync(logPath, "a");
|
|
11455
12198
|
}
|
|
11456
12199
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -11649,7 +12392,7 @@ async function runOrchestrator(opts) {
|
|
|
11649
12392
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
11650
12393
|
}
|
|
11651
12394
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
11652
|
-
const daemonLog = daemonRunning ?
|
|
12395
|
+
const daemonLog = daemonRunning ? import_node_path52.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
11653
12396
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
11654
12397
|
return result;
|
|
11655
12398
|
}
|
|
@@ -11687,11 +12430,11 @@ function printSummary(result, graph, dashboardUrl, daemonLog) {
|
|
|
11687
12430
|
|
|
11688
12431
|
// src/cli-verbs.ts
|
|
11689
12432
|
init_cjs_shims();
|
|
11690
|
-
var
|
|
12433
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
11691
12434
|
|
|
11692
12435
|
// src/cli-client.ts
|
|
11693
12436
|
init_cjs_shims();
|
|
11694
|
-
var
|
|
12437
|
+
var import_types31 = require("@neat.is/types");
|
|
11695
12438
|
var HttpError = class extends Error {
|
|
11696
12439
|
constructor(status2, message, responseBody = "") {
|
|
11697
12440
|
super(message);
|
|
@@ -11716,10 +12459,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
11716
12459
|
const root = baseUrl.replace(/\/$/, "");
|
|
11717
12460
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
11718
12461
|
return {
|
|
11719
|
-
async get(
|
|
12462
|
+
async get(path55) {
|
|
11720
12463
|
let res;
|
|
11721
12464
|
try {
|
|
11722
|
-
res = await fetch(`${root}${
|
|
12465
|
+
res = await fetch(`${root}${path55}`, {
|
|
11723
12466
|
headers: { ...authHeader }
|
|
11724
12467
|
});
|
|
11725
12468
|
} catch (err) {
|
|
@@ -11731,16 +12474,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
11731
12474
|
const body = await res.text().catch(() => "");
|
|
11732
12475
|
throw new HttpError(
|
|
11733
12476
|
res.status,
|
|
11734
|
-
`${res.status} ${res.statusText} on GET ${
|
|
12477
|
+
`${res.status} ${res.statusText} on GET ${path55}: ${body}`,
|
|
11735
12478
|
body
|
|
11736
12479
|
);
|
|
11737
12480
|
}
|
|
11738
12481
|
return await res.json();
|
|
11739
12482
|
},
|
|
11740
|
-
async post(
|
|
12483
|
+
async post(path55, body) {
|
|
11741
12484
|
let res;
|
|
11742
12485
|
try {
|
|
11743
|
-
res = await fetch(`${root}${
|
|
12486
|
+
res = await fetch(`${root}${path55}`, {
|
|
11744
12487
|
method: "POST",
|
|
11745
12488
|
headers: { "content-type": "application/json", ...authHeader },
|
|
11746
12489
|
body: JSON.stringify(body)
|
|
@@ -11754,7 +12497,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
11754
12497
|
const text = await res.text().catch(() => "");
|
|
11755
12498
|
throw new HttpError(
|
|
11756
12499
|
res.status,
|
|
11757
|
-
`${res.status} ${res.statusText} on POST ${
|
|
12500
|
+
`${res.status} ${res.statusText} on POST ${path55}: ${text}`,
|
|
11758
12501
|
text
|
|
11759
12502
|
);
|
|
11760
12503
|
}
|
|
@@ -11768,12 +12511,12 @@ function projectPath(project, suffix) {
|
|
|
11768
12511
|
}
|
|
11769
12512
|
async function runRootCause(client, input) {
|
|
11770
12513
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
11771
|
-
const
|
|
12514
|
+
const path55 = projectPath(
|
|
11772
12515
|
input.project,
|
|
11773
12516
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
11774
12517
|
);
|
|
11775
12518
|
try {
|
|
11776
|
-
const result = await client.get(
|
|
12519
|
+
const result = await client.get(path55);
|
|
11777
12520
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
11778
12521
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
11779
12522
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -11799,12 +12542,12 @@ async function runRootCause(client, input) {
|
|
|
11799
12542
|
}
|
|
11800
12543
|
async function runBlastRadius(client, input) {
|
|
11801
12544
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
11802
|
-
const
|
|
12545
|
+
const path55 = projectPath(
|
|
11803
12546
|
input.project,
|
|
11804
12547
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
11805
12548
|
);
|
|
11806
12549
|
try {
|
|
11807
|
-
const result = await client.get(
|
|
12550
|
+
const result = await client.get(path55);
|
|
11808
12551
|
if (result.totalAffected === 0) {
|
|
11809
12552
|
return {
|
|
11810
12553
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -11833,17 +12576,17 @@ async function runBlastRadius(client, input) {
|
|
|
11833
12576
|
}
|
|
11834
12577
|
}
|
|
11835
12578
|
function formatBlastEntry(n) {
|
|
11836
|
-
const tag = n.edgeProvenance ===
|
|
12579
|
+
const tag = n.edgeProvenance === import_types31.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
11837
12580
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
11838
12581
|
}
|
|
11839
12582
|
async function runDependencies(client, input) {
|
|
11840
12583
|
const depth = input.depth ?? 3;
|
|
11841
|
-
const
|
|
12584
|
+
const path55 = projectPath(
|
|
11842
12585
|
input.project,
|
|
11843
12586
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
11844
12587
|
);
|
|
11845
12588
|
try {
|
|
11846
|
-
const result = await client.get(
|
|
12589
|
+
const result = await client.get(path55);
|
|
11847
12590
|
if (result.total === 0) {
|
|
11848
12591
|
return {
|
|
11849
12592
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -11890,7 +12633,7 @@ async function runObservedDependencies(client, input) {
|
|
|
11890
12633
|
if (result.observed) {
|
|
11891
12634
|
return {
|
|
11892
12635
|
summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
|
|
11893
|
-
provenance:
|
|
12636
|
+
provenance: import_types31.Provenance.OBSERVED
|
|
11894
12637
|
};
|
|
11895
12638
|
}
|
|
11896
12639
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -11900,7 +12643,7 @@ async function runObservedDependencies(client, input) {
|
|
|
11900
12643
|
return {
|
|
11901
12644
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
11902
12645
|
block: blockLines.join("\n"),
|
|
11903
|
-
provenance:
|
|
12646
|
+
provenance: import_types31.Provenance.OBSERVED
|
|
11904
12647
|
};
|
|
11905
12648
|
} catch (err) {
|
|
11906
12649
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -11935,9 +12678,9 @@ function formatDuration(ms) {
|
|
|
11935
12678
|
return `${Math.round(h / 24)}d`;
|
|
11936
12679
|
}
|
|
11937
12680
|
async function runIncidents(client, input) {
|
|
11938
|
-
const
|
|
12681
|
+
const path55 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
11939
12682
|
try {
|
|
11940
|
-
const body = await client.get(
|
|
12683
|
+
const body = await client.get(path55);
|
|
11941
12684
|
const events = body.events;
|
|
11942
12685
|
if (events.length === 0) {
|
|
11943
12686
|
return {
|
|
@@ -11954,7 +12697,7 @@ async function runIncidents(client, input) {
|
|
|
11954
12697
|
return {
|
|
11955
12698
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
11956
12699
|
block: blockLines.join("\n"),
|
|
11957
|
-
provenance:
|
|
12700
|
+
provenance: import_types31.Provenance.OBSERVED
|
|
11958
12701
|
};
|
|
11959
12702
|
} catch (err) {
|
|
11960
12703
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -12063,7 +12806,7 @@ async function runStaleEdges(client, input) {
|
|
|
12063
12806
|
return {
|
|
12064
12807
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
12065
12808
|
block: blockLines.join("\n"),
|
|
12066
|
-
provenance:
|
|
12809
|
+
provenance: import_types31.Provenance.STALE
|
|
12067
12810
|
};
|
|
12068
12811
|
}
|
|
12069
12812
|
async function runPolicies(client, input) {
|
|
@@ -12227,7 +12970,7 @@ async function resolveProjectEntry(opts) {
|
|
|
12227
12970
|
const cwd = opts.cwd ?? process.cwd();
|
|
12228
12971
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
12229
12972
|
for (const entry2 of entries) {
|
|
12230
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
12973
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path53.default.sep}`)) {
|
|
12231
12974
|
return entry2;
|
|
12232
12975
|
}
|
|
12233
12976
|
}
|
|
@@ -12380,7 +13123,7 @@ async function runSync(opts) {
|
|
|
12380
13123
|
}
|
|
12381
13124
|
|
|
12382
13125
|
// src/cli.ts
|
|
12383
|
-
var
|
|
13126
|
+
var import_types32 = require("@neat.is/types");
|
|
12384
13127
|
function isNpxInvocation() {
|
|
12385
13128
|
if (process.env.npm_command === "exec") return true;
|
|
12386
13129
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -12671,12 +13414,12 @@ async function runInit(opts) {
|
|
|
12671
13414
|
printDiscoveryReport(opts, services);
|
|
12672
13415
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
12673
13416
|
const patch = renderPatch(sections);
|
|
12674
|
-
const patchPath =
|
|
13417
|
+
const patchPath = import_node_path54.default.join(opts.scanPath, "neat.patch");
|
|
12675
13418
|
if (opts.dryRun) {
|
|
12676
13419
|
await import_node_fs34.promises.writeFile(patchPath, patch, "utf8");
|
|
12677
13420
|
written.push(patchPath);
|
|
12678
13421
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
12679
|
-
const gitignorePath =
|
|
13422
|
+
const gitignorePath = import_node_path54.default.join(opts.scanPath, ".gitignore");
|
|
12680
13423
|
const gitignoreExists = await import_node_fs34.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
12681
13424
|
const verb = gitignoreExists ? "append" : "create";
|
|
12682
13425
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
@@ -12688,9 +13431,9 @@ async function runInit(opts) {
|
|
|
12688
13431
|
const graph = getGraph(graphKey);
|
|
12689
13432
|
const projectPaths = pathsForProject(
|
|
12690
13433
|
graphKey,
|
|
12691
|
-
|
|
13434
|
+
import_node_path54.default.join(opts.scanPath, "neat-out")
|
|
12692
13435
|
);
|
|
12693
|
-
const errorsPath =
|
|
13436
|
+
const errorsPath = import_node_path54.default.join(import_node_path54.default.dirname(opts.outPath), import_node_path54.default.basename(projectPaths.errorsPath));
|
|
12694
13437
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
12695
13438
|
await saveGraphToDisk(graph, opts.outPath);
|
|
12696
13439
|
written.push(opts.outPath);
|
|
@@ -12809,9 +13552,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
12809
13552
|
};
|
|
12810
13553
|
function claudeConfigPath() {
|
|
12811
13554
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
12812
|
-
if (override && override.length > 0) return
|
|
13555
|
+
if (override && override.length > 0) return import_node_path54.default.resolve(override);
|
|
12813
13556
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12814
|
-
return
|
|
13557
|
+
return import_node_path54.default.join(home, ".claude.json");
|
|
12815
13558
|
}
|
|
12816
13559
|
async function runSkill(opts) {
|
|
12817
13560
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -12835,7 +13578,7 @@ async function runSkill(opts) {
|
|
|
12835
13578
|
...existing,
|
|
12836
13579
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
12837
13580
|
};
|
|
12838
|
-
await import_node_fs34.promises.mkdir(
|
|
13581
|
+
await import_node_fs34.promises.mkdir(import_node_path54.default.dirname(target), { recursive: true });
|
|
12839
13582
|
await import_node_fs34.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
12840
13583
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
12841
13584
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
@@ -12885,12 +13628,12 @@ async function main() {
|
|
|
12885
13628
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
12886
13629
|
process.exit(2);
|
|
12887
13630
|
}
|
|
12888
|
-
const scanPath =
|
|
13631
|
+
const scanPath = import_node_path54.default.resolve(target);
|
|
12889
13632
|
const projectExplicit = parsed.project !== null;
|
|
12890
|
-
const projectName = projectExplicit ? project :
|
|
13633
|
+
const projectName = projectExplicit ? project : import_node_path54.default.basename(scanPath);
|
|
12891
13634
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
12892
|
-
const fallback = pathsForProject(projectKey,
|
|
12893
|
-
const outPath =
|
|
13635
|
+
const fallback = pathsForProject(projectKey, import_node_path54.default.join(scanPath, "neat-out")).snapshotPath;
|
|
13636
|
+
const outPath = import_node_path54.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
12894
13637
|
const result = await runInit({
|
|
12895
13638
|
scanPath,
|
|
12896
13639
|
outPath,
|
|
@@ -12911,21 +13654,21 @@ async function main() {
|
|
|
12911
13654
|
usage();
|
|
12912
13655
|
process.exit(2);
|
|
12913
13656
|
}
|
|
12914
|
-
const scanPath =
|
|
13657
|
+
const scanPath = import_node_path54.default.resolve(target);
|
|
12915
13658
|
const stat = await import_node_fs34.promises.stat(scanPath).catch(() => null);
|
|
12916
13659
|
if (!stat || !stat.isDirectory()) {
|
|
12917
13660
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
12918
13661
|
process.exit(2);
|
|
12919
13662
|
}
|
|
12920
|
-
const projectPaths = pathsForProject(project,
|
|
12921
|
-
const outPath =
|
|
12922
|
-
const errorsPath =
|
|
12923
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
13663
|
+
const projectPaths = pathsForProject(project, import_node_path54.default.join(scanPath, "neat-out"));
|
|
13664
|
+
const outPath = import_node_path54.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
13665
|
+
const errorsPath = import_node_path54.default.resolve(
|
|
13666
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path54.default.join(import_node_path54.default.dirname(outPath), import_node_path54.default.basename(projectPaths.errorsPath))
|
|
12924
13667
|
);
|
|
12925
|
-
const staleEventsPath =
|
|
12926
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
13668
|
+
const staleEventsPath = import_node_path54.default.resolve(
|
|
13669
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path54.default.join(import_node_path54.default.dirname(outPath), import_node_path54.default.basename(projectPaths.staleEventsPath))
|
|
12927
13670
|
);
|
|
12928
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
13671
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path54.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
12929
13672
|
const handle = await startWatch(getGraph(project), {
|
|
12930
13673
|
scanPath,
|
|
12931
13674
|
outPath,
|
|
@@ -13108,11 +13851,11 @@ async function main() {
|
|
|
13108
13851
|
process.exit(1);
|
|
13109
13852
|
}
|
|
13110
13853
|
async function tryOrchestrator(cmd, parsed) {
|
|
13111
|
-
const scanPath =
|
|
13854
|
+
const scanPath = import_node_path54.default.resolve(cmd);
|
|
13112
13855
|
const stat = await import_node_fs34.promises.stat(scanPath).catch(() => null);
|
|
13113
13856
|
if (!stat || !stat.isDirectory()) return null;
|
|
13114
13857
|
const projectExplicit = parsed.project !== null;
|
|
13115
|
-
const projectName = projectExplicit ? parsed.project :
|
|
13858
|
+
const projectName = projectExplicit ? parsed.project : import_node_path54.default.basename(scanPath);
|
|
13116
13859
|
const result = await runOrchestrator({
|
|
13117
13860
|
scanPath,
|
|
13118
13861
|
project: projectName,
|
|
@@ -13301,10 +14044,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
13301
14044
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
13302
14045
|
const out = [];
|
|
13303
14046
|
for (const p of parts) {
|
|
13304
|
-
const r =
|
|
14047
|
+
const r = import_types32.DivergenceTypeSchema.safeParse(p);
|
|
13305
14048
|
if (!r.success) {
|
|
13306
14049
|
console.error(
|
|
13307
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
14050
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types32.DivergenceTypeSchema.options.join(", ")}`
|
|
13308
14051
|
);
|
|
13309
14052
|
return 2;
|
|
13310
14053
|
}
|