@neat.is/core 0.6.1 → 0.6.2-dev.20260722
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BZ3AJVAC.js → chunk-2MAGU6RB.js} +11 -2
- package/dist/chunk-2MAGU6RB.js.map +1 -0
- package/dist/{chunk-3YUNROLT.js → chunk-64JJOXES.js} +3 -3
- package/dist/{chunk-MVINCLQM.js → chunk-OY7FGJAX.js} +2 -2
- package/dist/{chunk-7C6ZULNU.js → chunk-ZZ3VUCWL.js} +1492 -1346
- package/dist/chunk-ZZ3VUCWL.js.map +1 -0
- package/dist/cli.cjs +617 -461
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +466 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +475 -319
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-XS45HBET.js → otel-grpc-AHNPM5LX.js} +3 -3
- package/dist/server.cjs +2400 -2240
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-7C6ZULNU.js.map +0 -1
- package/dist/chunk-BZ3AJVAC.js.map +0 -1
- /package/dist/{chunk-3YUNROLT.js.map → chunk-64JJOXES.js.map} +0 -0
- /package/dist/{chunk-MVINCLQM.js.map → chunk-OY7FGJAX.js.map} +0 -0
- /package/dist/{otel-grpc-XS45HBET.js.map → otel-grpc-AHNPM5LX.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path64 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path64) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path64)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path51.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path51.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url2,
|
|
266
|
+
var import_node_url2, import_node_path51, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url2 = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path51 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -347,6 +347,14 @@ function normalizeDbSystem(attrs) {
|
|
|
347
347
|
if (typeof raw !== "string") return void 0;
|
|
348
348
|
return raw === "mongoose" ? "mongodb" : raw;
|
|
349
349
|
}
|
|
350
|
+
function tableFromSqlStatement(sql) {
|
|
351
|
+
if (typeof sql !== "string" || sql.length === 0) return null;
|
|
352
|
+
if (/\bjoin\b/i.test(sql)) return null;
|
|
353
|
+
const froms = sql.match(/\bfrom\b/gi);
|
|
354
|
+
if (froms && froms.length > 1) return null;
|
|
355
|
+
const m = /\b(?:from|into|update)\s+(?:"?[\w$]+"?\s*\.\s*)?"?([a-zA-Z_][\w$]*)"?/i.exec(sql);
|
|
356
|
+
return m ? m[1] : null;
|
|
357
|
+
}
|
|
350
358
|
function messagingDestinationOf(attrs) {
|
|
351
359
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
352
360
|
const v = attrs[key];
|
|
@@ -367,8 +375,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
367
375
|
const v = attrs[key];
|
|
368
376
|
if (typeof v === "string" && v.length > 0) {
|
|
369
377
|
const q = v.indexOf("?");
|
|
370
|
-
const
|
|
371
|
-
if (
|
|
378
|
+
const path64 = q === -1 ? v : v.slice(0, q);
|
|
379
|
+
if (path64.length > 0) return path64;
|
|
372
380
|
}
|
|
373
381
|
}
|
|
374
382
|
return void 0;
|
|
@@ -404,6 +412,7 @@ function parseOtlpRequest(body) {
|
|
|
404
412
|
dbSystem: normalizeDbSystem(attrs),
|
|
405
413
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
406
414
|
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
415
|
+
dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
|
|
407
416
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
408
417
|
messagingDestination: messagingDestinationOf(attrs),
|
|
409
418
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -423,10 +432,10 @@ function parseOtlpRequest(body) {
|
|
|
423
432
|
return out;
|
|
424
433
|
}
|
|
425
434
|
function loadProtoRoot() {
|
|
426
|
-
const here =
|
|
427
|
-
const protoRoot =
|
|
435
|
+
const here = import_node_path52.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
436
|
+
const protoRoot = import_node_path52.default.resolve(here, "..", "proto");
|
|
428
437
|
const root = new import_protobufjs.default.Root();
|
|
429
|
-
root.resolvePath = (_origin, target) =>
|
|
438
|
+
root.resolvePath = (_origin, target) => import_node_path52.default.resolve(protoRoot, target);
|
|
430
439
|
root.loadSync(
|
|
431
440
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
432
441
|
{ keepCase: true }
|
|
@@ -654,12 +663,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
654
663
|
}
|
|
655
664
|
}
|
|
656
665
|
}
|
|
657
|
-
var
|
|
666
|
+
var import_node_path52, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
658
667
|
var init_otel = __esm({
|
|
659
668
|
"src/otel.ts"() {
|
|
660
669
|
"use strict";
|
|
661
670
|
init_cjs_shims();
|
|
662
|
-
|
|
671
|
+
import_node_path52 = __toESM(require("path"), 1);
|
|
663
672
|
import_node_url3 = require("url");
|
|
664
673
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
665
674
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -696,7 +705,7 @@ __export(cli_exports, {
|
|
|
696
705
|
});
|
|
697
706
|
module.exports = __toCommonJS(cli_exports);
|
|
698
707
|
init_cjs_shims();
|
|
699
|
-
var
|
|
708
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
700
709
|
var import_node_fs41 = require("fs");
|
|
701
710
|
|
|
702
711
|
// src/banner.ts
|
|
@@ -1262,19 +1271,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1262
1271
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1263
1272
|
let best = { path: [start], edges: [] };
|
|
1264
1273
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1265
|
-
function step(node,
|
|
1266
|
-
if (
|
|
1267
|
-
best = { path: [...
|
|
1274
|
+
function step(node, path64, edges) {
|
|
1275
|
+
if (path64.length > best.path.length) {
|
|
1276
|
+
best = { path: [...path64], edges: [...edges] };
|
|
1268
1277
|
}
|
|
1269
|
-
if (
|
|
1278
|
+
if (path64.length - 1 >= maxDepth) return;
|
|
1270
1279
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1271
1280
|
for (const [srcId, edge] of incoming) {
|
|
1272
1281
|
if (visited.has(srcId)) continue;
|
|
1273
1282
|
visited.add(srcId);
|
|
1274
|
-
|
|
1283
|
+
path64.push(srcId);
|
|
1275
1284
|
edges.push(edge);
|
|
1276
|
-
step(srcId,
|
|
1277
|
-
|
|
1285
|
+
step(srcId, path64, edges);
|
|
1286
|
+
path64.pop();
|
|
1278
1287
|
edges.pop();
|
|
1279
1288
|
visited.delete(srcId);
|
|
1280
1289
|
}
|
|
@@ -1282,11 +1291,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
1282
1291
|
step(start, [start], []);
|
|
1283
1292
|
return best;
|
|
1284
1293
|
}
|
|
1285
|
-
function databaseRootCauseShape(graph, origin,
|
|
1294
|
+
function databaseRootCauseShape(graph, origin, walk4) {
|
|
1286
1295
|
const targetDb = origin;
|
|
1287
1296
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
1288
1297
|
if (candidatePairs.length === 0) return null;
|
|
1289
|
-
for (const id of
|
|
1298
|
+
for (const id of walk4.path) {
|
|
1290
1299
|
const owner = resolveOwningService(graph, id);
|
|
1291
1300
|
if (!owner) continue;
|
|
1292
1301
|
const { id: serviceId6, svc } = owner;
|
|
@@ -1313,8 +1322,8 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1313
1322
|
}
|
|
1314
1323
|
return null;
|
|
1315
1324
|
}
|
|
1316
|
-
function serviceRootCauseShape(graph, _origin,
|
|
1317
|
-
for (const id of
|
|
1325
|
+
function serviceRootCauseShape(graph, _origin, walk4) {
|
|
1326
|
+
for (const id of walk4.path) {
|
|
1318
1327
|
const owner = resolveOwningService(graph, id);
|
|
1319
1328
|
if (!owner) continue;
|
|
1320
1329
|
const { id: serviceId6, svc } = owner;
|
|
@@ -1350,10 +1359,10 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1350
1359
|
}
|
|
1351
1360
|
return null;
|
|
1352
1361
|
}
|
|
1353
|
-
function fileRootCauseShape(graph, origin,
|
|
1362
|
+
function fileRootCauseShape(graph, origin, walk4) {
|
|
1354
1363
|
const owner = resolveOwningService(graph, origin.id);
|
|
1355
1364
|
if (!owner) return null;
|
|
1356
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
1365
|
+
return serviceRootCauseShape(graph, owner.svc, walk4);
|
|
1357
1366
|
}
|
|
1358
1367
|
var rootCauseShapes = {
|
|
1359
1368
|
[import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -1365,16 +1374,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
1365
1374
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
1366
1375
|
const shape = rootCauseShapes[origin.type];
|
|
1367
1376
|
if (shape) {
|
|
1368
|
-
const
|
|
1369
|
-
const match = shape(graph, origin,
|
|
1377
|
+
const walk4 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
1378
|
+
const match = shape(graph, origin, walk4);
|
|
1370
1379
|
if (match) {
|
|
1371
1380
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
1372
1381
|
return import_types.RootCauseResultSchema.parse({
|
|
1373
1382
|
rootCauseNode: match.rootCauseNode,
|
|
1374
1383
|
rootCauseReason: reason,
|
|
1375
|
-
traversalPath:
|
|
1376
|
-
edgeProvenances:
|
|
1377
|
-
confidence: confidenceFromMix(
|
|
1384
|
+
traversalPath: walk4.path,
|
|
1385
|
+
edgeProvenances: walk4.edges.map((e) => e.provenance),
|
|
1386
|
+
confidence: confidenceFromMix(walk4.edges),
|
|
1378
1387
|
fixRecommendation: match.fixRecommendation
|
|
1379
1388
|
});
|
|
1380
1389
|
}
|
|
@@ -1395,8 +1404,8 @@ function localizeFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1395
1404
|
if (relevant.length === 0) return null;
|
|
1396
1405
|
const latest = [...relevant].sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
1397
1406
|
const attrs = latest.attributes ?? {};
|
|
1398
|
-
const filepath =
|
|
1399
|
-
const lineno =
|
|
1407
|
+
const filepath = codeFilepathOf(attrs);
|
|
1408
|
+
const lineno = codeLinenoOf(attrs);
|
|
1400
1409
|
const route = typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0;
|
|
1401
1410
|
const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
|
|
1402
1411
|
const sameMode = relevant.filter((ev) => ev.errorMessage === latest.errorMessage);
|
|
@@ -1468,26 +1477,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1468
1477
|
return best;
|
|
1469
1478
|
}
|
|
1470
1479
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1471
|
-
const
|
|
1480
|
+
const path64 = [originServiceId];
|
|
1472
1481
|
const edges = [];
|
|
1473
1482
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1474
1483
|
let current = originServiceId;
|
|
1475
1484
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1476
1485
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1477
1486
|
if (!hop) break;
|
|
1478
|
-
|
|
1487
|
+
path64.push(hop.nextService);
|
|
1479
1488
|
edges.push(hop.edge);
|
|
1480
1489
|
visited.add(hop.nextService);
|
|
1481
1490
|
current = hop.nextService;
|
|
1482
1491
|
}
|
|
1483
1492
|
if (edges.length === 0) return null;
|
|
1484
|
-
return { path:
|
|
1493
|
+
return { path: path64, edges, culprit: current };
|
|
1485
1494
|
}
|
|
1486
1495
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1487
1496
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1488
1497
|
if (!chain) return null;
|
|
1489
1498
|
const culprit = chain.culprit;
|
|
1490
|
-
const
|
|
1499
|
+
const path64 = [...chain.path];
|
|
1491
1500
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1492
1501
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1493
1502
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1495,14 +1504,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1495
1504
|
if (loc) {
|
|
1496
1505
|
let rootCauseNode = culprit;
|
|
1497
1506
|
if (loc.fileNode) {
|
|
1498
|
-
|
|
1507
|
+
path64.push(loc.fileNode);
|
|
1499
1508
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1500
1509
|
rootCauseNode = loc.fileNode;
|
|
1501
1510
|
}
|
|
1502
1511
|
return import_types.RootCauseResultSchema.parse({
|
|
1503
1512
|
rootCauseNode,
|
|
1504
1513
|
rootCauseReason: loc.rootCauseReason,
|
|
1505
|
-
traversalPath:
|
|
1514
|
+
traversalPath: path64,
|
|
1506
1515
|
edgeProvenances,
|
|
1507
1516
|
confidence,
|
|
1508
1517
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1514,7 +1523,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1514
1523
|
return import_types.RootCauseResultSchema.parse({
|
|
1515
1524
|
rootCauseNode: culprit,
|
|
1516
1525
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1517
|
-
traversalPath:
|
|
1526
|
+
traversalPath: path64,
|
|
1518
1527
|
edgeProvenances,
|
|
1519
1528
|
confidence,
|
|
1520
1529
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2272,9 +2281,30 @@ function isLoopbackHost(host) {
|
|
|
2272
2281
|
const h = host.toLowerCase();
|
|
2273
2282
|
return h === "localhost" || h === "ip6-localhost" || h === "::1" || h === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(h);
|
|
2274
2283
|
}
|
|
2284
|
+
var CODE_FILE_PATH_ATTR = "code.file.path";
|
|
2275
2285
|
var CODE_FILEPATH_ATTR = "code.filepath";
|
|
2286
|
+
var CODE_LINE_NUMBER_ATTR = "code.line.number";
|
|
2276
2287
|
var CODE_LINENO_ATTR = "code.lineno";
|
|
2288
|
+
var CODE_FUNCTION_NAME_ATTR = "code.function.name";
|
|
2277
2289
|
var CODE_FUNCTION_ATTR = "code.function";
|
|
2290
|
+
function codeFilepathOf(attrs) {
|
|
2291
|
+
const stable = attrs[CODE_FILE_PATH_ATTR];
|
|
2292
|
+
if (typeof stable === "string" && stable.length > 0) return stable;
|
|
2293
|
+
const prior = attrs[CODE_FILEPATH_ATTR];
|
|
2294
|
+
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
2295
|
+
}
|
|
2296
|
+
function codeLinenoOf(attrs) {
|
|
2297
|
+
const stable = attrs[CODE_LINE_NUMBER_ATTR];
|
|
2298
|
+
if (typeof stable === "number" && Number.isFinite(stable)) return stable;
|
|
2299
|
+
const prior = attrs[CODE_LINENO_ATTR];
|
|
2300
|
+
return typeof prior === "number" && Number.isFinite(prior) ? prior : void 0;
|
|
2301
|
+
}
|
|
2302
|
+
function codeFunctionOf(attrs) {
|
|
2303
|
+
const stable = attrs[CODE_FUNCTION_NAME_ATTR];
|
|
2304
|
+
if (typeof stable === "string" && stable.length > 0) return stable;
|
|
2305
|
+
const prior = attrs[CODE_FUNCTION_ATTR];
|
|
2306
|
+
return typeof prior === "string" && prior.length > 0 ? prior : void 0;
|
|
2307
|
+
}
|
|
2278
2308
|
function toPosix(p) {
|
|
2279
2309
|
return p.split("\\").join("/");
|
|
2280
2310
|
}
|
|
@@ -2352,10 +2382,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
2352
2382
|
}
|
|
2353
2383
|
}
|
|
2354
2384
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
2355
|
-
const filepath = span.attributes
|
|
2356
|
-
if (
|
|
2357
|
-
|
|
2358
|
-
let line = typeof linenoRaw === "number" && Number.isFinite(linenoRaw) ? linenoRaw : void 0;
|
|
2385
|
+
const filepath = codeFilepathOf(span.attributes);
|
|
2386
|
+
if (filepath === void 0) return null;
|
|
2387
|
+
let line = codeLinenoOf(span.attributes);
|
|
2359
2388
|
const abs = toPosix(filepath).replace(/^file:\/\//, "");
|
|
2360
2389
|
const resolved = resolveDistToSrc(abs, line);
|
|
2361
2390
|
let effectivePath = filepath;
|
|
@@ -2370,8 +2399,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
2370
2399
|
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
2371
2400
|
warnNoSourceMaps(serviceNode.name);
|
|
2372
2401
|
}
|
|
2373
|
-
const
|
|
2374
|
-
const fn = typeof fnRaw === "string" && fnRaw.length > 0 ? fnRaw : void 0;
|
|
2402
|
+
const fn = codeFunctionOf(span.attributes);
|
|
2375
2403
|
return {
|
|
2376
2404
|
relPath,
|
|
2377
2405
|
...line !== void 0 ? { line } : {},
|
|
@@ -2953,6 +2981,18 @@ async function handleSpan(ctx, span) {
|
|
|
2953
2981
|
callSiteEvidence
|
|
2954
2982
|
);
|
|
2955
2983
|
}
|
|
2984
|
+
if (span.dbTable) {
|
|
2985
|
+
const tableId = ensureInfraNode(ctx.graph, "sql-table", span.dbTable, "self");
|
|
2986
|
+
upsertObservedEdge(
|
|
2987
|
+
ctx.graph,
|
|
2988
|
+
import_types3.EdgeType.CALLS,
|
|
2989
|
+
observedSource(),
|
|
2990
|
+
tableId,
|
|
2991
|
+
ts,
|
|
2992
|
+
isError,
|
|
2993
|
+
callSiteEvidence
|
|
2994
|
+
);
|
|
2995
|
+
}
|
|
2956
2996
|
}
|
|
2957
2997
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2958
2998
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -4132,14 +4172,14 @@ function buildServiceHostIndex(services) {
|
|
|
4132
4172
|
}
|
|
4133
4173
|
async function walkSourceFiles(dir) {
|
|
4134
4174
|
const out = [];
|
|
4135
|
-
async function
|
|
4175
|
+
async function walk4(current) {
|
|
4136
4176
|
const entries = await import_node_fs11.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
4137
4177
|
for (const entry2 of entries) {
|
|
4138
4178
|
const full = import_node_path11.default.join(current, entry2.name);
|
|
4139
4179
|
if (entry2.isDirectory()) {
|
|
4140
4180
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
4141
4181
|
if (await isPythonVenvDir(full)) continue;
|
|
4142
|
-
await
|
|
4182
|
+
await walk4(full);
|
|
4143
4183
|
} 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
|
|
4144
4184
|
// would attribute our instrumentation imports to the user's service.
|
|
4145
4185
|
!isNeatAuthoredSourceFile(entry2.name)) {
|
|
@@ -4147,7 +4187,7 @@ async function walkSourceFiles(dir) {
|
|
|
4147
4187
|
}
|
|
4148
4188
|
}
|
|
4149
4189
|
}
|
|
4150
|
-
await
|
|
4190
|
+
await walk4(dir);
|
|
4151
4191
|
return out;
|
|
4152
4192
|
}
|
|
4153
4193
|
async function loadSourceFiles(dir) {
|
|
@@ -5297,20 +5337,20 @@ var import_node_path22 = __toESM(require("path"), 1);
|
|
|
5297
5337
|
var import_types10 = require("@neat.is/types");
|
|
5298
5338
|
async function walkConfigFiles(dir) {
|
|
5299
5339
|
const out = [];
|
|
5300
|
-
async function
|
|
5340
|
+
async function walk4(current) {
|
|
5301
5341
|
const entries = await import_node_fs15.promises.readdir(current, { withFileTypes: true });
|
|
5302
5342
|
for (const entry2 of entries) {
|
|
5303
5343
|
const full = import_node_path22.default.join(current, entry2.name);
|
|
5304
5344
|
if (entry2.isDirectory()) {
|
|
5305
5345
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
5306
5346
|
if (await isPythonVenvDir(full)) continue;
|
|
5307
|
-
await
|
|
5347
|
+
await walk4(full);
|
|
5308
5348
|
} else if (entry2.isFile() && isConfigFile(entry2.name).match) {
|
|
5309
5349
|
out.push(full);
|
|
5310
5350
|
}
|
|
5311
5351
|
}
|
|
5312
5352
|
}
|
|
5313
|
-
await
|
|
5353
|
+
await walk4(dir);
|
|
5314
5354
|
return out;
|
|
5315
5355
|
}
|
|
5316
5356
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -5837,20 +5877,20 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
5837
5877
|
}
|
|
5838
5878
|
async function walkProtoFiles(dir) {
|
|
5839
5879
|
const out = [];
|
|
5840
|
-
async function
|
|
5880
|
+
async function walk4(current) {
|
|
5841
5881
|
const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
5842
5882
|
for (const entry2 of entries) {
|
|
5843
5883
|
const full = import_node_path24.default.join(current, entry2.name);
|
|
5844
5884
|
if (entry2.isDirectory()) {
|
|
5845
5885
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
5846
5886
|
if (await isPythonVenvDir(full)) continue;
|
|
5847
|
-
await
|
|
5887
|
+
await walk4(full);
|
|
5848
5888
|
} else if (entry2.isFile() && import_node_path24.default.extname(entry2.name) === PROTO_EXTENSION) {
|
|
5849
5889
|
out.push(full);
|
|
5850
5890
|
}
|
|
5851
5891
|
}
|
|
5852
5892
|
}
|
|
5853
|
-
await
|
|
5893
|
+
await walk4(dir);
|
|
5854
5894
|
return out;
|
|
5855
5895
|
}
|
|
5856
5896
|
async function addGrpcMethods(graph, services) {
|
|
@@ -5918,7 +5958,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5918
5958
|
|
|
5919
5959
|
// src/extract/calls/index.ts
|
|
5920
5960
|
init_cjs_shims();
|
|
5921
|
-
var
|
|
5961
|
+
var import_types22 = require("@neat.is/types");
|
|
5922
5962
|
|
|
5923
5963
|
// src/extract/calls/http.ts
|
|
5924
5964
|
init_cjs_shims();
|
|
@@ -6927,15 +6967,130 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
|
6927
6967
|
return out;
|
|
6928
6968
|
}
|
|
6929
6969
|
|
|
6970
|
+
// src/extract/calls/sqlalchemy.ts
|
|
6971
|
+
init_cjs_shims();
|
|
6972
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6973
|
+
var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
|
|
6974
|
+
var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
6975
|
+
var import_types21 = require("@neat.is/types");
|
|
6976
|
+
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
6977
|
+
var PARSE_CHUNK5 = 16384;
|
|
6978
|
+
function makePyParser4() {
|
|
6979
|
+
const p = new import_tree_sitter5.default();
|
|
6980
|
+
p.setLanguage(import_tree_sitter_python4.default);
|
|
6981
|
+
return p;
|
|
6982
|
+
}
|
|
6983
|
+
function parseSource5(parser, source) {
|
|
6984
|
+
return parser.parse(
|
|
6985
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
6986
|
+
);
|
|
6987
|
+
}
|
|
6988
|
+
function flaskSqlalchemyTableName(className) {
|
|
6989
|
+
return className.replace(/((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))/g, "_$1").toLowerCase().replace(/^_+/, "");
|
|
6990
|
+
}
|
|
6991
|
+
function namedChildren(node) {
|
|
6992
|
+
const out = [];
|
|
6993
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6994
|
+
const c = node.namedChild(i);
|
|
6995
|
+
if (c) out.push(c);
|
|
6996
|
+
}
|
|
6997
|
+
return out;
|
|
6998
|
+
}
|
|
6999
|
+
function pyStaticStringText2(node) {
|
|
7000
|
+
if (node.type !== "string") return null;
|
|
7001
|
+
for (const child of namedChildren(node)) {
|
|
7002
|
+
if (child.type === "interpolation") return null;
|
|
7003
|
+
if (child.type === "string_content") return child.text;
|
|
7004
|
+
}
|
|
7005
|
+
return "";
|
|
7006
|
+
}
|
|
7007
|
+
function explicitTablename(body) {
|
|
7008
|
+
for (const stmt of namedChildren(body)) {
|
|
7009
|
+
if (stmt.type !== "expression_statement") continue;
|
|
7010
|
+
const assign = stmt.namedChild(0);
|
|
7011
|
+
if (assign?.type !== "assignment") continue;
|
|
7012
|
+
if (assign.childForFieldName("left")?.text !== "__tablename__") continue;
|
|
7013
|
+
const right = assign.childForFieldName("right");
|
|
7014
|
+
if (right?.type === "string") {
|
|
7015
|
+
const s = pyStaticStringText2(right);
|
|
7016
|
+
return s ? { name: s } : "computed";
|
|
7017
|
+
}
|
|
7018
|
+
return "computed";
|
|
7019
|
+
}
|
|
7020
|
+
return null;
|
|
7021
|
+
}
|
|
7022
|
+
function extendsFlaskModel(cls) {
|
|
7023
|
+
const supers = cls.childForFieldName("superclasses");
|
|
7024
|
+
if (!supers) return false;
|
|
7025
|
+
for (const a of namedChildren(supers)) {
|
|
7026
|
+
const t = a.text;
|
|
7027
|
+
if (t === "db.Model" || t === "Model" || t.endsWith(".Model")) return true;
|
|
7028
|
+
}
|
|
7029
|
+
return false;
|
|
7030
|
+
}
|
|
7031
|
+
function walk3(node, visit) {
|
|
7032
|
+
visit(node);
|
|
7033
|
+
for (const c of namedChildren(node)) walk3(c, visit);
|
|
7034
|
+
}
|
|
7035
|
+
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
7036
|
+
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
7037
|
+
const tree = parseSource5(makePyParser4(), file.content);
|
|
7038
|
+
const out = [];
|
|
7039
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7040
|
+
const push = (name, line) => {
|
|
7041
|
+
if (seen.has(name)) return;
|
|
7042
|
+
seen.add(name);
|
|
7043
|
+
out.push({
|
|
7044
|
+
infraId: (0, import_types21.infraId)("sql-table", name),
|
|
7045
|
+
name,
|
|
7046
|
+
kind: "sql-table",
|
|
7047
|
+
edgeType: "CALLS",
|
|
7048
|
+
confidenceKind: "verified-call-site",
|
|
7049
|
+
evidence: {
|
|
7050
|
+
file: import_node_path33.default.relative(serviceDir, file.path),
|
|
7051
|
+
line,
|
|
7052
|
+
snippet: snippet(file.content, line)
|
|
7053
|
+
}
|
|
7054
|
+
});
|
|
7055
|
+
};
|
|
7056
|
+
walk3(tree.rootNode, (node) => {
|
|
7057
|
+
if (node.type === "class_definition") {
|
|
7058
|
+
const body = node.childForFieldName("body");
|
|
7059
|
+
const nameNode = node.childForFieldName("name");
|
|
7060
|
+
if (!body || !nameNode) return;
|
|
7061
|
+
const line = node.startPosition.row + 1;
|
|
7062
|
+
const explicit = explicitTablename(body);
|
|
7063
|
+
if (explicit === "computed") return;
|
|
7064
|
+
if (explicit) {
|
|
7065
|
+
push(explicit.name, line);
|
|
7066
|
+
return;
|
|
7067
|
+
}
|
|
7068
|
+
if (extendsFlaskModel(node)) push(flaskSqlalchemyTableName(nameNode.text), line);
|
|
7069
|
+
return;
|
|
7070
|
+
}
|
|
7071
|
+
if (node.type === "call") {
|
|
7072
|
+
const fn = node.childForFieldName("function");
|
|
7073
|
+
const fnText = fn?.text;
|
|
7074
|
+
if (fnText !== "Table" && fnText !== "sa.Table" && fnText !== "sqlalchemy.Table") return;
|
|
7075
|
+
const first = node.childForFieldName("arguments")?.namedChild(0);
|
|
7076
|
+
if (first?.type === "string") {
|
|
7077
|
+
const s = pyStaticStringText2(first);
|
|
7078
|
+
if (s) push(s, node.startPosition.row + 1);
|
|
7079
|
+
}
|
|
7080
|
+
}
|
|
7081
|
+
});
|
|
7082
|
+
return out;
|
|
7083
|
+
}
|
|
7084
|
+
|
|
6930
7085
|
// src/extract/calls/index.ts
|
|
6931
7086
|
function edgeTypeFromEndpoint(ep) {
|
|
6932
7087
|
switch (ep.edgeType) {
|
|
6933
7088
|
case "PUBLISHES_TO":
|
|
6934
|
-
return
|
|
7089
|
+
return import_types22.EdgeType.PUBLISHES_TO;
|
|
6935
7090
|
case "CONSUMES_FROM":
|
|
6936
|
-
return
|
|
7091
|
+
return import_types22.EdgeType.CONSUMES_FROM;
|
|
6937
7092
|
default:
|
|
6938
|
-
return
|
|
7093
|
+
return import_types22.EdgeType.CALLS;
|
|
6939
7094
|
}
|
|
6940
7095
|
}
|
|
6941
7096
|
function isAwsKind(kind) {
|
|
@@ -6959,6 +7114,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6959
7114
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6960
7115
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6961
7116
|
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
7117
|
+
endpoints.push(...sqlalchemyEndpointsFromFile(maskedFile, service.dir));
|
|
6962
7118
|
}
|
|
6963
7119
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6964
7120
|
if (endpoints.length === 0) continue;
|
|
@@ -6967,7 +7123,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6967
7123
|
if (!graph.hasNode(ep.infraId)) {
|
|
6968
7124
|
const node = {
|
|
6969
7125
|
id: ep.infraId,
|
|
6970
|
-
type:
|
|
7126
|
+
type: import_types22.NodeType.InfraNode,
|
|
6971
7127
|
name: ep.name,
|
|
6972
7128
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6973
7129
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6979,7 +7135,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6979
7135
|
nodesAdded++;
|
|
6980
7136
|
}
|
|
6981
7137
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6982
|
-
const confidence = (0,
|
|
7138
|
+
const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
|
|
6983
7139
|
const relFile = toPosix2(ep.evidence.file);
|
|
6984
7140
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6985
7141
|
graph,
|
|
@@ -6989,7 +7145,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6989
7145
|
);
|
|
6990
7146
|
nodesAdded += n;
|
|
6991
7147
|
edgesAdded += e;
|
|
6992
|
-
if (!(0,
|
|
7148
|
+
if (!(0, import_types22.passesExtractedFloor)(confidence)) {
|
|
6993
7149
|
noteExtractedDropped({
|
|
6994
7150
|
source: fileNodeId,
|
|
6995
7151
|
target: ep.infraId,
|
|
@@ -7009,7 +7165,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
7009
7165
|
source: fileNodeId,
|
|
7010
7166
|
target: ep.infraId,
|
|
7011
7167
|
type: edgeType,
|
|
7012
|
-
provenance:
|
|
7168
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
7013
7169
|
confidence,
|
|
7014
7170
|
evidence: ep.evidence
|
|
7015
7171
|
};
|
|
@@ -7035,16 +7191,16 @@ init_cjs_shims();
|
|
|
7035
7191
|
|
|
7036
7192
|
// src/extract/infra/docker-compose.ts
|
|
7037
7193
|
init_cjs_shims();
|
|
7038
|
-
var
|
|
7039
|
-
var
|
|
7194
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7195
|
+
var import_types24 = require("@neat.is/types");
|
|
7040
7196
|
|
|
7041
7197
|
// src/extract/infra/shared.ts
|
|
7042
7198
|
init_cjs_shims();
|
|
7043
|
-
var
|
|
7199
|
+
var import_types23 = require("@neat.is/types");
|
|
7044
7200
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
7045
7201
|
return {
|
|
7046
|
-
id: (0,
|
|
7047
|
-
type:
|
|
7202
|
+
id: (0, import_types23.infraId)(kind, name),
|
|
7203
|
+
type: import_types23.NodeType.InfraNode,
|
|
7048
7204
|
name,
|
|
7049
7205
|
provider,
|
|
7050
7206
|
kind,
|
|
@@ -7088,8 +7244,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
7088
7244
|
source: anchorId,
|
|
7089
7245
|
target: node.id,
|
|
7090
7246
|
type: edgeType,
|
|
7091
|
-
provenance:
|
|
7092
|
-
confidence: (0,
|
|
7247
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7248
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
7093
7249
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7094
7250
|
};
|
|
7095
7251
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7106,7 +7262,7 @@ function dependsOnList(value) {
|
|
|
7106
7262
|
}
|
|
7107
7263
|
function serviceNameToServiceNode(name, services) {
|
|
7108
7264
|
for (const s of services) {
|
|
7109
|
-
if (s.node.name === name ||
|
|
7265
|
+
if (s.node.name === name || import_node_path34.default.basename(s.dir) === name) return s.node.id;
|
|
7110
7266
|
}
|
|
7111
7267
|
return null;
|
|
7112
7268
|
}
|
|
@@ -7115,7 +7271,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7115
7271
|
let edgesAdded = 0;
|
|
7116
7272
|
let composePath = null;
|
|
7117
7273
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
7118
|
-
const abs =
|
|
7274
|
+
const abs = import_node_path34.default.join(scanPath, name);
|
|
7119
7275
|
if (await exists(abs)) {
|
|
7120
7276
|
composePath = abs;
|
|
7121
7277
|
break;
|
|
@@ -7128,13 +7284,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7128
7284
|
} catch (err) {
|
|
7129
7285
|
recordExtractionError(
|
|
7130
7286
|
"infra docker-compose",
|
|
7131
|
-
|
|
7287
|
+
import_node_path34.default.relative(scanPath, composePath),
|
|
7132
7288
|
err
|
|
7133
7289
|
);
|
|
7134
7290
|
return { nodesAdded, edgesAdded };
|
|
7135
7291
|
}
|
|
7136
7292
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
7137
|
-
const evidenceFile =
|
|
7293
|
+
const evidenceFile = import_node_path34.default.relative(scanPath, composePath).split(import_node_path34.default.sep).join("/");
|
|
7138
7294
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
7139
7295
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
7140
7296
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -7156,15 +7312,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7156
7312
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
7157
7313
|
const targetId = composeNameToNodeId.get(dep);
|
|
7158
7314
|
if (!targetId) continue;
|
|
7159
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7315
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
|
|
7160
7316
|
if (graph.hasEdge(edgeId)) continue;
|
|
7161
7317
|
const edge = {
|
|
7162
7318
|
id: edgeId,
|
|
7163
7319
|
source: sourceId,
|
|
7164
7320
|
target: targetId,
|
|
7165
|
-
type:
|
|
7166
|
-
provenance:
|
|
7167
|
-
confidence: (0,
|
|
7321
|
+
type: import_types24.EdgeType.DEPENDS_ON,
|
|
7322
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7323
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
7168
7324
|
evidence: { file: evidenceFile }
|
|
7169
7325
|
};
|
|
7170
7326
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7176,9 +7332,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7176
7332
|
|
|
7177
7333
|
// src/extract/infra/dockerfile.ts
|
|
7178
7334
|
init_cjs_shims();
|
|
7179
|
-
var
|
|
7335
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7180
7336
|
var import_node_fs17 = require("fs");
|
|
7181
|
-
var
|
|
7337
|
+
var import_types25 = require("@neat.is/types");
|
|
7182
7338
|
function readDockerfile(content) {
|
|
7183
7339
|
let image = null;
|
|
7184
7340
|
const ports = [];
|
|
@@ -7207,7 +7363,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7207
7363
|
let nodesAdded = 0;
|
|
7208
7364
|
let edgesAdded = 0;
|
|
7209
7365
|
for (const service of services) {
|
|
7210
|
-
const dockerfilePath =
|
|
7366
|
+
const dockerfilePath = import_node_path35.default.join(service.dir, "Dockerfile");
|
|
7211
7367
|
if (!await exists(dockerfilePath)) continue;
|
|
7212
7368
|
let content;
|
|
7213
7369
|
try {
|
|
@@ -7215,7 +7371,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7215
7371
|
} catch (err) {
|
|
7216
7372
|
recordExtractionError(
|
|
7217
7373
|
"infra dockerfile",
|
|
7218
|
-
|
|
7374
|
+
import_node_path35.default.relative(scanPath, dockerfilePath),
|
|
7219
7375
|
err
|
|
7220
7376
|
);
|
|
7221
7377
|
continue;
|
|
@@ -7227,8 +7383,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7227
7383
|
graph.addNode(node.id, node);
|
|
7228
7384
|
nodesAdded++;
|
|
7229
7385
|
}
|
|
7230
|
-
const relDockerfile = toPosix2(
|
|
7231
|
-
const evidenceFile = toPosix2(
|
|
7386
|
+
const relDockerfile = toPosix2(import_node_path35.default.relative(service.dir, dockerfilePath));
|
|
7387
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, dockerfilePath));
|
|
7232
7388
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7233
7389
|
graph,
|
|
7234
7390
|
service.pkg.name,
|
|
@@ -7237,15 +7393,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7237
7393
|
);
|
|
7238
7394
|
nodesAdded += fn;
|
|
7239
7395
|
edgesAdded += fe;
|
|
7240
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7396
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
|
|
7241
7397
|
if (!graph.hasEdge(edgeId)) {
|
|
7242
7398
|
const edge = {
|
|
7243
7399
|
id: edgeId,
|
|
7244
7400
|
source: fileNodeId,
|
|
7245
7401
|
target: node.id,
|
|
7246
|
-
type:
|
|
7247
|
-
provenance:
|
|
7248
|
-
confidence: (0,
|
|
7402
|
+
type: import_types25.EdgeType.RUNS_ON,
|
|
7403
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7404
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7249
7405
|
evidence: {
|
|
7250
7406
|
file: evidenceFile,
|
|
7251
7407
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -7260,15 +7416,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7260
7416
|
graph.addNode(portNode.id, portNode);
|
|
7261
7417
|
nodesAdded++;
|
|
7262
7418
|
}
|
|
7263
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7419
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
|
|
7264
7420
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
7265
7421
|
const portEdge = {
|
|
7266
7422
|
id: portEdgeId,
|
|
7267
7423
|
source: fileNodeId,
|
|
7268
7424
|
target: portNode.id,
|
|
7269
|
-
type:
|
|
7270
|
-
provenance:
|
|
7271
|
-
confidence: (0,
|
|
7425
|
+
type: import_types25.EdgeType.CONNECTS_TO,
|
|
7426
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7427
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7272
7428
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
7273
7429
|
};
|
|
7274
7430
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -7281,8 +7437,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7281
7437
|
// src/extract/infra/terraform.ts
|
|
7282
7438
|
init_cjs_shims();
|
|
7283
7439
|
var import_node_fs18 = require("fs");
|
|
7284
|
-
var
|
|
7285
|
-
var
|
|
7440
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
7441
|
+
var import_types26 = require("@neat.is/types");
|
|
7286
7442
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
7287
7443
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
7288
7444
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -7292,11 +7448,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
7292
7448
|
for (const entry2 of entries) {
|
|
7293
7449
|
if (entry2.isDirectory()) {
|
|
7294
7450
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
7295
|
-
const child =
|
|
7451
|
+
const child = import_node_path36.default.join(start, entry2.name);
|
|
7296
7452
|
if (await isPythonVenvDir(child)) continue;
|
|
7297
7453
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
7298
7454
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
7299
|
-
out.push(
|
|
7455
|
+
out.push(import_node_path36.default.join(start, entry2.name));
|
|
7300
7456
|
}
|
|
7301
7457
|
}
|
|
7302
7458
|
return out;
|
|
@@ -7328,7 +7484,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7328
7484
|
const files = await walkTfFiles(scanPath);
|
|
7329
7485
|
for (const file of files) {
|
|
7330
7486
|
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
7331
|
-
const evidenceFile = toPosix2(
|
|
7487
|
+
const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, file));
|
|
7332
7488
|
const resources = [];
|
|
7333
7489
|
const byKey = /* @__PURE__ */ new Map();
|
|
7334
7490
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -7363,16 +7519,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7363
7519
|
if (!target) continue;
|
|
7364
7520
|
if (seen.has(target.nodeId)) continue;
|
|
7365
7521
|
seen.add(target.nodeId);
|
|
7366
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7522
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
|
|
7367
7523
|
if (graph.hasEdge(edgeId)) continue;
|
|
7368
7524
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
7369
7525
|
const edge = {
|
|
7370
7526
|
id: edgeId,
|
|
7371
7527
|
source: resource.nodeId,
|
|
7372
7528
|
target: target.nodeId,
|
|
7373
|
-
type:
|
|
7374
|
-
provenance:
|
|
7375
|
-
confidence: (0,
|
|
7529
|
+
type: import_types26.EdgeType.DEPENDS_ON,
|
|
7530
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7531
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7376
7532
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
7377
7533
|
};
|
|
7378
7534
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7386,7 +7542,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7386
7542
|
// src/extract/infra/k8s.ts
|
|
7387
7543
|
init_cjs_shims();
|
|
7388
7544
|
var import_node_fs19 = require("fs");
|
|
7389
|
-
var
|
|
7545
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7390
7546
|
var import_yaml3 = require("yaml");
|
|
7391
7547
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
7392
7548
|
Service: "k8s-service",
|
|
@@ -7404,11 +7560,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
7404
7560
|
for (const entry2 of entries) {
|
|
7405
7561
|
if (entry2.isDirectory()) {
|
|
7406
7562
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
7407
|
-
const child =
|
|
7563
|
+
const child = import_node_path37.default.join(start, entry2.name);
|
|
7408
7564
|
if (await isPythonVenvDir(child)) continue;
|
|
7409
7565
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
7410
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
7411
|
-
out.push(
|
|
7566
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path37.default.extname(entry2.name))) {
|
|
7567
|
+
out.push(import_node_path37.default.join(start, entry2.name));
|
|
7412
7568
|
}
|
|
7413
7569
|
}
|
|
7414
7570
|
return out;
|
|
@@ -7442,13 +7598,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7442
7598
|
// src/extract/infra/cloudflare.ts
|
|
7443
7599
|
init_cjs_shims();
|
|
7444
7600
|
var import_node_fs20 = require("fs");
|
|
7445
|
-
var
|
|
7601
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7446
7602
|
var import_smol_toml2 = require("smol-toml");
|
|
7447
|
-
var
|
|
7603
|
+
var import_types27 = require("@neat.is/types");
|
|
7448
7604
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7449
7605
|
async function readWranglerConfig(dir) {
|
|
7450
7606
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7451
|
-
const abs =
|
|
7607
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7452
7608
|
if (!await exists(abs)) continue;
|
|
7453
7609
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7454
7610
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7492,8 +7648,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7492
7648
|
source: anchorId,
|
|
7493
7649
|
target: node.id,
|
|
7494
7650
|
type: edgeType,
|
|
7495
|
-
provenance:
|
|
7496
|
-
confidence: (0,
|
|
7651
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7652
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7497
7653
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7498
7654
|
};
|
|
7499
7655
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7511,11 +7667,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7511
7667
|
try {
|
|
7512
7668
|
read = await readWranglerConfig(service.dir);
|
|
7513
7669
|
} catch (err) {
|
|
7514
|
-
recordExtractionError("infra cloudflare",
|
|
7670
|
+
recordExtractionError("infra cloudflare", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7515
7671
|
continue;
|
|
7516
7672
|
}
|
|
7517
7673
|
if (!read || !read.config.name) continue;
|
|
7518
|
-
const evidenceFile = toPosix2(
|
|
7674
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, read.relFile)));
|
|
7519
7675
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7520
7676
|
}
|
|
7521
7677
|
for (const worker of discovered) {
|
|
@@ -7527,7 +7683,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7527
7683
|
}
|
|
7528
7684
|
let anchorId = service.node.id;
|
|
7529
7685
|
if (config.main) {
|
|
7530
|
-
const entryRelPath = toPosix2(
|
|
7686
|
+
const entryRelPath = toPosix2(import_node_path38.default.normalize(config.main));
|
|
7531
7687
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7532
7688
|
graph,
|
|
7533
7689
|
service.pkg.name,
|
|
@@ -7554,15 +7710,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7554
7710
|
nodesAdded++;
|
|
7555
7711
|
}
|
|
7556
7712
|
if (runtimeNode.id !== anchorId) {
|
|
7557
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7713
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
|
|
7558
7714
|
if (!graph.hasEdge(runsOnId)) {
|
|
7559
7715
|
const edge = {
|
|
7560
7716
|
id: runsOnId,
|
|
7561
7717
|
source: anchorId,
|
|
7562
7718
|
target: runtimeNode.id,
|
|
7563
|
-
type:
|
|
7564
|
-
provenance:
|
|
7565
|
-
confidence: (0,
|
|
7719
|
+
type: import_types27.EdgeType.RUNS_ON,
|
|
7720
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7721
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7566
7722
|
evidence: {
|
|
7567
7723
|
file: evidenceFile,
|
|
7568
7724
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7576,7 +7732,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7576
7732
|
const result = addResourceEdge(
|
|
7577
7733
|
graph,
|
|
7578
7734
|
anchorId,
|
|
7579
|
-
|
|
7735
|
+
import_types27.EdgeType.CONNECTS_TO,
|
|
7580
7736
|
"cloudflare-route",
|
|
7581
7737
|
route,
|
|
7582
7738
|
evidenceFile,
|
|
@@ -7600,7 +7756,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7600
7756
|
const result = addResourceEdge(
|
|
7601
7757
|
graph,
|
|
7602
7758
|
anchorId,
|
|
7603
|
-
|
|
7759
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7604
7760
|
group.kind,
|
|
7605
7761
|
name,
|
|
7606
7762
|
evidenceFile,
|
|
@@ -7614,7 +7770,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7614
7770
|
const result = addResourceEdge(
|
|
7615
7771
|
graph,
|
|
7616
7772
|
anchorId,
|
|
7617
|
-
|
|
7773
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7618
7774
|
"cloudflare-cron",
|
|
7619
7775
|
cron,
|
|
7620
7776
|
evidenceFile,
|
|
@@ -7627,7 +7783,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7627
7783
|
const result = addResourceEdge(
|
|
7628
7784
|
graph,
|
|
7629
7785
|
anchorId,
|
|
7630
|
-
|
|
7786
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7631
7787
|
"cloudflare-env-var",
|
|
7632
7788
|
varName,
|
|
7633
7789
|
evidenceFile,
|
|
@@ -7640,15 +7796,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7640
7796
|
if (!svc.service) continue;
|
|
7641
7797
|
const target = workerIndex.get(svc.service);
|
|
7642
7798
|
if (target && target.anchorId !== anchorId) {
|
|
7643
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7799
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
|
|
7644
7800
|
if (!graph.hasEdge(edgeId)) {
|
|
7645
7801
|
const edge = {
|
|
7646
7802
|
id: edgeId,
|
|
7647
7803
|
source: anchorId,
|
|
7648
7804
|
target: target.anchorId,
|
|
7649
|
-
type:
|
|
7650
|
-
provenance:
|
|
7651
|
-
confidence: (0,
|
|
7805
|
+
type: import_types27.EdgeType.CALLS,
|
|
7806
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7807
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7652
7808
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7653
7809
|
};
|
|
7654
7810
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7659,7 +7815,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7659
7815
|
const result = addResourceEdge(
|
|
7660
7816
|
graph,
|
|
7661
7817
|
anchorId,
|
|
7662
|
-
|
|
7818
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7663
7819
|
"cloudflare-service-binding",
|
|
7664
7820
|
svc.service,
|
|
7665
7821
|
evidenceFile,
|
|
@@ -7675,12 +7831,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7675
7831
|
// src/extract/infra/vercel.ts
|
|
7676
7832
|
init_cjs_shims();
|
|
7677
7833
|
var import_node_fs21 = require("fs");
|
|
7678
|
-
var
|
|
7679
|
-
var
|
|
7834
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7835
|
+
var import_types28 = require("@neat.is/types");
|
|
7680
7836
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7681
7837
|
async function readVercelConfig(dir) {
|
|
7682
7838
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7683
|
-
const abs =
|
|
7839
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7684
7840
|
if (!await exists(abs)) continue;
|
|
7685
7841
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7686
7842
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7689,7 +7845,7 @@ async function readVercelConfig(dir) {
|
|
|
7689
7845
|
return null;
|
|
7690
7846
|
}
|
|
7691
7847
|
async function readLinkedProjectName(dir) {
|
|
7692
|
-
const abs =
|
|
7848
|
+
const abs = import_node_path39.default.join(dir, ".vercel", "project.json");
|
|
7693
7849
|
if (!await exists(abs)) return void 0;
|
|
7694
7850
|
const parsed = JSON.parse(await import_node_fs21.promises.readFile(abs, "utf8"));
|
|
7695
7851
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7707,7 +7863,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7707
7863
|
read = await readVercelConfig(service.dir);
|
|
7708
7864
|
projectName = await readLinkedProjectName(service.dir);
|
|
7709
7865
|
} catch (err) {
|
|
7710
|
-
recordExtractionError("infra vercel",
|
|
7866
|
+
recordExtractionError("infra vercel", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7711
7867
|
continue;
|
|
7712
7868
|
}
|
|
7713
7869
|
if (!read && !projectName) continue;
|
|
@@ -7723,7 +7879,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7723
7879
|
const anchorId = service.node.id;
|
|
7724
7880
|
if (!read) continue;
|
|
7725
7881
|
const { config, relFile, raw } = read;
|
|
7726
|
-
const evidenceFile = toPosix2(
|
|
7882
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7727
7883
|
const add = (edgeType, kind, name) => {
|
|
7728
7884
|
if (!name) return;
|
|
7729
7885
|
const result = emitPlatformResourceEdge(
|
|
@@ -7739,12 +7895,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7739
7895
|
nodesAdded += result.nodesAdded;
|
|
7740
7896
|
edgesAdded += result.edgesAdded;
|
|
7741
7897
|
};
|
|
7742
|
-
add(
|
|
7743
|
-
for (const cron of config.crons ?? []) add(
|
|
7744
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7745
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7898
|
+
add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7899
|
+
for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7900
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7901
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7746
7902
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7747
|
-
add(
|
|
7903
|
+
add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7748
7904
|
}
|
|
7749
7905
|
}
|
|
7750
7906
|
return { nodesAdded, edgesAdded };
|
|
@@ -7753,13 +7909,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7753
7909
|
// src/extract/infra/railway.ts
|
|
7754
7910
|
init_cjs_shims();
|
|
7755
7911
|
var import_node_fs22 = require("fs");
|
|
7756
|
-
var
|
|
7912
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7757
7913
|
var import_smol_toml3 = require("smol-toml");
|
|
7758
|
-
var
|
|
7914
|
+
var import_types29 = require("@neat.is/types");
|
|
7759
7915
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7760
7916
|
async function readRailwayConfig(dir) {
|
|
7761
7917
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7762
|
-
const abs =
|
|
7918
|
+
const abs = import_node_path40.default.join(dir, filename);
|
|
7763
7919
|
if (!await exists(abs)) continue;
|
|
7764
7920
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7765
7921
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7775,7 +7931,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7775
7931
|
try {
|
|
7776
7932
|
read = await readRailwayConfig(service.dir);
|
|
7777
7933
|
} catch (err) {
|
|
7778
|
-
recordExtractionError("infra railway",
|
|
7934
|
+
recordExtractionError("infra railway", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7779
7935
|
continue;
|
|
7780
7936
|
}
|
|
7781
7937
|
if (!read) continue;
|
|
@@ -7785,7 +7941,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7785
7941
|
}
|
|
7786
7942
|
const anchorId = service.node.id;
|
|
7787
7943
|
const { config, relFile, raw } = read;
|
|
7788
|
-
const evidenceFile = toPosix2(
|
|
7944
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7789
7945
|
const add = (edgeType, kind, name) => {
|
|
7790
7946
|
if (!name) return;
|
|
7791
7947
|
const result = emitPlatformResourceEdge(
|
|
@@ -7801,9 +7957,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7801
7957
|
nodesAdded += result.nodesAdded;
|
|
7802
7958
|
edgesAdded += result.edgesAdded;
|
|
7803
7959
|
};
|
|
7804
|
-
add(
|
|
7805
|
-
add(
|
|
7806
|
-
add(
|
|
7960
|
+
add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
|
|
7961
|
+
add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7962
|
+
add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7807
7963
|
}
|
|
7808
7964
|
return { nodesAdded, edgesAdded };
|
|
7809
7965
|
}
|
|
@@ -7811,12 +7967,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7811
7967
|
// src/extract/infra/supabase.ts
|
|
7812
7968
|
init_cjs_shims();
|
|
7813
7969
|
var import_node_fs23 = require("fs");
|
|
7814
|
-
var
|
|
7970
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7815
7971
|
var import_smol_toml4 = require("smol-toml");
|
|
7816
|
-
var
|
|
7972
|
+
var import_types30 = require("@neat.is/types");
|
|
7817
7973
|
async function readSupabaseConfig(dir) {
|
|
7818
|
-
const relFile =
|
|
7819
|
-
const abs =
|
|
7974
|
+
const relFile = import_node_path41.default.join("supabase", "config.toml");
|
|
7975
|
+
const abs = import_node_path41.default.join(dir, relFile);
|
|
7820
7976
|
if (!await exists(abs)) return null;
|
|
7821
7977
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
7822
7978
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7830,7 +7986,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7830
7986
|
try {
|
|
7831
7987
|
read = await readSupabaseConfig(service.dir);
|
|
7832
7988
|
} catch (err) {
|
|
7833
|
-
recordExtractionError("infra supabase",
|
|
7989
|
+
recordExtractionError("infra supabase", import_node_path41.default.relative(scanPath, service.dir), err);
|
|
7834
7990
|
continue;
|
|
7835
7991
|
}
|
|
7836
7992
|
if (!read) continue;
|
|
@@ -7845,7 +8001,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7845
8001
|
});
|
|
7846
8002
|
}
|
|
7847
8003
|
const anchorId = service.node.id;
|
|
7848
|
-
const evidenceFile = toPosix2(
|
|
8004
|
+
const evidenceFile = toPosix2(import_node_path41.default.relative(scanPath, import_node_path41.default.join(service.dir, relFile)));
|
|
7849
8005
|
const add = (edgeType, kind, name) => {
|
|
7850
8006
|
if (!name) return;
|
|
7851
8007
|
const result = emitPlatformResourceEdge(
|
|
@@ -7861,10 +8017,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7861
8017
|
nodesAdded += result.nodesAdded;
|
|
7862
8018
|
edgesAdded += result.edgesAdded;
|
|
7863
8019
|
};
|
|
7864
|
-
add(
|
|
7865
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7866
|
-
if (config.storage) add(
|
|
7867
|
-
if (config.auth) add(
|
|
8020
|
+
add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
8021
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
8022
|
+
if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
8023
|
+
if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7868
8024
|
}
|
|
7869
8025
|
return { nodesAdded, edgesAdded };
|
|
7870
8026
|
}
|
|
@@ -7886,17 +8042,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7886
8042
|
}
|
|
7887
8043
|
|
|
7888
8044
|
// src/extract/index.ts
|
|
7889
|
-
var
|
|
8045
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7890
8046
|
|
|
7891
8047
|
// src/extract/retire.ts
|
|
7892
8048
|
init_cjs_shims();
|
|
7893
8049
|
var import_node_fs24 = require("fs");
|
|
7894
|
-
var
|
|
7895
|
-
var
|
|
8050
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
8051
|
+
var import_types31 = require("@neat.is/types");
|
|
7896
8052
|
function dropOrphanedFileNodes(graph) {
|
|
7897
8053
|
const orphans = [];
|
|
7898
8054
|
graph.forEachNode((id, attrs) => {
|
|
7899
|
-
if (attrs.type !==
|
|
8055
|
+
if (attrs.type !== import_types31.NodeType.FileNode) return;
|
|
7900
8056
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7901
8057
|
orphans.push(id);
|
|
7902
8058
|
}
|
|
@@ -7909,7 +8065,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
7909
8065
|
const toDrop = [];
|
|
7910
8066
|
graph.forEachEdge((id, attrs) => {
|
|
7911
8067
|
const edge = attrs;
|
|
7912
|
-
if (edge.provenance !==
|
|
8068
|
+
if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
|
|
7913
8069
|
if (!edge.evidence?.file) return;
|
|
7914
8070
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
7915
8071
|
});
|
|
@@ -7922,14 +8078,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7922
8078
|
const bases = [scanPath, ...serviceDirs];
|
|
7923
8079
|
graph.forEachEdge((id, attrs) => {
|
|
7924
8080
|
const edge = attrs;
|
|
7925
|
-
if (edge.provenance !==
|
|
8081
|
+
if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
|
|
7926
8082
|
const evidenceFile = edge.evidence?.file;
|
|
7927
8083
|
if (!evidenceFile) return;
|
|
7928
|
-
if (
|
|
8084
|
+
if (import_node_path42.default.isAbsolute(evidenceFile)) {
|
|
7929
8085
|
if (!(0, import_node_fs24.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7930
8086
|
return;
|
|
7931
8087
|
}
|
|
7932
|
-
const found = bases.some((base) => (0, import_node_fs24.existsSync)(
|
|
8088
|
+
const found = bases.some((base) => (0, import_node_fs24.existsSync)(import_node_path42.default.join(base, evidenceFile)));
|
|
7933
8089
|
if (!found) toDrop.push(id);
|
|
7934
8090
|
});
|
|
7935
8091
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7971,7 +8127,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7971
8127
|
}
|
|
7972
8128
|
const droppedEntries = drainDroppedExtracted();
|
|
7973
8129
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7974
|
-
const rejectedPath =
|
|
8130
|
+
const rejectedPath = import_node_path43.default.join(import_node_path43.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7975
8131
|
try {
|
|
7976
8132
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7977
8133
|
} catch (err) {
|
|
@@ -8005,39 +8161,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
8005
8161
|
|
|
8006
8162
|
// src/divergences.ts
|
|
8007
8163
|
init_cjs_shims();
|
|
8008
|
-
var
|
|
8164
|
+
var import_types32 = require("@neat.is/types");
|
|
8009
8165
|
function bucketKey(source, target, type) {
|
|
8010
8166
|
return `${type}|${source}|${target}`;
|
|
8011
8167
|
}
|
|
8012
8168
|
function bucketSourceFor(graph, edge) {
|
|
8013
|
-
if (edge.type !==
|
|
8014
|
-
const parsed = (0,
|
|
8169
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
|
|
8170
|
+
const parsed = (0, import_types32.parseFileId)(edge.source);
|
|
8015
8171
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8016
8172
|
const target = graph.getNodeAttributes(edge.target);
|
|
8017
|
-
if (target.type !==
|
|
8018
|
-
return (0,
|
|
8173
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
|
|
8174
|
+
return (0, import_types32.serviceId)(parsed.service);
|
|
8019
8175
|
}
|
|
8020
8176
|
function bucketEdges(graph) {
|
|
8021
8177
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8022
8178
|
graph.forEachEdge((id, attrs) => {
|
|
8023
8179
|
const e = attrs;
|
|
8024
|
-
const parsed = (0,
|
|
8180
|
+
const parsed = (0, import_types32.parseEdgeId)(id);
|
|
8025
8181
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8026
8182
|
const source = bucketSourceFor(graph, e);
|
|
8027
8183
|
const key = bucketKey(source, e.target, e.type);
|
|
8028
8184
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8029
8185
|
switch (provenance) {
|
|
8030
|
-
case
|
|
8186
|
+
case import_types32.Provenance.EXTRACTED:
|
|
8031
8187
|
cur.extracted = e;
|
|
8032
8188
|
break;
|
|
8033
|
-
case
|
|
8189
|
+
case import_types32.Provenance.OBSERVED:
|
|
8034
8190
|
cur.observed = e;
|
|
8035
8191
|
break;
|
|
8036
|
-
case
|
|
8192
|
+
case import_types32.Provenance.INFERRED:
|
|
8037
8193
|
cur.inferred = e;
|
|
8038
8194
|
break;
|
|
8039
8195
|
default:
|
|
8040
|
-
if (e.provenance ===
|
|
8196
|
+
if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
|
|
8041
8197
|
}
|
|
8042
8198
|
buckets2.set(key, cur);
|
|
8043
8199
|
});
|
|
@@ -8046,12 +8202,12 @@ function bucketEdges(graph) {
|
|
|
8046
8202
|
function nodeIsFrontier(graph, nodeId) {
|
|
8047
8203
|
if (!graph.hasNode(nodeId)) return false;
|
|
8048
8204
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8049
|
-
return attrs.type ===
|
|
8205
|
+
return attrs.type === import_types32.NodeType.FrontierNode;
|
|
8050
8206
|
}
|
|
8051
8207
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8052
8208
|
if (!graph.hasNode(nodeId)) return false;
|
|
8053
8209
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8054
|
-
return attrs.type ===
|
|
8210
|
+
return attrs.type === import_types32.NodeType.WebSocketChannelNode;
|
|
8055
8211
|
}
|
|
8056
8212
|
function clampConfidence(n) {
|
|
8057
8213
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8071,14 +8227,14 @@ function gradedConfidence(edge) {
|
|
|
8071
8227
|
return clampConfidence(confidenceForEdge(edge));
|
|
8072
8228
|
}
|
|
8073
8229
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8230
|
+
import_types32.EdgeType.CALLS,
|
|
8231
|
+
import_types32.EdgeType.CONNECTS_TO,
|
|
8232
|
+
import_types32.EdgeType.PUBLISHES_TO,
|
|
8233
|
+
import_types32.EdgeType.CONSUMES_FROM
|
|
8078
8234
|
]);
|
|
8079
8235
|
function detectMissingDivergences(graph, bucket) {
|
|
8080
8236
|
const out = [];
|
|
8081
|
-
if (bucket.type ===
|
|
8237
|
+
if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
|
|
8082
8238
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8083
8239
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8084
8240
|
out.push({
|
|
@@ -8119,7 +8275,7 @@ function declaredHostFor(svc) {
|
|
|
8119
8275
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8120
8276
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8121
8277
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8122
|
-
if (e.type ===
|
|
8278
|
+
if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
|
|
8123
8279
|
return true;
|
|
8124
8280
|
}
|
|
8125
8281
|
}
|
|
@@ -8132,10 +8288,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8132
8288
|
const out = [];
|
|
8133
8289
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8134
8290
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8135
|
-
if (edge.type !==
|
|
8136
|
-
if (edge.provenance !==
|
|
8291
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8292
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8137
8293
|
const target = graph.getNodeAttributes(edge.target);
|
|
8138
|
-
if (target.type !==
|
|
8294
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8139
8295
|
const observedHost = target.host?.trim();
|
|
8140
8296
|
if (!observedHost) continue;
|
|
8141
8297
|
if (observedHost === declaredHost) continue;
|
|
@@ -8157,10 +8313,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8157
8313
|
const deps = svc.dependencies ?? {};
|
|
8158
8314
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8159
8315
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8160
|
-
if (edge.type !==
|
|
8161
|
-
if (edge.provenance !==
|
|
8316
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8317
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8162
8318
|
const target = graph.getNodeAttributes(edge.target);
|
|
8163
|
-
if (target.type !==
|
|
8319
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8164
8320
|
for (const pair of compatPairs()) {
|
|
8165
8321
|
if (pair.engine !== target.engine) continue;
|
|
8166
8322
|
const declared = deps[pair.driver];
|
|
@@ -8219,7 +8375,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8219
8375
|
for (const d of all) {
|
|
8220
8376
|
if (d.type !== "host-mismatch") continue;
|
|
8221
8377
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8222
|
-
declaredHalf.add((0,
|
|
8378
|
+
declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
|
|
8223
8379
|
}
|
|
8224
8380
|
if (observedHalf.size === 0) return all;
|
|
8225
8381
|
return all.filter((d) => {
|
|
@@ -8238,7 +8394,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8238
8394
|
}
|
|
8239
8395
|
graph.forEachNode((nodeId, attrs) => {
|
|
8240
8396
|
const n = attrs;
|
|
8241
|
-
if (n.type !==
|
|
8397
|
+
if (n.type !== import_types32.NodeType.ServiceNode) return;
|
|
8242
8398
|
const svc = n;
|
|
8243
8399
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8244
8400
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8272,7 +8428,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8272
8428
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8273
8429
|
return a.target.localeCompare(b.target);
|
|
8274
8430
|
});
|
|
8275
|
-
return
|
|
8431
|
+
return import_types32.DivergenceResultSchema.parse({
|
|
8276
8432
|
divergences: filtered,
|
|
8277
8433
|
totalAffected: filtered.length,
|
|
8278
8434
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8282,8 +8438,8 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8282
8438
|
// src/persist.ts
|
|
8283
8439
|
init_cjs_shims();
|
|
8284
8440
|
var import_node_fs25 = require("fs");
|
|
8285
|
-
var
|
|
8286
|
-
var
|
|
8441
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8442
|
+
var import_types33 = require("@neat.is/types");
|
|
8287
8443
|
var SCHEMA_VERSION = 4;
|
|
8288
8444
|
function migrateV1ToV2(payload) {
|
|
8289
8445
|
const nodes = payload.graph.nodes;
|
|
@@ -8305,12 +8461,12 @@ function migrateV2ToV3(payload) {
|
|
|
8305
8461
|
for (const edge of edges) {
|
|
8306
8462
|
const attrs = edge.attributes;
|
|
8307
8463
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
8308
|
-
attrs.provenance =
|
|
8464
|
+
attrs.provenance = import_types33.Provenance.OBSERVED;
|
|
8309
8465
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
8310
8466
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
8311
8467
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
8312
8468
|
if (type && source && target) {
|
|
8313
|
-
const newId = (0,
|
|
8469
|
+
const newId = (0, import_types33.observedEdgeId)(source, target, type);
|
|
8314
8470
|
attrs.id = newId;
|
|
8315
8471
|
if (edge.key) edge.key = newId;
|
|
8316
8472
|
}
|
|
@@ -8319,7 +8475,7 @@ function migrateV2ToV3(payload) {
|
|
|
8319
8475
|
return { ...payload, schemaVersion: 3 };
|
|
8320
8476
|
}
|
|
8321
8477
|
async function ensureDir(filePath) {
|
|
8322
|
-
await import_node_fs25.promises.mkdir(
|
|
8478
|
+
await import_node_fs25.promises.mkdir(import_node_path44.default.dirname(filePath), { recursive: true });
|
|
8323
8479
|
}
|
|
8324
8480
|
async function saveGraphToDisk(graph, outPath) {
|
|
8325
8481
|
await ensureDir(outPath);
|
|
@@ -8401,7 +8557,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
8401
8557
|
// src/gitignore.ts
|
|
8402
8558
|
init_cjs_shims();
|
|
8403
8559
|
var import_node_fs26 = require("fs");
|
|
8404
|
-
var
|
|
8560
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8405
8561
|
var NEAT_OUT_LINE = "neat-out/";
|
|
8406
8562
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
8407
8563
|
function isNeatOutLine(line) {
|
|
@@ -8409,7 +8565,7 @@ function isNeatOutLine(line) {
|
|
|
8409
8565
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
8410
8566
|
}
|
|
8411
8567
|
async function ensureNeatOutIgnored(projectDir) {
|
|
8412
|
-
const file =
|
|
8568
|
+
const file = import_node_path45.default.join(projectDir, ".gitignore");
|
|
8413
8569
|
let existing = null;
|
|
8414
8570
|
try {
|
|
8415
8571
|
existing = await import_node_fs26.promises.readFile(file, "utf8");
|
|
@@ -8436,7 +8592,7 @@ ${NEAT_OUT_LINE}
|
|
|
8436
8592
|
|
|
8437
8593
|
// src/summary.ts
|
|
8438
8594
|
init_cjs_shims();
|
|
8439
|
-
var
|
|
8595
|
+
var import_types34 = require("@neat.is/types");
|
|
8440
8596
|
function renderOtelEnvBlock() {
|
|
8441
8597
|
return [
|
|
8442
8598
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -8446,19 +8602,19 @@ function renderOtelEnvBlock() {
|
|
|
8446
8602
|
}
|
|
8447
8603
|
function findIncompatServices(nodes) {
|
|
8448
8604
|
return nodes.filter(
|
|
8449
|
-
(n) => n.type ===
|
|
8605
|
+
(n) => n.type === import_types34.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
8450
8606
|
);
|
|
8451
8607
|
}
|
|
8452
8608
|
function servicesWithoutObserved(nodes, edges) {
|
|
8453
8609
|
const seen = /* @__PURE__ */ new Set();
|
|
8454
8610
|
for (const e of edges) {
|
|
8455
|
-
if (e.provenance ===
|
|
8611
|
+
if (e.provenance === import_types34.Provenance.OBSERVED) {
|
|
8456
8612
|
seen.add(e.source);
|
|
8457
8613
|
seen.add(e.target);
|
|
8458
8614
|
}
|
|
8459
8615
|
}
|
|
8460
8616
|
return nodes.filter(
|
|
8461
|
-
(n) => n.type ===
|
|
8617
|
+
(n) => n.type === import_types34.NodeType.ServiceNode && !seen.has(n.id)
|
|
8462
8618
|
);
|
|
8463
8619
|
}
|
|
8464
8620
|
function formatDivergence(d) {
|
|
@@ -8533,26 +8689,26 @@ function formatIncompat(inc) {
|
|
|
8533
8689
|
// src/watch.ts
|
|
8534
8690
|
init_cjs_shims();
|
|
8535
8691
|
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
8536
|
-
var
|
|
8692
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
8537
8693
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
8538
8694
|
|
|
8539
8695
|
// src/api.ts
|
|
8540
8696
|
init_cjs_shims();
|
|
8541
8697
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
8542
8698
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
8543
|
-
var
|
|
8699
|
+
var import_types36 = require("@neat.is/types");
|
|
8544
8700
|
|
|
8545
8701
|
// src/extend/index.ts
|
|
8546
8702
|
init_cjs_shims();
|
|
8547
8703
|
var import_node_fs28 = require("fs");
|
|
8548
|
-
var
|
|
8704
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8549
8705
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
8550
8706
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
8551
8707
|
|
|
8552
8708
|
// src/installers/package-manager.ts
|
|
8553
8709
|
init_cjs_shims();
|
|
8554
8710
|
var import_node_fs27 = require("fs");
|
|
8555
|
-
var
|
|
8711
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8556
8712
|
var import_node_child_process = require("child_process");
|
|
8557
8713
|
var LOCKFILE_PRIORITY = [
|
|
8558
8714
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -8574,22 +8730,22 @@ async function exists2(p) {
|
|
|
8574
8730
|
}
|
|
8575
8731
|
}
|
|
8576
8732
|
async function detectPackageManager(serviceDir) {
|
|
8577
|
-
let dir =
|
|
8733
|
+
let dir = import_node_path46.default.resolve(serviceDir);
|
|
8578
8734
|
const stops = /* @__PURE__ */ new Set();
|
|
8579
8735
|
for (let i = 0; i < 64; i++) {
|
|
8580
8736
|
if (stops.has(dir)) break;
|
|
8581
8737
|
stops.add(dir);
|
|
8582
8738
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
8583
|
-
const lockPath =
|
|
8739
|
+
const lockPath = import_node_path46.default.join(dir, candidate.lockfile);
|
|
8584
8740
|
if (await exists2(lockPath)) {
|
|
8585
8741
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
8586
8742
|
}
|
|
8587
8743
|
}
|
|
8588
|
-
const parent =
|
|
8744
|
+
const parent = import_node_path46.default.dirname(dir);
|
|
8589
8745
|
if (parent === dir) break;
|
|
8590
8746
|
dir = parent;
|
|
8591
8747
|
}
|
|
8592
|
-
return { pm: "npm", cwd:
|
|
8748
|
+
return { pm: "npm", cwd: import_node_path46.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
8593
8749
|
}
|
|
8594
8750
|
async function runPackageManagerInstall(cmd) {
|
|
8595
8751
|
return new Promise((resolve) => {
|
|
@@ -8638,7 +8794,7 @@ async function fileExists2(p) {
|
|
|
8638
8794
|
}
|
|
8639
8795
|
}
|
|
8640
8796
|
async function readPackageJson(scanPath) {
|
|
8641
|
-
const pkgPath =
|
|
8797
|
+
const pkgPath = import_node_path47.default.join(scanPath, "package.json");
|
|
8642
8798
|
const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
|
|
8643
8799
|
return JSON.parse(raw);
|
|
8644
8800
|
}
|
|
@@ -8652,27 +8808,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
8652
8808
|
]);
|
|
8653
8809
|
async function findHookFiles(scanPath) {
|
|
8654
8810
|
const found = [];
|
|
8655
|
-
const
|
|
8811
|
+
const walk4 = async (dir) => {
|
|
8656
8812
|
const entries = await import_node_fs28.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8657
8813
|
for (const entry2 of entries) {
|
|
8658
8814
|
if (entry2.isDirectory()) {
|
|
8659
8815
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
8660
|
-
await
|
|
8816
|
+
await walk4(import_node_path47.default.join(dir, entry2.name));
|
|
8661
8817
|
} else if (entry2.isFile()) {
|
|
8662
8818
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
8663
|
-
const rel =
|
|
8664
|
-
found.push(rel.split(
|
|
8819
|
+
const rel = import_node_path47.default.relative(scanPath, import_node_path47.default.join(dir, entry2.name));
|
|
8820
|
+
found.push(rel.split(import_node_path47.default.sep).join("/"));
|
|
8665
8821
|
}
|
|
8666
8822
|
}
|
|
8667
8823
|
}
|
|
8668
8824
|
};
|
|
8669
|
-
await
|
|
8825
|
+
await walk4(scanPath);
|
|
8670
8826
|
return found.sort();
|
|
8671
8827
|
}
|
|
8672
8828
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8673
8829
|
let fallback = null;
|
|
8674
8830
|
for (const file of hookFiles) {
|
|
8675
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8831
|
+
const content = await import_node_fs28.promises.readFile(import_node_path47.default.join(scanPath, file), "utf8");
|
|
8676
8832
|
const patched = splicedContent(content, snippet2);
|
|
8677
8833
|
if (patched !== null) return { file, content, patched };
|
|
8678
8834
|
if (fallback === null) fallback = { file, content };
|
|
@@ -8680,11 +8836,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
8680
8836
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
8681
8837
|
}
|
|
8682
8838
|
function extendLogPath() {
|
|
8683
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8839
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path47.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
8684
8840
|
}
|
|
8685
8841
|
async function appendExtendLog(entry2) {
|
|
8686
8842
|
const logPath = extendLogPath();
|
|
8687
|
-
await import_node_fs28.promises.mkdir(
|
|
8843
|
+
await import_node_fs28.promises.mkdir(import_node_path47.default.dirname(logPath), { recursive: true });
|
|
8688
8844
|
await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
8689
8845
|
}
|
|
8690
8846
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -8743,7 +8899,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
8743
8899
|
}
|
|
8744
8900
|
async function describeProjectInstrumentation(ctx) {
|
|
8745
8901
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8746
|
-
const envNeat = await fileExists2(
|
|
8902
|
+
const envNeat = await fileExists2(import_node_path47.default.join(ctx.scanPath, ".env.neat"));
|
|
8747
8903
|
const registryInstrPackages = new Set(
|
|
8748
8904
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
8749
8905
|
);
|
|
@@ -8765,7 +8921,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
8765
8921
|
);
|
|
8766
8922
|
}
|
|
8767
8923
|
for (const file of hookFiles) {
|
|
8768
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8924
|
+
const content = await import_node_fs28.promises.readFile(import_node_path47.default.join(ctx.scanPath, file), "utf8");
|
|
8769
8925
|
if (content.includes(args.registration_snippet)) {
|
|
8770
8926
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
8771
8927
|
}
|
|
@@ -8777,10 +8933,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
8777
8933
|
);
|
|
8778
8934
|
}
|
|
8779
8935
|
const primaryFile = primary.file;
|
|
8780
|
-
const primaryPath =
|
|
8936
|
+
const primaryPath = import_node_path47.default.join(ctx.scanPath, primaryFile);
|
|
8781
8937
|
const filesTouched = [];
|
|
8782
8938
|
const depsAdded = [];
|
|
8783
|
-
const pkgPath =
|
|
8939
|
+
const pkgPath = import_node_path47.default.join(ctx.scanPath, "package.json");
|
|
8784
8940
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8785
8941
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
8786
8942
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -8819,7 +8975,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
8819
8975
|
};
|
|
8820
8976
|
}
|
|
8821
8977
|
for (const file of hookFiles) {
|
|
8822
|
-
const content = await import_node_fs28.promises.readFile(
|
|
8978
|
+
const content = await import_node_fs28.promises.readFile(import_node_path47.default.join(ctx.scanPath, file), "utf8");
|
|
8823
8979
|
if (content.includes(args.registration_snippet)) {
|
|
8824
8980
|
return {
|
|
8825
8981
|
library: args.library,
|
|
@@ -8860,7 +9016,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8860
9016
|
if (!match) {
|
|
8861
9017
|
return { undone: false, message: "no apply found for library" };
|
|
8862
9018
|
}
|
|
8863
|
-
const pkgPath =
|
|
9019
|
+
const pkgPath = import_node_path47.default.join(ctx.scanPath, "package.json");
|
|
8864
9020
|
if (await fileExists2(pkgPath)) {
|
|
8865
9021
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8866
9022
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8871,7 +9027,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8871
9027
|
}
|
|
8872
9028
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8873
9029
|
for (const file of hookFiles) {
|
|
8874
|
-
const filePath =
|
|
9030
|
+
const filePath = import_node_path47.default.join(ctx.scanPath, file);
|
|
8875
9031
|
const content = await import_node_fs28.promises.readFile(filePath, "utf8");
|
|
8876
9032
|
if (content.includes(match.registration_snippet)) {
|
|
8877
9033
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -9009,23 +9165,23 @@ function canonicalJson(value) {
|
|
|
9009
9165
|
|
|
9010
9166
|
// src/projects.ts
|
|
9011
9167
|
init_cjs_shims();
|
|
9012
|
-
var
|
|
9168
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
9013
9169
|
function pathsForProject(project, baseDir) {
|
|
9014
9170
|
if (project === DEFAULT_PROJECT) {
|
|
9015
9171
|
return {
|
|
9016
|
-
snapshotPath:
|
|
9017
|
-
errorsPath:
|
|
9018
|
-
staleEventsPath:
|
|
9019
|
-
embeddingsCachePath:
|
|
9020
|
-
policyViolationsPath:
|
|
9172
|
+
snapshotPath: import_node_path48.default.join(baseDir, "graph.json"),
|
|
9173
|
+
errorsPath: import_node_path48.default.join(baseDir, "errors.ndjson"),
|
|
9174
|
+
staleEventsPath: import_node_path48.default.join(baseDir, "stale-events.ndjson"),
|
|
9175
|
+
embeddingsCachePath: import_node_path48.default.join(baseDir, "embeddings.json"),
|
|
9176
|
+
policyViolationsPath: import_node_path48.default.join(baseDir, "policy-violations.ndjson")
|
|
9021
9177
|
};
|
|
9022
9178
|
}
|
|
9023
9179
|
return {
|
|
9024
|
-
snapshotPath:
|
|
9025
|
-
errorsPath:
|
|
9026
|
-
staleEventsPath:
|
|
9027
|
-
embeddingsCachePath:
|
|
9028
|
-
policyViolationsPath:
|
|
9180
|
+
snapshotPath: import_node_path48.default.join(baseDir, `${project}.json`),
|
|
9181
|
+
errorsPath: import_node_path48.default.join(baseDir, `errors.${project}.ndjson`),
|
|
9182
|
+
staleEventsPath: import_node_path48.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
9183
|
+
embeddingsCachePath: import_node_path48.default.join(baseDir, `embeddings.${project}.json`),
|
|
9184
|
+
policyViolationsPath: import_node_path48.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
9029
9185
|
};
|
|
9030
9186
|
}
|
|
9031
9187
|
var Projects = class {
|
|
@@ -9063,26 +9219,26 @@ var Projects = class {
|
|
|
9063
9219
|
init_cjs_shims();
|
|
9064
9220
|
var import_node_fs30 = require("fs");
|
|
9065
9221
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
9066
|
-
var
|
|
9067
|
-
var
|
|
9222
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
9223
|
+
var import_types35 = require("@neat.is/types");
|
|
9068
9224
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
9069
9225
|
var LOCK_RETRY_MS = 50;
|
|
9070
9226
|
function neatHome() {
|
|
9071
9227
|
const override = process.env.NEAT_HOME;
|
|
9072
|
-
if (override && override.length > 0) return
|
|
9073
|
-
return
|
|
9228
|
+
if (override && override.length > 0) return import_node_path49.default.resolve(override);
|
|
9229
|
+
return import_node_path49.default.join(import_node_os3.default.homedir(), ".neat");
|
|
9074
9230
|
}
|
|
9075
9231
|
function registryPath() {
|
|
9076
|
-
return
|
|
9232
|
+
return import_node_path49.default.join(neatHome(), "projects.json");
|
|
9077
9233
|
}
|
|
9078
9234
|
function registryLockPath() {
|
|
9079
|
-
return
|
|
9235
|
+
return import_node_path49.default.join(neatHome(), "projects.json.lock");
|
|
9080
9236
|
}
|
|
9081
9237
|
function daemonPidPath() {
|
|
9082
|
-
return
|
|
9238
|
+
return import_node_path49.default.join(neatHome(), "neatd.pid");
|
|
9083
9239
|
}
|
|
9084
9240
|
function daemonsDir() {
|
|
9085
|
-
return
|
|
9241
|
+
return import_node_path49.default.join(neatHome(), "daemons");
|
|
9086
9242
|
}
|
|
9087
9243
|
function isFiniteInt(v) {
|
|
9088
9244
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -9123,7 +9279,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
9123
9279
|
const out = [];
|
|
9124
9280
|
for (const name of names) {
|
|
9125
9281
|
if (!name.endsWith(".json")) continue;
|
|
9126
|
-
const file =
|
|
9282
|
+
const file = import_node_path49.default.join(dir, name);
|
|
9127
9283
|
let raw;
|
|
9128
9284
|
try {
|
|
9129
9285
|
raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
@@ -9244,7 +9400,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
9244
9400
|
}
|
|
9245
9401
|
}
|
|
9246
9402
|
async function normalizeProjectPath(input) {
|
|
9247
|
-
const resolved =
|
|
9403
|
+
const resolved = import_node_path49.default.resolve(input);
|
|
9248
9404
|
try {
|
|
9249
9405
|
return await import_node_fs30.promises.realpath(resolved);
|
|
9250
9406
|
} catch {
|
|
@@ -9252,7 +9408,7 @@ async function normalizeProjectPath(input) {
|
|
|
9252
9408
|
}
|
|
9253
9409
|
}
|
|
9254
9410
|
async function writeAtomically(target, contents) {
|
|
9255
|
-
await import_node_fs30.promises.mkdir(
|
|
9411
|
+
await import_node_fs30.promises.mkdir(import_node_path49.default.dirname(target), { recursive: true });
|
|
9256
9412
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
9257
9413
|
const fd = await import_node_fs30.promises.open(tmp, "w");
|
|
9258
9414
|
try {
|
|
@@ -9265,7 +9421,7 @@ async function writeAtomically(target, contents) {
|
|
|
9265
9421
|
}
|
|
9266
9422
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
9267
9423
|
const deadline = Date.now() + timeoutMs;
|
|
9268
|
-
await import_node_fs30.promises.mkdir(
|
|
9424
|
+
await import_node_fs30.promises.mkdir(import_node_path49.default.dirname(lockPath), { recursive: true });
|
|
9269
9425
|
let probedHolder = false;
|
|
9270
9426
|
while (true) {
|
|
9271
9427
|
try {
|
|
@@ -9318,10 +9474,10 @@ async function readRegistry() {
|
|
|
9318
9474
|
throw err;
|
|
9319
9475
|
}
|
|
9320
9476
|
const parsed = JSON.parse(raw);
|
|
9321
|
-
return
|
|
9477
|
+
return import_types35.RegistryFileSchema.parse(parsed);
|
|
9322
9478
|
}
|
|
9323
9479
|
async function writeRegistry(reg) {
|
|
9324
|
-
const validated =
|
|
9480
|
+
const validated = import_types35.RegistryFileSchema.parse(reg);
|
|
9325
9481
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
9326
9482
|
}
|
|
9327
9483
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -9507,7 +9663,7 @@ init_auth();
|
|
|
9507
9663
|
// src/connectors-config.ts
|
|
9508
9664
|
init_cjs_shims();
|
|
9509
9665
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
9510
|
-
var
|
|
9666
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9511
9667
|
var import_node_fs31 = require("fs");
|
|
9512
9668
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
9513
9669
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -9522,11 +9678,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
9522
9678
|
};
|
|
9523
9679
|
function neatHome2() {
|
|
9524
9680
|
const override = process.env.NEAT_HOME;
|
|
9525
|
-
if (override && override.length > 0) return
|
|
9526
|
-
return
|
|
9681
|
+
if (override && override.length > 0) return import_node_path50.default.resolve(override);
|
|
9682
|
+
return import_node_path50.default.join(import_node_os4.default.homedir(), ".neat");
|
|
9527
9683
|
}
|
|
9528
9684
|
function connectorsConfigPath(home = neatHome2()) {
|
|
9529
|
-
return
|
|
9685
|
+
return import_node_path50.default.join(home, "connectors.json");
|
|
9530
9686
|
}
|
|
9531
9687
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
9532
9688
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9657,7 +9813,7 @@ function connectorMatchesProject(entry2, project) {
|
|
|
9657
9813
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
9658
9814
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
9659
9815
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
9660
|
-
return
|
|
9816
|
+
return import_node_path50.default.join(home, "connectors.json.lock");
|
|
9661
9817
|
}
|
|
9662
9818
|
function isEnvRef(value) {
|
|
9663
9819
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -9670,7 +9826,7 @@ function redactCredentialRef(ref) {
|
|
|
9670
9826
|
return out;
|
|
9671
9827
|
}
|
|
9672
9828
|
async function writeConfigAtomically0600(file, contents) {
|
|
9673
|
-
await import_node_fs31.promises.mkdir(
|
|
9829
|
+
await import_node_fs31.promises.mkdir(import_node_path50.default.dirname(file), { recursive: true });
|
|
9674
9830
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
9675
9831
|
const fd = await import_node_fs31.promises.open(tmp, "w", 384);
|
|
9676
9832
|
try {
|
|
@@ -9684,7 +9840,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
9684
9840
|
}
|
|
9685
9841
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
9686
9842
|
const deadline = Date.now() + timeoutMs;
|
|
9687
|
-
await import_node_fs31.promises.mkdir(
|
|
9843
|
+
await import_node_fs31.promises.mkdir(import_node_path50.default.dirname(lockPath), { recursive: true });
|
|
9688
9844
|
for (; ; ) {
|
|
9689
9845
|
try {
|
|
9690
9846
|
const fd = await import_node_fs31.promises.open(lockPath, "wx");
|
|
@@ -9967,11 +10123,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9967
10123
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9968
10124
|
const parsed = [];
|
|
9969
10125
|
for (const c of candidates) {
|
|
9970
|
-
const r =
|
|
10126
|
+
const r = import_types36.DivergenceTypeSchema.safeParse(c);
|
|
9971
10127
|
if (!r.success) {
|
|
9972
10128
|
return reply.code(400).send({
|
|
9973
10129
|
error: `unknown divergence type "${c}"`,
|
|
9974
|
-
allowed:
|
|
10130
|
+
allowed: import_types36.DivergenceTypeSchema.options
|
|
9975
10131
|
});
|
|
9976
10132
|
}
|
|
9977
10133
|
parsed.push(r.data);
|
|
@@ -10242,7 +10398,7 @@ function registerRoutes(scope, ctx) {
|
|
|
10242
10398
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
10243
10399
|
let violations = await log.readAll();
|
|
10244
10400
|
if (req.query.severity) {
|
|
10245
|
-
const sev =
|
|
10401
|
+
const sev = import_types36.PolicySeveritySchema.safeParse(req.query.severity);
|
|
10246
10402
|
if (!sev.success) {
|
|
10247
10403
|
return reply.code(400).send({
|
|
10248
10404
|
error: "invalid severity",
|
|
@@ -10281,7 +10437,7 @@ function registerRoutes(scope, ctx) {
|
|
|
10281
10437
|
scope.post("/policies/check", async (req, reply) => {
|
|
10282
10438
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
10283
10439
|
if (!proj) return;
|
|
10284
|
-
const parsed =
|
|
10440
|
+
const parsed = import_types36.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
10285
10441
|
if (!parsed.success) {
|
|
10286
10442
|
return reply.code(400).send({
|
|
10287
10443
|
error: "invalid /policies/check body",
|
|
@@ -10583,13 +10739,13 @@ init_otel();
|
|
|
10583
10739
|
// src/daemon.ts
|
|
10584
10740
|
init_cjs_shims();
|
|
10585
10741
|
var import_node_fs33 = require("fs");
|
|
10586
|
-
var
|
|
10742
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
10587
10743
|
var import_node_module = require("module");
|
|
10588
10744
|
init_otel();
|
|
10589
10745
|
|
|
10590
10746
|
// src/connectors/index.ts
|
|
10591
10747
|
init_cjs_shims();
|
|
10592
|
-
var
|
|
10748
|
+
var import_types37 = require("@neat.is/types");
|
|
10593
10749
|
|
|
10594
10750
|
// src/connectors/registry.ts
|
|
10595
10751
|
init_cjs_shims();
|
|
@@ -10964,10 +11120,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10964
11120
|
// src/connectors/supabase/map.ts
|
|
10965
11121
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10966
11122
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10967
|
-
function targetFromRestPath(
|
|
10968
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
11123
|
+
function targetFromRestPath(path64) {
|
|
11124
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path64);
|
|
10969
11125
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10970
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
11126
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path64);
|
|
10971
11127
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10972
11128
|
return null;
|
|
10973
11129
|
}
|
|
@@ -11076,23 +11232,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
11076
11232
|
|
|
11077
11233
|
// src/connectors/supabase/resolve.ts
|
|
11078
11234
|
init_cjs_shims();
|
|
11079
|
-
var
|
|
11235
|
+
var import_types39 = require("@neat.is/types");
|
|
11080
11236
|
function createSupabaseResolveTarget(graph, config) {
|
|
11081
11237
|
return (signal, _ctx) => {
|
|
11082
11238
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
11083
11239
|
return null;
|
|
11084
11240
|
}
|
|
11085
|
-
const subResourceId = (0,
|
|
11241
|
+
const subResourceId = (0, import_types39.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
11086
11242
|
if (graph.hasNode(subResourceId)) {
|
|
11087
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
11243
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types39.EdgeType.CALLS };
|
|
11088
11244
|
}
|
|
11089
|
-
const bareResourceId = (0,
|
|
11245
|
+
const bareResourceId = (0, import_types39.infraId)(signal.targetKind, signal.targetName);
|
|
11090
11246
|
if (graph.hasNode(bareResourceId)) {
|
|
11091
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
11247
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types39.EdgeType.CALLS };
|
|
11092
11248
|
}
|
|
11093
|
-
const projectLevelId = (0,
|
|
11249
|
+
const projectLevelId = (0, import_types39.infraId)("supabase", config.nodeRef);
|
|
11094
11250
|
if (graph.hasNode(projectLevelId)) {
|
|
11095
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
11251
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types39.EdgeType.CALLS };
|
|
11096
11252
|
}
|
|
11097
11253
|
return null;
|
|
11098
11254
|
};
|
|
@@ -11185,7 +11341,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
11185
11341
|
|
|
11186
11342
|
// src/connectors/railway/index.ts
|
|
11187
11343
|
init_cjs_shims();
|
|
11188
|
-
var
|
|
11344
|
+
var import_types43 = require("@neat.is/types");
|
|
11189
11345
|
|
|
11190
11346
|
// src/connectors/railway/client.ts
|
|
11191
11347
|
init_cjs_shims();
|
|
@@ -11311,7 +11467,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
11311
11467
|
const out = [];
|
|
11312
11468
|
graph.forEachNode((_id, attrs) => {
|
|
11313
11469
|
const node = attrs;
|
|
11314
|
-
if (node.type !==
|
|
11470
|
+
if (node.type !== import_types43.NodeType.RouteNode) return;
|
|
11315
11471
|
const route = attrs;
|
|
11316
11472
|
if (route.service !== serviceName) return;
|
|
11317
11473
|
out.push({
|
|
@@ -11415,12 +11571,12 @@ function createRailwayResolveTarget(config) {
|
|
|
11415
11571
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
11416
11572
|
if (!serviceName) return null;
|
|
11417
11573
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
11418
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11574
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types43.EdgeType.CALLS };
|
|
11419
11575
|
}
|
|
11420
11576
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
11421
11577
|
const peerName = config.serviceNameById[signal.targetName];
|
|
11422
11578
|
if (!peerName) return null;
|
|
11423
|
-
return { targetNodeId: (0,
|
|
11579
|
+
return { targetNodeId: (0, import_types43.serviceId)(peerName), serviceName, edgeType: import_types43.EdgeType.CONNECTS_TO };
|
|
11424
11580
|
}
|
|
11425
11581
|
return null;
|
|
11426
11582
|
};
|
|
@@ -11544,9 +11700,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11544
11700
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11545
11701
|
if (secondSep === -1) return null;
|
|
11546
11702
|
const method = rest.slice(0, secondSep);
|
|
11547
|
-
const
|
|
11548
|
-
if (!resourceName || !method || !
|
|
11549
|
-
return { resourceName, method, path:
|
|
11703
|
+
const path64 = rest.slice(secondSep + 1);
|
|
11704
|
+
if (!resourceName || !method || !path64) return null;
|
|
11705
|
+
return { resourceName, method, path: path64 };
|
|
11550
11706
|
}
|
|
11551
11707
|
function resourceNameFor(type, labels) {
|
|
11552
11708
|
if (!labels) return null;
|
|
@@ -11584,14 +11740,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
11584
11740
|
if (!req) return null;
|
|
11585
11741
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11586
11742
|
const method = req.requestMethod.toUpperCase();
|
|
11587
|
-
const
|
|
11588
|
-
if (
|
|
11743
|
+
const path64 = pathFromRequestUrl(req.requestUrl);
|
|
11744
|
+
if (path64 === null) return null;
|
|
11589
11745
|
const timestamp = entry2.timestamp;
|
|
11590
11746
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11591
11747
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11592
11748
|
return {
|
|
11593
11749
|
targetKind: resourceType,
|
|
11594
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11750
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path64 }),
|
|
11595
11751
|
callCount: 1,
|
|
11596
11752
|
errorCount: isError ? 1 : 0,
|
|
11597
11753
|
lastObservedIso: timestamp
|
|
@@ -11608,7 +11764,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
11608
11764
|
|
|
11609
11765
|
// src/connectors/firebase/resolve.ts
|
|
11610
11766
|
init_cjs_shims();
|
|
11611
|
-
var
|
|
11767
|
+
var import_types44 = require("@neat.is/types");
|
|
11612
11768
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
11613
11769
|
switch (resourceType) {
|
|
11614
11770
|
case "cloud_function":
|
|
@@ -11623,7 +11779,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
11623
11779
|
const entries = [];
|
|
11624
11780
|
graph.forEachNode((_id, attrs) => {
|
|
11625
11781
|
const node = attrs;
|
|
11626
|
-
if (node.type !==
|
|
11782
|
+
if (node.type !== import_types44.NodeType.RouteNode) return;
|
|
11627
11783
|
const route = attrs;
|
|
11628
11784
|
if (route.service !== serviceName) return;
|
|
11629
11785
|
entries.push({
|
|
@@ -11655,7 +11811,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
11655
11811
|
return {
|
|
11656
11812
|
targetNodeId: match.routeNodeId,
|
|
11657
11813
|
serviceName,
|
|
11658
|
-
edgeType:
|
|
11814
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11659
11815
|
};
|
|
11660
11816
|
};
|
|
11661
11817
|
}
|
|
@@ -11682,7 +11838,7 @@ init_cjs_shims();
|
|
|
11682
11838
|
|
|
11683
11839
|
// src/connectors/cloudflare/connector.ts
|
|
11684
11840
|
init_cjs_shims();
|
|
11685
|
-
var
|
|
11841
|
+
var import_types46 = require("@neat.is/types");
|
|
11686
11842
|
|
|
11687
11843
|
// src/connectors/cloudflare/client.ts
|
|
11688
11844
|
init_cjs_shims();
|
|
@@ -11798,7 +11954,7 @@ function mapEventToSignal(event) {
|
|
|
11798
11954
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11799
11955
|
const statusCode = metadata?.statusCode;
|
|
11800
11956
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11801
|
-
const
|
|
11957
|
+
const path64 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11802
11958
|
return {
|
|
11803
11959
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11804
11960
|
targetName: scriptName,
|
|
@@ -11806,7 +11962,7 @@ function mapEventToSignal(event) {
|
|
|
11806
11962
|
errorCount: isError ? 1 : 0,
|
|
11807
11963
|
lastObservedIso: observedAt.toISOString(),
|
|
11808
11964
|
method,
|
|
11809
|
-
...
|
|
11965
|
+
...path64 ? { path: path64 } : {},
|
|
11810
11966
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11811
11967
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11812
11968
|
};
|
|
@@ -11846,19 +12002,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11846
12002
|
graph.forEachNode((id, attrs) => {
|
|
11847
12003
|
if (found) return;
|
|
11848
12004
|
const a = attrs;
|
|
11849
|
-
if (a.type ===
|
|
12005
|
+
if (a.type === import_types46.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11850
12006
|
found = id;
|
|
11851
12007
|
}
|
|
11852
12008
|
});
|
|
11853
12009
|
return found;
|
|
11854
12010
|
}
|
|
11855
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11856
|
-
const normalizedPath = normalizePathTemplate(
|
|
12011
|
+
function findMatchingRouteNode(graph, serviceName, method, path64) {
|
|
12012
|
+
const normalizedPath = normalizePathTemplate(path64);
|
|
11857
12013
|
let found = null;
|
|
11858
12014
|
graph.forEachNode((id, attrs) => {
|
|
11859
12015
|
if (found) return;
|
|
11860
12016
|
const a = attrs;
|
|
11861
|
-
if (a.type !==
|
|
12017
|
+
if (a.type !== import_types46.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11862
12018
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11863
12019
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11864
12020
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11870,18 +12026,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11870
12026
|
return (signal) => {
|
|
11871
12027
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11872
12028
|
const scriptName = signal.targetName;
|
|
11873
|
-
const { method, path:
|
|
12029
|
+
const { method, path: path64 } = signal;
|
|
11874
12030
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11875
|
-
if (!method || !
|
|
11876
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
12031
|
+
if (!method || !path64) return wholeFileId;
|
|
12032
|
+
return findMatchingRouteNode(graph, serviceName, method, path64) ?? wholeFileId;
|
|
11877
12033
|
};
|
|
11878
12034
|
const mapping = config.workers?.[scriptName];
|
|
11879
12035
|
if (mapping) {
|
|
11880
|
-
const wholeFileId = (0,
|
|
12036
|
+
const wholeFileId = (0, import_types46.fileId)(mapping.service, mapping.entryFile);
|
|
11881
12037
|
return {
|
|
11882
12038
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11883
12039
|
serviceName: mapping.service,
|
|
11884
|
-
edgeType:
|
|
12040
|
+
edgeType: import_types46.EdgeType.CALLS
|
|
11885
12041
|
};
|
|
11886
12042
|
}
|
|
11887
12043
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11890,13 +12046,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11890
12046
|
return {
|
|
11891
12047
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11892
12048
|
serviceName: fileNode.service,
|
|
11893
|
-
edgeType:
|
|
12049
|
+
edgeType: import_types46.EdgeType.CALLS
|
|
11894
12050
|
};
|
|
11895
12051
|
}
|
|
11896
12052
|
return {
|
|
11897
|
-
targetNodeId: (0,
|
|
12053
|
+
targetNodeId: (0, import_types46.infraId)("cloudflare-worker", scriptName),
|
|
11898
12054
|
serviceName: scriptName,
|
|
11899
|
-
edgeType:
|
|
12055
|
+
edgeType: import_types46.EdgeType.CALLS,
|
|
11900
12056
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11901
12057
|
};
|
|
11902
12058
|
};
|
|
@@ -12355,28 +12511,28 @@ init_auth();
|
|
|
12355
12511
|
// src/unrouted.ts
|
|
12356
12512
|
init_cjs_shims();
|
|
12357
12513
|
var import_node_fs32 = require("fs");
|
|
12358
|
-
var
|
|
12514
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
12359
12515
|
|
|
12360
12516
|
// src/daemon.ts
|
|
12361
|
-
var
|
|
12517
|
+
var import_types49 = require("@neat.is/types");
|
|
12362
12518
|
function daemonJsonPath(scanPath) {
|
|
12363
|
-
return
|
|
12519
|
+
return import_node_path54.default.join(scanPath, "neat-out", "daemon.json");
|
|
12364
12520
|
}
|
|
12365
12521
|
function daemonsDiscoveryDir(home) {
|
|
12366
12522
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
12367
|
-
return
|
|
12523
|
+
return import_node_path54.default.join(base, "daemons");
|
|
12368
12524
|
}
|
|
12369
12525
|
function daemonDiscoveryPath(project, home) {
|
|
12370
|
-
return
|
|
12526
|
+
return import_node_path54.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
12371
12527
|
}
|
|
12372
12528
|
function sanitizeDiscoveryName(project) {
|
|
12373
12529
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
12374
12530
|
}
|
|
12375
12531
|
function neatHomeFromEnv() {
|
|
12376
12532
|
const env = process.env.NEAT_HOME;
|
|
12377
|
-
if (env && env.length > 0) return
|
|
12533
|
+
if (env && env.length > 0) return import_node_path54.default.resolve(env);
|
|
12378
12534
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12379
|
-
return
|
|
12535
|
+
return import_node_path54.default.join(home, ".neat");
|
|
12380
12536
|
}
|
|
12381
12537
|
async function readDaemonRecord(scanPath) {
|
|
12382
12538
|
try {
|
|
@@ -12447,7 +12603,7 @@ init_otel_grpc();
|
|
|
12447
12603
|
// src/search.ts
|
|
12448
12604
|
init_cjs_shims();
|
|
12449
12605
|
var import_node_fs34 = require("fs");
|
|
12450
|
-
var
|
|
12606
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12451
12607
|
var import_node_crypto4 = require("crypto");
|
|
12452
12608
|
var DEFAULT_LIMIT = 10;
|
|
12453
12609
|
var NOMIC_DIM = 768;
|
|
@@ -12610,7 +12766,7 @@ async function readCache(cachePath) {
|
|
|
12610
12766
|
}
|
|
12611
12767
|
}
|
|
12612
12768
|
async function writeCache(cachePath, cache) {
|
|
12613
|
-
await import_node_fs34.promises.mkdir(
|
|
12769
|
+
await import_node_fs34.promises.mkdir(import_node_path55.default.dirname(cachePath), { recursive: true });
|
|
12614
12770
|
await import_node_fs34.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
12615
12771
|
}
|
|
12616
12772
|
var VectorIndex = class {
|
|
@@ -12768,8 +12924,8 @@ var ALL_PHASES = [
|
|
|
12768
12924
|
];
|
|
12769
12925
|
function classifyChange(relPath) {
|
|
12770
12926
|
const phases = /* @__PURE__ */ new Set();
|
|
12771
|
-
const base =
|
|
12772
|
-
const segments = relPath.split(
|
|
12927
|
+
const base = import_node_path56.default.basename(relPath).toLowerCase();
|
|
12928
|
+
const segments = relPath.split(import_node_path56.default.sep).map((s) => s.toLowerCase());
|
|
12773
12929
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
12774
12930
|
phases.add("services");
|
|
12775
12931
|
phases.add("aliases");
|
|
@@ -12897,9 +13053,9 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
12897
13053
|
for (const e of entries) {
|
|
12898
13054
|
if (count >= limit) return;
|
|
12899
13055
|
if (!e.isDirectory()) continue;
|
|
12900
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
13056
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path56.default.join(dir, e.name) + import_node_path56.default.sep))) continue;
|
|
12901
13057
|
count++;
|
|
12902
|
-
if (depth < 2) visit(
|
|
13058
|
+
if (depth < 2) visit(import_node_path56.default.join(dir, e.name), depth + 1);
|
|
12903
13059
|
}
|
|
12904
13060
|
};
|
|
12905
13061
|
visit(scanPath, 0);
|
|
@@ -12917,8 +13073,8 @@ async function startWatch(graph, opts) {
|
|
|
12917
13073
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
12918
13074
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
12919
13075
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
12920
|
-
const policyFilePath =
|
|
12921
|
-
const policyViolationsPath =
|
|
13076
|
+
const policyFilePath = import_node_path56.default.join(opts.scanPath, "policy.json");
|
|
13077
|
+
const policyViolationsPath = import_node_path56.default.join(import_node_path56.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
12922
13078
|
let policies = [];
|
|
12923
13079
|
try {
|
|
12924
13080
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -12969,7 +13125,7 @@ async function startWatch(graph, opts) {
|
|
|
12969
13125
|
assertBindAuthority(host, auth.authToken);
|
|
12970
13126
|
const port = opts.port ?? 8080;
|
|
12971
13127
|
const otelPort = opts.otelPort ?? 4318;
|
|
12972
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
13128
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path56.default.join(import_node_path56.default.dirname(opts.outPath), "embeddings.json");
|
|
12973
13129
|
let searchIndex;
|
|
12974
13130
|
try {
|
|
12975
13131
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -12987,7 +13143,7 @@ async function startWatch(graph, opts) {
|
|
|
12987
13143
|
// Paths are derived from the explicit options the watch caller passes
|
|
12988
13144
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
12989
13145
|
// fields so the registry shape is complete.
|
|
12990
|
-
...pathsForProject(projectName,
|
|
13146
|
+
...pathsForProject(projectName, import_node_path56.default.dirname(opts.outPath)),
|
|
12991
13147
|
snapshotPath: opts.outPath,
|
|
12992
13148
|
errorsPath: opts.errorsPath,
|
|
12993
13149
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -13105,9 +13261,9 @@ async function startWatch(graph, opts) {
|
|
|
13105
13261
|
};
|
|
13106
13262
|
const onPath = (absPath) => {
|
|
13107
13263
|
if (shouldIgnore(absPath)) return;
|
|
13108
|
-
const rel =
|
|
13264
|
+
const rel = import_node_path56.default.relative(opts.scanPath, absPath);
|
|
13109
13265
|
if (!rel || rel.startsWith("..")) return;
|
|
13110
|
-
pendingPaths.add(rel.split(
|
|
13266
|
+
pendingPaths.add(rel.split(import_node_path56.default.sep).join("/"));
|
|
13111
13267
|
const phases = classifyChange(rel);
|
|
13112
13268
|
if (phases.size === 0) {
|
|
13113
13269
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -13164,7 +13320,7 @@ async function startWatch(graph, opts) {
|
|
|
13164
13320
|
// src/deploy/detect.ts
|
|
13165
13321
|
init_cjs_shims();
|
|
13166
13322
|
var import_node_fs36 = require("fs");
|
|
13167
|
-
var
|
|
13323
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
13168
13324
|
var import_node_child_process2 = require("child_process");
|
|
13169
13325
|
var import_node_crypto5 = require("crypto");
|
|
13170
13326
|
function generateToken() {
|
|
@@ -13264,7 +13420,7 @@ async function runDeploy(opts = {}) {
|
|
|
13264
13420
|
const token = generateToken();
|
|
13265
13421
|
switch (substrate) {
|
|
13266
13422
|
case "docker-compose": {
|
|
13267
|
-
const artifactPath =
|
|
13423
|
+
const artifactPath = import_node_path57.default.join(cwd, "docker-compose.neat.yml");
|
|
13268
13424
|
const contents = emitDockerCompose(cwd);
|
|
13269
13425
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
13270
13426
|
return {
|
|
@@ -13272,11 +13428,11 @@ async function runDeploy(opts = {}) {
|
|
|
13272
13428
|
artifactPath,
|
|
13273
13429
|
token,
|
|
13274
13430
|
contents,
|
|
13275
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
13431
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path57.default.basename(artifactPath)} up -d`
|
|
13276
13432
|
};
|
|
13277
13433
|
}
|
|
13278
13434
|
case "systemd": {
|
|
13279
|
-
const artifactPath =
|
|
13435
|
+
const artifactPath = import_node_path57.default.join(cwd, "neat.service");
|
|
13280
13436
|
const contents = emitSystemdUnit(cwd);
|
|
13281
13437
|
await import_node_fs36.promises.writeFile(artifactPath, contents, "utf8");
|
|
13282
13438
|
return {
|
|
@@ -13312,7 +13468,7 @@ init_cjs_shims();
|
|
|
13312
13468
|
// src/installers/javascript.ts
|
|
13313
13469
|
init_cjs_shims();
|
|
13314
13470
|
var import_node_fs37 = require("fs");
|
|
13315
|
-
var
|
|
13471
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
13316
13472
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
13317
13473
|
|
|
13318
13474
|
// src/installers/templates.ts
|
|
@@ -13965,11 +14121,11 @@ var OTEL_ENV = {
|
|
|
13965
14121
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
13966
14122
|
};
|
|
13967
14123
|
function serviceNodeName(pkg, serviceDir) {
|
|
13968
|
-
return pkg.name ??
|
|
14124
|
+
return pkg.name ?? import_node_path58.default.basename(serviceDir);
|
|
13969
14125
|
}
|
|
13970
14126
|
function projectToken(pkg, serviceDir, project) {
|
|
13971
14127
|
if (project && project.length > 0) return project;
|
|
13972
|
-
return pkg.name ??
|
|
14128
|
+
return pkg.name ?? import_node_path58.default.basename(serviceDir);
|
|
13973
14129
|
}
|
|
13974
14130
|
async function readJsonFile(p) {
|
|
13975
14131
|
try {
|
|
@@ -13982,16 +14138,16 @@ async function readJsonFile(p) {
|
|
|
13982
14138
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
13983
14139
|
const deps = allDeps(pkg);
|
|
13984
14140
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
13985
|
-
const appJson = await readJsonFile(
|
|
14141
|
+
const appJson = await readJsonFile(import_node_path58.default.join(pkgRoot, "app.json"));
|
|
13986
14142
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
13987
14143
|
return "react-native";
|
|
13988
14144
|
}
|
|
13989
|
-
if (await exists3(
|
|
14145
|
+
if (await exists3(import_node_path58.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path58.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path58.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
13990
14146
|
return "browser-bundle";
|
|
13991
14147
|
}
|
|
13992
|
-
if (await exists3(
|
|
13993
|
-
if (await exists3(
|
|
13994
|
-
if (await exists3(
|
|
14148
|
+
if (await exists3(import_node_path58.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
14149
|
+
if (await exists3(import_node_path58.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
14150
|
+
if (await exists3(import_node_path58.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path58.default.join(pkgRoot, "deno.lock"))) {
|
|
13995
14151
|
return "deno";
|
|
13996
14152
|
}
|
|
13997
14153
|
const engines = pkg.engines ?? {};
|
|
@@ -14000,7 +14156,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
14000
14156
|
}
|
|
14001
14157
|
async function readPackageJson2(serviceDir) {
|
|
14002
14158
|
try {
|
|
14003
|
-
const raw = await import_node_fs37.promises.readFile(
|
|
14159
|
+
const raw = await import_node_fs37.promises.readFile(import_node_path58.default.join(serviceDir, "package.json"), "utf8");
|
|
14004
14160
|
return JSON.parse(raw);
|
|
14005
14161
|
} catch {
|
|
14006
14162
|
return null;
|
|
@@ -14044,7 +14200,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
14044
14200
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
14045
14201
|
async function findNextConfig(serviceDir) {
|
|
14046
14202
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
14047
|
-
const candidate =
|
|
14203
|
+
const candidate = import_node_path58.default.join(serviceDir, name);
|
|
14048
14204
|
if (await exists3(candidate)) return candidate;
|
|
14049
14205
|
}
|
|
14050
14206
|
return null;
|
|
@@ -14113,7 +14269,7 @@ function hasRemixDependency(pkg) {
|
|
|
14113
14269
|
}
|
|
14114
14270
|
async function findRemixEntry(serviceDir) {
|
|
14115
14271
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
14116
|
-
const candidate =
|
|
14272
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14117
14273
|
if (await exists3(candidate)) return candidate;
|
|
14118
14274
|
}
|
|
14119
14275
|
return null;
|
|
@@ -14125,14 +14281,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
14125
14281
|
}
|
|
14126
14282
|
async function findSvelteKitHooks(serviceDir) {
|
|
14127
14283
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
14128
|
-
const candidate =
|
|
14284
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14129
14285
|
if (await exists3(candidate)) return candidate;
|
|
14130
14286
|
}
|
|
14131
14287
|
return null;
|
|
14132
14288
|
}
|
|
14133
14289
|
async function findSvelteKitConfig(serviceDir) {
|
|
14134
14290
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
14135
|
-
const candidate =
|
|
14291
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14136
14292
|
if (await exists3(candidate)) return candidate;
|
|
14137
14293
|
}
|
|
14138
14294
|
return null;
|
|
@@ -14143,7 +14299,7 @@ function hasNuxtDependency(pkg) {
|
|
|
14143
14299
|
}
|
|
14144
14300
|
async function findNuxtConfig(serviceDir) {
|
|
14145
14301
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
14146
|
-
const candidate =
|
|
14302
|
+
const candidate = import_node_path58.default.join(serviceDir, name);
|
|
14147
14303
|
if (await exists3(candidate)) return candidate;
|
|
14148
14304
|
}
|
|
14149
14305
|
return null;
|
|
@@ -14154,7 +14310,7 @@ function hasAstroDependency(pkg) {
|
|
|
14154
14310
|
}
|
|
14155
14311
|
async function findAstroConfig(serviceDir) {
|
|
14156
14312
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
14157
|
-
const candidate =
|
|
14313
|
+
const candidate = import_node_path58.default.join(serviceDir, name);
|
|
14158
14314
|
if (await exists3(candidate)) return candidate;
|
|
14159
14315
|
}
|
|
14160
14316
|
return null;
|
|
@@ -14168,7 +14324,7 @@ function parseNextMajor(range) {
|
|
|
14168
14324
|
return Number.isFinite(n) ? n : null;
|
|
14169
14325
|
}
|
|
14170
14326
|
async function isTypeScriptProject(serviceDir) {
|
|
14171
|
-
return exists3(
|
|
14327
|
+
return exists3(import_node_path58.default.join(serviceDir, "tsconfig.json"));
|
|
14172
14328
|
}
|
|
14173
14329
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
14174
14330
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -14217,7 +14373,7 @@ function entryFromScript(script) {
|
|
|
14217
14373
|
}
|
|
14218
14374
|
async function resolveEntry(serviceDir, pkg) {
|
|
14219
14375
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
14220
|
-
const candidate =
|
|
14376
|
+
const candidate = import_node_path58.default.resolve(serviceDir, pkg.main);
|
|
14221
14377
|
if (await exists3(candidate)) return candidate;
|
|
14222
14378
|
}
|
|
14223
14379
|
if (pkg.bin) {
|
|
@@ -14231,40 +14387,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
14231
14387
|
if (typeof first === "string") binEntry = first;
|
|
14232
14388
|
}
|
|
14233
14389
|
if (binEntry) {
|
|
14234
|
-
const candidate =
|
|
14390
|
+
const candidate = import_node_path58.default.resolve(serviceDir, binEntry);
|
|
14235
14391
|
if (await exists3(candidate)) return candidate;
|
|
14236
14392
|
}
|
|
14237
14393
|
}
|
|
14238
14394
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
14239
14395
|
if (startEntry) {
|
|
14240
|
-
const candidate =
|
|
14396
|
+
const candidate = import_node_path58.default.resolve(serviceDir, startEntry);
|
|
14241
14397
|
if (await exists3(candidate)) return candidate;
|
|
14242
14398
|
}
|
|
14243
14399
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
14244
14400
|
if (devEntry) {
|
|
14245
|
-
const candidate =
|
|
14401
|
+
const candidate = import_node_path58.default.resolve(serviceDir, devEntry);
|
|
14246
14402
|
if (await exists3(candidate)) return candidate;
|
|
14247
14403
|
}
|
|
14248
14404
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
14249
|
-
const candidate =
|
|
14405
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14250
14406
|
if (await exists3(candidate)) return candidate;
|
|
14251
14407
|
}
|
|
14252
14408
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
14253
|
-
const candidate =
|
|
14409
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14254
14410
|
if (await exists3(candidate)) return candidate;
|
|
14255
14411
|
}
|
|
14256
14412
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
14257
|
-
const candidate =
|
|
14413
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14258
14414
|
if (await exists3(candidate)) return candidate;
|
|
14259
14415
|
}
|
|
14260
14416
|
for (const name of INDEX_CANDIDATES) {
|
|
14261
|
-
const candidate =
|
|
14417
|
+
const candidate = import_node_path58.default.join(serviceDir, name);
|
|
14262
14418
|
if (await exists3(candidate)) return candidate;
|
|
14263
14419
|
}
|
|
14264
14420
|
return null;
|
|
14265
14421
|
}
|
|
14266
14422
|
function dispatchEntry(entryFile, pkg) {
|
|
14267
|
-
const ext =
|
|
14423
|
+
const ext = import_node_path58.default.extname(entryFile).toLowerCase();
|
|
14268
14424
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
14269
14425
|
if (ext === ".mjs") return "esm";
|
|
14270
14426
|
if (ext === ".cjs") return "cjs";
|
|
@@ -14281,9 +14437,9 @@ function otelInitContents(flavor) {
|
|
|
14281
14437
|
return OTEL_INIT_CJS;
|
|
14282
14438
|
}
|
|
14283
14439
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
14284
|
-
let rel =
|
|
14440
|
+
let rel = import_node_path58.default.relative(import_node_path58.default.dirname(entryFile), otelInitFile);
|
|
14285
14441
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
14286
|
-
rel = rel.split(
|
|
14442
|
+
rel = rel.split(import_node_path58.default.sep).join("/");
|
|
14287
14443
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
14288
14444
|
if (flavor === "esm") return `import '${rel}'`;
|
|
14289
14445
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -14296,27 +14452,27 @@ function lineIsOtelInjection(line) {
|
|
|
14296
14452
|
}
|
|
14297
14453
|
async function detectsSrcLayout(serviceDir) {
|
|
14298
14454
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
14299
|
-
exists3(
|
|
14300
|
-
exists3(
|
|
14301
|
-
exists3(
|
|
14302
|
-
exists3(
|
|
14455
|
+
exists3(import_node_path58.default.join(serviceDir, "src", "app")),
|
|
14456
|
+
exists3(import_node_path58.default.join(serviceDir, "src", "pages")),
|
|
14457
|
+
exists3(import_node_path58.default.join(serviceDir, "app")),
|
|
14458
|
+
exists3(import_node_path58.default.join(serviceDir, "pages"))
|
|
14303
14459
|
]);
|
|
14304
14460
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
14305
14461
|
}
|
|
14306
14462
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
14307
14463
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
14308
14464
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
14309
|
-
const baseDir = srcLayout ?
|
|
14310
|
-
const instrumentationFile =
|
|
14311
|
-
const instrumentationNodeFile =
|
|
14465
|
+
const baseDir = srcLayout ? import_node_path58.default.join(serviceDir, "src") : serviceDir;
|
|
14466
|
+
const instrumentationFile = import_node_path58.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
14467
|
+
const instrumentationNodeFile = import_node_path58.default.join(
|
|
14312
14468
|
baseDir,
|
|
14313
14469
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
14314
14470
|
);
|
|
14315
|
-
const instrumentationEdgeFile =
|
|
14471
|
+
const instrumentationEdgeFile = import_node_path58.default.join(
|
|
14316
14472
|
baseDir,
|
|
14317
14473
|
useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
|
|
14318
14474
|
);
|
|
14319
|
-
const envNeatFile =
|
|
14475
|
+
const envNeatFile = import_node_path58.default.join(baseDir, ".env.neat");
|
|
14320
14476
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
14321
14477
|
const dependencyEdits = [];
|
|
14322
14478
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -14441,7 +14597,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
14441
14597
|
return edits;
|
|
14442
14598
|
}
|
|
14443
14599
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
14444
|
-
const envNeatFile =
|
|
14600
|
+
const envNeatFile = import_node_path58.default.join(serviceDir, ".env.neat");
|
|
14445
14601
|
if (!await exists3(envNeatFile)) {
|
|
14446
14602
|
generatedFiles.push({
|
|
14447
14603
|
file: envNeatFile,
|
|
@@ -14476,7 +14632,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
14476
14632
|
}
|
|
14477
14633
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
14478
14634
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
14479
|
-
const otelServerFile =
|
|
14635
|
+
const otelServerFile = import_node_path58.default.join(
|
|
14480
14636
|
serviceDir,
|
|
14481
14637
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
14482
14638
|
);
|
|
@@ -14533,11 +14689,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
14533
14689
|
}
|
|
14534
14690
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
14535
14691
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
14536
|
-
const otelInitFile =
|
|
14692
|
+
const otelInitFile = import_node_path58.default.join(
|
|
14537
14693
|
serviceDir,
|
|
14538
14694
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
14539
14695
|
);
|
|
14540
|
-
const resolvedHooksFile = hooksFile ??
|
|
14696
|
+
const resolvedHooksFile = hooksFile ?? import_node_path58.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
14541
14697
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
14542
14698
|
const generatedFiles = [];
|
|
14543
14699
|
const entrypointEdits = [];
|
|
@@ -14599,11 +14755,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
14599
14755
|
}
|
|
14600
14756
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
14601
14757
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
14602
|
-
const otelPluginFile =
|
|
14758
|
+
const otelPluginFile = import_node_path58.default.join(
|
|
14603
14759
|
serviceDir,
|
|
14604
14760
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
14605
14761
|
);
|
|
14606
|
-
const otelInitFile =
|
|
14762
|
+
const otelInitFile = import_node_path58.default.join(
|
|
14607
14763
|
serviceDir,
|
|
14608
14764
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
14609
14765
|
);
|
|
@@ -14654,19 +14810,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
14654
14810
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
14655
14811
|
async function findAstroMiddleware(serviceDir) {
|
|
14656
14812
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
14657
|
-
const candidate =
|
|
14813
|
+
const candidate = import_node_path58.default.join(serviceDir, rel);
|
|
14658
14814
|
if (await exists3(candidate)) return candidate;
|
|
14659
14815
|
}
|
|
14660
14816
|
return null;
|
|
14661
14817
|
}
|
|
14662
14818
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
14663
14819
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
14664
|
-
const otelInitFile =
|
|
14820
|
+
const otelInitFile = import_node_path58.default.join(
|
|
14665
14821
|
serviceDir,
|
|
14666
14822
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
14667
14823
|
);
|
|
14668
14824
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
14669
|
-
const middlewareFile = existingMiddleware ??
|
|
14825
|
+
const middlewareFile = existingMiddleware ?? import_node_path58.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
14670
14826
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
14671
14827
|
const generatedFiles = [];
|
|
14672
14828
|
const entrypointEdits = [];
|
|
@@ -14762,7 +14918,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
14762
14918
|
}
|
|
14763
14919
|
async function plan(serviceDir, opts) {
|
|
14764
14920
|
const pkg = await readPackageJson2(serviceDir);
|
|
14765
|
-
const manifestPath =
|
|
14921
|
+
const manifestPath = import_node_path58.default.join(serviceDir, "package.json");
|
|
14766
14922
|
const project = opts?.project;
|
|
14767
14923
|
const empty = {
|
|
14768
14924
|
language: "javascript",
|
|
@@ -14797,8 +14953,8 @@ async function plan(serviceDir, opts) {
|
|
|
14797
14953
|
return { ...empty, libOnly: true };
|
|
14798
14954
|
}
|
|
14799
14955
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
14800
|
-
const otelInitFile =
|
|
14801
|
-
const envNeatFile =
|
|
14956
|
+
const otelInitFile = import_node_path58.default.join(import_node_path58.default.dirname(entryFile), otelInitFilename(flavor));
|
|
14957
|
+
const envNeatFile = import_node_path58.default.join(serviceDir, ".env.neat");
|
|
14802
14958
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
14803
14959
|
const dependencyEdits = [];
|
|
14804
14960
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -14867,13 +15023,13 @@ async function plan(serviceDir, opts) {
|
|
|
14867
15023
|
};
|
|
14868
15024
|
}
|
|
14869
15025
|
function isAllowedWritePath(serviceDir, target) {
|
|
14870
|
-
const rel =
|
|
15026
|
+
const rel = import_node_path58.default.relative(serviceDir, target);
|
|
14871
15027
|
if (rel.startsWith("..")) return false;
|
|
14872
|
-
const base =
|
|
15028
|
+
const base = import_node_path58.default.basename(target);
|
|
14873
15029
|
if (base === "package.json") return true;
|
|
14874
15030
|
if (base === ".env.neat") return true;
|
|
14875
15031
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
14876
|
-
const relPosix = rel.split(
|
|
15032
|
+
const relPosix = rel.split(import_node_path58.default.sep).join("/");
|
|
14877
15033
|
if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
14878
15034
|
if (relPosix === base) return true;
|
|
14879
15035
|
if (relPosix === `src/${base}`) return true;
|
|
@@ -14890,7 +15046,7 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
14890
15046
|
return false;
|
|
14891
15047
|
}
|
|
14892
15048
|
async function writeAtomic(file, contents) {
|
|
14893
|
-
await import_node_fs37.promises.mkdir(
|
|
15049
|
+
await import_node_fs37.promises.mkdir(import_node_path58.default.dirname(file), { recursive: true });
|
|
14894
15050
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
14895
15051
|
await import_node_fs37.promises.writeFile(tmp, contents, "utf8");
|
|
14896
15052
|
await import_node_fs37.promises.rename(tmp, file);
|
|
@@ -15074,7 +15230,7 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
15074
15230
|
...removed.map((f) => `removed: ${f}`),
|
|
15075
15231
|
""
|
|
15076
15232
|
];
|
|
15077
|
-
const rollbackPath =
|
|
15233
|
+
const rollbackPath = import_node_path58.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
15078
15234
|
await import_node_fs37.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
15079
15235
|
}
|
|
15080
15236
|
function injectInstrumentationHook(raw) {
|
|
@@ -15105,7 +15261,7 @@ var javascriptInstaller = {
|
|
|
15105
15261
|
// src/installers/python.ts
|
|
15106
15262
|
init_cjs_shims();
|
|
15107
15263
|
var import_node_fs38 = require("fs");
|
|
15108
|
-
var
|
|
15264
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
15109
15265
|
var SDK_PACKAGES2 = [
|
|
15110
15266
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
15111
15267
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -15126,7 +15282,7 @@ async function exists4(p) {
|
|
|
15126
15282
|
async function detect2(serviceDir) {
|
|
15127
15283
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
15128
15284
|
for (const m of markers) {
|
|
15129
|
-
if (await exists4(
|
|
15285
|
+
if (await exists4(import_node_path59.default.join(serviceDir, m))) return true;
|
|
15130
15286
|
}
|
|
15131
15287
|
return false;
|
|
15132
15288
|
}
|
|
@@ -15136,7 +15292,7 @@ function reqPackageName(line) {
|
|
|
15136
15292
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
15137
15293
|
}
|
|
15138
15294
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
15139
|
-
const file =
|
|
15295
|
+
const file = import_node_path59.default.join(serviceDir, "requirements.txt");
|
|
15140
15296
|
if (!await exists4(file)) return null;
|
|
15141
15297
|
const raw = await import_node_fs38.promises.readFile(file, "utf8");
|
|
15142
15298
|
const presentNames = new Set(
|
|
@@ -15146,7 +15302,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
15146
15302
|
return { manifest: file, missing: [...missing] };
|
|
15147
15303
|
}
|
|
15148
15304
|
async function planProcfileEdits(serviceDir) {
|
|
15149
|
-
const procfile =
|
|
15305
|
+
const procfile = import_node_path59.default.join(serviceDir, "Procfile");
|
|
15150
15306
|
if (!await exists4(procfile)) return [];
|
|
15151
15307
|
const raw = await import_node_fs38.promises.readFile(procfile, "utf8");
|
|
15152
15308
|
const edits = [];
|
|
@@ -15235,7 +15391,7 @@ async function apply2(installPlan) {
|
|
|
15235
15391
|
if (raw === void 0) {
|
|
15236
15392
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
15237
15393
|
}
|
|
15238
|
-
const base =
|
|
15394
|
+
const base = import_node_path59.default.basename(file);
|
|
15239
15395
|
if (base === "requirements.txt") {
|
|
15240
15396
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
15241
15397
|
if (edits.length > 0) {
|
|
@@ -15274,7 +15430,7 @@ async function rollback2(installPlan, originals) {
|
|
|
15274
15430
|
...restored.map((f) => `restored: ${f}`),
|
|
15275
15431
|
""
|
|
15276
15432
|
];
|
|
15277
|
-
const rollbackPath =
|
|
15433
|
+
const rollbackPath = import_node_path59.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
15278
15434
|
await import_node_fs38.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
15279
15435
|
}
|
|
15280
15436
|
var pythonInstaller = {
|
|
@@ -15401,7 +15557,7 @@ init_cjs_shims();
|
|
|
15401
15557
|
var import_node_fs39 = require("fs");
|
|
15402
15558
|
var import_node_http = __toESM(require("http"), 1);
|
|
15403
15559
|
var import_node_net = __toESM(require("net"), 1);
|
|
15404
|
-
var
|
|
15560
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
15405
15561
|
var import_node_url4 = require("url");
|
|
15406
15562
|
var import_node_child_process3 = require("child_process");
|
|
15407
15563
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
@@ -15411,7 +15567,7 @@ async function extractAndPersist(opts) {
|
|
|
15411
15567
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
15412
15568
|
resetGraph(graphKey);
|
|
15413
15569
|
const graph = getGraph(graphKey);
|
|
15414
|
-
const projectPaths = pathsForProject(graphKey,
|
|
15570
|
+
const projectPaths = pathsForProject(graphKey, import_node_path60.default.join(opts.scanPath, "neat-out"));
|
|
15415
15571
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
15416
15572
|
errorsPath: projectPaths.errorsPath
|
|
15417
15573
|
});
|
|
@@ -15463,7 +15619,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15463
15619
|
libOnly++;
|
|
15464
15620
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
15465
15621
|
if (appDeps.length > 0) {
|
|
15466
|
-
const svcName =
|
|
15622
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15467
15623
|
const list = appDeps.join(", ");
|
|
15468
15624
|
console.warn(
|
|
15469
15625
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -15476,7 +15632,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15476
15632
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
15477
15633
|
} else if (outcome.outcome === "react-native") {
|
|
15478
15634
|
reactNative++;
|
|
15479
|
-
const svcName =
|
|
15635
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15480
15636
|
console.log(
|
|
15481
15637
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
15482
15638
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -15487,7 +15643,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15487
15643
|
);
|
|
15488
15644
|
} else if (outcome.outcome === "bun") {
|
|
15489
15645
|
bun++;
|
|
15490
|
-
const svcName =
|
|
15646
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15491
15647
|
console.log(
|
|
15492
15648
|
`neat: ${svc.dir} detected as Bun
|
|
15493
15649
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -15498,7 +15654,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15498
15654
|
);
|
|
15499
15655
|
} else if (outcome.outcome === "deno") {
|
|
15500
15656
|
deno++;
|
|
15501
|
-
const svcName =
|
|
15657
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15502
15658
|
console.log(
|
|
15503
15659
|
`neat: ${svc.dir} detected as Deno
|
|
15504
15660
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -15509,7 +15665,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15509
15665
|
);
|
|
15510
15666
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
15511
15667
|
cloudflareWorkers++;
|
|
15512
|
-
const svcName =
|
|
15668
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15513
15669
|
console.log(
|
|
15514
15670
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
15515
15671
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -15520,7 +15676,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15520
15676
|
);
|
|
15521
15677
|
} else if (outcome.outcome === "electron") {
|
|
15522
15678
|
electron++;
|
|
15523
|
-
const svcName =
|
|
15679
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15524
15680
|
console.log(
|
|
15525
15681
|
`neat: ${svc.dir} detected as Electron
|
|
15526
15682
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -15533,7 +15689,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
15533
15689
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
15534
15690
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
15535
15691
|
if (gaps.length > 0) {
|
|
15536
|
-
const svcName =
|
|
15692
|
+
const svcName = import_node_path60.default.basename(svc.dir);
|
|
15537
15693
|
const list = gaps.join(", ");
|
|
15538
15694
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
15539
15695
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -15739,8 +15895,8 @@ async function persistedPortsFor(scanPath) {
|
|
|
15739
15895
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
15740
15896
|
}
|
|
15741
15897
|
async function acquireSpawnLock(scanPath) {
|
|
15742
|
-
const lockPath =
|
|
15743
|
-
await import_node_fs39.promises.mkdir(
|
|
15898
|
+
const lockPath = import_node_path60.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
15899
|
+
await import_node_fs39.promises.mkdir(import_node_path60.default.dirname(lockPath), { recursive: true });
|
|
15744
15900
|
const STALE_LOCK_MS = 6e4;
|
|
15745
15901
|
try {
|
|
15746
15902
|
const fd = await import_node_fs39.promises.open(lockPath, "wx");
|
|
@@ -15785,13 +15941,13 @@ async function healthIsForProject(restPort, project) {
|
|
|
15785
15941
|
return false;
|
|
15786
15942
|
}
|
|
15787
15943
|
function daemonLogPath(projectPath2) {
|
|
15788
|
-
return
|
|
15944
|
+
return import_node_path60.default.join(projectPath2, "neat-out", "daemon.log");
|
|
15789
15945
|
}
|
|
15790
15946
|
function spawnDaemonDetached(spec) {
|
|
15791
|
-
const here =
|
|
15947
|
+
const here = import_node_path60.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
15792
15948
|
const candidates = [
|
|
15793
|
-
|
|
15794
|
-
|
|
15949
|
+
import_node_path60.default.join(here, "neatd.cjs"),
|
|
15950
|
+
import_node_path60.default.join(here, "neatd.js")
|
|
15795
15951
|
];
|
|
15796
15952
|
let entry2 = null;
|
|
15797
15953
|
const fsSync = require("fs");
|
|
@@ -15821,7 +15977,7 @@ function spawnDaemonDetached(spec) {
|
|
|
15821
15977
|
let logFd = null;
|
|
15822
15978
|
if (spec) {
|
|
15823
15979
|
const logPath = daemonLogPath(spec.projectPath);
|
|
15824
|
-
fsSync.mkdirSync(
|
|
15980
|
+
fsSync.mkdirSync(import_node_path60.default.dirname(logPath), { recursive: true });
|
|
15825
15981
|
logFd = fsSync.openSync(logPath, "a");
|
|
15826
15982
|
}
|
|
15827
15983
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -16020,7 +16176,7 @@ async function runOrchestrator(opts) {
|
|
|
16020
16176
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
16021
16177
|
}
|
|
16022
16178
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
16023
|
-
const daemonLog = daemonRunning ?
|
|
16179
|
+
const daemonLog = daemonRunning ? import_node_path60.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
16024
16180
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
16025
16181
|
return result;
|
|
16026
16182
|
}
|
|
@@ -16477,7 +16633,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
|
|
|
16477
16633
|
|
|
16478
16634
|
// src/hooks-cli.ts
|
|
16479
16635
|
init_cjs_shims();
|
|
16480
|
-
var
|
|
16636
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
16481
16637
|
var import_node_os5 = __toESM(require("os"), 1);
|
|
16482
16638
|
var import_node_fs40 = require("fs");
|
|
16483
16639
|
var import_node_url5 = require("url");
|
|
@@ -16486,14 +16642,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
|
|
|
16486
16642
|
var GUIDE_INSTALL_NAME = "neat-graph-first.md";
|
|
16487
16643
|
var HOOK_MATCHER = "Grep|Glob|Bash";
|
|
16488
16644
|
function moduleDir() {
|
|
16489
|
-
return typeof __dirname !== "undefined" ? __dirname :
|
|
16645
|
+
return typeof __dirname !== "undefined" ? __dirname : import_node_path61.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
16490
16646
|
}
|
|
16491
16647
|
async function readSkillAsset(rel) {
|
|
16492
16648
|
const here = moduleDir();
|
|
16493
16649
|
const candidates = [
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
|
|
16650
|
+
import_node_path61.default.resolve(here, "../../claude-skill", rel),
|
|
16651
|
+
import_node_path61.default.resolve(here, "../../../claude-skill", rel),
|
|
16652
|
+
import_node_path61.default.resolve(here, "../claude-skill", rel)
|
|
16497
16653
|
];
|
|
16498
16654
|
for (const candidate of candidates) {
|
|
16499
16655
|
try {
|
|
@@ -16507,17 +16663,17 @@ async function readSkillAsset(rel) {
|
|
|
16507
16663
|
}
|
|
16508
16664
|
function neatHome3() {
|
|
16509
16665
|
const override = process.env.NEAT_HOME;
|
|
16510
|
-
if (override && override.length > 0) return
|
|
16511
|
-
return
|
|
16666
|
+
if (override && override.length > 0) return import_node_path61.default.resolve(override);
|
|
16667
|
+
return import_node_path61.default.join(import_node_os5.default.homedir(), ".neat");
|
|
16512
16668
|
}
|
|
16513
16669
|
function claudeSettingsPath() {
|
|
16514
16670
|
const override = process.env.NEAT_CLAUDE_SETTINGS;
|
|
16515
|
-
if (override && override.length > 0) return
|
|
16671
|
+
if (override && override.length > 0) return import_node_path61.default.resolve(override);
|
|
16516
16672
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
|
|
16517
|
-
return
|
|
16673
|
+
return import_node_path61.default.join(home, ".claude", "settings.json");
|
|
16518
16674
|
}
|
|
16519
16675
|
function installedHookPath() {
|
|
16520
|
-
return
|
|
16676
|
+
return import_node_path61.default.join(neatHome3(), "hooks", HOOK_FILENAME);
|
|
16521
16677
|
}
|
|
16522
16678
|
function isNeatSearchEntry(entry2) {
|
|
16523
16679
|
return (entry2.hooks ?? []).some(
|
|
@@ -16550,9 +16706,9 @@ async function runHooks(opts) {
|
|
|
16550
16706
|
const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
|
|
16551
16707
|
const guide = await readSkillAsset(GUIDE_FILENAME);
|
|
16552
16708
|
const scriptPath = installedHookPath();
|
|
16553
|
-
await import_node_fs40.promises.mkdir(
|
|
16709
|
+
await import_node_fs40.promises.mkdir(import_node_path61.default.dirname(scriptPath), { recursive: true });
|
|
16554
16710
|
await import_node_fs40.promises.writeFile(scriptPath, hookScript, { mode: 493 });
|
|
16555
|
-
const guidePath =
|
|
16711
|
+
const guidePath = import_node_path61.default.join(neatHome3(), GUIDE_INSTALL_NAME);
|
|
16556
16712
|
await import_node_fs40.promises.writeFile(guidePath, guide, "utf8");
|
|
16557
16713
|
const settingsFile = claudeSettingsPath();
|
|
16558
16714
|
let settings = {};
|
|
@@ -16579,7 +16735,7 @@ async function runHooks(opts) {
|
|
|
16579
16735
|
...settings,
|
|
16580
16736
|
hooks: { ...hooks, PreToolUse: preToolUse }
|
|
16581
16737
|
};
|
|
16582
|
-
await import_node_fs40.promises.mkdir(
|
|
16738
|
+
await import_node_fs40.promises.mkdir(import_node_path61.default.dirname(settingsFile), { recursive: true });
|
|
16583
16739
|
await import_node_fs40.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
16584
16740
|
console.log(`neat hooks: installed the search-nudge hook`);
|
|
16585
16741
|
console.log(` script: ${scriptPath}`);
|
|
@@ -16651,11 +16807,11 @@ async function runHooksCommand(args) {
|
|
|
16651
16807
|
|
|
16652
16808
|
// src/cli-verbs.ts
|
|
16653
16809
|
init_cjs_shims();
|
|
16654
|
-
var
|
|
16810
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
16655
16811
|
|
|
16656
16812
|
// src/cli-client.ts
|
|
16657
16813
|
init_cjs_shims();
|
|
16658
|
-
var
|
|
16814
|
+
var import_types50 = require("@neat.is/types");
|
|
16659
16815
|
var HttpError = class extends Error {
|
|
16660
16816
|
constructor(status2, message, responseBody = "") {
|
|
16661
16817
|
super(message);
|
|
@@ -16680,10 +16836,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
16680
16836
|
const root = baseUrl.replace(/\/$/, "");
|
|
16681
16837
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
16682
16838
|
return {
|
|
16683
|
-
async get(
|
|
16839
|
+
async get(path64) {
|
|
16684
16840
|
let res;
|
|
16685
16841
|
try {
|
|
16686
|
-
res = await fetch(`${root}${
|
|
16842
|
+
res = await fetch(`${root}${path64}`, {
|
|
16687
16843
|
headers: { ...authHeader }
|
|
16688
16844
|
});
|
|
16689
16845
|
} catch (err) {
|
|
@@ -16695,16 +16851,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
16695
16851
|
const body = await res.text().catch(() => "");
|
|
16696
16852
|
throw new HttpError(
|
|
16697
16853
|
res.status,
|
|
16698
|
-
`${res.status} ${res.statusText} on GET ${
|
|
16854
|
+
`${res.status} ${res.statusText} on GET ${path64}: ${body}`,
|
|
16699
16855
|
body
|
|
16700
16856
|
);
|
|
16701
16857
|
}
|
|
16702
16858
|
return await res.json();
|
|
16703
16859
|
},
|
|
16704
|
-
async post(
|
|
16860
|
+
async post(path64, body) {
|
|
16705
16861
|
let res;
|
|
16706
16862
|
try {
|
|
16707
|
-
res = await fetch(`${root}${
|
|
16863
|
+
res = await fetch(`${root}${path64}`, {
|
|
16708
16864
|
method: "POST",
|
|
16709
16865
|
headers: { "content-type": "application/json", ...authHeader },
|
|
16710
16866
|
body: JSON.stringify(body)
|
|
@@ -16718,7 +16874,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
16718
16874
|
const text = await res.text().catch(() => "");
|
|
16719
16875
|
throw new HttpError(
|
|
16720
16876
|
res.status,
|
|
16721
|
-
`${res.status} ${res.statusText} on POST ${
|
|
16877
|
+
`${res.status} ${res.statusText} on POST ${path64}: ${text}`,
|
|
16722
16878
|
text
|
|
16723
16879
|
);
|
|
16724
16880
|
}
|
|
@@ -16732,12 +16888,12 @@ function projectPath(project, suffix) {
|
|
|
16732
16888
|
}
|
|
16733
16889
|
async function runRootCause(client, input) {
|
|
16734
16890
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
16735
|
-
const
|
|
16891
|
+
const path64 = projectPath(
|
|
16736
16892
|
input.project,
|
|
16737
16893
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
16738
16894
|
);
|
|
16739
16895
|
try {
|
|
16740
|
-
const result = await client.get(
|
|
16896
|
+
const result = await client.get(path64);
|
|
16741
16897
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
16742
16898
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
16743
16899
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -16763,12 +16919,12 @@ async function runRootCause(client, input) {
|
|
|
16763
16919
|
}
|
|
16764
16920
|
async function runBlastRadius(client, input) {
|
|
16765
16921
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
16766
|
-
const
|
|
16922
|
+
const path64 = projectPath(
|
|
16767
16923
|
input.project,
|
|
16768
16924
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
16769
16925
|
);
|
|
16770
16926
|
try {
|
|
16771
|
-
const result = await client.get(
|
|
16927
|
+
const result = await client.get(path64);
|
|
16772
16928
|
if (result.totalAffected === 0) {
|
|
16773
16929
|
return {
|
|
16774
16930
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -16797,17 +16953,17 @@ async function runBlastRadius(client, input) {
|
|
|
16797
16953
|
}
|
|
16798
16954
|
}
|
|
16799
16955
|
function formatBlastEntry(n) {
|
|
16800
|
-
const tag = n.edgeProvenance ===
|
|
16956
|
+
const tag = n.edgeProvenance === import_types50.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
16801
16957
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
16802
16958
|
}
|
|
16803
16959
|
async function runDependencies(client, input) {
|
|
16804
16960
|
const depth = input.depth ?? 3;
|
|
16805
|
-
const
|
|
16961
|
+
const path64 = projectPath(
|
|
16806
16962
|
input.project,
|
|
16807
16963
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
16808
16964
|
);
|
|
16809
16965
|
try {
|
|
16810
|
-
const result = await client.get(
|
|
16966
|
+
const result = await client.get(path64);
|
|
16811
16967
|
if (result.total === 0) {
|
|
16812
16968
|
return {
|
|
16813
16969
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -16854,7 +17010,7 @@ async function runObservedDependencies(client, input) {
|
|
|
16854
17010
|
if (result.observed) {
|
|
16855
17011
|
return {
|
|
16856
17012
|
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.`,
|
|
16857
|
-
provenance:
|
|
17013
|
+
provenance: import_types50.Provenance.OBSERVED
|
|
16858
17014
|
};
|
|
16859
17015
|
}
|
|
16860
17016
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -16864,7 +17020,7 @@ async function runObservedDependencies(client, input) {
|
|
|
16864
17020
|
return {
|
|
16865
17021
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
16866
17022
|
block: blockLines.join("\n"),
|
|
16867
|
-
provenance:
|
|
17023
|
+
provenance: import_types50.Provenance.OBSERVED
|
|
16868
17024
|
};
|
|
16869
17025
|
} catch (err) {
|
|
16870
17026
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -16899,9 +17055,9 @@ function formatDuration(ms) {
|
|
|
16899
17055
|
return `${Math.round(h / 24)}d`;
|
|
16900
17056
|
}
|
|
16901
17057
|
async function runIncidents(client, input) {
|
|
16902
|
-
const
|
|
17058
|
+
const path64 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
16903
17059
|
try {
|
|
16904
|
-
const body = await client.get(
|
|
17060
|
+
const body = await client.get(path64);
|
|
16905
17061
|
const events = body.events;
|
|
16906
17062
|
if (events.length === 0) {
|
|
16907
17063
|
return {
|
|
@@ -16918,7 +17074,7 @@ async function runIncidents(client, input) {
|
|
|
16918
17074
|
return {
|
|
16919
17075
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
16920
17076
|
block: blockLines.join("\n"),
|
|
16921
|
-
provenance:
|
|
17077
|
+
provenance: import_types50.Provenance.OBSERVED
|
|
16922
17078
|
};
|
|
16923
17079
|
} catch (err) {
|
|
16924
17080
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -17027,7 +17183,7 @@ async function runStaleEdges(client, input) {
|
|
|
17027
17183
|
return {
|
|
17028
17184
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
17029
17185
|
block: blockLines.join("\n"),
|
|
17030
|
-
provenance:
|
|
17186
|
+
provenance: import_types50.Provenance.STALE
|
|
17031
17187
|
};
|
|
17032
17188
|
}
|
|
17033
17189
|
async function runPolicies(client, input) {
|
|
@@ -17191,7 +17347,7 @@ async function resolveProjectEntry(opts) {
|
|
|
17191
17347
|
const cwd = opts.cwd ?? process.cwd();
|
|
17192
17348
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
17193
17349
|
for (const entry2 of entries) {
|
|
17194
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
17350
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path62.default.sep}`)) {
|
|
17195
17351
|
return entry2;
|
|
17196
17352
|
}
|
|
17197
17353
|
}
|
|
@@ -17344,7 +17500,7 @@ async function runSync(opts) {
|
|
|
17344
17500
|
}
|
|
17345
17501
|
|
|
17346
17502
|
// src/cli.ts
|
|
17347
|
-
var
|
|
17503
|
+
var import_types51 = require("@neat.is/types");
|
|
17348
17504
|
function isNpxInvocation() {
|
|
17349
17505
|
if (process.env.npm_command === "exec") return true;
|
|
17350
17506
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -17655,12 +17811,12 @@ async function runInit(opts) {
|
|
|
17655
17811
|
printDiscoveryReport(opts, services);
|
|
17656
17812
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
17657
17813
|
const patch = renderPatch(sections);
|
|
17658
|
-
const patchPath =
|
|
17814
|
+
const patchPath = import_node_path63.default.join(opts.scanPath, "neat.patch");
|
|
17659
17815
|
if (opts.dryRun) {
|
|
17660
17816
|
await import_node_fs41.promises.writeFile(patchPath, patch, "utf8");
|
|
17661
17817
|
written.push(patchPath);
|
|
17662
17818
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
17663
|
-
const gitignorePath =
|
|
17819
|
+
const gitignorePath = import_node_path63.default.join(opts.scanPath, ".gitignore");
|
|
17664
17820
|
const gitignoreExists = await import_node_fs41.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
17665
17821
|
const verb = gitignoreExists ? "append" : "create";
|
|
17666
17822
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
@@ -17672,9 +17828,9 @@ async function runInit(opts) {
|
|
|
17672
17828
|
const graph = getGraph(graphKey);
|
|
17673
17829
|
const projectPaths = pathsForProject(
|
|
17674
17830
|
graphKey,
|
|
17675
|
-
|
|
17831
|
+
import_node_path63.default.join(opts.scanPath, "neat-out")
|
|
17676
17832
|
);
|
|
17677
|
-
const errorsPath =
|
|
17833
|
+
const errorsPath = import_node_path63.default.join(import_node_path63.default.dirname(opts.outPath), import_node_path63.default.basename(projectPaths.errorsPath));
|
|
17678
17834
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
17679
17835
|
await saveGraphToDisk(graph, opts.outPath);
|
|
17680
17836
|
written.push(opts.outPath);
|
|
@@ -17793,9 +17949,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
17793
17949
|
};
|
|
17794
17950
|
function claudeConfigPath() {
|
|
17795
17951
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
17796
|
-
if (override && override.length > 0) return
|
|
17952
|
+
if (override && override.length > 0) return import_node_path63.default.resolve(override);
|
|
17797
17953
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
17798
|
-
return
|
|
17954
|
+
return import_node_path63.default.join(home, ".claude.json");
|
|
17799
17955
|
}
|
|
17800
17956
|
async function runSkill(opts) {
|
|
17801
17957
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -17819,7 +17975,7 @@ async function runSkill(opts) {
|
|
|
17819
17975
|
...existing,
|
|
17820
17976
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
17821
17977
|
};
|
|
17822
|
-
await import_node_fs41.promises.mkdir(
|
|
17978
|
+
await import_node_fs41.promises.mkdir(import_node_path63.default.dirname(target), { recursive: true });
|
|
17823
17979
|
await import_node_fs41.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
17824
17980
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
17825
17981
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
@@ -17885,12 +18041,12 @@ async function main() {
|
|
|
17885
18041
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
17886
18042
|
process.exit(2);
|
|
17887
18043
|
}
|
|
17888
|
-
const scanPath =
|
|
18044
|
+
const scanPath = import_node_path63.default.resolve(target);
|
|
17889
18045
|
const projectExplicit = parsed.project !== null;
|
|
17890
|
-
const projectName = projectExplicit ? project :
|
|
18046
|
+
const projectName = projectExplicit ? project : import_node_path63.default.basename(scanPath);
|
|
17891
18047
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
17892
|
-
const fallback = pathsForProject(projectKey,
|
|
17893
|
-
const outPath =
|
|
18048
|
+
const fallback = pathsForProject(projectKey, import_node_path63.default.join(scanPath, "neat-out")).snapshotPath;
|
|
18049
|
+
const outPath = import_node_path63.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
17894
18050
|
const result = await runInit({
|
|
17895
18051
|
scanPath,
|
|
17896
18052
|
outPath,
|
|
@@ -17911,21 +18067,21 @@ async function main() {
|
|
|
17911
18067
|
usage2();
|
|
17912
18068
|
process.exit(2);
|
|
17913
18069
|
}
|
|
17914
|
-
const scanPath =
|
|
18070
|
+
const scanPath = import_node_path63.default.resolve(target);
|
|
17915
18071
|
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
17916
18072
|
if (!stat || !stat.isDirectory()) {
|
|
17917
18073
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
17918
18074
|
process.exit(2);
|
|
17919
18075
|
}
|
|
17920
|
-
const projectPaths = pathsForProject(project,
|
|
17921
|
-
const outPath =
|
|
17922
|
-
const errorsPath =
|
|
17923
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
18076
|
+
const projectPaths = pathsForProject(project, import_node_path63.default.join(scanPath, "neat-out"));
|
|
18077
|
+
const outPath = import_node_path63.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
18078
|
+
const errorsPath = import_node_path63.default.resolve(
|
|
18079
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path63.default.join(import_node_path63.default.dirname(outPath), import_node_path63.default.basename(projectPaths.errorsPath))
|
|
17924
18080
|
);
|
|
17925
|
-
const staleEventsPath =
|
|
17926
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
18081
|
+
const staleEventsPath = import_node_path63.default.resolve(
|
|
18082
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path63.default.join(import_node_path63.default.dirname(outPath), import_node_path63.default.basename(projectPaths.staleEventsPath))
|
|
17927
18083
|
);
|
|
17928
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
18084
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path63.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
17929
18085
|
const handle = await startWatch(getGraph(project), {
|
|
17930
18086
|
scanPath,
|
|
17931
18087
|
outPath,
|
|
@@ -18108,11 +18264,11 @@ async function main() {
|
|
|
18108
18264
|
process.exit(1);
|
|
18109
18265
|
}
|
|
18110
18266
|
async function tryOrchestrator(cmd, parsed) {
|
|
18111
|
-
const scanPath =
|
|
18267
|
+
const scanPath = import_node_path63.default.resolve(cmd);
|
|
18112
18268
|
const stat = await import_node_fs41.promises.stat(scanPath).catch(() => null);
|
|
18113
18269
|
if (!stat || !stat.isDirectory()) return null;
|
|
18114
18270
|
const projectExplicit = parsed.project !== null;
|
|
18115
|
-
const projectName = projectExplicit ? parsed.project :
|
|
18271
|
+
const projectName = projectExplicit ? parsed.project : import_node_path63.default.basename(scanPath);
|
|
18116
18272
|
const result = await runOrchestrator({
|
|
18117
18273
|
scanPath,
|
|
18118
18274
|
project: projectName,
|
|
@@ -18301,10 +18457,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
18301
18457
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
18302
18458
|
const out = [];
|
|
18303
18459
|
for (const p of parts) {
|
|
18304
|
-
const r =
|
|
18460
|
+
const r = import_types51.DivergenceTypeSchema.safeParse(p);
|
|
18305
18461
|
if (!r.success) {
|
|
18306
18462
|
console.error(
|
|
18307
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
18463
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types51.DivergenceTypeSchema.options.join(", ")}`
|
|
18308
18464
|
);
|
|
18309
18465
|
return 2;
|
|
18310
18466
|
}
|