@neat.is/core 0.5.3 → 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-GJHEZC5K.js → chunk-PEFX3DBR.js} +422 -111
- package/dist/chunk-PEFX3DBR.js.map +1 -0
- package/dist/{chunk-X2AMX3QZ.js → chunk-VR73QNJD.js} +263 -10
- package/dist/chunk-VR73QNJD.js.map +1 -0
- package/dist/cli.cjs +1094 -448
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +95 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +798 -282
- 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 +807 -291
- 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 +498 -172
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-CFDPIMRP.js.map +0 -1
- package/dist/chunk-GJHEZC5K.js.map +0 -1
- package/dist/chunk-X2AMX3QZ.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(
|
|
@@ -5776,7 +5789,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5776
5789
|
|
|
5777
5790
|
// src/extract/calls/index.ts
|
|
5778
5791
|
init_cjs_shims();
|
|
5779
|
-
var
|
|
5792
|
+
var import_types21 = require("@neat.is/types");
|
|
5780
5793
|
|
|
5781
5794
|
// src/extract/calls/http.ts
|
|
5782
5795
|
init_cjs_shims();
|
|
@@ -6489,15 +6502,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6489
6502
|
return out;
|
|
6490
6503
|
}
|
|
6491
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
|
+
|
|
6492
6801
|
// src/extract/calls/index.ts
|
|
6493
6802
|
function edgeTypeFromEndpoint(ep) {
|
|
6494
6803
|
switch (ep.edgeType) {
|
|
6495
6804
|
case "PUBLISHES_TO":
|
|
6496
|
-
return
|
|
6805
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6497
6806
|
case "CONSUMES_FROM":
|
|
6498
|
-
return
|
|
6807
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6499
6808
|
default:
|
|
6500
|
-
return
|
|
6809
|
+
return import_types21.EdgeType.CALLS;
|
|
6501
6810
|
}
|
|
6502
6811
|
}
|
|
6503
6812
|
function isAwsKind(kind) {
|
|
@@ -6509,23 +6818,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6509
6818
|
for (const service of services) {
|
|
6510
6819
|
const files = await loadSourceFiles(service.dir);
|
|
6511
6820
|
const endpoints = [];
|
|
6821
|
+
const maskedFiles = [];
|
|
6512
6822
|
for (const file of files) {
|
|
6513
6823
|
if (isTestPath(file.path)) continue;
|
|
6514
6824
|
const masked = maskCommentsInSource(file.content);
|
|
6515
6825
|
const maskedFile = { path: file.path, content: masked };
|
|
6826
|
+
maskedFiles.push(maskedFile);
|
|
6516
6827
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6517
6828
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6518
6829
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6519
6830
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6520
6831
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6832
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6521
6833
|
}
|
|
6834
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6522
6835
|
if (endpoints.length === 0) continue;
|
|
6523
6836
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6524
6837
|
for (const ep of endpoints) {
|
|
6525
6838
|
if (!graph.hasNode(ep.infraId)) {
|
|
6526
6839
|
const node = {
|
|
6527
6840
|
id: ep.infraId,
|
|
6528
|
-
type:
|
|
6841
|
+
type: import_types21.NodeType.InfraNode,
|
|
6529
6842
|
name: ep.name,
|
|
6530
6843
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6531
6844
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6537,7 +6850,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6537
6850
|
nodesAdded++;
|
|
6538
6851
|
}
|
|
6539
6852
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6540
|
-
const confidence = (0,
|
|
6853
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6541
6854
|
const relFile = toPosix2(ep.evidence.file);
|
|
6542
6855
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6543
6856
|
graph,
|
|
@@ -6547,7 +6860,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6547
6860
|
);
|
|
6548
6861
|
nodesAdded += n;
|
|
6549
6862
|
edgesAdded += e;
|
|
6550
|
-
if (!(0,
|
|
6863
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6551
6864
|
noteExtractedDropped({
|
|
6552
6865
|
source: fileNodeId,
|
|
6553
6866
|
target: ep.infraId,
|
|
@@ -6567,7 +6880,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6567
6880
|
source: fileNodeId,
|
|
6568
6881
|
target: ep.infraId,
|
|
6569
6882
|
type: edgeType,
|
|
6570
|
-
provenance:
|
|
6883
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6571
6884
|
confidence,
|
|
6572
6885
|
evidence: ep.evidence
|
|
6573
6886
|
};
|
|
@@ -6593,16 +6906,16 @@ init_cjs_shims();
|
|
|
6593
6906
|
|
|
6594
6907
|
// src/extract/infra/docker-compose.ts
|
|
6595
6908
|
init_cjs_shims();
|
|
6596
|
-
var
|
|
6597
|
-
var
|
|
6909
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6910
|
+
var import_types23 = require("@neat.is/types");
|
|
6598
6911
|
|
|
6599
6912
|
// src/extract/infra/shared.ts
|
|
6600
6913
|
init_cjs_shims();
|
|
6601
|
-
var
|
|
6914
|
+
var import_types22 = require("@neat.is/types");
|
|
6602
6915
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6603
6916
|
return {
|
|
6604
|
-
id: (0,
|
|
6605
|
-
type:
|
|
6917
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6918
|
+
type: import_types22.NodeType.InfraNode,
|
|
6606
6919
|
name,
|
|
6607
6920
|
provider,
|
|
6608
6921
|
kind,
|
|
@@ -6646,8 +6959,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6646
6959
|
source: anchorId,
|
|
6647
6960
|
target: node.id,
|
|
6648
6961
|
type: edgeType,
|
|
6649
|
-
provenance:
|
|
6650
|
-
confidence: (0,
|
|
6962
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6963
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6651
6964
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6652
6965
|
};
|
|
6653
6966
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6664,7 +6977,7 @@ function dependsOnList(value) {
|
|
|
6664
6977
|
}
|
|
6665
6978
|
function serviceNameToServiceNode(name, services) {
|
|
6666
6979
|
for (const s of services) {
|
|
6667
|
-
if (s.node.name === name ||
|
|
6980
|
+
if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
|
|
6668
6981
|
}
|
|
6669
6982
|
return null;
|
|
6670
6983
|
}
|
|
@@ -6673,7 +6986,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6673
6986
|
let edgesAdded = 0;
|
|
6674
6987
|
let composePath = null;
|
|
6675
6988
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6676
|
-
const abs =
|
|
6989
|
+
const abs = import_node_path32.default.join(scanPath, name);
|
|
6677
6990
|
if (await exists(abs)) {
|
|
6678
6991
|
composePath = abs;
|
|
6679
6992
|
break;
|
|
@@ -6686,13 +6999,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6686
6999
|
} catch (err) {
|
|
6687
7000
|
recordExtractionError(
|
|
6688
7001
|
"infra docker-compose",
|
|
6689
|
-
|
|
7002
|
+
import_node_path32.default.relative(scanPath, composePath),
|
|
6690
7003
|
err
|
|
6691
7004
|
);
|
|
6692
7005
|
return { nodesAdded, edgesAdded };
|
|
6693
7006
|
}
|
|
6694
7007
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6695
|
-
const evidenceFile =
|
|
7008
|
+
const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
|
|
6696
7009
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6697
7010
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6698
7011
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6714,15 +7027,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6714
7027
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6715
7028
|
const targetId = composeNameToNodeId.get(dep);
|
|
6716
7029
|
if (!targetId) continue;
|
|
6717
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
7030
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6718
7031
|
if (graph.hasEdge(edgeId)) continue;
|
|
6719
7032
|
const edge = {
|
|
6720
7033
|
id: edgeId,
|
|
6721
7034
|
source: sourceId,
|
|
6722
7035
|
target: targetId,
|
|
6723
|
-
type:
|
|
6724
|
-
provenance:
|
|
6725
|
-
confidence: (0,
|
|
7036
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
7037
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7038
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6726
7039
|
evidence: { file: evidenceFile }
|
|
6727
7040
|
};
|
|
6728
7041
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6734,9 +7047,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6734
7047
|
|
|
6735
7048
|
// src/extract/infra/dockerfile.ts
|
|
6736
7049
|
init_cjs_shims();
|
|
6737
|
-
var
|
|
7050
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6738
7051
|
var import_node_fs16 = require("fs");
|
|
6739
|
-
var
|
|
7052
|
+
var import_types24 = require("@neat.is/types");
|
|
6740
7053
|
function readDockerfile(content) {
|
|
6741
7054
|
let image = null;
|
|
6742
7055
|
const ports = [];
|
|
@@ -6765,7 +7078,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6765
7078
|
let nodesAdded = 0;
|
|
6766
7079
|
let edgesAdded = 0;
|
|
6767
7080
|
for (const service of services) {
|
|
6768
|
-
const dockerfilePath =
|
|
7081
|
+
const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
|
|
6769
7082
|
if (!await exists(dockerfilePath)) continue;
|
|
6770
7083
|
let content;
|
|
6771
7084
|
try {
|
|
@@ -6773,7 +7086,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6773
7086
|
} catch (err) {
|
|
6774
7087
|
recordExtractionError(
|
|
6775
7088
|
"infra dockerfile",
|
|
6776
|
-
|
|
7089
|
+
import_node_path33.default.relative(scanPath, dockerfilePath),
|
|
6777
7090
|
err
|
|
6778
7091
|
);
|
|
6779
7092
|
continue;
|
|
@@ -6785,8 +7098,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6785
7098
|
graph.addNode(node.id, node);
|
|
6786
7099
|
nodesAdded++;
|
|
6787
7100
|
}
|
|
6788
|
-
const relDockerfile = toPosix2(
|
|
6789
|
-
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));
|
|
6790
7103
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6791
7104
|
graph,
|
|
6792
7105
|
service.pkg.name,
|
|
@@ -6795,15 +7108,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6795
7108
|
);
|
|
6796
7109
|
nodesAdded += fn;
|
|
6797
7110
|
edgesAdded += fe;
|
|
6798
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7111
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6799
7112
|
if (!graph.hasEdge(edgeId)) {
|
|
6800
7113
|
const edge = {
|
|
6801
7114
|
id: edgeId,
|
|
6802
7115
|
source: fileNodeId,
|
|
6803
7116
|
target: node.id,
|
|
6804
|
-
type:
|
|
6805
|
-
provenance:
|
|
6806
|
-
confidence: (0,
|
|
7117
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7118
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7119
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6807
7120
|
evidence: {
|
|
6808
7121
|
file: evidenceFile,
|
|
6809
7122
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6818,15 +7131,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6818
7131
|
graph.addNode(portNode.id, portNode);
|
|
6819
7132
|
nodesAdded++;
|
|
6820
7133
|
}
|
|
6821
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7134
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6822
7135
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6823
7136
|
const portEdge = {
|
|
6824
7137
|
id: portEdgeId,
|
|
6825
7138
|
source: fileNodeId,
|
|
6826
7139
|
target: portNode.id,
|
|
6827
|
-
type:
|
|
6828
|
-
provenance:
|
|
6829
|
-
confidence: (0,
|
|
7140
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7141
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7142
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6830
7143
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6831
7144
|
};
|
|
6832
7145
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6839,8 +7152,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6839
7152
|
// src/extract/infra/terraform.ts
|
|
6840
7153
|
init_cjs_shims();
|
|
6841
7154
|
var import_node_fs17 = require("fs");
|
|
6842
|
-
var
|
|
6843
|
-
var
|
|
7155
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7156
|
+
var import_types25 = require("@neat.is/types");
|
|
6844
7157
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6845
7158
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6846
7159
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6850,11 +7163,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6850
7163
|
for (const entry of entries) {
|
|
6851
7164
|
if (entry.isDirectory()) {
|
|
6852
7165
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
6853
|
-
const child =
|
|
7166
|
+
const child = import_node_path34.default.join(start, entry.name);
|
|
6854
7167
|
if (await isPythonVenvDir(child)) continue;
|
|
6855
7168
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6856
7169
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
6857
|
-
out.push(
|
|
7170
|
+
out.push(import_node_path34.default.join(start, entry.name));
|
|
6858
7171
|
}
|
|
6859
7172
|
}
|
|
6860
7173
|
return out;
|
|
@@ -6886,7 +7199,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6886
7199
|
const files = await walkTfFiles(scanPath);
|
|
6887
7200
|
for (const file of files) {
|
|
6888
7201
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
6889
|
-
const evidenceFile = toPosix2(
|
|
7202
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
|
|
6890
7203
|
const resources = [];
|
|
6891
7204
|
const byKey = /* @__PURE__ */ new Map();
|
|
6892
7205
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6921,16 +7234,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6921
7234
|
if (!target) continue;
|
|
6922
7235
|
if (seen.has(target.nodeId)) continue;
|
|
6923
7236
|
seen.add(target.nodeId);
|
|
6924
|
-
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);
|
|
6925
7238
|
if (graph.hasEdge(edgeId)) continue;
|
|
6926
7239
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6927
7240
|
const edge = {
|
|
6928
7241
|
id: edgeId,
|
|
6929
7242
|
source: resource.nodeId,
|
|
6930
7243
|
target: target.nodeId,
|
|
6931
|
-
type:
|
|
6932
|
-
provenance:
|
|
6933
|
-
confidence: (0,
|
|
7244
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7245
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7246
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6934
7247
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6935
7248
|
};
|
|
6936
7249
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6944,7 +7257,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6944
7257
|
// src/extract/infra/k8s.ts
|
|
6945
7258
|
init_cjs_shims();
|
|
6946
7259
|
var import_node_fs18 = require("fs");
|
|
6947
|
-
var
|
|
7260
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6948
7261
|
var import_yaml3 = require("yaml");
|
|
6949
7262
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6950
7263
|
Service: "k8s-service",
|
|
@@ -6962,11 +7275,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6962
7275
|
for (const entry of entries) {
|
|
6963
7276
|
if (entry.isDirectory()) {
|
|
6964
7277
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6965
|
-
const child =
|
|
7278
|
+
const child = import_node_path35.default.join(start, entry.name);
|
|
6966
7279
|
if (await isPythonVenvDir(child)) continue;
|
|
6967
7280
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6968
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6969
|
-
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));
|
|
6970
7283
|
}
|
|
6971
7284
|
}
|
|
6972
7285
|
return out;
|
|
@@ -7000,13 +7313,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7000
7313
|
// src/extract/infra/cloudflare.ts
|
|
7001
7314
|
init_cjs_shims();
|
|
7002
7315
|
var import_node_fs19 = require("fs");
|
|
7003
|
-
var
|
|
7316
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
7004
7317
|
var import_smol_toml2 = require("smol-toml");
|
|
7005
|
-
var
|
|
7318
|
+
var import_types26 = require("@neat.is/types");
|
|
7006
7319
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7007
7320
|
async function readWranglerConfig(dir) {
|
|
7008
7321
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7009
|
-
const abs =
|
|
7322
|
+
const abs = import_node_path36.default.join(dir, filename);
|
|
7010
7323
|
if (!await exists(abs)) continue;
|
|
7011
7324
|
const raw = await import_node_fs19.promises.readFile(abs, "utf8");
|
|
7012
7325
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7050,8 +7363,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7050
7363
|
source: anchorId,
|
|
7051
7364
|
target: node.id,
|
|
7052
7365
|
type: edgeType,
|
|
7053
|
-
provenance:
|
|
7054
|
-
confidence: (0,
|
|
7366
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7367
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7055
7368
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7056
7369
|
};
|
|
7057
7370
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7069,11 +7382,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7069
7382
|
try {
|
|
7070
7383
|
read = await readWranglerConfig(service.dir);
|
|
7071
7384
|
} catch (err) {
|
|
7072
|
-
recordExtractionError("infra cloudflare",
|
|
7385
|
+
recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
|
|
7073
7386
|
continue;
|
|
7074
7387
|
}
|
|
7075
7388
|
if (!read || !read.config.name) continue;
|
|
7076
|
-
const evidenceFile = toPosix2(
|
|
7389
|
+
const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
|
|
7077
7390
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7078
7391
|
}
|
|
7079
7392
|
for (const worker of discovered) {
|
|
@@ -7085,7 +7398,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7085
7398
|
}
|
|
7086
7399
|
let anchorId = service.node.id;
|
|
7087
7400
|
if (config.main) {
|
|
7088
|
-
const entryRelPath = toPosix2(
|
|
7401
|
+
const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
|
|
7089
7402
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7090
7403
|
graph,
|
|
7091
7404
|
service.pkg.name,
|
|
@@ -7112,15 +7425,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7112
7425
|
nodesAdded++;
|
|
7113
7426
|
}
|
|
7114
7427
|
if (runtimeNode.id !== anchorId) {
|
|
7115
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7428
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7116
7429
|
if (!graph.hasEdge(runsOnId)) {
|
|
7117
7430
|
const edge = {
|
|
7118
7431
|
id: runsOnId,
|
|
7119
7432
|
source: anchorId,
|
|
7120
7433
|
target: runtimeNode.id,
|
|
7121
|
-
type:
|
|
7122
|
-
provenance:
|
|
7123
|
-
confidence: (0,
|
|
7434
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7435
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7436
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7124
7437
|
evidence: {
|
|
7125
7438
|
file: evidenceFile,
|
|
7126
7439
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7134,7 +7447,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7134
7447
|
const result = addResourceEdge(
|
|
7135
7448
|
graph,
|
|
7136
7449
|
anchorId,
|
|
7137
|
-
|
|
7450
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7138
7451
|
"cloudflare-route",
|
|
7139
7452
|
route,
|
|
7140
7453
|
evidenceFile,
|
|
@@ -7158,7 +7471,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7158
7471
|
const result = addResourceEdge(
|
|
7159
7472
|
graph,
|
|
7160
7473
|
anchorId,
|
|
7161
|
-
|
|
7474
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7162
7475
|
group.kind,
|
|
7163
7476
|
name,
|
|
7164
7477
|
evidenceFile,
|
|
@@ -7172,7 +7485,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7172
7485
|
const result = addResourceEdge(
|
|
7173
7486
|
graph,
|
|
7174
7487
|
anchorId,
|
|
7175
|
-
|
|
7488
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7176
7489
|
"cloudflare-cron",
|
|
7177
7490
|
cron,
|
|
7178
7491
|
evidenceFile,
|
|
@@ -7185,7 +7498,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7185
7498
|
const result = addResourceEdge(
|
|
7186
7499
|
graph,
|
|
7187
7500
|
anchorId,
|
|
7188
|
-
|
|
7501
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7189
7502
|
"cloudflare-env-var",
|
|
7190
7503
|
varName,
|
|
7191
7504
|
evidenceFile,
|
|
@@ -7198,15 +7511,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7198
7511
|
if (!svc.service) continue;
|
|
7199
7512
|
const target = workerIndex.get(svc.service);
|
|
7200
7513
|
if (target && target.anchorId !== anchorId) {
|
|
7201
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7514
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7202
7515
|
if (!graph.hasEdge(edgeId)) {
|
|
7203
7516
|
const edge = {
|
|
7204
7517
|
id: edgeId,
|
|
7205
7518
|
source: anchorId,
|
|
7206
7519
|
target: target.anchorId,
|
|
7207
|
-
type:
|
|
7208
|
-
provenance:
|
|
7209
|
-
confidence: (0,
|
|
7520
|
+
type: import_types26.EdgeType.CALLS,
|
|
7521
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7522
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7210
7523
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7211
7524
|
};
|
|
7212
7525
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7217,7 +7530,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7217
7530
|
const result = addResourceEdge(
|
|
7218
7531
|
graph,
|
|
7219
7532
|
anchorId,
|
|
7220
|
-
|
|
7533
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7221
7534
|
"cloudflare-service-binding",
|
|
7222
7535
|
svc.service,
|
|
7223
7536
|
evidenceFile,
|
|
@@ -7233,12 +7546,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7233
7546
|
// src/extract/infra/vercel.ts
|
|
7234
7547
|
init_cjs_shims();
|
|
7235
7548
|
var import_node_fs20 = require("fs");
|
|
7236
|
-
var
|
|
7237
|
-
var
|
|
7549
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7550
|
+
var import_types27 = require("@neat.is/types");
|
|
7238
7551
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7239
7552
|
async function readVercelConfig(dir) {
|
|
7240
7553
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7241
|
-
const abs =
|
|
7554
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7242
7555
|
if (!await exists(abs)) continue;
|
|
7243
7556
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7244
7557
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7247,7 +7560,7 @@ async function readVercelConfig(dir) {
|
|
|
7247
7560
|
return null;
|
|
7248
7561
|
}
|
|
7249
7562
|
async function readLinkedProjectName(dir) {
|
|
7250
|
-
const abs =
|
|
7563
|
+
const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
|
|
7251
7564
|
if (!await exists(abs)) return void 0;
|
|
7252
7565
|
const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
|
|
7253
7566
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7265,7 +7578,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7265
7578
|
read = await readVercelConfig(service.dir);
|
|
7266
7579
|
projectName = await readLinkedProjectName(service.dir);
|
|
7267
7580
|
} catch (err) {
|
|
7268
|
-
recordExtractionError("infra vercel",
|
|
7581
|
+
recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7269
7582
|
continue;
|
|
7270
7583
|
}
|
|
7271
7584
|
if (!read && !projectName) continue;
|
|
@@ -7281,7 +7594,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7281
7594
|
const anchorId = service.node.id;
|
|
7282
7595
|
if (!read) continue;
|
|
7283
7596
|
const { config, relFile, raw } = read;
|
|
7284
|
-
const evidenceFile = toPosix2(
|
|
7597
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
|
|
7285
7598
|
const add = (edgeType, kind, name) => {
|
|
7286
7599
|
if (!name) return;
|
|
7287
7600
|
const result = emitPlatformResourceEdge(
|
|
@@ -7297,12 +7610,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7297
7610
|
nodesAdded += result.nodesAdded;
|
|
7298
7611
|
edgesAdded += result.edgesAdded;
|
|
7299
7612
|
};
|
|
7300
|
-
add(
|
|
7301
|
-
for (const cron of config.crons ?? []) add(
|
|
7302
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7303
|
-
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);
|
|
7304
7617
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7305
|
-
add(
|
|
7618
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7306
7619
|
}
|
|
7307
7620
|
}
|
|
7308
7621
|
return { nodesAdded, edgesAdded };
|
|
@@ -7311,13 +7624,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7311
7624
|
// src/extract/infra/railway.ts
|
|
7312
7625
|
init_cjs_shims();
|
|
7313
7626
|
var import_node_fs21 = require("fs");
|
|
7314
|
-
var
|
|
7627
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7315
7628
|
var import_smol_toml3 = require("smol-toml");
|
|
7316
|
-
var
|
|
7629
|
+
var import_types28 = require("@neat.is/types");
|
|
7317
7630
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7318
7631
|
async function readRailwayConfig(dir) {
|
|
7319
7632
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7320
|
-
const abs =
|
|
7633
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7321
7634
|
if (!await exists(abs)) continue;
|
|
7322
7635
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7323
7636
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7333,7 +7646,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7333
7646
|
try {
|
|
7334
7647
|
read = await readRailwayConfig(service.dir);
|
|
7335
7648
|
} catch (err) {
|
|
7336
|
-
recordExtractionError("infra railway",
|
|
7649
|
+
recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7337
7650
|
continue;
|
|
7338
7651
|
}
|
|
7339
7652
|
if (!read) continue;
|
|
@@ -7343,7 +7656,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7343
7656
|
}
|
|
7344
7657
|
const anchorId = service.node.id;
|
|
7345
7658
|
const { config, relFile, raw } = read;
|
|
7346
|
-
const evidenceFile = toPosix2(
|
|
7659
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7347
7660
|
const add = (edgeType, kind, name) => {
|
|
7348
7661
|
if (!name) return;
|
|
7349
7662
|
const result = emitPlatformResourceEdge(
|
|
@@ -7359,9 +7672,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7359
7672
|
nodesAdded += result.nodesAdded;
|
|
7360
7673
|
edgesAdded += result.edgesAdded;
|
|
7361
7674
|
};
|
|
7362
|
-
add(
|
|
7363
|
-
add(
|
|
7364
|
-
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);
|
|
7365
7678
|
}
|
|
7366
7679
|
return { nodesAdded, edgesAdded };
|
|
7367
7680
|
}
|
|
@@ -7369,12 +7682,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7369
7682
|
// src/extract/infra/supabase.ts
|
|
7370
7683
|
init_cjs_shims();
|
|
7371
7684
|
var import_node_fs22 = require("fs");
|
|
7372
|
-
var
|
|
7685
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7373
7686
|
var import_smol_toml4 = require("smol-toml");
|
|
7374
|
-
var
|
|
7687
|
+
var import_types29 = require("@neat.is/types");
|
|
7375
7688
|
async function readSupabaseConfig(dir) {
|
|
7376
|
-
const relFile =
|
|
7377
|
-
const abs =
|
|
7689
|
+
const relFile = import_node_path39.default.join("supabase", "config.toml");
|
|
7690
|
+
const abs = import_node_path39.default.join(dir, relFile);
|
|
7378
7691
|
if (!await exists(abs)) return null;
|
|
7379
7692
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7380
7693
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7388,7 +7701,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7388
7701
|
try {
|
|
7389
7702
|
read = await readSupabaseConfig(service.dir);
|
|
7390
7703
|
} catch (err) {
|
|
7391
|
-
recordExtractionError("infra supabase",
|
|
7704
|
+
recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7392
7705
|
continue;
|
|
7393
7706
|
}
|
|
7394
7707
|
if (!read) continue;
|
|
@@ -7403,7 +7716,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7403
7716
|
});
|
|
7404
7717
|
}
|
|
7405
7718
|
const anchorId = service.node.id;
|
|
7406
|
-
const evidenceFile = toPosix2(
|
|
7719
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7407
7720
|
const add = (edgeType, kind, name) => {
|
|
7408
7721
|
if (!name) return;
|
|
7409
7722
|
const result = emitPlatformResourceEdge(
|
|
@@ -7419,10 +7732,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7419
7732
|
nodesAdded += result.nodesAdded;
|
|
7420
7733
|
edgesAdded += result.edgesAdded;
|
|
7421
7734
|
};
|
|
7422
|
-
add(
|
|
7423
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7424
|
-
if (config.storage) add(
|
|
7425
|
-
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");
|
|
7426
7739
|
}
|
|
7427
7740
|
return { nodesAdded, edgesAdded };
|
|
7428
7741
|
}
|
|
@@ -7444,17 +7757,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7444
7757
|
}
|
|
7445
7758
|
|
|
7446
7759
|
// src/extract/index.ts
|
|
7447
|
-
var
|
|
7760
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7448
7761
|
|
|
7449
7762
|
// src/extract/retire.ts
|
|
7450
7763
|
init_cjs_shims();
|
|
7451
7764
|
var import_node_fs23 = require("fs");
|
|
7452
|
-
var
|
|
7453
|
-
var
|
|
7765
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7766
|
+
var import_types30 = require("@neat.is/types");
|
|
7454
7767
|
function dropOrphanedFileNodes(graph) {
|
|
7455
7768
|
const orphans = [];
|
|
7456
7769
|
graph.forEachNode((id, attrs) => {
|
|
7457
|
-
if (attrs.type !==
|
|
7770
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7458
7771
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7459
7772
|
orphans.push(id);
|
|
7460
7773
|
}
|
|
@@ -7467,14 +7780,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7467
7780
|
const bases = [scanPath, ...serviceDirs];
|
|
7468
7781
|
graph.forEachEdge((id, attrs) => {
|
|
7469
7782
|
const edge = attrs;
|
|
7470
|
-
if (edge.provenance !==
|
|
7783
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7471
7784
|
const evidenceFile = edge.evidence?.file;
|
|
7472
7785
|
if (!evidenceFile) return;
|
|
7473
|
-
if (
|
|
7786
|
+
if (import_node_path40.default.isAbsolute(evidenceFile)) {
|
|
7474
7787
|
if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7475
7788
|
return;
|
|
7476
7789
|
}
|
|
7477
|
-
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)));
|
|
7478
7791
|
if (!found) toDrop.push(id);
|
|
7479
7792
|
});
|
|
7480
7793
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7516,7 +7829,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7516
7829
|
}
|
|
7517
7830
|
const droppedEntries = drainDroppedExtracted();
|
|
7518
7831
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7519
|
-
const rejectedPath =
|
|
7832
|
+
const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7520
7833
|
try {
|
|
7521
7834
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7522
7835
|
} catch (err) {
|
|
@@ -7551,8 +7864,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7551
7864
|
// src/persist.ts
|
|
7552
7865
|
init_cjs_shims();
|
|
7553
7866
|
var import_node_fs24 = require("fs");
|
|
7554
|
-
var
|
|
7555
|
-
var
|
|
7867
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7868
|
+
var import_types31 = require("@neat.is/types");
|
|
7556
7869
|
var SCHEMA_VERSION = 4;
|
|
7557
7870
|
function migrateV1ToV2(payload) {
|
|
7558
7871
|
const nodes = payload.graph.nodes;
|
|
@@ -7574,12 +7887,12 @@ function migrateV2ToV3(payload) {
|
|
|
7574
7887
|
for (const edge of edges) {
|
|
7575
7888
|
const attrs = edge.attributes;
|
|
7576
7889
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7577
|
-
attrs.provenance =
|
|
7890
|
+
attrs.provenance = import_types31.Provenance.OBSERVED;
|
|
7578
7891
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7579
7892
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7580
7893
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7581
7894
|
if (type && source && target) {
|
|
7582
|
-
const newId = (0,
|
|
7895
|
+
const newId = (0, import_types31.observedEdgeId)(source, target, type);
|
|
7583
7896
|
attrs.id = newId;
|
|
7584
7897
|
if (edge.key) edge.key = newId;
|
|
7585
7898
|
}
|
|
@@ -7588,7 +7901,7 @@ function migrateV2ToV3(payload) {
|
|
|
7588
7901
|
return { ...payload, schemaVersion: 3 };
|
|
7589
7902
|
}
|
|
7590
7903
|
async function ensureDir(filePath) {
|
|
7591
|
-
await import_node_fs24.promises.mkdir(
|
|
7904
|
+
await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
|
|
7592
7905
|
}
|
|
7593
7906
|
async function saveGraphToDisk(graph, outPath) {
|
|
7594
7907
|
await ensureDir(outPath);
|
|
@@ -7671,19 +7984,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7671
7984
|
init_cjs_shims();
|
|
7672
7985
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
7673
7986
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
7674
|
-
var
|
|
7987
|
+
var import_types34 = require("@neat.is/types");
|
|
7675
7988
|
|
|
7676
7989
|
// src/extend/index.ts
|
|
7677
7990
|
init_cjs_shims();
|
|
7678
7991
|
var import_node_fs26 = require("fs");
|
|
7679
|
-
var
|
|
7992
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7680
7993
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
7681
7994
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
7682
7995
|
|
|
7683
7996
|
// src/installers/package-manager.ts
|
|
7684
7997
|
init_cjs_shims();
|
|
7685
7998
|
var import_node_fs25 = require("fs");
|
|
7686
|
-
var
|
|
7999
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7687
8000
|
var import_node_child_process = require("child_process");
|
|
7688
8001
|
var LOCKFILE_PRIORITY = [
|
|
7689
8002
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -7705,22 +8018,22 @@ async function exists2(p) {
|
|
|
7705
8018
|
}
|
|
7706
8019
|
}
|
|
7707
8020
|
async function detectPackageManager(serviceDir) {
|
|
7708
|
-
let dir =
|
|
8021
|
+
let dir = import_node_path43.default.resolve(serviceDir);
|
|
7709
8022
|
const stops = /* @__PURE__ */ new Set();
|
|
7710
8023
|
for (let i = 0; i < 64; i++) {
|
|
7711
8024
|
if (stops.has(dir)) break;
|
|
7712
8025
|
stops.add(dir);
|
|
7713
8026
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
7714
|
-
const lockPath =
|
|
8027
|
+
const lockPath = import_node_path43.default.join(dir, candidate.lockfile);
|
|
7715
8028
|
if (await exists2(lockPath)) {
|
|
7716
8029
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
7717
8030
|
}
|
|
7718
8031
|
}
|
|
7719
|
-
const parent =
|
|
8032
|
+
const parent = import_node_path43.default.dirname(dir);
|
|
7720
8033
|
if (parent === dir) break;
|
|
7721
8034
|
dir = parent;
|
|
7722
8035
|
}
|
|
7723
|
-
return { pm: "npm", cwd:
|
|
8036
|
+
return { pm: "npm", cwd: import_node_path43.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
7724
8037
|
}
|
|
7725
8038
|
async function runPackageManagerInstall(cmd) {
|
|
7726
8039
|
return new Promise((resolve) => {
|
|
@@ -7769,7 +8082,7 @@ async function fileExists2(p) {
|
|
|
7769
8082
|
}
|
|
7770
8083
|
}
|
|
7771
8084
|
async function readPackageJson(scanPath) {
|
|
7772
|
-
const pkgPath =
|
|
8085
|
+
const pkgPath = import_node_path44.default.join(scanPath, "package.json");
|
|
7773
8086
|
const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
|
|
7774
8087
|
return JSON.parse(raw);
|
|
7775
8088
|
}
|
|
@@ -7788,11 +8101,11 @@ async function findHookFiles(scanPath) {
|
|
|
7788
8101
|
for (const entry of entries) {
|
|
7789
8102
|
if (entry.isDirectory()) {
|
|
7790
8103
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
7791
|
-
await walk3(
|
|
8104
|
+
await walk3(import_node_path44.default.join(dir, entry.name));
|
|
7792
8105
|
} else if (entry.isFile()) {
|
|
7793
8106
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
7794
|
-
const rel =
|
|
7795
|
-
found.push(rel.split(
|
|
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("/"));
|
|
7796
8109
|
}
|
|
7797
8110
|
}
|
|
7798
8111
|
}
|
|
@@ -7803,7 +8116,7 @@ async function findHookFiles(scanPath) {
|
|
|
7803
8116
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
7804
8117
|
let fallback = null;
|
|
7805
8118
|
for (const file of hookFiles) {
|
|
7806
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8119
|
+
const content = await import_node_fs26.promises.readFile(import_node_path44.default.join(scanPath, file), "utf8");
|
|
7807
8120
|
const patched = splicedContent(content, snippet2);
|
|
7808
8121
|
if (patched !== null) return { file, content, patched };
|
|
7809
8122
|
if (fallback === null) fallback = { file, content };
|
|
@@ -7811,11 +8124,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
7811
8124
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
7812
8125
|
}
|
|
7813
8126
|
function extendLogPath() {
|
|
7814
|
-
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");
|
|
7815
8128
|
}
|
|
7816
8129
|
async function appendExtendLog(entry) {
|
|
7817
8130
|
const logPath = extendLogPath();
|
|
7818
|
-
await import_node_fs26.promises.mkdir(
|
|
8131
|
+
await import_node_fs26.promises.mkdir(import_node_path44.default.dirname(logPath), { recursive: true });
|
|
7819
8132
|
await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
7820
8133
|
}
|
|
7821
8134
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -7874,7 +8187,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
7874
8187
|
}
|
|
7875
8188
|
async function describeProjectInstrumentation(ctx) {
|
|
7876
8189
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7877
|
-
const envNeat = await fileExists2(
|
|
8190
|
+
const envNeat = await fileExists2(import_node_path44.default.join(ctx.scanPath, ".env.neat"));
|
|
7878
8191
|
const registryInstrPackages = new Set(
|
|
7879
8192
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
7880
8193
|
);
|
|
@@ -7896,7 +8209,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
7896
8209
|
);
|
|
7897
8210
|
}
|
|
7898
8211
|
for (const file of hookFiles) {
|
|
7899
|
-
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");
|
|
7900
8213
|
if (content.includes(args.registration_snippet)) {
|
|
7901
8214
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
7902
8215
|
}
|
|
@@ -7908,10 +8221,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
7908
8221
|
);
|
|
7909
8222
|
}
|
|
7910
8223
|
const primaryFile = primary.file;
|
|
7911
|
-
const primaryPath =
|
|
8224
|
+
const primaryPath = import_node_path44.default.join(ctx.scanPath, primaryFile);
|
|
7912
8225
|
const filesTouched = [];
|
|
7913
8226
|
const depsAdded = [];
|
|
7914
|
-
const pkgPath =
|
|
8227
|
+
const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
|
|
7915
8228
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7916
8229
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
7917
8230
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -7950,7 +8263,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7950
8263
|
};
|
|
7951
8264
|
}
|
|
7952
8265
|
for (const file of hookFiles) {
|
|
7953
|
-
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");
|
|
7954
8267
|
if (content.includes(args.registration_snippet)) {
|
|
7955
8268
|
return {
|
|
7956
8269
|
library: args.library,
|
|
@@ -7991,7 +8304,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
7991
8304
|
if (!match) {
|
|
7992
8305
|
return { undone: false, message: "no apply found for library" };
|
|
7993
8306
|
}
|
|
7994
|
-
const pkgPath =
|
|
8307
|
+
const pkgPath = import_node_path44.default.join(ctx.scanPath, "package.json");
|
|
7995
8308
|
if (await fileExists2(pkgPath)) {
|
|
7996
8309
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7997
8310
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8002,7 +8315,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8002
8315
|
}
|
|
8003
8316
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8004
8317
|
for (const file of hookFiles) {
|
|
8005
|
-
const filePath =
|
|
8318
|
+
const filePath = import_node_path44.default.join(ctx.scanPath, file);
|
|
8006
8319
|
const content = await import_node_fs26.promises.readFile(filePath, "utf8");
|
|
8007
8320
|
if (content.includes(match.registration_snippet)) {
|
|
8008
8321
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8018,39 +8331,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
8018
8331
|
|
|
8019
8332
|
// src/divergences.ts
|
|
8020
8333
|
init_cjs_shims();
|
|
8021
|
-
var
|
|
8334
|
+
var import_types32 = require("@neat.is/types");
|
|
8022
8335
|
function bucketKey(source, target, type) {
|
|
8023
8336
|
return `${type}|${source}|${target}`;
|
|
8024
8337
|
}
|
|
8025
8338
|
function bucketSourceFor(graph, edge) {
|
|
8026
|
-
if (edge.type !==
|
|
8027
|
-
const parsed = (0,
|
|
8339
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
|
|
8340
|
+
const parsed = (0, import_types32.parseFileId)(edge.source);
|
|
8028
8341
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8029
8342
|
const target = graph.getNodeAttributes(edge.target);
|
|
8030
|
-
if (target.type !==
|
|
8031
|
-
return (0,
|
|
8343
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
|
|
8344
|
+
return (0, import_types32.serviceId)(parsed.service);
|
|
8032
8345
|
}
|
|
8033
8346
|
function bucketEdges(graph) {
|
|
8034
8347
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8035
8348
|
graph.forEachEdge((id, attrs) => {
|
|
8036
8349
|
const e = attrs;
|
|
8037
|
-
const parsed = (0,
|
|
8350
|
+
const parsed = (0, import_types32.parseEdgeId)(id);
|
|
8038
8351
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8039
8352
|
const source = bucketSourceFor(graph, e);
|
|
8040
8353
|
const key = bucketKey(source, e.target, e.type);
|
|
8041
8354
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8042
8355
|
switch (provenance) {
|
|
8043
|
-
case
|
|
8356
|
+
case import_types32.Provenance.EXTRACTED:
|
|
8044
8357
|
cur.extracted = e;
|
|
8045
8358
|
break;
|
|
8046
|
-
case
|
|
8359
|
+
case import_types32.Provenance.OBSERVED:
|
|
8047
8360
|
cur.observed = e;
|
|
8048
8361
|
break;
|
|
8049
|
-
case
|
|
8362
|
+
case import_types32.Provenance.INFERRED:
|
|
8050
8363
|
cur.inferred = e;
|
|
8051
8364
|
break;
|
|
8052
8365
|
default:
|
|
8053
|
-
if (e.provenance ===
|
|
8366
|
+
if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
|
|
8054
8367
|
}
|
|
8055
8368
|
buckets2.set(key, cur);
|
|
8056
8369
|
});
|
|
@@ -8059,12 +8372,12 @@ function bucketEdges(graph) {
|
|
|
8059
8372
|
function nodeIsFrontier(graph, nodeId) {
|
|
8060
8373
|
if (!graph.hasNode(nodeId)) return false;
|
|
8061
8374
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8062
|
-
return attrs.type ===
|
|
8375
|
+
return attrs.type === import_types32.NodeType.FrontierNode;
|
|
8063
8376
|
}
|
|
8064
8377
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8065
8378
|
if (!graph.hasNode(nodeId)) return false;
|
|
8066
8379
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8067
|
-
return attrs.type ===
|
|
8380
|
+
return attrs.type === import_types32.NodeType.WebSocketChannelNode;
|
|
8068
8381
|
}
|
|
8069
8382
|
function clampConfidence(n) {
|
|
8070
8383
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8084,14 +8397,14 @@ function gradedConfidence(edge) {
|
|
|
8084
8397
|
return clampConfidence(confidenceForEdge(edge));
|
|
8085
8398
|
}
|
|
8086
8399
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8400
|
+
import_types32.EdgeType.CALLS,
|
|
8401
|
+
import_types32.EdgeType.CONNECTS_TO,
|
|
8402
|
+
import_types32.EdgeType.PUBLISHES_TO,
|
|
8403
|
+
import_types32.EdgeType.CONSUMES_FROM
|
|
8091
8404
|
]);
|
|
8092
8405
|
function detectMissingDivergences(graph, bucket) {
|
|
8093
8406
|
const out = [];
|
|
8094
|
-
if (bucket.type ===
|
|
8407
|
+
if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
|
|
8095
8408
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8096
8409
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8097
8410
|
out.push({
|
|
@@ -8132,7 +8445,7 @@ function declaredHostFor(svc) {
|
|
|
8132
8445
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8133
8446
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8134
8447
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8135
|
-
if (e.type ===
|
|
8448
|
+
if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
|
|
8136
8449
|
return true;
|
|
8137
8450
|
}
|
|
8138
8451
|
}
|
|
@@ -8145,10 +8458,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8145
8458
|
const out = [];
|
|
8146
8459
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8147
8460
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8148
|
-
if (edge.type !==
|
|
8149
|
-
if (edge.provenance !==
|
|
8461
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8462
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8150
8463
|
const target = graph.getNodeAttributes(edge.target);
|
|
8151
|
-
if (target.type !==
|
|
8464
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8152
8465
|
const observedHost = target.host?.trim();
|
|
8153
8466
|
if (!observedHost) continue;
|
|
8154
8467
|
if (observedHost === declaredHost) continue;
|
|
@@ -8170,10 +8483,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8170
8483
|
const deps = svc.dependencies ?? {};
|
|
8171
8484
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8172
8485
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8173
|
-
if (edge.type !==
|
|
8174
|
-
if (edge.provenance !==
|
|
8486
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8487
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8175
8488
|
const target = graph.getNodeAttributes(edge.target);
|
|
8176
|
-
if (target.type !==
|
|
8489
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8177
8490
|
for (const pair of compatPairs()) {
|
|
8178
8491
|
if (pair.engine !== target.engine) continue;
|
|
8179
8492
|
const declared = deps[pair.driver];
|
|
@@ -8232,7 +8545,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8232
8545
|
for (const d of all) {
|
|
8233
8546
|
if (d.type !== "host-mismatch") continue;
|
|
8234
8547
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8235
|
-
declaredHalf.add((0,
|
|
8548
|
+
declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
|
|
8236
8549
|
}
|
|
8237
8550
|
if (observedHalf.size === 0) return all;
|
|
8238
8551
|
return all.filter((d) => {
|
|
@@ -8251,7 +8564,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8251
8564
|
}
|
|
8252
8565
|
graph.forEachNode((nodeId, attrs) => {
|
|
8253
8566
|
const n = attrs;
|
|
8254
|
-
if (n.type !==
|
|
8567
|
+
if (n.type !== import_types32.NodeType.ServiceNode) return;
|
|
8255
8568
|
const svc = n;
|
|
8256
8569
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8257
8570
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8285,7 +8598,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8285
8598
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8286
8599
|
return a.target.localeCompare(b.target);
|
|
8287
8600
|
});
|
|
8288
|
-
return
|
|
8601
|
+
return import_types32.DivergenceResultSchema.parse({
|
|
8289
8602
|
divergences: filtered,
|
|
8290
8603
|
totalAffected: filtered.length,
|
|
8291
8604
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8416,23 +8729,23 @@ function canonicalJson(value) {
|
|
|
8416
8729
|
|
|
8417
8730
|
// src/projects.ts
|
|
8418
8731
|
init_cjs_shims();
|
|
8419
|
-
var
|
|
8732
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8420
8733
|
function pathsForProject(project, baseDir) {
|
|
8421
8734
|
if (project === DEFAULT_PROJECT) {
|
|
8422
8735
|
return {
|
|
8423
|
-
snapshotPath:
|
|
8424
|
-
errorsPath:
|
|
8425
|
-
staleEventsPath:
|
|
8426
|
-
embeddingsCachePath:
|
|
8427
|
-
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")
|
|
8428
8741
|
};
|
|
8429
8742
|
}
|
|
8430
8743
|
return {
|
|
8431
|
-
snapshotPath:
|
|
8432
|
-
errorsPath:
|
|
8433
|
-
staleEventsPath:
|
|
8434
|
-
embeddingsCachePath:
|
|
8435
|
-
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`)
|
|
8436
8749
|
};
|
|
8437
8750
|
}
|
|
8438
8751
|
var Projects = class {
|
|
@@ -8470,23 +8783,23 @@ var Projects = class {
|
|
|
8470
8783
|
init_cjs_shims();
|
|
8471
8784
|
var import_node_fs28 = require("fs");
|
|
8472
8785
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8473
|
-
var
|
|
8474
|
-
var
|
|
8786
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8787
|
+
var import_types33 = require("@neat.is/types");
|
|
8475
8788
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8476
8789
|
var LOCK_RETRY_MS = 50;
|
|
8477
8790
|
function neatHome() {
|
|
8478
8791
|
const override = process.env.NEAT_HOME;
|
|
8479
|
-
if (override && override.length > 0) return
|
|
8480
|
-
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");
|
|
8481
8794
|
}
|
|
8482
8795
|
function registryPath() {
|
|
8483
|
-
return
|
|
8796
|
+
return import_node_path46.default.join(neatHome(), "projects.json");
|
|
8484
8797
|
}
|
|
8485
8798
|
function registryLockPath() {
|
|
8486
|
-
return
|
|
8799
|
+
return import_node_path46.default.join(neatHome(), "projects.json.lock");
|
|
8487
8800
|
}
|
|
8488
8801
|
function daemonPidPath() {
|
|
8489
|
-
return
|
|
8802
|
+
return import_node_path46.default.join(neatHome(), "neatd.pid");
|
|
8490
8803
|
}
|
|
8491
8804
|
function isPidAliveDefault(pid) {
|
|
8492
8805
|
try {
|
|
@@ -8546,7 +8859,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8546
8859
|
}
|
|
8547
8860
|
}
|
|
8548
8861
|
async function normalizeProjectPath(input) {
|
|
8549
|
-
const resolved =
|
|
8862
|
+
const resolved = import_node_path46.default.resolve(input);
|
|
8550
8863
|
try {
|
|
8551
8864
|
return await import_node_fs28.promises.realpath(resolved);
|
|
8552
8865
|
} catch {
|
|
@@ -8554,7 +8867,7 @@ async function normalizeProjectPath(input) {
|
|
|
8554
8867
|
}
|
|
8555
8868
|
}
|
|
8556
8869
|
async function writeAtomically(target, contents) {
|
|
8557
|
-
await import_node_fs28.promises.mkdir(
|
|
8870
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
|
|
8558
8871
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8559
8872
|
const fd = await import_node_fs28.promises.open(tmp, "w");
|
|
8560
8873
|
try {
|
|
@@ -8567,7 +8880,7 @@ async function writeAtomically(target, contents) {
|
|
|
8567
8880
|
}
|
|
8568
8881
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8569
8882
|
const deadline = Date.now() + timeoutMs;
|
|
8570
|
-
await import_node_fs28.promises.mkdir(
|
|
8883
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
|
|
8571
8884
|
let probedHolder = false;
|
|
8572
8885
|
while (true) {
|
|
8573
8886
|
try {
|
|
@@ -8620,10 +8933,10 @@ async function readRegistry() {
|
|
|
8620
8933
|
throw err;
|
|
8621
8934
|
}
|
|
8622
8935
|
const parsed = JSON.parse(raw);
|
|
8623
|
-
return
|
|
8936
|
+
return import_types33.RegistryFileSchema.parse(parsed);
|
|
8624
8937
|
}
|
|
8625
8938
|
async function writeRegistry(reg) {
|
|
8626
|
-
const validated =
|
|
8939
|
+
const validated = import_types33.RegistryFileSchema.parse(reg);
|
|
8627
8940
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8628
8941
|
}
|
|
8629
8942
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -8818,7 +9131,7 @@ init_auth();
|
|
|
8818
9131
|
// src/connectors-config.ts
|
|
8819
9132
|
init_cjs_shims();
|
|
8820
9133
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
8821
|
-
var
|
|
9134
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8822
9135
|
var import_node_fs29 = require("fs");
|
|
8823
9136
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
8824
9137
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -8833,11 +9146,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
8833
9146
|
};
|
|
8834
9147
|
function neatHome2() {
|
|
8835
9148
|
const override = process.env.NEAT_HOME;
|
|
8836
|
-
if (override && override.length > 0) return
|
|
8837
|
-
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");
|
|
8838
9151
|
}
|
|
8839
9152
|
function connectorsConfigPath(home = neatHome2()) {
|
|
8840
|
-
return
|
|
9153
|
+
return import_node_path47.default.join(home, "connectors.json");
|
|
8841
9154
|
}
|
|
8842
9155
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
8843
9156
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9173,11 +9486,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9173
9486
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9174
9487
|
const parsed = [];
|
|
9175
9488
|
for (const c of candidates) {
|
|
9176
|
-
const r =
|
|
9489
|
+
const r = import_types34.DivergenceTypeSchema.safeParse(c);
|
|
9177
9490
|
if (!r.success) {
|
|
9178
9491
|
return reply.code(400).send({
|
|
9179
9492
|
error: `unknown divergence type "${c}"`,
|
|
9180
|
-
allowed:
|
|
9493
|
+
allowed: import_types34.DivergenceTypeSchema.options
|
|
9181
9494
|
});
|
|
9182
9495
|
}
|
|
9183
9496
|
parsed.push(r.data);
|
|
@@ -9448,7 +9761,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9448
9761
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9449
9762
|
let violations = await log.readAll();
|
|
9450
9763
|
if (req.query.severity) {
|
|
9451
|
-
const sev =
|
|
9764
|
+
const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
|
|
9452
9765
|
if (!sev.success) {
|
|
9453
9766
|
return reply.code(400).send({
|
|
9454
9767
|
error: "invalid severity",
|
|
@@ -9487,7 +9800,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9487
9800
|
scope.post("/policies/check", async (req, reply) => {
|
|
9488
9801
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9489
9802
|
if (!proj) return;
|
|
9490
|
-
const parsed =
|
|
9803
|
+
const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9491
9804
|
if (!parsed.success) {
|
|
9492
9805
|
return reply.code(400).send({
|
|
9493
9806
|
error: "invalid /policies/check body",
|
|
@@ -9753,21 +10066,21 @@ init_otel_grpc();
|
|
|
9753
10066
|
// src/daemon.ts
|
|
9754
10067
|
init_cjs_shims();
|
|
9755
10068
|
var import_node_fs31 = require("fs");
|
|
9756
|
-
var
|
|
10069
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
9757
10070
|
var import_node_module = require("module");
|
|
9758
10071
|
init_otel();
|
|
9759
10072
|
|
|
9760
10073
|
// src/connectors/index.ts
|
|
9761
10074
|
init_cjs_shims();
|
|
9762
|
-
var
|
|
10075
|
+
var import_types35 = require("@neat.is/types");
|
|
9763
10076
|
var NO_ENV = "unknown";
|
|
9764
10077
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
9765
10078
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9766
10079
|
const sites = [];
|
|
9767
10080
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
9768
10081
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9769
|
-
if (edge.provenance !==
|
|
9770
|
-
const parsed = (0,
|
|
10082
|
+
if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
|
|
10083
|
+
const parsed = (0, import_types35.parseFileId)(edge.source);
|
|
9771
10084
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
9772
10085
|
const site = { relPath: edge.evidence.file };
|
|
9773
10086
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -9778,7 +10091,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
9778
10091
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
9779
10092
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9780
10093
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
9781
|
-
if (attrs.type !==
|
|
10094
|
+
if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
|
|
9782
10095
|
const site = { relPath: attrs.path };
|
|
9783
10096
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
9784
10097
|
return site;
|
|
@@ -9952,7 +10265,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
9952
10265
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
9953
10266
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
9954
10267
|
// pg_stat_statements connection (see module header above).
|
|
9955
|
-
"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 }
|
|
9956
10274
|
};
|
|
9957
10275
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
9958
10276
|
function defaultRateLimitFor(provider) {
|
|
@@ -10244,10 +10562,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10244
10562
|
// src/connectors/supabase/map.ts
|
|
10245
10563
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10246
10564
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10247
|
-
function targetFromRestPath(
|
|
10248
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10565
|
+
function targetFromRestPath(path52) {
|
|
10566
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path52);
|
|
10249
10567
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10250
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10568
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path52);
|
|
10251
10569
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10252
10570
|
return null;
|
|
10253
10571
|
}
|
|
@@ -10356,23 +10674,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10356
10674
|
|
|
10357
10675
|
// src/connectors/supabase/resolve.ts
|
|
10358
10676
|
init_cjs_shims();
|
|
10359
|
-
var
|
|
10677
|
+
var import_types37 = require("@neat.is/types");
|
|
10360
10678
|
function createSupabaseResolveTarget(graph, config) {
|
|
10361
10679
|
return (signal, _ctx) => {
|
|
10362
10680
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10363
10681
|
return null;
|
|
10364
10682
|
}
|
|
10365
|
-
const subResourceId = (0,
|
|
10683
|
+
const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10366
10684
|
if (graph.hasNode(subResourceId)) {
|
|
10367
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10685
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10368
10686
|
}
|
|
10369
|
-
const bareResourceId = (0,
|
|
10687
|
+
const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
|
|
10370
10688
|
if (graph.hasNode(bareResourceId)) {
|
|
10371
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10689
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10372
10690
|
}
|
|
10373
|
-
const projectLevelId = (0,
|
|
10691
|
+
const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
|
|
10374
10692
|
if (graph.hasNode(projectLevelId)) {
|
|
10375
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10693
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10376
10694
|
}
|
|
10377
10695
|
return null;
|
|
10378
10696
|
};
|
|
@@ -10465,7 +10783,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10465
10783
|
|
|
10466
10784
|
// src/connectors/railway/index.ts
|
|
10467
10785
|
init_cjs_shims();
|
|
10468
|
-
var
|
|
10786
|
+
var import_types41 = require("@neat.is/types");
|
|
10469
10787
|
|
|
10470
10788
|
// src/connectors/railway/client.ts
|
|
10471
10789
|
init_cjs_shims();
|
|
@@ -10591,7 +10909,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10591
10909
|
const out = [];
|
|
10592
10910
|
graph.forEachNode((_id, attrs) => {
|
|
10593
10911
|
const node = attrs;
|
|
10594
|
-
if (node.type !==
|
|
10912
|
+
if (node.type !== import_types41.NodeType.RouteNode) return;
|
|
10595
10913
|
const route = attrs;
|
|
10596
10914
|
if (route.service !== serviceName) return;
|
|
10597
10915
|
out.push({
|
|
@@ -10695,12 +11013,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10695
11013
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10696
11014
|
if (!serviceName) return null;
|
|
10697
11015
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10698
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
11016
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
|
|
10699
11017
|
}
|
|
10700
11018
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10701
11019
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10702
11020
|
if (!peerName) return null;
|
|
10703
|
-
return { targetNodeId: (0,
|
|
11021
|
+
return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
|
|
10704
11022
|
}
|
|
10705
11023
|
return null;
|
|
10706
11024
|
};
|
|
@@ -10824,9 +11142,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
10824
11142
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
10825
11143
|
if (secondSep === -1) return null;
|
|
10826
11144
|
const method = rest.slice(0, secondSep);
|
|
10827
|
-
const
|
|
10828
|
-
if (!resourceName || !method || !
|
|
10829
|
-
return { resourceName, method, path:
|
|
11145
|
+
const path52 = rest.slice(secondSep + 1);
|
|
11146
|
+
if (!resourceName || !method || !path52) return null;
|
|
11147
|
+
return { resourceName, method, path: path52 };
|
|
10830
11148
|
}
|
|
10831
11149
|
function resourceNameFor(type, labels) {
|
|
10832
11150
|
if (!labels) return null;
|
|
@@ -10864,14 +11182,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
10864
11182
|
if (!req) return null;
|
|
10865
11183
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
10866
11184
|
const method = req.requestMethod.toUpperCase();
|
|
10867
|
-
const
|
|
10868
|
-
if (
|
|
11185
|
+
const path52 = pathFromRequestUrl(req.requestUrl);
|
|
11186
|
+
if (path52 === null) return null;
|
|
10869
11187
|
const timestamp = entry.timestamp;
|
|
10870
11188
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
10871
11189
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
10872
11190
|
return {
|
|
10873
11191
|
targetKind: resourceType,
|
|
10874
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11192
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path52 }),
|
|
10875
11193
|
callCount: 1,
|
|
10876
11194
|
errorCount: isError ? 1 : 0,
|
|
10877
11195
|
lastObservedIso: timestamp
|
|
@@ -10888,7 +11206,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
10888
11206
|
|
|
10889
11207
|
// src/connectors/firebase/resolve.ts
|
|
10890
11208
|
init_cjs_shims();
|
|
10891
|
-
var
|
|
11209
|
+
var import_types42 = require("@neat.is/types");
|
|
10892
11210
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
10893
11211
|
switch (resourceType) {
|
|
10894
11212
|
case "cloud_function":
|
|
@@ -10903,7 +11221,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
10903
11221
|
const entries = [];
|
|
10904
11222
|
graph.forEachNode((_id, attrs) => {
|
|
10905
11223
|
const node = attrs;
|
|
10906
|
-
if (node.type !==
|
|
11224
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10907
11225
|
const route = attrs;
|
|
10908
11226
|
if (route.service !== serviceName) return;
|
|
10909
11227
|
entries.push({
|
|
@@ -10935,7 +11253,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
10935
11253
|
return {
|
|
10936
11254
|
targetNodeId: match.routeNodeId,
|
|
10937
11255
|
serviceName,
|
|
10938
|
-
edgeType:
|
|
11256
|
+
edgeType: import_types42.EdgeType.CALLS
|
|
10939
11257
|
};
|
|
10940
11258
|
};
|
|
10941
11259
|
}
|
|
@@ -10962,7 +11280,7 @@ init_cjs_shims();
|
|
|
10962
11280
|
|
|
10963
11281
|
// src/connectors/cloudflare/connector.ts
|
|
10964
11282
|
init_cjs_shims();
|
|
10965
|
-
var
|
|
11283
|
+
var import_types44 = require("@neat.is/types");
|
|
10966
11284
|
|
|
10967
11285
|
// src/connectors/cloudflare/client.ts
|
|
10968
11286
|
init_cjs_shims();
|
|
@@ -11078,7 +11396,7 @@ function mapEventToSignal(event) {
|
|
|
11078
11396
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11079
11397
|
const statusCode = metadata?.statusCode;
|
|
11080
11398
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11081
|
-
const
|
|
11399
|
+
const path52 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11082
11400
|
return {
|
|
11083
11401
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11084
11402
|
targetName: scriptName,
|
|
@@ -11086,7 +11404,7 @@ function mapEventToSignal(event) {
|
|
|
11086
11404
|
errorCount: isError ? 1 : 0,
|
|
11087
11405
|
lastObservedIso: observedAt.toISOString(),
|
|
11088
11406
|
method,
|
|
11089
|
-
...
|
|
11407
|
+
...path52 ? { path: path52 } : {},
|
|
11090
11408
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11091
11409
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11092
11410
|
};
|
|
@@ -11126,19 +11444,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11126
11444
|
graph.forEachNode((id, attrs) => {
|
|
11127
11445
|
if (found) return;
|
|
11128
11446
|
const a = attrs;
|
|
11129
|
-
if (a.type ===
|
|
11447
|
+
if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11130
11448
|
found = id;
|
|
11131
11449
|
}
|
|
11132
11450
|
});
|
|
11133
11451
|
return found;
|
|
11134
11452
|
}
|
|
11135
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11136
|
-
const normalizedPath = normalizePathTemplate(
|
|
11453
|
+
function findMatchingRouteNode(graph, serviceName, method, path52) {
|
|
11454
|
+
const normalizedPath = normalizePathTemplate(path52);
|
|
11137
11455
|
let found = null;
|
|
11138
11456
|
graph.forEachNode((id, attrs) => {
|
|
11139
11457
|
if (found) return;
|
|
11140
11458
|
const a = attrs;
|
|
11141
|
-
if (a.type !==
|
|
11459
|
+
if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11142
11460
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11143
11461
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11144
11462
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11150,18 +11468,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11150
11468
|
return (signal) => {
|
|
11151
11469
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11152
11470
|
const scriptName = signal.targetName;
|
|
11153
|
-
const { method, path:
|
|
11471
|
+
const { method, path: path52 } = signal;
|
|
11154
11472
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11155
|
-
if (!method || !
|
|
11156
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11473
|
+
if (!method || !path52) return wholeFileId;
|
|
11474
|
+
return findMatchingRouteNode(graph, serviceName, method, path52) ?? wholeFileId;
|
|
11157
11475
|
};
|
|
11158
11476
|
const mapping = config.workers?.[scriptName];
|
|
11159
11477
|
if (mapping) {
|
|
11160
|
-
const wholeFileId = (0,
|
|
11478
|
+
const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
|
|
11161
11479
|
return {
|
|
11162
11480
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11163
11481
|
serviceName: mapping.service,
|
|
11164
|
-
edgeType:
|
|
11482
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11165
11483
|
};
|
|
11166
11484
|
}
|
|
11167
11485
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11170,18 +11488,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11170
11488
|
return {
|
|
11171
11489
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11172
11490
|
serviceName: fileNode.service,
|
|
11173
|
-
edgeType:
|
|
11491
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11174
11492
|
};
|
|
11175
11493
|
}
|
|
11176
11494
|
return {
|
|
11177
|
-
targetNodeId: (0,
|
|
11495
|
+
targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
|
|
11178
11496
|
serviceName: scriptName,
|
|
11179
|
-
edgeType:
|
|
11497
|
+
edgeType: import_types44.EdgeType.CALLS,
|
|
11180
11498
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11181
11499
|
};
|
|
11182
11500
|
};
|
|
11183
11501
|
}
|
|
11184
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
|
+
|
|
11185
11609
|
// src/connectors/registry.ts
|
|
11186
11610
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11187
11611
|
async function authProbe(input) {
|
|
@@ -11333,6 +11757,91 @@ var PROVIDER_DISPATCH = {
|
|
|
11333
11757
|
}
|
|
11334
11758
|
}
|
|
11335
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
|
+
}
|
|
11336
11845
|
function resolveEntryCredentials(dispatch, entry, env) {
|
|
11337
11846
|
let credentials;
|
|
11338
11847
|
try {
|
|
@@ -11355,6 +11864,13 @@ function resolveEntryCredentials(dispatch, entry, env) {
|
|
|
11355
11864
|
function buildRegistration(entry, graph, env = process.env) {
|
|
11356
11865
|
const dispatch = PROVIDER_DISPATCH[entry.provider];
|
|
11357
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
|
+
}
|
|
11358
11874
|
return { ok: false, reason: `unknown provider "${entry.provider}"` };
|
|
11359
11875
|
}
|
|
11360
11876
|
const creds = resolveEntryCredentials(dispatch, entry, env);
|
|
@@ -11405,7 +11921,7 @@ async function loadConnectorRegistrations(input) {
|
|
|
11405
11921
|
if (!connectorMatchesProject(entry, project)) continue;
|
|
11406
11922
|
const result = buildRegistration(entry, graph, env);
|
|
11407
11923
|
if (result.ok) registrations.push(result.registration);
|
|
11408
|
-
else onSkip?.(entry, result.reason);
|
|
11924
|
+
else if (!result.push) onSkip?.(entry, result.reason);
|
|
11409
11925
|
}
|
|
11410
11926
|
return registrations;
|
|
11411
11927
|
}
|
|
@@ -11416,7 +11932,7 @@ init_auth();
|
|
|
11416
11932
|
// src/unrouted.ts
|
|
11417
11933
|
init_cjs_shims();
|
|
11418
11934
|
var import_node_fs30 = require("fs");
|
|
11419
|
-
var
|
|
11935
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11420
11936
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
11421
11937
|
return {
|
|
11422
11938
|
timestamp: now.toISOString(),
|
|
@@ -11426,34 +11942,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
11426
11942
|
};
|
|
11427
11943
|
}
|
|
11428
11944
|
async function appendUnroutedSpan(neatHome3, record) {
|
|
11429
|
-
const target =
|
|
11945
|
+
const target = import_node_path50.default.join(neatHome3, "errors.ndjson");
|
|
11430
11946
|
await import_node_fs30.promises.mkdir(neatHome3, { recursive: true });
|
|
11431
11947
|
await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
11432
11948
|
}
|
|
11433
11949
|
function unroutedErrorsPath(neatHome3) {
|
|
11434
|
-
return
|
|
11950
|
+
return import_node_path50.default.join(neatHome3, "errors.ndjson");
|
|
11435
11951
|
}
|
|
11436
11952
|
|
|
11437
11953
|
// src/daemon.ts
|
|
11438
|
-
var
|
|
11954
|
+
var import_types47 = require("@neat.is/types");
|
|
11439
11955
|
function daemonJsonPath(scanPath) {
|
|
11440
|
-
return
|
|
11956
|
+
return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
|
|
11441
11957
|
}
|
|
11442
11958
|
function daemonsDiscoveryDir(home) {
|
|
11443
11959
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11444
|
-
return
|
|
11960
|
+
return import_node_path51.default.join(base, "daemons");
|
|
11445
11961
|
}
|
|
11446
11962
|
function daemonDiscoveryPath(project, home) {
|
|
11447
|
-
return
|
|
11963
|
+
return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11448
11964
|
}
|
|
11449
11965
|
function sanitizeDiscoveryName(project) {
|
|
11450
11966
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11451
11967
|
}
|
|
11452
11968
|
function neatHomeFromEnv() {
|
|
11453
11969
|
const env = process.env.NEAT_HOME;
|
|
11454
|
-
if (env && env.length > 0) return
|
|
11970
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11455
11971
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11456
|
-
return
|
|
11972
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11457
11973
|
}
|
|
11458
11974
|
function resolveNeatVersion() {
|
|
11459
11975
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -11508,11 +12024,11 @@ function teardownSlot(slot) {
|
|
|
11508
12024
|
}
|
|
11509
12025
|
}
|
|
11510
12026
|
function neatHomeFor(opts) {
|
|
11511
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
12027
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
|
|
11512
12028
|
const env = process.env.NEAT_HOME;
|
|
11513
|
-
if (env && env.length > 0) return
|
|
12029
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11514
12030
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11515
|
-
return
|
|
12031
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11516
12032
|
}
|
|
11517
12033
|
function routeSpanToProject(serviceName, projects) {
|
|
11518
12034
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -11556,11 +12072,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
11556
12072
|
if (!serviceName) return true;
|
|
11557
12073
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
11558
12074
|
return graph.someNode(
|
|
11559
|
-
(_id, attrs) => attrs.type ===
|
|
12075
|
+
(_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
|
|
11560
12076
|
);
|
|
11561
12077
|
}
|
|
11562
12078
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
11563
|
-
const paths = pathsForProject(entry.name,
|
|
12079
|
+
const paths = pathsForProject(entry.name, import_node_path51.default.join(entry.path, "neat-out"));
|
|
11564
12080
|
try {
|
|
11565
12081
|
const stat = await import_node_fs31.promises.stat(entry.path);
|
|
11566
12082
|
if (!stat.isDirectory()) {
|
|
@@ -11691,7 +12207,7 @@ async function startDaemon(opts = {}) {
|
|
|
11691
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;
|
|
11692
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;
|
|
11693
12209
|
const singleProject = projectArg;
|
|
11694
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
12210
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
|
|
11695
12211
|
if (singleProject && !singleProjectPath) {
|
|
11696
12212
|
throw new Error(
|
|
11697
12213
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -11706,7 +12222,7 @@ async function startDaemon(opts = {}) {
|
|
|
11706
12222
|
);
|
|
11707
12223
|
}
|
|
11708
12224
|
}
|
|
11709
|
-
const pidPath =
|
|
12225
|
+
const pidPath = import_node_path51.default.join(home, "neatd.pid");
|
|
11710
12226
|
await writeAtomically(pidPath, `${process.pid}
|
|
11711
12227
|
`);
|
|
11712
12228
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12113,8 +12629,8 @@ async function startDaemon(opts = {}) {
|
|
|
12113
12629
|
let registryWatcher = null;
|
|
12114
12630
|
let reloadTimer = null;
|
|
12115
12631
|
if (!singleProject) try {
|
|
12116
|
-
const regDir =
|
|
12117
|
-
const regBase =
|
|
12632
|
+
const regDir = import_node_path51.default.dirname(regPath);
|
|
12633
|
+
const regBase = import_node_path51.default.basename(regPath);
|
|
12118
12634
|
registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
|
|
12119
12635
|
if (filename !== null && filename !== regBase) return;
|
|
12120
12636
|
if (reloadTimer) clearTimeout(reloadTimer);
|