@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/neatd.cjs
CHANGED
|
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
|
|
|
61
61
|
]);
|
|
62
62
|
const publicRead = opts.publicRead === true;
|
|
63
63
|
app.addHook("preHandler", (req2, reply, done) => {
|
|
64
|
-
const
|
|
65
|
-
if (exactUnauthPaths.has(
|
|
64
|
+
const path54 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
65
|
+
if (exactUnauthPaths.has(path54) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path54)) {
|
|
66
66
|
done();
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function resolveProtoRoot() {
|
|
197
|
-
const here =
|
|
198
|
-
return
|
|
197
|
+
const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
198
|
+
return import_node_path48.default.resolve(here, "..", "proto");
|
|
199
199
|
}
|
|
200
200
|
function loadTraceService() {
|
|
201
201
|
const protoRoot = resolveProtoRoot();
|
|
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
263
263
|
})
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
|
-
var import_node_url,
|
|
266
|
+
var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
|
|
267
267
|
var init_otel_grpc = __esm({
|
|
268
268
|
"src/otel-grpc.ts"() {
|
|
269
269
|
"use strict";
|
|
270
270
|
init_cjs_shims();
|
|
271
271
|
import_node_url = require("url");
|
|
272
|
-
|
|
272
|
+
import_node_path48 = __toESM(require("path"), 1);
|
|
273
273
|
import_node_crypto2 = require("crypto");
|
|
274
274
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
275
275
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -362,8 +362,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
362
362
|
const v = attrs[key];
|
|
363
363
|
if (typeof v === "string" && v.length > 0) {
|
|
364
364
|
const q = v.indexOf("?");
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
365
|
+
const path54 = q === -1 ? v : v.slice(0, q);
|
|
366
|
+
if (path54.length > 0) return path54;
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
return void 0;
|
|
@@ -398,6 +398,7 @@ function parseOtlpRequest(body) {
|
|
|
398
398
|
attributes: attrs,
|
|
399
399
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
400
400
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
401
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
401
402
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
402
403
|
messagingDestination: messagingDestinationOf(attrs),
|
|
403
404
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -417,10 +418,10 @@ function parseOtlpRequest(body) {
|
|
|
417
418
|
return out;
|
|
418
419
|
}
|
|
419
420
|
function loadProtoRoot() {
|
|
420
|
-
const here =
|
|
421
|
-
const protoRoot =
|
|
421
|
+
const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
422
|
+
const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
|
|
422
423
|
const root = new import_protobufjs.default.Root();
|
|
423
|
-
root.resolvePath = (_origin, target) =>
|
|
424
|
+
root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
|
|
424
425
|
root.loadSync(
|
|
425
426
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
426
427
|
{ keepCase: true }
|
|
@@ -648,12 +649,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
648
649
|
}
|
|
649
650
|
}
|
|
650
651
|
}
|
|
651
|
-
var
|
|
652
|
+
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;
|
|
652
653
|
var init_otel = __esm({
|
|
653
654
|
"src/otel.ts"() {
|
|
654
655
|
"use strict";
|
|
655
656
|
init_cjs_shims();
|
|
656
|
-
|
|
657
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
657
658
|
import_node_url2 = require("url");
|
|
658
659
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
659
660
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -677,13 +678,13 @@ __export(neatd_exports, {
|
|
|
677
678
|
module.exports = __toCommonJS(neatd_exports);
|
|
678
679
|
init_cjs_shims();
|
|
679
680
|
var import_node_fs33 = require("fs");
|
|
680
|
-
var
|
|
681
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
681
682
|
var import_node_module2 = require("module");
|
|
682
683
|
|
|
683
684
|
// src/daemon.ts
|
|
684
685
|
init_cjs_shims();
|
|
685
686
|
var import_node_fs31 = require("fs");
|
|
686
|
-
var
|
|
687
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
687
688
|
var import_node_module = require("module");
|
|
688
689
|
|
|
689
690
|
// src/graph.ts
|
|
@@ -1213,19 +1214,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1213
1214
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1214
1215
|
let best = { path: [start], edges: [] };
|
|
1215
1216
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1216
|
-
function step(node,
|
|
1217
|
-
if (
|
|
1218
|
-
best = { path: [...
|
|
1217
|
+
function step(node, path54, edges) {
|
|
1218
|
+
if (path54.length > best.path.length) {
|
|
1219
|
+
best = { path: [...path54], edges: [...edges] };
|
|
1219
1220
|
}
|
|
1220
|
-
if (
|
|
1221
|
+
if (path54.length - 1 >= maxDepth) return;
|
|
1221
1222
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1222
1223
|
for (const [srcId, edge] of incoming) {
|
|
1223
1224
|
if (visited.has(srcId)) continue;
|
|
1224
1225
|
visited.add(srcId);
|
|
1225
|
-
|
|
1226
|
+
path54.push(srcId);
|
|
1226
1227
|
edges.push(edge);
|
|
1227
|
-
step(srcId,
|
|
1228
|
-
|
|
1228
|
+
step(srcId, path54, edges);
|
|
1229
|
+
path54.pop();
|
|
1229
1230
|
edges.pop();
|
|
1230
1231
|
visited.delete(srcId);
|
|
1231
1232
|
}
|
|
@@ -1419,26 +1420,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1419
1420
|
return best;
|
|
1420
1421
|
}
|
|
1421
1422
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1422
|
-
const
|
|
1423
|
+
const path54 = [originServiceId];
|
|
1423
1424
|
const edges = [];
|
|
1424
1425
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1425
1426
|
let current = originServiceId;
|
|
1426
1427
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1427
1428
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1428
1429
|
if (!hop) break;
|
|
1429
|
-
|
|
1430
|
+
path54.push(hop.nextService);
|
|
1430
1431
|
edges.push(hop.edge);
|
|
1431
1432
|
visited.add(hop.nextService);
|
|
1432
1433
|
current = hop.nextService;
|
|
1433
1434
|
}
|
|
1434
1435
|
if (edges.length === 0) return null;
|
|
1435
|
-
return { path:
|
|
1436
|
+
return { path: path54, edges, culprit: current };
|
|
1436
1437
|
}
|
|
1437
1438
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1438
1439
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1439
1440
|
if (!chain) return null;
|
|
1440
1441
|
const culprit = chain.culprit;
|
|
1441
|
-
const
|
|
1442
|
+
const path54 = [...chain.path];
|
|
1442
1443
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1443
1444
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1444
1445
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1446,14 +1447,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1446
1447
|
if (loc) {
|
|
1447
1448
|
let rootCauseNode = culprit;
|
|
1448
1449
|
if (loc.fileNode) {
|
|
1449
|
-
|
|
1450
|
+
path54.push(loc.fileNode);
|
|
1450
1451
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1451
1452
|
rootCauseNode = loc.fileNode;
|
|
1452
1453
|
}
|
|
1453
1454
|
return import_types.RootCauseResultSchema.parse({
|
|
1454
1455
|
rootCauseNode,
|
|
1455
1456
|
rootCauseReason: loc.rootCauseReason,
|
|
1456
|
-
traversalPath:
|
|
1457
|
+
traversalPath: path54,
|
|
1457
1458
|
edgeProvenances,
|
|
1458
1459
|
confidence,
|
|
1459
1460
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1465,7 +1466,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1465
1466
|
return import_types.RootCauseResultSchema.parse({
|
|
1466
1467
|
rootCauseNode: culprit,
|
|
1467
1468
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1468
|
-
traversalPath:
|
|
1469
|
+
traversalPath: path54,
|
|
1469
1470
|
edgeProvenances,
|
|
1470
1471
|
confidence,
|
|
1471
1472
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2892,6 +2893,18 @@ async function handleSpan(ctx, span) {
|
|
|
2892
2893
|
callSiteEvidence
|
|
2893
2894
|
);
|
|
2894
2895
|
if (result) affectedNode = targetId;
|
|
2896
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
2897
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
2898
|
+
upsertObservedEdge(
|
|
2899
|
+
ctx.graph,
|
|
2900
|
+
import_types3.EdgeType.CALLS,
|
|
2901
|
+
observedSource(),
|
|
2902
|
+
collectionId,
|
|
2903
|
+
ts,
|
|
2904
|
+
isError,
|
|
2905
|
+
callSiteEvidence
|
|
2906
|
+
);
|
|
2907
|
+
}
|
|
2895
2908
|
}
|
|
2896
2909
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2897
2910
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -5736,7 +5749,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5736
5749
|
|
|
5737
5750
|
// src/extract/calls/index.ts
|
|
5738
5751
|
init_cjs_shims();
|
|
5739
|
-
var
|
|
5752
|
+
var import_types21 = require("@neat.is/types");
|
|
5740
5753
|
|
|
5741
5754
|
// src/extract/calls/http.ts
|
|
5742
5755
|
init_cjs_shims();
|
|
@@ -6449,15 +6462,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6449
6462
|
return out;
|
|
6450
6463
|
}
|
|
6451
6464
|
|
|
6465
|
+
// src/extract/calls/mongoose.ts
|
|
6466
|
+
init_cjs_shims();
|
|
6467
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6468
|
+
var import_types20 = require("@neat.is/types");
|
|
6469
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
6470
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
6471
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
6472
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
6473
|
+
"advice",
|
|
6474
|
+
"energy",
|
|
6475
|
+
"excretion",
|
|
6476
|
+
"digestion",
|
|
6477
|
+
"cooperation",
|
|
6478
|
+
"health",
|
|
6479
|
+
"justice",
|
|
6480
|
+
"labour",
|
|
6481
|
+
"machinery",
|
|
6482
|
+
"equipment",
|
|
6483
|
+
"information",
|
|
6484
|
+
"pollution",
|
|
6485
|
+
"sewage",
|
|
6486
|
+
"paper",
|
|
6487
|
+
"money",
|
|
6488
|
+
"species",
|
|
6489
|
+
"series",
|
|
6490
|
+
"rain",
|
|
6491
|
+
"rice",
|
|
6492
|
+
"fish",
|
|
6493
|
+
"sheep",
|
|
6494
|
+
"moose",
|
|
6495
|
+
"deer",
|
|
6496
|
+
"news",
|
|
6497
|
+
"expertise",
|
|
6498
|
+
"status",
|
|
6499
|
+
"media"
|
|
6500
|
+
]);
|
|
6501
|
+
var PLURALIZE_RULES = [
|
|
6502
|
+
[/human$/gi, "humans"],
|
|
6503
|
+
[/(m)an$/gi, "$1en"],
|
|
6504
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
6505
|
+
[/(child)$/gi, "$1ren"],
|
|
6506
|
+
[/^(ox)$/gi, "$1en"],
|
|
6507
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
6508
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
6509
|
+
[/(alias|status)$/gi, "$1es"],
|
|
6510
|
+
[/(bu)s$/gi, "$1ses"],
|
|
6511
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
6512
|
+
[/([ti])um$/gi, "$1a"],
|
|
6513
|
+
[/sis$/gi, "ses"],
|
|
6514
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
6515
|
+
[/(hive)$/gi, "$1s"],
|
|
6516
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
6517
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
6518
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
6519
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
6520
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
6521
|
+
[/(quiz)$/gi, "$1zes"],
|
|
6522
|
+
[/s$/gi, "s"],
|
|
6523
|
+
[/([^a-z])$/, "$1"],
|
|
6524
|
+
[/$/gi, "s"]
|
|
6525
|
+
];
|
|
6526
|
+
function pluralizeCollection(name) {
|
|
6527
|
+
const str = name.toLowerCase();
|
|
6528
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
6529
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
6530
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
6531
|
+
}
|
|
6532
|
+
return str;
|
|
6533
|
+
}
|
|
6534
|
+
var MODEL_METHODS = [
|
|
6535
|
+
"find",
|
|
6536
|
+
"findOne",
|
|
6537
|
+
"findById",
|
|
6538
|
+
"findByIdAndUpdate",
|
|
6539
|
+
"findByIdAndDelete",
|
|
6540
|
+
"findOneAndUpdate",
|
|
6541
|
+
"findOneAndDelete",
|
|
6542
|
+
"findOneAndReplace",
|
|
6543
|
+
"countDocuments",
|
|
6544
|
+
"estimatedDocumentCount",
|
|
6545
|
+
"distinct",
|
|
6546
|
+
"aggregate",
|
|
6547
|
+
"exists",
|
|
6548
|
+
"where",
|
|
6549
|
+
"populate",
|
|
6550
|
+
"watch",
|
|
6551
|
+
"hydrate",
|
|
6552
|
+
"create",
|
|
6553
|
+
"insertMany",
|
|
6554
|
+
"insertOne",
|
|
6555
|
+
"updateOne",
|
|
6556
|
+
"updateMany",
|
|
6557
|
+
"replaceOne",
|
|
6558
|
+
"deleteOne",
|
|
6559
|
+
"deleteMany",
|
|
6560
|
+
"bulkWrite",
|
|
6561
|
+
"bulkSave",
|
|
6562
|
+
"save",
|
|
6563
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
6564
|
+
"count",
|
|
6565
|
+
"update",
|
|
6566
|
+
"remove",
|
|
6567
|
+
"findOneAndRemove",
|
|
6568
|
+
"findByIdAndRemove"
|
|
6569
|
+
];
|
|
6570
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
6571
|
+
function schemaCollectionVars(content) {
|
|
6572
|
+
const out = /* @__PURE__ */ new Map();
|
|
6573
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
6574
|
+
let m;
|
|
6575
|
+
while ((m = re.exec(content)) !== null) {
|
|
6576
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
6577
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
6578
|
+
if (opt) out.set(m[1], opt[1]);
|
|
6579
|
+
}
|
|
6580
|
+
return out;
|
|
6581
|
+
}
|
|
6582
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
6583
|
+
const trimmed = rest.trim();
|
|
6584
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
6585
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
6586
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
6587
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
6588
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
6589
|
+
}
|
|
6590
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
6591
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
6592
|
+
}
|
|
6593
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
6594
|
+
}
|
|
6595
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
6596
|
+
const schemaColl = schemaCollectionVars(content);
|
|
6597
|
+
const out = [];
|
|
6598
|
+
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;
|
|
6599
|
+
let m;
|
|
6600
|
+
while ((m = re.exec(content)) !== null) {
|
|
6601
|
+
out.push({
|
|
6602
|
+
modelName: m[2],
|
|
6603
|
+
varName: m[1] ?? null,
|
|
6604
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
6605
|
+
matchText: m[0]
|
|
6606
|
+
});
|
|
6607
|
+
}
|
|
6608
|
+
return out;
|
|
6609
|
+
}
|
|
6610
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
6611
|
+
const line = lineOf(file.content, matchText);
|
|
6612
|
+
return {
|
|
6613
|
+
infraId: (0, import_types20.infraId)(r.kind, r.name),
|
|
6614
|
+
name: r.name,
|
|
6615
|
+
kind: r.kind,
|
|
6616
|
+
edgeType: "CALLS",
|
|
6617
|
+
confidenceKind: "verified-call-site",
|
|
6618
|
+
evidence: { file: import_node_path31.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
6619
|
+
};
|
|
6620
|
+
}
|
|
6621
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
6622
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
6623
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
6624
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
6625
|
+
const content = file.content;
|
|
6626
|
+
const out = [];
|
|
6627
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6628
|
+
const push = (r, matchText) => {
|
|
6629
|
+
const key = `${r.kind}/${r.name}`;
|
|
6630
|
+
if (seen.has(key)) return;
|
|
6631
|
+
seen.add(key);
|
|
6632
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
6633
|
+
};
|
|
6634
|
+
if (hasMongoose) {
|
|
6635
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
6636
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
6637
|
+
}
|
|
6638
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
6639
|
+
let c;
|
|
6640
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
6641
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
6642
|
+
}
|
|
6643
|
+
return out;
|
|
6644
|
+
}
|
|
6645
|
+
function parseDestructure(inner, specifier, out) {
|
|
6646
|
+
for (const raw of inner.split(",")) {
|
|
6647
|
+
const part = raw.trim();
|
|
6648
|
+
if (!part) continue;
|
|
6649
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
6650
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
6651
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
6652
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
6653
|
+
else {
|
|
6654
|
+
const id = /^(\w+)$/.exec(part);
|
|
6655
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6659
|
+
function parseImportBindings(content) {
|
|
6660
|
+
const out = [];
|
|
6661
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
6662
|
+
let m;
|
|
6663
|
+
while ((m = esm.exec(content)) !== null) {
|
|
6664
|
+
const clause = m[1].trim();
|
|
6665
|
+
const spec = m[2];
|
|
6666
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
6667
|
+
if (ns) {
|
|
6668
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
6669
|
+
continue;
|
|
6670
|
+
}
|
|
6671
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
6672
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
6673
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
6674
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
6675
|
+
}
|
|
6676
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
6677
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
6678
|
+
const lhs = m[1];
|
|
6679
|
+
const spec = m[2];
|
|
6680
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
6681
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
6682
|
+
}
|
|
6683
|
+
return out;
|
|
6684
|
+
}
|
|
6685
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
6686
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
6687
|
+
if (defs.length === 0) return null;
|
|
6688
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
6689
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
6690
|
+
const byName = new Map(byVar);
|
|
6691
|
+
let def;
|
|
6692
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
6693
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
6694
|
+
if (!def) {
|
|
6695
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
6696
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
6697
|
+
}
|
|
6698
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
6699
|
+
return { byName, ...def ? { default: def } : {} };
|
|
6700
|
+
}
|
|
6701
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
6702
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
6703
|
+
if (mongooseFiles.length === 0) return [];
|
|
6704
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
6705
|
+
const registry = /* @__PURE__ */ new Map();
|
|
6706
|
+
for (const f of mongooseFiles) {
|
|
6707
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
6708
|
+
if (fx) registry.set(toPosix2(import_node_path31.default.relative(serviceDir, f.path)), fx);
|
|
6709
|
+
}
|
|
6710
|
+
if (registry.size === 0) return [];
|
|
6711
|
+
const out = [];
|
|
6712
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6713
|
+
for (const f of files) {
|
|
6714
|
+
const bindings = parseImportBindings(f.content);
|
|
6715
|
+
if (bindings.length === 0) continue;
|
|
6716
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
6717
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
6718
|
+
for (const b of bindings) {
|
|
6719
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path31.default.dirname(f.path), serviceDir, null);
|
|
6720
|
+
if (!resolvedRel) continue;
|
|
6721
|
+
const fx = registry.get(resolvedRel);
|
|
6722
|
+
if (!fx) continue;
|
|
6723
|
+
if (b.kind === "named" && b.exportName) {
|
|
6724
|
+
const coll = fx.byName.get(b.exportName);
|
|
6725
|
+
if (coll) directColl.set(b.local, coll);
|
|
6726
|
+
} else if (b.kind === "default") {
|
|
6727
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6728
|
+
} else {
|
|
6729
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6730
|
+
nsExports.set(b.local, fx);
|
|
6731
|
+
}
|
|
6732
|
+
}
|
|
6733
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
6734
|
+
const localDefs = new Set(
|
|
6735
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
6736
|
+
);
|
|
6737
|
+
const emit = (collection, matchText) => {
|
|
6738
|
+
const key = `${f.path}::${collection}`;
|
|
6739
|
+
if (seen.has(key)) return;
|
|
6740
|
+
seen.add(key);
|
|
6741
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
6742
|
+
};
|
|
6743
|
+
for (const [local, collection] of directColl) {
|
|
6744
|
+
if (localDefs.has(local)) continue;
|
|
6745
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6746
|
+
let qm;
|
|
6747
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
6748
|
+
}
|
|
6749
|
+
for (const [local, fx] of nsExports) {
|
|
6750
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6751
|
+
let qm;
|
|
6752
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
6753
|
+
const coll = fx.byName.get(qm[1]);
|
|
6754
|
+
if (coll) emit(coll, qm[0]);
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
return out;
|
|
6759
|
+
}
|
|
6760
|
+
|
|
6452
6761
|
// src/extract/calls/index.ts
|
|
6453
6762
|
function edgeTypeFromEndpoint(ep) {
|
|
6454
6763
|
switch (ep.edgeType) {
|
|
6455
6764
|
case "PUBLISHES_TO":
|
|
6456
|
-
return
|
|
6765
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6457
6766
|
case "CONSUMES_FROM":
|
|
6458
|
-
return
|
|
6767
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6459
6768
|
default:
|
|
6460
|
-
return
|
|
6769
|
+
return import_types21.EdgeType.CALLS;
|
|
6461
6770
|
}
|
|
6462
6771
|
}
|
|
6463
6772
|
function isAwsKind(kind) {
|
|
@@ -6469,23 +6778,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6469
6778
|
for (const service of services) {
|
|
6470
6779
|
const files = await loadSourceFiles(service.dir);
|
|
6471
6780
|
const endpoints = [];
|
|
6781
|
+
const maskedFiles = [];
|
|
6472
6782
|
for (const file of files) {
|
|
6473
6783
|
if (isTestPath(file.path)) continue;
|
|
6474
6784
|
const masked = maskCommentsInSource(file.content);
|
|
6475
6785
|
const maskedFile = { path: file.path, content: masked };
|
|
6786
|
+
maskedFiles.push(maskedFile);
|
|
6476
6787
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6477
6788
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6478
6789
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6479
6790
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6480
6791
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6792
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6481
6793
|
}
|
|
6794
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6482
6795
|
if (endpoints.length === 0) continue;
|
|
6483
6796
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6484
6797
|
for (const ep of endpoints) {
|
|
6485
6798
|
if (!graph.hasNode(ep.infraId)) {
|
|
6486
6799
|
const node = {
|
|
6487
6800
|
id: ep.infraId,
|
|
6488
|
-
type:
|
|
6801
|
+
type: import_types21.NodeType.InfraNode,
|
|
6489
6802
|
name: ep.name,
|
|
6490
6803
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6491
6804
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6497,7 +6810,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6497
6810
|
nodesAdded++;
|
|
6498
6811
|
}
|
|
6499
6812
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6500
|
-
const confidence = (0,
|
|
6813
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6501
6814
|
const relFile = toPosix2(ep.evidence.file);
|
|
6502
6815
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6503
6816
|
graph,
|
|
@@ -6507,7 +6820,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6507
6820
|
);
|
|
6508
6821
|
nodesAdded += n;
|
|
6509
6822
|
edgesAdded += e;
|
|
6510
|
-
if (!(0,
|
|
6823
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6511
6824
|
noteExtractedDropped({
|
|
6512
6825
|
source: fileNodeId,
|
|
6513
6826
|
target: ep.infraId,
|
|
@@ -6527,7 +6840,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6527
6840
|
source: fileNodeId,
|
|
6528
6841
|
target: ep.infraId,
|
|
6529
6842
|
type: edgeType,
|
|
6530
|
-
provenance:
|
|
6843
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6531
6844
|
confidence,
|
|
6532
6845
|
evidence: ep.evidence
|
|
6533
6846
|
};
|
|
@@ -6553,16 +6866,16 @@ init_cjs_shims();
|
|
|
6553
6866
|
|
|
6554
6867
|
// src/extract/infra/docker-compose.ts
|
|
6555
6868
|
init_cjs_shims();
|
|
6556
|
-
var
|
|
6557
|
-
var
|
|
6869
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6870
|
+
var import_types23 = require("@neat.is/types");
|
|
6558
6871
|
|
|
6559
6872
|
// src/extract/infra/shared.ts
|
|
6560
6873
|
init_cjs_shims();
|
|
6561
|
-
var
|
|
6874
|
+
var import_types22 = require("@neat.is/types");
|
|
6562
6875
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6563
6876
|
return {
|
|
6564
|
-
id: (0,
|
|
6565
|
-
type:
|
|
6877
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6878
|
+
type: import_types22.NodeType.InfraNode,
|
|
6566
6879
|
name,
|
|
6567
6880
|
provider,
|
|
6568
6881
|
kind,
|
|
@@ -6606,8 +6919,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6606
6919
|
source: anchorId,
|
|
6607
6920
|
target: node.id,
|
|
6608
6921
|
type: edgeType,
|
|
6609
|
-
provenance:
|
|
6610
|
-
confidence: (0,
|
|
6922
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6923
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6611
6924
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6612
6925
|
};
|
|
6613
6926
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6624,7 +6937,7 @@ function dependsOnList(value) {
|
|
|
6624
6937
|
}
|
|
6625
6938
|
function serviceNameToServiceNode(name, services) {
|
|
6626
6939
|
for (const s of services) {
|
|
6627
|
-
if (s.node.name === name ||
|
|
6940
|
+
if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
|
|
6628
6941
|
}
|
|
6629
6942
|
return null;
|
|
6630
6943
|
}
|
|
@@ -6633,7 +6946,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6633
6946
|
let edgesAdded = 0;
|
|
6634
6947
|
let composePath = null;
|
|
6635
6948
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6636
|
-
const abs =
|
|
6949
|
+
const abs = import_node_path32.default.join(scanPath, name);
|
|
6637
6950
|
if (await exists(abs)) {
|
|
6638
6951
|
composePath = abs;
|
|
6639
6952
|
break;
|
|
@@ -6646,13 +6959,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6646
6959
|
} catch (err) {
|
|
6647
6960
|
recordExtractionError(
|
|
6648
6961
|
"infra docker-compose",
|
|
6649
|
-
|
|
6962
|
+
import_node_path32.default.relative(scanPath, composePath),
|
|
6650
6963
|
err
|
|
6651
6964
|
);
|
|
6652
6965
|
return { nodesAdded, edgesAdded };
|
|
6653
6966
|
}
|
|
6654
6967
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6655
|
-
const evidenceFile =
|
|
6968
|
+
const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
|
|
6656
6969
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6657
6970
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6658
6971
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6674,15 +6987,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6674
6987
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6675
6988
|
const targetId = composeNameToNodeId.get(dep);
|
|
6676
6989
|
if (!targetId) continue;
|
|
6677
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
6990
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6678
6991
|
if (graph.hasEdge(edgeId)) continue;
|
|
6679
6992
|
const edge = {
|
|
6680
6993
|
id: edgeId,
|
|
6681
6994
|
source: sourceId,
|
|
6682
6995
|
target: targetId,
|
|
6683
|
-
type:
|
|
6684
|
-
provenance:
|
|
6685
|
-
confidence: (0,
|
|
6996
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
6997
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
6998
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6686
6999
|
evidence: { file: evidenceFile }
|
|
6687
7000
|
};
|
|
6688
7001
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6694,9 +7007,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6694
7007
|
|
|
6695
7008
|
// src/extract/infra/dockerfile.ts
|
|
6696
7009
|
init_cjs_shims();
|
|
6697
|
-
var
|
|
7010
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6698
7011
|
var import_node_fs16 = require("fs");
|
|
6699
|
-
var
|
|
7012
|
+
var import_types24 = require("@neat.is/types");
|
|
6700
7013
|
function readDockerfile(content) {
|
|
6701
7014
|
let image = null;
|
|
6702
7015
|
const ports = [];
|
|
@@ -6725,7 +7038,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6725
7038
|
let nodesAdded = 0;
|
|
6726
7039
|
let edgesAdded = 0;
|
|
6727
7040
|
for (const service of services) {
|
|
6728
|
-
const dockerfilePath =
|
|
7041
|
+
const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
|
|
6729
7042
|
if (!await exists(dockerfilePath)) continue;
|
|
6730
7043
|
let content;
|
|
6731
7044
|
try {
|
|
@@ -6733,7 +7046,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6733
7046
|
} catch (err) {
|
|
6734
7047
|
recordExtractionError(
|
|
6735
7048
|
"infra dockerfile",
|
|
6736
|
-
|
|
7049
|
+
import_node_path33.default.relative(scanPath, dockerfilePath),
|
|
6737
7050
|
err
|
|
6738
7051
|
);
|
|
6739
7052
|
continue;
|
|
@@ -6745,8 +7058,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6745
7058
|
graph.addNode(node.id, node);
|
|
6746
7059
|
nodesAdded++;
|
|
6747
7060
|
}
|
|
6748
|
-
const relDockerfile = toPosix2(
|
|
6749
|
-
const evidenceFile = toPosix2(
|
|
7061
|
+
const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
|
|
7062
|
+
const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
|
|
6750
7063
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6751
7064
|
graph,
|
|
6752
7065
|
service.pkg.name,
|
|
@@ -6755,15 +7068,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6755
7068
|
);
|
|
6756
7069
|
nodesAdded += fn;
|
|
6757
7070
|
edgesAdded += fe;
|
|
6758
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7071
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6759
7072
|
if (!graph.hasEdge(edgeId)) {
|
|
6760
7073
|
const edge = {
|
|
6761
7074
|
id: edgeId,
|
|
6762
7075
|
source: fileNodeId,
|
|
6763
7076
|
target: node.id,
|
|
6764
|
-
type:
|
|
6765
|
-
provenance:
|
|
6766
|
-
confidence: (0,
|
|
7077
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7078
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7079
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6767
7080
|
evidence: {
|
|
6768
7081
|
file: evidenceFile,
|
|
6769
7082
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6778,15 +7091,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6778
7091
|
graph.addNode(portNode.id, portNode);
|
|
6779
7092
|
nodesAdded++;
|
|
6780
7093
|
}
|
|
6781
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7094
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6782
7095
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6783
7096
|
const portEdge = {
|
|
6784
7097
|
id: portEdgeId,
|
|
6785
7098
|
source: fileNodeId,
|
|
6786
7099
|
target: portNode.id,
|
|
6787
|
-
type:
|
|
6788
|
-
provenance:
|
|
6789
|
-
confidence: (0,
|
|
7100
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7101
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7102
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6790
7103
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6791
7104
|
};
|
|
6792
7105
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6799,8 +7112,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6799
7112
|
// src/extract/infra/terraform.ts
|
|
6800
7113
|
init_cjs_shims();
|
|
6801
7114
|
var import_node_fs17 = require("fs");
|
|
6802
|
-
var
|
|
6803
|
-
var
|
|
7115
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7116
|
+
var import_types25 = require("@neat.is/types");
|
|
6804
7117
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6805
7118
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6806
7119
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6810,11 +7123,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6810
7123
|
for (const entry2 of entries) {
|
|
6811
7124
|
if (entry2.isDirectory()) {
|
|
6812
7125
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
6813
|
-
const child =
|
|
7126
|
+
const child = import_node_path34.default.join(start, entry2.name);
|
|
6814
7127
|
if (await isPythonVenvDir(child)) continue;
|
|
6815
7128
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6816
7129
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
6817
|
-
out.push(
|
|
7130
|
+
out.push(import_node_path34.default.join(start, entry2.name));
|
|
6818
7131
|
}
|
|
6819
7132
|
}
|
|
6820
7133
|
return out;
|
|
@@ -6846,7 +7159,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6846
7159
|
const files = await walkTfFiles(scanPath);
|
|
6847
7160
|
for (const file of files) {
|
|
6848
7161
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
6849
|
-
const evidenceFile = toPosix2(
|
|
7162
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
|
|
6850
7163
|
const resources = [];
|
|
6851
7164
|
const byKey = /* @__PURE__ */ new Map();
|
|
6852
7165
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6881,16 +7194,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6881
7194
|
if (!target) continue;
|
|
6882
7195
|
if (seen.has(target.nodeId)) continue;
|
|
6883
7196
|
seen.add(target.nodeId);
|
|
6884
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7197
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6885
7198
|
if (graph.hasEdge(edgeId)) continue;
|
|
6886
7199
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6887
7200
|
const edge = {
|
|
6888
7201
|
id: edgeId,
|
|
6889
7202
|
source: resource.nodeId,
|
|
6890
7203
|
target: target.nodeId,
|
|
6891
|
-
type:
|
|
6892
|
-
provenance:
|
|
6893
|
-
confidence: (0,
|
|
7204
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7205
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7206
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6894
7207
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6895
7208
|
};
|
|
6896
7209
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6904,7 +7217,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6904
7217
|
// src/extract/infra/k8s.ts
|
|
6905
7218
|
init_cjs_shims();
|
|
6906
7219
|
var import_node_fs18 = require("fs");
|
|
6907
|
-
var
|
|
7220
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6908
7221
|
var import_yaml3 = require("yaml");
|
|
6909
7222
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6910
7223
|
Service: "k8s-service",
|
|
@@ -6922,11 +7235,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6922
7235
|
for (const entry2 of entries) {
|
|
6923
7236
|
if (entry2.isDirectory()) {
|
|
6924
7237
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
6925
|
-
const child =
|
|
7238
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
6926
7239
|
if (await isPythonVenvDir(child)) continue;
|
|
6927
7240
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6928
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6929
|
-
out.push(
|
|
7241
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry2.name))) {
|
|
7242
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
6930
7243
|
}
|
|
6931
7244
|
}
|
|
6932
7245
|
return out;
|
|
@@ -6960,13 +7273,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6960
7273
|
// src/extract/infra/cloudflare.ts
|
|
6961
7274
|
init_cjs_shims();
|
|
6962
7275
|
var import_node_fs19 = require("fs");
|
|
6963
|
-
var
|
|
7276
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6964
7277
|
var import_smol_toml2 = require("smol-toml");
|
|
6965
|
-
var
|
|
7278
|
+
var import_types26 = require("@neat.is/types");
|
|
6966
7279
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
6967
7280
|
async function readWranglerConfig(dir) {
|
|
6968
7281
|
for (const filename of WRANGLER_FILENAMES) {
|
|
6969
|
-
const abs =
|
|
7282
|
+
const abs = import_node_path36.default.join(dir, filename);
|
|
6970
7283
|
if (!await exists(abs)) continue;
|
|
6971
7284
|
const raw = await import_node_fs19.promises.readFile(abs, "utf8");
|
|
6972
7285
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7010,8 +7323,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7010
7323
|
source: anchorId,
|
|
7011
7324
|
target: node.id,
|
|
7012
7325
|
type: edgeType,
|
|
7013
|
-
provenance:
|
|
7014
|
-
confidence: (0,
|
|
7326
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7327
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7015
7328
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7016
7329
|
};
|
|
7017
7330
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7029,11 +7342,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7029
7342
|
try {
|
|
7030
7343
|
read = await readWranglerConfig(service.dir);
|
|
7031
7344
|
} catch (err) {
|
|
7032
|
-
recordExtractionError("infra cloudflare",
|
|
7345
|
+
recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
|
|
7033
7346
|
continue;
|
|
7034
7347
|
}
|
|
7035
7348
|
if (!read || !read.config.name) continue;
|
|
7036
|
-
const evidenceFile = toPosix2(
|
|
7349
|
+
const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
|
|
7037
7350
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7038
7351
|
}
|
|
7039
7352
|
for (const worker of discovered) {
|
|
@@ -7045,7 +7358,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7045
7358
|
}
|
|
7046
7359
|
let anchorId = service.node.id;
|
|
7047
7360
|
if (config.main) {
|
|
7048
|
-
const entryRelPath = toPosix2(
|
|
7361
|
+
const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
|
|
7049
7362
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7050
7363
|
graph,
|
|
7051
7364
|
service.pkg.name,
|
|
@@ -7072,15 +7385,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7072
7385
|
nodesAdded++;
|
|
7073
7386
|
}
|
|
7074
7387
|
if (runtimeNode.id !== anchorId) {
|
|
7075
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7388
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7076
7389
|
if (!graph.hasEdge(runsOnId)) {
|
|
7077
7390
|
const edge = {
|
|
7078
7391
|
id: runsOnId,
|
|
7079
7392
|
source: anchorId,
|
|
7080
7393
|
target: runtimeNode.id,
|
|
7081
|
-
type:
|
|
7082
|
-
provenance:
|
|
7083
|
-
confidence: (0,
|
|
7394
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7395
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7396
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7084
7397
|
evidence: {
|
|
7085
7398
|
file: evidenceFile,
|
|
7086
7399
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7094,7 +7407,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7094
7407
|
const result = addResourceEdge(
|
|
7095
7408
|
graph,
|
|
7096
7409
|
anchorId,
|
|
7097
|
-
|
|
7410
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7098
7411
|
"cloudflare-route",
|
|
7099
7412
|
route,
|
|
7100
7413
|
evidenceFile,
|
|
@@ -7118,7 +7431,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7118
7431
|
const result = addResourceEdge(
|
|
7119
7432
|
graph,
|
|
7120
7433
|
anchorId,
|
|
7121
|
-
|
|
7434
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7122
7435
|
group.kind,
|
|
7123
7436
|
name,
|
|
7124
7437
|
evidenceFile,
|
|
@@ -7132,7 +7445,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7132
7445
|
const result = addResourceEdge(
|
|
7133
7446
|
graph,
|
|
7134
7447
|
anchorId,
|
|
7135
|
-
|
|
7448
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7136
7449
|
"cloudflare-cron",
|
|
7137
7450
|
cron,
|
|
7138
7451
|
evidenceFile,
|
|
@@ -7145,7 +7458,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7145
7458
|
const result = addResourceEdge(
|
|
7146
7459
|
graph,
|
|
7147
7460
|
anchorId,
|
|
7148
|
-
|
|
7461
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7149
7462
|
"cloudflare-env-var",
|
|
7150
7463
|
varName,
|
|
7151
7464
|
evidenceFile,
|
|
@@ -7158,15 +7471,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7158
7471
|
if (!svc.service) continue;
|
|
7159
7472
|
const target = workerIndex.get(svc.service);
|
|
7160
7473
|
if (target && target.anchorId !== anchorId) {
|
|
7161
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7474
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7162
7475
|
if (!graph.hasEdge(edgeId)) {
|
|
7163
7476
|
const edge = {
|
|
7164
7477
|
id: edgeId,
|
|
7165
7478
|
source: anchorId,
|
|
7166
7479
|
target: target.anchorId,
|
|
7167
|
-
type:
|
|
7168
|
-
provenance:
|
|
7169
|
-
confidence: (0,
|
|
7480
|
+
type: import_types26.EdgeType.CALLS,
|
|
7481
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7482
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7170
7483
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7171
7484
|
};
|
|
7172
7485
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7177,7 +7490,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7177
7490
|
const result = addResourceEdge(
|
|
7178
7491
|
graph,
|
|
7179
7492
|
anchorId,
|
|
7180
|
-
|
|
7493
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7181
7494
|
"cloudflare-service-binding",
|
|
7182
7495
|
svc.service,
|
|
7183
7496
|
evidenceFile,
|
|
@@ -7193,12 +7506,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7193
7506
|
// src/extract/infra/vercel.ts
|
|
7194
7507
|
init_cjs_shims();
|
|
7195
7508
|
var import_node_fs20 = require("fs");
|
|
7196
|
-
var
|
|
7197
|
-
var
|
|
7509
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7510
|
+
var import_types27 = require("@neat.is/types");
|
|
7198
7511
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7199
7512
|
async function readVercelConfig(dir) {
|
|
7200
7513
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7201
|
-
const abs =
|
|
7514
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7202
7515
|
if (!await exists(abs)) continue;
|
|
7203
7516
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7204
7517
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7207,7 +7520,7 @@ async function readVercelConfig(dir) {
|
|
|
7207
7520
|
return null;
|
|
7208
7521
|
}
|
|
7209
7522
|
async function readLinkedProjectName(dir) {
|
|
7210
|
-
const abs =
|
|
7523
|
+
const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
|
|
7211
7524
|
if (!await exists(abs)) return void 0;
|
|
7212
7525
|
const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
|
|
7213
7526
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7225,7 +7538,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7225
7538
|
read = await readVercelConfig(service.dir);
|
|
7226
7539
|
projectName = await readLinkedProjectName(service.dir);
|
|
7227
7540
|
} catch (err) {
|
|
7228
|
-
recordExtractionError("infra vercel",
|
|
7541
|
+
recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7229
7542
|
continue;
|
|
7230
7543
|
}
|
|
7231
7544
|
if (!read && !projectName) continue;
|
|
@@ -7241,7 +7554,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7241
7554
|
const anchorId = service.node.id;
|
|
7242
7555
|
if (!read) continue;
|
|
7243
7556
|
const { config, relFile, raw } = read;
|
|
7244
|
-
const evidenceFile = toPosix2(
|
|
7557
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
|
|
7245
7558
|
const add = (edgeType, kind, name) => {
|
|
7246
7559
|
if (!name) return;
|
|
7247
7560
|
const result = emitPlatformResourceEdge(
|
|
@@ -7257,12 +7570,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7257
7570
|
nodesAdded += result.nodesAdded;
|
|
7258
7571
|
edgesAdded += result.edgesAdded;
|
|
7259
7572
|
};
|
|
7260
|
-
add(
|
|
7261
|
-
for (const cron of config.crons ?? []) add(
|
|
7262
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7263
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
7573
|
+
add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7574
|
+
for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7575
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7576
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7264
7577
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7265
|
-
add(
|
|
7578
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7266
7579
|
}
|
|
7267
7580
|
}
|
|
7268
7581
|
return { nodesAdded, edgesAdded };
|
|
@@ -7271,13 +7584,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7271
7584
|
// src/extract/infra/railway.ts
|
|
7272
7585
|
init_cjs_shims();
|
|
7273
7586
|
var import_node_fs21 = require("fs");
|
|
7274
|
-
var
|
|
7587
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7275
7588
|
var import_smol_toml3 = require("smol-toml");
|
|
7276
|
-
var
|
|
7589
|
+
var import_types28 = require("@neat.is/types");
|
|
7277
7590
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7278
7591
|
async function readRailwayConfig(dir) {
|
|
7279
7592
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7280
|
-
const abs =
|
|
7593
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7281
7594
|
if (!await exists(abs)) continue;
|
|
7282
7595
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7283
7596
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7293,7 +7606,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7293
7606
|
try {
|
|
7294
7607
|
read = await readRailwayConfig(service.dir);
|
|
7295
7608
|
} catch (err) {
|
|
7296
|
-
recordExtractionError("infra railway",
|
|
7609
|
+
recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7297
7610
|
continue;
|
|
7298
7611
|
}
|
|
7299
7612
|
if (!read) continue;
|
|
@@ -7303,7 +7616,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7303
7616
|
}
|
|
7304
7617
|
const anchorId = service.node.id;
|
|
7305
7618
|
const { config, relFile, raw } = read;
|
|
7306
|
-
const evidenceFile = toPosix2(
|
|
7619
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7307
7620
|
const add = (edgeType, kind, name) => {
|
|
7308
7621
|
if (!name) return;
|
|
7309
7622
|
const result = emitPlatformResourceEdge(
|
|
@@ -7319,9 +7632,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7319
7632
|
nodesAdded += result.nodesAdded;
|
|
7320
7633
|
edgesAdded += result.edgesAdded;
|
|
7321
7634
|
};
|
|
7322
|
-
add(
|
|
7323
|
-
add(
|
|
7324
|
-
add(
|
|
7635
|
+
add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
|
|
7636
|
+
add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7637
|
+
add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7325
7638
|
}
|
|
7326
7639
|
return { nodesAdded, edgesAdded };
|
|
7327
7640
|
}
|
|
@@ -7329,12 +7642,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7329
7642
|
// src/extract/infra/supabase.ts
|
|
7330
7643
|
init_cjs_shims();
|
|
7331
7644
|
var import_node_fs22 = require("fs");
|
|
7332
|
-
var
|
|
7645
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7333
7646
|
var import_smol_toml4 = require("smol-toml");
|
|
7334
|
-
var
|
|
7647
|
+
var import_types29 = require("@neat.is/types");
|
|
7335
7648
|
async function readSupabaseConfig(dir) {
|
|
7336
|
-
const relFile =
|
|
7337
|
-
const abs =
|
|
7649
|
+
const relFile = import_node_path39.default.join("supabase", "config.toml");
|
|
7650
|
+
const abs = import_node_path39.default.join(dir, relFile);
|
|
7338
7651
|
if (!await exists(abs)) return null;
|
|
7339
7652
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7340
7653
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7348,7 +7661,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7348
7661
|
try {
|
|
7349
7662
|
read = await readSupabaseConfig(service.dir);
|
|
7350
7663
|
} catch (err) {
|
|
7351
|
-
recordExtractionError("infra supabase",
|
|
7664
|
+
recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7352
7665
|
continue;
|
|
7353
7666
|
}
|
|
7354
7667
|
if (!read) continue;
|
|
@@ -7363,7 +7676,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7363
7676
|
});
|
|
7364
7677
|
}
|
|
7365
7678
|
const anchorId = service.node.id;
|
|
7366
|
-
const evidenceFile = toPosix2(
|
|
7679
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7367
7680
|
const add = (edgeType, kind, name) => {
|
|
7368
7681
|
if (!name) return;
|
|
7369
7682
|
const result = emitPlatformResourceEdge(
|
|
@@ -7379,10 +7692,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7379
7692
|
nodesAdded += result.nodesAdded;
|
|
7380
7693
|
edgesAdded += result.edgesAdded;
|
|
7381
7694
|
};
|
|
7382
|
-
add(
|
|
7383
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7384
|
-
if (config.storage) add(
|
|
7385
|
-
if (config.auth) add(
|
|
7695
|
+
add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7696
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7697
|
+
if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7698
|
+
if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7386
7699
|
}
|
|
7387
7700
|
return { nodesAdded, edgesAdded };
|
|
7388
7701
|
}
|
|
@@ -7404,17 +7717,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7404
7717
|
}
|
|
7405
7718
|
|
|
7406
7719
|
// src/extract/index.ts
|
|
7407
|
-
var
|
|
7720
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7408
7721
|
|
|
7409
7722
|
// src/extract/retire.ts
|
|
7410
7723
|
init_cjs_shims();
|
|
7411
7724
|
var import_node_fs23 = require("fs");
|
|
7412
|
-
var
|
|
7413
|
-
var
|
|
7725
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7726
|
+
var import_types30 = require("@neat.is/types");
|
|
7414
7727
|
function dropOrphanedFileNodes(graph) {
|
|
7415
7728
|
const orphans = [];
|
|
7416
7729
|
graph.forEachNode((id, attrs) => {
|
|
7417
|
-
if (attrs.type !==
|
|
7730
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7418
7731
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7419
7732
|
orphans.push(id);
|
|
7420
7733
|
}
|
|
@@ -7427,14 +7740,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7427
7740
|
const bases = [scanPath, ...serviceDirs];
|
|
7428
7741
|
graph.forEachEdge((id, attrs) => {
|
|
7429
7742
|
const edge = attrs;
|
|
7430
|
-
if (edge.provenance !==
|
|
7743
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7431
7744
|
const evidenceFile = edge.evidence?.file;
|
|
7432
7745
|
if (!evidenceFile) return;
|
|
7433
|
-
if (
|
|
7746
|
+
if (import_node_path40.default.isAbsolute(evidenceFile)) {
|
|
7434
7747
|
if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7435
7748
|
return;
|
|
7436
7749
|
}
|
|
7437
|
-
const found = bases.some((base) => (0, import_node_fs23.existsSync)(
|
|
7750
|
+
const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path40.default.join(base, evidenceFile)));
|
|
7438
7751
|
if (!found) toDrop.push(id);
|
|
7439
7752
|
});
|
|
7440
7753
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7476,7 +7789,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7476
7789
|
}
|
|
7477
7790
|
const droppedEntries = drainDroppedExtracted();
|
|
7478
7791
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7479
|
-
const rejectedPath =
|
|
7792
|
+
const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7480
7793
|
try {
|
|
7481
7794
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7482
7795
|
} catch (err) {
|
|
@@ -7511,8 +7824,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7511
7824
|
// src/persist.ts
|
|
7512
7825
|
init_cjs_shims();
|
|
7513
7826
|
var import_node_fs24 = require("fs");
|
|
7514
|
-
var
|
|
7515
|
-
var
|
|
7827
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7828
|
+
var import_types31 = require("@neat.is/types");
|
|
7516
7829
|
var SCHEMA_VERSION = 4;
|
|
7517
7830
|
function migrateV1ToV2(payload) {
|
|
7518
7831
|
const nodes = payload.graph.nodes;
|
|
@@ -7534,12 +7847,12 @@ function migrateV2ToV3(payload) {
|
|
|
7534
7847
|
for (const edge of edges) {
|
|
7535
7848
|
const attrs = edge.attributes;
|
|
7536
7849
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7537
|
-
attrs.provenance =
|
|
7850
|
+
attrs.provenance = import_types31.Provenance.OBSERVED;
|
|
7538
7851
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7539
7852
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7540
7853
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7541
7854
|
if (type && source && target) {
|
|
7542
|
-
const newId = (0,
|
|
7855
|
+
const newId = (0, import_types31.observedEdgeId)(source, target, type);
|
|
7543
7856
|
attrs.id = newId;
|
|
7544
7857
|
if (edge.key) edge.key = newId;
|
|
7545
7858
|
}
|
|
@@ -7548,7 +7861,7 @@ function migrateV2ToV3(payload) {
|
|
|
7548
7861
|
return { ...payload, schemaVersion: 3 };
|
|
7549
7862
|
}
|
|
7550
7863
|
async function ensureDir(filePath) {
|
|
7551
|
-
await import_node_fs24.promises.mkdir(
|
|
7864
|
+
await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
|
|
7552
7865
|
}
|
|
7553
7866
|
async function saveGraphToDisk(graph, outPath) {
|
|
7554
7867
|
await ensureDir(outPath);
|
|
@@ -7629,23 +7942,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7629
7942
|
|
|
7630
7943
|
// src/projects.ts
|
|
7631
7944
|
init_cjs_shims();
|
|
7632
|
-
var
|
|
7945
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7633
7946
|
function pathsForProject(project, baseDir) {
|
|
7634
7947
|
if (project === DEFAULT_PROJECT) {
|
|
7635
7948
|
return {
|
|
7636
|
-
snapshotPath:
|
|
7637
|
-
errorsPath:
|
|
7638
|
-
staleEventsPath:
|
|
7639
|
-
embeddingsCachePath:
|
|
7640
|
-
policyViolationsPath:
|
|
7949
|
+
snapshotPath: import_node_path43.default.join(baseDir, "graph.json"),
|
|
7950
|
+
errorsPath: import_node_path43.default.join(baseDir, "errors.ndjson"),
|
|
7951
|
+
staleEventsPath: import_node_path43.default.join(baseDir, "stale-events.ndjson"),
|
|
7952
|
+
embeddingsCachePath: import_node_path43.default.join(baseDir, "embeddings.json"),
|
|
7953
|
+
policyViolationsPath: import_node_path43.default.join(baseDir, "policy-violations.ndjson")
|
|
7641
7954
|
};
|
|
7642
7955
|
}
|
|
7643
7956
|
return {
|
|
7644
|
-
snapshotPath:
|
|
7645
|
-
errorsPath:
|
|
7646
|
-
staleEventsPath:
|
|
7647
|
-
embeddingsCachePath:
|
|
7648
|
-
policyViolationsPath:
|
|
7957
|
+
snapshotPath: import_node_path43.default.join(baseDir, `${project}.json`),
|
|
7958
|
+
errorsPath: import_node_path43.default.join(baseDir, `errors.${project}.ndjson`),
|
|
7959
|
+
staleEventsPath: import_node_path43.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
7960
|
+
embeddingsCachePath: import_node_path43.default.join(baseDir, `embeddings.${project}.json`),
|
|
7961
|
+
policyViolationsPath: import_node_path43.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
7649
7962
|
};
|
|
7650
7963
|
}
|
|
7651
7964
|
var Projects = class {
|
|
@@ -7683,19 +7996,19 @@ var Projects = class {
|
|
|
7683
7996
|
init_cjs_shims();
|
|
7684
7997
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
7685
7998
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
7686
|
-
var
|
|
7999
|
+
var import_types34 = require("@neat.is/types");
|
|
7687
8000
|
|
|
7688
8001
|
// src/extend/index.ts
|
|
7689
8002
|
init_cjs_shims();
|
|
7690
8003
|
var import_node_fs26 = require("fs");
|
|
7691
|
-
var
|
|
8004
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
7692
8005
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
7693
8006
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
7694
8007
|
|
|
7695
8008
|
// src/installers/package-manager.ts
|
|
7696
8009
|
init_cjs_shims();
|
|
7697
8010
|
var import_node_fs25 = require("fs");
|
|
7698
|
-
var
|
|
8011
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7699
8012
|
var import_node_child_process = require("child_process");
|
|
7700
8013
|
var LOCKFILE_PRIORITY = [
|
|
7701
8014
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -7717,22 +8030,22 @@ async function exists2(p) {
|
|
|
7717
8030
|
}
|
|
7718
8031
|
}
|
|
7719
8032
|
async function detectPackageManager(serviceDir) {
|
|
7720
|
-
let dir =
|
|
8033
|
+
let dir = import_node_path44.default.resolve(serviceDir);
|
|
7721
8034
|
const stops = /* @__PURE__ */ new Set();
|
|
7722
8035
|
for (let i = 0; i < 64; i++) {
|
|
7723
8036
|
if (stops.has(dir)) break;
|
|
7724
8037
|
stops.add(dir);
|
|
7725
8038
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
7726
|
-
const lockPath =
|
|
8039
|
+
const lockPath = import_node_path44.default.join(dir, candidate.lockfile);
|
|
7727
8040
|
if (await exists2(lockPath)) {
|
|
7728
8041
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
7729
8042
|
}
|
|
7730
8043
|
}
|
|
7731
|
-
const parent =
|
|
8044
|
+
const parent = import_node_path44.default.dirname(dir);
|
|
7732
8045
|
if (parent === dir) break;
|
|
7733
8046
|
dir = parent;
|
|
7734
8047
|
}
|
|
7735
|
-
return { pm: "npm", cwd:
|
|
8048
|
+
return { pm: "npm", cwd: import_node_path44.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
7736
8049
|
}
|
|
7737
8050
|
async function runPackageManagerInstall(cmd) {
|
|
7738
8051
|
return new Promise((resolve) => {
|
|
@@ -7781,7 +8094,7 @@ async function fileExists2(p) {
|
|
|
7781
8094
|
}
|
|
7782
8095
|
}
|
|
7783
8096
|
async function readPackageJson(scanPath) {
|
|
7784
|
-
const pkgPath =
|
|
8097
|
+
const pkgPath = import_node_path45.default.join(scanPath, "package.json");
|
|
7785
8098
|
const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
|
|
7786
8099
|
return JSON.parse(raw);
|
|
7787
8100
|
}
|
|
@@ -7800,11 +8113,11 @@ async function findHookFiles(scanPath) {
|
|
|
7800
8113
|
for (const entry2 of entries) {
|
|
7801
8114
|
if (entry2.isDirectory()) {
|
|
7802
8115
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
7803
|
-
await walk3(
|
|
8116
|
+
await walk3(import_node_path45.default.join(dir, entry2.name));
|
|
7804
8117
|
} else if (entry2.isFile()) {
|
|
7805
8118
|
if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
|
|
7806
|
-
const rel =
|
|
7807
|
-
found.push(rel.split(
|
|
8119
|
+
const rel = import_node_path45.default.relative(scanPath, import_node_path45.default.join(dir, entry2.name));
|
|
8120
|
+
found.push(rel.split(import_node_path45.default.sep).join("/"));
|
|
7808
8121
|
}
|
|
7809
8122
|
}
|
|
7810
8123
|
}
|
|
@@ -7815,7 +8128,7 @@ async function findHookFiles(scanPath) {
|
|
|
7815
8128
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
7816
8129
|
let fallback = null;
|
|
7817
8130
|
for (const file of hookFiles) {
|
|
7818
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8131
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(scanPath, file), "utf8");
|
|
7819
8132
|
const patched = splicedContent(content, snippet2);
|
|
7820
8133
|
if (patched !== null) return { file, content, patched };
|
|
7821
8134
|
if (fallback === null) fallback = { file, content };
|
|
@@ -7823,11 +8136,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
7823
8136
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
7824
8137
|
}
|
|
7825
8138
|
function extendLogPath() {
|
|
7826
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8139
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path45.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
7827
8140
|
}
|
|
7828
8141
|
async function appendExtendLog(entry2) {
|
|
7829
8142
|
const logPath = extendLogPath();
|
|
7830
|
-
await import_node_fs26.promises.mkdir(
|
|
8143
|
+
await import_node_fs26.promises.mkdir(import_node_path45.default.dirname(logPath), { recursive: true });
|
|
7831
8144
|
await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
7832
8145
|
}
|
|
7833
8146
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -7886,7 +8199,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
7886
8199
|
}
|
|
7887
8200
|
async function describeProjectInstrumentation(ctx) {
|
|
7888
8201
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7889
|
-
const envNeat = await fileExists2(
|
|
8202
|
+
const envNeat = await fileExists2(import_node_path45.default.join(ctx.scanPath, ".env.neat"));
|
|
7890
8203
|
const registryInstrPackages = new Set(
|
|
7891
8204
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
7892
8205
|
);
|
|
@@ -7908,7 +8221,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
7908
8221
|
);
|
|
7909
8222
|
}
|
|
7910
8223
|
for (const file of hookFiles) {
|
|
7911
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8224
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
|
|
7912
8225
|
if (content.includes(args.registration_snippet)) {
|
|
7913
8226
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
7914
8227
|
}
|
|
@@ -7920,10 +8233,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
7920
8233
|
);
|
|
7921
8234
|
}
|
|
7922
8235
|
const primaryFile = primary.file;
|
|
7923
|
-
const primaryPath =
|
|
8236
|
+
const primaryPath = import_node_path45.default.join(ctx.scanPath, primaryFile);
|
|
7924
8237
|
const filesTouched = [];
|
|
7925
8238
|
const depsAdded = [];
|
|
7926
|
-
const pkgPath =
|
|
8239
|
+
const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
|
|
7927
8240
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7928
8241
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
7929
8242
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -7962,7 +8275,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7962
8275
|
};
|
|
7963
8276
|
}
|
|
7964
8277
|
for (const file of hookFiles) {
|
|
7965
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8278
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
|
|
7966
8279
|
if (content.includes(args.registration_snippet)) {
|
|
7967
8280
|
return {
|
|
7968
8281
|
library: args.library,
|
|
@@ -8003,7 +8316,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8003
8316
|
if (!match) {
|
|
8004
8317
|
return { undone: false, message: "no apply found for library" };
|
|
8005
8318
|
}
|
|
8006
|
-
const pkgPath =
|
|
8319
|
+
const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
|
|
8007
8320
|
if (await fileExists2(pkgPath)) {
|
|
8008
8321
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8009
8322
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8014,7 +8327,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8014
8327
|
}
|
|
8015
8328
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8016
8329
|
for (const file of hookFiles) {
|
|
8017
|
-
const filePath =
|
|
8330
|
+
const filePath = import_node_path45.default.join(ctx.scanPath, file);
|
|
8018
8331
|
const content = await import_node_fs26.promises.readFile(filePath, "utf8");
|
|
8019
8332
|
if (content.includes(match.registration_snippet)) {
|
|
8020
8333
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8030,39 +8343,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
8030
8343
|
|
|
8031
8344
|
// src/divergences.ts
|
|
8032
8345
|
init_cjs_shims();
|
|
8033
|
-
var
|
|
8346
|
+
var import_types32 = require("@neat.is/types");
|
|
8034
8347
|
function bucketKey(source, target, type) {
|
|
8035
8348
|
return `${type}|${source}|${target}`;
|
|
8036
8349
|
}
|
|
8037
8350
|
function bucketSourceFor(graph, edge) {
|
|
8038
|
-
if (edge.type !==
|
|
8039
|
-
const parsed = (0,
|
|
8351
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
|
|
8352
|
+
const parsed = (0, import_types32.parseFileId)(edge.source);
|
|
8040
8353
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8041
8354
|
const target = graph.getNodeAttributes(edge.target);
|
|
8042
|
-
if (target.type !==
|
|
8043
|
-
return (0,
|
|
8355
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
|
|
8356
|
+
return (0, import_types32.serviceId)(parsed.service);
|
|
8044
8357
|
}
|
|
8045
8358
|
function bucketEdges(graph) {
|
|
8046
8359
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8047
8360
|
graph.forEachEdge((id, attrs) => {
|
|
8048
8361
|
const e = attrs;
|
|
8049
|
-
const parsed = (0,
|
|
8362
|
+
const parsed = (0, import_types32.parseEdgeId)(id);
|
|
8050
8363
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8051
8364
|
const source = bucketSourceFor(graph, e);
|
|
8052
8365
|
const key = bucketKey(source, e.target, e.type);
|
|
8053
8366
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8054
8367
|
switch (provenance) {
|
|
8055
|
-
case
|
|
8368
|
+
case import_types32.Provenance.EXTRACTED:
|
|
8056
8369
|
cur.extracted = e;
|
|
8057
8370
|
break;
|
|
8058
|
-
case
|
|
8371
|
+
case import_types32.Provenance.OBSERVED:
|
|
8059
8372
|
cur.observed = e;
|
|
8060
8373
|
break;
|
|
8061
|
-
case
|
|
8374
|
+
case import_types32.Provenance.INFERRED:
|
|
8062
8375
|
cur.inferred = e;
|
|
8063
8376
|
break;
|
|
8064
8377
|
default:
|
|
8065
|
-
if (e.provenance ===
|
|
8378
|
+
if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
|
|
8066
8379
|
}
|
|
8067
8380
|
buckets2.set(key, cur);
|
|
8068
8381
|
});
|
|
@@ -8071,12 +8384,12 @@ function bucketEdges(graph) {
|
|
|
8071
8384
|
function nodeIsFrontier(graph, nodeId) {
|
|
8072
8385
|
if (!graph.hasNode(nodeId)) return false;
|
|
8073
8386
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8074
|
-
return attrs.type ===
|
|
8387
|
+
return attrs.type === import_types32.NodeType.FrontierNode;
|
|
8075
8388
|
}
|
|
8076
8389
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8077
8390
|
if (!graph.hasNode(nodeId)) return false;
|
|
8078
8391
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8079
|
-
return attrs.type ===
|
|
8392
|
+
return attrs.type === import_types32.NodeType.WebSocketChannelNode;
|
|
8080
8393
|
}
|
|
8081
8394
|
function clampConfidence(n) {
|
|
8082
8395
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8096,14 +8409,14 @@ function gradedConfidence(edge) {
|
|
|
8096
8409
|
return clampConfidence(confidenceForEdge(edge));
|
|
8097
8410
|
}
|
|
8098
8411
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8412
|
+
import_types32.EdgeType.CALLS,
|
|
8413
|
+
import_types32.EdgeType.CONNECTS_TO,
|
|
8414
|
+
import_types32.EdgeType.PUBLISHES_TO,
|
|
8415
|
+
import_types32.EdgeType.CONSUMES_FROM
|
|
8103
8416
|
]);
|
|
8104
8417
|
function detectMissingDivergences(graph, bucket) {
|
|
8105
8418
|
const out = [];
|
|
8106
|
-
if (bucket.type ===
|
|
8419
|
+
if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
|
|
8107
8420
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8108
8421
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8109
8422
|
out.push({
|
|
@@ -8144,7 +8457,7 @@ function declaredHostFor(svc) {
|
|
|
8144
8457
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8145
8458
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8146
8459
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8147
|
-
if (e.type ===
|
|
8460
|
+
if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
|
|
8148
8461
|
return true;
|
|
8149
8462
|
}
|
|
8150
8463
|
}
|
|
@@ -8157,10 +8470,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8157
8470
|
const out = [];
|
|
8158
8471
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8159
8472
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8160
|
-
if (edge.type !==
|
|
8161
|
-
if (edge.provenance !==
|
|
8473
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8474
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8162
8475
|
const target = graph.getNodeAttributes(edge.target);
|
|
8163
|
-
if (target.type !==
|
|
8476
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8164
8477
|
const observedHost = target.host?.trim();
|
|
8165
8478
|
if (!observedHost) continue;
|
|
8166
8479
|
if (observedHost === declaredHost) continue;
|
|
@@ -8182,10 +8495,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8182
8495
|
const deps = svc.dependencies ?? {};
|
|
8183
8496
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8184
8497
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8185
|
-
if (edge.type !==
|
|
8186
|
-
if (edge.provenance !==
|
|
8498
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8499
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8187
8500
|
const target = graph.getNodeAttributes(edge.target);
|
|
8188
|
-
if (target.type !==
|
|
8501
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8189
8502
|
for (const pair of compatPairs()) {
|
|
8190
8503
|
if (pair.engine !== target.engine) continue;
|
|
8191
8504
|
const declared = deps[pair.driver];
|
|
@@ -8244,7 +8557,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8244
8557
|
for (const d of all) {
|
|
8245
8558
|
if (d.type !== "host-mismatch") continue;
|
|
8246
8559
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8247
|
-
declaredHalf.add((0,
|
|
8560
|
+
declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
|
|
8248
8561
|
}
|
|
8249
8562
|
if (observedHalf.size === 0) return all;
|
|
8250
8563
|
return all.filter((d) => {
|
|
@@ -8263,7 +8576,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8263
8576
|
}
|
|
8264
8577
|
graph.forEachNode((nodeId, attrs) => {
|
|
8265
8578
|
const n = attrs;
|
|
8266
|
-
if (n.type !==
|
|
8579
|
+
if (n.type !== import_types32.NodeType.ServiceNode) return;
|
|
8267
8580
|
const svc = n;
|
|
8268
8581
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8269
8582
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8297,7 +8610,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8297
8610
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8298
8611
|
return a.target.localeCompare(b.target);
|
|
8299
8612
|
});
|
|
8300
|
-
return
|
|
8613
|
+
return import_types32.DivergenceResultSchema.parse({
|
|
8301
8614
|
divergences: filtered,
|
|
8302
8615
|
totalAffected: filtered.length,
|
|
8303
8616
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8430,23 +8743,23 @@ function canonicalJson(value) {
|
|
|
8430
8743
|
init_cjs_shims();
|
|
8431
8744
|
var import_node_fs28 = require("fs");
|
|
8432
8745
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8433
|
-
var
|
|
8434
|
-
var
|
|
8746
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8747
|
+
var import_types33 = require("@neat.is/types");
|
|
8435
8748
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8436
8749
|
var LOCK_RETRY_MS = 50;
|
|
8437
8750
|
function neatHome() {
|
|
8438
8751
|
const override = process.env.NEAT_HOME;
|
|
8439
|
-
if (override && override.length > 0) return
|
|
8440
|
-
return
|
|
8752
|
+
if (override && override.length > 0) return import_node_path46.default.resolve(override);
|
|
8753
|
+
return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8441
8754
|
}
|
|
8442
8755
|
function registryPath() {
|
|
8443
|
-
return
|
|
8756
|
+
return import_node_path46.default.join(neatHome(), "projects.json");
|
|
8444
8757
|
}
|
|
8445
8758
|
function registryLockPath() {
|
|
8446
|
-
return
|
|
8759
|
+
return import_node_path46.default.join(neatHome(), "projects.json.lock");
|
|
8447
8760
|
}
|
|
8448
8761
|
function daemonPidPath() {
|
|
8449
|
-
return
|
|
8762
|
+
return import_node_path46.default.join(neatHome(), "neatd.pid");
|
|
8450
8763
|
}
|
|
8451
8764
|
function isPidAliveDefault(pid) {
|
|
8452
8765
|
try {
|
|
@@ -8506,7 +8819,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8506
8819
|
}
|
|
8507
8820
|
}
|
|
8508
8821
|
async function writeAtomically(target, contents) {
|
|
8509
|
-
await import_node_fs28.promises.mkdir(
|
|
8822
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
|
|
8510
8823
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8511
8824
|
const fd = await import_node_fs28.promises.open(tmp, "w");
|
|
8512
8825
|
try {
|
|
@@ -8519,7 +8832,7 @@ async function writeAtomically(target, contents) {
|
|
|
8519
8832
|
}
|
|
8520
8833
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8521
8834
|
const deadline = Date.now() + timeoutMs;
|
|
8522
|
-
await import_node_fs28.promises.mkdir(
|
|
8835
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
|
|
8523
8836
|
let probedHolder = false;
|
|
8524
8837
|
while (true) {
|
|
8525
8838
|
try {
|
|
@@ -8572,10 +8885,10 @@ async function readRegistry() {
|
|
|
8572
8885
|
throw err;
|
|
8573
8886
|
}
|
|
8574
8887
|
const parsed = JSON.parse(raw);
|
|
8575
|
-
return
|
|
8888
|
+
return import_types33.RegistryFileSchema.parse(parsed);
|
|
8576
8889
|
}
|
|
8577
8890
|
async function writeRegistry(reg) {
|
|
8578
|
-
const validated =
|
|
8891
|
+
const validated = import_types33.RegistryFileSchema.parse(reg);
|
|
8579
8892
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8580
8893
|
}
|
|
8581
8894
|
async function getProject(name) {
|
|
@@ -8720,7 +9033,7 @@ init_auth();
|
|
|
8720
9033
|
// src/connectors-config.ts
|
|
8721
9034
|
init_cjs_shims();
|
|
8722
9035
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
8723
|
-
var
|
|
9036
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8724
9037
|
var import_node_fs29 = require("fs");
|
|
8725
9038
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
8726
9039
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -8735,11 +9048,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
8735
9048
|
};
|
|
8736
9049
|
function neatHome2() {
|
|
8737
9050
|
const override = process.env.NEAT_HOME;
|
|
8738
|
-
if (override && override.length > 0) return
|
|
8739
|
-
return
|
|
9051
|
+
if (override && override.length > 0) return import_node_path47.default.resolve(override);
|
|
9052
|
+
return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
|
|
8740
9053
|
}
|
|
8741
9054
|
function connectorsConfigPath(home = neatHome2()) {
|
|
8742
|
-
return
|
|
9055
|
+
return import_node_path47.default.join(home, "connectors.json");
|
|
8743
9056
|
}
|
|
8744
9057
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
8745
9058
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9075,11 +9388,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9075
9388
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9076
9389
|
const parsed = [];
|
|
9077
9390
|
for (const c of candidates) {
|
|
9078
|
-
const r =
|
|
9391
|
+
const r = import_types34.DivergenceTypeSchema.safeParse(c);
|
|
9079
9392
|
if (!r.success) {
|
|
9080
9393
|
return reply.code(400).send({
|
|
9081
9394
|
error: `unknown divergence type "${c}"`,
|
|
9082
|
-
allowed:
|
|
9395
|
+
allowed: import_types34.DivergenceTypeSchema.options
|
|
9083
9396
|
});
|
|
9084
9397
|
}
|
|
9085
9398
|
parsed.push(r.data);
|
|
@@ -9350,7 +9663,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9350
9663
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9351
9664
|
let violations = await log.readAll();
|
|
9352
9665
|
if (req2.query.severity) {
|
|
9353
|
-
const sev =
|
|
9666
|
+
const sev = import_types34.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
9354
9667
|
if (!sev.success) {
|
|
9355
9668
|
return reply.code(400).send({
|
|
9356
9669
|
error: "invalid severity",
|
|
@@ -9389,7 +9702,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9389
9702
|
scope.post("/policies/check", async (req2, reply) => {
|
|
9390
9703
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
9391
9704
|
if (!proj) return;
|
|
9392
|
-
const parsed =
|
|
9705
|
+
const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
9393
9706
|
if (!parsed.success) {
|
|
9394
9707
|
return reply.code(400).send({
|
|
9395
9708
|
error: "invalid /policies/check body",
|
|
@@ -9653,15 +9966,15 @@ init_otel();
|
|
|
9653
9966
|
|
|
9654
9967
|
// src/connectors/index.ts
|
|
9655
9968
|
init_cjs_shims();
|
|
9656
|
-
var
|
|
9969
|
+
var import_types35 = require("@neat.is/types");
|
|
9657
9970
|
var NO_ENV = "unknown";
|
|
9658
9971
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
9659
9972
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9660
9973
|
const sites = [];
|
|
9661
9974
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
9662
9975
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9663
|
-
if (edge.provenance !==
|
|
9664
|
-
const parsed = (0,
|
|
9976
|
+
if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
|
|
9977
|
+
const parsed = (0, import_types35.parseFileId)(edge.source);
|
|
9665
9978
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
9666
9979
|
const site = { relPath: edge.evidence.file };
|
|
9667
9980
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -9672,7 +9985,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
9672
9985
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
9673
9986
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9674
9987
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
9675
|
-
if (attrs.type !==
|
|
9988
|
+
if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
|
|
9676
9989
|
const site = { relPath: attrs.path };
|
|
9677
9990
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
9678
9991
|
return site;
|
|
@@ -9846,7 +10159,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
9846
10159
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
9847
10160
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
9848
10161
|
// pg_stat_statements connection (see module header above).
|
|
9849
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
10162
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
10163
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
10164
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
10165
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
10166
|
+
// conservative pending a documented Drains-API rate limit.
|
|
10167
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
9850
10168
|
};
|
|
9851
10169
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
9852
10170
|
function defaultRateLimitFor(provider) {
|
|
@@ -10138,10 +10456,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10138
10456
|
// src/connectors/supabase/map.ts
|
|
10139
10457
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10140
10458
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10141
|
-
function targetFromRestPath(
|
|
10142
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10459
|
+
function targetFromRestPath(path54) {
|
|
10460
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path54);
|
|
10143
10461
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10144
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10462
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path54);
|
|
10145
10463
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10146
10464
|
return null;
|
|
10147
10465
|
}
|
|
@@ -10250,23 +10568,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10250
10568
|
|
|
10251
10569
|
// src/connectors/supabase/resolve.ts
|
|
10252
10570
|
init_cjs_shims();
|
|
10253
|
-
var
|
|
10571
|
+
var import_types37 = require("@neat.is/types");
|
|
10254
10572
|
function createSupabaseResolveTarget(graph, config) {
|
|
10255
10573
|
return (signal, _ctx) => {
|
|
10256
10574
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10257
10575
|
return null;
|
|
10258
10576
|
}
|
|
10259
|
-
const subResourceId = (0,
|
|
10577
|
+
const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10260
10578
|
if (graph.hasNode(subResourceId)) {
|
|
10261
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10579
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10262
10580
|
}
|
|
10263
|
-
const bareResourceId = (0,
|
|
10581
|
+
const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
|
|
10264
10582
|
if (graph.hasNode(bareResourceId)) {
|
|
10265
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10583
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10266
10584
|
}
|
|
10267
|
-
const projectLevelId = (0,
|
|
10585
|
+
const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
|
|
10268
10586
|
if (graph.hasNode(projectLevelId)) {
|
|
10269
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10587
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10270
10588
|
}
|
|
10271
10589
|
return null;
|
|
10272
10590
|
};
|
|
@@ -10359,7 +10677,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10359
10677
|
|
|
10360
10678
|
// src/connectors/railway/index.ts
|
|
10361
10679
|
init_cjs_shims();
|
|
10362
|
-
var
|
|
10680
|
+
var import_types41 = require("@neat.is/types");
|
|
10363
10681
|
|
|
10364
10682
|
// src/connectors/railway/client.ts
|
|
10365
10683
|
init_cjs_shims();
|
|
@@ -10485,7 +10803,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10485
10803
|
const out = [];
|
|
10486
10804
|
graph.forEachNode((_id, attrs) => {
|
|
10487
10805
|
const node = attrs;
|
|
10488
|
-
if (node.type !==
|
|
10806
|
+
if (node.type !== import_types41.NodeType.RouteNode) return;
|
|
10489
10807
|
const route = attrs;
|
|
10490
10808
|
if (route.service !== serviceName) return;
|
|
10491
10809
|
out.push({
|
|
@@ -10589,12 +10907,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10589
10907
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10590
10908
|
if (!serviceName) return null;
|
|
10591
10909
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10592
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
10910
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
|
|
10593
10911
|
}
|
|
10594
10912
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10595
10913
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10596
10914
|
if (!peerName) return null;
|
|
10597
|
-
return { targetNodeId: (0,
|
|
10915
|
+
return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
|
|
10598
10916
|
}
|
|
10599
10917
|
return null;
|
|
10600
10918
|
};
|
|
@@ -10718,9 +11036,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
10718
11036
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
10719
11037
|
if (secondSep === -1) return null;
|
|
10720
11038
|
const method = rest.slice(0, secondSep);
|
|
10721
|
-
const
|
|
10722
|
-
if (!resourceName || !method || !
|
|
10723
|
-
return { resourceName, method, path:
|
|
11039
|
+
const path54 = rest.slice(secondSep + 1);
|
|
11040
|
+
if (!resourceName || !method || !path54) return null;
|
|
11041
|
+
return { resourceName, method, path: path54 };
|
|
10724
11042
|
}
|
|
10725
11043
|
function resourceNameFor(type, labels) {
|
|
10726
11044
|
if (!labels) return null;
|
|
@@ -10758,14 +11076,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
10758
11076
|
if (!req2) return null;
|
|
10759
11077
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
10760
11078
|
const method = req2.requestMethod.toUpperCase();
|
|
10761
|
-
const
|
|
10762
|
-
if (
|
|
11079
|
+
const path54 = pathFromRequestUrl(req2.requestUrl);
|
|
11080
|
+
if (path54 === null) return null;
|
|
10763
11081
|
const timestamp = entry2.timestamp;
|
|
10764
11082
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
10765
11083
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
10766
11084
|
return {
|
|
10767
11085
|
targetKind: resourceType,
|
|
10768
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11086
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path54 }),
|
|
10769
11087
|
callCount: 1,
|
|
10770
11088
|
errorCount: isError ? 1 : 0,
|
|
10771
11089
|
lastObservedIso: timestamp
|
|
@@ -10782,7 +11100,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
10782
11100
|
|
|
10783
11101
|
// src/connectors/firebase/resolve.ts
|
|
10784
11102
|
init_cjs_shims();
|
|
10785
|
-
var
|
|
11103
|
+
var import_types42 = require("@neat.is/types");
|
|
10786
11104
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
10787
11105
|
switch (resourceType) {
|
|
10788
11106
|
case "cloud_function":
|
|
@@ -10797,7 +11115,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
10797
11115
|
const entries = [];
|
|
10798
11116
|
graph.forEachNode((_id, attrs) => {
|
|
10799
11117
|
const node = attrs;
|
|
10800
|
-
if (node.type !==
|
|
11118
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10801
11119
|
const route = attrs;
|
|
10802
11120
|
if (route.service !== serviceName) return;
|
|
10803
11121
|
entries.push({
|
|
@@ -10829,7 +11147,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
10829
11147
|
return {
|
|
10830
11148
|
targetNodeId: match.routeNodeId,
|
|
10831
11149
|
serviceName,
|
|
10832
|
-
edgeType:
|
|
11150
|
+
edgeType: import_types42.EdgeType.CALLS
|
|
10833
11151
|
};
|
|
10834
11152
|
};
|
|
10835
11153
|
}
|
|
@@ -10856,7 +11174,7 @@ init_cjs_shims();
|
|
|
10856
11174
|
|
|
10857
11175
|
// src/connectors/cloudflare/connector.ts
|
|
10858
11176
|
init_cjs_shims();
|
|
10859
|
-
var
|
|
11177
|
+
var import_types44 = require("@neat.is/types");
|
|
10860
11178
|
|
|
10861
11179
|
// src/connectors/cloudflare/client.ts
|
|
10862
11180
|
init_cjs_shims();
|
|
@@ -10972,7 +11290,7 @@ function mapEventToSignal(event) {
|
|
|
10972
11290
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
10973
11291
|
const statusCode = metadata?.statusCode;
|
|
10974
11292
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
10975
|
-
const
|
|
11293
|
+
const path54 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
10976
11294
|
return {
|
|
10977
11295
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
10978
11296
|
targetName: scriptName,
|
|
@@ -10980,7 +11298,7 @@ function mapEventToSignal(event) {
|
|
|
10980
11298
|
errorCount: isError ? 1 : 0,
|
|
10981
11299
|
lastObservedIso: observedAt.toISOString(),
|
|
10982
11300
|
method,
|
|
10983
|
-
...
|
|
11301
|
+
...path54 ? { path: path54 } : {},
|
|
10984
11302
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
10985
11303
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
10986
11304
|
};
|
|
@@ -11020,19 +11338,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11020
11338
|
graph.forEachNode((id, attrs) => {
|
|
11021
11339
|
if (found) return;
|
|
11022
11340
|
const a = attrs;
|
|
11023
|
-
if (a.type ===
|
|
11341
|
+
if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11024
11342
|
found = id;
|
|
11025
11343
|
}
|
|
11026
11344
|
});
|
|
11027
11345
|
return found;
|
|
11028
11346
|
}
|
|
11029
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11030
|
-
const normalizedPath = normalizePathTemplate(
|
|
11347
|
+
function findMatchingRouteNode(graph, serviceName, method, path54) {
|
|
11348
|
+
const normalizedPath = normalizePathTemplate(path54);
|
|
11031
11349
|
let found = null;
|
|
11032
11350
|
graph.forEachNode((id, attrs) => {
|
|
11033
11351
|
if (found) return;
|
|
11034
11352
|
const a = attrs;
|
|
11035
|
-
if (a.type !==
|
|
11353
|
+
if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11036
11354
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11037
11355
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11038
11356
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11044,18 +11362,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11044
11362
|
return (signal) => {
|
|
11045
11363
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11046
11364
|
const scriptName = signal.targetName;
|
|
11047
|
-
const { method, path:
|
|
11365
|
+
const { method, path: path54 } = signal;
|
|
11048
11366
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11049
|
-
if (!method || !
|
|
11050
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11367
|
+
if (!method || !path54) return wholeFileId;
|
|
11368
|
+
return findMatchingRouteNode(graph, serviceName, method, path54) ?? wholeFileId;
|
|
11051
11369
|
};
|
|
11052
11370
|
const mapping = config.workers?.[scriptName];
|
|
11053
11371
|
if (mapping) {
|
|
11054
|
-
const wholeFileId = (0,
|
|
11372
|
+
const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
|
|
11055
11373
|
return {
|
|
11056
11374
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11057
11375
|
serviceName: mapping.service,
|
|
11058
|
-
edgeType:
|
|
11376
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11059
11377
|
};
|
|
11060
11378
|
}
|
|
11061
11379
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11064,18 +11382,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11064
11382
|
return {
|
|
11065
11383
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11066
11384
|
serviceName: fileNode.service,
|
|
11067
|
-
edgeType:
|
|
11385
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11068
11386
|
};
|
|
11069
11387
|
}
|
|
11070
11388
|
return {
|
|
11071
|
-
targetNodeId: (0,
|
|
11389
|
+
targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
|
|
11072
11390
|
serviceName: scriptName,
|
|
11073
|
-
edgeType:
|
|
11391
|
+
edgeType: import_types44.EdgeType.CALLS,
|
|
11074
11392
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11075
11393
|
};
|
|
11076
11394
|
};
|
|
11077
11395
|
}
|
|
11078
11396
|
|
|
11397
|
+
// src/connectors/vercel/index.ts
|
|
11398
|
+
init_cjs_shims();
|
|
11399
|
+
|
|
11400
|
+
// src/connectors/vercel/client.ts
|
|
11401
|
+
init_cjs_shims();
|
|
11402
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
11403
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
11404
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
11405
|
+
function apiBase(config) {
|
|
11406
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
11407
|
+
}
|
|
11408
|
+
function teamQuery(config) {
|
|
11409
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
11410
|
+
}
|
|
11411
|
+
function drainDelivery(config, otelToken) {
|
|
11412
|
+
return {
|
|
11413
|
+
type: "http",
|
|
11414
|
+
endpoint: config.endpoint,
|
|
11415
|
+
encoding: "json",
|
|
11416
|
+
headers: bearerAuthHeader(otelToken),
|
|
11417
|
+
...config.secret ? { secret: config.secret } : {}
|
|
11418
|
+
};
|
|
11419
|
+
}
|
|
11420
|
+
async function describeError(res) {
|
|
11421
|
+
try {
|
|
11422
|
+
const data = await res.json();
|
|
11423
|
+
const message = data?.error?.message;
|
|
11424
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
11425
|
+
} catch {
|
|
11426
|
+
return "";
|
|
11427
|
+
}
|
|
11428
|
+
}
|
|
11429
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
11430
|
+
const projectIds = config.projectIds ?? [];
|
|
11431
|
+
const body = {
|
|
11432
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
11433
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
11434
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
11435
|
+
schemas: TRACE_SCHEMAS,
|
|
11436
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
11437
|
+
source: { kind: "self-served" }
|
|
11438
|
+
};
|
|
11439
|
+
const res = await junctionFetch(
|
|
11440
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
11441
|
+
{
|
|
11442
|
+
method: "POST",
|
|
11443
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11444
|
+
body: JSON.stringify(body)
|
|
11445
|
+
},
|
|
11446
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11447
|
+
);
|
|
11448
|
+
if (!res.ok) {
|
|
11449
|
+
throw new Error(
|
|
11450
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11451
|
+
);
|
|
11452
|
+
}
|
|
11453
|
+
const payload = await res.json().catch(() => null);
|
|
11454
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
11455
|
+
throw new Error(
|
|
11456
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
11457
|
+
);
|
|
11458
|
+
}
|
|
11459
|
+
return {
|
|
11460
|
+
id: payload.id,
|
|
11461
|
+
...payload.status ? { status: payload.status } : {},
|
|
11462
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
11463
|
+
};
|
|
11464
|
+
}
|
|
11465
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
11466
|
+
const res = await junctionFetch(
|
|
11467
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
11468
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
11469
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11470
|
+
);
|
|
11471
|
+
if (res.ok || res.status === 404) return;
|
|
11472
|
+
throw new Error(
|
|
11473
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11474
|
+
);
|
|
11475
|
+
}
|
|
11476
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
11477
|
+
const res = await junctionFetch(
|
|
11478
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
11479
|
+
{
|
|
11480
|
+
method: "POST",
|
|
11481
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11482
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
11483
|
+
},
|
|
11484
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11485
|
+
);
|
|
11486
|
+
if (res.status === 401 || res.status === 403) {
|
|
11487
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
11488
|
+
}
|
|
11489
|
+
if (!res.ok) {
|
|
11490
|
+
return {
|
|
11491
|
+
status: "failure",
|
|
11492
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11493
|
+
};
|
|
11494
|
+
}
|
|
11495
|
+
const payload = await res.json().catch(() => null);
|
|
11496
|
+
return {
|
|
11497
|
+
...payload?.status ? { status: payload.status } : {},
|
|
11498
|
+
...payload?.error ? { error: payload.error } : {},
|
|
11499
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
11500
|
+
};
|
|
11501
|
+
}
|
|
11502
|
+
|
|
11079
11503
|
// src/connectors/registry.ts
|
|
11080
11504
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11081
11505
|
async function authProbe(input) {
|
|
@@ -11227,6 +11651,91 @@ var PROVIDER_DISPATCH = {
|
|
|
11227
11651
|
}
|
|
11228
11652
|
}
|
|
11229
11653
|
};
|
|
11654
|
+
function vercelCredsFrom(credentials) {
|
|
11655
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
11656
|
+
}
|
|
11657
|
+
function vercelConfigFromOptions(options) {
|
|
11658
|
+
const raw = options.projectIds;
|
|
11659
|
+
let projectIds;
|
|
11660
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
11661
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11662
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
11663
|
+
}
|
|
11664
|
+
return {
|
|
11665
|
+
teamId: String(options.teamId ?? ""),
|
|
11666
|
+
endpoint: String(options.endpoint ?? ""),
|
|
11667
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
11668
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
11669
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
11670
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
11671
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
11672
|
+
};
|
|
11673
|
+
}
|
|
11674
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
11675
|
+
vercel: {
|
|
11676
|
+
provider: "vercel",
|
|
11677
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
11678
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
11679
|
+
primaryCredentialKey: "token",
|
|
11680
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
11681
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
11682
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
11683
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
11684
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
11685
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
11686
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
11687
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
11688
|
+
const result = await testVercelDrainDelivery(
|
|
11689
|
+
vercelConfigFromOptions(options),
|
|
11690
|
+
vercelCredsFrom(credentials),
|
|
11691
|
+
fetchImpl
|
|
11692
|
+
);
|
|
11693
|
+
if (result.status === "success") return { ok: true };
|
|
11694
|
+
return {
|
|
11695
|
+
ok: false,
|
|
11696
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
11697
|
+
};
|
|
11698
|
+
},
|
|
11699
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
11700
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
11701
|
+
// not a failure (the entry still points at a real drain).
|
|
11702
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
11703
|
+
try {
|
|
11704
|
+
const created = await createVercelDrain(
|
|
11705
|
+
vercelConfigFromOptions(options),
|
|
11706
|
+
vercelCredsFrom(credentials),
|
|
11707
|
+
fetchImpl
|
|
11708
|
+
);
|
|
11709
|
+
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;
|
|
11710
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
11711
|
+
} catch (err) {
|
|
11712
|
+
return { ok: false, reason: err.message };
|
|
11713
|
+
}
|
|
11714
|
+
},
|
|
11715
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
11716
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
11717
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
11718
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
11719
|
+
if (!drainId) {
|
|
11720
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
11721
|
+
}
|
|
11722
|
+
try {
|
|
11723
|
+
await deleteVercelDrain(
|
|
11724
|
+
vercelConfigFromOptions(options),
|
|
11725
|
+
drainId,
|
|
11726
|
+
vercelCredsFrom(credentials),
|
|
11727
|
+
fetchImpl
|
|
11728
|
+
);
|
|
11729
|
+
return { ok: true };
|
|
11730
|
+
} catch (err) {
|
|
11731
|
+
return { ok: false, reason: err.message };
|
|
11732
|
+
}
|
|
11733
|
+
}
|
|
11734
|
+
}
|
|
11735
|
+
};
|
|
11736
|
+
function isPushProvider(provider) {
|
|
11737
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
11738
|
+
}
|
|
11230
11739
|
function resolveEntryCredentials(dispatch, entry2, env) {
|
|
11231
11740
|
let credentials;
|
|
11232
11741
|
try {
|
|
@@ -11249,6 +11758,13 @@ function resolveEntryCredentials(dispatch, entry2, env) {
|
|
|
11249
11758
|
function buildRegistration(entry2, graph, env = process.env) {
|
|
11250
11759
|
const dispatch = PROVIDER_DISPATCH[entry2.provider];
|
|
11251
11760
|
if (!dispatch) {
|
|
11761
|
+
if (isPushProvider(entry2.provider)) {
|
|
11762
|
+
return {
|
|
11763
|
+
ok: false,
|
|
11764
|
+
push: true,
|
|
11765
|
+
reason: `push provider "${entry2.provider}" ingests via the OTLP receiver \u2014 nothing to poll`
|
|
11766
|
+
};
|
|
11767
|
+
}
|
|
11252
11768
|
return { ok: false, reason: `unknown provider "${entry2.provider}"` };
|
|
11253
11769
|
}
|
|
11254
11770
|
const creds = resolveEntryCredentials(dispatch, entry2, env);
|
|
@@ -11299,7 +11815,7 @@ async function loadConnectorRegistrations(input) {
|
|
|
11299
11815
|
if (!connectorMatchesProject(entry2, project)) continue;
|
|
11300
11816
|
const result = buildRegistration(entry2, graph, env);
|
|
11301
11817
|
if (result.ok) registrations.push(result.registration);
|
|
11302
|
-
else onSkip?.(entry2, result.reason);
|
|
11818
|
+
else if (!result.push) onSkip?.(entry2, result.reason);
|
|
11303
11819
|
}
|
|
11304
11820
|
return registrations;
|
|
11305
11821
|
}
|
|
@@ -11310,7 +11826,7 @@ init_auth();
|
|
|
11310
11826
|
// src/unrouted.ts
|
|
11311
11827
|
init_cjs_shims();
|
|
11312
11828
|
var import_node_fs30 = require("fs");
|
|
11313
|
-
var
|
|
11829
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11314
11830
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
11315
11831
|
return {
|
|
11316
11832
|
timestamp: now.toISOString(),
|
|
@@ -11320,34 +11836,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
11320
11836
|
};
|
|
11321
11837
|
}
|
|
11322
11838
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
11323
|
-
const target =
|
|
11839
|
+
const target = import_node_path50.default.join(neatHome4, "errors.ndjson");
|
|
11324
11840
|
await import_node_fs30.promises.mkdir(neatHome4, { recursive: true });
|
|
11325
11841
|
await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
11326
11842
|
}
|
|
11327
11843
|
function unroutedErrorsPath(neatHome4) {
|
|
11328
|
-
return
|
|
11844
|
+
return import_node_path50.default.join(neatHome4, "errors.ndjson");
|
|
11329
11845
|
}
|
|
11330
11846
|
|
|
11331
11847
|
// src/daemon.ts
|
|
11332
|
-
var
|
|
11848
|
+
var import_types47 = require("@neat.is/types");
|
|
11333
11849
|
function daemonJsonPath(scanPath) {
|
|
11334
|
-
return
|
|
11850
|
+
return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
|
|
11335
11851
|
}
|
|
11336
11852
|
function daemonsDiscoveryDir(home) {
|
|
11337
11853
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11338
|
-
return
|
|
11854
|
+
return import_node_path51.default.join(base, "daemons");
|
|
11339
11855
|
}
|
|
11340
11856
|
function daemonDiscoveryPath(project, home) {
|
|
11341
|
-
return
|
|
11857
|
+
return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11342
11858
|
}
|
|
11343
11859
|
function sanitizeDiscoveryName(project) {
|
|
11344
11860
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11345
11861
|
}
|
|
11346
11862
|
function neatHomeFromEnv() {
|
|
11347
11863
|
const env = process.env.NEAT_HOME;
|
|
11348
|
-
if (env && env.length > 0) return
|
|
11864
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11349
11865
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11350
|
-
return
|
|
11866
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11351
11867
|
}
|
|
11352
11868
|
function resolveNeatVersion() {
|
|
11353
11869
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -11416,11 +11932,11 @@ function teardownSlot(slot) {
|
|
|
11416
11932
|
}
|
|
11417
11933
|
}
|
|
11418
11934
|
function neatHomeFor(opts) {
|
|
11419
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
11935
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
|
|
11420
11936
|
const env = process.env.NEAT_HOME;
|
|
11421
|
-
if (env && env.length > 0) return
|
|
11937
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11422
11938
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11423
|
-
return
|
|
11939
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11424
11940
|
}
|
|
11425
11941
|
function routeSpanToProject(serviceName, projects) {
|
|
11426
11942
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -11464,11 +11980,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
11464
11980
|
if (!serviceName) return true;
|
|
11465
11981
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
11466
11982
|
return graph.someNode(
|
|
11467
|
-
(_id, attrs) => attrs.type ===
|
|
11983
|
+
(_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
|
|
11468
11984
|
);
|
|
11469
11985
|
}
|
|
11470
11986
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
11471
|
-
const paths = pathsForProject(entry2.name,
|
|
11987
|
+
const paths = pathsForProject(entry2.name, import_node_path51.default.join(entry2.path, "neat-out"));
|
|
11472
11988
|
try {
|
|
11473
11989
|
const stat = await import_node_fs31.promises.stat(entry2.path);
|
|
11474
11990
|
if (!stat.isDirectory()) {
|
|
@@ -11599,7 +12115,7 @@ async function startDaemon(opts = {}) {
|
|
|
11599
12115
|
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;
|
|
11600
12116
|
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;
|
|
11601
12117
|
const singleProject = projectArg;
|
|
11602
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
12118
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
|
|
11603
12119
|
if (singleProject && !singleProjectPath) {
|
|
11604
12120
|
throw new Error(
|
|
11605
12121
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -11614,7 +12130,7 @@ async function startDaemon(opts = {}) {
|
|
|
11614
12130
|
);
|
|
11615
12131
|
}
|
|
11616
12132
|
}
|
|
11617
|
-
const pidPath =
|
|
12133
|
+
const pidPath = import_node_path51.default.join(home, "neatd.pid");
|
|
11618
12134
|
await writeAtomically(pidPath, `${process.pid}
|
|
11619
12135
|
`);
|
|
11620
12136
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12021,8 +12537,8 @@ async function startDaemon(opts = {}) {
|
|
|
12021
12537
|
let registryWatcher = null;
|
|
12022
12538
|
let reloadTimer = null;
|
|
12023
12539
|
if (!singleProject) try {
|
|
12024
|
-
const regDir =
|
|
12025
|
-
const regBase =
|
|
12540
|
+
const regDir = import_node_path51.default.dirname(regPath);
|
|
12541
|
+
const regBase = import_node_path51.default.basename(regPath);
|
|
12026
12542
|
registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
|
|
12027
12543
|
if (filename !== null && filename !== regBase) return;
|
|
12028
12544
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
@@ -12097,7 +12613,7 @@ init_cjs_shims();
|
|
|
12097
12613
|
var import_node_child_process2 = require("child_process");
|
|
12098
12614
|
var import_node_fs32 = require("fs");
|
|
12099
12615
|
var import_node_net = __toESM(require("net"), 1);
|
|
12100
|
-
var
|
|
12616
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
12101
12617
|
var DEFAULT_WEB_PORT = 6328;
|
|
12102
12618
|
var DEFAULT_REST_PORT = 8080;
|
|
12103
12619
|
function asValidPort(value) {
|
|
@@ -12106,11 +12622,11 @@ function asValidPort(value) {
|
|
|
12106
12622
|
}
|
|
12107
12623
|
function projectRoot() {
|
|
12108
12624
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
12109
|
-
return
|
|
12625
|
+
return import_node_path52.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
12110
12626
|
}
|
|
12111
12627
|
async function readDaemonPorts(root) {
|
|
12112
12628
|
try {
|
|
12113
|
-
const raw = await import_node_fs32.promises.readFile(
|
|
12629
|
+
const raw = await import_node_fs32.promises.readFile(import_node_path52.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
12114
12630
|
const parsed = JSON.parse(raw);
|
|
12115
12631
|
const ports = parsed?.ports ?? {};
|
|
12116
12632
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -12155,10 +12671,10 @@ function resolveWebPackageDir() {
|
|
|
12155
12671
|
eval("require")
|
|
12156
12672
|
);
|
|
12157
12673
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
12158
|
-
return
|
|
12674
|
+
return import_node_path52.default.dirname(pkgJsonPath);
|
|
12159
12675
|
}
|
|
12160
12676
|
function resolveStandaloneServerEntry(webDir) {
|
|
12161
|
-
return
|
|
12677
|
+
return import_node_path52.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
12162
12678
|
}
|
|
12163
12679
|
async function pickInternalPort() {
|
|
12164
12680
|
return new Promise((resolve, reject) => {
|
|
@@ -12211,7 +12727,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
12211
12727
|
NEAT_API_URL: apiUrl
|
|
12212
12728
|
};
|
|
12213
12729
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
12214
|
-
cwd:
|
|
12730
|
+
cwd: import_node_path52.default.dirname(serverEntry),
|
|
12215
12731
|
env,
|
|
12216
12732
|
stdio: ["ignore", "inherit", "inherit"],
|
|
12217
12733
|
detached: false
|
|
@@ -12387,14 +12903,14 @@ function localVersion() {
|
|
|
12387
12903
|
}
|
|
12388
12904
|
function neatHome3() {
|
|
12389
12905
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
12390
|
-
return
|
|
12906
|
+
return import_node_path53.default.resolve(process.env.NEAT_HOME);
|
|
12391
12907
|
}
|
|
12392
12908
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12393
|
-
return
|
|
12909
|
+
return import_node_path53.default.join(home, ".neat");
|
|
12394
12910
|
}
|
|
12395
12911
|
async function readPid() {
|
|
12396
12912
|
try {
|
|
12397
|
-
const raw = await import_node_fs33.promises.readFile(
|
|
12913
|
+
const raw = await import_node_fs33.promises.readFile(import_node_path53.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
12398
12914
|
const n = Number.parseInt(raw.trim(), 10);
|
|
12399
12915
|
return Number.isFinite(n) ? n : null;
|
|
12400
12916
|
} catch {
|