@neat.is/core 0.5.2 → 0.5.4-dev.20260721
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-I72HTUOG.js → chunk-4RU3AAOI.js} +2 -2
- package/dist/{chunk-CFDPIMRP.js → chunk-I4NZ7PSN.js} +3 -2
- package/dist/chunk-I4NZ7PSN.js.map +1 -0
- package/dist/{chunk-BI3XKGVG.js → chunk-PEFX3DBR.js} +473 -127
- package/dist/chunk-PEFX3DBR.js.map +1 -0
- package/dist/{chunk-DPEPI2N6.js → chunk-VR73QNJD.js} +293 -22
- package/dist/chunk-VR73QNJD.js.map +1 -0
- package/dist/cli.cjs +1371 -479
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +343 -75
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +879 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +888 -319
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-TEGOXE6T.js} +3 -3
- package/dist/server.cjs +553 -192
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-BI3XKGVG.js.map +0 -1
- package/dist/chunk-CFDPIMRP.js.map +0 -1
- package/dist/chunk-DPEPI2N6.js.map +0 -1
- /package/dist/{chunk-I72HTUOG.js.map → chunk-4RU3AAOI.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-TEGOXE6T.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
|
|
|
60
60
|
]);
|
|
61
61
|
const publicRead = opts.publicRead === true;
|
|
62
62
|
app.addHook("preHandler", (req, reply, done) => {
|
|
63
|
-
const
|
|
64
|
-
if (exactUnauthPaths.has(
|
|
63
|
+
const path52 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
64
|
+
if (exactUnauthPaths.has(path52) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path52)) {
|
|
65
65
|
done();
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
function resolveProtoRoot() {
|
|
196
|
-
const here =
|
|
197
|
-
return
|
|
196
|
+
const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
197
|
+
return import_node_path48.default.resolve(here, "..", "proto");
|
|
198
198
|
}
|
|
199
199
|
function loadTraceService() {
|
|
200
200
|
const protoRoot = resolveProtoRoot();
|
|
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
262
262
|
})
|
|
263
263
|
};
|
|
264
264
|
}
|
|
265
|
-
var import_node_url,
|
|
265
|
+
var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
|
|
266
266
|
var init_otel_grpc = __esm({
|
|
267
267
|
"src/otel-grpc.ts"() {
|
|
268
268
|
"use strict";
|
|
269
269
|
init_cjs_shims();
|
|
270
270
|
import_node_url = require("url");
|
|
271
|
-
|
|
271
|
+
import_node_path48 = __toESM(require("path"), 1);
|
|
272
272
|
import_node_crypto2 = require("crypto");
|
|
273
273
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
274
274
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -361,8 +361,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
361
361
|
const v = attrs[key];
|
|
362
362
|
if (typeof v === "string" && v.length > 0) {
|
|
363
363
|
const q = v.indexOf("?");
|
|
364
|
-
const
|
|
365
|
-
if (
|
|
364
|
+
const path52 = q === -1 ? v : v.slice(0, q);
|
|
365
|
+
if (path52.length > 0) return path52;
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
return void 0;
|
|
@@ -397,6 +397,7 @@ function parseOtlpRequest(body) {
|
|
|
397
397
|
attributes: attrs,
|
|
398
398
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
399
399
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
400
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
400
401
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
401
402
|
messagingDestination: messagingDestinationOf(attrs),
|
|
402
403
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -416,10 +417,10 @@ function parseOtlpRequest(body) {
|
|
|
416
417
|
return out;
|
|
417
418
|
}
|
|
418
419
|
function loadProtoRoot() {
|
|
419
|
-
const here =
|
|
420
|
-
const protoRoot =
|
|
420
|
+
const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
421
|
+
const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
|
|
421
422
|
const root = new import_protobufjs.default.Root();
|
|
422
|
-
root.resolvePath = (_origin, target) =>
|
|
423
|
+
root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
|
|
423
424
|
root.loadSync(
|
|
424
425
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
425
426
|
{ keepCase: true }
|
|
@@ -655,12 +656,12 @@ function logSpanHandler(span) {
|
|
|
655
656
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
656
657
|
);
|
|
657
658
|
}
|
|
658
|
-
var
|
|
659
|
+
var import_node_path49, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
|
|
659
660
|
var init_otel = __esm({
|
|
660
661
|
"src/otel.ts"() {
|
|
661
662
|
"use strict";
|
|
662
663
|
init_cjs_shims();
|
|
663
|
-
|
|
664
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
664
665
|
import_node_url2 = require("url");
|
|
665
666
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
666
667
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -1250,19 +1251,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1250
1251
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1251
1252
|
let best = { path: [start], edges: [] };
|
|
1252
1253
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1253
|
-
function step(node,
|
|
1254
|
-
if (
|
|
1255
|
-
best = { path: [...
|
|
1254
|
+
function step(node, path52, edges) {
|
|
1255
|
+
if (path52.length > best.path.length) {
|
|
1256
|
+
best = { path: [...path52], edges: [...edges] };
|
|
1256
1257
|
}
|
|
1257
|
-
if (
|
|
1258
|
+
if (path52.length - 1 >= maxDepth) return;
|
|
1258
1259
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1259
1260
|
for (const [srcId, edge] of incoming) {
|
|
1260
1261
|
if (visited.has(srcId)) continue;
|
|
1261
1262
|
visited.add(srcId);
|
|
1262
|
-
|
|
1263
|
+
path52.push(srcId);
|
|
1263
1264
|
edges.push(edge);
|
|
1264
|
-
step(srcId,
|
|
1265
|
-
|
|
1265
|
+
step(srcId, path52, edges);
|
|
1266
|
+
path52.pop();
|
|
1266
1267
|
edges.pop();
|
|
1267
1268
|
visited.delete(srcId);
|
|
1268
1269
|
}
|
|
@@ -1456,26 +1457,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1456
1457
|
return best;
|
|
1457
1458
|
}
|
|
1458
1459
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1459
|
-
const
|
|
1460
|
+
const path52 = [originServiceId];
|
|
1460
1461
|
const edges = [];
|
|
1461
1462
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1462
1463
|
let current = originServiceId;
|
|
1463
1464
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1464
1465
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1465
1466
|
if (!hop) break;
|
|
1466
|
-
|
|
1467
|
+
path52.push(hop.nextService);
|
|
1467
1468
|
edges.push(hop.edge);
|
|
1468
1469
|
visited.add(hop.nextService);
|
|
1469
1470
|
current = hop.nextService;
|
|
1470
1471
|
}
|
|
1471
1472
|
if (edges.length === 0) return null;
|
|
1472
|
-
return { path:
|
|
1473
|
+
return { path: path52, edges, culprit: current };
|
|
1473
1474
|
}
|
|
1474
1475
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1475
1476
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1476
1477
|
if (!chain) return null;
|
|
1477
1478
|
const culprit = chain.culprit;
|
|
1478
|
-
const
|
|
1479
|
+
const path52 = [...chain.path];
|
|
1479
1480
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1480
1481
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1481
1482
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1483,14 +1484,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1483
1484
|
if (loc) {
|
|
1484
1485
|
let rootCauseNode = culprit;
|
|
1485
1486
|
if (loc.fileNode) {
|
|
1486
|
-
|
|
1487
|
+
path52.push(loc.fileNode);
|
|
1487
1488
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1488
1489
|
rootCauseNode = loc.fileNode;
|
|
1489
1490
|
}
|
|
1490
1491
|
return import_types.RootCauseResultSchema.parse({
|
|
1491
1492
|
rootCauseNode,
|
|
1492
1493
|
rootCauseReason: loc.rootCauseReason,
|
|
1493
|
-
traversalPath:
|
|
1494
|
+
traversalPath: path52,
|
|
1494
1495
|
edgeProvenances,
|
|
1495
1496
|
confidence,
|
|
1496
1497
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1502,7 +1503,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1502
1503
|
return import_types.RootCauseResultSchema.parse({
|
|
1503
1504
|
rootCauseNode: culprit,
|
|
1504
1505
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1505
|
-
traversalPath:
|
|
1506
|
+
traversalPath: path52,
|
|
1506
1507
|
edgeProvenances,
|
|
1507
1508
|
confidence,
|
|
1508
1509
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2929,6 +2930,18 @@ async function handleSpan(ctx, span) {
|
|
|
2929
2930
|
callSiteEvidence
|
|
2930
2931
|
);
|
|
2931
2932
|
if (result) affectedNode = targetId;
|
|
2933
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
2934
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
2935
|
+
upsertObservedEdge(
|
|
2936
|
+
ctx.graph,
|
|
2937
|
+
import_types3.EdgeType.CALLS,
|
|
2938
|
+
observedSource(),
|
|
2939
|
+
collectionId,
|
|
2940
|
+
ts,
|
|
2941
|
+
isError,
|
|
2942
|
+
callSiteEvidence
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2932
2945
|
}
|
|
2933
2946
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2934
2947
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -4673,14 +4686,21 @@ function engineFromImage(image) {
|
|
|
4673
4686
|
// src/extract/databases/dotenv.ts
|
|
4674
4687
|
var CONNECTION_KEYS = /* @__PURE__ */ new Set([
|
|
4675
4688
|
"DATABASE_URL",
|
|
4689
|
+
"DATABASE_URI",
|
|
4676
4690
|
"DB_URL",
|
|
4691
|
+
"DB_URI",
|
|
4677
4692
|
"POSTGRES_URL",
|
|
4693
|
+
"POSTGRES_URI",
|
|
4678
4694
|
"POSTGRESQL_URL",
|
|
4695
|
+
"POSTGRESQL_URI",
|
|
4679
4696
|
"MYSQL_URL",
|
|
4697
|
+
"MYSQL_URI",
|
|
4698
|
+
"MONGODB_URL",
|
|
4680
4699
|
"MONGODB_URI",
|
|
4681
4700
|
"MONGO_URL",
|
|
4682
4701
|
"MONGO_URI",
|
|
4683
|
-
"REDIS_URL"
|
|
4702
|
+
"REDIS_URL",
|
|
4703
|
+
"REDIS_URI"
|
|
4684
4704
|
]);
|
|
4685
4705
|
function parseDotenvLine(line) {
|
|
4686
4706
|
const trimmed = line.trim();
|
|
@@ -5769,7 +5789,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5769
5789
|
|
|
5770
5790
|
// src/extract/calls/index.ts
|
|
5771
5791
|
init_cjs_shims();
|
|
5772
|
-
var
|
|
5792
|
+
var import_types21 = require("@neat.is/types");
|
|
5773
5793
|
|
|
5774
5794
|
// src/extract/calls/http.ts
|
|
5775
5795
|
init_cjs_shims();
|
|
@@ -6482,15 +6502,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6482
6502
|
return out;
|
|
6483
6503
|
}
|
|
6484
6504
|
|
|
6505
|
+
// src/extract/calls/mongoose.ts
|
|
6506
|
+
init_cjs_shims();
|
|
6507
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6508
|
+
var import_types20 = require("@neat.is/types");
|
|
6509
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
6510
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
6511
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
6512
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
6513
|
+
"advice",
|
|
6514
|
+
"energy",
|
|
6515
|
+
"excretion",
|
|
6516
|
+
"digestion",
|
|
6517
|
+
"cooperation",
|
|
6518
|
+
"health",
|
|
6519
|
+
"justice",
|
|
6520
|
+
"labour",
|
|
6521
|
+
"machinery",
|
|
6522
|
+
"equipment",
|
|
6523
|
+
"information",
|
|
6524
|
+
"pollution",
|
|
6525
|
+
"sewage",
|
|
6526
|
+
"paper",
|
|
6527
|
+
"money",
|
|
6528
|
+
"species",
|
|
6529
|
+
"series",
|
|
6530
|
+
"rain",
|
|
6531
|
+
"rice",
|
|
6532
|
+
"fish",
|
|
6533
|
+
"sheep",
|
|
6534
|
+
"moose",
|
|
6535
|
+
"deer",
|
|
6536
|
+
"news",
|
|
6537
|
+
"expertise",
|
|
6538
|
+
"status",
|
|
6539
|
+
"media"
|
|
6540
|
+
]);
|
|
6541
|
+
var PLURALIZE_RULES = [
|
|
6542
|
+
[/human$/gi, "humans"],
|
|
6543
|
+
[/(m)an$/gi, "$1en"],
|
|
6544
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
6545
|
+
[/(child)$/gi, "$1ren"],
|
|
6546
|
+
[/^(ox)$/gi, "$1en"],
|
|
6547
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
6548
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
6549
|
+
[/(alias|status)$/gi, "$1es"],
|
|
6550
|
+
[/(bu)s$/gi, "$1ses"],
|
|
6551
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
6552
|
+
[/([ti])um$/gi, "$1a"],
|
|
6553
|
+
[/sis$/gi, "ses"],
|
|
6554
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
6555
|
+
[/(hive)$/gi, "$1s"],
|
|
6556
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
6557
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
6558
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
6559
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
6560
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
6561
|
+
[/(quiz)$/gi, "$1zes"],
|
|
6562
|
+
[/s$/gi, "s"],
|
|
6563
|
+
[/([^a-z])$/, "$1"],
|
|
6564
|
+
[/$/gi, "s"]
|
|
6565
|
+
];
|
|
6566
|
+
function pluralizeCollection(name) {
|
|
6567
|
+
const str = name.toLowerCase();
|
|
6568
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
6569
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
6570
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
6571
|
+
}
|
|
6572
|
+
return str;
|
|
6573
|
+
}
|
|
6574
|
+
var MODEL_METHODS = [
|
|
6575
|
+
"find",
|
|
6576
|
+
"findOne",
|
|
6577
|
+
"findById",
|
|
6578
|
+
"findByIdAndUpdate",
|
|
6579
|
+
"findByIdAndDelete",
|
|
6580
|
+
"findOneAndUpdate",
|
|
6581
|
+
"findOneAndDelete",
|
|
6582
|
+
"findOneAndReplace",
|
|
6583
|
+
"countDocuments",
|
|
6584
|
+
"estimatedDocumentCount",
|
|
6585
|
+
"distinct",
|
|
6586
|
+
"aggregate",
|
|
6587
|
+
"exists",
|
|
6588
|
+
"where",
|
|
6589
|
+
"populate",
|
|
6590
|
+
"watch",
|
|
6591
|
+
"hydrate",
|
|
6592
|
+
"create",
|
|
6593
|
+
"insertMany",
|
|
6594
|
+
"insertOne",
|
|
6595
|
+
"updateOne",
|
|
6596
|
+
"updateMany",
|
|
6597
|
+
"replaceOne",
|
|
6598
|
+
"deleteOne",
|
|
6599
|
+
"deleteMany",
|
|
6600
|
+
"bulkWrite",
|
|
6601
|
+
"bulkSave",
|
|
6602
|
+
"save",
|
|
6603
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
6604
|
+
"count",
|
|
6605
|
+
"update",
|
|
6606
|
+
"remove",
|
|
6607
|
+
"findOneAndRemove",
|
|
6608
|
+
"findByIdAndRemove"
|
|
6609
|
+
];
|
|
6610
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
6611
|
+
function schemaCollectionVars(content) {
|
|
6612
|
+
const out = /* @__PURE__ */ new Map();
|
|
6613
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
6614
|
+
let m;
|
|
6615
|
+
while ((m = re.exec(content)) !== null) {
|
|
6616
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
6617
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
6618
|
+
if (opt) out.set(m[1], opt[1]);
|
|
6619
|
+
}
|
|
6620
|
+
return out;
|
|
6621
|
+
}
|
|
6622
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
6623
|
+
const trimmed = rest.trim();
|
|
6624
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
6625
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
6626
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
6627
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
6628
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
6629
|
+
}
|
|
6630
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
6631
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
6632
|
+
}
|
|
6633
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
6634
|
+
}
|
|
6635
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
6636
|
+
const schemaColl = schemaCollectionVars(content);
|
|
6637
|
+
const out = [];
|
|
6638
|
+
const re = /(?:(?:const|let|var)\s+(\w+)\s*=\s*)?(?:await\s+)?(?:\b\w+\s*\.\s*)?\bmodel\s*\(\s*['"`]([\w$]+)['"`]\s*(?:,\s*([\s\S]{0,300}?))?\)/g;
|
|
6639
|
+
let m;
|
|
6640
|
+
while ((m = re.exec(content)) !== null) {
|
|
6641
|
+
out.push({
|
|
6642
|
+
modelName: m[2],
|
|
6643
|
+
varName: m[1] ?? null,
|
|
6644
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
6645
|
+
matchText: m[0]
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6648
|
+
return out;
|
|
6649
|
+
}
|
|
6650
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
6651
|
+
const line = lineOf(file.content, matchText);
|
|
6652
|
+
return {
|
|
6653
|
+
infraId: (0, import_types20.infraId)(r.kind, r.name),
|
|
6654
|
+
name: r.name,
|
|
6655
|
+
kind: r.kind,
|
|
6656
|
+
edgeType: "CALLS",
|
|
6657
|
+
confidenceKind: "verified-call-site",
|
|
6658
|
+
evidence: { file: import_node_path31.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
6659
|
+
};
|
|
6660
|
+
}
|
|
6661
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
6662
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
6663
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
6664
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
6665
|
+
const content = file.content;
|
|
6666
|
+
const out = [];
|
|
6667
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6668
|
+
const push = (r, matchText) => {
|
|
6669
|
+
const key = `${r.kind}/${r.name}`;
|
|
6670
|
+
if (seen.has(key)) return;
|
|
6671
|
+
seen.add(key);
|
|
6672
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
6673
|
+
};
|
|
6674
|
+
if (hasMongoose) {
|
|
6675
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
6676
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
6677
|
+
}
|
|
6678
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
6679
|
+
let c;
|
|
6680
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
6681
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
6682
|
+
}
|
|
6683
|
+
return out;
|
|
6684
|
+
}
|
|
6685
|
+
function parseDestructure(inner, specifier, out) {
|
|
6686
|
+
for (const raw of inner.split(",")) {
|
|
6687
|
+
const part = raw.trim();
|
|
6688
|
+
if (!part) continue;
|
|
6689
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
6690
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
6691
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
6692
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
6693
|
+
else {
|
|
6694
|
+
const id = /^(\w+)$/.exec(part);
|
|
6695
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6699
|
+
function parseImportBindings(content) {
|
|
6700
|
+
const out = [];
|
|
6701
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
6702
|
+
let m;
|
|
6703
|
+
while ((m = esm.exec(content)) !== null) {
|
|
6704
|
+
const clause = m[1].trim();
|
|
6705
|
+
const spec = m[2];
|
|
6706
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
6707
|
+
if (ns) {
|
|
6708
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
6709
|
+
continue;
|
|
6710
|
+
}
|
|
6711
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
6712
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
6713
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
6714
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
6715
|
+
}
|
|
6716
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
6717
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
6718
|
+
const lhs = m[1];
|
|
6719
|
+
const spec = m[2];
|
|
6720
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
6721
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
6722
|
+
}
|
|
6723
|
+
return out;
|
|
6724
|
+
}
|
|
6725
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
6726
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
6727
|
+
if (defs.length === 0) return null;
|
|
6728
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
6729
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
6730
|
+
const byName = new Map(byVar);
|
|
6731
|
+
let def;
|
|
6732
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
6733
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
6734
|
+
if (!def) {
|
|
6735
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
6736
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
6737
|
+
}
|
|
6738
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
6739
|
+
return { byName, ...def ? { default: def } : {} };
|
|
6740
|
+
}
|
|
6741
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
6742
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
6743
|
+
if (mongooseFiles.length === 0) return [];
|
|
6744
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
6745
|
+
const registry = /* @__PURE__ */ new Map();
|
|
6746
|
+
for (const f of mongooseFiles) {
|
|
6747
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
6748
|
+
if (fx) registry.set(toPosix2(import_node_path31.default.relative(serviceDir, f.path)), fx);
|
|
6749
|
+
}
|
|
6750
|
+
if (registry.size === 0) return [];
|
|
6751
|
+
const out = [];
|
|
6752
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6753
|
+
for (const f of files) {
|
|
6754
|
+
const bindings = parseImportBindings(f.content);
|
|
6755
|
+
if (bindings.length === 0) continue;
|
|
6756
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
6757
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
6758
|
+
for (const b of bindings) {
|
|
6759
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path31.default.dirname(f.path), serviceDir, null);
|
|
6760
|
+
if (!resolvedRel) continue;
|
|
6761
|
+
const fx = registry.get(resolvedRel);
|
|
6762
|
+
if (!fx) continue;
|
|
6763
|
+
if (b.kind === "named" && b.exportName) {
|
|
6764
|
+
const coll = fx.byName.get(b.exportName);
|
|
6765
|
+
if (coll) directColl.set(b.local, coll);
|
|
6766
|
+
} else if (b.kind === "default") {
|
|
6767
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6768
|
+
} else {
|
|
6769
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6770
|
+
nsExports.set(b.local, fx);
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
6774
|
+
const localDefs = new Set(
|
|
6775
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
6776
|
+
);
|
|
6777
|
+
const emit = (collection, matchText) => {
|
|
6778
|
+
const key = `${f.path}::${collection}`;
|
|
6779
|
+
if (seen.has(key)) return;
|
|
6780
|
+
seen.add(key);
|
|
6781
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
6782
|
+
};
|
|
6783
|
+
for (const [local, collection] of directColl) {
|
|
6784
|
+
if (localDefs.has(local)) continue;
|
|
6785
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6786
|
+
let qm;
|
|
6787
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
6788
|
+
}
|
|
6789
|
+
for (const [local, fx] of nsExports) {
|
|
6790
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6791
|
+
let qm;
|
|
6792
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
6793
|
+
const coll = fx.byName.get(qm[1]);
|
|
6794
|
+
if (coll) emit(coll, qm[0]);
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
return out;
|
|
6799
|
+
}
|
|
6800
|
+
|
|
6485
6801
|
// src/extract/calls/index.ts
|
|
6486
6802
|
function edgeTypeFromEndpoint(ep) {
|
|
6487
6803
|
switch (ep.edgeType) {
|
|
6488
6804
|
case "PUBLISHES_TO":
|
|
6489
|
-
return
|
|
6805
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6490
6806
|
case "CONSUMES_FROM":
|
|
6491
|
-
return
|
|
6807
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6492
6808
|
default:
|
|
6493
|
-
return
|
|
6809
|
+
return import_types21.EdgeType.CALLS;
|
|
6494
6810
|
}
|
|
6495
6811
|
}
|
|
6496
6812
|
function isAwsKind(kind) {
|
|
@@ -6502,23 +6818,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6502
6818
|
for (const service of services) {
|
|
6503
6819
|
const files = await loadSourceFiles(service.dir);
|
|
6504
6820
|
const endpoints = [];
|
|
6821
|
+
const maskedFiles = [];
|
|
6505
6822
|
for (const file of files) {
|
|
6506
6823
|
if (isTestPath(file.path)) continue;
|
|
6507
6824
|
const masked = maskCommentsInSource(file.content);
|
|
6508
6825
|
const maskedFile = { path: file.path, content: masked };
|
|
6826
|
+
maskedFiles.push(maskedFile);
|
|
6509
6827
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6510
6828
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6511
6829
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6512
6830
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6513
6831
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6832
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6514
6833
|
}
|
|
6834
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6515
6835
|
if (endpoints.length === 0) continue;
|
|
6516
6836
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6517
6837
|
for (const ep of endpoints) {
|
|
6518
6838
|
if (!graph.hasNode(ep.infraId)) {
|
|
6519
6839
|
const node = {
|
|
6520
6840
|
id: ep.infraId,
|
|
6521
|
-
type:
|
|
6841
|
+
type: import_types21.NodeType.InfraNode,
|
|
6522
6842
|
name: ep.name,
|
|
6523
6843
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6524
6844
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6530,7 +6850,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6530
6850
|
nodesAdded++;
|
|
6531
6851
|
}
|
|
6532
6852
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6533
|
-
const confidence = (0,
|
|
6853
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6534
6854
|
const relFile = toPosix2(ep.evidence.file);
|
|
6535
6855
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6536
6856
|
graph,
|
|
@@ -6540,7 +6860,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6540
6860
|
);
|
|
6541
6861
|
nodesAdded += n;
|
|
6542
6862
|
edgesAdded += e;
|
|
6543
|
-
if (!(0,
|
|
6863
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6544
6864
|
noteExtractedDropped({
|
|
6545
6865
|
source: fileNodeId,
|
|
6546
6866
|
target: ep.infraId,
|
|
@@ -6560,7 +6880,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6560
6880
|
source: fileNodeId,
|
|
6561
6881
|
target: ep.infraId,
|
|
6562
6882
|
type: edgeType,
|
|
6563
|
-
provenance:
|
|
6883
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6564
6884
|
confidence,
|
|
6565
6885
|
evidence: ep.evidence
|
|
6566
6886
|
};
|
|
@@ -6586,16 +6906,16 @@ init_cjs_shims();
|
|
|
6586
6906
|
|
|
6587
6907
|
// src/extract/infra/docker-compose.ts
|
|
6588
6908
|
init_cjs_shims();
|
|
6589
|
-
var
|
|
6590
|
-
var
|
|
6909
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6910
|
+
var import_types23 = require("@neat.is/types");
|
|
6591
6911
|
|
|
6592
6912
|
// src/extract/infra/shared.ts
|
|
6593
6913
|
init_cjs_shims();
|
|
6594
|
-
var
|
|
6914
|
+
var import_types22 = require("@neat.is/types");
|
|
6595
6915
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6596
6916
|
return {
|
|
6597
|
-
id: (0,
|
|
6598
|
-
type:
|
|
6917
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6918
|
+
type: import_types22.NodeType.InfraNode,
|
|
6599
6919
|
name,
|
|
6600
6920
|
provider,
|
|
6601
6921
|
kind,
|
|
@@ -6639,8 +6959,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6639
6959
|
source: anchorId,
|
|
6640
6960
|
target: node.id,
|
|
6641
6961
|
type: edgeType,
|
|
6642
|
-
provenance:
|
|
6643
|
-
confidence: (0,
|
|
6962
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6963
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6644
6964
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6645
6965
|
};
|
|
6646
6966
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6657,7 +6977,7 @@ function dependsOnList(value) {
|
|
|
6657
6977
|
}
|
|
6658
6978
|
function serviceNameToServiceNode(name, services) {
|
|
6659
6979
|
for (const s of services) {
|
|
6660
|
-
if (s.node.name === name ||
|
|
6980
|
+
if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
|
|
6661
6981
|
}
|
|
6662
6982
|
return null;
|
|
6663
6983
|
}
|
|
@@ -6666,7 +6986,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6666
6986
|
let edgesAdded = 0;
|
|
6667
6987
|
let composePath = null;
|
|
6668
6988
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6669
|
-
const abs =
|
|
6989
|
+
const abs = import_node_path32.default.join(scanPath, name);
|
|
6670
6990
|
if (await exists(abs)) {
|
|
6671
6991
|
composePath = abs;
|
|
6672
6992
|
break;
|
|
@@ -6679,13 +6999,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6679
6999
|
} catch (err) {
|
|
6680
7000
|
recordExtractionError(
|
|
6681
7001
|
"infra docker-compose",
|
|
6682
|
-
|
|
7002
|
+
import_node_path32.default.relative(scanPath, composePath),
|
|
6683
7003
|
err
|
|
6684
7004
|
);
|
|
6685
7005
|
return { nodesAdded, edgesAdded };
|
|
6686
7006
|
}
|
|
6687
7007
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6688
|
-
const evidenceFile =
|
|
7008
|
+
const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
|
|
6689
7009
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6690
7010
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6691
7011
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6707,15 +7027,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6707
7027
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6708
7028
|
const targetId = composeNameToNodeId.get(dep);
|
|
6709
7029
|
if (!targetId) continue;
|
|
6710
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7030
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6711
7031
|
if (graph.hasEdge(edgeId)) continue;
|
|
6712
7032
|
const edge = {
|
|
6713
7033
|
id: edgeId,
|
|
6714
7034
|
source: sourceId,
|
|
6715
7035
|
target: targetId,
|
|
6716
|
-
type:
|
|
6717
|
-
provenance:
|
|
6718
|
-
confidence: (0,
|
|
7036
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
7037
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7038
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6719
7039
|
evidence: { file: evidenceFile }
|
|
6720
7040
|
};
|
|
6721
7041
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6727,9 +7047,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6727
7047
|
|
|
6728
7048
|
// src/extract/infra/dockerfile.ts
|
|
6729
7049
|
init_cjs_shims();
|
|
6730
|
-
var
|
|
7050
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6731
7051
|
var import_node_fs16 = require("fs");
|
|
6732
|
-
var
|
|
7052
|
+
var import_types24 = require("@neat.is/types");
|
|
6733
7053
|
function readDockerfile(content) {
|
|
6734
7054
|
let image = null;
|
|
6735
7055
|
const ports = [];
|
|
@@ -6758,7 +7078,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6758
7078
|
let nodesAdded = 0;
|
|
6759
7079
|
let edgesAdded = 0;
|
|
6760
7080
|
for (const service of services) {
|
|
6761
|
-
const dockerfilePath =
|
|
7081
|
+
const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
|
|
6762
7082
|
if (!await exists(dockerfilePath)) continue;
|
|
6763
7083
|
let content;
|
|
6764
7084
|
try {
|
|
@@ -6766,7 +7086,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6766
7086
|
} catch (err) {
|
|
6767
7087
|
recordExtractionError(
|
|
6768
7088
|
"infra dockerfile",
|
|
6769
|
-
|
|
7089
|
+
import_node_path33.default.relative(scanPath, dockerfilePath),
|
|
6770
7090
|
err
|
|
6771
7091
|
);
|
|
6772
7092
|
continue;
|
|
@@ -6778,8 +7098,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6778
7098
|
graph.addNode(node.id, node);
|
|
6779
7099
|
nodesAdded++;
|
|
6780
7100
|
}
|
|
6781
|
-
const relDockerfile = toPosix2(
|
|
6782
|
-
const evidenceFile = toPosix2(
|
|
7101
|
+
const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
|
|
7102
|
+
const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
|
|
6783
7103
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6784
7104
|
graph,
|
|
6785
7105
|
service.pkg.name,
|
|
@@ -6788,15 +7108,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6788
7108
|
);
|
|
6789
7109
|
nodesAdded += fn;
|
|
6790
7110
|
edgesAdded += fe;
|
|
6791
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7111
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6792
7112
|
if (!graph.hasEdge(edgeId)) {
|
|
6793
7113
|
const edge = {
|
|
6794
7114
|
id: edgeId,
|
|
6795
7115
|
source: fileNodeId,
|
|
6796
7116
|
target: node.id,
|
|
6797
|
-
type:
|
|
6798
|
-
provenance:
|
|
6799
|
-
confidence: (0,
|
|
7117
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7118
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7119
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6800
7120
|
evidence: {
|
|
6801
7121
|
file: evidenceFile,
|
|
6802
7122
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6811,15 +7131,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6811
7131
|
graph.addNode(portNode.id, portNode);
|
|
6812
7132
|
nodesAdded++;
|
|
6813
7133
|
}
|
|
6814
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7134
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6815
7135
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6816
7136
|
const portEdge = {
|
|
6817
7137
|
id: portEdgeId,
|
|
6818
7138
|
source: fileNodeId,
|
|
6819
7139
|
target: portNode.id,
|
|
6820
|
-
type:
|
|
6821
|
-
provenance:
|
|
6822
|
-
confidence: (0,
|
|
7140
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7141
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7142
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6823
7143
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6824
7144
|
};
|
|
6825
7145
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6832,8 +7152,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6832
7152
|
// src/extract/infra/terraform.ts
|
|
6833
7153
|
init_cjs_shims();
|
|
6834
7154
|
var import_node_fs17 = require("fs");
|
|
6835
|
-
var
|
|
6836
|
-
var
|
|
7155
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7156
|
+
var import_types25 = require("@neat.is/types");
|
|
6837
7157
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6838
7158
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6839
7159
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6843,11 +7163,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6843
7163
|
for (const entry of entries) {
|
|
6844
7164
|
if (entry.isDirectory()) {
|
|
6845
7165
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
6846
|
-
const child =
|
|
7166
|
+
const child = import_node_path34.default.join(start, entry.name);
|
|
6847
7167
|
if (await isPythonVenvDir(child)) continue;
|
|
6848
7168
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6849
7169
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
6850
|
-
out.push(
|
|
7170
|
+
out.push(import_node_path34.default.join(start, entry.name));
|
|
6851
7171
|
}
|
|
6852
7172
|
}
|
|
6853
7173
|
return out;
|
|
@@ -6879,7 +7199,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6879
7199
|
const files = await walkTfFiles(scanPath);
|
|
6880
7200
|
for (const file of files) {
|
|
6881
7201
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
6882
|
-
const evidenceFile = toPosix2(
|
|
7202
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
|
|
6883
7203
|
const resources = [];
|
|
6884
7204
|
const byKey = /* @__PURE__ */ new Map();
|
|
6885
7205
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6914,16 +7234,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6914
7234
|
if (!target) continue;
|
|
6915
7235
|
if (seen.has(target.nodeId)) continue;
|
|
6916
7236
|
seen.add(target.nodeId);
|
|
6917
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7237
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6918
7238
|
if (graph.hasEdge(edgeId)) continue;
|
|
6919
7239
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6920
7240
|
const edge = {
|
|
6921
7241
|
id: edgeId,
|
|
6922
7242
|
source: resource.nodeId,
|
|
6923
7243
|
target: target.nodeId,
|
|
6924
|
-
type:
|
|
6925
|
-
provenance:
|
|
6926
|
-
confidence: (0,
|
|
7244
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7245
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7246
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6927
7247
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6928
7248
|
};
|
|
6929
7249
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6937,7 +7257,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6937
7257
|
// src/extract/infra/k8s.ts
|
|
6938
7258
|
init_cjs_shims();
|
|
6939
7259
|
var import_node_fs18 = require("fs");
|
|
6940
|
-
var
|
|
7260
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6941
7261
|
var import_yaml3 = require("yaml");
|
|
6942
7262
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6943
7263
|
Service: "k8s-service",
|
|
@@ -6955,11 +7275,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6955
7275
|
for (const entry of entries) {
|
|
6956
7276
|
if (entry.isDirectory()) {
|
|
6957
7277
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6958
|
-
const child =
|
|
7278
|
+
const child = import_node_path35.default.join(start, entry.name);
|
|
6959
7279
|
if (await isPythonVenvDir(child)) continue;
|
|
6960
7280
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6961
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6962
|
-
out.push(
|
|
7281
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry.name))) {
|
|
7282
|
+
out.push(import_node_path35.default.join(start, entry.name));
|
|
6963
7283
|
}
|
|
6964
7284
|
}
|
|
6965
7285
|
return out;
|
|
@@ -6993,13 +7313,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6993
7313
|
// src/extract/infra/cloudflare.ts
|
|
6994
7314
|
init_cjs_shims();
|
|
6995
7315
|
var import_node_fs19 = require("fs");
|
|
6996
|
-
var
|
|
7316
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6997
7317
|
var import_smol_toml2 = require("smol-toml");
|
|
6998
|
-
var
|
|
7318
|
+
var import_types26 = require("@neat.is/types");
|
|
6999
7319
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7000
7320
|
async function readWranglerConfig(dir) {
|
|
7001
7321
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7002
|
-
const abs =
|
|
7322
|
+
const abs = import_node_path36.default.join(dir, filename);
|
|
7003
7323
|
if (!await exists(abs)) continue;
|
|
7004
7324
|
const raw = await import_node_fs19.promises.readFile(abs, "utf8");
|
|
7005
7325
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7043,8 +7363,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7043
7363
|
source: anchorId,
|
|
7044
7364
|
target: node.id,
|
|
7045
7365
|
type: edgeType,
|
|
7046
|
-
provenance:
|
|
7047
|
-
confidence: (0,
|
|
7366
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7367
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7048
7368
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7049
7369
|
};
|
|
7050
7370
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7062,11 +7382,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7062
7382
|
try {
|
|
7063
7383
|
read = await readWranglerConfig(service.dir);
|
|
7064
7384
|
} catch (err) {
|
|
7065
|
-
recordExtractionError("infra cloudflare",
|
|
7385
|
+
recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
|
|
7066
7386
|
continue;
|
|
7067
7387
|
}
|
|
7068
7388
|
if (!read || !read.config.name) continue;
|
|
7069
|
-
const evidenceFile = toPosix2(
|
|
7389
|
+
const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
|
|
7070
7390
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7071
7391
|
}
|
|
7072
7392
|
for (const worker of discovered) {
|
|
@@ -7078,7 +7398,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7078
7398
|
}
|
|
7079
7399
|
let anchorId = service.node.id;
|
|
7080
7400
|
if (config.main) {
|
|
7081
|
-
const entryRelPath = toPosix2(
|
|
7401
|
+
const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
|
|
7082
7402
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7083
7403
|
graph,
|
|
7084
7404
|
service.pkg.name,
|
|
@@ -7105,15 +7425,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7105
7425
|
nodesAdded++;
|
|
7106
7426
|
}
|
|
7107
7427
|
if (runtimeNode.id !== anchorId) {
|
|
7108
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7428
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7109
7429
|
if (!graph.hasEdge(runsOnId)) {
|
|
7110
7430
|
const edge = {
|
|
7111
7431
|
id: runsOnId,
|
|
7112
7432
|
source: anchorId,
|
|
7113
7433
|
target: runtimeNode.id,
|
|
7114
|
-
type:
|
|
7115
|
-
provenance:
|
|
7116
|
-
confidence: (0,
|
|
7434
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7435
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7436
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7117
7437
|
evidence: {
|
|
7118
7438
|
file: evidenceFile,
|
|
7119
7439
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7127,7 +7447,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7127
7447
|
const result = addResourceEdge(
|
|
7128
7448
|
graph,
|
|
7129
7449
|
anchorId,
|
|
7130
|
-
|
|
7450
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7131
7451
|
"cloudflare-route",
|
|
7132
7452
|
route,
|
|
7133
7453
|
evidenceFile,
|
|
@@ -7151,7 +7471,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7151
7471
|
const result = addResourceEdge(
|
|
7152
7472
|
graph,
|
|
7153
7473
|
anchorId,
|
|
7154
|
-
|
|
7474
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7155
7475
|
group.kind,
|
|
7156
7476
|
name,
|
|
7157
7477
|
evidenceFile,
|
|
@@ -7165,7 +7485,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7165
7485
|
const result = addResourceEdge(
|
|
7166
7486
|
graph,
|
|
7167
7487
|
anchorId,
|
|
7168
|
-
|
|
7488
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7169
7489
|
"cloudflare-cron",
|
|
7170
7490
|
cron,
|
|
7171
7491
|
evidenceFile,
|
|
@@ -7178,7 +7498,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7178
7498
|
const result = addResourceEdge(
|
|
7179
7499
|
graph,
|
|
7180
7500
|
anchorId,
|
|
7181
|
-
|
|
7501
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7182
7502
|
"cloudflare-env-var",
|
|
7183
7503
|
varName,
|
|
7184
7504
|
evidenceFile,
|
|
@@ -7191,15 +7511,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7191
7511
|
if (!svc.service) continue;
|
|
7192
7512
|
const target = workerIndex.get(svc.service);
|
|
7193
7513
|
if (target && target.anchorId !== anchorId) {
|
|
7194
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7514
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7195
7515
|
if (!graph.hasEdge(edgeId)) {
|
|
7196
7516
|
const edge = {
|
|
7197
7517
|
id: edgeId,
|
|
7198
7518
|
source: anchorId,
|
|
7199
7519
|
target: target.anchorId,
|
|
7200
|
-
type:
|
|
7201
|
-
provenance:
|
|
7202
|
-
confidence: (0,
|
|
7520
|
+
type: import_types26.EdgeType.CALLS,
|
|
7521
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7522
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7203
7523
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7204
7524
|
};
|
|
7205
7525
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7210,7 +7530,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7210
7530
|
const result = addResourceEdge(
|
|
7211
7531
|
graph,
|
|
7212
7532
|
anchorId,
|
|
7213
|
-
|
|
7533
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7214
7534
|
"cloudflare-service-binding",
|
|
7215
7535
|
svc.service,
|
|
7216
7536
|
evidenceFile,
|
|
@@ -7226,12 +7546,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7226
7546
|
// src/extract/infra/vercel.ts
|
|
7227
7547
|
init_cjs_shims();
|
|
7228
7548
|
var import_node_fs20 = require("fs");
|
|
7229
|
-
var
|
|
7230
|
-
var
|
|
7549
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7550
|
+
var import_types27 = require("@neat.is/types");
|
|
7231
7551
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7232
7552
|
async function readVercelConfig(dir) {
|
|
7233
7553
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7234
|
-
const abs =
|
|
7554
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7235
7555
|
if (!await exists(abs)) continue;
|
|
7236
7556
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7237
7557
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7240,7 +7560,7 @@ async function readVercelConfig(dir) {
|
|
|
7240
7560
|
return null;
|
|
7241
7561
|
}
|
|
7242
7562
|
async function readLinkedProjectName(dir) {
|
|
7243
|
-
const abs =
|
|
7563
|
+
const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
|
|
7244
7564
|
if (!await exists(abs)) return void 0;
|
|
7245
7565
|
const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
|
|
7246
7566
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7258,7 +7578,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7258
7578
|
read = await readVercelConfig(service.dir);
|
|
7259
7579
|
projectName = await readLinkedProjectName(service.dir);
|
|
7260
7580
|
} catch (err) {
|
|
7261
|
-
recordExtractionError("infra vercel",
|
|
7581
|
+
recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7262
7582
|
continue;
|
|
7263
7583
|
}
|
|
7264
7584
|
if (!read && !projectName) continue;
|
|
@@ -7274,7 +7594,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7274
7594
|
const anchorId = service.node.id;
|
|
7275
7595
|
if (!read) continue;
|
|
7276
7596
|
const { config, relFile, raw } = read;
|
|
7277
|
-
const evidenceFile = toPosix2(
|
|
7597
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
|
|
7278
7598
|
const add = (edgeType, kind, name) => {
|
|
7279
7599
|
if (!name) return;
|
|
7280
7600
|
const result = emitPlatformResourceEdge(
|
|
@@ -7290,12 +7610,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7290
7610
|
nodesAdded += result.nodesAdded;
|
|
7291
7611
|
edgesAdded += result.edgesAdded;
|
|
7292
7612
|
};
|
|
7293
|
-
add(
|
|
7294
|
-
for (const cron of config.crons ?? []) add(
|
|
7295
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7296
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7613
|
+
add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7614
|
+
for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7615
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7616
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7297
7617
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7298
|
-
add(
|
|
7618
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7299
7619
|
}
|
|
7300
7620
|
}
|
|
7301
7621
|
return { nodesAdded, edgesAdded };
|
|
@@ -7304,13 +7624,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7304
7624
|
// src/extract/infra/railway.ts
|
|
7305
7625
|
init_cjs_shims();
|
|
7306
7626
|
var import_node_fs21 = require("fs");
|
|
7307
|
-
var
|
|
7627
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7308
7628
|
var import_smol_toml3 = require("smol-toml");
|
|
7309
|
-
var
|
|
7629
|
+
var import_types28 = require("@neat.is/types");
|
|
7310
7630
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7311
7631
|
async function readRailwayConfig(dir) {
|
|
7312
7632
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7313
|
-
const abs =
|
|
7633
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7314
7634
|
if (!await exists(abs)) continue;
|
|
7315
7635
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7316
7636
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7326,7 +7646,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7326
7646
|
try {
|
|
7327
7647
|
read = await readRailwayConfig(service.dir);
|
|
7328
7648
|
} catch (err) {
|
|
7329
|
-
recordExtractionError("infra railway",
|
|
7649
|
+
recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7330
7650
|
continue;
|
|
7331
7651
|
}
|
|
7332
7652
|
if (!read) continue;
|
|
@@ -7336,7 +7656,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7336
7656
|
}
|
|
7337
7657
|
const anchorId = service.node.id;
|
|
7338
7658
|
const { config, relFile, raw } = read;
|
|
7339
|
-
const evidenceFile = toPosix2(
|
|
7659
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7340
7660
|
const add = (edgeType, kind, name) => {
|
|
7341
7661
|
if (!name) return;
|
|
7342
7662
|
const result = emitPlatformResourceEdge(
|
|
@@ -7352,9 +7672,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7352
7672
|
nodesAdded += result.nodesAdded;
|
|
7353
7673
|
edgesAdded += result.edgesAdded;
|
|
7354
7674
|
};
|
|
7355
|
-
add(
|
|
7356
|
-
add(
|
|
7357
|
-
add(
|
|
7675
|
+
add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
|
|
7676
|
+
add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7677
|
+
add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7358
7678
|
}
|
|
7359
7679
|
return { nodesAdded, edgesAdded };
|
|
7360
7680
|
}
|
|
@@ -7362,12 +7682,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7362
7682
|
// src/extract/infra/supabase.ts
|
|
7363
7683
|
init_cjs_shims();
|
|
7364
7684
|
var import_node_fs22 = require("fs");
|
|
7365
|
-
var
|
|
7685
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7366
7686
|
var import_smol_toml4 = require("smol-toml");
|
|
7367
|
-
var
|
|
7687
|
+
var import_types29 = require("@neat.is/types");
|
|
7368
7688
|
async function readSupabaseConfig(dir) {
|
|
7369
|
-
const relFile =
|
|
7370
|
-
const abs =
|
|
7689
|
+
const relFile = import_node_path39.default.join("supabase", "config.toml");
|
|
7690
|
+
const abs = import_node_path39.default.join(dir, relFile);
|
|
7371
7691
|
if (!await exists(abs)) return null;
|
|
7372
7692
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7373
7693
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7381,7 +7701,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7381
7701
|
try {
|
|
7382
7702
|
read = await readSupabaseConfig(service.dir);
|
|
7383
7703
|
} catch (err) {
|
|
7384
|
-
recordExtractionError("infra supabase",
|
|
7704
|
+
recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7385
7705
|
continue;
|
|
7386
7706
|
}
|
|
7387
7707
|
if (!read) continue;
|
|
@@ -7396,7 +7716,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7396
7716
|
});
|
|
7397
7717
|
}
|
|
7398
7718
|
const anchorId = service.node.id;
|
|
7399
|
-
const evidenceFile = toPosix2(
|
|
7719
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7400
7720
|
const add = (edgeType, kind, name) => {
|
|
7401
7721
|
if (!name) return;
|
|
7402
7722
|
const result = emitPlatformResourceEdge(
|
|
@@ -7412,10 +7732,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7412
7732
|
nodesAdded += result.nodesAdded;
|
|
7413
7733
|
edgesAdded += result.edgesAdded;
|
|
7414
7734
|
};
|
|
7415
|
-
add(
|
|
7416
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7417
|
-
if (config.storage) add(
|
|
7418
|
-
if (config.auth) add(
|
|
7735
|
+
add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7736
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7737
|
+
if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7738
|
+
if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7419
7739
|
}
|
|
7420
7740
|
return { nodesAdded, edgesAdded };
|
|
7421
7741
|
}
|
|
@@ -7437,17 +7757,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7437
7757
|
}
|
|
7438
7758
|
|
|
7439
7759
|
// src/extract/index.ts
|
|
7440
|
-
var
|
|
7760
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7441
7761
|
|
|
7442
7762
|
// src/extract/retire.ts
|
|
7443
7763
|
init_cjs_shims();
|
|
7444
7764
|
var import_node_fs23 = require("fs");
|
|
7445
|
-
var
|
|
7446
|
-
var
|
|
7765
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7766
|
+
var import_types30 = require("@neat.is/types");
|
|
7447
7767
|
function dropOrphanedFileNodes(graph) {
|
|
7448
7768
|
const orphans = [];
|
|
7449
7769
|
graph.forEachNode((id, attrs) => {
|
|
7450
|
-
if (attrs.type !==
|
|
7770
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7451
7771
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7452
7772
|
orphans.push(id);
|
|
7453
7773
|
}
|
|
@@ -7460,14 +7780,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7460
7780
|
const bases = [scanPath, ...serviceDirs];
|
|
7461
7781
|
graph.forEachEdge((id, attrs) => {
|
|
7462
7782
|
const edge = attrs;
|
|
7463
|
-
if (edge.provenance !==
|
|
7783
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7464
7784
|
const evidenceFile = edge.evidence?.file;
|
|
7465
7785
|
if (!evidenceFile) return;
|
|
7466
|
-
if (
|
|
7786
|
+
if (import_node_path40.default.isAbsolute(evidenceFile)) {
|
|
7467
7787
|
if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7468
7788
|
return;
|
|
7469
7789
|
}
|
|
7470
|
-
const found = bases.some((base) => (0, import_node_fs23.existsSync)(
|
|
7790
|
+
const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path40.default.join(base, evidenceFile)));
|
|
7471
7791
|
if (!found) toDrop.push(id);
|
|
7472
7792
|
});
|
|
7473
7793
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7509,7 +7829,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7509
7829
|
}
|
|
7510
7830
|
const droppedEntries = drainDroppedExtracted();
|
|
7511
7831
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7512
|
-
const rejectedPath =
|
|
7832
|
+
const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7513
7833
|
try {
|
|
7514
7834
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7515
7835
|
} catch (err) {
|
|
@@ -7544,8 +7864,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7544
7864
|
// src/persist.ts
|
|
7545
7865
|
init_cjs_shims();
|
|
7546
7866
|
var import_node_fs24 = require("fs");
|
|
7547
|
-
var
|
|
7548
|
-
var
|
|
7867
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7868
|
+
var import_types31 = require("@neat.is/types");
|
|
7549
7869
|
var SCHEMA_VERSION = 4;
|
|
7550
7870
|
function migrateV1ToV2(payload) {
|
|
7551
7871
|
const nodes = payload.graph.nodes;
|
|
@@ -7567,12 +7887,12 @@ function migrateV2ToV3(payload) {
|
|
|
7567
7887
|
for (const edge of edges) {
|
|
7568
7888
|
const attrs = edge.attributes;
|
|
7569
7889
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7570
|
-
attrs.provenance =
|
|
7890
|
+
attrs.provenance = import_types31.Provenance.OBSERVED;
|
|
7571
7891
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7572
7892
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7573
7893
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7574
7894
|
if (type && source && target) {
|
|
7575
|
-
const newId = (0,
|
|
7895
|
+
const newId = (0, import_types31.observedEdgeId)(source, target, type);
|
|
7576
7896
|
attrs.id = newId;
|
|
7577
7897
|
if (edge.key) edge.key = newId;
|
|
7578
7898
|
}
|
|
@@ -7581,7 +7901,7 @@ function migrateV2ToV3(payload) {
|
|
|
7581
7901
|
return { ...payload, schemaVersion: 3 };
|
|
7582
7902
|
}
|
|
7583
7903
|
async function ensureDir(filePath) {
|
|
7584
|
-
await import_node_fs24.promises.mkdir(
|
|
7904
|
+
await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
|
|
7585
7905
|
}
|
|
7586
7906
|
async function saveGraphToDisk(graph, outPath) {
|
|
7587
7907
|
await ensureDir(outPath);
|
|
@@ -7664,19 +7984,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7664
7984
|
init_cjs_shims();
|
|
7665
7985
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
7666
7986
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
7667
|
-
var
|
|
7987
|
+
var import_types34 = require("@neat.is/types");
|
|
7668
7988
|
|
|
7669
7989
|
// src/extend/index.ts
|
|
7670
7990
|
init_cjs_shims();
|
|
7671
7991
|
var import_node_fs26 = require("fs");
|
|
7672
|
-
var
|
|
7992
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7673
7993
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
7674
7994
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
7675
7995
|
|
|
7676
7996
|
// src/installers/package-manager.ts
|
|
7677
7997
|
init_cjs_shims();
|
|
7678
7998
|
var import_node_fs25 = require("fs");
|
|
7679
|
-
var
|
|
7999
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7680
8000
|
var import_node_child_process = require("child_process");
|
|
7681
8001
|
var LOCKFILE_PRIORITY = [
|
|
7682
8002
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -7698,22 +8018,22 @@ async function exists2(p) {
|
|
|
7698
8018
|
}
|
|
7699
8019
|
}
|
|
7700
8020
|
async function detectPackageManager(serviceDir) {
|
|
7701
|
-
let dir =
|
|
8021
|
+
let dir = import_node_path43.default.resolve(serviceDir);
|
|
7702
8022
|
const stops = /* @__PURE__ */ new Set();
|
|
7703
8023
|
for (let i = 0; i < 64; i++) {
|
|
7704
8024
|
if (stops.has(dir)) break;
|
|
7705
8025
|
stops.add(dir);
|
|
7706
8026
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
7707
|
-
const lockPath =
|
|
8027
|
+
const lockPath = import_node_path43.default.join(dir, candidate.lockfile);
|
|
7708
8028
|
if (await exists2(lockPath)) {
|
|
7709
8029
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
7710
8030
|
}
|
|
7711
8031
|
}
|
|
7712
|
-
const parent =
|
|
8032
|
+
const parent = import_node_path43.default.dirname(dir);
|
|
7713
8033
|
if (parent === dir) break;
|
|
7714
8034
|
dir = parent;
|
|
7715
8035
|
}
|
|
7716
|
-
return { pm: "npm", cwd:
|
|
8036
|
+
return { pm: "npm", cwd: import_node_path43.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
7717
8037
|
}
|
|
7718
8038
|
async function runPackageManagerInstall(cmd) {
|
|
7719
8039
|
return new Promise((resolve) => {
|
|
@@ -7762,22 +8082,53 @@ async function fileExists2(p) {
|
|
|
7762
8082
|
}
|
|
7763
8083
|
}
|
|
7764
8084
|
async function readPackageJson(scanPath) {
|
|
7765
|
-
const pkgPath =
|
|
8085
|
+
const pkgPath = import_node_path44.default.join(scanPath, "package.json");
|
|
7766
8086
|
const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
|
|
7767
8087
|
return JSON.parse(raw);
|
|
7768
8088
|
}
|
|
8089
|
+
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
8090
|
+
"node_modules",
|
|
8091
|
+
"dist",
|
|
8092
|
+
"build",
|
|
8093
|
+
"out",
|
|
8094
|
+
"coverage",
|
|
8095
|
+
"neat-out"
|
|
8096
|
+
]);
|
|
7769
8097
|
async function findHookFiles(scanPath) {
|
|
7770
|
-
const
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
8098
|
+
const found = [];
|
|
8099
|
+
const walk3 = async (dir) => {
|
|
8100
|
+
const entries = await import_node_fs26.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
8101
|
+
for (const entry of entries) {
|
|
8102
|
+
if (entry.isDirectory()) {
|
|
8103
|
+
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
8104
|
+
await walk3(import_node_path44.default.join(dir, entry.name));
|
|
8105
|
+
} else if (entry.isFile()) {
|
|
8106
|
+
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
8107
|
+
const rel = import_node_path44.default.relative(scanPath, import_node_path44.default.join(dir, entry.name));
|
|
8108
|
+
found.push(rel.split(import_node_path44.default.sep).join("/"));
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
}
|
|
8112
|
+
};
|
|
8113
|
+
await walk3(scanPath);
|
|
8114
|
+
return found.sort();
|
|
8115
|
+
}
|
|
8116
|
+
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
8117
|
+
let fallback = null;
|
|
8118
|
+
for (const file of hookFiles) {
|
|
8119
|
+
const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(scanPath, file), "utf8");
|
|
8120
|
+
const patched = splicedContent(content, snippet2);
|
|
8121
|
+
if (patched !== null) return { file, content, patched };
|
|
8122
|
+
if (fallback === null) fallback = { file, content };
|
|
8123
|
+
}
|
|
8124
|
+
return { file: fallback.file, content: fallback.content, patched: null };
|
|
7774
8125
|
}
|
|
7775
8126
|
function extendLogPath() {
|
|
7776
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8127
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path44.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
7777
8128
|
}
|
|
7778
8129
|
async function appendExtendLog(entry) {
|
|
7779
8130
|
const logPath = extendLogPath();
|
|
7780
|
-
await import_node_fs26.promises.mkdir(
|
|
8131
|
+
await import_node_fs26.promises.mkdir(import_node_path44.default.dirname(logPath), { recursive: true });
|
|
7781
8132
|
await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
7782
8133
|
}
|
|
7783
8134
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -7836,7 +8187,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
7836
8187
|
}
|
|
7837
8188
|
async function describeProjectInstrumentation(ctx) {
|
|
7838
8189
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7839
|
-
const envNeat = await fileExists2(
|
|
8190
|
+
const envNeat = await fileExists2(import_node_path44.default.join(ctx.scanPath, ".env.neat"));
|
|
7840
8191
|
const registryInstrPackages = new Set(
|
|
7841
8192
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
7842
8193
|
);
|
|
@@ -7858,16 +8209,22 @@ async function applyExtension(ctx, args, options) {
|
|
|
7858
8209
|
);
|
|
7859
8210
|
}
|
|
7860
8211
|
for (const file of hookFiles) {
|
|
7861
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8212
|
+
const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(ctx.scanPath, file), "utf8");
|
|
7862
8213
|
if (content.includes(args.registration_snippet)) {
|
|
7863
8214
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
7864
8215
|
}
|
|
7865
8216
|
}
|
|
7866
|
-
const
|
|
7867
|
-
|
|
8217
|
+
const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
|
|
8218
|
+
if (primary.patched === null) {
|
|
8219
|
+
throw new Error(
|
|
8220
|
+
`Could not find instrumentation insertion point in ${hookFiles.join(", ")}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
8221
|
+
);
|
|
8222
|
+
}
|
|
8223
|
+
const primaryFile = primary.file;
|
|
8224
|
+
const primaryPath = import_node_path44.default.join(ctx.scanPath, primaryFile);
|
|
7868
8225
|
const filesTouched = [];
|
|
7869
8226
|
const depsAdded = [];
|
|
7870
|
-
const pkgPath =
|
|
8227
|
+
const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
|
|
7871
8228
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7872
8229
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
7873
8230
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -7875,14 +8232,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
7875
8232
|
filesTouched.push("package.json");
|
|
7876
8233
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
7877
8234
|
}
|
|
7878
|
-
|
|
7879
|
-
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
7880
|
-
if (!patched) {
|
|
7881
|
-
throw new Error(
|
|
7882
|
-
`Could not find instrumentation insertion point in ${primaryFile}. Expected __INSTRUMENTATION_BLOCK__, instrumentations.push(, or new NodeSDK(.`
|
|
7883
|
-
);
|
|
7884
|
-
}
|
|
7885
|
-
await import_node_fs26.promises.writeFile(primaryPath, patched, "utf8");
|
|
8235
|
+
await import_node_fs26.promises.writeFile(primaryPath, primary.patched, "utf8");
|
|
7886
8236
|
filesTouched.push(primaryFile);
|
|
7887
8237
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
7888
8238
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -7913,7 +8263,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7913
8263
|
};
|
|
7914
8264
|
}
|
|
7915
8265
|
for (const file of hookFiles) {
|
|
7916
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8266
|
+
const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(ctx.scanPath, file), "utf8");
|
|
7917
8267
|
if (content.includes(args.registration_snippet)) {
|
|
7918
8268
|
return {
|
|
7919
8269
|
library: args.library,
|
|
@@ -7924,7 +8274,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7924
8274
|
};
|
|
7925
8275
|
}
|
|
7926
8276
|
}
|
|
7927
|
-
const
|
|
8277
|
+
const primary = await pickPrimaryHookFile(ctx.scanPath, hookFiles, args.registration_snippet);
|
|
7928
8278
|
const filesTouched = [];
|
|
7929
8279
|
const depsToAdd = [];
|
|
7930
8280
|
let packageJsonPatch = {};
|
|
@@ -7935,10 +8285,8 @@ async function dryRunExtension(ctx, args) {
|
|
|
7935
8285
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
7936
8286
|
filesTouched.push("package.json");
|
|
7937
8287
|
}
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
if (patched) {
|
|
7941
|
-
filesTouched.push(primaryFile);
|
|
8288
|
+
if (primary.patched !== null) {
|
|
8289
|
+
filesTouched.push(primary.file);
|
|
7942
8290
|
templatePatch = `+ ${args.registration_snippet}`;
|
|
7943
8291
|
} else {
|
|
7944
8292
|
templatePatch = "Could not find insertion point in hook file.";
|
|
@@ -7956,7 +8304,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
7956
8304
|
if (!match) {
|
|
7957
8305
|
return { undone: false, message: "no apply found for library" };
|
|
7958
8306
|
}
|
|
7959
|
-
const pkgPath =
|
|
8307
|
+
const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
|
|
7960
8308
|
if (await fileExists2(pkgPath)) {
|
|
7961
8309
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7962
8310
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -7967,7 +8315,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
7967
8315
|
}
|
|
7968
8316
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7969
8317
|
for (const file of hookFiles) {
|
|
7970
|
-
const filePath =
|
|
8318
|
+
const filePath = import_node_path44.default.join(ctx.scanPath, file);
|
|
7971
8319
|
const content = await import_node_fs26.promises.readFile(filePath, "utf8");
|
|
7972
8320
|
if (content.includes(match.registration_snippet)) {
|
|
7973
8321
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -7983,39 +8331,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
7983
8331
|
|
|
7984
8332
|
// src/divergences.ts
|
|
7985
8333
|
init_cjs_shims();
|
|
7986
|
-
var
|
|
8334
|
+
var import_types32 = require("@neat.is/types");
|
|
7987
8335
|
function bucketKey(source, target, type) {
|
|
7988
8336
|
return `${type}|${source}|${target}`;
|
|
7989
8337
|
}
|
|
7990
8338
|
function bucketSourceFor(graph, edge) {
|
|
7991
|
-
if (edge.type !==
|
|
7992
|
-
const parsed = (0,
|
|
8339
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
|
|
8340
|
+
const parsed = (0, import_types32.parseFileId)(edge.source);
|
|
7993
8341
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7994
8342
|
const target = graph.getNodeAttributes(edge.target);
|
|
7995
|
-
if (target.type !==
|
|
7996
|
-
return (0,
|
|
8343
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
|
|
8344
|
+
return (0, import_types32.serviceId)(parsed.service);
|
|
7997
8345
|
}
|
|
7998
8346
|
function bucketEdges(graph) {
|
|
7999
8347
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8000
8348
|
graph.forEachEdge((id, attrs) => {
|
|
8001
8349
|
const e = attrs;
|
|
8002
|
-
const parsed = (0,
|
|
8350
|
+
const parsed = (0, import_types32.parseEdgeId)(id);
|
|
8003
8351
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8004
8352
|
const source = bucketSourceFor(graph, e);
|
|
8005
8353
|
const key = bucketKey(source, e.target, e.type);
|
|
8006
8354
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8007
8355
|
switch (provenance) {
|
|
8008
|
-
case
|
|
8356
|
+
case import_types32.Provenance.EXTRACTED:
|
|
8009
8357
|
cur.extracted = e;
|
|
8010
8358
|
break;
|
|
8011
|
-
case
|
|
8359
|
+
case import_types32.Provenance.OBSERVED:
|
|
8012
8360
|
cur.observed = e;
|
|
8013
8361
|
break;
|
|
8014
|
-
case
|
|
8362
|
+
case import_types32.Provenance.INFERRED:
|
|
8015
8363
|
cur.inferred = e;
|
|
8016
8364
|
break;
|
|
8017
8365
|
default:
|
|
8018
|
-
if (e.provenance ===
|
|
8366
|
+
if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
|
|
8019
8367
|
}
|
|
8020
8368
|
buckets2.set(key, cur);
|
|
8021
8369
|
});
|
|
@@ -8024,12 +8372,12 @@ function bucketEdges(graph) {
|
|
|
8024
8372
|
function nodeIsFrontier(graph, nodeId) {
|
|
8025
8373
|
if (!graph.hasNode(nodeId)) return false;
|
|
8026
8374
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8027
|
-
return attrs.type ===
|
|
8375
|
+
return attrs.type === import_types32.NodeType.FrontierNode;
|
|
8028
8376
|
}
|
|
8029
8377
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8030
8378
|
if (!graph.hasNode(nodeId)) return false;
|
|
8031
8379
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8032
|
-
return attrs.type ===
|
|
8380
|
+
return attrs.type === import_types32.NodeType.WebSocketChannelNode;
|
|
8033
8381
|
}
|
|
8034
8382
|
function clampConfidence(n) {
|
|
8035
8383
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8049,14 +8397,14 @@ function gradedConfidence(edge) {
|
|
|
8049
8397
|
return clampConfidence(confidenceForEdge(edge));
|
|
8050
8398
|
}
|
|
8051
8399
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8400
|
+
import_types32.EdgeType.CALLS,
|
|
8401
|
+
import_types32.EdgeType.CONNECTS_TO,
|
|
8402
|
+
import_types32.EdgeType.PUBLISHES_TO,
|
|
8403
|
+
import_types32.EdgeType.CONSUMES_FROM
|
|
8056
8404
|
]);
|
|
8057
8405
|
function detectMissingDivergences(graph, bucket) {
|
|
8058
8406
|
const out = [];
|
|
8059
|
-
if (bucket.type ===
|
|
8407
|
+
if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
|
|
8060
8408
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8061
8409
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8062
8410
|
out.push({
|
|
@@ -8097,7 +8445,7 @@ function declaredHostFor(svc) {
|
|
|
8097
8445
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8098
8446
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8099
8447
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8100
|
-
if (e.type ===
|
|
8448
|
+
if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
|
|
8101
8449
|
return true;
|
|
8102
8450
|
}
|
|
8103
8451
|
}
|
|
@@ -8110,10 +8458,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8110
8458
|
const out = [];
|
|
8111
8459
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8112
8460
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8113
|
-
if (edge.type !==
|
|
8114
|
-
if (edge.provenance !==
|
|
8461
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8462
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8115
8463
|
const target = graph.getNodeAttributes(edge.target);
|
|
8116
|
-
if (target.type !==
|
|
8464
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8117
8465
|
const observedHost = target.host?.trim();
|
|
8118
8466
|
if (!observedHost) continue;
|
|
8119
8467
|
if (observedHost === declaredHost) continue;
|
|
@@ -8135,10 +8483,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8135
8483
|
const deps = svc.dependencies ?? {};
|
|
8136
8484
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8137
8485
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8138
|
-
if (edge.type !==
|
|
8139
|
-
if (edge.provenance !==
|
|
8486
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8487
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8140
8488
|
const target = graph.getNodeAttributes(edge.target);
|
|
8141
|
-
if (target.type !==
|
|
8489
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8142
8490
|
for (const pair of compatPairs()) {
|
|
8143
8491
|
if (pair.engine !== target.engine) continue;
|
|
8144
8492
|
const declared = deps[pair.driver];
|
|
@@ -8197,7 +8545,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8197
8545
|
for (const d of all) {
|
|
8198
8546
|
if (d.type !== "host-mismatch") continue;
|
|
8199
8547
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8200
|
-
declaredHalf.add((0,
|
|
8548
|
+
declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
|
|
8201
8549
|
}
|
|
8202
8550
|
if (observedHalf.size === 0) return all;
|
|
8203
8551
|
return all.filter((d) => {
|
|
@@ -8216,7 +8564,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8216
8564
|
}
|
|
8217
8565
|
graph.forEachNode((nodeId, attrs) => {
|
|
8218
8566
|
const n = attrs;
|
|
8219
|
-
if (n.type !==
|
|
8567
|
+
if (n.type !== import_types32.NodeType.ServiceNode) return;
|
|
8220
8568
|
const svc = n;
|
|
8221
8569
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8222
8570
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8250,7 +8598,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8250
8598
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8251
8599
|
return a.target.localeCompare(b.target);
|
|
8252
8600
|
});
|
|
8253
|
-
return
|
|
8601
|
+
return import_types32.DivergenceResultSchema.parse({
|
|
8254
8602
|
divergences: filtered,
|
|
8255
8603
|
totalAffected: filtered.length,
|
|
8256
8604
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8381,23 +8729,23 @@ function canonicalJson(value) {
|
|
|
8381
8729
|
|
|
8382
8730
|
// src/projects.ts
|
|
8383
8731
|
init_cjs_shims();
|
|
8384
|
-
var
|
|
8732
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8385
8733
|
function pathsForProject(project, baseDir) {
|
|
8386
8734
|
if (project === DEFAULT_PROJECT) {
|
|
8387
8735
|
return {
|
|
8388
|
-
snapshotPath:
|
|
8389
|
-
errorsPath:
|
|
8390
|
-
staleEventsPath:
|
|
8391
|
-
embeddingsCachePath:
|
|
8392
|
-
policyViolationsPath:
|
|
8736
|
+
snapshotPath: import_node_path45.default.join(baseDir, "graph.json"),
|
|
8737
|
+
errorsPath: import_node_path45.default.join(baseDir, "errors.ndjson"),
|
|
8738
|
+
staleEventsPath: import_node_path45.default.join(baseDir, "stale-events.ndjson"),
|
|
8739
|
+
embeddingsCachePath: import_node_path45.default.join(baseDir, "embeddings.json"),
|
|
8740
|
+
policyViolationsPath: import_node_path45.default.join(baseDir, "policy-violations.ndjson")
|
|
8393
8741
|
};
|
|
8394
8742
|
}
|
|
8395
8743
|
return {
|
|
8396
|
-
snapshotPath:
|
|
8397
|
-
errorsPath:
|
|
8398
|
-
staleEventsPath:
|
|
8399
|
-
embeddingsCachePath:
|
|
8400
|
-
policyViolationsPath:
|
|
8744
|
+
snapshotPath: import_node_path45.default.join(baseDir, `${project}.json`),
|
|
8745
|
+
errorsPath: import_node_path45.default.join(baseDir, `errors.${project}.ndjson`),
|
|
8746
|
+
staleEventsPath: import_node_path45.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
8747
|
+
embeddingsCachePath: import_node_path45.default.join(baseDir, `embeddings.${project}.json`),
|
|
8748
|
+
policyViolationsPath: import_node_path45.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
8401
8749
|
};
|
|
8402
8750
|
}
|
|
8403
8751
|
var Projects = class {
|
|
@@ -8435,23 +8783,23 @@ var Projects = class {
|
|
|
8435
8783
|
init_cjs_shims();
|
|
8436
8784
|
var import_node_fs28 = require("fs");
|
|
8437
8785
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8438
|
-
var
|
|
8439
|
-
var
|
|
8786
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8787
|
+
var import_types33 = require("@neat.is/types");
|
|
8440
8788
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8441
8789
|
var LOCK_RETRY_MS = 50;
|
|
8442
8790
|
function neatHome() {
|
|
8443
8791
|
const override = process.env.NEAT_HOME;
|
|
8444
|
-
if (override && override.length > 0) return
|
|
8445
|
-
return
|
|
8792
|
+
if (override && override.length > 0) return import_node_path46.default.resolve(override);
|
|
8793
|
+
return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8446
8794
|
}
|
|
8447
8795
|
function registryPath() {
|
|
8448
|
-
return
|
|
8796
|
+
return import_node_path46.default.join(neatHome(), "projects.json");
|
|
8449
8797
|
}
|
|
8450
8798
|
function registryLockPath() {
|
|
8451
|
-
return
|
|
8799
|
+
return import_node_path46.default.join(neatHome(), "projects.json.lock");
|
|
8452
8800
|
}
|
|
8453
8801
|
function daemonPidPath() {
|
|
8454
|
-
return
|
|
8802
|
+
return import_node_path46.default.join(neatHome(), "neatd.pid");
|
|
8455
8803
|
}
|
|
8456
8804
|
function isPidAliveDefault(pid) {
|
|
8457
8805
|
try {
|
|
@@ -8511,7 +8859,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8511
8859
|
}
|
|
8512
8860
|
}
|
|
8513
8861
|
async function normalizeProjectPath(input) {
|
|
8514
|
-
const resolved =
|
|
8862
|
+
const resolved = import_node_path46.default.resolve(input);
|
|
8515
8863
|
try {
|
|
8516
8864
|
return await import_node_fs28.promises.realpath(resolved);
|
|
8517
8865
|
} catch {
|
|
@@ -8519,7 +8867,7 @@ async function normalizeProjectPath(input) {
|
|
|
8519
8867
|
}
|
|
8520
8868
|
}
|
|
8521
8869
|
async function writeAtomically(target, contents) {
|
|
8522
|
-
await import_node_fs28.promises.mkdir(
|
|
8870
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
|
|
8523
8871
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8524
8872
|
const fd = await import_node_fs28.promises.open(tmp, "w");
|
|
8525
8873
|
try {
|
|
@@ -8532,7 +8880,7 @@ async function writeAtomically(target, contents) {
|
|
|
8532
8880
|
}
|
|
8533
8881
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8534
8882
|
const deadline = Date.now() + timeoutMs;
|
|
8535
|
-
await import_node_fs28.promises.mkdir(
|
|
8883
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
|
|
8536
8884
|
let probedHolder = false;
|
|
8537
8885
|
while (true) {
|
|
8538
8886
|
try {
|
|
@@ -8585,10 +8933,10 @@ async function readRegistry() {
|
|
|
8585
8933
|
throw err;
|
|
8586
8934
|
}
|
|
8587
8935
|
const parsed = JSON.parse(raw);
|
|
8588
|
-
return
|
|
8936
|
+
return import_types33.RegistryFileSchema.parse(parsed);
|
|
8589
8937
|
}
|
|
8590
8938
|
async function writeRegistry(reg) {
|
|
8591
|
-
const validated =
|
|
8939
|
+
const validated = import_types33.RegistryFileSchema.parse(reg);
|
|
8592
8940
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8593
8941
|
}
|
|
8594
8942
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -8783,7 +9131,7 @@ init_auth();
|
|
|
8783
9131
|
// src/connectors-config.ts
|
|
8784
9132
|
init_cjs_shims();
|
|
8785
9133
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
8786
|
-
var
|
|
9134
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8787
9135
|
var import_node_fs29 = require("fs");
|
|
8788
9136
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
8789
9137
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -8798,11 +9146,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
8798
9146
|
};
|
|
8799
9147
|
function neatHome2() {
|
|
8800
9148
|
const override = process.env.NEAT_HOME;
|
|
8801
|
-
if (override && override.length > 0) return
|
|
8802
|
-
return
|
|
9149
|
+
if (override && override.length > 0) return import_node_path47.default.resolve(override);
|
|
9150
|
+
return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
|
|
8803
9151
|
}
|
|
8804
9152
|
function connectorsConfigPath(home = neatHome2()) {
|
|
8805
|
-
return
|
|
9153
|
+
return import_node_path47.default.join(home, "connectors.json");
|
|
8806
9154
|
}
|
|
8807
9155
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
8808
9156
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9138,11 +9486,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9138
9486
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9139
9487
|
const parsed = [];
|
|
9140
9488
|
for (const c of candidates) {
|
|
9141
|
-
const r =
|
|
9489
|
+
const r = import_types34.DivergenceTypeSchema.safeParse(c);
|
|
9142
9490
|
if (!r.success) {
|
|
9143
9491
|
return reply.code(400).send({
|
|
9144
9492
|
error: `unknown divergence type "${c}"`,
|
|
9145
|
-
allowed:
|
|
9493
|
+
allowed: import_types34.DivergenceTypeSchema.options
|
|
9146
9494
|
});
|
|
9147
9495
|
}
|
|
9148
9496
|
parsed.push(r.data);
|
|
@@ -9413,7 +9761,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9413
9761
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9414
9762
|
let violations = await log.readAll();
|
|
9415
9763
|
if (req.query.severity) {
|
|
9416
|
-
const sev =
|
|
9764
|
+
const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
|
|
9417
9765
|
if (!sev.success) {
|
|
9418
9766
|
return reply.code(400).send({
|
|
9419
9767
|
error: "invalid severity",
|
|
@@ -9452,7 +9800,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9452
9800
|
scope.post("/policies/check", async (req, reply) => {
|
|
9453
9801
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9454
9802
|
if (!proj) return;
|
|
9455
|
-
const parsed =
|
|
9803
|
+
const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9456
9804
|
if (!parsed.success) {
|
|
9457
9805
|
return reply.code(400).send({
|
|
9458
9806
|
error: "invalid /policies/check body",
|
|
@@ -9586,7 +9934,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9586
9934
|
});
|
|
9587
9935
|
}
|
|
9588
9936
|
async function buildApi(opts) {
|
|
9589
|
-
const app = (0, import_fastify.default)({ logger: false });
|
|
9937
|
+
const app = (0, import_fastify.default)({ logger: false, routerOptions: { maxParamLength: 1024 } });
|
|
9590
9938
|
await app.register(import_cors.default, { origin: true });
|
|
9591
9939
|
const env = readAuthEnv();
|
|
9592
9940
|
const authToken = opts.authToken ?? env.authToken;
|
|
@@ -9718,21 +10066,21 @@ init_otel_grpc();
|
|
|
9718
10066
|
// src/daemon.ts
|
|
9719
10067
|
init_cjs_shims();
|
|
9720
10068
|
var import_node_fs31 = require("fs");
|
|
9721
|
-
var
|
|
10069
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
9722
10070
|
var import_node_module = require("module");
|
|
9723
10071
|
init_otel();
|
|
9724
10072
|
|
|
9725
10073
|
// src/connectors/index.ts
|
|
9726
10074
|
init_cjs_shims();
|
|
9727
|
-
var
|
|
10075
|
+
var import_types35 = require("@neat.is/types");
|
|
9728
10076
|
var NO_ENV = "unknown";
|
|
9729
10077
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
9730
10078
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9731
10079
|
const sites = [];
|
|
9732
10080
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
9733
10081
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9734
|
-
if (edge.provenance !==
|
|
9735
|
-
const parsed = (0,
|
|
10082
|
+
if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
|
|
10083
|
+
const parsed = (0, import_types35.parseFileId)(edge.source);
|
|
9736
10084
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
9737
10085
|
const site = { relPath: edge.evidence.file };
|
|
9738
10086
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -9743,7 +10091,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
9743
10091
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
9744
10092
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9745
10093
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
9746
|
-
if (attrs.type !==
|
|
10094
|
+
if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
|
|
9747
10095
|
const site = { relPath: attrs.path };
|
|
9748
10096
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
9749
10097
|
return site;
|
|
@@ -9771,8 +10119,8 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
9771
10119
|
line: callSite.line
|
|
9772
10120
|
} : void 0;
|
|
9773
10121
|
const calls = Math.trunc(signal.callCount);
|
|
9774
|
-
if (calls < 1) continue;
|
|
9775
|
-
const errors = Math.min(Math.max(Math.trunc(signal.errorCount), 0), calls);
|
|
10122
|
+
if (!Number.isFinite(calls) || calls < 1) continue;
|
|
10123
|
+
const errors = Number.isFinite(signal.errorCount) ? Math.min(Math.max(Math.trunc(signal.errorCount), 0), calls) : 0;
|
|
9776
10124
|
let created = false;
|
|
9777
10125
|
let ok = true;
|
|
9778
10126
|
for (let i = 0; i < calls; i++) {
|
|
@@ -9917,7 +10265,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
9917
10265
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
9918
10266
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
9919
10267
|
// pg_stat_statements connection (see module header above).
|
|
9920
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
10268
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
10269
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
10270
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
10271
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
10272
|
+
// conservative pending a documented Drains-API rate limit.
|
|
10273
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
9921
10274
|
};
|
|
9922
10275
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
9923
10276
|
function defaultRateLimitFor(provider) {
|
|
@@ -10209,17 +10562,20 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10209
10562
|
// src/connectors/supabase/map.ts
|
|
10210
10563
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10211
10564
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10212
|
-
function targetFromRestPath(
|
|
10213
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10565
|
+
function targetFromRestPath(path52) {
|
|
10566
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path52);
|
|
10214
10567
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10215
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10568
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path52);
|
|
10216
10569
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10217
10570
|
return null;
|
|
10218
10571
|
}
|
|
10219
10572
|
var ERROR_STATUS_THRESHOLD = 500;
|
|
10220
10573
|
function mapEdgeLogRowsToSignals(rows) {
|
|
10221
10574
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
10575
|
+
if (!Array.isArray(rows)) return [];
|
|
10222
10576
|
for (const row of rows) {
|
|
10577
|
+
if (!row || typeof row !== "object") continue;
|
|
10578
|
+
if (typeof row.path !== "string" || typeof row.timestamp !== "string") continue;
|
|
10223
10579
|
const target = targetFromRestPath(row.path);
|
|
10224
10580
|
if (!target) continue;
|
|
10225
10581
|
const key = `${target.targetKind}:${target.name}`;
|
|
@@ -10260,9 +10616,12 @@ function tableNameFromQueryText(query) {
|
|
|
10260
10616
|
function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
|
|
10261
10617
|
const signals = [];
|
|
10262
10618
|
const seen = /* @__PURE__ */ new Set();
|
|
10619
|
+
if (!Array.isArray(rows)) return signals;
|
|
10263
10620
|
for (const row of rows) {
|
|
10264
|
-
|
|
10621
|
+
if (!row || typeof row !== "object" || typeof row.queryid !== "string") continue;
|
|
10265
10622
|
const calls = Number(row.calls);
|
|
10623
|
+
if (!Number.isFinite(calls)) continue;
|
|
10624
|
+
seen.add(row.queryid);
|
|
10266
10625
|
const prior = previous.get(row.queryid);
|
|
10267
10626
|
previous.set(row.queryid, { calls });
|
|
10268
10627
|
if (!prior || calls < prior.calls) continue;
|
|
@@ -10315,23 +10674,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10315
10674
|
|
|
10316
10675
|
// src/connectors/supabase/resolve.ts
|
|
10317
10676
|
init_cjs_shims();
|
|
10318
|
-
var
|
|
10677
|
+
var import_types37 = require("@neat.is/types");
|
|
10319
10678
|
function createSupabaseResolveTarget(graph, config) {
|
|
10320
10679
|
return (signal, _ctx) => {
|
|
10321
10680
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10322
10681
|
return null;
|
|
10323
10682
|
}
|
|
10324
|
-
const subResourceId = (0,
|
|
10683
|
+
const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10325
10684
|
if (graph.hasNode(subResourceId)) {
|
|
10326
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10685
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10327
10686
|
}
|
|
10328
|
-
const bareResourceId = (0,
|
|
10687
|
+
const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
|
|
10329
10688
|
if (graph.hasNode(bareResourceId)) {
|
|
10330
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10689
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10331
10690
|
}
|
|
10332
|
-
const projectLevelId = (0,
|
|
10691
|
+
const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
|
|
10333
10692
|
if (graph.hasNode(projectLevelId)) {
|
|
10334
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10693
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10335
10694
|
}
|
|
10336
10695
|
return null;
|
|
10337
10696
|
};
|
|
@@ -10424,7 +10783,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10424
10783
|
|
|
10425
10784
|
// src/connectors/railway/index.ts
|
|
10426
10785
|
init_cjs_shims();
|
|
10427
|
-
var
|
|
10786
|
+
var import_types41 = require("@neat.is/types");
|
|
10428
10787
|
|
|
10429
10788
|
// src/connectors/railway/client.ts
|
|
10430
10789
|
init_cjs_shims();
|
|
@@ -10550,7 +10909,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10550
10909
|
const out = [];
|
|
10551
10910
|
graph.forEachNode((_id, attrs) => {
|
|
10552
10911
|
const node = attrs;
|
|
10553
|
-
if (node.type !==
|
|
10912
|
+
if (node.type !== import_types41.NodeType.RouteNode) return;
|
|
10554
10913
|
const route = attrs;
|
|
10555
10914
|
if (route.service !== serviceName) return;
|
|
10556
10915
|
out.push({
|
|
@@ -10586,7 +10945,12 @@ function upsertBucket(buckets2, key, isError, timestamp, build) {
|
|
|
10586
10945
|
}
|
|
10587
10946
|
function mapRailwayHttpLogsToSignals(entries, routeIndex) {
|
|
10588
10947
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
10948
|
+
if (!Array.isArray(entries)) return [];
|
|
10589
10949
|
for (const entry of entries) {
|
|
10950
|
+
if (!entry || typeof entry !== "object") continue;
|
|
10951
|
+
if (typeof entry.method !== "string" || typeof entry.path !== "string" || typeof entry.timestamp !== "string") {
|
|
10952
|
+
continue;
|
|
10953
|
+
}
|
|
10590
10954
|
const method = entry.method.toUpperCase();
|
|
10591
10955
|
const normalizedPath = normalizePathTemplate(entry.path);
|
|
10592
10956
|
const match = findRailwayRoute(routeIndex, method, normalizedPath);
|
|
@@ -10625,8 +10989,11 @@ function mapRailwayHttpLogsToSignals(entries, routeIndex) {
|
|
|
10625
10989
|
}
|
|
10626
10990
|
function mapRailwayNetworkFlowLogsToSignals(entries) {
|
|
10627
10991
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
10992
|
+
if (!Array.isArray(entries)) return [];
|
|
10628
10993
|
for (const entry of entries) {
|
|
10629
|
-
if (!entry
|
|
10994
|
+
if (!entry || typeof entry !== "object") continue;
|
|
10995
|
+
if (typeof entry.peerServiceId !== "string" || entry.peerServiceId.length === 0) continue;
|
|
10996
|
+
if (typeof entry.timestamp !== "string") continue;
|
|
10630
10997
|
const isError = entry.dropCause !== null && entry.dropCause !== "";
|
|
10631
10998
|
upsertBucket(buckets2, entry.peerServiceId, isError, entry.timestamp, () => ({
|
|
10632
10999
|
targetKind: PEER_SERVICE_TARGET_KIND,
|
|
@@ -10646,12 +11013,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10646
11013
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10647
11014
|
if (!serviceName) return null;
|
|
10648
11015
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10649
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11016
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
|
|
10650
11017
|
}
|
|
10651
11018
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10652
11019
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10653
11020
|
if (!peerName) return null;
|
|
10654
|
-
return { targetNodeId: (0,
|
|
11021
|
+
return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
|
|
10655
11022
|
}
|
|
10656
11023
|
return null;
|
|
10657
11024
|
};
|
|
@@ -10754,7 +11121,7 @@ async function fetchHttpRequestLogEntries(creds, sinceIso) {
|
|
|
10754
11121
|
throw new Error(`Cloud Logging entries.list failed: ${res.status} ${res.statusText}`);
|
|
10755
11122
|
}
|
|
10756
11123
|
const json = await res.json();
|
|
10757
|
-
out.push(...json.entries
|
|
11124
|
+
if (Array.isArray(json.entries)) out.push(...json.entries);
|
|
10758
11125
|
if (!json.nextPageToken) break;
|
|
10759
11126
|
pageToken = json.nextPageToken;
|
|
10760
11127
|
}
|
|
@@ -10775,9 +11142,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
10775
11142
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
10776
11143
|
if (secondSep === -1) return null;
|
|
10777
11144
|
const method = rest.slice(0, secondSep);
|
|
10778
|
-
const
|
|
10779
|
-
if (!resourceName || !method || !
|
|
10780
|
-
return { resourceName, method, path:
|
|
11145
|
+
const path52 = rest.slice(secondSep + 1);
|
|
11146
|
+
if (!resourceName || !method || !path52) return null;
|
|
11147
|
+
return { resourceName, method, path: path52 };
|
|
10781
11148
|
}
|
|
10782
11149
|
function resourceNameFor(type, labels) {
|
|
10783
11150
|
if (!labels) return null;
|
|
@@ -10791,7 +11158,7 @@ function resourceNameFor(type, labels) {
|
|
|
10791
11158
|
}
|
|
10792
11159
|
}
|
|
10793
11160
|
function pathFromRequestUrl(requestUrl) {
|
|
10794
|
-
if (
|
|
11161
|
+
if (typeof requestUrl !== "string" || requestUrl.length === 0) return null;
|
|
10795
11162
|
if (requestUrl.startsWith("/")) {
|
|
10796
11163
|
const withoutQuery = requestUrl.split("?")[0];
|
|
10797
11164
|
return withoutQuery && withoutQuery.length > 0 ? withoutQuery : "/";
|
|
@@ -10806,22 +11173,23 @@ function pathFromRequestUrl(requestUrl) {
|
|
|
10806
11173
|
}
|
|
10807
11174
|
var ERROR_STATUS_THRESHOLD2 = 500;
|
|
10808
11175
|
function mapLogEntryToSignal(entry) {
|
|
11176
|
+
if (!entry || typeof entry !== "object") return null;
|
|
10809
11177
|
const resourceType = entry.resource?.type;
|
|
10810
11178
|
if (!resourceType || !isFirebaseResourceType(resourceType)) return null;
|
|
10811
11179
|
const resourceName = resourceNameFor(resourceType, entry.resource?.labels);
|
|
10812
11180
|
if (!resourceName) return null;
|
|
10813
11181
|
const req = entry.httpRequest;
|
|
10814
11182
|
if (!req) return null;
|
|
10815
|
-
if (
|
|
11183
|
+
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
10816
11184
|
const method = req.requestMethod.toUpperCase();
|
|
10817
|
-
const
|
|
10818
|
-
if (
|
|
11185
|
+
const path52 = pathFromRequestUrl(req.requestUrl);
|
|
11186
|
+
if (path52 === null) return null;
|
|
10819
11187
|
const timestamp = entry.timestamp;
|
|
10820
|
-
if (
|
|
11188
|
+
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
10821
11189
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
10822
11190
|
return {
|
|
10823
11191
|
targetKind: resourceType,
|
|
10824
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11192
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path52 }),
|
|
10825
11193
|
callCount: 1,
|
|
10826
11194
|
errorCount: isError ? 1 : 0,
|
|
10827
11195
|
lastObservedIso: timestamp
|
|
@@ -10838,7 +11206,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
10838
11206
|
|
|
10839
11207
|
// src/connectors/firebase/resolve.ts
|
|
10840
11208
|
init_cjs_shims();
|
|
10841
|
-
var
|
|
11209
|
+
var import_types42 = require("@neat.is/types");
|
|
10842
11210
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
10843
11211
|
switch (resourceType) {
|
|
10844
11212
|
case "cloud_function":
|
|
@@ -10853,7 +11221,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
10853
11221
|
const entries = [];
|
|
10854
11222
|
graph.forEachNode((_id, attrs) => {
|
|
10855
11223
|
const node = attrs;
|
|
10856
|
-
if (node.type !==
|
|
11224
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10857
11225
|
const route = attrs;
|
|
10858
11226
|
if (route.service !== serviceName) return;
|
|
10859
11227
|
entries.push({
|
|
@@ -10885,7 +11253,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
10885
11253
|
return {
|
|
10886
11254
|
targetNodeId: match.routeNodeId,
|
|
10887
11255
|
serviceName,
|
|
10888
|
-
edgeType:
|
|
11256
|
+
edgeType: import_types42.EdgeType.CALLS
|
|
10889
11257
|
};
|
|
10890
11258
|
};
|
|
10891
11259
|
}
|
|
@@ -10912,7 +11280,7 @@ init_cjs_shims();
|
|
|
10912
11280
|
|
|
10913
11281
|
// src/connectors/cloudflare/connector.ts
|
|
10914
11282
|
init_cjs_shims();
|
|
10915
|
-
var
|
|
11283
|
+
var import_types44 = require("@neat.is/types");
|
|
10916
11284
|
|
|
10917
11285
|
// src/connectors/cloudflare/client.ts
|
|
10918
11286
|
init_cjs_shims();
|
|
@@ -10969,7 +11337,7 @@ async function queryWorkerInvocations(ctx, config, window, fetchImpl = fetch) {
|
|
|
10969
11337
|
throw new Error(`cloudflare connector: telemetry query returned an error (${message})`);
|
|
10970
11338
|
}
|
|
10971
11339
|
const events = payload.result?.events?.events;
|
|
10972
|
-
if (events
|
|
11340
|
+
if (!Array.isArray(events)) {
|
|
10973
11341
|
console.warn(
|
|
10974
11342
|
"[neat connector] cloudflare: telemetry query returned success:true but no result.events.events array \u2014 the response shape may have changed; treating as zero events this tick"
|
|
10975
11343
|
);
|
|
@@ -10999,7 +11367,7 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
|
|
|
10999
11367
|
]);
|
|
11000
11368
|
var LEADING_TOKEN_RE = /^(\S+)\s+\S/;
|
|
11001
11369
|
function parseHttpMethodFromTrigger(trigger) {
|
|
11002
|
-
if (
|
|
11370
|
+
if (typeof trigger !== "string") return null;
|
|
11003
11371
|
const match = LEADING_TOKEN_RE.exec(trigger.trim());
|
|
11004
11372
|
const token = match?.[1];
|
|
11005
11373
|
if (!token) return null;
|
|
@@ -11015,25 +11383,28 @@ function parsePathFromTrigger(trigger) {
|
|
|
11015
11383
|
}
|
|
11016
11384
|
var ERROR_STATUS_THRESHOLD3 = 500;
|
|
11017
11385
|
function mapEventToSignal(event) {
|
|
11386
|
+
if (!event || typeof event !== "object") return null;
|
|
11018
11387
|
const metadata = event.$metadata;
|
|
11019
11388
|
const workers = event.$workers;
|
|
11020
11389
|
const method = parseHttpMethodFromTrigger(metadata?.trigger);
|
|
11021
11390
|
if (!method) return null;
|
|
11022
11391
|
const scriptName = workers?.scriptName ?? metadata?.service;
|
|
11023
|
-
if (
|
|
11392
|
+
if (typeof scriptName !== "string" || scriptName.length === 0) return null;
|
|
11024
11393
|
const timestampMs = event.timestamp ?? metadata?.startTime;
|
|
11025
11394
|
if (typeof timestampMs !== "number" || !Number.isFinite(timestampMs)) return null;
|
|
11395
|
+
const observedAt = new Date(timestampMs);
|
|
11396
|
+
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11026
11397
|
const statusCode = metadata?.statusCode;
|
|
11027
11398
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11028
|
-
const
|
|
11399
|
+
const path52 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11029
11400
|
return {
|
|
11030
11401
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11031
11402
|
targetName: scriptName,
|
|
11032
11403
|
callCount: 1,
|
|
11033
11404
|
errorCount: isError ? 1 : 0,
|
|
11034
|
-
lastObservedIso:
|
|
11405
|
+
lastObservedIso: observedAt.toISOString(),
|
|
11035
11406
|
method,
|
|
11036
|
-
...
|
|
11407
|
+
...path52 ? { path: path52 } : {},
|
|
11037
11408
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11038
11409
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11039
11410
|
};
|
|
@@ -11073,19 +11444,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11073
11444
|
graph.forEachNode((id, attrs) => {
|
|
11074
11445
|
if (found) return;
|
|
11075
11446
|
const a = attrs;
|
|
11076
|
-
if (a.type ===
|
|
11447
|
+
if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11077
11448
|
found = id;
|
|
11078
11449
|
}
|
|
11079
11450
|
});
|
|
11080
11451
|
return found;
|
|
11081
11452
|
}
|
|
11082
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11083
|
-
const normalizedPath = normalizePathTemplate(
|
|
11453
|
+
function findMatchingRouteNode(graph, serviceName, method, path52) {
|
|
11454
|
+
const normalizedPath = normalizePathTemplate(path52);
|
|
11084
11455
|
let found = null;
|
|
11085
11456
|
graph.forEachNode((id, attrs) => {
|
|
11086
11457
|
if (found) return;
|
|
11087
11458
|
const a = attrs;
|
|
11088
|
-
if (a.type !==
|
|
11459
|
+
if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11089
11460
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11090
11461
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11091
11462
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11097,18 +11468,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11097
11468
|
return (signal) => {
|
|
11098
11469
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11099
11470
|
const scriptName = signal.targetName;
|
|
11100
|
-
const { method, path:
|
|
11471
|
+
const { method, path: path52 } = signal;
|
|
11101
11472
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11102
|
-
if (!method || !
|
|
11103
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11473
|
+
if (!method || !path52) return wholeFileId;
|
|
11474
|
+
return findMatchingRouteNode(graph, serviceName, method, path52) ?? wholeFileId;
|
|
11104
11475
|
};
|
|
11105
11476
|
const mapping = config.workers?.[scriptName];
|
|
11106
11477
|
if (mapping) {
|
|
11107
|
-
const wholeFileId = (0,
|
|
11478
|
+
const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
|
|
11108
11479
|
return {
|
|
11109
11480
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11110
11481
|
serviceName: mapping.service,
|
|
11111
|
-
edgeType:
|
|
11482
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11112
11483
|
};
|
|
11113
11484
|
}
|
|
11114
11485
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11117,18 +11488,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11117
11488
|
return {
|
|
11118
11489
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11119
11490
|
serviceName: fileNode.service,
|
|
11120
|
-
edgeType:
|
|
11491
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11121
11492
|
};
|
|
11122
11493
|
}
|
|
11123
11494
|
return {
|
|
11124
|
-
targetNodeId: (0,
|
|
11495
|
+
targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
|
|
11125
11496
|
serviceName: scriptName,
|
|
11126
|
-
edgeType:
|
|
11497
|
+
edgeType: import_types44.EdgeType.CALLS,
|
|
11127
11498
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11128
11499
|
};
|
|
11129
11500
|
};
|
|
11130
11501
|
}
|
|
11131
11502
|
|
|
11503
|
+
// src/connectors/vercel/index.ts
|
|
11504
|
+
init_cjs_shims();
|
|
11505
|
+
|
|
11506
|
+
// src/connectors/vercel/client.ts
|
|
11507
|
+
init_cjs_shims();
|
|
11508
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
11509
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
11510
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
11511
|
+
function apiBase(config) {
|
|
11512
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
11513
|
+
}
|
|
11514
|
+
function teamQuery(config) {
|
|
11515
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
11516
|
+
}
|
|
11517
|
+
function drainDelivery(config, otelToken) {
|
|
11518
|
+
return {
|
|
11519
|
+
type: "http",
|
|
11520
|
+
endpoint: config.endpoint,
|
|
11521
|
+
encoding: "json",
|
|
11522
|
+
headers: bearerAuthHeader(otelToken),
|
|
11523
|
+
...config.secret ? { secret: config.secret } : {}
|
|
11524
|
+
};
|
|
11525
|
+
}
|
|
11526
|
+
async function describeError(res) {
|
|
11527
|
+
try {
|
|
11528
|
+
const data = await res.json();
|
|
11529
|
+
const message = data?.error?.message;
|
|
11530
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
11531
|
+
} catch {
|
|
11532
|
+
return "";
|
|
11533
|
+
}
|
|
11534
|
+
}
|
|
11535
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
11536
|
+
const projectIds = config.projectIds ?? [];
|
|
11537
|
+
const body = {
|
|
11538
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
11539
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
11540
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
11541
|
+
schemas: TRACE_SCHEMAS,
|
|
11542
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
11543
|
+
source: { kind: "self-served" }
|
|
11544
|
+
};
|
|
11545
|
+
const res = await junctionFetch(
|
|
11546
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
11547
|
+
{
|
|
11548
|
+
method: "POST",
|
|
11549
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11550
|
+
body: JSON.stringify(body)
|
|
11551
|
+
},
|
|
11552
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11553
|
+
);
|
|
11554
|
+
if (!res.ok) {
|
|
11555
|
+
throw new Error(
|
|
11556
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11557
|
+
);
|
|
11558
|
+
}
|
|
11559
|
+
const payload = await res.json().catch(() => null);
|
|
11560
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
11561
|
+
throw new Error(
|
|
11562
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
11563
|
+
);
|
|
11564
|
+
}
|
|
11565
|
+
return {
|
|
11566
|
+
id: payload.id,
|
|
11567
|
+
...payload.status ? { status: payload.status } : {},
|
|
11568
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
11569
|
+
};
|
|
11570
|
+
}
|
|
11571
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
11572
|
+
const res = await junctionFetch(
|
|
11573
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
11574
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
11575
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11576
|
+
);
|
|
11577
|
+
if (res.ok || res.status === 404) return;
|
|
11578
|
+
throw new Error(
|
|
11579
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11580
|
+
);
|
|
11581
|
+
}
|
|
11582
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
11583
|
+
const res = await junctionFetch(
|
|
11584
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
11585
|
+
{
|
|
11586
|
+
method: "POST",
|
|
11587
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11588
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
11589
|
+
},
|
|
11590
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11591
|
+
);
|
|
11592
|
+
if (res.status === 401 || res.status === 403) {
|
|
11593
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
11594
|
+
}
|
|
11595
|
+
if (!res.ok) {
|
|
11596
|
+
return {
|
|
11597
|
+
status: "failure",
|
|
11598
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11599
|
+
};
|
|
11600
|
+
}
|
|
11601
|
+
const payload = await res.json().catch(() => null);
|
|
11602
|
+
return {
|
|
11603
|
+
...payload?.status ? { status: payload.status } : {},
|
|
11604
|
+
...payload?.error ? { error: payload.error } : {},
|
|
11605
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
11606
|
+
};
|
|
11607
|
+
}
|
|
11608
|
+
|
|
11132
11609
|
// src/connectors/registry.ts
|
|
11133
11610
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11134
11611
|
async function authProbe(input) {
|
|
@@ -11280,6 +11757,91 @@ var PROVIDER_DISPATCH = {
|
|
|
11280
11757
|
}
|
|
11281
11758
|
}
|
|
11282
11759
|
};
|
|
11760
|
+
function vercelCredsFrom(credentials) {
|
|
11761
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
11762
|
+
}
|
|
11763
|
+
function vercelConfigFromOptions(options) {
|
|
11764
|
+
const raw = options.projectIds;
|
|
11765
|
+
let projectIds;
|
|
11766
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
11767
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11768
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
11769
|
+
}
|
|
11770
|
+
return {
|
|
11771
|
+
teamId: String(options.teamId ?? ""),
|
|
11772
|
+
endpoint: String(options.endpoint ?? ""),
|
|
11773
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
11774
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
11775
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
11776
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
11777
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
11778
|
+
};
|
|
11779
|
+
}
|
|
11780
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
11781
|
+
vercel: {
|
|
11782
|
+
provider: "vercel",
|
|
11783
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
11784
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
11785
|
+
primaryCredentialKey: "token",
|
|
11786
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
11787
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
11788
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
11789
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
11790
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
11791
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
11792
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
11793
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
11794
|
+
const result = await testVercelDrainDelivery(
|
|
11795
|
+
vercelConfigFromOptions(options),
|
|
11796
|
+
vercelCredsFrom(credentials),
|
|
11797
|
+
fetchImpl
|
|
11798
|
+
);
|
|
11799
|
+
if (result.status === "success") return { ok: true };
|
|
11800
|
+
return {
|
|
11801
|
+
ok: false,
|
|
11802
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
11803
|
+
};
|
|
11804
|
+
},
|
|
11805
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
11806
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
11807
|
+
// not a failure (the entry still points at a real drain).
|
|
11808
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
11809
|
+
try {
|
|
11810
|
+
const created = await createVercelDrain(
|
|
11811
|
+
vercelConfigFromOptions(options),
|
|
11812
|
+
vercelCredsFrom(credentials),
|
|
11813
|
+
fetchImpl
|
|
11814
|
+
);
|
|
11815
|
+
const note = created.status && created.status !== "enabled" ? `the drain was created but its status is "${created.status}"${created.disabledReason ? ` (${created.disabledReason})` : ""} \u2014 check the Vercel dashboard` : void 0;
|
|
11816
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
11817
|
+
} catch (err) {
|
|
11818
|
+
return { ok: false, reason: err.message };
|
|
11819
|
+
}
|
|
11820
|
+
},
|
|
11821
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
11822
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
11823
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
11824
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
11825
|
+
if (!drainId) {
|
|
11826
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
11827
|
+
}
|
|
11828
|
+
try {
|
|
11829
|
+
await deleteVercelDrain(
|
|
11830
|
+
vercelConfigFromOptions(options),
|
|
11831
|
+
drainId,
|
|
11832
|
+
vercelCredsFrom(credentials),
|
|
11833
|
+
fetchImpl
|
|
11834
|
+
);
|
|
11835
|
+
return { ok: true };
|
|
11836
|
+
} catch (err) {
|
|
11837
|
+
return { ok: false, reason: err.message };
|
|
11838
|
+
}
|
|
11839
|
+
}
|
|
11840
|
+
}
|
|
11841
|
+
};
|
|
11842
|
+
function isPushProvider(provider) {
|
|
11843
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
11844
|
+
}
|
|
11283
11845
|
function resolveEntryCredentials(dispatch, entry, env) {
|
|
11284
11846
|
let credentials;
|
|
11285
11847
|
try {
|
|
@@ -11302,6 +11864,13 @@ function resolveEntryCredentials(dispatch, entry, env) {
|
|
|
11302
11864
|
function buildRegistration(entry, graph, env = process.env) {
|
|
11303
11865
|
const dispatch = PROVIDER_DISPATCH[entry.provider];
|
|
11304
11866
|
if (!dispatch) {
|
|
11867
|
+
if (isPushProvider(entry.provider)) {
|
|
11868
|
+
return {
|
|
11869
|
+
ok: false,
|
|
11870
|
+
push: true,
|
|
11871
|
+
reason: `push provider "${entry.provider}" ingests via the OTLP receiver \u2014 nothing to poll`
|
|
11872
|
+
};
|
|
11873
|
+
}
|
|
11305
11874
|
return { ok: false, reason: `unknown provider "${entry.provider}"` };
|
|
11306
11875
|
}
|
|
11307
11876
|
const creds = resolveEntryCredentials(dispatch, entry, env);
|
|
@@ -11352,7 +11921,7 @@ async function loadConnectorRegistrations(input) {
|
|
|
11352
11921
|
if (!connectorMatchesProject(entry, project)) continue;
|
|
11353
11922
|
const result = buildRegistration(entry, graph, env);
|
|
11354
11923
|
if (result.ok) registrations.push(result.registration);
|
|
11355
|
-
else onSkip?.(entry, result.reason);
|
|
11924
|
+
else if (!result.push) onSkip?.(entry, result.reason);
|
|
11356
11925
|
}
|
|
11357
11926
|
return registrations;
|
|
11358
11927
|
}
|
|
@@ -11363,7 +11932,7 @@ init_auth();
|
|
|
11363
11932
|
// src/unrouted.ts
|
|
11364
11933
|
init_cjs_shims();
|
|
11365
11934
|
var import_node_fs30 = require("fs");
|
|
11366
|
-
var
|
|
11935
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11367
11936
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
11368
11937
|
return {
|
|
11369
11938
|
timestamp: now.toISOString(),
|
|
@@ -11373,34 +11942,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
11373
11942
|
};
|
|
11374
11943
|
}
|
|
11375
11944
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
11376
|
-
const target =
|
|
11945
|
+
const target = import_node_path50.default.join(neatHome3, "errors.ndjson");
|
|
11377
11946
|
await import_node_fs30.promises.mkdir(neatHome3, { recursive: true });
|
|
11378
11947
|
await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
11379
11948
|
}
|
|
11380
11949
|
function unroutedErrorsPath(neatHome3) {
|
|
11381
|
-
return
|
|
11950
|
+
return import_node_path50.default.join(neatHome3, "errors.ndjson");
|
|
11382
11951
|
}
|
|
11383
11952
|
|
|
11384
11953
|
// src/daemon.ts
|
|
11385
|
-
var
|
|
11954
|
+
var import_types47 = require("@neat.is/types");
|
|
11386
11955
|
function daemonJsonPath(scanPath) {
|
|
11387
|
-
return
|
|
11956
|
+
return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
|
|
11388
11957
|
}
|
|
11389
11958
|
function daemonsDiscoveryDir(home) {
|
|
11390
11959
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11391
|
-
return
|
|
11960
|
+
return import_node_path51.default.join(base, "daemons");
|
|
11392
11961
|
}
|
|
11393
11962
|
function daemonDiscoveryPath(project, home) {
|
|
11394
|
-
return
|
|
11963
|
+
return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11395
11964
|
}
|
|
11396
11965
|
function sanitizeDiscoveryName(project) {
|
|
11397
11966
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11398
11967
|
}
|
|
11399
11968
|
function neatHomeFromEnv() {
|
|
11400
11969
|
const env = process.env.NEAT_HOME;
|
|
11401
|
-
if (env && env.length > 0) return
|
|
11970
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11402
11971
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11403
|
-
return
|
|
11972
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11404
11973
|
}
|
|
11405
11974
|
function resolveNeatVersion() {
|
|
11406
11975
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -11455,11 +12024,11 @@ function teardownSlot(slot) {
|
|
|
11455
12024
|
}
|
|
11456
12025
|
}
|
|
11457
12026
|
function neatHomeFor(opts) {
|
|
11458
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
12027
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
|
|
11459
12028
|
const env = process.env.NEAT_HOME;
|
|
11460
|
-
if (env && env.length > 0) return
|
|
12029
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11461
12030
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11462
|
-
return
|
|
12031
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11463
12032
|
}
|
|
11464
12033
|
function routeSpanToProject(serviceName, projects) {
|
|
11465
12034
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -11503,11 +12072,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
11503
12072
|
if (!serviceName) return true;
|
|
11504
12073
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
11505
12074
|
return graph.someNode(
|
|
11506
|
-
(_id, attrs) => attrs.type ===
|
|
12075
|
+
(_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
|
|
11507
12076
|
);
|
|
11508
12077
|
}
|
|
11509
12078
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
11510
|
-
const paths = pathsForProject(entry.name,
|
|
12079
|
+
const paths = pathsForProject(entry.name, import_node_path51.default.join(entry.path, "neat-out"));
|
|
11511
12080
|
try {
|
|
11512
12081
|
const stat = await import_node_fs31.promises.stat(entry.path);
|
|
11513
12082
|
if (!stat.isDirectory()) {
|
|
@@ -11638,7 +12207,7 @@ async function startDaemon(opts = {}) {
|
|
|
11638
12207
|
const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
|
|
11639
12208
|
const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
|
|
11640
12209
|
const singleProject = projectArg;
|
|
11641
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
12210
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
|
|
11642
12211
|
if (singleProject && !singleProjectPath) {
|
|
11643
12212
|
throw new Error(
|
|
11644
12213
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -11653,7 +12222,7 @@ async function startDaemon(opts = {}) {
|
|
|
11653
12222
|
);
|
|
11654
12223
|
}
|
|
11655
12224
|
}
|
|
11656
|
-
const pidPath =
|
|
12225
|
+
const pidPath = import_node_path51.default.join(home, "neatd.pid");
|
|
11657
12226
|
await writeAtomically(pidPath, `${process.pid}
|
|
11658
12227
|
`);
|
|
11659
12228
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12060,8 +12629,8 @@ async function startDaemon(opts = {}) {
|
|
|
12060
12629
|
let registryWatcher = null;
|
|
12061
12630
|
let reloadTimer = null;
|
|
12062
12631
|
if (!singleProject) try {
|
|
12063
|
-
const regDir =
|
|
12064
|
-
const regBase =
|
|
12632
|
+
const regDir = import_node_path51.default.dirname(regPath);
|
|
12633
|
+
const regBase = import_node_path51.default.basename(regPath);
|
|
12065
12634
|
registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
|
|
12066
12635
|
if (filename !== null && filename !== regBase) return;
|
|
12067
12636
|
if (reloadTimer) clearTimeout(reloadTimer);
|