@neat.is/core 0.4.26-dev.20260703 → 0.4.26
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-2LNICOVU.js → chunk-A3322JYS.js} +29 -2
- package/dist/chunk-A3322JYS.js.map +1 -0
- package/dist/{chunk-4OR4RQEO.js → chunk-BIY46Q6U.js} +13 -1
- package/dist/chunk-BIY46Q6U.js.map +1 -0
- package/dist/{chunk-WZYH5DVG.js → chunk-QM6BMPVJ.js} +419 -218
- package/dist/chunk-QM6BMPVJ.js.map +1 -0
- package/dist/{chunk-C5NCCKPZ.js → chunk-UV5WSM7M.js} +2 -2
- package/dist/{chunk-S7TDPQDD.js → chunk-XV4D7A3Z.js} +105 -6
- package/dist/chunk-XV4D7A3Z.js.map +1 -0
- package/dist/cli.cjs +732 -456
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +49 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +587 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -1
- package/dist/index.d.ts +97 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +600 -286
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-LFYZDSK3.js → otel-grpc-ET5Z6KI6.js} +3 -3
- package/dist/server.cjs +410 -182
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-2LNICOVU.js.map +0 -1
- package/dist/chunk-4OR4RQEO.js.map +0 -1
- package/dist/chunk-S7TDPQDD.js.map +0 -1
- package/dist/chunk-WZYH5DVG.js.map +0 -1
- /package/dist/{chunk-C5NCCKPZ.js.map → chunk-UV5WSM7M.js.map} +0 -0
- /package/dist/{otel-grpc-LFYZDSK3.js.map → otel-grpc-ET5Z6KI6.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -58,9 +58,9 @@ function mountBearerAuth(app, opts) {
|
|
|
58
58
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
59
59
|
const publicRead = opts.publicRead === true;
|
|
60
60
|
app.addHook("preHandler", (req, reply, done) => {
|
|
61
|
-
const
|
|
61
|
+
const path56 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
62
62
|
for (const suffix of suffixes) {
|
|
63
|
-
if (
|
|
63
|
+
if (path56 === suffix || path56.endsWith(suffix)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -190,8 +190,8 @@ function reshapeGrpcRequest(req) {
|
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
function resolveProtoRoot() {
|
|
193
|
-
const here =
|
|
194
|
-
return
|
|
193
|
+
const here = import_node_path44.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
194
|
+
return import_node_path44.default.resolve(here, "..", "proto");
|
|
195
195
|
}
|
|
196
196
|
function loadTraceService() {
|
|
197
197
|
const protoRoot = resolveProtoRoot();
|
|
@@ -259,13 +259,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
259
259
|
})
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
|
-
var import_node_url2,
|
|
262
|
+
var import_node_url2, import_node_path44, import_node_crypto2, grpc, protoLoader;
|
|
263
263
|
var init_otel_grpc = __esm({
|
|
264
264
|
"src/otel-grpc.ts"() {
|
|
265
265
|
"use strict";
|
|
266
266
|
init_cjs_shims();
|
|
267
267
|
import_node_url2 = require("url");
|
|
268
|
-
|
|
268
|
+
import_node_path44 = __toESM(require("path"), 1);
|
|
269
269
|
import_node_crypto2 = require("crypto");
|
|
270
270
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
271
271
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -345,6 +345,29 @@ function messagingDestinationOf(attrs) {
|
|
|
345
345
|
}
|
|
346
346
|
return void 0;
|
|
347
347
|
}
|
|
348
|
+
function hasWebsocketUpgradeHeader(attrs) {
|
|
349
|
+
const v = attrs["http.request.header.upgrade"];
|
|
350
|
+
const matches = (s) => typeof s === "string" && s.trim().toLowerCase() === "websocket";
|
|
351
|
+
if (Array.isArray(v)) return v.some(matches);
|
|
352
|
+
return matches(v);
|
|
353
|
+
}
|
|
354
|
+
function websocketChannelPathOf(attrs) {
|
|
355
|
+
const route = attrs["http.route"];
|
|
356
|
+
if (typeof route === "string" && route.length > 0) return route;
|
|
357
|
+
for (const key of ["url.path", "http.target"]) {
|
|
358
|
+
const v = attrs[key];
|
|
359
|
+
if (typeof v === "string" && v.length > 0) {
|
|
360
|
+
const q = v.indexOf("?");
|
|
361
|
+
const path56 = q === -1 ? v : v.slice(0, q);
|
|
362
|
+
if (path56.length > 0) return path56;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return void 0;
|
|
366
|
+
}
|
|
367
|
+
function websocketChannelOf(attrs) {
|
|
368
|
+
if (!hasWebsocketUpgradeHeader(attrs)) return void 0;
|
|
369
|
+
return websocketChannelPathOf(attrs);
|
|
370
|
+
}
|
|
348
371
|
function parseOtlpRequest(body) {
|
|
349
372
|
const out = [];
|
|
350
373
|
for (const rs of body.resourceSpans ?? []) {
|
|
@@ -375,6 +398,10 @@ function parseOtlpRequest(body) {
|
|
|
375
398
|
messagingDestination: messagingDestinationOf(attrs),
|
|
376
399
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
377
400
|
graphqlOperationType: typeof attrs["graphql.operation.type"] === "string" && attrs["graphql.operation.type"].length > 0 ? attrs["graphql.operation.type"] : void 0,
|
|
401
|
+
rpcSystem: typeof attrs["rpc.system"] === "string" && attrs["rpc.system"].length > 0 ? attrs["rpc.system"] : void 0,
|
|
402
|
+
rpcService: typeof attrs["rpc.service"] === "string" && attrs["rpc.service"].length > 0 ? attrs["rpc.service"] : void 0,
|
|
403
|
+
rpcMethod: typeof attrs["rpc.method"] === "string" && attrs["rpc.method"].length > 0 ? attrs["rpc.method"] : void 0,
|
|
404
|
+
websocketChannel: websocketChannelOf(attrs),
|
|
378
405
|
statusCode: span.status?.code,
|
|
379
406
|
errorMessage: span.status?.message,
|
|
380
407
|
exception: extractExceptionFromEvents(span.events)
|
|
@@ -386,10 +413,10 @@ function parseOtlpRequest(body) {
|
|
|
386
413
|
return out;
|
|
387
414
|
}
|
|
388
415
|
function loadProtoRoot() {
|
|
389
|
-
const here =
|
|
390
|
-
const protoRoot =
|
|
416
|
+
const here = import_node_path45.default.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
417
|
+
const protoRoot = import_node_path45.default.resolve(here, "..", "proto");
|
|
391
418
|
const root = new import_protobufjs.default.Root();
|
|
392
|
-
root.resolvePath = (_origin, target) =>
|
|
419
|
+
root.resolvePath = (_origin, target) => import_node_path45.default.resolve(protoRoot, target);
|
|
393
420
|
root.loadSync(
|
|
394
421
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
395
422
|
{ keepCase: true }
|
|
@@ -617,12 +644,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
617
644
|
}
|
|
618
645
|
}
|
|
619
646
|
}
|
|
620
|
-
var
|
|
647
|
+
var import_node_path45, import_node_url3, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
621
648
|
var init_otel = __esm({
|
|
622
649
|
"src/otel.ts"() {
|
|
623
650
|
"use strict";
|
|
624
651
|
init_cjs_shims();
|
|
625
|
-
|
|
652
|
+
import_node_path45 = __toESM(require("path"), 1);
|
|
626
653
|
import_node_url3 = require("url");
|
|
627
654
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
628
655
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -659,8 +686,8 @@ __export(cli_exports, {
|
|
|
659
686
|
});
|
|
660
687
|
module.exports = __toCommonJS(cli_exports);
|
|
661
688
|
init_cjs_shims();
|
|
662
|
-
var
|
|
663
|
-
var
|
|
689
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
690
|
+
var import_node_fs35 = require("fs");
|
|
664
691
|
|
|
665
692
|
// src/banner.ts
|
|
666
693
|
init_cjs_shims();
|
|
@@ -1225,19 +1252,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1225
1252
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1226
1253
|
let best = { path: [start], edges: [] };
|
|
1227
1254
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1228
|
-
function step(node,
|
|
1229
|
-
if (
|
|
1230
|
-
best = { path: [...
|
|
1255
|
+
function step(node, path56, edges) {
|
|
1256
|
+
if (path56.length > best.path.length) {
|
|
1257
|
+
best = { path: [...path56], edges: [...edges] };
|
|
1231
1258
|
}
|
|
1232
|
-
if (
|
|
1259
|
+
if (path56.length - 1 >= maxDepth) return;
|
|
1233
1260
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1234
1261
|
for (const [srcId, edge] of incoming) {
|
|
1235
1262
|
if (visited.has(srcId)) continue;
|
|
1236
1263
|
visited.add(srcId);
|
|
1237
|
-
|
|
1264
|
+
path56.push(srcId);
|
|
1238
1265
|
edges.push(edge);
|
|
1239
|
-
step(srcId,
|
|
1240
|
-
|
|
1266
|
+
step(srcId, path56, edges);
|
|
1267
|
+
path56.pop();
|
|
1241
1268
|
edges.pop();
|
|
1242
1269
|
visited.delete(srcId);
|
|
1243
1270
|
}
|
|
@@ -1431,26 +1458,26 @@ function dominantFailingCall(graph, serviceId4, visited) {
|
|
|
1431
1458
|
return best;
|
|
1432
1459
|
}
|
|
1433
1460
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1434
|
-
const
|
|
1461
|
+
const path56 = [originServiceId];
|
|
1435
1462
|
const edges = [];
|
|
1436
1463
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1437
1464
|
let current = originServiceId;
|
|
1438
1465
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1439
1466
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1440
1467
|
if (!hop) break;
|
|
1441
|
-
|
|
1468
|
+
path56.push(hop.nextService);
|
|
1442
1469
|
edges.push(hop.edge);
|
|
1443
1470
|
visited.add(hop.nextService);
|
|
1444
1471
|
current = hop.nextService;
|
|
1445
1472
|
}
|
|
1446
1473
|
if (edges.length === 0) return null;
|
|
1447
|
-
return { path:
|
|
1474
|
+
return { path: path56, edges, culprit: current };
|
|
1448
1475
|
}
|
|
1449
1476
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1450
1477
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1451
1478
|
if (!chain) return null;
|
|
1452
1479
|
const culprit = chain.culprit;
|
|
1453
|
-
const
|
|
1480
|
+
const path56 = [...chain.path];
|
|
1454
1481
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1455
1482
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1456
1483
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1458,14 +1485,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1458
1485
|
if (loc) {
|
|
1459
1486
|
let rootCauseNode = culprit;
|
|
1460
1487
|
if (loc.fileNode) {
|
|
1461
|
-
|
|
1488
|
+
path56.push(loc.fileNode);
|
|
1462
1489
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1463
1490
|
rootCauseNode = loc.fileNode;
|
|
1464
1491
|
}
|
|
1465
1492
|
return import_types.RootCauseResultSchema.parse({
|
|
1466
1493
|
rootCauseNode,
|
|
1467
1494
|
rootCauseReason: loc.rootCauseReason,
|
|
1468
|
-
traversalPath:
|
|
1495
|
+
traversalPath: path56,
|
|
1469
1496
|
edgeProvenances,
|
|
1470
1497
|
confidence,
|
|
1471
1498
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1477,7 +1504,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1477
1504
|
return import_types.RootCauseResultSchema.parse({
|
|
1478
1505
|
rootCauseNode: culprit,
|
|
1479
1506
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1480
|
-
traversalPath:
|
|
1507
|
+
traversalPath: path56,
|
|
1481
1508
|
edgeProvenances,
|
|
1482
1509
|
confidence,
|
|
1483
1510
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2427,6 +2454,40 @@ function ensureGraphqlOperationNode(graph, serviceName, operationType, operation
|
|
|
2427
2454
|
graph.addNode(id, node);
|
|
2428
2455
|
return id;
|
|
2429
2456
|
}
|
|
2457
|
+
function spanServesGrpcMethod(kind) {
|
|
2458
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2459
|
+
}
|
|
2460
|
+
function ensureGrpcMethodNode(graph, rpcService, rpcMethod) {
|
|
2461
|
+
const id = (0, import_types3.grpcMethodId)(rpcService, rpcMethod);
|
|
2462
|
+
if (graph.hasNode(id)) return id;
|
|
2463
|
+
const node = {
|
|
2464
|
+
id,
|
|
2465
|
+
type: import_types3.NodeType.GrpcMethodNode,
|
|
2466
|
+
name: `${rpcService}/${rpcMethod}`,
|
|
2467
|
+
rpcService,
|
|
2468
|
+
rpcMethod,
|
|
2469
|
+
discoveredVia: "otel"
|
|
2470
|
+
};
|
|
2471
|
+
graph.addNode(id, node);
|
|
2472
|
+
return id;
|
|
2473
|
+
}
|
|
2474
|
+
function spanServesWebsocketChannel(kind) {
|
|
2475
|
+
return kind !== WIRE_SPAN_KIND_CLIENT && kind !== WIRE_SPAN_KIND_PRODUCER && kind !== WIRE_SPAN_KIND_CONSUMER;
|
|
2476
|
+
}
|
|
2477
|
+
function ensureWebsocketChannelNode(graph, serviceName, channel) {
|
|
2478
|
+
const id = (0, import_types3.websocketChannelId)(serviceName, channel);
|
|
2479
|
+
if (graph.hasNode(id)) return id;
|
|
2480
|
+
const node = {
|
|
2481
|
+
id,
|
|
2482
|
+
type: import_types3.NodeType.WebSocketChannelNode,
|
|
2483
|
+
name: channel,
|
|
2484
|
+
service: serviceName,
|
|
2485
|
+
channel,
|
|
2486
|
+
discoveredVia: "otel"
|
|
2487
|
+
};
|
|
2488
|
+
graph.addNode(id, node);
|
|
2489
|
+
return id;
|
|
2490
|
+
}
|
|
2430
2491
|
function messagingDestinationKind(system) {
|
|
2431
2492
|
return `${system}-topic`;
|
|
2432
2493
|
}
|
|
@@ -2862,6 +2923,34 @@ async function handleSpan(ctx, span) {
|
|
|
2862
2923
|
callSiteEvidence
|
|
2863
2924
|
);
|
|
2864
2925
|
if (result) affectedNode = targetId;
|
|
2926
|
+
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
2927
|
+
const targetId = ensureGrpcMethodNode(ctx.graph, span.rpcService, span.rpcMethod);
|
|
2928
|
+
const result = upsertObservedEdge(
|
|
2929
|
+
ctx.graph,
|
|
2930
|
+
import_types3.EdgeType.CONTAINS,
|
|
2931
|
+
observedSource(),
|
|
2932
|
+
targetId,
|
|
2933
|
+
ts,
|
|
2934
|
+
isError,
|
|
2935
|
+
callSiteEvidence
|
|
2936
|
+
);
|
|
2937
|
+
if (result) affectedNode = targetId;
|
|
2938
|
+
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
2939
|
+
const targetId = ensureWebsocketChannelNode(
|
|
2940
|
+
ctx.graph,
|
|
2941
|
+
span.service,
|
|
2942
|
+
span.websocketChannel
|
|
2943
|
+
);
|
|
2944
|
+
const result = upsertObservedEdge(
|
|
2945
|
+
ctx.graph,
|
|
2946
|
+
import_types3.EdgeType.CONNECTS_TO,
|
|
2947
|
+
observedSource(),
|
|
2948
|
+
targetId,
|
|
2949
|
+
ts,
|
|
2950
|
+
isError,
|
|
2951
|
+
callSiteEvidence
|
|
2952
|
+
);
|
|
2953
|
+
if (result) affectedNode = targetId;
|
|
2865
2954
|
} else {
|
|
2866
2955
|
const host = pickAddress(span);
|
|
2867
2956
|
let resolvedViaAddress = false;
|
|
@@ -5444,17 +5533,138 @@ async function addRoutes(graph, services) {
|
|
|
5444
5533
|
return { nodesAdded, edgesAdded };
|
|
5445
5534
|
}
|
|
5446
5535
|
|
|
5536
|
+
// src/extract/proto.ts
|
|
5537
|
+
init_cjs_shims();
|
|
5538
|
+
var import_node_fs16 = require("fs");
|
|
5539
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
5540
|
+
var import_types12 = require("@neat.is/types");
|
|
5541
|
+
var PROTO_EXTENSION = ".proto";
|
|
5542
|
+
function packageOf(content) {
|
|
5543
|
+
const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
|
|
5544
|
+
return m ? m[1] : null;
|
|
5545
|
+
}
|
|
5546
|
+
function lineAt(content, offset) {
|
|
5547
|
+
return content.slice(0, offset).split("\n").length;
|
|
5548
|
+
}
|
|
5549
|
+
function grpcMethodsFromProto(content, fqPackage) {
|
|
5550
|
+
const out = [];
|
|
5551
|
+
const serviceRe = /\bservice\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/g;
|
|
5552
|
+
let sm;
|
|
5553
|
+
while ((sm = serviceRe.exec(content)) !== null) {
|
|
5554
|
+
const serviceName = sm[1];
|
|
5555
|
+
const rpcService = fqPackage ? `${fqPackage}.${serviceName}` : serviceName;
|
|
5556
|
+
const bodyStart = serviceRe.lastIndex;
|
|
5557
|
+
let depth = 1;
|
|
5558
|
+
let i = bodyStart;
|
|
5559
|
+
for (; i < content.length && depth > 0; i++) {
|
|
5560
|
+
const ch = content[i];
|
|
5561
|
+
if (ch === "{") depth++;
|
|
5562
|
+
else if (ch === "}") depth--;
|
|
5563
|
+
}
|
|
5564
|
+
const body = content.slice(bodyStart, i - 1);
|
|
5565
|
+
const rpcRe = /\brpc\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
|
5566
|
+
let rm;
|
|
5567
|
+
while ((rm = rpcRe.exec(body)) !== null) {
|
|
5568
|
+
const rpcMethod = rm[1];
|
|
5569
|
+
const line = lineAt(content, bodyStart + rm.index);
|
|
5570
|
+
out.push({ rpcService, rpcMethod, line });
|
|
5571
|
+
}
|
|
5572
|
+
serviceRe.lastIndex = i;
|
|
5573
|
+
}
|
|
5574
|
+
return out;
|
|
5575
|
+
}
|
|
5576
|
+
async function walkProtoFiles(dir) {
|
|
5577
|
+
const out = [];
|
|
5578
|
+
async function walk3(current) {
|
|
5579
|
+
const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
5580
|
+
for (const entry2 of entries) {
|
|
5581
|
+
const full = import_node_path24.default.join(current, entry2.name);
|
|
5582
|
+
if (entry2.isDirectory()) {
|
|
5583
|
+
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
5584
|
+
if (await isPythonVenvDir(full)) continue;
|
|
5585
|
+
await walk3(full);
|
|
5586
|
+
} else if (entry2.isFile() && import_node_path24.default.extname(entry2.name) === PROTO_EXTENSION) {
|
|
5587
|
+
out.push(full);
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5591
|
+
await walk3(dir);
|
|
5592
|
+
return out;
|
|
5593
|
+
}
|
|
5594
|
+
async function addGrpcMethods(graph, services) {
|
|
5595
|
+
let nodesAdded = 0;
|
|
5596
|
+
let edgesAdded = 0;
|
|
5597
|
+
for (const service of services) {
|
|
5598
|
+
const protoPaths = await walkProtoFiles(service.dir);
|
|
5599
|
+
for (const protoPath of protoPaths) {
|
|
5600
|
+
if (isTestPath(protoPath)) continue;
|
|
5601
|
+
const relFile = toPosix2(import_node_path24.default.relative(service.dir, protoPath));
|
|
5602
|
+
let content;
|
|
5603
|
+
try {
|
|
5604
|
+
content = await import_node_fs16.promises.readFile(protoPath, "utf8");
|
|
5605
|
+
} catch (err) {
|
|
5606
|
+
recordExtractionError("proto extraction", protoPath, err);
|
|
5607
|
+
continue;
|
|
5608
|
+
}
|
|
5609
|
+
let methods;
|
|
5610
|
+
try {
|
|
5611
|
+
methods = grpcMethodsFromProto(content, packageOf(content));
|
|
5612
|
+
} catch (err) {
|
|
5613
|
+
recordExtractionError("proto extraction", protoPath, err);
|
|
5614
|
+
continue;
|
|
5615
|
+
}
|
|
5616
|
+
if (methods.length === 0) continue;
|
|
5617
|
+
for (const method of methods) {
|
|
5618
|
+
const mid = (0, import_types12.grpcMethodId)(method.rpcService, method.rpcMethod);
|
|
5619
|
+
if (!graph.hasNode(mid)) {
|
|
5620
|
+
const node = {
|
|
5621
|
+
id: mid,
|
|
5622
|
+
type: import_types12.NodeType.GrpcMethodNode,
|
|
5623
|
+
name: `${method.rpcService}/${method.rpcMethod}`,
|
|
5624
|
+
rpcService: method.rpcService,
|
|
5625
|
+
rpcMethod: method.rpcMethod,
|
|
5626
|
+
path: relFile,
|
|
5627
|
+
line: method.line,
|
|
5628
|
+
discoveredVia: "static"
|
|
5629
|
+
};
|
|
5630
|
+
graph.addNode(mid, node);
|
|
5631
|
+
nodesAdded++;
|
|
5632
|
+
}
|
|
5633
|
+
const containsId = (0, import_types12.extractedEdgeId)(service.node.id, mid, import_types12.EdgeType.CONTAINS);
|
|
5634
|
+
if (!graph.hasEdge(containsId)) {
|
|
5635
|
+
const edge = {
|
|
5636
|
+
id: containsId,
|
|
5637
|
+
source: service.node.id,
|
|
5638
|
+
target: mid,
|
|
5639
|
+
type: import_types12.EdgeType.CONTAINS,
|
|
5640
|
+
provenance: import_types12.Provenance.EXTRACTED,
|
|
5641
|
+
confidence: (0, import_types12.confidenceForExtracted)("structural"),
|
|
5642
|
+
evidence: {
|
|
5643
|
+
file: relFile,
|
|
5644
|
+
line: method.line,
|
|
5645
|
+
snippet: snippet(content, method.line)
|
|
5646
|
+
}
|
|
5647
|
+
};
|
|
5648
|
+
graph.addEdgeWithKey(containsId, service.node.id, mid, edge);
|
|
5649
|
+
edgesAdded++;
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5654
|
+
return { nodesAdded, edgesAdded };
|
|
5655
|
+
}
|
|
5656
|
+
|
|
5447
5657
|
// src/extract/calls/index.ts
|
|
5448
5658
|
init_cjs_shims();
|
|
5449
|
-
var
|
|
5659
|
+
var import_types20 = require("@neat.is/types");
|
|
5450
5660
|
|
|
5451
5661
|
// src/extract/calls/http.ts
|
|
5452
5662
|
init_cjs_shims();
|
|
5453
|
-
var
|
|
5663
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
5454
5664
|
var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
|
|
5455
5665
|
var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5456
5666
|
var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
|
|
5457
|
-
var
|
|
5667
|
+
var import_types13 = require("@neat.is/types");
|
|
5458
5668
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
5459
5669
|
var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
|
|
5460
5670
|
function isInsideJsxExternalLink(node) {
|
|
@@ -5524,7 +5734,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5524
5734
|
const seen = /* @__PURE__ */ new Set();
|
|
5525
5735
|
for (const file of files) {
|
|
5526
5736
|
if (isTestPath(file.path)) continue;
|
|
5527
|
-
const parser =
|
|
5737
|
+
const parser = import_node_path25.default.extname(file.path) === ".py" ? pyParser : jsParser;
|
|
5528
5738
|
let sites;
|
|
5529
5739
|
try {
|
|
5530
5740
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -5533,14 +5743,14 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5533
5743
|
continue;
|
|
5534
5744
|
}
|
|
5535
5745
|
if (sites.length === 0) continue;
|
|
5536
|
-
const relFile = toPosix2(
|
|
5746
|
+
const relFile = toPosix2(import_node_path25.default.relative(service.dir, file.path));
|
|
5537
5747
|
for (const site of sites) {
|
|
5538
5748
|
const targetId = hostToNodeId.get(site.host);
|
|
5539
5749
|
if (!targetId || targetId === service.node.id) continue;
|
|
5540
5750
|
const dedupKey = `${relFile}|${targetId}`;
|
|
5541
5751
|
if (seen.has(dedupKey)) continue;
|
|
5542
5752
|
seen.add(dedupKey);
|
|
5543
|
-
const confidence = (0,
|
|
5753
|
+
const confidence = (0, import_types13.confidenceForExtracted)("url-literal-service-target");
|
|
5544
5754
|
const ev = {
|
|
5545
5755
|
file: relFile,
|
|
5546
5756
|
line: site.line,
|
|
@@ -5554,25 +5764,25 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5554
5764
|
);
|
|
5555
5765
|
nodesAdded += n;
|
|
5556
5766
|
edgesAdded += e;
|
|
5557
|
-
if (!(0,
|
|
5767
|
+
if (!(0, import_types13.passesExtractedFloor)(confidence)) {
|
|
5558
5768
|
noteExtractedDropped({
|
|
5559
5769
|
source: fileNodeId,
|
|
5560
5770
|
target: targetId,
|
|
5561
|
-
type:
|
|
5771
|
+
type: import_types13.EdgeType.CALLS,
|
|
5562
5772
|
confidence,
|
|
5563
5773
|
confidenceKind: "url-literal-service-target",
|
|
5564
5774
|
evidence: ev
|
|
5565
5775
|
});
|
|
5566
5776
|
continue;
|
|
5567
5777
|
}
|
|
5568
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId,
|
|
5778
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, targetId, import_types13.EdgeType.CALLS);
|
|
5569
5779
|
if (!graph.hasEdge(edgeId)) {
|
|
5570
5780
|
const edge = {
|
|
5571
5781
|
id: edgeId,
|
|
5572
5782
|
source: fileNodeId,
|
|
5573
5783
|
target: targetId,
|
|
5574
|
-
type:
|
|
5575
|
-
provenance:
|
|
5784
|
+
type: import_types13.EdgeType.CALLS,
|
|
5785
|
+
provenance: import_types13.Provenance.EXTRACTED,
|
|
5576
5786
|
confidence,
|
|
5577
5787
|
evidence: ev
|
|
5578
5788
|
};
|
|
@@ -5587,10 +5797,10 @@ async function addHttpCallEdges(graph, services) {
|
|
|
5587
5797
|
|
|
5588
5798
|
// src/extract/calls/route-match.ts
|
|
5589
5799
|
init_cjs_shims();
|
|
5590
|
-
var
|
|
5800
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
5591
5801
|
var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
|
|
5592
5802
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
5593
|
-
var
|
|
5803
|
+
var import_types14 = require("@neat.is/types");
|
|
5594
5804
|
var PARSE_CHUNK4 = 16384;
|
|
5595
5805
|
function parseSource4(parser, source) {
|
|
5596
5806
|
return parser.parse(
|
|
@@ -5755,9 +5965,9 @@ function buildRouteIndex(graph) {
|
|
|
5755
5965
|
const index = /* @__PURE__ */ new Map();
|
|
5756
5966
|
graph.forEachNode((_id, attrs) => {
|
|
5757
5967
|
const node = attrs;
|
|
5758
|
-
if (node.type !==
|
|
5968
|
+
if (node.type !== import_types14.NodeType.RouteNode) return;
|
|
5759
5969
|
const route = attrs;
|
|
5760
|
-
const owner = (0,
|
|
5970
|
+
const owner = (0, import_types14.serviceId)(route.service);
|
|
5761
5971
|
const entry2 = {
|
|
5762
5972
|
method: route.method.toUpperCase(),
|
|
5763
5973
|
normalizedPath: normalizePathTemplate(route.pathTemplate),
|
|
@@ -5786,7 +5996,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5786
5996
|
const seen = /* @__PURE__ */ new Set();
|
|
5787
5997
|
for (const file of files) {
|
|
5788
5998
|
if (isTestPath(file.path)) continue;
|
|
5789
|
-
if (!JS_CLIENT_EXTENSIONS.has(
|
|
5999
|
+
if (!JS_CLIENT_EXTENSIONS.has(import_node_path26.default.extname(file.path))) continue;
|
|
5790
6000
|
let sites;
|
|
5791
6001
|
try {
|
|
5792
6002
|
sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
|
|
@@ -5795,7 +6005,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5795
6005
|
continue;
|
|
5796
6006
|
}
|
|
5797
6007
|
if (sites.length === 0) continue;
|
|
5798
|
-
const relFile = toPosix2(
|
|
6008
|
+
const relFile = toPosix2(import_node_path26.default.relative(service.dir, file.path));
|
|
5799
6009
|
for (const site of sites) {
|
|
5800
6010
|
const serverServiceId = hostToNodeId.get(site.host);
|
|
5801
6011
|
if (!serverServiceId || serverServiceId === service.node.id) continue;
|
|
@@ -5815,7 +6025,7 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5815
6025
|
);
|
|
5816
6026
|
nodesAdded += n;
|
|
5817
6027
|
edgesAdded += e;
|
|
5818
|
-
const confidence = (0,
|
|
6028
|
+
const confidence = (0, import_types14.confidenceForExtracted)("verified-call-site");
|
|
5819
6029
|
const ev = {
|
|
5820
6030
|
file: relFile,
|
|
5821
6031
|
line: site.line,
|
|
@@ -5823,25 +6033,25 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5823
6033
|
method: site.method ?? match.method,
|
|
5824
6034
|
pathTemplate: site.pathTemplate
|
|
5825
6035
|
};
|
|
5826
|
-
if (!(0,
|
|
6036
|
+
if (!(0, import_types14.passesExtractedFloor)(confidence)) {
|
|
5827
6037
|
noteExtractedDropped({
|
|
5828
6038
|
source: fileNodeId,
|
|
5829
6039
|
target: match.routeNodeId,
|
|
5830
|
-
type:
|
|
6040
|
+
type: import_types14.EdgeType.CALLS,
|
|
5831
6041
|
confidence,
|
|
5832
6042
|
confidenceKind: "verified-call-site",
|
|
5833
6043
|
evidence: ev
|
|
5834
6044
|
});
|
|
5835
6045
|
continue;
|
|
5836
6046
|
}
|
|
5837
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, match.routeNodeId,
|
|
6047
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types14.EdgeType.CALLS);
|
|
5838
6048
|
if (!graph.hasEdge(edgeId)) {
|
|
5839
6049
|
const edge = {
|
|
5840
6050
|
id: edgeId,
|
|
5841
6051
|
source: fileNodeId,
|
|
5842
6052
|
target: match.routeNodeId,
|
|
5843
|
-
type:
|
|
5844
|
-
provenance:
|
|
6053
|
+
type: import_types14.EdgeType.CALLS,
|
|
6054
|
+
provenance: import_types14.Provenance.EXTRACTED,
|
|
5845
6055
|
confidence,
|
|
5846
6056
|
evidence: ev
|
|
5847
6057
|
};
|
|
@@ -5856,8 +6066,8 @@ async function addRouteCallEdges(graph, services) {
|
|
|
5856
6066
|
|
|
5857
6067
|
// src/extract/calls/kafka.ts
|
|
5858
6068
|
init_cjs_shims();
|
|
5859
|
-
var
|
|
5860
|
-
var
|
|
6069
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
6070
|
+
var import_types15 = require("@neat.is/types");
|
|
5861
6071
|
var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5862
6072
|
var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
|
|
5863
6073
|
function findAll(re, text) {
|
|
@@ -5878,7 +6088,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5878
6088
|
seen.add(key);
|
|
5879
6089
|
const line = lineOf(file.content, topic);
|
|
5880
6090
|
out.push({
|
|
5881
|
-
infraId: (0,
|
|
6091
|
+
infraId: (0, import_types15.infraId)("kafka-topic", topic),
|
|
5882
6092
|
name: topic,
|
|
5883
6093
|
kind: "kafka-topic",
|
|
5884
6094
|
edgeType,
|
|
@@ -5887,7 +6097,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5887
6097
|
// tier (ADR-066).
|
|
5888
6098
|
confidenceKind: "verified-call-site",
|
|
5889
6099
|
evidence: {
|
|
5890
|
-
file:
|
|
6100
|
+
file: import_node_path27.default.relative(serviceDir, file.path),
|
|
5891
6101
|
line,
|
|
5892
6102
|
snippet: snippet(file.content, line)
|
|
5893
6103
|
}
|
|
@@ -5900,8 +6110,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
5900
6110
|
|
|
5901
6111
|
// src/extract/calls/redis.ts
|
|
5902
6112
|
init_cjs_shims();
|
|
5903
|
-
var
|
|
5904
|
-
var
|
|
6113
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
6114
|
+
var import_types16 = require("@neat.is/types");
|
|
5905
6115
|
var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
|
|
5906
6116
|
function redisEndpointsFromFile(file, serviceDir) {
|
|
5907
6117
|
const out = [];
|
|
@@ -5914,7 +6124,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5914
6124
|
seen.add(host);
|
|
5915
6125
|
const line = lineOf(file.content, host);
|
|
5916
6126
|
out.push({
|
|
5917
|
-
infraId: (0,
|
|
6127
|
+
infraId: (0, import_types16.infraId)("redis", host),
|
|
5918
6128
|
name: host,
|
|
5919
6129
|
kind: "redis",
|
|
5920
6130
|
edgeType: "CALLS",
|
|
@@ -5923,7 +6133,7 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5923
6133
|
// support tier (ADR-066).
|
|
5924
6134
|
confidenceKind: "url-with-structural-support",
|
|
5925
6135
|
evidence: {
|
|
5926
|
-
file:
|
|
6136
|
+
file: import_node_path28.default.relative(serviceDir, file.path),
|
|
5927
6137
|
line,
|
|
5928
6138
|
snippet: snippet(file.content, line)
|
|
5929
6139
|
}
|
|
@@ -5934,8 +6144,8 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
5934
6144
|
|
|
5935
6145
|
// src/extract/calls/aws.ts
|
|
5936
6146
|
init_cjs_shims();
|
|
5937
|
-
var
|
|
5938
|
-
var
|
|
6147
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
6148
|
+
var import_types17 = require("@neat.is/types");
|
|
5939
6149
|
var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5940
6150
|
var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
|
|
5941
6151
|
function hasMarker(text, markers) {
|
|
@@ -5959,7 +6169,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5959
6169
|
seen.add(key);
|
|
5960
6170
|
const line = lineOf(file.content, name);
|
|
5961
6171
|
out.push({
|
|
5962
|
-
infraId: (0,
|
|
6172
|
+
infraId: (0, import_types17.infraId)(kind, name),
|
|
5963
6173
|
name,
|
|
5964
6174
|
kind,
|
|
5965
6175
|
edgeType: "CALLS",
|
|
@@ -5968,7 +6178,7 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5968
6178
|
// (ADR-066).
|
|
5969
6179
|
confidenceKind: "verified-call-site",
|
|
5970
6180
|
evidence: {
|
|
5971
|
-
file:
|
|
6181
|
+
file: import_node_path29.default.relative(serviceDir, file.path),
|
|
5972
6182
|
line,
|
|
5973
6183
|
snippet: snippet(file.content, line)
|
|
5974
6184
|
}
|
|
@@ -5993,8 +6203,8 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
5993
6203
|
|
|
5994
6204
|
// src/extract/calls/grpc.ts
|
|
5995
6205
|
init_cjs_shims();
|
|
5996
|
-
var
|
|
5997
|
-
var
|
|
6206
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
6207
|
+
var import_types18 = require("@neat.is/types");
|
|
5998
6208
|
var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
|
|
5999
6209
|
var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
|
|
6000
6210
|
var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
|
|
@@ -6043,7 +6253,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6043
6253
|
const { kind } = classified;
|
|
6044
6254
|
const line = lineOf(file.content, m[0]);
|
|
6045
6255
|
out.push({
|
|
6046
|
-
infraId: (0,
|
|
6256
|
+
infraId: (0, import_types18.infraId)(kind, name),
|
|
6047
6257
|
name,
|
|
6048
6258
|
kind,
|
|
6049
6259
|
edgeType: "CALLS",
|
|
@@ -6052,7 +6262,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6052
6262
|
// tier (ADR-066).
|
|
6053
6263
|
confidenceKind: "verified-call-site",
|
|
6054
6264
|
evidence: {
|
|
6055
|
-
file:
|
|
6265
|
+
file: import_node_path30.default.relative(serviceDir, file.path),
|
|
6056
6266
|
line,
|
|
6057
6267
|
snippet: snippet(file.content, line)
|
|
6058
6268
|
}
|
|
@@ -6063,8 +6273,8 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
6063
6273
|
|
|
6064
6274
|
// src/extract/calls/supabase.ts
|
|
6065
6275
|
init_cjs_shims();
|
|
6066
|
-
var
|
|
6067
|
-
var
|
|
6276
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6277
|
+
var import_types19 = require("@neat.is/types");
|
|
6068
6278
|
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
6069
6279
|
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
6070
6280
|
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
@@ -6102,7 +6312,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6102
6312
|
seen.add(name);
|
|
6103
6313
|
const line = lineOf(file.content, m[0]);
|
|
6104
6314
|
out.push({
|
|
6105
|
-
infraId: (0,
|
|
6315
|
+
infraId: (0, import_types19.infraId)("supabase", name),
|
|
6106
6316
|
name,
|
|
6107
6317
|
kind: "supabase",
|
|
6108
6318
|
edgeType: "CALLS",
|
|
@@ -6112,7 +6322,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6112
6322
|
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
6113
6323
|
confidenceKind: "verified-call-site",
|
|
6114
6324
|
evidence: {
|
|
6115
|
-
file:
|
|
6325
|
+
file: import_node_path31.default.relative(serviceDir, file.path),
|
|
6116
6326
|
line,
|
|
6117
6327
|
snippet: snippet(file.content, line)
|
|
6118
6328
|
}
|
|
@@ -6125,11 +6335,11 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6125
6335
|
function edgeTypeFromEndpoint(ep) {
|
|
6126
6336
|
switch (ep.edgeType) {
|
|
6127
6337
|
case "PUBLISHES_TO":
|
|
6128
|
-
return
|
|
6338
|
+
return import_types20.EdgeType.PUBLISHES_TO;
|
|
6129
6339
|
case "CONSUMES_FROM":
|
|
6130
|
-
return
|
|
6340
|
+
return import_types20.EdgeType.CONSUMES_FROM;
|
|
6131
6341
|
default:
|
|
6132
|
-
return
|
|
6342
|
+
return import_types20.EdgeType.CALLS;
|
|
6133
6343
|
}
|
|
6134
6344
|
}
|
|
6135
6345
|
function isAwsKind(kind) {
|
|
@@ -6157,7 +6367,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6157
6367
|
if (!graph.hasNode(ep.infraId)) {
|
|
6158
6368
|
const node = {
|
|
6159
6369
|
id: ep.infraId,
|
|
6160
|
-
type:
|
|
6370
|
+
type: import_types20.NodeType.InfraNode,
|
|
6161
6371
|
name: ep.name,
|
|
6162
6372
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6163
6373
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6169,7 +6379,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6169
6379
|
nodesAdded++;
|
|
6170
6380
|
}
|
|
6171
6381
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6172
|
-
const confidence = (0,
|
|
6382
|
+
const confidence = (0, import_types20.confidenceForExtracted)(ep.confidenceKind);
|
|
6173
6383
|
const relFile = toPosix2(ep.evidence.file);
|
|
6174
6384
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6175
6385
|
graph,
|
|
@@ -6179,7 +6389,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6179
6389
|
);
|
|
6180
6390
|
nodesAdded += n;
|
|
6181
6391
|
edgesAdded += e;
|
|
6182
|
-
if (!(0,
|
|
6392
|
+
if (!(0, import_types20.passesExtractedFloor)(confidence)) {
|
|
6183
6393
|
noteExtractedDropped({
|
|
6184
6394
|
source: fileNodeId,
|
|
6185
6395
|
target: ep.infraId,
|
|
@@ -6199,7 +6409,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6199
6409
|
source: fileNodeId,
|
|
6200
6410
|
target: ep.infraId,
|
|
6201
6411
|
type: edgeType,
|
|
6202
|
-
provenance:
|
|
6412
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
6203
6413
|
confidence,
|
|
6204
6414
|
evidence: ep.evidence
|
|
6205
6415
|
};
|
|
@@ -6225,16 +6435,16 @@ init_cjs_shims();
|
|
|
6225
6435
|
|
|
6226
6436
|
// src/extract/infra/docker-compose.ts
|
|
6227
6437
|
init_cjs_shims();
|
|
6228
|
-
var
|
|
6229
|
-
var
|
|
6438
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6439
|
+
var import_types22 = require("@neat.is/types");
|
|
6230
6440
|
|
|
6231
6441
|
// src/extract/infra/shared.ts
|
|
6232
6442
|
init_cjs_shims();
|
|
6233
|
-
var
|
|
6443
|
+
var import_types21 = require("@neat.is/types");
|
|
6234
6444
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6235
6445
|
return {
|
|
6236
|
-
id: (0,
|
|
6237
|
-
type:
|
|
6446
|
+
id: (0, import_types21.infraId)(kind, name),
|
|
6447
|
+
type: import_types21.NodeType.InfraNode,
|
|
6238
6448
|
name,
|
|
6239
6449
|
provider,
|
|
6240
6450
|
kind,
|
|
@@ -6263,7 +6473,7 @@ function dependsOnList(value) {
|
|
|
6263
6473
|
}
|
|
6264
6474
|
function serviceNameToServiceNode(name, services) {
|
|
6265
6475
|
for (const s of services) {
|
|
6266
|
-
if (s.node.name === name ||
|
|
6476
|
+
if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
|
|
6267
6477
|
}
|
|
6268
6478
|
return null;
|
|
6269
6479
|
}
|
|
@@ -6272,7 +6482,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6272
6482
|
let edgesAdded = 0;
|
|
6273
6483
|
let composePath = null;
|
|
6274
6484
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6275
|
-
const abs =
|
|
6485
|
+
const abs = import_node_path32.default.join(scanPath, name);
|
|
6276
6486
|
if (await exists(abs)) {
|
|
6277
6487
|
composePath = abs;
|
|
6278
6488
|
break;
|
|
@@ -6285,13 +6495,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6285
6495
|
} catch (err) {
|
|
6286
6496
|
recordExtractionError(
|
|
6287
6497
|
"infra docker-compose",
|
|
6288
|
-
|
|
6498
|
+
import_node_path32.default.relative(scanPath, composePath),
|
|
6289
6499
|
err
|
|
6290
6500
|
);
|
|
6291
6501
|
return { nodesAdded, edgesAdded };
|
|
6292
6502
|
}
|
|
6293
6503
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6294
|
-
const evidenceFile =
|
|
6504
|
+
const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
|
|
6295
6505
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6296
6506
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6297
6507
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6313,15 +6523,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6313
6523
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6314
6524
|
const targetId = composeNameToNodeId.get(dep);
|
|
6315
6525
|
if (!targetId) continue;
|
|
6316
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
6526
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types22.EdgeType.DEPENDS_ON);
|
|
6317
6527
|
if (graph.hasEdge(edgeId)) continue;
|
|
6318
6528
|
const edge = {
|
|
6319
6529
|
id: edgeId,
|
|
6320
6530
|
source: sourceId,
|
|
6321
6531
|
target: targetId,
|
|
6322
|
-
type:
|
|
6323
|
-
provenance:
|
|
6324
|
-
confidence: (0,
|
|
6532
|
+
type: import_types22.EdgeType.DEPENDS_ON,
|
|
6533
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6534
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6325
6535
|
evidence: { file: evidenceFile }
|
|
6326
6536
|
};
|
|
6327
6537
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6333,9 +6543,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6333
6543
|
|
|
6334
6544
|
// src/extract/infra/dockerfile.ts
|
|
6335
6545
|
init_cjs_shims();
|
|
6336
|
-
var
|
|
6337
|
-
var
|
|
6338
|
-
var
|
|
6546
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6547
|
+
var import_node_fs17 = require("fs");
|
|
6548
|
+
var import_types23 = require("@neat.is/types");
|
|
6339
6549
|
function readDockerfile(content) {
|
|
6340
6550
|
let image = null;
|
|
6341
6551
|
const ports = [];
|
|
@@ -6364,15 +6574,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6364
6574
|
let nodesAdded = 0;
|
|
6365
6575
|
let edgesAdded = 0;
|
|
6366
6576
|
for (const service of services) {
|
|
6367
|
-
const dockerfilePath =
|
|
6577
|
+
const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
|
|
6368
6578
|
if (!await exists(dockerfilePath)) continue;
|
|
6369
6579
|
let content;
|
|
6370
6580
|
try {
|
|
6371
|
-
content = await
|
|
6581
|
+
content = await import_node_fs17.promises.readFile(dockerfilePath, "utf8");
|
|
6372
6582
|
} catch (err) {
|
|
6373
6583
|
recordExtractionError(
|
|
6374
6584
|
"infra dockerfile",
|
|
6375
|
-
|
|
6585
|
+
import_node_path33.default.relative(scanPath, dockerfilePath),
|
|
6376
6586
|
err
|
|
6377
6587
|
);
|
|
6378
6588
|
continue;
|
|
@@ -6384,8 +6594,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6384
6594
|
graph.addNode(node.id, node);
|
|
6385
6595
|
nodesAdded++;
|
|
6386
6596
|
}
|
|
6387
|
-
const relDockerfile = toPosix2(
|
|
6388
|
-
const evidenceFile = toPosix2(
|
|
6597
|
+
const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
|
|
6598
|
+
const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
|
|
6389
6599
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6390
6600
|
graph,
|
|
6391
6601
|
service.pkg.name,
|
|
@@ -6394,15 +6604,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6394
6604
|
);
|
|
6395
6605
|
nodesAdded += fn;
|
|
6396
6606
|
edgesAdded += fe;
|
|
6397
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
6607
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types23.EdgeType.RUNS_ON);
|
|
6398
6608
|
if (!graph.hasEdge(edgeId)) {
|
|
6399
6609
|
const edge = {
|
|
6400
6610
|
id: edgeId,
|
|
6401
6611
|
source: fileNodeId,
|
|
6402
6612
|
target: node.id,
|
|
6403
|
-
type:
|
|
6404
|
-
provenance:
|
|
6405
|
-
confidence: (0,
|
|
6613
|
+
type: import_types23.EdgeType.RUNS_ON,
|
|
6614
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6615
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6406
6616
|
evidence: {
|
|
6407
6617
|
file: evidenceFile,
|
|
6408
6618
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6417,15 +6627,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6417
6627
|
graph.addNode(portNode.id, portNode);
|
|
6418
6628
|
nodesAdded++;
|
|
6419
6629
|
}
|
|
6420
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
6630
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types23.EdgeType.CONNECTS_TO);
|
|
6421
6631
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6422
6632
|
const portEdge = {
|
|
6423
6633
|
id: portEdgeId,
|
|
6424
6634
|
source: fileNodeId,
|
|
6425
6635
|
target: portNode.id,
|
|
6426
|
-
type:
|
|
6427
|
-
provenance:
|
|
6428
|
-
confidence: (0,
|
|
6636
|
+
type: import_types23.EdgeType.CONNECTS_TO,
|
|
6637
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6638
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6429
6639
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6430
6640
|
};
|
|
6431
6641
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6437,23 +6647,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6437
6647
|
|
|
6438
6648
|
// src/extract/infra/terraform.ts
|
|
6439
6649
|
init_cjs_shims();
|
|
6440
|
-
var
|
|
6441
|
-
var
|
|
6442
|
-
var
|
|
6650
|
+
var import_node_fs18 = require("fs");
|
|
6651
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
6652
|
+
var import_types24 = require("@neat.is/types");
|
|
6443
6653
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6444
6654
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6445
6655
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
6446
6656
|
if (depth > max) return [];
|
|
6447
6657
|
const out = [];
|
|
6448
|
-
const entries = await
|
|
6658
|
+
const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6449
6659
|
for (const entry2 of entries) {
|
|
6450
6660
|
if (entry2.isDirectory()) {
|
|
6451
6661
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
6452
|
-
const child =
|
|
6662
|
+
const child = import_node_path34.default.join(start, entry2.name);
|
|
6453
6663
|
if (await isPythonVenvDir(child)) continue;
|
|
6454
6664
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6455
6665
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
6456
|
-
out.push(
|
|
6666
|
+
out.push(import_node_path34.default.join(start, entry2.name));
|
|
6457
6667
|
}
|
|
6458
6668
|
}
|
|
6459
6669
|
return out;
|
|
@@ -6472,7 +6682,7 @@ function blockBody(content, from) {
|
|
|
6472
6682
|
}
|
|
6473
6683
|
return null;
|
|
6474
6684
|
}
|
|
6475
|
-
function
|
|
6685
|
+
function lineAt2(content, index) {
|
|
6476
6686
|
let line = 1;
|
|
6477
6687
|
for (let i = 0; i < index && i < content.length; i++) {
|
|
6478
6688
|
if (content[i] === "\n") line++;
|
|
@@ -6484,8 +6694,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6484
6694
|
let edgesAdded = 0;
|
|
6485
6695
|
const files = await walkTfFiles(scanPath);
|
|
6486
6696
|
for (const file of files) {
|
|
6487
|
-
const content = await
|
|
6488
|
-
const evidenceFile = toPosix2(
|
|
6697
|
+
const content = await import_node_fs18.promises.readFile(file, "utf8");
|
|
6698
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
|
|
6489
6699
|
const resources = [];
|
|
6490
6700
|
const byKey = /* @__PURE__ */ new Map();
|
|
6491
6701
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6520,16 +6730,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6520
6730
|
if (!target) continue;
|
|
6521
6731
|
if (seen.has(target.nodeId)) continue;
|
|
6522
6732
|
seen.add(target.nodeId);
|
|
6523
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
6733
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types24.EdgeType.DEPENDS_ON);
|
|
6524
6734
|
if (graph.hasEdge(edgeId)) continue;
|
|
6525
|
-
const line =
|
|
6735
|
+
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6526
6736
|
const edge = {
|
|
6527
6737
|
id: edgeId,
|
|
6528
6738
|
source: resource.nodeId,
|
|
6529
6739
|
target: target.nodeId,
|
|
6530
|
-
type:
|
|
6531
|
-
provenance:
|
|
6532
|
-
confidence: (0,
|
|
6740
|
+
type: import_types24.EdgeType.DEPENDS_ON,
|
|
6741
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
6742
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6533
6743
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6534
6744
|
};
|
|
6535
6745
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6542,8 +6752,8 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6542
6752
|
|
|
6543
6753
|
// src/extract/infra/k8s.ts
|
|
6544
6754
|
init_cjs_shims();
|
|
6545
|
-
var
|
|
6546
|
-
var
|
|
6755
|
+
var import_node_fs19 = require("fs");
|
|
6756
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6547
6757
|
var import_yaml3 = require("yaml");
|
|
6548
6758
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6549
6759
|
Service: "k8s-service",
|
|
@@ -6557,15 +6767,15 @@ var K8S_KIND_TO_INFRA_KIND = {
|
|
|
6557
6767
|
async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
6558
6768
|
if (depth > max) return [];
|
|
6559
6769
|
const out = [];
|
|
6560
|
-
const entries = await
|
|
6770
|
+
const entries = await import_node_fs19.promises.readdir(start, { withFileTypes: true }).catch(() => []);
|
|
6561
6771
|
for (const entry2 of entries) {
|
|
6562
6772
|
if (entry2.isDirectory()) {
|
|
6563
6773
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
6564
|
-
const child =
|
|
6774
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
6565
6775
|
if (await isPythonVenvDir(child)) continue;
|
|
6566
6776
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6567
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6568
|
-
out.push(
|
|
6777
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry2.name))) {
|
|
6778
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
6569
6779
|
}
|
|
6570
6780
|
}
|
|
6571
6781
|
return out;
|
|
@@ -6574,7 +6784,7 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6574
6784
|
let nodesAdded = 0;
|
|
6575
6785
|
const files = await walkYamlFiles2(scanPath);
|
|
6576
6786
|
for (const file of files) {
|
|
6577
|
-
const content = await
|
|
6787
|
+
const content = await import_node_fs19.promises.readFile(file, "utf8");
|
|
6578
6788
|
let docs;
|
|
6579
6789
|
try {
|
|
6580
6790
|
docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
|
|
@@ -6609,17 +6819,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
6609
6819
|
}
|
|
6610
6820
|
|
|
6611
6821
|
// src/extract/index.ts
|
|
6612
|
-
var
|
|
6822
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
6613
6823
|
|
|
6614
6824
|
// src/extract/retire.ts
|
|
6615
6825
|
init_cjs_shims();
|
|
6616
|
-
var
|
|
6617
|
-
var
|
|
6618
|
-
var
|
|
6826
|
+
var import_node_fs20 = require("fs");
|
|
6827
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6828
|
+
var import_types25 = require("@neat.is/types");
|
|
6619
6829
|
function dropOrphanedFileNodes(graph) {
|
|
6620
6830
|
const orphans = [];
|
|
6621
6831
|
graph.forEachNode((id, attrs) => {
|
|
6622
|
-
if (attrs.type !==
|
|
6832
|
+
if (attrs.type !== import_types25.NodeType.FileNode) return;
|
|
6623
6833
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
6624
6834
|
orphans.push(id);
|
|
6625
6835
|
}
|
|
@@ -6632,7 +6842,7 @@ function retireEdgesByFile(graph, file) {
|
|
|
6632
6842
|
const toDrop = [];
|
|
6633
6843
|
graph.forEachEdge((id, attrs) => {
|
|
6634
6844
|
const edge = attrs;
|
|
6635
|
-
if (edge.provenance !==
|
|
6845
|
+
if (edge.provenance !== import_types25.Provenance.EXTRACTED) return;
|
|
6636
6846
|
if (!edge.evidence?.file) return;
|
|
6637
6847
|
if (edge.evidence.file === normalized) toDrop.push(id);
|
|
6638
6848
|
});
|
|
@@ -6645,14 +6855,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
6645
6855
|
const bases = [scanPath, ...serviceDirs];
|
|
6646
6856
|
graph.forEachEdge((id, attrs) => {
|
|
6647
6857
|
const edge = attrs;
|
|
6648
|
-
if (edge.provenance !==
|
|
6858
|
+
if (edge.provenance !== import_types25.Provenance.EXTRACTED) return;
|
|
6649
6859
|
const evidenceFile = edge.evidence?.file;
|
|
6650
6860
|
if (!evidenceFile) return;
|
|
6651
|
-
if (
|
|
6652
|
-
if (!(0,
|
|
6861
|
+
if (import_node_path36.default.isAbsolute(evidenceFile)) {
|
|
6862
|
+
if (!(0, import_node_fs20.existsSync)(evidenceFile)) toDrop.push(id);
|
|
6653
6863
|
return;
|
|
6654
6864
|
}
|
|
6655
|
-
const found = bases.some((base) => (0,
|
|
6865
|
+
const found = bases.some((base) => (0, import_node_fs20.existsSync)(import_node_path36.default.join(base, evidenceFile)));
|
|
6656
6866
|
if (!found) toDrop.push(id);
|
|
6657
6867
|
});
|
|
6658
6868
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -6672,6 +6882,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6672
6882
|
const phase2 = await addDatabasesAndCompat(graph, services, scanPath);
|
|
6673
6883
|
const phase3 = await addConfigNodes(graph, services, scanPath);
|
|
6674
6884
|
const routePhase = await addRoutes(graph, services);
|
|
6885
|
+
const grpcPhase = await addGrpcMethods(graph, services);
|
|
6675
6886
|
const phase4 = await addCallEdges(graph, services);
|
|
6676
6887
|
const phase5 = await addInfra(graph, scanPath, services);
|
|
6677
6888
|
const ghostsRetired = retireExtractedEdgesByMissingFile(
|
|
@@ -6693,7 +6904,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6693
6904
|
}
|
|
6694
6905
|
const droppedEntries = drainDroppedExtracted();
|
|
6695
6906
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
6696
|
-
const rejectedPath =
|
|
6907
|
+
const rejectedPath = import_node_path37.default.join(import_node_path37.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
6697
6908
|
try {
|
|
6698
6909
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
6699
6910
|
} catch (err) {
|
|
@@ -6703,8 +6914,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6703
6914
|
}
|
|
6704
6915
|
}
|
|
6705
6916
|
const result = {
|
|
6706
|
-
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
6707
|
-
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
6917
|
+
nodesAdded: phase1Nodes + fileEnum.nodesAdded + importGraph.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + grpcPhase.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
6918
|
+
edgesAdded: fileEnum.edgesAdded + importGraph.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + grpcPhase.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
6708
6919
|
frontiersPromoted,
|
|
6709
6920
|
extractionErrors: errorEntries.length,
|
|
6710
6921
|
errorEntries,
|
|
@@ -6727,7 +6938,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
6727
6938
|
|
|
6728
6939
|
// src/divergences.ts
|
|
6729
6940
|
init_cjs_shims();
|
|
6730
|
-
var
|
|
6941
|
+
var import_types26 = require("@neat.is/types");
|
|
6731
6942
|
function bucketKey(source, target, type) {
|
|
6732
6943
|
return `${type}|${source}|${target}`;
|
|
6733
6944
|
}
|
|
@@ -6735,22 +6946,22 @@ function bucketEdges(graph) {
|
|
|
6735
6946
|
const buckets = /* @__PURE__ */ new Map();
|
|
6736
6947
|
graph.forEachEdge((id, attrs) => {
|
|
6737
6948
|
const e = attrs;
|
|
6738
|
-
const parsed = (0,
|
|
6949
|
+
const parsed = (0, import_types26.parseEdgeId)(id);
|
|
6739
6950
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
6740
6951
|
const key = bucketKey(e.source, e.target, e.type);
|
|
6741
6952
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
6742
6953
|
switch (provenance) {
|
|
6743
|
-
case
|
|
6954
|
+
case import_types26.Provenance.EXTRACTED:
|
|
6744
6955
|
cur.extracted = e;
|
|
6745
6956
|
break;
|
|
6746
|
-
case
|
|
6957
|
+
case import_types26.Provenance.OBSERVED:
|
|
6747
6958
|
cur.observed = e;
|
|
6748
6959
|
break;
|
|
6749
|
-
case
|
|
6960
|
+
case import_types26.Provenance.INFERRED:
|
|
6750
6961
|
cur.inferred = e;
|
|
6751
6962
|
break;
|
|
6752
6963
|
default:
|
|
6753
|
-
if (e.provenance ===
|
|
6964
|
+
if (e.provenance === import_types26.Provenance.STALE) cur.stale = e;
|
|
6754
6965
|
}
|
|
6755
6966
|
buckets.set(key, cur);
|
|
6756
6967
|
});
|
|
@@ -6759,7 +6970,12 @@ function bucketEdges(graph) {
|
|
|
6759
6970
|
function nodeIsFrontier(graph, nodeId) {
|
|
6760
6971
|
if (!graph.hasNode(nodeId)) return false;
|
|
6761
6972
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
6762
|
-
return attrs.type ===
|
|
6973
|
+
return attrs.type === import_types26.NodeType.FrontierNode;
|
|
6974
|
+
}
|
|
6975
|
+
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
6976
|
+
if (!graph.hasNode(nodeId)) return false;
|
|
6977
|
+
const attrs = graph.getNodeAttributes(nodeId);
|
|
6978
|
+
return attrs.type === import_types26.NodeType.WebSocketChannelNode;
|
|
6763
6979
|
}
|
|
6764
6980
|
function clampConfidence(n) {
|
|
6765
6981
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -6779,14 +6995,14 @@ function gradedConfidence(edge) {
|
|
|
6779
6995
|
return clampConfidence(confidenceForEdge(edge));
|
|
6780
6996
|
}
|
|
6781
6997
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6998
|
+
import_types26.EdgeType.CALLS,
|
|
6999
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7000
|
+
import_types26.EdgeType.PUBLISHES_TO,
|
|
7001
|
+
import_types26.EdgeType.CONSUMES_FROM
|
|
6786
7002
|
]);
|
|
6787
7003
|
function detectMissingDivergences(graph, bucket) {
|
|
6788
7004
|
const out = [];
|
|
6789
|
-
if (bucket.type ===
|
|
7005
|
+
if (bucket.type === import_types26.EdgeType.CONTAINS) return out;
|
|
6790
7006
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
6791
7007
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
6792
7008
|
out.push({
|
|
@@ -6801,7 +7017,7 @@ function detectMissingDivergences(graph, bucket) {
|
|
|
6801
7017
|
});
|
|
6802
7018
|
}
|
|
6803
7019
|
}
|
|
6804
|
-
if (bucket.observed && !bucket.extracted) {
|
|
7020
|
+
if (bucket.observed && !bucket.extracted && !nodeIsWebsocketChannel(graph, bucket.target)) {
|
|
6805
7021
|
out.push({
|
|
6806
7022
|
type: "missing-extracted",
|
|
6807
7023
|
source: bucket.source,
|
|
@@ -6827,7 +7043,7 @@ function declaredHostFor(svc) {
|
|
|
6827
7043
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
6828
7044
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
6829
7045
|
const e = graph.getEdgeAttributes(edgeId);
|
|
6830
|
-
if (e.type ===
|
|
7046
|
+
if (e.type === import_types26.EdgeType.CONFIGURED_BY && e.provenance === import_types26.Provenance.EXTRACTED) {
|
|
6831
7047
|
return true;
|
|
6832
7048
|
}
|
|
6833
7049
|
}
|
|
@@ -6840,10 +7056,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
6840
7056
|
const out = [];
|
|
6841
7057
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
6842
7058
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6843
|
-
if (edge.type !==
|
|
6844
|
-
if (edge.provenance !==
|
|
7059
|
+
if (edge.type !== import_types26.EdgeType.CONNECTS_TO) continue;
|
|
7060
|
+
if (edge.provenance !== import_types26.Provenance.OBSERVED) continue;
|
|
6845
7061
|
const target = graph.getNodeAttributes(edge.target);
|
|
6846
|
-
if (target.type !==
|
|
7062
|
+
if (target.type !== import_types26.NodeType.DatabaseNode) continue;
|
|
6847
7063
|
const observedHost = target.host?.trim();
|
|
6848
7064
|
if (!observedHost) continue;
|
|
6849
7065
|
if (observedHost === declaredHost) continue;
|
|
@@ -6865,10 +7081,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
6865
7081
|
const deps = svc.dependencies ?? {};
|
|
6866
7082
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
6867
7083
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
6868
|
-
if (edge.type !==
|
|
6869
|
-
if (edge.provenance !==
|
|
7084
|
+
if (edge.type !== import_types26.EdgeType.CONNECTS_TO) continue;
|
|
7085
|
+
if (edge.provenance !== import_types26.Provenance.OBSERVED) continue;
|
|
6870
7086
|
const target = graph.getNodeAttributes(edge.target);
|
|
6871
|
-
if (target.type !==
|
|
7087
|
+
if (target.type !== import_types26.NodeType.DatabaseNode) continue;
|
|
6872
7088
|
for (const pair of compatPairs()) {
|
|
6873
7089
|
if (pair.engine !== target.engine) continue;
|
|
6874
7090
|
const declared = deps[pair.driver];
|
|
@@ -6927,7 +7143,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
6927
7143
|
for (const d of all) {
|
|
6928
7144
|
if (d.type !== "host-mismatch") continue;
|
|
6929
7145
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
6930
|
-
declaredHalf.add((0,
|
|
7146
|
+
declaredHalf.add((0, import_types26.databaseId)(d.extractedHost));
|
|
6931
7147
|
}
|
|
6932
7148
|
if (observedHalf.size === 0) return all;
|
|
6933
7149
|
return all.filter((d) => {
|
|
@@ -6946,7 +7162,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
6946
7162
|
}
|
|
6947
7163
|
graph.forEachNode((nodeId, attrs) => {
|
|
6948
7164
|
const n = attrs;
|
|
6949
|
-
if (n.type !==
|
|
7165
|
+
if (n.type !== import_types26.NodeType.ServiceNode) return;
|
|
6950
7166
|
const svc = n;
|
|
6951
7167
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
6952
7168
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -6980,7 +7196,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
6980
7196
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
6981
7197
|
return a.target.localeCompare(b.target);
|
|
6982
7198
|
});
|
|
6983
|
-
return
|
|
7199
|
+
return import_types26.DivergenceResultSchema.parse({
|
|
6984
7200
|
divergences: filtered,
|
|
6985
7201
|
totalAffected: filtered.length,
|
|
6986
7202
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -6989,9 +7205,9 @@ function computeDivergences(graph, opts = {}) {
|
|
|
6989
7205
|
|
|
6990
7206
|
// src/persist.ts
|
|
6991
7207
|
init_cjs_shims();
|
|
6992
|
-
var
|
|
6993
|
-
var
|
|
6994
|
-
var
|
|
7208
|
+
var import_node_fs21 = require("fs");
|
|
7209
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7210
|
+
var import_types27 = require("@neat.is/types");
|
|
6995
7211
|
var SCHEMA_VERSION = 4;
|
|
6996
7212
|
function migrateV1ToV2(payload) {
|
|
6997
7213
|
const nodes = payload.graph.nodes;
|
|
@@ -7013,12 +7229,12 @@ function migrateV2ToV3(payload) {
|
|
|
7013
7229
|
for (const edge of edges) {
|
|
7014
7230
|
const attrs = edge.attributes;
|
|
7015
7231
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7016
|
-
attrs.provenance =
|
|
7232
|
+
attrs.provenance = import_types27.Provenance.OBSERVED;
|
|
7017
7233
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7018
7234
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7019
7235
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7020
7236
|
if (type && source && target) {
|
|
7021
|
-
const newId = (0,
|
|
7237
|
+
const newId = (0, import_types27.observedEdgeId)(source, target, type);
|
|
7022
7238
|
attrs.id = newId;
|
|
7023
7239
|
if (edge.key) edge.key = newId;
|
|
7024
7240
|
}
|
|
@@ -7027,7 +7243,7 @@ function migrateV2ToV3(payload) {
|
|
|
7027
7243
|
return { ...payload, schemaVersion: 3 };
|
|
7028
7244
|
}
|
|
7029
7245
|
async function ensureDir(filePath) {
|
|
7030
|
-
await
|
|
7246
|
+
await import_node_fs21.promises.mkdir(import_node_path38.default.dirname(filePath), { recursive: true });
|
|
7031
7247
|
}
|
|
7032
7248
|
async function saveGraphToDisk(graph, outPath) {
|
|
7033
7249
|
await ensureDir(outPath);
|
|
@@ -7037,13 +7253,13 @@ async function saveGraphToDisk(graph, outPath) {
|
|
|
7037
7253
|
graph: graph.export()
|
|
7038
7254
|
};
|
|
7039
7255
|
const tmp = `${outPath}.tmp`;
|
|
7040
|
-
await
|
|
7041
|
-
await
|
|
7256
|
+
await import_node_fs21.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
|
|
7257
|
+
await import_node_fs21.promises.rename(tmp, outPath);
|
|
7042
7258
|
}
|
|
7043
7259
|
async function loadGraphFromDisk(graph, outPath) {
|
|
7044
7260
|
let raw;
|
|
7045
7261
|
try {
|
|
7046
|
-
raw = await
|
|
7262
|
+
raw = await import_node_fs21.promises.readFile(outPath, "utf8");
|
|
7047
7263
|
} catch (err) {
|
|
7048
7264
|
if (err.code === "ENOENT") return;
|
|
7049
7265
|
throw err;
|
|
@@ -7108,8 +7324,8 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7108
7324
|
|
|
7109
7325
|
// src/gitignore.ts
|
|
7110
7326
|
init_cjs_shims();
|
|
7111
|
-
var
|
|
7112
|
-
var
|
|
7327
|
+
var import_node_fs22 = require("fs");
|
|
7328
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7113
7329
|
var NEAT_OUT_LINE = "neat-out/";
|
|
7114
7330
|
var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
|
|
7115
7331
|
function isNeatOutLine(line) {
|
|
@@ -7117,15 +7333,15 @@ function isNeatOutLine(line) {
|
|
|
7117
7333
|
return trimmed === "neat-out/" || trimmed === "neat-out";
|
|
7118
7334
|
}
|
|
7119
7335
|
async function ensureNeatOutIgnored(projectDir) {
|
|
7120
|
-
const file =
|
|
7336
|
+
const file = import_node_path39.default.join(projectDir, ".gitignore");
|
|
7121
7337
|
let existing = null;
|
|
7122
7338
|
try {
|
|
7123
|
-
existing = await
|
|
7339
|
+
existing = await import_node_fs22.promises.readFile(file, "utf8");
|
|
7124
7340
|
} catch (err) {
|
|
7125
7341
|
if (err.code !== "ENOENT") throw err;
|
|
7126
7342
|
}
|
|
7127
7343
|
if (existing === null) {
|
|
7128
|
-
await
|
|
7344
|
+
await import_node_fs22.promises.writeFile(file, `${NEAT_HEADER}
|
|
7129
7345
|
${NEAT_OUT_LINE}
|
|
7130
7346
|
`, "utf8");
|
|
7131
7347
|
return { action: "created", file };
|
|
@@ -7138,13 +7354,13 @@ ${NEAT_OUT_LINE}
|
|
|
7138
7354
|
${NEAT_HEADER}
|
|
7139
7355
|
${NEAT_OUT_LINE}
|
|
7140
7356
|
`;
|
|
7141
|
-
await
|
|
7357
|
+
await import_node_fs22.promises.writeFile(file, existing + appended, "utf8");
|
|
7142
7358
|
return { action: "added", file };
|
|
7143
7359
|
}
|
|
7144
7360
|
|
|
7145
7361
|
// src/summary.ts
|
|
7146
7362
|
init_cjs_shims();
|
|
7147
|
-
var
|
|
7363
|
+
var import_types28 = require("@neat.is/types");
|
|
7148
7364
|
function renderOtelEnvBlock() {
|
|
7149
7365
|
return [
|
|
7150
7366
|
"for prod OTel routing, set these in your deploy platform's env:",
|
|
@@ -7154,19 +7370,19 @@ function renderOtelEnvBlock() {
|
|
|
7154
7370
|
}
|
|
7155
7371
|
function findIncompatServices(nodes) {
|
|
7156
7372
|
return nodes.filter(
|
|
7157
|
-
(n) => n.type ===
|
|
7373
|
+
(n) => n.type === import_types28.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
|
|
7158
7374
|
);
|
|
7159
7375
|
}
|
|
7160
7376
|
function servicesWithoutObserved(nodes, edges) {
|
|
7161
7377
|
const seen = /* @__PURE__ */ new Set();
|
|
7162
7378
|
for (const e of edges) {
|
|
7163
|
-
if (e.provenance ===
|
|
7379
|
+
if (e.provenance === import_types28.Provenance.OBSERVED) {
|
|
7164
7380
|
seen.add(e.source);
|
|
7165
7381
|
seen.add(e.target);
|
|
7166
7382
|
}
|
|
7167
7383
|
}
|
|
7168
7384
|
return nodes.filter(
|
|
7169
|
-
(n) => n.type ===
|
|
7385
|
+
(n) => n.type === import_types28.NodeType.ServiceNode && !seen.has(n.id)
|
|
7170
7386
|
);
|
|
7171
7387
|
}
|
|
7172
7388
|
function formatDivergence(d) {
|
|
@@ -7240,27 +7456,27 @@ function formatIncompat(inc) {
|
|
|
7240
7456
|
|
|
7241
7457
|
// src/watch.ts
|
|
7242
7458
|
init_cjs_shims();
|
|
7243
|
-
var
|
|
7244
|
-
var
|
|
7459
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
7460
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
7245
7461
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
7246
7462
|
|
|
7247
7463
|
// src/api.ts
|
|
7248
7464
|
init_cjs_shims();
|
|
7249
7465
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
7250
7466
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
7251
|
-
var
|
|
7467
|
+
var import_types30 = require("@neat.is/types");
|
|
7252
7468
|
|
|
7253
7469
|
// src/extend/index.ts
|
|
7254
7470
|
init_cjs_shims();
|
|
7255
|
-
var
|
|
7256
|
-
var
|
|
7471
|
+
var import_node_fs24 = require("fs");
|
|
7472
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7257
7473
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
7258
7474
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
7259
7475
|
|
|
7260
7476
|
// src/installers/package-manager.ts
|
|
7261
7477
|
init_cjs_shims();
|
|
7262
|
-
var
|
|
7263
|
-
var
|
|
7478
|
+
var import_node_fs23 = require("fs");
|
|
7479
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7264
7480
|
var import_node_child_process = require("child_process");
|
|
7265
7481
|
var LOCKFILE_PRIORITY = [
|
|
7266
7482
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -7275,29 +7491,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
7275
7491
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
7276
7492
|
async function exists2(p) {
|
|
7277
7493
|
try {
|
|
7278
|
-
await
|
|
7494
|
+
await import_node_fs23.promises.access(p);
|
|
7279
7495
|
return true;
|
|
7280
7496
|
} catch {
|
|
7281
7497
|
return false;
|
|
7282
7498
|
}
|
|
7283
7499
|
}
|
|
7284
7500
|
async function detectPackageManager(serviceDir) {
|
|
7285
|
-
let dir =
|
|
7501
|
+
let dir = import_node_path40.default.resolve(serviceDir);
|
|
7286
7502
|
const stops = /* @__PURE__ */ new Set();
|
|
7287
7503
|
for (let i = 0; i < 64; i++) {
|
|
7288
7504
|
if (stops.has(dir)) break;
|
|
7289
7505
|
stops.add(dir);
|
|
7290
7506
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
7291
|
-
const lockPath =
|
|
7507
|
+
const lockPath = import_node_path40.default.join(dir, candidate.lockfile);
|
|
7292
7508
|
if (await exists2(lockPath)) {
|
|
7293
7509
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
7294
7510
|
}
|
|
7295
7511
|
}
|
|
7296
|
-
const parent =
|
|
7512
|
+
const parent = import_node_path40.default.dirname(dir);
|
|
7297
7513
|
if (parent === dir) break;
|
|
7298
7514
|
dir = parent;
|
|
7299
7515
|
}
|
|
7300
|
-
return { pm: "npm", cwd:
|
|
7516
|
+
return { pm: "npm", cwd: import_node_path40.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
7301
7517
|
}
|
|
7302
7518
|
async function runPackageManagerInstall(cmd) {
|
|
7303
7519
|
return new Promise((resolve) => {
|
|
@@ -7339,30 +7555,30 @@ ${err.message}`
|
|
|
7339
7555
|
// src/extend/index.ts
|
|
7340
7556
|
async function fileExists2(p) {
|
|
7341
7557
|
try {
|
|
7342
|
-
await
|
|
7558
|
+
await import_node_fs24.promises.access(p);
|
|
7343
7559
|
return true;
|
|
7344
7560
|
} catch {
|
|
7345
7561
|
return false;
|
|
7346
7562
|
}
|
|
7347
7563
|
}
|
|
7348
7564
|
async function readPackageJson(scanPath) {
|
|
7349
|
-
const pkgPath =
|
|
7350
|
-
const raw = await
|
|
7565
|
+
const pkgPath = import_node_path41.default.join(scanPath, "package.json");
|
|
7566
|
+
const raw = await import_node_fs24.promises.readFile(pkgPath, "utf8");
|
|
7351
7567
|
return JSON.parse(raw);
|
|
7352
7568
|
}
|
|
7353
7569
|
async function findHookFiles(scanPath) {
|
|
7354
|
-
const entries = await
|
|
7570
|
+
const entries = await import_node_fs24.promises.readdir(scanPath);
|
|
7355
7571
|
return entries.filter(
|
|
7356
7572
|
(e) => (e.startsWith("instrumentation") || e.startsWith("otel-init")) && /\.(ts|js)$/.test(e)
|
|
7357
7573
|
).sort();
|
|
7358
7574
|
}
|
|
7359
7575
|
function extendLogPath() {
|
|
7360
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
7576
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path41.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
7361
7577
|
}
|
|
7362
7578
|
async function appendExtendLog(entry2) {
|
|
7363
7579
|
const logPath = extendLogPath();
|
|
7364
|
-
await
|
|
7365
|
-
await
|
|
7580
|
+
await import_node_fs24.promises.mkdir(import_node_path41.default.dirname(logPath), { recursive: true });
|
|
7581
|
+
await import_node_fs24.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
7366
7582
|
}
|
|
7367
7583
|
function splicedContent(fileContent, snippet2) {
|
|
7368
7584
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -7420,7 +7636,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
7420
7636
|
}
|
|
7421
7637
|
async function describeProjectInstrumentation(ctx) {
|
|
7422
7638
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7423
|
-
const envNeat = await fileExists2(
|
|
7639
|
+
const envNeat = await fileExists2(import_node_path41.default.join(ctx.scanPath, ".env.neat"));
|
|
7424
7640
|
const registryInstrPackages = new Set(
|
|
7425
7641
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
7426
7642
|
);
|
|
@@ -7442,31 +7658,31 @@ async function applyExtension(ctx, args, options) {
|
|
|
7442
7658
|
);
|
|
7443
7659
|
}
|
|
7444
7660
|
for (const file of hookFiles) {
|
|
7445
|
-
const content = await
|
|
7661
|
+
const content = await import_node_fs24.promises.readFile(import_node_path41.default.join(ctx.scanPath, file), "utf8");
|
|
7446
7662
|
if (content.includes(args.registration_snippet)) {
|
|
7447
7663
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
7448
7664
|
}
|
|
7449
7665
|
}
|
|
7450
7666
|
const primaryFile = hookFiles[0];
|
|
7451
|
-
const primaryPath =
|
|
7667
|
+
const primaryPath = import_node_path41.default.join(ctx.scanPath, primaryFile);
|
|
7452
7668
|
const filesTouched = [];
|
|
7453
7669
|
const depsAdded = [];
|
|
7454
|
-
const pkgPath =
|
|
7670
|
+
const pkgPath = import_node_path41.default.join(ctx.scanPath, "package.json");
|
|
7455
7671
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7456
7672
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
7457
7673
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
7458
|
-
await
|
|
7674
|
+
await import_node_fs24.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
7459
7675
|
filesTouched.push("package.json");
|
|
7460
7676
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
7461
7677
|
}
|
|
7462
|
-
const hookContent = await
|
|
7678
|
+
const hookContent = await import_node_fs24.promises.readFile(primaryPath, "utf8");
|
|
7463
7679
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
7464
7680
|
if (!patched) {
|
|
7465
7681
|
throw new Error(
|
|
7466
7682
|
`Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
7467
7683
|
);
|
|
7468
7684
|
}
|
|
7469
|
-
await
|
|
7685
|
+
await import_node_fs24.promises.writeFile(primaryPath, patched, "utf8");
|
|
7470
7686
|
filesTouched.push(primaryFile);
|
|
7471
7687
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
7472
7688
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -7497,7 +7713,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7497
7713
|
};
|
|
7498
7714
|
}
|
|
7499
7715
|
for (const file of hookFiles) {
|
|
7500
|
-
const content = await
|
|
7716
|
+
const content = await import_node_fs24.promises.readFile(import_node_path41.default.join(ctx.scanPath, file), "utf8");
|
|
7501
7717
|
if (content.includes(args.registration_snippet)) {
|
|
7502
7718
|
return {
|
|
7503
7719
|
library: args.library,
|
|
@@ -7519,7 +7735,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7519
7735
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
7520
7736
|
filesTouched.push("package.json");
|
|
7521
7737
|
}
|
|
7522
|
-
const hookContent = await
|
|
7738
|
+
const hookContent = await import_node_fs24.promises.readFile(import_node_path41.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
7523
7739
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
7524
7740
|
if (patched) {
|
|
7525
7741
|
filesTouched.push(primaryFile);
|
|
@@ -7534,28 +7750,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
7534
7750
|
if (!await fileExists2(logPath)) {
|
|
7535
7751
|
return { undone: false, message: "no apply found for library" };
|
|
7536
7752
|
}
|
|
7537
|
-
const raw = await
|
|
7753
|
+
const raw = await import_node_fs24.promises.readFile(logPath, "utf8");
|
|
7538
7754
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
7539
7755
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
7540
7756
|
if (!match) {
|
|
7541
7757
|
return { undone: false, message: "no apply found for library" };
|
|
7542
7758
|
}
|
|
7543
|
-
const pkgPath =
|
|
7759
|
+
const pkgPath = import_node_path41.default.join(ctx.scanPath, "package.json");
|
|
7544
7760
|
if (await fileExists2(pkgPath)) {
|
|
7545
7761
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7546
7762
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
7547
7763
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
7548
7764
|
pkg.dependencies = rest;
|
|
7549
|
-
await
|
|
7765
|
+
await import_node_fs24.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
7550
7766
|
}
|
|
7551
7767
|
}
|
|
7552
7768
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7553
7769
|
for (const file of hookFiles) {
|
|
7554
|
-
const filePath =
|
|
7555
|
-
const content = await
|
|
7770
|
+
const filePath = import_node_path41.default.join(ctx.scanPath, file);
|
|
7771
|
+
const content = await import_node_fs24.promises.readFile(filePath, "utf8");
|
|
7556
7772
|
if (content.includes(match.registration_snippet)) {
|
|
7557
7773
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
7558
|
-
await
|
|
7774
|
+
await import_node_fs24.promises.writeFile(filePath, filtered, "utf8");
|
|
7559
7775
|
break;
|
|
7560
7776
|
}
|
|
7561
7777
|
}
|
|
@@ -7567,7 +7783,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
7567
7783
|
|
|
7568
7784
|
// src/diff.ts
|
|
7569
7785
|
init_cjs_shims();
|
|
7570
|
-
var
|
|
7786
|
+
var import_node_fs25 = require("fs");
|
|
7571
7787
|
async function loadSnapshotForDiff(target) {
|
|
7572
7788
|
if (/^https?:\/\//i.test(target)) {
|
|
7573
7789
|
const res = await fetch(target);
|
|
@@ -7576,7 +7792,7 @@ async function loadSnapshotForDiff(target) {
|
|
|
7576
7792
|
}
|
|
7577
7793
|
return await res.json();
|
|
7578
7794
|
}
|
|
7579
|
-
const raw = await
|
|
7795
|
+
const raw = await import_node_fs25.promises.readFile(target, "utf8");
|
|
7580
7796
|
return JSON.parse(raw);
|
|
7581
7797
|
}
|
|
7582
7798
|
function indexEntries(entries) {
|
|
@@ -7644,23 +7860,23 @@ function canonicalJson(value) {
|
|
|
7644
7860
|
|
|
7645
7861
|
// src/projects.ts
|
|
7646
7862
|
init_cjs_shims();
|
|
7647
|
-
var
|
|
7863
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7648
7864
|
function pathsForProject(project, baseDir) {
|
|
7649
7865
|
if (project === DEFAULT_PROJECT) {
|
|
7650
7866
|
return {
|
|
7651
|
-
snapshotPath:
|
|
7652
|
-
errorsPath:
|
|
7653
|
-
staleEventsPath:
|
|
7654
|
-
embeddingsCachePath:
|
|
7655
|
-
policyViolationsPath:
|
|
7867
|
+
snapshotPath: import_node_path42.default.join(baseDir, "graph.json"),
|
|
7868
|
+
errorsPath: import_node_path42.default.join(baseDir, "errors.ndjson"),
|
|
7869
|
+
staleEventsPath: import_node_path42.default.join(baseDir, "stale-events.ndjson"),
|
|
7870
|
+
embeddingsCachePath: import_node_path42.default.join(baseDir, "embeddings.json"),
|
|
7871
|
+
policyViolationsPath: import_node_path42.default.join(baseDir, "policy-violations.ndjson")
|
|
7656
7872
|
};
|
|
7657
7873
|
}
|
|
7658
7874
|
return {
|
|
7659
|
-
snapshotPath:
|
|
7660
|
-
errorsPath:
|
|
7661
|
-
staleEventsPath:
|
|
7662
|
-
embeddingsCachePath:
|
|
7663
|
-
policyViolationsPath:
|
|
7875
|
+
snapshotPath: import_node_path42.default.join(baseDir, `${project}.json`),
|
|
7876
|
+
errorsPath: import_node_path42.default.join(baseDir, `errors.${project}.ndjson`),
|
|
7877
|
+
staleEventsPath: import_node_path42.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
7878
|
+
embeddingsCachePath: import_node_path42.default.join(baseDir, `embeddings.${project}.json`),
|
|
7879
|
+
policyViolationsPath: import_node_path42.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
7664
7880
|
};
|
|
7665
7881
|
}
|
|
7666
7882
|
var Projects = class {
|
|
@@ -7696,28 +7912,28 @@ var Projects = class {
|
|
|
7696
7912
|
|
|
7697
7913
|
// src/registry.ts
|
|
7698
7914
|
init_cjs_shims();
|
|
7699
|
-
var
|
|
7915
|
+
var import_node_fs26 = require("fs");
|
|
7700
7916
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
7701
|
-
var
|
|
7702
|
-
var
|
|
7917
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7918
|
+
var import_types29 = require("@neat.is/types");
|
|
7703
7919
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
7704
7920
|
var LOCK_RETRY_MS = 50;
|
|
7705
7921
|
function neatHome() {
|
|
7706
7922
|
const override = process.env.NEAT_HOME;
|
|
7707
|
-
if (override && override.length > 0) return
|
|
7708
|
-
return
|
|
7923
|
+
if (override && override.length > 0) return import_node_path43.default.resolve(override);
|
|
7924
|
+
return import_node_path43.default.join(import_node_os3.default.homedir(), ".neat");
|
|
7709
7925
|
}
|
|
7710
7926
|
function registryPath() {
|
|
7711
|
-
return
|
|
7927
|
+
return import_node_path43.default.join(neatHome(), "projects.json");
|
|
7712
7928
|
}
|
|
7713
7929
|
function registryLockPath() {
|
|
7714
|
-
return
|
|
7930
|
+
return import_node_path43.default.join(neatHome(), "projects.json.lock");
|
|
7715
7931
|
}
|
|
7716
7932
|
function daemonPidPath() {
|
|
7717
|
-
return
|
|
7933
|
+
return import_node_path43.default.join(neatHome(), "neatd.pid");
|
|
7718
7934
|
}
|
|
7719
7935
|
function daemonsDir() {
|
|
7720
|
-
return
|
|
7936
|
+
return import_node_path43.default.join(neatHome(), "daemons");
|
|
7721
7937
|
}
|
|
7722
7938
|
function isFiniteInt(v) {
|
|
7723
7939
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -7750,7 +7966,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
7750
7966
|
const dir = daemonsDir();
|
|
7751
7967
|
let names;
|
|
7752
7968
|
try {
|
|
7753
|
-
names = await
|
|
7969
|
+
names = await import_node_fs26.promises.readdir(dir);
|
|
7754
7970
|
} catch (err) {
|
|
7755
7971
|
if (err.code === "ENOENT") return [];
|
|
7756
7972
|
throw err;
|
|
@@ -7758,10 +7974,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
7758
7974
|
const out = [];
|
|
7759
7975
|
for (const name of names) {
|
|
7760
7976
|
if (!name.endsWith(".json")) continue;
|
|
7761
|
-
const file =
|
|
7977
|
+
const file = import_node_path43.default.join(dir, name);
|
|
7762
7978
|
let raw;
|
|
7763
7979
|
try {
|
|
7764
|
-
raw = await
|
|
7980
|
+
raw = await import_node_fs26.promises.readFile(file, "utf8");
|
|
7765
7981
|
} catch {
|
|
7766
7982
|
continue;
|
|
7767
7983
|
}
|
|
@@ -7774,7 +7990,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
7774
7990
|
return out;
|
|
7775
7991
|
}
|
|
7776
7992
|
async function removeDaemonRecord(source) {
|
|
7777
|
-
await
|
|
7993
|
+
await import_node_fs26.promises.unlink(source).catch(() => {
|
|
7778
7994
|
});
|
|
7779
7995
|
}
|
|
7780
7996
|
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
@@ -7831,7 +8047,7 @@ function isPidAliveDefault(pid) {
|
|
|
7831
8047
|
}
|
|
7832
8048
|
async function readPidFile(file) {
|
|
7833
8049
|
try {
|
|
7834
|
-
const raw = await
|
|
8050
|
+
const raw = await import_node_fs26.promises.readFile(file, "utf8");
|
|
7835
8051
|
const pid = Number.parseInt(raw.trim(), 10);
|
|
7836
8052
|
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
7837
8053
|
} catch {
|
|
@@ -7879,32 +8095,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
7879
8095
|
}
|
|
7880
8096
|
}
|
|
7881
8097
|
async function normalizeProjectPath(input) {
|
|
7882
|
-
const resolved =
|
|
8098
|
+
const resolved = import_node_path43.default.resolve(input);
|
|
7883
8099
|
try {
|
|
7884
|
-
return await
|
|
8100
|
+
return await import_node_fs26.promises.realpath(resolved);
|
|
7885
8101
|
} catch {
|
|
7886
8102
|
return resolved;
|
|
7887
8103
|
}
|
|
7888
8104
|
}
|
|
7889
8105
|
async function writeAtomically(target, contents) {
|
|
7890
|
-
await
|
|
8106
|
+
await import_node_fs26.promises.mkdir(import_node_path43.default.dirname(target), { recursive: true });
|
|
7891
8107
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
7892
|
-
const fd = await
|
|
8108
|
+
const fd = await import_node_fs26.promises.open(tmp, "w");
|
|
7893
8109
|
try {
|
|
7894
8110
|
await fd.writeFile(contents, "utf8");
|
|
7895
8111
|
await fd.sync();
|
|
7896
8112
|
} finally {
|
|
7897
8113
|
await fd.close();
|
|
7898
8114
|
}
|
|
7899
|
-
await
|
|
8115
|
+
await import_node_fs26.promises.rename(tmp, target);
|
|
7900
8116
|
}
|
|
7901
8117
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
7902
8118
|
const deadline = Date.now() + timeoutMs;
|
|
7903
|
-
await
|
|
8119
|
+
await import_node_fs26.promises.mkdir(import_node_path43.default.dirname(lockPath), { recursive: true });
|
|
7904
8120
|
let probedHolder = false;
|
|
7905
8121
|
while (true) {
|
|
7906
8122
|
try {
|
|
7907
|
-
const fd = await
|
|
8123
|
+
const fd = await import_node_fs26.promises.open(lockPath, "wx");
|
|
7908
8124
|
try {
|
|
7909
8125
|
await fd.writeFile(`${process.pid}
|
|
7910
8126
|
`, "utf8");
|
|
@@ -7929,7 +8145,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
|
|
|
7929
8145
|
}
|
|
7930
8146
|
}
|
|
7931
8147
|
async function releaseLock(lockPath) {
|
|
7932
|
-
await
|
|
8148
|
+
await import_node_fs26.promises.unlink(lockPath).catch(() => {
|
|
7933
8149
|
});
|
|
7934
8150
|
}
|
|
7935
8151
|
async function withLock(fn) {
|
|
@@ -7945,7 +8161,7 @@ async function readRegistry() {
|
|
|
7945
8161
|
const file = registryPath();
|
|
7946
8162
|
let raw;
|
|
7947
8163
|
try {
|
|
7948
|
-
raw = await
|
|
8164
|
+
raw = await import_node_fs26.promises.readFile(file, "utf8");
|
|
7949
8165
|
} catch (err) {
|
|
7950
8166
|
if (err.code === "ENOENT") {
|
|
7951
8167
|
return { version: 1, projects: [] };
|
|
@@ -7953,10 +8169,10 @@ async function readRegistry() {
|
|
|
7953
8169
|
throw err;
|
|
7954
8170
|
}
|
|
7955
8171
|
const parsed = JSON.parse(raw);
|
|
7956
|
-
return
|
|
8172
|
+
return import_types29.RegistryFileSchema.parse(parsed);
|
|
7957
8173
|
}
|
|
7958
8174
|
async function writeRegistry(reg) {
|
|
7959
|
-
const validated =
|
|
8175
|
+
const validated = import_types29.RegistryFileSchema.parse(reg);
|
|
7960
8176
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
7961
8177
|
}
|
|
7962
8178
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -8041,7 +8257,7 @@ function pruneTtlMs() {
|
|
|
8041
8257
|
}
|
|
8042
8258
|
async function statPathStatus(p) {
|
|
8043
8259
|
try {
|
|
8044
|
-
const stat = await
|
|
8260
|
+
const stat = await import_node_fs26.promises.stat(p);
|
|
8045
8261
|
return stat.isDirectory() ? "present" : "unknown";
|
|
8046
8262
|
} catch (err) {
|
|
8047
8263
|
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
@@ -8288,11 +8504,11 @@ function registerRoutes(scope, ctx) {
|
|
|
8288
8504
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
8289
8505
|
const parsed = [];
|
|
8290
8506
|
for (const c of candidates) {
|
|
8291
|
-
const r =
|
|
8507
|
+
const r = import_types30.DivergenceTypeSchema.safeParse(c);
|
|
8292
8508
|
if (!r.success) {
|
|
8293
8509
|
return reply.code(400).send({
|
|
8294
8510
|
error: `unknown divergence type "${c}"`,
|
|
8295
|
-
allowed:
|
|
8511
|
+
allowed: import_types30.DivergenceTypeSchema.options
|
|
8296
8512
|
});
|
|
8297
8513
|
}
|
|
8298
8514
|
parsed.push(r.data);
|
|
@@ -8511,7 +8727,7 @@ function registerRoutes(scope, ctx) {
|
|
|
8511
8727
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
8512
8728
|
let violations = await log.readAll();
|
|
8513
8729
|
if (req.query.severity) {
|
|
8514
|
-
const sev =
|
|
8730
|
+
const sev = import_types30.PolicySeveritySchema.safeParse(req.query.severity);
|
|
8515
8731
|
if (!sev.success) {
|
|
8516
8732
|
return reply.code(400).send({
|
|
8517
8733
|
error: "invalid severity",
|
|
@@ -8550,7 +8766,7 @@ function registerRoutes(scope, ctx) {
|
|
|
8550
8766
|
scope.post("/policies/check", async (req, reply) => {
|
|
8551
8767
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
8552
8768
|
if (!proj) return;
|
|
8553
|
-
const parsed =
|
|
8769
|
+
const parsed = import_types30.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
8554
8770
|
if (!parsed.success) {
|
|
8555
8771
|
return reply.code(400).send({
|
|
8556
8772
|
error: "invalid /policies/check body",
|
|
@@ -8812,41 +9028,46 @@ init_otel();
|
|
|
8812
9028
|
|
|
8813
9029
|
// src/daemon.ts
|
|
8814
9030
|
init_cjs_shims();
|
|
8815
|
-
var
|
|
8816
|
-
var
|
|
9031
|
+
var import_node_fs28 = require("fs");
|
|
9032
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8817
9033
|
var import_node_module = require("module");
|
|
8818
9034
|
init_otel();
|
|
9035
|
+
|
|
9036
|
+
// src/connectors/index.ts
|
|
9037
|
+
init_cjs_shims();
|
|
9038
|
+
|
|
9039
|
+
// src/daemon.ts
|
|
8819
9040
|
init_auth();
|
|
8820
9041
|
|
|
8821
9042
|
// src/unrouted.ts
|
|
8822
9043
|
init_cjs_shims();
|
|
8823
|
-
var
|
|
8824
|
-
var
|
|
9044
|
+
var import_node_fs27 = require("fs");
|
|
9045
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8825
9046
|
|
|
8826
9047
|
// src/daemon.ts
|
|
8827
|
-
var
|
|
9048
|
+
var import_types31 = require("@neat.is/types");
|
|
8828
9049
|
function daemonJsonPath(scanPath) {
|
|
8829
|
-
return
|
|
9050
|
+
return import_node_path47.default.join(scanPath, "neat-out", "daemon.json");
|
|
8830
9051
|
}
|
|
8831
9052
|
function daemonsDiscoveryDir(home) {
|
|
8832
9053
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
8833
|
-
return
|
|
9054
|
+
return import_node_path47.default.join(base, "daemons");
|
|
8834
9055
|
}
|
|
8835
9056
|
function daemonDiscoveryPath(project, home) {
|
|
8836
|
-
return
|
|
9057
|
+
return import_node_path47.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
8837
9058
|
}
|
|
8838
9059
|
function sanitizeDiscoveryName(project) {
|
|
8839
9060
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
8840
9061
|
}
|
|
8841
9062
|
function neatHomeFromEnv() {
|
|
8842
9063
|
const env = process.env.NEAT_HOME;
|
|
8843
|
-
if (env && env.length > 0) return
|
|
9064
|
+
if (env && env.length > 0) return import_node_path47.default.resolve(env);
|
|
8844
9065
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
8845
|
-
return
|
|
9066
|
+
return import_node_path47.default.join(home, ".neat");
|
|
8846
9067
|
}
|
|
8847
9068
|
async function readDaemonRecord(scanPath) {
|
|
8848
9069
|
try {
|
|
8849
|
-
const raw = await
|
|
9070
|
+
const raw = await import_node_fs28.promises.readFile(daemonJsonPath(scanPath), "utf8");
|
|
8850
9071
|
const parsed = JSON.parse(raw);
|
|
8851
9072
|
if (typeof parsed.project === "string" && parsed.ports && typeof parsed.ports.rest === "number" && typeof parsed.ports.otlp === "number" && typeof parsed.ports.web === "number") {
|
|
8852
9073
|
return parsed;
|
|
@@ -8886,7 +9107,7 @@ async function clearDaemonRecord(record, home) {
|
|
|
8886
9107
|
} catch {
|
|
8887
9108
|
}
|
|
8888
9109
|
try {
|
|
8889
|
-
await
|
|
9110
|
+
await import_node_fs28.promises.unlink(daemonDiscoveryPath(record.project, home));
|
|
8890
9111
|
} catch {
|
|
8891
9112
|
}
|
|
8892
9113
|
}
|
|
@@ -8912,8 +9133,8 @@ init_otel_grpc();
|
|
|
8912
9133
|
|
|
8913
9134
|
// src/search.ts
|
|
8914
9135
|
init_cjs_shims();
|
|
8915
|
-
var
|
|
8916
|
-
var
|
|
9136
|
+
var import_node_fs29 = require("fs");
|
|
9137
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
8917
9138
|
var import_node_crypto3 = require("crypto");
|
|
8918
9139
|
var DEFAULT_LIMIT = 10;
|
|
8919
9140
|
var NOMIC_DIM = 768;
|
|
@@ -8960,6 +9181,18 @@ function embedText(node) {
|
|
|
8960
9181
|
if (opType) parts.push(`operationType=${opType}`);
|
|
8961
9182
|
break;
|
|
8962
9183
|
}
|
|
9184
|
+
case "GrpcMethodNode": {
|
|
9185
|
+
const rpcService = node.rpcService;
|
|
9186
|
+
const rpcMethod = node.rpcMethod;
|
|
9187
|
+
if (rpcService) parts.push(`rpcService=${rpcService}`);
|
|
9188
|
+
if (rpcMethod) parts.push(`rpcMethod=${rpcMethod}`);
|
|
9189
|
+
break;
|
|
9190
|
+
}
|
|
9191
|
+
case "WebSocketChannelNode": {
|
|
9192
|
+
const channel = node.channel;
|
|
9193
|
+
if (channel) parts.push(`channel=${channel}`);
|
|
9194
|
+
break;
|
|
9195
|
+
}
|
|
8963
9196
|
default:
|
|
8964
9197
|
break;
|
|
8965
9198
|
}
|
|
@@ -9055,7 +9288,7 @@ async function pickEmbedder() {
|
|
|
9055
9288
|
}
|
|
9056
9289
|
async function readCache(cachePath) {
|
|
9057
9290
|
try {
|
|
9058
|
-
const raw = await
|
|
9291
|
+
const raw = await import_node_fs29.promises.readFile(cachePath, "utf8");
|
|
9059
9292
|
const parsed = JSON.parse(raw);
|
|
9060
9293
|
if (parsed.version !== 1) return null;
|
|
9061
9294
|
return parsed;
|
|
@@ -9064,8 +9297,8 @@ async function readCache(cachePath) {
|
|
|
9064
9297
|
}
|
|
9065
9298
|
}
|
|
9066
9299
|
async function writeCache(cachePath, cache) {
|
|
9067
|
-
await
|
|
9068
|
-
await
|
|
9300
|
+
await import_node_fs29.promises.mkdir(import_node_path48.default.dirname(cachePath), { recursive: true });
|
|
9301
|
+
await import_node_fs29.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
9069
9302
|
}
|
|
9070
9303
|
var VectorIndex = class {
|
|
9071
9304
|
constructor(embedder, cachePath) {
|
|
@@ -9222,8 +9455,8 @@ var ALL_PHASES = [
|
|
|
9222
9455
|
];
|
|
9223
9456
|
function classifyChange(relPath) {
|
|
9224
9457
|
const phases = /* @__PURE__ */ new Set();
|
|
9225
|
-
const base =
|
|
9226
|
-
const segments = relPath.split(
|
|
9458
|
+
const base = import_node_path49.default.basename(relPath).toLowerCase();
|
|
9459
|
+
const segments = relPath.split(import_node_path49.default.sep).map((s) => s.toLowerCase());
|
|
9227
9460
|
if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
|
|
9228
9461
|
phases.add("services");
|
|
9229
9462
|
phases.add("aliases");
|
|
@@ -9344,16 +9577,16 @@ function countWatchableDirs(scanPath, limit) {
|
|
|
9344
9577
|
if (count >= limit) return;
|
|
9345
9578
|
let entries;
|
|
9346
9579
|
try {
|
|
9347
|
-
entries =
|
|
9580
|
+
entries = import_node_fs30.default.readdirSync(dir, { withFileTypes: true });
|
|
9348
9581
|
} catch {
|
|
9349
9582
|
return;
|
|
9350
9583
|
}
|
|
9351
9584
|
for (const e of entries) {
|
|
9352
9585
|
if (count >= limit) return;
|
|
9353
9586
|
if (!e.isDirectory()) continue;
|
|
9354
|
-
if (IGNORED_WATCH_PATHS.some((re) => re.test(
|
|
9587
|
+
if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path49.default.join(dir, e.name) + import_node_path49.default.sep))) continue;
|
|
9355
9588
|
count++;
|
|
9356
|
-
if (depth < 2) visit(
|
|
9589
|
+
if (depth < 2) visit(import_node_path49.default.join(dir, e.name), depth + 1);
|
|
9357
9590
|
}
|
|
9358
9591
|
};
|
|
9359
9592
|
visit(scanPath, 0);
|
|
@@ -9371,8 +9604,8 @@ async function startWatch(graph, opts) {
|
|
|
9371
9604
|
const projectName = opts.project ?? DEFAULT_PROJECT;
|
|
9372
9605
|
await loadGraphFromDisk(graph, opts.outPath);
|
|
9373
9606
|
const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
|
|
9374
|
-
const policyFilePath =
|
|
9375
|
-
const policyViolationsPath =
|
|
9607
|
+
const policyFilePath = import_node_path49.default.join(opts.scanPath, "policy.json");
|
|
9608
|
+
const policyViolationsPath = import_node_path49.default.join(import_node_path49.default.dirname(opts.outPath), "policy-violations.ndjson");
|
|
9376
9609
|
let policies = [];
|
|
9377
9610
|
try {
|
|
9378
9611
|
policies = await loadPolicyFile(policyFilePath);
|
|
@@ -9423,7 +9656,7 @@ async function startWatch(graph, opts) {
|
|
|
9423
9656
|
assertBindAuthority(host, auth.authToken);
|
|
9424
9657
|
const port = opts.port ?? 8080;
|
|
9425
9658
|
const otelPort = opts.otelPort ?? 4318;
|
|
9426
|
-
const cachePath = opts.embeddingsCachePath ??
|
|
9659
|
+
const cachePath = opts.embeddingsCachePath ?? import_node_path49.default.join(import_node_path49.default.dirname(opts.outPath), "embeddings.json");
|
|
9427
9660
|
let searchIndex;
|
|
9428
9661
|
try {
|
|
9429
9662
|
searchIndex = await buildSearchIndex(graph, { cachePath });
|
|
@@ -9441,7 +9674,7 @@ async function startWatch(graph, opts) {
|
|
|
9441
9674
|
// Paths are derived from the explicit options the watch caller passes
|
|
9442
9675
|
// — pathsForProject is only used to fill in the embeddings/snapshot
|
|
9443
9676
|
// fields so the registry shape is complete.
|
|
9444
|
-
...pathsForProject(projectName,
|
|
9677
|
+
...pathsForProject(projectName, import_node_path49.default.dirname(opts.outPath)),
|
|
9445
9678
|
snapshotPath: opts.outPath,
|
|
9446
9679
|
errorsPath: opts.errorsPath,
|
|
9447
9680
|
staleEventsPath: opts.staleEventsPath
|
|
@@ -9559,9 +9792,9 @@ async function startWatch(graph, opts) {
|
|
|
9559
9792
|
};
|
|
9560
9793
|
const onPath = (absPath) => {
|
|
9561
9794
|
if (shouldIgnore(absPath)) return;
|
|
9562
|
-
const rel =
|
|
9795
|
+
const rel = import_node_path49.default.relative(opts.scanPath, absPath);
|
|
9563
9796
|
if (!rel || rel.startsWith("..")) return;
|
|
9564
|
-
pendingPaths.add(rel.split(
|
|
9797
|
+
pendingPaths.add(rel.split(import_node_path49.default.sep).join("/"));
|
|
9565
9798
|
const phases = classifyChange(rel);
|
|
9566
9799
|
if (phases.size === 0) {
|
|
9567
9800
|
for (const p of ALL_PHASES) pending.add(p);
|
|
@@ -9617,8 +9850,8 @@ async function startWatch(graph, opts) {
|
|
|
9617
9850
|
|
|
9618
9851
|
// src/deploy/detect.ts
|
|
9619
9852
|
init_cjs_shims();
|
|
9620
|
-
var
|
|
9621
|
-
var
|
|
9853
|
+
var import_node_fs31 = require("fs");
|
|
9854
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9622
9855
|
var import_node_child_process2 = require("child_process");
|
|
9623
9856
|
var import_node_crypto4 = require("crypto");
|
|
9624
9857
|
function generateToken() {
|
|
@@ -9718,21 +9951,21 @@ async function runDeploy(opts = {}) {
|
|
|
9718
9951
|
const token = generateToken();
|
|
9719
9952
|
switch (substrate) {
|
|
9720
9953
|
case "docker-compose": {
|
|
9721
|
-
const artifactPath =
|
|
9954
|
+
const artifactPath = import_node_path50.default.join(cwd, "docker-compose.neat.yml");
|
|
9722
9955
|
const contents = emitDockerCompose(cwd);
|
|
9723
|
-
await
|
|
9956
|
+
await import_node_fs31.promises.writeFile(artifactPath, contents, "utf8");
|
|
9724
9957
|
return {
|
|
9725
9958
|
substrate,
|
|
9726
9959
|
artifactPath,
|
|
9727
9960
|
token,
|
|
9728
9961
|
contents,
|
|
9729
|
-
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${
|
|
9962
|
+
startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path50.default.basename(artifactPath)} up -d`
|
|
9730
9963
|
};
|
|
9731
9964
|
}
|
|
9732
9965
|
case "systemd": {
|
|
9733
|
-
const artifactPath =
|
|
9966
|
+
const artifactPath = import_node_path50.default.join(cwd, "neat.service");
|
|
9734
9967
|
const contents = emitSystemdUnit(cwd);
|
|
9735
|
-
await
|
|
9968
|
+
await import_node_fs31.promises.writeFile(artifactPath, contents, "utf8");
|
|
9736
9969
|
return {
|
|
9737
9970
|
substrate,
|
|
9738
9971
|
artifactPath,
|
|
@@ -9765,8 +9998,8 @@ init_cjs_shims();
|
|
|
9765
9998
|
|
|
9766
9999
|
// src/installers/javascript.ts
|
|
9767
10000
|
init_cjs_shims();
|
|
9768
|
-
var
|
|
9769
|
-
var
|
|
10001
|
+
var import_node_fs32 = require("fs");
|
|
10002
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
9770
10003
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
9771
10004
|
|
|
9772
10005
|
// src/installers/templates.ts
|
|
@@ -10191,6 +10424,9 @@ export async function register() {
|
|
|
10191
10424
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
10192
10425
|
await import('./instrumentation.node')
|
|
10193
10426
|
}
|
|
10427
|
+
if (process.env.NEXT_RUNTIME === 'edge') {
|
|
10428
|
+
await import('./instrumentation.edge')
|
|
10429
|
+
}
|
|
10194
10430
|
}
|
|
10195
10431
|
`;
|
|
10196
10432
|
var NEXT_INSTRUMENTATION_JS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
@@ -10198,6 +10434,9 @@ export async function register() {
|
|
|
10198
10434
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
10199
10435
|
await import('./instrumentation.node')
|
|
10200
10436
|
}
|
|
10437
|
+
if (process.env.NEXT_RUNTIME === 'edge') {
|
|
10438
|
+
await import('./instrumentation.edge')
|
|
10439
|
+
}
|
|
10201
10440
|
}
|
|
10202
10441
|
`;
|
|
10203
10442
|
var NEXT_INSTRUMENTATION_NODE_TS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
@@ -10233,6 +10472,18 @@ ${registrations.join("\n")}
|
|
|
10233
10472
|
`;
|
|
10234
10473
|
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
|
|
10235
10474
|
}
|
|
10475
|
+
var NEXT_INSTRUMENTATION_EDGE_HEADER = "// Generated by `neat init --apply` (ADR-126). Next.js edge-runtime instrumentation via @vercel/otel.";
|
|
10476
|
+
var NEXT_INSTRUMENTATION_EDGE_TS = `${NEXT_INSTRUMENTATION_EDGE_HEADER}
|
|
10477
|
+
import { registerOTel } from '@vercel/otel'
|
|
10478
|
+
|
|
10479
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
10480
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/v1/traces'
|
|
10481
|
+
${OTEL_OTLP_PROTOCOL_JS}
|
|
10482
|
+
${OTEL_OTLP_HEADERS_JS}
|
|
10483
|
+
|
|
10484
|
+
registerOTel({ serviceName: process.env.OTEL_SERVICE_NAME })
|
|
10485
|
+
`;
|
|
10486
|
+
var NEXT_INSTRUMENTATION_EDGE_JS = NEXT_INSTRUMENTATION_EDGE_TS;
|
|
10236
10487
|
var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074). OpenTelemetry SDK hook.";
|
|
10237
10488
|
var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
10238
10489
|
${OTEL_ESM_NODE_IMPORTS}
|
|
@@ -10333,6 +10584,7 @@ var SDK_PACKAGES = [
|
|
|
10333
10584
|
{ name: "@opentelemetry/sdk-node", version: "^0.57.0" },
|
|
10334
10585
|
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
|
|
10335
10586
|
];
|
|
10587
|
+
var NEXT_EDGE_PACKAGES = [{ name: "@vercel/otel", version: "^2.1.3" }];
|
|
10336
10588
|
function getMajor(versionRange) {
|
|
10337
10589
|
if (!versionRange) return 0;
|
|
10338
10590
|
const match = versionRange.match(/(\d+)/);
|
|
@@ -10362,15 +10614,15 @@ var OTEL_ENV = {
|
|
|
10362
10614
|
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
10363
10615
|
};
|
|
10364
10616
|
function serviceNodeName(pkg, serviceDir) {
|
|
10365
|
-
return pkg.name ??
|
|
10617
|
+
return pkg.name ?? import_node_path51.default.basename(serviceDir);
|
|
10366
10618
|
}
|
|
10367
10619
|
function projectToken(pkg, serviceDir, project) {
|
|
10368
10620
|
if (project && project.length > 0) return project;
|
|
10369
|
-
return pkg.name ??
|
|
10621
|
+
return pkg.name ?? import_node_path51.default.basename(serviceDir);
|
|
10370
10622
|
}
|
|
10371
10623
|
async function readJsonFile(p) {
|
|
10372
10624
|
try {
|
|
10373
|
-
const raw = await
|
|
10625
|
+
const raw = await import_node_fs32.promises.readFile(p, "utf8");
|
|
10374
10626
|
return JSON.parse(raw);
|
|
10375
10627
|
} catch {
|
|
10376
10628
|
return null;
|
|
@@ -10379,16 +10631,16 @@ async function readJsonFile(p) {
|
|
|
10379
10631
|
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
10380
10632
|
const deps = allDeps(pkg);
|
|
10381
10633
|
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
10382
|
-
const appJson = await readJsonFile(
|
|
10634
|
+
const appJson = await readJsonFile(import_node_path51.default.join(pkgRoot, "app.json"));
|
|
10383
10635
|
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
10384
10636
|
return "react-native";
|
|
10385
10637
|
}
|
|
10386
|
-
if (await exists3(
|
|
10638
|
+
if (await exists3(import_node_path51.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path51.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path51.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
10387
10639
|
return "browser-bundle";
|
|
10388
10640
|
}
|
|
10389
|
-
if (await exists3(
|
|
10390
|
-
if (await exists3(
|
|
10391
|
-
if (await exists3(
|
|
10641
|
+
if (await exists3(import_node_path51.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
|
|
10642
|
+
if (await exists3(import_node_path51.default.join(pkgRoot, "bun.lockb"))) return "bun";
|
|
10643
|
+
if (await exists3(import_node_path51.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path51.default.join(pkgRoot, "deno.lock"))) {
|
|
10392
10644
|
return "deno";
|
|
10393
10645
|
}
|
|
10394
10646
|
const engines = pkg.engines ?? {};
|
|
@@ -10397,7 +10649,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
|
|
|
10397
10649
|
}
|
|
10398
10650
|
async function readPackageJson2(serviceDir) {
|
|
10399
10651
|
try {
|
|
10400
|
-
const raw = await
|
|
10652
|
+
const raw = await import_node_fs32.promises.readFile(import_node_path51.default.join(serviceDir, "package.json"), "utf8");
|
|
10401
10653
|
return JSON.parse(raw);
|
|
10402
10654
|
} catch {
|
|
10403
10655
|
return null;
|
|
@@ -10405,7 +10657,7 @@ async function readPackageJson2(serviceDir) {
|
|
|
10405
10657
|
}
|
|
10406
10658
|
async function exists3(p) {
|
|
10407
10659
|
try {
|
|
10408
|
-
await
|
|
10660
|
+
await import_node_fs32.promises.stat(p);
|
|
10409
10661
|
return true;
|
|
10410
10662
|
} catch {
|
|
10411
10663
|
return false;
|
|
@@ -10413,7 +10665,7 @@ async function exists3(p) {
|
|
|
10413
10665
|
}
|
|
10414
10666
|
async function readFileMaybe(p) {
|
|
10415
10667
|
try {
|
|
10416
|
-
return await
|
|
10668
|
+
return await import_node_fs32.promises.readFile(p, "utf8");
|
|
10417
10669
|
} catch {
|
|
10418
10670
|
return null;
|
|
10419
10671
|
}
|
|
@@ -10441,7 +10693,7 @@ function needsVersionUpgrade(installed, expected) {
|
|
|
10441
10693
|
var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
|
|
10442
10694
|
async function findNextConfig(serviceDir) {
|
|
10443
10695
|
for (const name of NEXT_CONFIG_CANDIDATES) {
|
|
10444
|
-
const candidate =
|
|
10696
|
+
const candidate = import_node_path51.default.join(serviceDir, name);
|
|
10445
10697
|
if (await exists3(candidate)) return candidate;
|
|
10446
10698
|
}
|
|
10447
10699
|
return null;
|
|
@@ -10510,7 +10762,7 @@ function hasRemixDependency(pkg) {
|
|
|
10510
10762
|
}
|
|
10511
10763
|
async function findRemixEntry(serviceDir) {
|
|
10512
10764
|
for (const rel of REMIX_ENTRY_CANDIDATES) {
|
|
10513
|
-
const candidate =
|
|
10765
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
10514
10766
|
if (await exists3(candidate)) return candidate;
|
|
10515
10767
|
}
|
|
10516
10768
|
return null;
|
|
@@ -10522,14 +10774,14 @@ function hasSvelteKitDependency(pkg) {
|
|
|
10522
10774
|
}
|
|
10523
10775
|
async function findSvelteKitHooks(serviceDir) {
|
|
10524
10776
|
for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
|
|
10525
|
-
const candidate =
|
|
10777
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
10526
10778
|
if (await exists3(candidate)) return candidate;
|
|
10527
10779
|
}
|
|
10528
10780
|
return null;
|
|
10529
10781
|
}
|
|
10530
10782
|
async function findSvelteKitConfig(serviceDir) {
|
|
10531
10783
|
for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
|
|
10532
|
-
const candidate =
|
|
10784
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
10533
10785
|
if (await exists3(candidate)) return candidate;
|
|
10534
10786
|
}
|
|
10535
10787
|
return null;
|
|
@@ -10540,7 +10792,7 @@ function hasNuxtDependency(pkg) {
|
|
|
10540
10792
|
}
|
|
10541
10793
|
async function findNuxtConfig(serviceDir) {
|
|
10542
10794
|
for (const name of NUXT_CONFIG_CANDIDATES) {
|
|
10543
|
-
const candidate =
|
|
10795
|
+
const candidate = import_node_path51.default.join(serviceDir, name);
|
|
10544
10796
|
if (await exists3(candidate)) return candidate;
|
|
10545
10797
|
}
|
|
10546
10798
|
return null;
|
|
@@ -10551,7 +10803,7 @@ function hasAstroDependency(pkg) {
|
|
|
10551
10803
|
}
|
|
10552
10804
|
async function findAstroConfig(serviceDir) {
|
|
10553
10805
|
for (const name of ASTRO_CONFIG_CANDIDATES) {
|
|
10554
|
-
const candidate =
|
|
10806
|
+
const candidate = import_node_path51.default.join(serviceDir, name);
|
|
10555
10807
|
if (await exists3(candidate)) return candidate;
|
|
10556
10808
|
}
|
|
10557
10809
|
return null;
|
|
@@ -10565,7 +10817,7 @@ function parseNextMajor(range) {
|
|
|
10565
10817
|
return Number.isFinite(n) ? n : null;
|
|
10566
10818
|
}
|
|
10567
10819
|
async function isTypeScriptProject(serviceDir) {
|
|
10568
|
-
return exists3(
|
|
10820
|
+
return exists3(import_node_path51.default.join(serviceDir, "tsconfig.json"));
|
|
10569
10821
|
}
|
|
10570
10822
|
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
10571
10823
|
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
@@ -10614,7 +10866,7 @@ function entryFromScript(script) {
|
|
|
10614
10866
|
}
|
|
10615
10867
|
async function resolveEntry(serviceDir, pkg) {
|
|
10616
10868
|
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
10617
|
-
const candidate =
|
|
10869
|
+
const candidate = import_node_path51.default.resolve(serviceDir, pkg.main);
|
|
10618
10870
|
if (await exists3(candidate)) return candidate;
|
|
10619
10871
|
}
|
|
10620
10872
|
if (pkg.bin) {
|
|
@@ -10628,40 +10880,40 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
10628
10880
|
if (typeof first === "string") binEntry = first;
|
|
10629
10881
|
}
|
|
10630
10882
|
if (binEntry) {
|
|
10631
|
-
const candidate =
|
|
10883
|
+
const candidate = import_node_path51.default.resolve(serviceDir, binEntry);
|
|
10632
10884
|
if (await exists3(candidate)) return candidate;
|
|
10633
10885
|
}
|
|
10634
10886
|
}
|
|
10635
10887
|
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
10636
10888
|
if (startEntry) {
|
|
10637
|
-
const candidate =
|
|
10889
|
+
const candidate = import_node_path51.default.resolve(serviceDir, startEntry);
|
|
10638
10890
|
if (await exists3(candidate)) return candidate;
|
|
10639
10891
|
}
|
|
10640
10892
|
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
10641
10893
|
if (devEntry) {
|
|
10642
|
-
const candidate =
|
|
10894
|
+
const candidate = import_node_path51.default.resolve(serviceDir, devEntry);
|
|
10643
10895
|
if (await exists3(candidate)) return candidate;
|
|
10644
10896
|
}
|
|
10645
10897
|
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
10646
|
-
const candidate =
|
|
10898
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
10647
10899
|
if (await exists3(candidate)) return candidate;
|
|
10648
10900
|
}
|
|
10649
10901
|
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
10650
|
-
const candidate =
|
|
10902
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
10651
10903
|
if (await exists3(candidate)) return candidate;
|
|
10652
10904
|
}
|
|
10653
10905
|
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
10654
|
-
const candidate =
|
|
10906
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
10655
10907
|
if (await exists3(candidate)) return candidate;
|
|
10656
10908
|
}
|
|
10657
10909
|
for (const name of INDEX_CANDIDATES) {
|
|
10658
|
-
const candidate =
|
|
10910
|
+
const candidate = import_node_path51.default.join(serviceDir, name);
|
|
10659
10911
|
if (await exists3(candidate)) return candidate;
|
|
10660
10912
|
}
|
|
10661
10913
|
return null;
|
|
10662
10914
|
}
|
|
10663
10915
|
function dispatchEntry(entryFile, pkg) {
|
|
10664
|
-
const ext =
|
|
10916
|
+
const ext = import_node_path51.default.extname(entryFile).toLowerCase();
|
|
10665
10917
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
10666
10918
|
if (ext === ".mjs") return "esm";
|
|
10667
10919
|
if (ext === ".cjs") return "cjs";
|
|
@@ -10678,9 +10930,9 @@ function otelInitContents(flavor) {
|
|
|
10678
10930
|
return OTEL_INIT_CJS;
|
|
10679
10931
|
}
|
|
10680
10932
|
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
10681
|
-
let rel =
|
|
10933
|
+
let rel = import_node_path51.default.relative(import_node_path51.default.dirname(entryFile), otelInitFile);
|
|
10682
10934
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
10683
|
-
rel = rel.split(
|
|
10935
|
+
rel = rel.split(import_node_path51.default.sep).join("/");
|
|
10684
10936
|
if (flavor === "cjs") return `require('${rel}')`;
|
|
10685
10937
|
if (flavor === "esm") return `import '${rel}'`;
|
|
10686
10938
|
const tsRel = rel.replace(/\.ts$/, "");
|
|
@@ -10693,23 +10945,27 @@ function lineIsOtelInjection(line) {
|
|
|
10693
10945
|
}
|
|
10694
10946
|
async function detectsSrcLayout(serviceDir) {
|
|
10695
10947
|
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
10696
|
-
exists3(
|
|
10697
|
-
exists3(
|
|
10698
|
-
exists3(
|
|
10699
|
-
exists3(
|
|
10948
|
+
exists3(import_node_path51.default.join(serviceDir, "src", "app")),
|
|
10949
|
+
exists3(import_node_path51.default.join(serviceDir, "src", "pages")),
|
|
10950
|
+
exists3(import_node_path51.default.join(serviceDir, "app")),
|
|
10951
|
+
exists3(import_node_path51.default.join(serviceDir, "pages"))
|
|
10700
10952
|
]);
|
|
10701
10953
|
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
10702
10954
|
}
|
|
10703
10955
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
10704
10956
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10705
10957
|
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
10706
|
-
const baseDir = srcLayout ?
|
|
10707
|
-
const instrumentationFile =
|
|
10708
|
-
const instrumentationNodeFile =
|
|
10958
|
+
const baseDir = srcLayout ? import_node_path51.default.join(serviceDir, "src") : serviceDir;
|
|
10959
|
+
const instrumentationFile = import_node_path51.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
10960
|
+
const instrumentationNodeFile = import_node_path51.default.join(
|
|
10709
10961
|
baseDir,
|
|
10710
10962
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
10711
10963
|
);
|
|
10712
|
-
const
|
|
10964
|
+
const instrumentationEdgeFile = import_node_path51.default.join(
|
|
10965
|
+
baseDir,
|
|
10966
|
+
useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
|
|
10967
|
+
);
|
|
10968
|
+
const envNeatFile = import_node_path51.default.join(baseDir, ".env.neat");
|
|
10713
10969
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
10714
10970
|
const dependencyEdits = [];
|
|
10715
10971
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -10721,6 +10977,15 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
10721
10977
|
}
|
|
10722
10978
|
dependencyEdits.push({ file: manifestPath, kind: "add", name: sdk.name, version: sdk.version });
|
|
10723
10979
|
}
|
|
10980
|
+
for (const sdk of NEXT_EDGE_PACKAGES) {
|
|
10981
|
+
if (sdk.name in existingDeps) {
|
|
10982
|
+
if (needsVersionUpgrade(existingDeps[sdk.name], sdk.version)) {
|
|
10983
|
+
dependencyEdits.push({ file: manifestPath, kind: "upgrade", name: sdk.name, version: sdk.version, fromVersion: existingDeps[sdk.name] });
|
|
10984
|
+
}
|
|
10985
|
+
continue;
|
|
10986
|
+
}
|
|
10987
|
+
dependencyEdits.push({ file: manifestPath, kind: "add", name: sdk.name, version: sdk.version });
|
|
10988
|
+
}
|
|
10724
10989
|
const nonBundled = detectNonBundledInstrumentations(pkg);
|
|
10725
10990
|
for (const inst of nonBundled) {
|
|
10726
10991
|
if (inst.pkg in existingDeps) {
|
|
@@ -10754,6 +11019,17 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
10754
11019
|
skipIfExists: true
|
|
10755
11020
|
});
|
|
10756
11021
|
}
|
|
11022
|
+
if (!await exists3(instrumentationEdgeFile)) {
|
|
11023
|
+
generatedFiles.push({
|
|
11024
|
+
file: instrumentationEdgeFile,
|
|
11025
|
+
contents: renderFrameworkOtelInit(
|
|
11026
|
+
useTs ? NEXT_INSTRUMENTATION_EDGE_TS : NEXT_INSTRUMENTATION_EDGE_JS,
|
|
11027
|
+
svcName,
|
|
11028
|
+
projectName
|
|
11029
|
+
),
|
|
11030
|
+
skipIfExists: true
|
|
11031
|
+
});
|
|
11032
|
+
}
|
|
10757
11033
|
if (!await exists3(envNeatFile)) {
|
|
10758
11034
|
generatedFiles.push({
|
|
10759
11035
|
file: envNeatFile,
|
|
@@ -10766,7 +11042,7 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
10766
11042
|
const nextMajor = parseNextMajor(nextRange);
|
|
10767
11043
|
if (nextMajor !== null && nextMajor < 15) {
|
|
10768
11044
|
try {
|
|
10769
|
-
const raw = await
|
|
11045
|
+
const raw = await import_node_fs32.promises.readFile(nextConfigPath, "utf8");
|
|
10770
11046
|
if (!raw.includes("instrumentationHook")) {
|
|
10771
11047
|
nextConfigEdit = {
|
|
10772
11048
|
file: nextConfigPath,
|
|
@@ -10814,7 +11090,7 @@ function buildDependencyEdits(pkg, manifestPath) {
|
|
|
10814
11090
|
return edits;
|
|
10815
11091
|
}
|
|
10816
11092
|
async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
10817
|
-
const envNeatFile =
|
|
11093
|
+
const envNeatFile = import_node_path51.default.join(serviceDir, ".env.neat");
|
|
10818
11094
|
if (!await exists3(envNeatFile)) {
|
|
10819
11095
|
generatedFiles.push({
|
|
10820
11096
|
file: envNeatFile,
|
|
@@ -10849,7 +11125,7 @@ function fileImportsOtelHook(raw, specifiers) {
|
|
|
10849
11125
|
}
|
|
10850
11126
|
async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
10851
11127
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10852
|
-
const otelServerFile =
|
|
11128
|
+
const otelServerFile = import_node_path51.default.join(
|
|
10853
11129
|
serviceDir,
|
|
10854
11130
|
useTs ? "app/otel.server.ts" : "app/otel.server.js"
|
|
10855
11131
|
);
|
|
@@ -10870,7 +11146,7 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
10870
11146
|
await queueEnvNeat(serviceDir, pkg, project, generatedFiles);
|
|
10871
11147
|
const entrypointEdits = [];
|
|
10872
11148
|
try {
|
|
10873
|
-
const raw = await
|
|
11149
|
+
const raw = await import_node_fs32.promises.readFile(entryFile, "utf8");
|
|
10874
11150
|
if (!fileImportsOtelHook(raw, ["./otel.server"])) {
|
|
10875
11151
|
const lines = raw.split(/\r?\n/);
|
|
10876
11152
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
@@ -10906,11 +11182,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
10906
11182
|
}
|
|
10907
11183
|
async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
|
|
10908
11184
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10909
|
-
const otelInitFile =
|
|
11185
|
+
const otelInitFile = import_node_path51.default.join(
|
|
10910
11186
|
serviceDir,
|
|
10911
11187
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
10912
11188
|
);
|
|
10913
|
-
const resolvedHooksFile = hooksFile ??
|
|
11189
|
+
const resolvedHooksFile = hooksFile ?? import_node_path51.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
|
|
10914
11190
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
10915
11191
|
const generatedFiles = [];
|
|
10916
11192
|
const entrypointEdits = [];
|
|
@@ -10935,7 +11211,7 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
10935
11211
|
});
|
|
10936
11212
|
} else {
|
|
10937
11213
|
try {
|
|
10938
|
-
const raw = await
|
|
11214
|
+
const raw = await import_node_fs32.promises.readFile(hooksFile, "utf8");
|
|
10939
11215
|
if (!fileImportsOtelHook(raw, ["./otel-init"])) {
|
|
10940
11216
|
const lines = raw.split(/\r?\n/);
|
|
10941
11217
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
@@ -10972,11 +11248,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
10972
11248
|
}
|
|
10973
11249
|
async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
10974
11250
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
10975
|
-
const otelPluginFile =
|
|
11251
|
+
const otelPluginFile = import_node_path51.default.join(
|
|
10976
11252
|
serviceDir,
|
|
10977
11253
|
useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
|
|
10978
11254
|
);
|
|
10979
|
-
const otelInitFile =
|
|
11255
|
+
const otelInitFile = import_node_path51.default.join(
|
|
10980
11256
|
serviceDir,
|
|
10981
11257
|
useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
|
|
10982
11258
|
);
|
|
@@ -11027,19 +11303,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
11027
11303
|
var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
|
|
11028
11304
|
async function findAstroMiddleware(serviceDir) {
|
|
11029
11305
|
for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
|
|
11030
|
-
const candidate =
|
|
11306
|
+
const candidate = import_node_path51.default.join(serviceDir, rel);
|
|
11031
11307
|
if (await exists3(candidate)) return candidate;
|
|
11032
11308
|
}
|
|
11033
11309
|
return null;
|
|
11034
11310
|
}
|
|
11035
11311
|
async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
11036
11312
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
11037
|
-
const otelInitFile =
|
|
11313
|
+
const otelInitFile = import_node_path51.default.join(
|
|
11038
11314
|
serviceDir,
|
|
11039
11315
|
useTs ? "src/otel-init.ts" : "src/otel-init.js"
|
|
11040
11316
|
);
|
|
11041
11317
|
const existingMiddleware = await findAstroMiddleware(serviceDir);
|
|
11042
|
-
const middlewareFile = existingMiddleware ??
|
|
11318
|
+
const middlewareFile = existingMiddleware ?? import_node_path51.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
|
|
11043
11319
|
const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
|
|
11044
11320
|
const generatedFiles = [];
|
|
11045
11321
|
const entrypointEdits = [];
|
|
@@ -11064,7 +11340,7 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
|
11064
11340
|
});
|
|
11065
11341
|
} else {
|
|
11066
11342
|
try {
|
|
11067
|
-
const raw = await
|
|
11343
|
+
const raw = await import_node_fs32.promises.readFile(existingMiddleware, "utf8");
|
|
11068
11344
|
if (!fileImportsOtelHook(raw, ["./otel-init"])) {
|
|
11069
11345
|
const lines = raw.split(/\r?\n/);
|
|
11070
11346
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
@@ -11135,7 +11411,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
|
|
|
11135
11411
|
}
|
|
11136
11412
|
async function plan(serviceDir, opts) {
|
|
11137
11413
|
const pkg = await readPackageJson2(serviceDir);
|
|
11138
|
-
const manifestPath =
|
|
11414
|
+
const manifestPath = import_node_path51.default.join(serviceDir, "package.json");
|
|
11139
11415
|
const project = opts?.project;
|
|
11140
11416
|
const empty = {
|
|
11141
11417
|
language: "javascript",
|
|
@@ -11170,8 +11446,8 @@ async function plan(serviceDir, opts) {
|
|
|
11170
11446
|
return { ...empty, libOnly: true };
|
|
11171
11447
|
}
|
|
11172
11448
|
const flavor = dispatchEntry(entryFile, pkg);
|
|
11173
|
-
const otelInitFile =
|
|
11174
|
-
const envNeatFile =
|
|
11449
|
+
const otelInitFile = import_node_path51.default.join(import_node_path51.default.dirname(entryFile), otelInitFilename(flavor));
|
|
11450
|
+
const envNeatFile = import_node_path51.default.join(serviceDir, ".env.neat");
|
|
11175
11451
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
11176
11452
|
const dependencyEdits = [];
|
|
11177
11453
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -11195,7 +11471,7 @@ async function plan(serviceDir, opts) {
|
|
|
11195
11471
|
}
|
|
11196
11472
|
const entrypointEdits = [];
|
|
11197
11473
|
try {
|
|
11198
|
-
const raw = await
|
|
11474
|
+
const raw = await import_node_fs32.promises.readFile(entryFile, "utf8");
|
|
11199
11475
|
const lines = raw.split(/\r?\n/);
|
|
11200
11476
|
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
11201
11477
|
if (!lineIsOtelInjection(firstReal)) {
|
|
@@ -11240,14 +11516,14 @@ async function plan(serviceDir, opts) {
|
|
|
11240
11516
|
};
|
|
11241
11517
|
}
|
|
11242
11518
|
function isAllowedWritePath(serviceDir, target) {
|
|
11243
|
-
const rel =
|
|
11519
|
+
const rel = import_node_path51.default.relative(serviceDir, target);
|
|
11244
11520
|
if (rel.startsWith("..")) return false;
|
|
11245
|
-
const base =
|
|
11521
|
+
const base = import_node_path51.default.basename(target);
|
|
11246
11522
|
if (base === "package.json") return true;
|
|
11247
11523
|
if (base === ".env.neat") return true;
|
|
11248
11524
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
11249
|
-
const relPosix = rel.split(
|
|
11250
|
-
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
11525
|
+
const relPosix = rel.split(import_node_path51.default.sep).join("/");
|
|
11526
|
+
if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
11251
11527
|
if (relPosix === base) return true;
|
|
11252
11528
|
if (relPosix === `src/${base}`) return true;
|
|
11253
11529
|
return false;
|
|
@@ -11263,10 +11539,10 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
11263
11539
|
return false;
|
|
11264
11540
|
}
|
|
11265
11541
|
async function writeAtomic(file, contents) {
|
|
11266
|
-
await
|
|
11542
|
+
await import_node_fs32.promises.mkdir(import_node_path51.default.dirname(file), { recursive: true });
|
|
11267
11543
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
11268
|
-
await
|
|
11269
|
-
await
|
|
11544
|
+
await import_node_fs32.promises.writeFile(tmp, contents, "utf8");
|
|
11545
|
+
await import_node_fs32.promises.rename(tmp, file);
|
|
11270
11546
|
}
|
|
11271
11547
|
async function apply(installPlan) {
|
|
11272
11548
|
const { serviceDir } = installPlan;
|
|
@@ -11342,7 +11618,7 @@ async function apply(installPlan) {
|
|
|
11342
11618
|
for (const target of allTargets) {
|
|
11343
11619
|
if (await exists3(target)) {
|
|
11344
11620
|
try {
|
|
11345
|
-
originals.set(target, await
|
|
11621
|
+
originals.set(target, await import_node_fs32.promises.readFile(target, "utf8"));
|
|
11346
11622
|
} catch {
|
|
11347
11623
|
}
|
|
11348
11624
|
}
|
|
@@ -11425,14 +11701,14 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
11425
11701
|
const removed = [];
|
|
11426
11702
|
for (const [file, raw] of originals.entries()) {
|
|
11427
11703
|
try {
|
|
11428
|
-
await
|
|
11704
|
+
await import_node_fs32.promises.writeFile(file, raw, "utf8");
|
|
11429
11705
|
restored.push(file);
|
|
11430
11706
|
} catch {
|
|
11431
11707
|
}
|
|
11432
11708
|
}
|
|
11433
11709
|
for (const file of createdFiles) {
|
|
11434
11710
|
try {
|
|
11435
|
-
await
|
|
11711
|
+
await import_node_fs32.promises.unlink(file);
|
|
11436
11712
|
removed.push(file);
|
|
11437
11713
|
} catch {
|
|
11438
11714
|
}
|
|
@@ -11447,8 +11723,8 @@ async function rollback(installPlan, originals, createdFiles) {
|
|
|
11447
11723
|
...removed.map((f) => `removed: ${f}`),
|
|
11448
11724
|
""
|
|
11449
11725
|
];
|
|
11450
|
-
const rollbackPath =
|
|
11451
|
-
await
|
|
11726
|
+
const rollbackPath = import_node_path51.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
11727
|
+
await import_node_fs32.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
11452
11728
|
}
|
|
11453
11729
|
function injectInstrumentationHook(raw) {
|
|
11454
11730
|
if (raw.includes("instrumentationHook")) return raw;
|
|
@@ -11477,8 +11753,8 @@ var javascriptInstaller = {
|
|
|
11477
11753
|
|
|
11478
11754
|
// src/installers/python.ts
|
|
11479
11755
|
init_cjs_shims();
|
|
11480
|
-
var
|
|
11481
|
-
var
|
|
11756
|
+
var import_node_fs33 = require("fs");
|
|
11757
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
11482
11758
|
var SDK_PACKAGES2 = [
|
|
11483
11759
|
{ name: "opentelemetry-distro", version: ">=0.49b0" },
|
|
11484
11760
|
{ name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
|
|
@@ -11490,7 +11766,7 @@ var OTEL_ENV2 = {
|
|
|
11490
11766
|
};
|
|
11491
11767
|
async function exists4(p) {
|
|
11492
11768
|
try {
|
|
11493
|
-
await
|
|
11769
|
+
await import_node_fs33.promises.stat(p);
|
|
11494
11770
|
return true;
|
|
11495
11771
|
} catch {
|
|
11496
11772
|
return false;
|
|
@@ -11499,7 +11775,7 @@ async function exists4(p) {
|
|
|
11499
11775
|
async function detect2(serviceDir) {
|
|
11500
11776
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
11501
11777
|
for (const m of markers) {
|
|
11502
|
-
if (await exists4(
|
|
11778
|
+
if (await exists4(import_node_path52.default.join(serviceDir, m))) return true;
|
|
11503
11779
|
}
|
|
11504
11780
|
return false;
|
|
11505
11781
|
}
|
|
@@ -11509,9 +11785,9 @@ function reqPackageName(line) {
|
|
|
11509
11785
|
return head.replace(/[<>=!~].*$/, "").toLowerCase();
|
|
11510
11786
|
}
|
|
11511
11787
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
11512
|
-
const file =
|
|
11788
|
+
const file = import_node_path52.default.join(serviceDir, "requirements.txt");
|
|
11513
11789
|
if (!await exists4(file)) return null;
|
|
11514
|
-
const raw = await
|
|
11790
|
+
const raw = await import_node_fs33.promises.readFile(file, "utf8");
|
|
11515
11791
|
const presentNames = new Set(
|
|
11516
11792
|
raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
|
|
11517
11793
|
);
|
|
@@ -11519,9 +11795,9 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
11519
11795
|
return { manifest: file, missing: [...missing] };
|
|
11520
11796
|
}
|
|
11521
11797
|
async function planProcfileEdits(serviceDir) {
|
|
11522
|
-
const procfile =
|
|
11798
|
+
const procfile = import_node_path52.default.join(serviceDir, "Procfile");
|
|
11523
11799
|
if (!await exists4(procfile)) return [];
|
|
11524
|
-
const raw = await
|
|
11800
|
+
const raw = await import_node_fs33.promises.readFile(procfile, "utf8");
|
|
11525
11801
|
const edits = [];
|
|
11526
11802
|
for (const line of raw.split(/\r?\n/)) {
|
|
11527
11803
|
if (line.length === 0) continue;
|
|
@@ -11573,8 +11849,8 @@ async function applyRequirementsTxt(manifest, edits, original) {
|
|
|
11573
11849
|
const next = `${original}${trailing}${newlines.join("\n")}
|
|
11574
11850
|
`;
|
|
11575
11851
|
const tmp = `${manifest}.${process.pid}.${Date.now()}.tmp`;
|
|
11576
|
-
await
|
|
11577
|
-
await
|
|
11852
|
+
await import_node_fs33.promises.writeFile(tmp, next, "utf8");
|
|
11853
|
+
await import_node_fs33.promises.rename(tmp, manifest);
|
|
11578
11854
|
}
|
|
11579
11855
|
async function applyProcfile(procfile, edits, original) {
|
|
11580
11856
|
let next = original;
|
|
@@ -11583,8 +11859,8 @@ async function applyProcfile(procfile, edits, original) {
|
|
|
11583
11859
|
next = next.replace(e.before, e.after);
|
|
11584
11860
|
}
|
|
11585
11861
|
const tmp = `${procfile}.${process.pid}.${Date.now()}.tmp`;
|
|
11586
|
-
await
|
|
11587
|
-
await
|
|
11862
|
+
await import_node_fs33.promises.writeFile(tmp, next, "utf8");
|
|
11863
|
+
await import_node_fs33.promises.rename(tmp, procfile);
|
|
11588
11864
|
}
|
|
11589
11865
|
async function apply2(installPlan) {
|
|
11590
11866
|
const { serviceDir } = installPlan;
|
|
@@ -11597,7 +11873,7 @@ async function apply2(installPlan) {
|
|
|
11597
11873
|
const originals = /* @__PURE__ */ new Map();
|
|
11598
11874
|
for (const file of touched) {
|
|
11599
11875
|
try {
|
|
11600
|
-
originals.set(file, await
|
|
11876
|
+
originals.set(file, await import_node_fs33.promises.readFile(file, "utf8"));
|
|
11601
11877
|
} catch {
|
|
11602
11878
|
}
|
|
11603
11879
|
}
|
|
@@ -11608,7 +11884,7 @@ async function apply2(installPlan) {
|
|
|
11608
11884
|
if (raw === void 0) {
|
|
11609
11885
|
throw new Error(`python installer: cannot read ${file} during apply`);
|
|
11610
11886
|
}
|
|
11611
|
-
const base =
|
|
11887
|
+
const base = import_node_path52.default.basename(file);
|
|
11612
11888
|
if (base === "requirements.txt") {
|
|
11613
11889
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
11614
11890
|
if (edits.length > 0) {
|
|
@@ -11633,7 +11909,7 @@ async function rollback2(installPlan, originals) {
|
|
|
11633
11909
|
const restored = [];
|
|
11634
11910
|
for (const [file, raw] of originals.entries()) {
|
|
11635
11911
|
try {
|
|
11636
|
-
await
|
|
11912
|
+
await import_node_fs33.promises.writeFile(file, raw, "utf8");
|
|
11637
11913
|
restored.push(file);
|
|
11638
11914
|
} catch {
|
|
11639
11915
|
}
|
|
@@ -11647,8 +11923,8 @@ async function rollback2(installPlan, originals) {
|
|
|
11647
11923
|
...restored.map((f) => `restored: ${f}`),
|
|
11648
11924
|
""
|
|
11649
11925
|
];
|
|
11650
|
-
const rollbackPath =
|
|
11651
|
-
await
|
|
11926
|
+
const rollbackPath = import_node_path52.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
11927
|
+
await import_node_fs33.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
|
|
11652
11928
|
}
|
|
11653
11929
|
var pythonInstaller = {
|
|
11654
11930
|
name: "python",
|
|
@@ -11771,10 +12047,10 @@ function renderPatch(sections) {
|
|
|
11771
12047
|
|
|
11772
12048
|
// src/orchestrator.ts
|
|
11773
12049
|
init_cjs_shims();
|
|
11774
|
-
var
|
|
12050
|
+
var import_node_fs34 = require("fs");
|
|
11775
12051
|
var import_node_http = __toESM(require("http"), 1);
|
|
11776
12052
|
var import_node_net = __toESM(require("net"), 1);
|
|
11777
|
-
var
|
|
12053
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
11778
12054
|
var import_node_child_process3 = require("child_process");
|
|
11779
12055
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
11780
12056
|
async function extractAndPersist(opts) {
|
|
@@ -11783,7 +12059,7 @@ async function extractAndPersist(opts) {
|
|
|
11783
12059
|
const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
|
|
11784
12060
|
resetGraph(graphKey);
|
|
11785
12061
|
const graph = getGraph(graphKey);
|
|
11786
|
-
const projectPaths = pathsForProject(graphKey,
|
|
12062
|
+
const projectPaths = pathsForProject(graphKey, import_node_path53.default.join(opts.scanPath, "neat-out"));
|
|
11787
12063
|
const extraction = await extractFromDirectory(graph, opts.scanPath, {
|
|
11788
12064
|
errorsPath: projectPaths.errorsPath
|
|
11789
12065
|
});
|
|
@@ -11835,7 +12111,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11835
12111
|
libOnly++;
|
|
11836
12112
|
const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
|
|
11837
12113
|
if (appDeps.length > 0) {
|
|
11838
|
-
const svcName =
|
|
12114
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11839
12115
|
const list = appDeps.join(", ");
|
|
11840
12116
|
console.warn(
|
|
11841
12117
|
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
@@ -11848,7 +12124,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11848
12124
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
11849
12125
|
} else if (outcome.outcome === "react-native") {
|
|
11850
12126
|
reactNative++;
|
|
11851
|
-
const svcName =
|
|
12127
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11852
12128
|
console.log(
|
|
11853
12129
|
`neat: ${svc.dir} detected as React Native / Expo
|
|
11854
12130
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11859,7 +12135,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11859
12135
|
);
|
|
11860
12136
|
} else if (outcome.outcome === "bun") {
|
|
11861
12137
|
bun++;
|
|
11862
|
-
const svcName =
|
|
12138
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11863
12139
|
console.log(
|
|
11864
12140
|
`neat: ${svc.dir} detected as Bun
|
|
11865
12141
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11870,7 +12146,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11870
12146
|
);
|
|
11871
12147
|
} else if (outcome.outcome === "deno") {
|
|
11872
12148
|
deno++;
|
|
11873
|
-
const svcName =
|
|
12149
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11874
12150
|
console.log(
|
|
11875
12151
|
`neat: ${svc.dir} detected as Deno
|
|
11876
12152
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11881,7 +12157,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11881
12157
|
);
|
|
11882
12158
|
} else if (outcome.outcome === "cloudflare-workers") {
|
|
11883
12159
|
cloudflareWorkers++;
|
|
11884
|
-
const svcName =
|
|
12160
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11885
12161
|
console.log(
|
|
11886
12162
|
`neat: ${svc.dir} detected as Cloudflare Workers
|
|
11887
12163
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11892,7 +12168,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11892
12168
|
);
|
|
11893
12169
|
} else if (outcome.outcome === "electron") {
|
|
11894
12170
|
electron++;
|
|
11895
|
-
const svcName =
|
|
12171
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11896
12172
|
console.log(
|
|
11897
12173
|
`neat: ${svc.dir} detected as Electron
|
|
11898
12174
|
The installer doesn't cover this runtime deterministically.
|
|
@@ -11905,7 +12181,7 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
11905
12181
|
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
11906
12182
|
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
11907
12183
|
if (gaps.length > 0) {
|
|
11908
|
-
const svcName =
|
|
12184
|
+
const svcName = import_node_path53.default.basename(svc.dir);
|
|
11909
12185
|
const list = gaps.join(", ");
|
|
11910
12186
|
const subject = gaps.length === 1 ? "this library" : "these libraries";
|
|
11911
12187
|
const aux = gaps.length === 1 ? "isn't" : "aren't";
|
|
@@ -12111,24 +12387,24 @@ async function persistedPortsFor(scanPath) {
|
|
|
12111
12387
|
return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
|
|
12112
12388
|
}
|
|
12113
12389
|
async function acquireSpawnLock(scanPath) {
|
|
12114
|
-
const lockPath =
|
|
12115
|
-
await
|
|
12390
|
+
const lockPath = import_node_path53.default.join(scanPath, "neat-out", "daemon.spawn.lock");
|
|
12391
|
+
await import_node_fs34.promises.mkdir(import_node_path53.default.dirname(lockPath), { recursive: true });
|
|
12116
12392
|
const STALE_LOCK_MS = 6e4;
|
|
12117
12393
|
try {
|
|
12118
|
-
const fd = await
|
|
12394
|
+
const fd = await import_node_fs34.promises.open(lockPath, "wx");
|
|
12119
12395
|
await fd.writeFile(`${process.pid}
|
|
12120
12396
|
`, "utf8");
|
|
12121
12397
|
await fd.close();
|
|
12122
12398
|
return async () => {
|
|
12123
|
-
await
|
|
12399
|
+
await import_node_fs34.promises.unlink(lockPath).catch(() => {
|
|
12124
12400
|
});
|
|
12125
12401
|
};
|
|
12126
12402
|
} catch (err) {
|
|
12127
12403
|
if (err.code !== "EEXIST") return null;
|
|
12128
12404
|
try {
|
|
12129
|
-
const stat = await
|
|
12405
|
+
const stat = await import_node_fs34.promises.stat(lockPath);
|
|
12130
12406
|
if (Date.now() - stat.mtimeMs > STALE_LOCK_MS) {
|
|
12131
|
-
await
|
|
12407
|
+
await import_node_fs34.promises.unlink(lockPath).catch(() => {
|
|
12132
12408
|
});
|
|
12133
12409
|
return acquireSpawnLock(scanPath);
|
|
12134
12410
|
}
|
|
@@ -12157,13 +12433,13 @@ async function healthIsForProject(restPort, project) {
|
|
|
12157
12433
|
return false;
|
|
12158
12434
|
}
|
|
12159
12435
|
function daemonLogPath(projectPath2) {
|
|
12160
|
-
return
|
|
12436
|
+
return import_node_path53.default.join(projectPath2, "neat-out", "daemon.log");
|
|
12161
12437
|
}
|
|
12162
12438
|
function spawnDaemonDetached(spec) {
|
|
12163
|
-
const here =
|
|
12439
|
+
const here = import_node_path53.default.dirname(new URL(importMetaUrl).pathname);
|
|
12164
12440
|
const candidates = [
|
|
12165
|
-
|
|
12166
|
-
|
|
12441
|
+
import_node_path53.default.join(here, "neatd.cjs"),
|
|
12442
|
+
import_node_path53.default.join(here, "neatd.js")
|
|
12167
12443
|
];
|
|
12168
12444
|
let entry2 = null;
|
|
12169
12445
|
const fsSync = require("fs");
|
|
@@ -12193,7 +12469,7 @@ function spawnDaemonDetached(spec) {
|
|
|
12193
12469
|
let logFd = null;
|
|
12194
12470
|
if (spec) {
|
|
12195
12471
|
const logPath = daemonLogPath(spec.projectPath);
|
|
12196
|
-
fsSync.mkdirSync(
|
|
12472
|
+
fsSync.mkdirSync(import_node_path53.default.dirname(logPath), { recursive: true });
|
|
12197
12473
|
logFd = fsSync.openSync(logPath, "a");
|
|
12198
12474
|
}
|
|
12199
12475
|
const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
|
|
@@ -12232,7 +12508,7 @@ async function runOrchestrator(opts) {
|
|
|
12232
12508
|
browser: "skipped"
|
|
12233
12509
|
}
|
|
12234
12510
|
};
|
|
12235
|
-
const stat = await
|
|
12511
|
+
const stat = await import_node_fs34.promises.stat(opts.scanPath).catch(() => null);
|
|
12236
12512
|
if (!stat || !stat.isDirectory()) {
|
|
12237
12513
|
console.error(`neat: ${opts.scanPath} is not a directory`);
|
|
12238
12514
|
result.exitCode = 2;
|
|
@@ -12392,7 +12668,7 @@ async function runOrchestrator(opts) {
|
|
|
12392
12668
|
result.steps.browser = openBrowser(dashboardUrl);
|
|
12393
12669
|
}
|
|
12394
12670
|
const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
|
|
12395
|
-
const daemonLog = daemonRunning ?
|
|
12671
|
+
const daemonLog = daemonRunning ? import_node_path53.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
|
|
12396
12672
|
printSummary(result, graph, dashboardUrl, daemonLog);
|
|
12397
12673
|
return result;
|
|
12398
12674
|
}
|
|
@@ -12430,11 +12706,11 @@ function printSummary(result, graph, dashboardUrl, daemonLog) {
|
|
|
12430
12706
|
|
|
12431
12707
|
// src/cli-verbs.ts
|
|
12432
12708
|
init_cjs_shims();
|
|
12433
|
-
var
|
|
12709
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12434
12710
|
|
|
12435
12711
|
// src/cli-client.ts
|
|
12436
12712
|
init_cjs_shims();
|
|
12437
|
-
var
|
|
12713
|
+
var import_types32 = require("@neat.is/types");
|
|
12438
12714
|
var HttpError = class extends Error {
|
|
12439
12715
|
constructor(status2, message, responseBody = "") {
|
|
12440
12716
|
super(message);
|
|
@@ -12459,10 +12735,10 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
12459
12735
|
const root = baseUrl.replace(/\/$/, "");
|
|
12460
12736
|
const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
|
|
12461
12737
|
return {
|
|
12462
|
-
async get(
|
|
12738
|
+
async get(path56) {
|
|
12463
12739
|
let res;
|
|
12464
12740
|
try {
|
|
12465
|
-
res = await fetch(`${root}${
|
|
12741
|
+
res = await fetch(`${root}${path56}`, {
|
|
12466
12742
|
headers: { ...authHeader }
|
|
12467
12743
|
});
|
|
12468
12744
|
} catch (err) {
|
|
@@ -12474,16 +12750,16 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
12474
12750
|
const body = await res.text().catch(() => "");
|
|
12475
12751
|
throw new HttpError(
|
|
12476
12752
|
res.status,
|
|
12477
|
-
`${res.status} ${res.statusText} on GET ${
|
|
12753
|
+
`${res.status} ${res.statusText} on GET ${path56}: ${body}`,
|
|
12478
12754
|
body
|
|
12479
12755
|
);
|
|
12480
12756
|
}
|
|
12481
12757
|
return await res.json();
|
|
12482
12758
|
},
|
|
12483
|
-
async post(
|
|
12759
|
+
async post(path56, body) {
|
|
12484
12760
|
let res;
|
|
12485
12761
|
try {
|
|
12486
|
-
res = await fetch(`${root}${
|
|
12762
|
+
res = await fetch(`${root}${path56}`, {
|
|
12487
12763
|
method: "POST",
|
|
12488
12764
|
headers: { "content-type": "application/json", ...authHeader },
|
|
12489
12765
|
body: JSON.stringify(body)
|
|
@@ -12497,7 +12773,7 @@ function createHttpClient(baseUrl, bearerToken) {
|
|
|
12497
12773
|
const text = await res.text().catch(() => "");
|
|
12498
12774
|
throw new HttpError(
|
|
12499
12775
|
res.status,
|
|
12500
|
-
`${res.status} ${res.statusText} on POST ${
|
|
12776
|
+
`${res.status} ${res.statusText} on POST ${path56}: ${text}`,
|
|
12501
12777
|
text
|
|
12502
12778
|
);
|
|
12503
12779
|
}
|
|
@@ -12511,12 +12787,12 @@ function projectPath(project, suffix) {
|
|
|
12511
12787
|
}
|
|
12512
12788
|
async function runRootCause(client, input) {
|
|
12513
12789
|
const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
|
|
12514
|
-
const
|
|
12790
|
+
const path56 = projectPath(
|
|
12515
12791
|
input.project,
|
|
12516
12792
|
`/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
|
|
12517
12793
|
);
|
|
12518
12794
|
try {
|
|
12519
|
-
const result = await client.get(
|
|
12795
|
+
const result = await client.get(path56);
|
|
12520
12796
|
const arrowPath = result.traversalPath.join(" \u2190 ");
|
|
12521
12797
|
const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
|
|
12522
12798
|
const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
|
|
@@ -12542,12 +12818,12 @@ async function runRootCause(client, input) {
|
|
|
12542
12818
|
}
|
|
12543
12819
|
async function runBlastRadius(client, input) {
|
|
12544
12820
|
const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
|
|
12545
|
-
const
|
|
12821
|
+
const path56 = projectPath(
|
|
12546
12822
|
input.project,
|
|
12547
12823
|
`/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
|
|
12548
12824
|
);
|
|
12549
12825
|
try {
|
|
12550
|
-
const result = await client.get(
|
|
12826
|
+
const result = await client.get(path56);
|
|
12551
12827
|
if (result.totalAffected === 0) {
|
|
12552
12828
|
return {
|
|
12553
12829
|
summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
|
|
@@ -12576,17 +12852,17 @@ async function runBlastRadius(client, input) {
|
|
|
12576
12852
|
}
|
|
12577
12853
|
}
|
|
12578
12854
|
function formatBlastEntry(n) {
|
|
12579
|
-
const tag = n.edgeProvenance ===
|
|
12855
|
+
const tag = n.edgeProvenance === import_types32.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
|
|
12580
12856
|
return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
|
|
12581
12857
|
}
|
|
12582
12858
|
async function runDependencies(client, input) {
|
|
12583
12859
|
const depth = input.depth ?? 3;
|
|
12584
|
-
const
|
|
12860
|
+
const path56 = projectPath(
|
|
12585
12861
|
input.project,
|
|
12586
12862
|
`/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
|
|
12587
12863
|
);
|
|
12588
12864
|
try {
|
|
12589
|
-
const result = await client.get(
|
|
12865
|
+
const result = await client.get(path56);
|
|
12590
12866
|
if (result.total === 0) {
|
|
12591
12867
|
return {
|
|
12592
12868
|
summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
|
|
@@ -12633,7 +12909,7 @@ async function runObservedDependencies(client, input) {
|
|
|
12633
12909
|
if (result.observed) {
|
|
12634
12910
|
return {
|
|
12635
12911
|
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.`,
|
|
12636
|
-
provenance:
|
|
12912
|
+
provenance: import_types32.Provenance.OBSERVED
|
|
12637
12913
|
};
|
|
12638
12914
|
}
|
|
12639
12915
|
const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
|
|
@@ -12643,7 +12919,7 @@ async function runObservedDependencies(client, input) {
|
|
|
12643
12919
|
return {
|
|
12644
12920
|
summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
|
|
12645
12921
|
block: blockLines.join("\n"),
|
|
12646
|
-
provenance:
|
|
12922
|
+
provenance: import_types32.Provenance.OBSERVED
|
|
12647
12923
|
};
|
|
12648
12924
|
} catch (err) {
|
|
12649
12925
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -12678,9 +12954,9 @@ function formatDuration(ms) {
|
|
|
12678
12954
|
return `${Math.round(h / 24)}d`;
|
|
12679
12955
|
}
|
|
12680
12956
|
async function runIncidents(client, input) {
|
|
12681
|
-
const
|
|
12957
|
+
const path56 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
|
|
12682
12958
|
try {
|
|
12683
|
-
const body = await client.get(
|
|
12959
|
+
const body = await client.get(path56);
|
|
12684
12960
|
const events = body.events;
|
|
12685
12961
|
if (events.length === 0) {
|
|
12686
12962
|
return {
|
|
@@ -12697,7 +12973,7 @@ async function runIncidents(client, input) {
|
|
|
12697
12973
|
return {
|
|
12698
12974
|
summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
|
|
12699
12975
|
block: blockLines.join("\n"),
|
|
12700
|
-
provenance:
|
|
12976
|
+
provenance: import_types32.Provenance.OBSERVED
|
|
12701
12977
|
};
|
|
12702
12978
|
} catch (err) {
|
|
12703
12979
|
if (err instanceof HttpError && err.status === 404) {
|
|
@@ -12806,7 +13082,7 @@ async function runStaleEdges(client, input) {
|
|
|
12806
13082
|
return {
|
|
12807
13083
|
summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
|
|
12808
13084
|
block: blockLines.join("\n"),
|
|
12809
|
-
provenance:
|
|
13085
|
+
provenance: import_types32.Provenance.STALE
|
|
12810
13086
|
};
|
|
12811
13087
|
}
|
|
12812
13088
|
async function runPolicies(client, input) {
|
|
@@ -12970,7 +13246,7 @@ async function resolveProjectEntry(opts) {
|
|
|
12970
13246
|
const cwd = opts.cwd ?? process.cwd();
|
|
12971
13247
|
const resolvedCwd = await normalizeProjectPath(cwd);
|
|
12972
13248
|
for (const entry2 of entries) {
|
|
12973
|
-
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${
|
|
13249
|
+
if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path54.default.sep}`)) {
|
|
12974
13250
|
return entry2;
|
|
12975
13251
|
}
|
|
12976
13252
|
}
|
|
@@ -13123,7 +13399,7 @@ async function runSync(opts) {
|
|
|
13123
13399
|
}
|
|
13124
13400
|
|
|
13125
13401
|
// src/cli.ts
|
|
13126
|
-
var
|
|
13402
|
+
var import_types33 = require("@neat.is/types");
|
|
13127
13403
|
function isNpxInvocation() {
|
|
13128
13404
|
if (process.env.npm_command === "exec") return true;
|
|
13129
13405
|
const execpath = process.env.npm_execpath ?? "";
|
|
@@ -13405,7 +13681,7 @@ async function buildPatchSections(services, project) {
|
|
|
13405
13681
|
}
|
|
13406
13682
|
async function runInit(opts) {
|
|
13407
13683
|
const written = [];
|
|
13408
|
-
const stat = await
|
|
13684
|
+
const stat = await import_node_fs35.promises.stat(opts.scanPath).catch(() => null);
|
|
13409
13685
|
if (!stat || !stat.isDirectory()) {
|
|
13410
13686
|
console.error(`neat init: ${opts.scanPath} is not a directory`);
|
|
13411
13687
|
return { exitCode: 2, writtenFiles: written };
|
|
@@ -13414,13 +13690,13 @@ async function runInit(opts) {
|
|
|
13414
13690
|
printDiscoveryReport(opts, services);
|
|
13415
13691
|
const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
|
|
13416
13692
|
const patch = renderPatch(sections);
|
|
13417
|
-
const patchPath =
|
|
13693
|
+
const patchPath = import_node_path55.default.join(opts.scanPath, "neat.patch");
|
|
13418
13694
|
if (opts.dryRun) {
|
|
13419
|
-
await
|
|
13695
|
+
await import_node_fs35.promises.writeFile(patchPath, patch, "utf8");
|
|
13420
13696
|
written.push(patchPath);
|
|
13421
13697
|
console.log(`dry-run: patch written to ${patchPath}`);
|
|
13422
|
-
const gitignorePath =
|
|
13423
|
-
const gitignoreExists = await
|
|
13698
|
+
const gitignorePath = import_node_path55.default.join(opts.scanPath, ".gitignore");
|
|
13699
|
+
const gitignoreExists = await import_node_fs35.promises.stat(gitignorePath).then(() => true).catch(() => false);
|
|
13424
13700
|
const verb = gitignoreExists ? "append" : "create";
|
|
13425
13701
|
console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
|
|
13426
13702
|
console.log("rerun without --dry-run to register and snapshot.");
|
|
@@ -13431,9 +13707,9 @@ async function runInit(opts) {
|
|
|
13431
13707
|
const graph = getGraph(graphKey);
|
|
13432
13708
|
const projectPaths = pathsForProject(
|
|
13433
13709
|
graphKey,
|
|
13434
|
-
|
|
13710
|
+
import_node_path55.default.join(opts.scanPath, "neat-out")
|
|
13435
13711
|
);
|
|
13436
|
-
const errorsPath =
|
|
13712
|
+
const errorsPath = import_node_path55.default.join(import_node_path55.default.dirname(opts.outPath), import_node_path55.default.basename(projectPaths.errorsPath));
|
|
13437
13713
|
const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
|
|
13438
13714
|
await saveGraphToDisk(graph, opts.outPath);
|
|
13439
13715
|
written.push(opts.outPath);
|
|
@@ -13512,7 +13788,7 @@ async function runInit(opts) {
|
|
|
13512
13788
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
13513
13789
|
}
|
|
13514
13790
|
} else {
|
|
13515
|
-
await
|
|
13791
|
+
await import_node_fs35.promises.writeFile(patchPath, patch, "utf8");
|
|
13516
13792
|
written.push(patchPath);
|
|
13517
13793
|
}
|
|
13518
13794
|
}
|
|
@@ -13552,9 +13828,9 @@ var CLAUDE_SKILL_CONFIG = {
|
|
|
13552
13828
|
};
|
|
13553
13829
|
function claudeConfigPath() {
|
|
13554
13830
|
const override = process.env.NEAT_CLAUDE_CONFIG;
|
|
13555
|
-
if (override && override.length > 0) return
|
|
13831
|
+
if (override && override.length > 0) return import_node_path55.default.resolve(override);
|
|
13556
13832
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
13557
|
-
return
|
|
13833
|
+
return import_node_path55.default.join(home, ".claude.json");
|
|
13558
13834
|
}
|
|
13559
13835
|
async function runSkill(opts) {
|
|
13560
13836
|
const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
|
|
@@ -13566,7 +13842,7 @@ async function runSkill(opts) {
|
|
|
13566
13842
|
const target = claudeConfigPath();
|
|
13567
13843
|
let existing = {};
|
|
13568
13844
|
try {
|
|
13569
|
-
existing = JSON.parse(await
|
|
13845
|
+
existing = JSON.parse(await import_node_fs35.promises.readFile(target, "utf8"));
|
|
13570
13846
|
} catch (err) {
|
|
13571
13847
|
if (err.code !== "ENOENT") {
|
|
13572
13848
|
console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
|
|
@@ -13578,8 +13854,8 @@ async function runSkill(opts) {
|
|
|
13578
13854
|
...existing,
|
|
13579
13855
|
mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
|
|
13580
13856
|
};
|
|
13581
|
-
await
|
|
13582
|
-
await
|
|
13857
|
+
await import_node_fs35.promises.mkdir(import_node_path55.default.dirname(target), { recursive: true });
|
|
13858
|
+
await import_node_fs35.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
13583
13859
|
console.log(`neat skill: wrote mcpServers.neat to ${target}`);
|
|
13584
13860
|
console.log("restart Claude Code to pick up the new MCP server.");
|
|
13585
13861
|
return { exitCode: 0 };
|
|
@@ -13628,12 +13904,12 @@ async function main() {
|
|
|
13628
13904
|
console.error("neat init: --apply and --dry-run are mutually exclusive");
|
|
13629
13905
|
process.exit(2);
|
|
13630
13906
|
}
|
|
13631
|
-
const scanPath =
|
|
13907
|
+
const scanPath = import_node_path55.default.resolve(target);
|
|
13632
13908
|
const projectExplicit = parsed.project !== null;
|
|
13633
|
-
const projectName = projectExplicit ? project :
|
|
13909
|
+
const projectName = projectExplicit ? project : import_node_path55.default.basename(scanPath);
|
|
13634
13910
|
const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
|
|
13635
|
-
const fallback = pathsForProject(projectKey,
|
|
13636
|
-
const outPath =
|
|
13911
|
+
const fallback = pathsForProject(projectKey, import_node_path55.default.join(scanPath, "neat-out")).snapshotPath;
|
|
13912
|
+
const outPath = import_node_path55.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
|
|
13637
13913
|
const result = await runInit({
|
|
13638
13914
|
scanPath,
|
|
13639
13915
|
outPath,
|
|
@@ -13654,21 +13930,21 @@ async function main() {
|
|
|
13654
13930
|
usage();
|
|
13655
13931
|
process.exit(2);
|
|
13656
13932
|
}
|
|
13657
|
-
const scanPath =
|
|
13658
|
-
const stat = await
|
|
13933
|
+
const scanPath = import_node_path55.default.resolve(target);
|
|
13934
|
+
const stat = await import_node_fs35.promises.stat(scanPath).catch(() => null);
|
|
13659
13935
|
if (!stat || !stat.isDirectory()) {
|
|
13660
13936
|
console.error(`neat watch: ${scanPath} is not a directory`);
|
|
13661
13937
|
process.exit(2);
|
|
13662
13938
|
}
|
|
13663
|
-
const projectPaths = pathsForProject(project,
|
|
13664
|
-
const outPath =
|
|
13665
|
-
const errorsPath =
|
|
13666
|
-
process.env.NEAT_ERRORS_PATH ??
|
|
13939
|
+
const projectPaths = pathsForProject(project, import_node_path55.default.join(scanPath, "neat-out"));
|
|
13940
|
+
const outPath = import_node_path55.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
|
|
13941
|
+
const errorsPath = import_node_path55.default.resolve(
|
|
13942
|
+
process.env.NEAT_ERRORS_PATH ?? import_node_path55.default.join(import_node_path55.default.dirname(outPath), import_node_path55.default.basename(projectPaths.errorsPath))
|
|
13667
13943
|
);
|
|
13668
|
-
const staleEventsPath =
|
|
13669
|
-
process.env.NEAT_STALE_EVENTS_PATH ??
|
|
13944
|
+
const staleEventsPath = import_node_path55.default.resolve(
|
|
13945
|
+
process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path55.default.join(import_node_path55.default.dirname(outPath), import_node_path55.default.basename(projectPaths.staleEventsPath))
|
|
13670
13946
|
);
|
|
13671
|
-
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ?
|
|
13947
|
+
const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path55.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
|
|
13672
13948
|
const handle = await startWatch(getGraph(project), {
|
|
13673
13949
|
scanPath,
|
|
13674
13950
|
outPath,
|
|
@@ -13851,11 +14127,11 @@ async function main() {
|
|
|
13851
14127
|
process.exit(1);
|
|
13852
14128
|
}
|
|
13853
14129
|
async function tryOrchestrator(cmd, parsed) {
|
|
13854
|
-
const scanPath =
|
|
13855
|
-
const stat = await
|
|
14130
|
+
const scanPath = import_node_path55.default.resolve(cmd);
|
|
14131
|
+
const stat = await import_node_fs35.promises.stat(scanPath).catch(() => null);
|
|
13856
14132
|
if (!stat || !stat.isDirectory()) return null;
|
|
13857
14133
|
const projectExplicit = parsed.project !== null;
|
|
13858
|
-
const projectName = projectExplicit ? parsed.project :
|
|
14134
|
+
const projectName = projectExplicit ? parsed.project : import_node_path55.default.basename(scanPath);
|
|
13859
14135
|
const result = await runOrchestrator({
|
|
13860
14136
|
scanPath,
|
|
13861
14137
|
project: projectName,
|
|
@@ -14044,10 +14320,10 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
14044
14320
|
const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
14045
14321
|
const out = [];
|
|
14046
14322
|
for (const p of parts) {
|
|
14047
|
-
const r =
|
|
14323
|
+
const r = import_types33.DivergenceTypeSchema.safeParse(p);
|
|
14048
14324
|
if (!r.success) {
|
|
14049
14325
|
console.error(
|
|
14050
|
-
`neat divergences: unknown --type "${p}". allowed: ${
|
|
14326
|
+
`neat divergences: unknown --type "${p}". allowed: ${import_types33.DivergenceTypeSchema.options.join(", ")}`
|
|
14051
14327
|
);
|
|
14052
14328
|
return 2;
|
|
14053
14329
|
}
|