@neat.is/core 0.5.3 → 0.6.0
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-X2AMX3QZ.js → chunk-5PVQJLPR.js} +272 -13
- package/dist/chunk-5PVQJLPR.js.map +1 -0
- package/dist/{chunk-CFDPIMRP.js → chunk-BZ3AJVAC.js} +9 -3
- package/dist/chunk-BZ3AJVAC.js.map +1 -0
- package/dist/{chunk-GJHEZC5K.js → chunk-CS4GHQO3.js} +458 -111
- package/dist/chunk-CS4GHQO3.js.map +1 -0
- package/dist/{chunk-I72HTUOG.js → chunk-MVINCLQM.js} +2 -2
- package/dist/cli.cjs +1190 -458
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +140 -25
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +849 -286
- 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 +858 -295
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-XS45HBET.js} +3 -3
- package/dist/server.cjs +540 -173
- 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-MVINCLQM.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-XS45HBET.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);
|
|
@@ -342,6 +342,11 @@ function pickEnv(spanAttrs, resourceAttrs) {
|
|
|
342
342
|
}
|
|
343
343
|
return ENV_FALLBACK;
|
|
344
344
|
}
|
|
345
|
+
function normalizeDbSystem(attrs) {
|
|
346
|
+
const raw = attrs["db.system"];
|
|
347
|
+
if (typeof raw !== "string") return void 0;
|
|
348
|
+
return raw === "mongoose" ? "mongodb" : raw;
|
|
349
|
+
}
|
|
345
350
|
function messagingDestinationOf(attrs) {
|
|
346
351
|
for (const key of ["messaging.destination.name", "messaging.destination"]) {
|
|
347
352
|
const v = attrs[key];
|
|
@@ -362,8 +367,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
362
367
|
const v = attrs[key];
|
|
363
368
|
if (typeof v === "string" && v.length > 0) {
|
|
364
369
|
const q = v.indexOf("?");
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
370
|
+
const path54 = q === -1 ? v : v.slice(0, q);
|
|
371
|
+
if (path54.length > 0) return path54;
|
|
367
372
|
}
|
|
368
373
|
}
|
|
369
374
|
return void 0;
|
|
@@ -396,8 +401,9 @@ function parseOtlpRequest(body) {
|
|
|
396
401
|
durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
|
|
397
402
|
env: pickEnv(attrs, resourceAttrs),
|
|
398
403
|
attributes: attrs,
|
|
399
|
-
dbSystem:
|
|
404
|
+
dbSystem: normalizeDbSystem(attrs),
|
|
400
405
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
406
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
401
407
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
402
408
|
messagingDestination: messagingDestinationOf(attrs),
|
|
403
409
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -417,10 +423,10 @@ function parseOtlpRequest(body) {
|
|
|
417
423
|
return out;
|
|
418
424
|
}
|
|
419
425
|
function loadProtoRoot() {
|
|
420
|
-
const here =
|
|
421
|
-
const protoRoot =
|
|
426
|
+
const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
427
|
+
const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
|
|
422
428
|
const root = new import_protobufjs.default.Root();
|
|
423
|
-
root.resolvePath = (_origin, target) =>
|
|
429
|
+
root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
|
|
424
430
|
root.loadSync(
|
|
425
431
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
426
432
|
{ keepCase: true }
|
|
@@ -648,12 +654,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
|
|
|
648
654
|
}
|
|
649
655
|
}
|
|
650
656
|
}
|
|
651
|
-
var
|
|
657
|
+
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
658
|
var init_otel = __esm({
|
|
653
659
|
"src/otel.ts"() {
|
|
654
660
|
"use strict";
|
|
655
661
|
init_cjs_shims();
|
|
656
|
-
|
|
662
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
657
663
|
import_node_url2 = require("url");
|
|
658
664
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
659
665
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -677,13 +683,13 @@ __export(neatd_exports, {
|
|
|
677
683
|
module.exports = __toCommonJS(neatd_exports);
|
|
678
684
|
init_cjs_shims();
|
|
679
685
|
var import_node_fs33 = require("fs");
|
|
680
|
-
var
|
|
686
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
681
687
|
var import_node_module2 = require("module");
|
|
682
688
|
|
|
683
689
|
// src/daemon.ts
|
|
684
690
|
init_cjs_shims();
|
|
685
691
|
var import_node_fs31 = require("fs");
|
|
686
|
-
var
|
|
692
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
687
693
|
var import_node_module = require("module");
|
|
688
694
|
|
|
689
695
|
// src/graph.ts
|
|
@@ -1213,19 +1219,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1213
1219
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1214
1220
|
let best = { path: [start], edges: [] };
|
|
1215
1221
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1216
|
-
function step(node,
|
|
1217
|
-
if (
|
|
1218
|
-
best = { path: [...
|
|
1222
|
+
function step(node, path54, edges) {
|
|
1223
|
+
if (path54.length > best.path.length) {
|
|
1224
|
+
best = { path: [...path54], edges: [...edges] };
|
|
1219
1225
|
}
|
|
1220
|
-
if (
|
|
1226
|
+
if (path54.length - 1 >= maxDepth) return;
|
|
1221
1227
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1222
1228
|
for (const [srcId, edge] of incoming) {
|
|
1223
1229
|
if (visited.has(srcId)) continue;
|
|
1224
1230
|
visited.add(srcId);
|
|
1225
|
-
|
|
1231
|
+
path54.push(srcId);
|
|
1226
1232
|
edges.push(edge);
|
|
1227
|
-
step(srcId,
|
|
1228
|
-
|
|
1233
|
+
step(srcId, path54, edges);
|
|
1234
|
+
path54.pop();
|
|
1229
1235
|
edges.pop();
|
|
1230
1236
|
visited.delete(srcId);
|
|
1231
1237
|
}
|
|
@@ -1419,26 +1425,26 @@ function dominantFailingCall(graph, serviceId6, visited) {
|
|
|
1419
1425
|
return best;
|
|
1420
1426
|
}
|
|
1421
1427
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1422
|
-
const
|
|
1428
|
+
const path54 = [originServiceId];
|
|
1423
1429
|
const edges = [];
|
|
1424
1430
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1425
1431
|
let current = originServiceId;
|
|
1426
1432
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1427
1433
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1428
1434
|
if (!hop) break;
|
|
1429
|
-
|
|
1435
|
+
path54.push(hop.nextService);
|
|
1430
1436
|
edges.push(hop.edge);
|
|
1431
1437
|
visited.add(hop.nextService);
|
|
1432
1438
|
current = hop.nextService;
|
|
1433
1439
|
}
|
|
1434
1440
|
if (edges.length === 0) return null;
|
|
1435
|
-
return { path:
|
|
1441
|
+
return { path: path54, edges, culprit: current };
|
|
1436
1442
|
}
|
|
1437
1443
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1438
1444
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1439
1445
|
if (!chain) return null;
|
|
1440
1446
|
const culprit = chain.culprit;
|
|
1441
|
-
const
|
|
1447
|
+
const path54 = [...chain.path];
|
|
1442
1448
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1443
1449
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1444
1450
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1446,14 +1452,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1446
1452
|
if (loc) {
|
|
1447
1453
|
let rootCauseNode = culprit;
|
|
1448
1454
|
if (loc.fileNode) {
|
|
1449
|
-
|
|
1455
|
+
path54.push(loc.fileNode);
|
|
1450
1456
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1451
1457
|
rootCauseNode = loc.fileNode;
|
|
1452
1458
|
}
|
|
1453
1459
|
return import_types.RootCauseResultSchema.parse({
|
|
1454
1460
|
rootCauseNode,
|
|
1455
1461
|
rootCauseReason: loc.rootCauseReason,
|
|
1456
|
-
traversalPath:
|
|
1462
|
+
traversalPath: path54,
|
|
1457
1463
|
edgeProvenances,
|
|
1458
1464
|
confidence,
|
|
1459
1465
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1465,7 +1471,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1465
1471
|
return import_types.RootCauseResultSchema.parse({
|
|
1466
1472
|
rootCauseNode: culprit,
|
|
1467
1473
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1468
|
-
traversalPath:
|
|
1474
|
+
traversalPath: path54,
|
|
1469
1475
|
edgeProvenances,
|
|
1470
1476
|
confidence,
|
|
1471
1477
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -2892,6 +2898,18 @@ async function handleSpan(ctx, span) {
|
|
|
2892
2898
|
callSiteEvidence
|
|
2893
2899
|
);
|
|
2894
2900
|
if (result) affectedNode = targetId;
|
|
2901
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
2902
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
2903
|
+
upsertObservedEdge(
|
|
2904
|
+
ctx.graph,
|
|
2905
|
+
import_types3.EdgeType.CALLS,
|
|
2906
|
+
observedSource(),
|
|
2907
|
+
collectionId,
|
|
2908
|
+
ts,
|
|
2909
|
+
isError,
|
|
2910
|
+
callSiteEvidence
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2895
2913
|
}
|
|
2896
2914
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
2897
2915
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -5736,7 +5754,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
5736
5754
|
|
|
5737
5755
|
// src/extract/calls/index.ts
|
|
5738
5756
|
init_cjs_shims();
|
|
5739
|
-
var
|
|
5757
|
+
var import_types21 = require("@neat.is/types");
|
|
5740
5758
|
|
|
5741
5759
|
// src/extract/calls/http.ts
|
|
5742
5760
|
init_cjs_shims();
|
|
@@ -6449,15 +6467,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6449
6467
|
return out;
|
|
6450
6468
|
}
|
|
6451
6469
|
|
|
6470
|
+
// src/extract/calls/mongoose.ts
|
|
6471
|
+
init_cjs_shims();
|
|
6472
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
6473
|
+
var import_types20 = require("@neat.is/types");
|
|
6474
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
6475
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
6476
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
6477
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
6478
|
+
"advice",
|
|
6479
|
+
"energy",
|
|
6480
|
+
"excretion",
|
|
6481
|
+
"digestion",
|
|
6482
|
+
"cooperation",
|
|
6483
|
+
"health",
|
|
6484
|
+
"justice",
|
|
6485
|
+
"labour",
|
|
6486
|
+
"machinery",
|
|
6487
|
+
"equipment",
|
|
6488
|
+
"information",
|
|
6489
|
+
"pollution",
|
|
6490
|
+
"sewage",
|
|
6491
|
+
"paper",
|
|
6492
|
+
"money",
|
|
6493
|
+
"species",
|
|
6494
|
+
"series",
|
|
6495
|
+
"rain",
|
|
6496
|
+
"rice",
|
|
6497
|
+
"fish",
|
|
6498
|
+
"sheep",
|
|
6499
|
+
"moose",
|
|
6500
|
+
"deer",
|
|
6501
|
+
"news",
|
|
6502
|
+
"expertise",
|
|
6503
|
+
"status",
|
|
6504
|
+
"media"
|
|
6505
|
+
]);
|
|
6506
|
+
var PLURALIZE_RULES = [
|
|
6507
|
+
[/human$/gi, "humans"],
|
|
6508
|
+
[/(m)an$/gi, "$1en"],
|
|
6509
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
6510
|
+
[/(child)$/gi, "$1ren"],
|
|
6511
|
+
[/^(ox)$/gi, "$1en"],
|
|
6512
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
6513
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
6514
|
+
[/(alias|status)$/gi, "$1es"],
|
|
6515
|
+
[/(bu)s$/gi, "$1ses"],
|
|
6516
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
6517
|
+
[/([ti])um$/gi, "$1a"],
|
|
6518
|
+
[/sis$/gi, "ses"],
|
|
6519
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
6520
|
+
[/(hive)$/gi, "$1s"],
|
|
6521
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
6522
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
6523
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
6524
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
6525
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
6526
|
+
[/(quiz)$/gi, "$1zes"],
|
|
6527
|
+
[/s$/gi, "s"],
|
|
6528
|
+
[/([^a-z])$/, "$1"],
|
|
6529
|
+
[/$/gi, "s"]
|
|
6530
|
+
];
|
|
6531
|
+
function pluralizeCollection(name) {
|
|
6532
|
+
const str = name.toLowerCase();
|
|
6533
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
6534
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
6535
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
6536
|
+
}
|
|
6537
|
+
return str;
|
|
6538
|
+
}
|
|
6539
|
+
var MODEL_METHODS = [
|
|
6540
|
+
"find",
|
|
6541
|
+
"findOne",
|
|
6542
|
+
"findById",
|
|
6543
|
+
"findByIdAndUpdate",
|
|
6544
|
+
"findByIdAndDelete",
|
|
6545
|
+
"findOneAndUpdate",
|
|
6546
|
+
"findOneAndDelete",
|
|
6547
|
+
"findOneAndReplace",
|
|
6548
|
+
"countDocuments",
|
|
6549
|
+
"estimatedDocumentCount",
|
|
6550
|
+
"distinct",
|
|
6551
|
+
"aggregate",
|
|
6552
|
+
"exists",
|
|
6553
|
+
"where",
|
|
6554
|
+
"populate",
|
|
6555
|
+
"watch",
|
|
6556
|
+
"hydrate",
|
|
6557
|
+
"create",
|
|
6558
|
+
"insertMany",
|
|
6559
|
+
"insertOne",
|
|
6560
|
+
"updateOne",
|
|
6561
|
+
"updateMany",
|
|
6562
|
+
"replaceOne",
|
|
6563
|
+
"deleteOne",
|
|
6564
|
+
"deleteMany",
|
|
6565
|
+
"bulkWrite",
|
|
6566
|
+
"bulkSave",
|
|
6567
|
+
"save",
|
|
6568
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
6569
|
+
"count",
|
|
6570
|
+
"update",
|
|
6571
|
+
"remove",
|
|
6572
|
+
"findOneAndRemove",
|
|
6573
|
+
"findByIdAndRemove"
|
|
6574
|
+
];
|
|
6575
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
6576
|
+
function schemaCollectionVars(content) {
|
|
6577
|
+
const out = /* @__PURE__ */ new Map();
|
|
6578
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
6579
|
+
let m;
|
|
6580
|
+
while ((m = re.exec(content)) !== null) {
|
|
6581
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
6582
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
6583
|
+
if (opt) out.set(m[1], opt[1]);
|
|
6584
|
+
}
|
|
6585
|
+
return out;
|
|
6586
|
+
}
|
|
6587
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
6588
|
+
const trimmed = rest.trim();
|
|
6589
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
6590
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
6591
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
6592
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
6593
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
6594
|
+
}
|
|
6595
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
6596
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
6597
|
+
}
|
|
6598
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
6599
|
+
}
|
|
6600
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
6601
|
+
const schemaColl = schemaCollectionVars(content);
|
|
6602
|
+
const out = [];
|
|
6603
|
+
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;
|
|
6604
|
+
let m;
|
|
6605
|
+
while ((m = re.exec(content)) !== null) {
|
|
6606
|
+
out.push({
|
|
6607
|
+
modelName: m[2],
|
|
6608
|
+
varName: m[1] ?? null,
|
|
6609
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
6610
|
+
matchText: m[0]
|
|
6611
|
+
});
|
|
6612
|
+
}
|
|
6613
|
+
return out;
|
|
6614
|
+
}
|
|
6615
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
6616
|
+
const line = lineOf(file.content, matchText);
|
|
6617
|
+
return {
|
|
6618
|
+
infraId: (0, import_types20.infraId)(r.kind, r.name),
|
|
6619
|
+
name: r.name,
|
|
6620
|
+
kind: r.kind,
|
|
6621
|
+
edgeType: "CALLS",
|
|
6622
|
+
confidenceKind: "verified-call-site",
|
|
6623
|
+
evidence: { file: import_node_path31.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
6624
|
+
};
|
|
6625
|
+
}
|
|
6626
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
6627
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
6628
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
6629
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
6630
|
+
const content = file.content;
|
|
6631
|
+
const out = [];
|
|
6632
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6633
|
+
const push = (r, matchText) => {
|
|
6634
|
+
const key = `${r.kind}/${r.name}`;
|
|
6635
|
+
if (seen.has(key)) return;
|
|
6636
|
+
seen.add(key);
|
|
6637
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
6638
|
+
};
|
|
6639
|
+
if (hasMongoose) {
|
|
6640
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
6641
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
6642
|
+
}
|
|
6643
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
6644
|
+
let c;
|
|
6645
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
6646
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
6647
|
+
}
|
|
6648
|
+
return out;
|
|
6649
|
+
}
|
|
6650
|
+
function parseDestructure(inner, specifier, out) {
|
|
6651
|
+
for (const raw of inner.split(",")) {
|
|
6652
|
+
const part = raw.trim();
|
|
6653
|
+
if (!part) continue;
|
|
6654
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
6655
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
6656
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
6657
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
6658
|
+
else {
|
|
6659
|
+
const id = /^(\w+)$/.exec(part);
|
|
6660
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6664
|
+
function parseImportBindings(content) {
|
|
6665
|
+
const out = [];
|
|
6666
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
6667
|
+
let m;
|
|
6668
|
+
while ((m = esm.exec(content)) !== null) {
|
|
6669
|
+
const clause = m[1].trim();
|
|
6670
|
+
const spec = m[2];
|
|
6671
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
6672
|
+
if (ns) {
|
|
6673
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
6674
|
+
continue;
|
|
6675
|
+
}
|
|
6676
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
6677
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
6678
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
6679
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
6680
|
+
}
|
|
6681
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
6682
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
6683
|
+
const lhs = m[1];
|
|
6684
|
+
const spec = m[2];
|
|
6685
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
6686
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
6687
|
+
}
|
|
6688
|
+
return out;
|
|
6689
|
+
}
|
|
6690
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
6691
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
6692
|
+
if (defs.length === 0) return null;
|
|
6693
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
6694
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
6695
|
+
const byName = new Map(byVar);
|
|
6696
|
+
let def;
|
|
6697
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
6698
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
6699
|
+
if (!def) {
|
|
6700
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
6701
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
6702
|
+
}
|
|
6703
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
6704
|
+
return { byName, ...def ? { default: def } : {} };
|
|
6705
|
+
}
|
|
6706
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
6707
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
6708
|
+
if (mongooseFiles.length === 0) return [];
|
|
6709
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
6710
|
+
const registry = /* @__PURE__ */ new Map();
|
|
6711
|
+
for (const f of mongooseFiles) {
|
|
6712
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
6713
|
+
if (fx) registry.set(toPosix2(import_node_path31.default.relative(serviceDir, f.path)), fx);
|
|
6714
|
+
}
|
|
6715
|
+
if (registry.size === 0) return [];
|
|
6716
|
+
const out = [];
|
|
6717
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6718
|
+
for (const f of files) {
|
|
6719
|
+
const bindings = parseImportBindings(f.content);
|
|
6720
|
+
if (bindings.length === 0) continue;
|
|
6721
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
6722
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
6723
|
+
for (const b of bindings) {
|
|
6724
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path31.default.dirname(f.path), serviceDir, null);
|
|
6725
|
+
if (!resolvedRel) continue;
|
|
6726
|
+
const fx = registry.get(resolvedRel);
|
|
6727
|
+
if (!fx) continue;
|
|
6728
|
+
if (b.kind === "named" && b.exportName) {
|
|
6729
|
+
const coll = fx.byName.get(b.exportName);
|
|
6730
|
+
if (coll) directColl.set(b.local, coll);
|
|
6731
|
+
} else if (b.kind === "default") {
|
|
6732
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6733
|
+
} else {
|
|
6734
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
6735
|
+
nsExports.set(b.local, fx);
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
6739
|
+
const localDefs = new Set(
|
|
6740
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
6741
|
+
);
|
|
6742
|
+
const emit = (collection, matchText) => {
|
|
6743
|
+
const key = `${f.path}::${collection}`;
|
|
6744
|
+
if (seen.has(key)) return;
|
|
6745
|
+
seen.add(key);
|
|
6746
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
6747
|
+
};
|
|
6748
|
+
for (const [local, collection] of directColl) {
|
|
6749
|
+
if (localDefs.has(local)) continue;
|
|
6750
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6751
|
+
let qm;
|
|
6752
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
6753
|
+
}
|
|
6754
|
+
for (const [local, fx] of nsExports) {
|
|
6755
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
6756
|
+
let qm;
|
|
6757
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
6758
|
+
const coll = fx.byName.get(qm[1]);
|
|
6759
|
+
if (coll) emit(coll, qm[0]);
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6762
|
+
}
|
|
6763
|
+
return out;
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6452
6766
|
// src/extract/calls/index.ts
|
|
6453
6767
|
function edgeTypeFromEndpoint(ep) {
|
|
6454
6768
|
switch (ep.edgeType) {
|
|
6455
6769
|
case "PUBLISHES_TO":
|
|
6456
|
-
return
|
|
6770
|
+
return import_types21.EdgeType.PUBLISHES_TO;
|
|
6457
6771
|
case "CONSUMES_FROM":
|
|
6458
|
-
return
|
|
6772
|
+
return import_types21.EdgeType.CONSUMES_FROM;
|
|
6459
6773
|
default:
|
|
6460
|
-
return
|
|
6774
|
+
return import_types21.EdgeType.CALLS;
|
|
6461
6775
|
}
|
|
6462
6776
|
}
|
|
6463
6777
|
function isAwsKind(kind) {
|
|
@@ -6469,23 +6783,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6469
6783
|
for (const service of services) {
|
|
6470
6784
|
const files = await loadSourceFiles(service.dir);
|
|
6471
6785
|
const endpoints = [];
|
|
6786
|
+
const maskedFiles = [];
|
|
6472
6787
|
for (const file of files) {
|
|
6473
6788
|
if (isTestPath(file.path)) continue;
|
|
6474
6789
|
const masked = maskCommentsInSource(file.content);
|
|
6475
6790
|
const maskedFile = { path: file.path, content: masked };
|
|
6791
|
+
maskedFiles.push(maskedFile);
|
|
6476
6792
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
6477
6793
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
6478
6794
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
6479
6795
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
6480
6796
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
6797
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
6481
6798
|
}
|
|
6799
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
6482
6800
|
if (endpoints.length === 0) continue;
|
|
6483
6801
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
6484
6802
|
for (const ep of endpoints) {
|
|
6485
6803
|
if (!graph.hasNode(ep.infraId)) {
|
|
6486
6804
|
const node = {
|
|
6487
6805
|
id: ep.infraId,
|
|
6488
|
-
type:
|
|
6806
|
+
type: import_types21.NodeType.InfraNode,
|
|
6489
6807
|
name: ep.name,
|
|
6490
6808
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
6491
6809
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -6497,7 +6815,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6497
6815
|
nodesAdded++;
|
|
6498
6816
|
}
|
|
6499
6817
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
6500
|
-
const confidence = (0,
|
|
6818
|
+
const confidence = (0, import_types21.confidenceForExtracted)(ep.confidenceKind);
|
|
6501
6819
|
const relFile = toPosix2(ep.evidence.file);
|
|
6502
6820
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
6503
6821
|
graph,
|
|
@@ -6507,7 +6825,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6507
6825
|
);
|
|
6508
6826
|
nodesAdded += n;
|
|
6509
6827
|
edgesAdded += e;
|
|
6510
|
-
if (!(0,
|
|
6828
|
+
if (!(0, import_types21.passesExtractedFloor)(confidence)) {
|
|
6511
6829
|
noteExtractedDropped({
|
|
6512
6830
|
source: fileNodeId,
|
|
6513
6831
|
target: ep.infraId,
|
|
@@ -6527,7 +6845,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
6527
6845
|
source: fileNodeId,
|
|
6528
6846
|
target: ep.infraId,
|
|
6529
6847
|
type: edgeType,
|
|
6530
|
-
provenance:
|
|
6848
|
+
provenance: import_types21.Provenance.EXTRACTED,
|
|
6531
6849
|
confidence,
|
|
6532
6850
|
evidence: ep.evidence
|
|
6533
6851
|
};
|
|
@@ -6553,16 +6871,16 @@ init_cjs_shims();
|
|
|
6553
6871
|
|
|
6554
6872
|
// src/extract/infra/docker-compose.ts
|
|
6555
6873
|
init_cjs_shims();
|
|
6556
|
-
var
|
|
6557
|
-
var
|
|
6874
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
6875
|
+
var import_types23 = require("@neat.is/types");
|
|
6558
6876
|
|
|
6559
6877
|
// src/extract/infra/shared.ts
|
|
6560
6878
|
init_cjs_shims();
|
|
6561
|
-
var
|
|
6879
|
+
var import_types22 = require("@neat.is/types");
|
|
6562
6880
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
6563
6881
|
return {
|
|
6564
|
-
id: (0,
|
|
6565
|
-
type:
|
|
6882
|
+
id: (0, import_types22.infraId)(kind, name),
|
|
6883
|
+
type: import_types22.NodeType.InfraNode,
|
|
6566
6884
|
name,
|
|
6567
6885
|
provider,
|
|
6568
6886
|
kind,
|
|
@@ -6606,8 +6924,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
6606
6924
|
source: anchorId,
|
|
6607
6925
|
target: node.id,
|
|
6608
6926
|
type: edgeType,
|
|
6609
|
-
provenance:
|
|
6610
|
-
confidence: (0,
|
|
6927
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
6928
|
+
confidence: (0, import_types22.confidenceForExtracted)("structural"),
|
|
6611
6929
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
6612
6930
|
};
|
|
6613
6931
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6624,7 +6942,7 @@ function dependsOnList(value) {
|
|
|
6624
6942
|
}
|
|
6625
6943
|
function serviceNameToServiceNode(name, services) {
|
|
6626
6944
|
for (const s of services) {
|
|
6627
|
-
if (s.node.name === name ||
|
|
6945
|
+
if (s.node.name === name || import_node_path32.default.basename(s.dir) === name) return s.node.id;
|
|
6628
6946
|
}
|
|
6629
6947
|
return null;
|
|
6630
6948
|
}
|
|
@@ -6633,7 +6951,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6633
6951
|
let edgesAdded = 0;
|
|
6634
6952
|
let composePath = null;
|
|
6635
6953
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
6636
|
-
const abs =
|
|
6954
|
+
const abs = import_node_path32.default.join(scanPath, name);
|
|
6637
6955
|
if (await exists(abs)) {
|
|
6638
6956
|
composePath = abs;
|
|
6639
6957
|
break;
|
|
@@ -6646,13 +6964,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6646
6964
|
} catch (err) {
|
|
6647
6965
|
recordExtractionError(
|
|
6648
6966
|
"infra docker-compose",
|
|
6649
|
-
|
|
6967
|
+
import_node_path32.default.relative(scanPath, composePath),
|
|
6650
6968
|
err
|
|
6651
6969
|
);
|
|
6652
6970
|
return { nodesAdded, edgesAdded };
|
|
6653
6971
|
}
|
|
6654
6972
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
6655
|
-
const evidenceFile =
|
|
6973
|
+
const evidenceFile = import_node_path32.default.relative(scanPath, composePath).split(import_node_path32.default.sep).join("/");
|
|
6656
6974
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
6657
6975
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
6658
6976
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -6674,15 +6992,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6674
6992
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
6675
6993
|
const targetId = composeNameToNodeId.get(dep);
|
|
6676
6994
|
if (!targetId) continue;
|
|
6677
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
6995
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types23.EdgeType.DEPENDS_ON);
|
|
6678
6996
|
if (graph.hasEdge(edgeId)) continue;
|
|
6679
6997
|
const edge = {
|
|
6680
6998
|
id: edgeId,
|
|
6681
6999
|
source: sourceId,
|
|
6682
7000
|
target: targetId,
|
|
6683
|
-
type:
|
|
6684
|
-
provenance:
|
|
6685
|
-
confidence: (0,
|
|
7001
|
+
type: import_types23.EdgeType.DEPENDS_ON,
|
|
7002
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7003
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
6686
7004
|
evidence: { file: evidenceFile }
|
|
6687
7005
|
};
|
|
6688
7006
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6694,9 +7012,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
6694
7012
|
|
|
6695
7013
|
// src/extract/infra/dockerfile.ts
|
|
6696
7014
|
init_cjs_shims();
|
|
6697
|
-
var
|
|
7015
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
6698
7016
|
var import_node_fs16 = require("fs");
|
|
6699
|
-
var
|
|
7017
|
+
var import_types24 = require("@neat.is/types");
|
|
6700
7018
|
function readDockerfile(content) {
|
|
6701
7019
|
let image = null;
|
|
6702
7020
|
const ports = [];
|
|
@@ -6725,7 +7043,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6725
7043
|
let nodesAdded = 0;
|
|
6726
7044
|
let edgesAdded = 0;
|
|
6727
7045
|
for (const service of services) {
|
|
6728
|
-
const dockerfilePath =
|
|
7046
|
+
const dockerfilePath = import_node_path33.default.join(service.dir, "Dockerfile");
|
|
6729
7047
|
if (!await exists(dockerfilePath)) continue;
|
|
6730
7048
|
let content;
|
|
6731
7049
|
try {
|
|
@@ -6733,7 +7051,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6733
7051
|
} catch (err) {
|
|
6734
7052
|
recordExtractionError(
|
|
6735
7053
|
"infra dockerfile",
|
|
6736
|
-
|
|
7054
|
+
import_node_path33.default.relative(scanPath, dockerfilePath),
|
|
6737
7055
|
err
|
|
6738
7056
|
);
|
|
6739
7057
|
continue;
|
|
@@ -6745,8 +7063,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6745
7063
|
graph.addNode(node.id, node);
|
|
6746
7064
|
nodesAdded++;
|
|
6747
7065
|
}
|
|
6748
|
-
const relDockerfile = toPosix2(
|
|
6749
|
-
const evidenceFile = toPosix2(
|
|
7066
|
+
const relDockerfile = toPosix2(import_node_path33.default.relative(service.dir, dockerfilePath));
|
|
7067
|
+
const evidenceFile = toPosix2(import_node_path33.default.relative(scanPath, dockerfilePath));
|
|
6750
7068
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
6751
7069
|
graph,
|
|
6752
7070
|
service.pkg.name,
|
|
@@ -6755,15 +7073,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6755
7073
|
);
|
|
6756
7074
|
nodesAdded += fn;
|
|
6757
7075
|
edgesAdded += fe;
|
|
6758
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
7076
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types24.EdgeType.RUNS_ON);
|
|
6759
7077
|
if (!graph.hasEdge(edgeId)) {
|
|
6760
7078
|
const edge = {
|
|
6761
7079
|
id: edgeId,
|
|
6762
7080
|
source: fileNodeId,
|
|
6763
7081
|
target: node.id,
|
|
6764
|
-
type:
|
|
6765
|
-
provenance:
|
|
6766
|
-
confidence: (0,
|
|
7082
|
+
type: import_types24.EdgeType.RUNS_ON,
|
|
7083
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7084
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6767
7085
|
evidence: {
|
|
6768
7086
|
file: evidenceFile,
|
|
6769
7087
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -6778,15 +7096,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6778
7096
|
graph.addNode(portNode.id, portNode);
|
|
6779
7097
|
nodesAdded++;
|
|
6780
7098
|
}
|
|
6781
|
-
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7099
|
+
const portEdgeId = (0, import_types4.extractedEdgeId)(fileNodeId, portNode.id, import_types24.EdgeType.CONNECTS_TO);
|
|
6782
7100
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
6783
7101
|
const portEdge = {
|
|
6784
7102
|
id: portEdgeId,
|
|
6785
7103
|
source: fileNodeId,
|
|
6786
7104
|
target: portNode.id,
|
|
6787
|
-
type:
|
|
6788
|
-
provenance:
|
|
6789
|
-
confidence: (0,
|
|
7105
|
+
type: import_types24.EdgeType.CONNECTS_TO,
|
|
7106
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7107
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
6790
7108
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
6791
7109
|
};
|
|
6792
7110
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -6799,8 +7117,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
6799
7117
|
// src/extract/infra/terraform.ts
|
|
6800
7118
|
init_cjs_shims();
|
|
6801
7119
|
var import_node_fs17 = require("fs");
|
|
6802
|
-
var
|
|
6803
|
-
var
|
|
7120
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7121
|
+
var import_types25 = require("@neat.is/types");
|
|
6804
7122
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
6805
7123
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
6806
7124
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -6810,11 +7128,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
6810
7128
|
for (const entry2 of entries) {
|
|
6811
7129
|
if (entry2.isDirectory()) {
|
|
6812
7130
|
if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
|
|
6813
|
-
const child =
|
|
7131
|
+
const child = import_node_path34.default.join(start, entry2.name);
|
|
6814
7132
|
if (await isPythonVenvDir(child)) continue;
|
|
6815
7133
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
6816
7134
|
} else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
|
|
6817
|
-
out.push(
|
|
7135
|
+
out.push(import_node_path34.default.join(start, entry2.name));
|
|
6818
7136
|
}
|
|
6819
7137
|
}
|
|
6820
7138
|
return out;
|
|
@@ -6846,7 +7164,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6846
7164
|
const files = await walkTfFiles(scanPath);
|
|
6847
7165
|
for (const file of files) {
|
|
6848
7166
|
const content = await import_node_fs17.promises.readFile(file, "utf8");
|
|
6849
|
-
const evidenceFile = toPosix2(
|
|
7167
|
+
const evidenceFile = toPosix2(import_node_path34.default.relative(scanPath, file));
|
|
6850
7168
|
const resources = [];
|
|
6851
7169
|
const byKey = /* @__PURE__ */ new Map();
|
|
6852
7170
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -6881,16 +7199,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6881
7199
|
if (!target) continue;
|
|
6882
7200
|
if (seen.has(target.nodeId)) continue;
|
|
6883
7201
|
seen.add(target.nodeId);
|
|
6884
|
-
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7202
|
+
const edgeId = (0, import_types4.extractedEdgeId)(resource.nodeId, target.nodeId, import_types25.EdgeType.DEPENDS_ON);
|
|
6885
7203
|
if (graph.hasEdge(edgeId)) continue;
|
|
6886
7204
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
6887
7205
|
const edge = {
|
|
6888
7206
|
id: edgeId,
|
|
6889
7207
|
source: resource.nodeId,
|
|
6890
7208
|
target: target.nodeId,
|
|
6891
|
-
type:
|
|
6892
|
-
provenance:
|
|
6893
|
-
confidence: (0,
|
|
7209
|
+
type: import_types25.EdgeType.DEPENDS_ON,
|
|
7210
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7211
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
6894
7212
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
6895
7213
|
};
|
|
6896
7214
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -6904,7 +7222,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
6904
7222
|
// src/extract/infra/k8s.ts
|
|
6905
7223
|
init_cjs_shims();
|
|
6906
7224
|
var import_node_fs18 = require("fs");
|
|
6907
|
-
var
|
|
7225
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
6908
7226
|
var import_yaml3 = require("yaml");
|
|
6909
7227
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
6910
7228
|
Service: "k8s-service",
|
|
@@ -6922,11 +7240,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
6922
7240
|
for (const entry2 of entries) {
|
|
6923
7241
|
if (entry2.isDirectory()) {
|
|
6924
7242
|
if (IGNORED_DIRS.has(entry2.name)) continue;
|
|
6925
|
-
const child =
|
|
7243
|
+
const child = import_node_path35.default.join(start, entry2.name);
|
|
6926
7244
|
if (await isPythonVenvDir(child)) continue;
|
|
6927
7245
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
6928
|
-
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
6929
|
-
out.push(
|
|
7246
|
+
} else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path35.default.extname(entry2.name))) {
|
|
7247
|
+
out.push(import_node_path35.default.join(start, entry2.name));
|
|
6930
7248
|
}
|
|
6931
7249
|
}
|
|
6932
7250
|
return out;
|
|
@@ -6960,13 +7278,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
6960
7278
|
// src/extract/infra/cloudflare.ts
|
|
6961
7279
|
init_cjs_shims();
|
|
6962
7280
|
var import_node_fs19 = require("fs");
|
|
6963
|
-
var
|
|
7281
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
6964
7282
|
var import_smol_toml2 = require("smol-toml");
|
|
6965
|
-
var
|
|
7283
|
+
var import_types26 = require("@neat.is/types");
|
|
6966
7284
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
6967
7285
|
async function readWranglerConfig(dir) {
|
|
6968
7286
|
for (const filename of WRANGLER_FILENAMES) {
|
|
6969
|
-
const abs =
|
|
7287
|
+
const abs = import_node_path36.default.join(dir, filename);
|
|
6970
7288
|
if (!await exists(abs)) continue;
|
|
6971
7289
|
const raw = await import_node_fs19.promises.readFile(abs, "utf8");
|
|
6972
7290
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7010,8 +7328,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7010
7328
|
source: anchorId,
|
|
7011
7329
|
target: node.id,
|
|
7012
7330
|
type: edgeType,
|
|
7013
|
-
provenance:
|
|
7014
|
-
confidence: (0,
|
|
7331
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7332
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7015
7333
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7016
7334
|
};
|
|
7017
7335
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7029,11 +7347,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7029
7347
|
try {
|
|
7030
7348
|
read = await readWranglerConfig(service.dir);
|
|
7031
7349
|
} catch (err) {
|
|
7032
|
-
recordExtractionError("infra cloudflare",
|
|
7350
|
+
recordExtractionError("infra cloudflare", import_node_path36.default.relative(scanPath, service.dir), err);
|
|
7033
7351
|
continue;
|
|
7034
7352
|
}
|
|
7035
7353
|
if (!read || !read.config.name) continue;
|
|
7036
|
-
const evidenceFile = toPosix2(
|
|
7354
|
+
const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, import_node_path36.default.join(service.dir, read.relFile)));
|
|
7037
7355
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7038
7356
|
}
|
|
7039
7357
|
for (const worker of discovered) {
|
|
@@ -7045,7 +7363,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7045
7363
|
}
|
|
7046
7364
|
let anchorId = service.node.id;
|
|
7047
7365
|
if (config.main) {
|
|
7048
|
-
const entryRelPath = toPosix2(
|
|
7366
|
+
const entryRelPath = toPosix2(import_node_path36.default.normalize(config.main));
|
|
7049
7367
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7050
7368
|
graph,
|
|
7051
7369
|
service.pkg.name,
|
|
@@ -7072,15 +7390,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7072
7390
|
nodesAdded++;
|
|
7073
7391
|
}
|
|
7074
7392
|
if (runtimeNode.id !== anchorId) {
|
|
7075
|
-
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7393
|
+
const runsOnId = (0, import_types4.extractedEdgeId)(anchorId, runtimeNode.id, import_types26.EdgeType.RUNS_ON);
|
|
7076
7394
|
if (!graph.hasEdge(runsOnId)) {
|
|
7077
7395
|
const edge = {
|
|
7078
7396
|
id: runsOnId,
|
|
7079
7397
|
source: anchorId,
|
|
7080
7398
|
target: runtimeNode.id,
|
|
7081
|
-
type:
|
|
7082
|
-
provenance:
|
|
7083
|
-
confidence: (0,
|
|
7399
|
+
type: import_types26.EdgeType.RUNS_ON,
|
|
7400
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7401
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7084
7402
|
evidence: {
|
|
7085
7403
|
file: evidenceFile,
|
|
7086
7404
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7094,7 +7412,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7094
7412
|
const result = addResourceEdge(
|
|
7095
7413
|
graph,
|
|
7096
7414
|
anchorId,
|
|
7097
|
-
|
|
7415
|
+
import_types26.EdgeType.CONNECTS_TO,
|
|
7098
7416
|
"cloudflare-route",
|
|
7099
7417
|
route,
|
|
7100
7418
|
evidenceFile,
|
|
@@ -7118,7 +7436,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7118
7436
|
const result = addResourceEdge(
|
|
7119
7437
|
graph,
|
|
7120
7438
|
anchorId,
|
|
7121
|
-
|
|
7439
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7122
7440
|
group.kind,
|
|
7123
7441
|
name,
|
|
7124
7442
|
evidenceFile,
|
|
@@ -7132,7 +7450,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7132
7450
|
const result = addResourceEdge(
|
|
7133
7451
|
graph,
|
|
7134
7452
|
anchorId,
|
|
7135
|
-
|
|
7453
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7136
7454
|
"cloudflare-cron",
|
|
7137
7455
|
cron,
|
|
7138
7456
|
evidenceFile,
|
|
@@ -7145,7 +7463,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7145
7463
|
const result = addResourceEdge(
|
|
7146
7464
|
graph,
|
|
7147
7465
|
anchorId,
|
|
7148
|
-
|
|
7466
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7149
7467
|
"cloudflare-env-var",
|
|
7150
7468
|
varName,
|
|
7151
7469
|
evidenceFile,
|
|
@@ -7158,15 +7476,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7158
7476
|
if (!svc.service) continue;
|
|
7159
7477
|
const target = workerIndex.get(svc.service);
|
|
7160
7478
|
if (target && target.anchorId !== anchorId) {
|
|
7161
|
-
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId,
|
|
7479
|
+
const edgeId = (0, import_types4.extractedEdgeId)(anchorId, target.anchorId, import_types26.EdgeType.CALLS);
|
|
7162
7480
|
if (!graph.hasEdge(edgeId)) {
|
|
7163
7481
|
const edge = {
|
|
7164
7482
|
id: edgeId,
|
|
7165
7483
|
source: anchorId,
|
|
7166
7484
|
target: target.anchorId,
|
|
7167
|
-
type:
|
|
7168
|
-
provenance:
|
|
7169
|
-
confidence: (0,
|
|
7485
|
+
type: import_types26.EdgeType.CALLS,
|
|
7486
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7487
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7170
7488
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7171
7489
|
};
|
|
7172
7490
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7177,7 +7495,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7177
7495
|
const result = addResourceEdge(
|
|
7178
7496
|
graph,
|
|
7179
7497
|
anchorId,
|
|
7180
|
-
|
|
7498
|
+
import_types26.EdgeType.DEPENDS_ON,
|
|
7181
7499
|
"cloudflare-service-binding",
|
|
7182
7500
|
svc.service,
|
|
7183
7501
|
evidenceFile,
|
|
@@ -7193,12 +7511,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7193
7511
|
// src/extract/infra/vercel.ts
|
|
7194
7512
|
init_cjs_shims();
|
|
7195
7513
|
var import_node_fs20 = require("fs");
|
|
7196
|
-
var
|
|
7197
|
-
var
|
|
7514
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7515
|
+
var import_types27 = require("@neat.is/types");
|
|
7198
7516
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7199
7517
|
async function readVercelConfig(dir) {
|
|
7200
7518
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7201
|
-
const abs =
|
|
7519
|
+
const abs = import_node_path37.default.join(dir, filename);
|
|
7202
7520
|
if (!await exists(abs)) continue;
|
|
7203
7521
|
const raw = await import_node_fs20.promises.readFile(abs, "utf8");
|
|
7204
7522
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7207,7 +7525,7 @@ async function readVercelConfig(dir) {
|
|
|
7207
7525
|
return null;
|
|
7208
7526
|
}
|
|
7209
7527
|
async function readLinkedProjectName(dir) {
|
|
7210
|
-
const abs =
|
|
7528
|
+
const abs = import_node_path37.default.join(dir, ".vercel", "project.json");
|
|
7211
7529
|
if (!await exists(abs)) return void 0;
|
|
7212
7530
|
const parsed = JSON.parse(await import_node_fs20.promises.readFile(abs, "utf8"));
|
|
7213
7531
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7225,7 +7543,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7225
7543
|
read = await readVercelConfig(service.dir);
|
|
7226
7544
|
projectName = await readLinkedProjectName(service.dir);
|
|
7227
7545
|
} catch (err) {
|
|
7228
|
-
recordExtractionError("infra vercel",
|
|
7546
|
+
recordExtractionError("infra vercel", import_node_path37.default.relative(scanPath, service.dir), err);
|
|
7229
7547
|
continue;
|
|
7230
7548
|
}
|
|
7231
7549
|
if (!read && !projectName) continue;
|
|
@@ -7241,7 +7559,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7241
7559
|
const anchorId = service.node.id;
|
|
7242
7560
|
if (!read) continue;
|
|
7243
7561
|
const { config, relFile, raw } = read;
|
|
7244
|
-
const evidenceFile = toPosix2(
|
|
7562
|
+
const evidenceFile = toPosix2(import_node_path37.default.relative(scanPath, import_node_path37.default.join(service.dir, relFile)));
|
|
7245
7563
|
const add = (edgeType, kind, name) => {
|
|
7246
7564
|
if (!name) return;
|
|
7247
7565
|
const result = emitPlatformResourceEdge(
|
|
@@ -7257,12 +7575,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7257
7575
|
nodesAdded += result.nodesAdded;
|
|
7258
7576
|
edgesAdded += result.edgesAdded;
|
|
7259
7577
|
};
|
|
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(
|
|
7578
|
+
add(import_types27.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
7579
|
+
for (const cron of config.crons ?? []) add(import_types27.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
7580
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7581
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types27.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7264
7582
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7265
|
-
add(
|
|
7583
|
+
add(import_types27.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7266
7584
|
}
|
|
7267
7585
|
}
|
|
7268
7586
|
return { nodesAdded, edgesAdded };
|
|
@@ -7271,13 +7589,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7271
7589
|
// src/extract/infra/railway.ts
|
|
7272
7590
|
init_cjs_shims();
|
|
7273
7591
|
var import_node_fs21 = require("fs");
|
|
7274
|
-
var
|
|
7592
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7275
7593
|
var import_smol_toml3 = require("smol-toml");
|
|
7276
|
-
var
|
|
7594
|
+
var import_types28 = require("@neat.is/types");
|
|
7277
7595
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7278
7596
|
async function readRailwayConfig(dir) {
|
|
7279
7597
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7280
|
-
const abs =
|
|
7598
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7281
7599
|
if (!await exists(abs)) continue;
|
|
7282
7600
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7283
7601
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7293,7 +7611,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7293
7611
|
try {
|
|
7294
7612
|
read = await readRailwayConfig(service.dir);
|
|
7295
7613
|
} catch (err) {
|
|
7296
|
-
recordExtractionError("infra railway",
|
|
7614
|
+
recordExtractionError("infra railway", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7297
7615
|
continue;
|
|
7298
7616
|
}
|
|
7299
7617
|
if (!read) continue;
|
|
@@ -7303,7 +7621,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7303
7621
|
}
|
|
7304
7622
|
const anchorId = service.node.id;
|
|
7305
7623
|
const { config, relFile, raw } = read;
|
|
7306
|
-
const evidenceFile = toPosix2(
|
|
7624
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, relFile)));
|
|
7307
7625
|
const add = (edgeType, kind, name) => {
|
|
7308
7626
|
if (!name) return;
|
|
7309
7627
|
const result = emitPlatformResourceEdge(
|
|
@@ -7319,9 +7637,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7319
7637
|
nodesAdded += result.nodesAdded;
|
|
7320
7638
|
edgesAdded += result.edgesAdded;
|
|
7321
7639
|
};
|
|
7322
|
-
add(
|
|
7323
|
-
add(
|
|
7324
|
-
add(
|
|
7640
|
+
add(import_types28.EdgeType.RUNS_ON, "railway", "railway");
|
|
7641
|
+
add(import_types28.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
7642
|
+
add(import_types28.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7325
7643
|
}
|
|
7326
7644
|
return { nodesAdded, edgesAdded };
|
|
7327
7645
|
}
|
|
@@ -7329,12 +7647,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7329
7647
|
// src/extract/infra/supabase.ts
|
|
7330
7648
|
init_cjs_shims();
|
|
7331
7649
|
var import_node_fs22 = require("fs");
|
|
7332
|
-
var
|
|
7650
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
7333
7651
|
var import_smol_toml4 = require("smol-toml");
|
|
7334
|
-
var
|
|
7652
|
+
var import_types29 = require("@neat.is/types");
|
|
7335
7653
|
async function readSupabaseConfig(dir) {
|
|
7336
|
-
const relFile =
|
|
7337
|
-
const abs =
|
|
7654
|
+
const relFile = import_node_path39.default.join("supabase", "config.toml");
|
|
7655
|
+
const abs = import_node_path39.default.join(dir, relFile);
|
|
7338
7656
|
if (!await exists(abs)) return null;
|
|
7339
7657
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7340
7658
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7348,7 +7666,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7348
7666
|
try {
|
|
7349
7667
|
read = await readSupabaseConfig(service.dir);
|
|
7350
7668
|
} catch (err) {
|
|
7351
|
-
recordExtractionError("infra supabase",
|
|
7669
|
+
recordExtractionError("infra supabase", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7352
7670
|
continue;
|
|
7353
7671
|
}
|
|
7354
7672
|
if (!read) continue;
|
|
@@ -7363,7 +7681,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7363
7681
|
});
|
|
7364
7682
|
}
|
|
7365
7683
|
const anchorId = service.node.id;
|
|
7366
|
-
const evidenceFile = toPosix2(
|
|
7684
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7367
7685
|
const add = (edgeType, kind, name) => {
|
|
7368
7686
|
if (!name) return;
|
|
7369
7687
|
const result = emitPlatformResourceEdge(
|
|
@@ -7379,10 +7697,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7379
7697
|
nodesAdded += result.nodesAdded;
|
|
7380
7698
|
edgesAdded += result.edgesAdded;
|
|
7381
7699
|
};
|
|
7382
|
-
add(
|
|
7383
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7384
|
-
if (config.storage) add(
|
|
7385
|
-
if (config.auth) add(
|
|
7700
|
+
add(import_types29.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
7701
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types29.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
7702
|
+
if (config.storage) add(import_types29.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
7703
|
+
if (config.auth) add(import_types29.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7386
7704
|
}
|
|
7387
7705
|
return { nodesAdded, edgesAdded };
|
|
7388
7706
|
}
|
|
@@ -7404,17 +7722,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7404
7722
|
}
|
|
7405
7723
|
|
|
7406
7724
|
// src/extract/index.ts
|
|
7407
|
-
var
|
|
7725
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7408
7726
|
|
|
7409
7727
|
// src/extract/retire.ts
|
|
7410
7728
|
init_cjs_shims();
|
|
7411
7729
|
var import_node_fs23 = require("fs");
|
|
7412
|
-
var
|
|
7413
|
-
var
|
|
7730
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7731
|
+
var import_types30 = require("@neat.is/types");
|
|
7414
7732
|
function dropOrphanedFileNodes(graph) {
|
|
7415
7733
|
const orphans = [];
|
|
7416
7734
|
graph.forEachNode((id, attrs) => {
|
|
7417
|
-
if (attrs.type !==
|
|
7735
|
+
if (attrs.type !== import_types30.NodeType.FileNode) return;
|
|
7418
7736
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7419
7737
|
orphans.push(id);
|
|
7420
7738
|
}
|
|
@@ -7427,14 +7745,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7427
7745
|
const bases = [scanPath, ...serviceDirs];
|
|
7428
7746
|
graph.forEachEdge((id, attrs) => {
|
|
7429
7747
|
const edge = attrs;
|
|
7430
|
-
if (edge.provenance !==
|
|
7748
|
+
if (edge.provenance !== import_types30.Provenance.EXTRACTED) return;
|
|
7431
7749
|
const evidenceFile = edge.evidence?.file;
|
|
7432
7750
|
if (!evidenceFile) return;
|
|
7433
|
-
if (
|
|
7751
|
+
if (import_node_path40.default.isAbsolute(evidenceFile)) {
|
|
7434
7752
|
if (!(0, import_node_fs23.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7435
7753
|
return;
|
|
7436
7754
|
}
|
|
7437
|
-
const found = bases.some((base) => (0, import_node_fs23.existsSync)(
|
|
7755
|
+
const found = bases.some((base) => (0, import_node_fs23.existsSync)(import_node_path40.default.join(base, evidenceFile)));
|
|
7438
7756
|
if (!found) toDrop.push(id);
|
|
7439
7757
|
});
|
|
7440
7758
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -7476,7 +7794,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7476
7794
|
}
|
|
7477
7795
|
const droppedEntries = drainDroppedExtracted();
|
|
7478
7796
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
7479
|
-
const rejectedPath =
|
|
7797
|
+
const rejectedPath = import_node_path41.default.join(import_node_path41.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
7480
7798
|
try {
|
|
7481
7799
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
7482
7800
|
} catch (err) {
|
|
@@ -7511,8 +7829,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
7511
7829
|
// src/persist.ts
|
|
7512
7830
|
init_cjs_shims();
|
|
7513
7831
|
var import_node_fs24 = require("fs");
|
|
7514
|
-
var
|
|
7515
|
-
var
|
|
7832
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
7833
|
+
var import_types31 = require("@neat.is/types");
|
|
7516
7834
|
var SCHEMA_VERSION = 4;
|
|
7517
7835
|
function migrateV1ToV2(payload) {
|
|
7518
7836
|
const nodes = payload.graph.nodes;
|
|
@@ -7534,12 +7852,12 @@ function migrateV2ToV3(payload) {
|
|
|
7534
7852
|
for (const edge of edges) {
|
|
7535
7853
|
const attrs = edge.attributes;
|
|
7536
7854
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
7537
|
-
attrs.provenance =
|
|
7855
|
+
attrs.provenance = import_types31.Provenance.OBSERVED;
|
|
7538
7856
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
7539
7857
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
7540
7858
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
7541
7859
|
if (type && source && target) {
|
|
7542
|
-
const newId = (0,
|
|
7860
|
+
const newId = (0, import_types31.observedEdgeId)(source, target, type);
|
|
7543
7861
|
attrs.id = newId;
|
|
7544
7862
|
if (edge.key) edge.key = newId;
|
|
7545
7863
|
}
|
|
@@ -7548,7 +7866,7 @@ function migrateV2ToV3(payload) {
|
|
|
7548
7866
|
return { ...payload, schemaVersion: 3 };
|
|
7549
7867
|
}
|
|
7550
7868
|
async function ensureDir(filePath) {
|
|
7551
|
-
await import_node_fs24.promises.mkdir(
|
|
7869
|
+
await import_node_fs24.promises.mkdir(import_node_path42.default.dirname(filePath), { recursive: true });
|
|
7552
7870
|
}
|
|
7553
7871
|
async function saveGraphToDisk(graph, outPath) {
|
|
7554
7872
|
await ensureDir(outPath);
|
|
@@ -7629,23 +7947,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
7629
7947
|
|
|
7630
7948
|
// src/projects.ts
|
|
7631
7949
|
init_cjs_shims();
|
|
7632
|
-
var
|
|
7950
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7633
7951
|
function pathsForProject(project, baseDir) {
|
|
7634
7952
|
if (project === DEFAULT_PROJECT) {
|
|
7635
7953
|
return {
|
|
7636
|
-
snapshotPath:
|
|
7637
|
-
errorsPath:
|
|
7638
|
-
staleEventsPath:
|
|
7639
|
-
embeddingsCachePath:
|
|
7640
|
-
policyViolationsPath:
|
|
7954
|
+
snapshotPath: import_node_path43.default.join(baseDir, "graph.json"),
|
|
7955
|
+
errorsPath: import_node_path43.default.join(baseDir, "errors.ndjson"),
|
|
7956
|
+
staleEventsPath: import_node_path43.default.join(baseDir, "stale-events.ndjson"),
|
|
7957
|
+
embeddingsCachePath: import_node_path43.default.join(baseDir, "embeddings.json"),
|
|
7958
|
+
policyViolationsPath: import_node_path43.default.join(baseDir, "policy-violations.ndjson")
|
|
7641
7959
|
};
|
|
7642
7960
|
}
|
|
7643
7961
|
return {
|
|
7644
|
-
snapshotPath:
|
|
7645
|
-
errorsPath:
|
|
7646
|
-
staleEventsPath:
|
|
7647
|
-
embeddingsCachePath:
|
|
7648
|
-
policyViolationsPath:
|
|
7962
|
+
snapshotPath: import_node_path43.default.join(baseDir, `${project}.json`),
|
|
7963
|
+
errorsPath: import_node_path43.default.join(baseDir, `errors.${project}.ndjson`),
|
|
7964
|
+
staleEventsPath: import_node_path43.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
7965
|
+
embeddingsCachePath: import_node_path43.default.join(baseDir, `embeddings.${project}.json`),
|
|
7966
|
+
policyViolationsPath: import_node_path43.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
7649
7967
|
};
|
|
7650
7968
|
}
|
|
7651
7969
|
var Projects = class {
|
|
@@ -7683,19 +8001,19 @@ var Projects = class {
|
|
|
7683
8001
|
init_cjs_shims();
|
|
7684
8002
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
7685
8003
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
7686
|
-
var
|
|
8004
|
+
var import_types34 = require("@neat.is/types");
|
|
7687
8005
|
|
|
7688
8006
|
// src/extend/index.ts
|
|
7689
8007
|
init_cjs_shims();
|
|
7690
8008
|
var import_node_fs26 = require("fs");
|
|
7691
|
-
var
|
|
8009
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
7692
8010
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
7693
8011
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
7694
8012
|
|
|
7695
8013
|
// src/installers/package-manager.ts
|
|
7696
8014
|
init_cjs_shims();
|
|
7697
8015
|
var import_node_fs25 = require("fs");
|
|
7698
|
-
var
|
|
8016
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
7699
8017
|
var import_node_child_process = require("child_process");
|
|
7700
8018
|
var LOCKFILE_PRIORITY = [
|
|
7701
8019
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -7717,22 +8035,22 @@ async function exists2(p) {
|
|
|
7717
8035
|
}
|
|
7718
8036
|
}
|
|
7719
8037
|
async function detectPackageManager(serviceDir) {
|
|
7720
|
-
let dir =
|
|
8038
|
+
let dir = import_node_path44.default.resolve(serviceDir);
|
|
7721
8039
|
const stops = /* @__PURE__ */ new Set();
|
|
7722
8040
|
for (let i = 0; i < 64; i++) {
|
|
7723
8041
|
if (stops.has(dir)) break;
|
|
7724
8042
|
stops.add(dir);
|
|
7725
8043
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
7726
|
-
const lockPath =
|
|
8044
|
+
const lockPath = import_node_path44.default.join(dir, candidate.lockfile);
|
|
7727
8045
|
if (await exists2(lockPath)) {
|
|
7728
8046
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
7729
8047
|
}
|
|
7730
8048
|
}
|
|
7731
|
-
const parent =
|
|
8049
|
+
const parent = import_node_path44.default.dirname(dir);
|
|
7732
8050
|
if (parent === dir) break;
|
|
7733
8051
|
dir = parent;
|
|
7734
8052
|
}
|
|
7735
|
-
return { pm: "npm", cwd:
|
|
8053
|
+
return { pm: "npm", cwd: import_node_path44.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
7736
8054
|
}
|
|
7737
8055
|
async function runPackageManagerInstall(cmd) {
|
|
7738
8056
|
return new Promise((resolve) => {
|
|
@@ -7781,7 +8099,7 @@ async function fileExists2(p) {
|
|
|
7781
8099
|
}
|
|
7782
8100
|
}
|
|
7783
8101
|
async function readPackageJson(scanPath) {
|
|
7784
|
-
const pkgPath =
|
|
8102
|
+
const pkgPath = import_node_path45.default.join(scanPath, "package.json");
|
|
7785
8103
|
const raw = await import_node_fs26.promises.readFile(pkgPath, "utf8");
|
|
7786
8104
|
return JSON.parse(raw);
|
|
7787
8105
|
}
|
|
@@ -7800,11 +8118,11 @@ async function findHookFiles(scanPath) {
|
|
|
7800
8118
|
for (const entry2 of entries) {
|
|
7801
8119
|
if (entry2.isDirectory()) {
|
|
7802
8120
|
if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
|
|
7803
|
-
await walk3(
|
|
8121
|
+
await walk3(import_node_path45.default.join(dir, entry2.name));
|
|
7804
8122
|
} else if (entry2.isFile()) {
|
|
7805
8123
|
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(
|
|
8124
|
+
const rel = import_node_path45.default.relative(scanPath, import_node_path45.default.join(dir, entry2.name));
|
|
8125
|
+
found.push(rel.split(import_node_path45.default.sep).join("/"));
|
|
7808
8126
|
}
|
|
7809
8127
|
}
|
|
7810
8128
|
}
|
|
@@ -7815,7 +8133,7 @@ async function findHookFiles(scanPath) {
|
|
|
7815
8133
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
7816
8134
|
let fallback = null;
|
|
7817
8135
|
for (const file of hookFiles) {
|
|
7818
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8136
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(scanPath, file), "utf8");
|
|
7819
8137
|
const patched = splicedContent(content, snippet2);
|
|
7820
8138
|
if (patched !== null) return { file, content, patched };
|
|
7821
8139
|
if (fallback === null) fallback = { file, content };
|
|
@@ -7823,11 +8141,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
7823
8141
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
7824
8142
|
}
|
|
7825
8143
|
function extendLogPath() {
|
|
7826
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
8144
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path45.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
7827
8145
|
}
|
|
7828
8146
|
async function appendExtendLog(entry2) {
|
|
7829
8147
|
const logPath = extendLogPath();
|
|
7830
|
-
await import_node_fs26.promises.mkdir(
|
|
8148
|
+
await import_node_fs26.promises.mkdir(import_node_path45.default.dirname(logPath), { recursive: true });
|
|
7831
8149
|
await import_node_fs26.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
|
|
7832
8150
|
}
|
|
7833
8151
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -7886,7 +8204,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
7886
8204
|
}
|
|
7887
8205
|
async function describeProjectInstrumentation(ctx) {
|
|
7888
8206
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
7889
|
-
const envNeat = await fileExists2(
|
|
8207
|
+
const envNeat = await fileExists2(import_node_path45.default.join(ctx.scanPath, ".env.neat"));
|
|
7890
8208
|
const registryInstrPackages = new Set(
|
|
7891
8209
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
7892
8210
|
);
|
|
@@ -7908,7 +8226,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
7908
8226
|
);
|
|
7909
8227
|
}
|
|
7910
8228
|
for (const file of hookFiles) {
|
|
7911
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8229
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
|
|
7912
8230
|
if (content.includes(args.registration_snippet)) {
|
|
7913
8231
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
7914
8232
|
}
|
|
@@ -7920,10 +8238,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
7920
8238
|
);
|
|
7921
8239
|
}
|
|
7922
8240
|
const primaryFile = primary.file;
|
|
7923
|
-
const primaryPath =
|
|
8241
|
+
const primaryPath = import_node_path45.default.join(ctx.scanPath, primaryFile);
|
|
7924
8242
|
const filesTouched = [];
|
|
7925
8243
|
const depsAdded = [];
|
|
7926
|
-
const pkgPath =
|
|
8244
|
+
const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
|
|
7927
8245
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
7928
8246
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
7929
8247
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -7962,7 +8280,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
7962
8280
|
};
|
|
7963
8281
|
}
|
|
7964
8282
|
for (const file of hookFiles) {
|
|
7965
|
-
const content = await import_node_fs26.promises.readFile(
|
|
8283
|
+
const content = await import_node_fs26.promises.readFile(import_node_path45.default.join(ctx.scanPath, file), "utf8");
|
|
7966
8284
|
if (content.includes(args.registration_snippet)) {
|
|
7967
8285
|
return {
|
|
7968
8286
|
library: args.library,
|
|
@@ -8003,7 +8321,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8003
8321
|
if (!match) {
|
|
8004
8322
|
return { undone: false, message: "no apply found for library" };
|
|
8005
8323
|
}
|
|
8006
|
-
const pkgPath =
|
|
8324
|
+
const pkgPath = import_node_path45.default.join(ctx.scanPath, "package.json");
|
|
8007
8325
|
if (await fileExists2(pkgPath)) {
|
|
8008
8326
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
8009
8327
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -8014,7 +8332,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
8014
8332
|
}
|
|
8015
8333
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
8016
8334
|
for (const file of hookFiles) {
|
|
8017
|
-
const filePath =
|
|
8335
|
+
const filePath = import_node_path45.default.join(ctx.scanPath, file);
|
|
8018
8336
|
const content = await import_node_fs26.promises.readFile(filePath, "utf8");
|
|
8019
8337
|
if (content.includes(match.registration_snippet)) {
|
|
8020
8338
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -8030,39 +8348,39 @@ async function rollbackExtension(ctx, args) {
|
|
|
8030
8348
|
|
|
8031
8349
|
// src/divergences.ts
|
|
8032
8350
|
init_cjs_shims();
|
|
8033
|
-
var
|
|
8351
|
+
var import_types32 = require("@neat.is/types");
|
|
8034
8352
|
function bucketKey(source, target, type) {
|
|
8035
8353
|
return `${type}|${source}|${target}`;
|
|
8036
8354
|
}
|
|
8037
8355
|
function bucketSourceFor(graph, edge) {
|
|
8038
|
-
if (edge.type !==
|
|
8039
|
-
const parsed = (0,
|
|
8356
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) return edge.source;
|
|
8357
|
+
const parsed = (0, import_types32.parseFileId)(edge.source);
|
|
8040
8358
|
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8041
8359
|
const target = graph.getNodeAttributes(edge.target);
|
|
8042
|
-
if (target.type !==
|
|
8043
|
-
return (0,
|
|
8360
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) return edge.source;
|
|
8361
|
+
return (0, import_types32.serviceId)(parsed.service);
|
|
8044
8362
|
}
|
|
8045
8363
|
function bucketEdges(graph) {
|
|
8046
8364
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
8047
8365
|
graph.forEachEdge((id, attrs) => {
|
|
8048
8366
|
const e = attrs;
|
|
8049
|
-
const parsed = (0,
|
|
8367
|
+
const parsed = (0, import_types32.parseEdgeId)(id);
|
|
8050
8368
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
8051
8369
|
const source = bucketSourceFor(graph, e);
|
|
8052
8370
|
const key = bucketKey(source, e.target, e.type);
|
|
8053
8371
|
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
8054
8372
|
switch (provenance) {
|
|
8055
|
-
case
|
|
8373
|
+
case import_types32.Provenance.EXTRACTED:
|
|
8056
8374
|
cur.extracted = e;
|
|
8057
8375
|
break;
|
|
8058
|
-
case
|
|
8376
|
+
case import_types32.Provenance.OBSERVED:
|
|
8059
8377
|
cur.observed = e;
|
|
8060
8378
|
break;
|
|
8061
|
-
case
|
|
8379
|
+
case import_types32.Provenance.INFERRED:
|
|
8062
8380
|
cur.inferred = e;
|
|
8063
8381
|
break;
|
|
8064
8382
|
default:
|
|
8065
|
-
if (e.provenance ===
|
|
8383
|
+
if (e.provenance === import_types32.Provenance.STALE) cur.stale = e;
|
|
8066
8384
|
}
|
|
8067
8385
|
buckets2.set(key, cur);
|
|
8068
8386
|
});
|
|
@@ -8071,12 +8389,12 @@ function bucketEdges(graph) {
|
|
|
8071
8389
|
function nodeIsFrontier(graph, nodeId) {
|
|
8072
8390
|
if (!graph.hasNode(nodeId)) return false;
|
|
8073
8391
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8074
|
-
return attrs.type ===
|
|
8392
|
+
return attrs.type === import_types32.NodeType.FrontierNode;
|
|
8075
8393
|
}
|
|
8076
8394
|
function nodeIsWebsocketChannel(graph, nodeId) {
|
|
8077
8395
|
if (!graph.hasNode(nodeId)) return false;
|
|
8078
8396
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
8079
|
-
return attrs.type ===
|
|
8397
|
+
return attrs.type === import_types32.NodeType.WebSocketChannelNode;
|
|
8080
8398
|
}
|
|
8081
8399
|
function clampConfidence(n) {
|
|
8082
8400
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -8096,14 +8414,14 @@ function gradedConfidence(edge) {
|
|
|
8096
8414
|
return clampConfidence(confidenceForEdge(edge));
|
|
8097
8415
|
}
|
|
8098
8416
|
var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8417
|
+
import_types32.EdgeType.CALLS,
|
|
8418
|
+
import_types32.EdgeType.CONNECTS_TO,
|
|
8419
|
+
import_types32.EdgeType.PUBLISHES_TO,
|
|
8420
|
+
import_types32.EdgeType.CONSUMES_FROM
|
|
8103
8421
|
]);
|
|
8104
8422
|
function detectMissingDivergences(graph, bucket) {
|
|
8105
8423
|
const out = [];
|
|
8106
|
-
if (bucket.type ===
|
|
8424
|
+
if (bucket.type === import_types32.EdgeType.CONTAINS) return out;
|
|
8107
8425
|
if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
|
|
8108
8426
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
8109
8427
|
out.push({
|
|
@@ -8144,7 +8462,7 @@ function declaredHostFor(svc) {
|
|
|
8144
8462
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
8145
8463
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8146
8464
|
const e = graph.getEdgeAttributes(edgeId);
|
|
8147
|
-
if (e.type ===
|
|
8465
|
+
if (e.type === import_types32.EdgeType.CONFIGURED_BY && e.provenance === import_types32.Provenance.EXTRACTED) {
|
|
8148
8466
|
return true;
|
|
8149
8467
|
}
|
|
8150
8468
|
}
|
|
@@ -8157,10 +8475,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
8157
8475
|
const out = [];
|
|
8158
8476
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8159
8477
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8160
|
-
if (edge.type !==
|
|
8161
|
-
if (edge.provenance !==
|
|
8478
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8479
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8162
8480
|
const target = graph.getNodeAttributes(edge.target);
|
|
8163
|
-
if (target.type !==
|
|
8481
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8164
8482
|
const observedHost = target.host?.trim();
|
|
8165
8483
|
if (!observedHost) continue;
|
|
8166
8484
|
if (observedHost === declaredHost) continue;
|
|
@@ -8182,10 +8500,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
8182
8500
|
const deps = svc.dependencies ?? {};
|
|
8183
8501
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
8184
8502
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
8185
|
-
if (edge.type !==
|
|
8186
|
-
if (edge.provenance !==
|
|
8503
|
+
if (edge.type !== import_types32.EdgeType.CONNECTS_TO) continue;
|
|
8504
|
+
if (edge.provenance !== import_types32.Provenance.OBSERVED) continue;
|
|
8187
8505
|
const target = graph.getNodeAttributes(edge.target);
|
|
8188
|
-
if (target.type !==
|
|
8506
|
+
if (target.type !== import_types32.NodeType.DatabaseNode) continue;
|
|
8189
8507
|
for (const pair of compatPairs()) {
|
|
8190
8508
|
if (pair.engine !== target.engine) continue;
|
|
8191
8509
|
const declared = deps[pair.driver];
|
|
@@ -8244,7 +8562,7 @@ function suppressHostMismatchHalves(all) {
|
|
|
8244
8562
|
for (const d of all) {
|
|
8245
8563
|
if (d.type !== "host-mismatch") continue;
|
|
8246
8564
|
observedHalf.add(`${d.source}->${d.target}`);
|
|
8247
|
-
declaredHalf.add((0,
|
|
8565
|
+
declaredHalf.add((0, import_types32.databaseId)(d.extractedHost));
|
|
8248
8566
|
}
|
|
8249
8567
|
if (observedHalf.size === 0) return all;
|
|
8250
8568
|
return all.filter((d) => {
|
|
@@ -8263,7 +8581,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8263
8581
|
}
|
|
8264
8582
|
graph.forEachNode((nodeId, attrs) => {
|
|
8265
8583
|
const n = attrs;
|
|
8266
|
-
if (n.type !==
|
|
8584
|
+
if (n.type !== import_types32.NodeType.ServiceNode) return;
|
|
8267
8585
|
const svc = n;
|
|
8268
8586
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
8269
8587
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -8297,7 +8615,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
8297
8615
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
8298
8616
|
return a.target.localeCompare(b.target);
|
|
8299
8617
|
});
|
|
8300
|
-
return
|
|
8618
|
+
return import_types32.DivergenceResultSchema.parse({
|
|
8301
8619
|
divergences: filtered,
|
|
8302
8620
|
totalAffected: filtered.length,
|
|
8303
8621
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -8430,23 +8748,23 @@ function canonicalJson(value) {
|
|
|
8430
8748
|
init_cjs_shims();
|
|
8431
8749
|
var import_node_fs28 = require("fs");
|
|
8432
8750
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8433
|
-
var
|
|
8434
|
-
var
|
|
8751
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8752
|
+
var import_types33 = require("@neat.is/types");
|
|
8435
8753
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
8436
8754
|
var LOCK_RETRY_MS = 50;
|
|
8437
8755
|
function neatHome() {
|
|
8438
8756
|
const override = process.env.NEAT_HOME;
|
|
8439
|
-
if (override && override.length > 0) return
|
|
8440
|
-
return
|
|
8757
|
+
if (override && override.length > 0) return import_node_path46.default.resolve(override);
|
|
8758
|
+
return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8441
8759
|
}
|
|
8442
8760
|
function registryPath() {
|
|
8443
|
-
return
|
|
8761
|
+
return import_node_path46.default.join(neatHome(), "projects.json");
|
|
8444
8762
|
}
|
|
8445
8763
|
function registryLockPath() {
|
|
8446
|
-
return
|
|
8764
|
+
return import_node_path46.default.join(neatHome(), "projects.json.lock");
|
|
8447
8765
|
}
|
|
8448
8766
|
function daemonPidPath() {
|
|
8449
|
-
return
|
|
8767
|
+
return import_node_path46.default.join(neatHome(), "neatd.pid");
|
|
8450
8768
|
}
|
|
8451
8769
|
function isPidAliveDefault(pid) {
|
|
8452
8770
|
try {
|
|
@@ -8506,7 +8824,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
8506
8824
|
}
|
|
8507
8825
|
}
|
|
8508
8826
|
async function writeAtomically(target, contents) {
|
|
8509
|
-
await import_node_fs28.promises.mkdir(
|
|
8827
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(target), { recursive: true });
|
|
8510
8828
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
8511
8829
|
const fd = await import_node_fs28.promises.open(tmp, "w");
|
|
8512
8830
|
try {
|
|
@@ -8519,7 +8837,7 @@ async function writeAtomically(target, contents) {
|
|
|
8519
8837
|
}
|
|
8520
8838
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
8521
8839
|
const deadline = Date.now() + timeoutMs;
|
|
8522
|
-
await import_node_fs28.promises.mkdir(
|
|
8840
|
+
await import_node_fs28.promises.mkdir(import_node_path46.default.dirname(lockPath), { recursive: true });
|
|
8523
8841
|
let probedHolder = false;
|
|
8524
8842
|
while (true) {
|
|
8525
8843
|
try {
|
|
@@ -8572,10 +8890,10 @@ async function readRegistry() {
|
|
|
8572
8890
|
throw err;
|
|
8573
8891
|
}
|
|
8574
8892
|
const parsed = JSON.parse(raw);
|
|
8575
|
-
return
|
|
8893
|
+
return import_types33.RegistryFileSchema.parse(parsed);
|
|
8576
8894
|
}
|
|
8577
8895
|
async function writeRegistry(reg) {
|
|
8578
|
-
const validated =
|
|
8896
|
+
const validated = import_types33.RegistryFileSchema.parse(reg);
|
|
8579
8897
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
8580
8898
|
}
|
|
8581
8899
|
async function getProject(name) {
|
|
@@ -8720,7 +9038,7 @@ init_auth();
|
|
|
8720
9038
|
// src/connectors-config.ts
|
|
8721
9039
|
init_cjs_shims();
|
|
8722
9040
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
8723
|
-
var
|
|
9041
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8724
9042
|
var import_node_fs29 = require("fs");
|
|
8725
9043
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
8726
9044
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -8735,11 +9053,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
8735
9053
|
};
|
|
8736
9054
|
function neatHome2() {
|
|
8737
9055
|
const override = process.env.NEAT_HOME;
|
|
8738
|
-
if (override && override.length > 0) return
|
|
8739
|
-
return
|
|
9056
|
+
if (override && override.length > 0) return import_node_path47.default.resolve(override);
|
|
9057
|
+
return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
|
|
8740
9058
|
}
|
|
8741
9059
|
function connectorsConfigPath(home = neatHome2()) {
|
|
8742
|
-
return
|
|
9060
|
+
return import_node_path47.default.join(home, "connectors.json");
|
|
8743
9061
|
}
|
|
8744
9062
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
8745
9063
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -9075,11 +9393,11 @@ function registerRoutes(scope, ctx) {
|
|
|
9075
9393
|
const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9076
9394
|
const parsed = [];
|
|
9077
9395
|
for (const c of candidates) {
|
|
9078
|
-
const r =
|
|
9396
|
+
const r = import_types34.DivergenceTypeSchema.safeParse(c);
|
|
9079
9397
|
if (!r.success) {
|
|
9080
9398
|
return reply.code(400).send({
|
|
9081
9399
|
error: `unknown divergence type "${c}"`,
|
|
9082
|
-
allowed:
|
|
9400
|
+
allowed: import_types34.DivergenceTypeSchema.options
|
|
9083
9401
|
});
|
|
9084
9402
|
}
|
|
9085
9403
|
parsed.push(r.data);
|
|
@@ -9350,7 +9668,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9350
9668
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
9351
9669
|
let violations = await log.readAll();
|
|
9352
9670
|
if (req2.query.severity) {
|
|
9353
|
-
const sev =
|
|
9671
|
+
const sev = import_types34.PolicySeveritySchema.safeParse(req2.query.severity);
|
|
9354
9672
|
if (!sev.success) {
|
|
9355
9673
|
return reply.code(400).send({
|
|
9356
9674
|
error: "invalid severity",
|
|
@@ -9389,7 +9707,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9389
9707
|
scope.post("/policies/check", async (req2, reply) => {
|
|
9390
9708
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
9391
9709
|
if (!proj) return;
|
|
9392
|
-
const parsed =
|
|
9710
|
+
const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
|
|
9393
9711
|
if (!parsed.success) {
|
|
9394
9712
|
return reply.code(400).send({
|
|
9395
9713
|
error: "invalid /policies/check body",
|
|
@@ -9461,6 +9779,42 @@ function registerRoutes(scope, ctx) {
|
|
|
9461
9779
|
return reply.code(500).send({ error: err.message });
|
|
9462
9780
|
}
|
|
9463
9781
|
});
|
|
9782
|
+
scope.get("/instrumentation", async (req2, reply) => {
|
|
9783
|
+
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
9784
|
+
if (!proj) return;
|
|
9785
|
+
if (!proj.scanPath) {
|
|
9786
|
+
return { engaged: null };
|
|
9787
|
+
}
|
|
9788
|
+
try {
|
|
9789
|
+
const state = await describeProjectInstrumentation({ project: proj.name, scanPath: proj.scanPath });
|
|
9790
|
+
const uninstrumented = await listUninstrumented({ project: proj.name, scanPath: proj.scanPath });
|
|
9791
|
+
if (state.hookFiles.length === 0) {
|
|
9792
|
+
return {
|
|
9793
|
+
engaged: false,
|
|
9794
|
+
diagnosis: {
|
|
9795
|
+
reason: "No instrumented entry point found \u2014 NEAT hasn't written an OTel init hook for this project.",
|
|
9796
|
+
fixCommand: "neat init",
|
|
9797
|
+
detail: "Run `neat init` so NEAT writes the instrumentation hook next to your entry point, then run your app."
|
|
9798
|
+
}
|
|
9799
|
+
};
|
|
9800
|
+
}
|
|
9801
|
+
if (uninstrumented.length > 0) {
|
|
9802
|
+
const names = uninstrumented.map((u) => u.library);
|
|
9803
|
+
const more = names.length > 1 ? ` (and ${names.length - 1} more)` : "";
|
|
9804
|
+
return {
|
|
9805
|
+
engaged: false,
|
|
9806
|
+
diagnosis: {
|
|
9807
|
+
reason: `\`${names[0]}\`${more} isn't in the auto-instrumentation set, so spans from it won't reach the graph.`,
|
|
9808
|
+
fixCommand: "neat extend",
|
|
9809
|
+
detail: `Uninstrumented on your hot path: ${names.join(", ")}. Run \`neat extend\` to wire the missing instrumentation.`
|
|
9810
|
+
}
|
|
9811
|
+
};
|
|
9812
|
+
}
|
|
9813
|
+
return { engaged: true };
|
|
9814
|
+
} catch {
|
|
9815
|
+
return { engaged: null };
|
|
9816
|
+
}
|
|
9817
|
+
});
|
|
9464
9818
|
scope.post("/extend/apply", async (req2, reply) => {
|
|
9465
9819
|
const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
|
|
9466
9820
|
if (!proj) return;
|
|
@@ -9653,15 +10007,15 @@ init_otel();
|
|
|
9653
10007
|
|
|
9654
10008
|
// src/connectors/index.ts
|
|
9655
10009
|
init_cjs_shims();
|
|
9656
|
-
var
|
|
10010
|
+
var import_types35 = require("@neat.is/types");
|
|
9657
10011
|
var NO_ENV = "unknown";
|
|
9658
10012
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
9659
10013
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9660
10014
|
const sites = [];
|
|
9661
10015
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
9662
10016
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
9663
|
-
if (edge.provenance !==
|
|
9664
|
-
const parsed = (0,
|
|
10017
|
+
if (edge.provenance !== import_types35.Provenance.EXTRACTED) continue;
|
|
10018
|
+
const parsed = (0, import_types35.parseFileId)(edge.source);
|
|
9665
10019
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
9666
10020
|
const site = { relPath: edge.evidence.file };
|
|
9667
10021
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -9672,7 +10026,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
9672
10026
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
9673
10027
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9674
10028
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
9675
|
-
if (attrs.type !==
|
|
10029
|
+
if (attrs.type !== import_types35.NodeType.RouteNode || !attrs.path) return void 0;
|
|
9676
10030
|
const site = { relPath: attrs.path };
|
|
9677
10031
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
9678
10032
|
return site;
|
|
@@ -9846,7 +10200,12 @@ var JUNCTION_DEFAULT_RATE_LIMITS = {
|
|
|
9846
10200
|
supabase: { capacity: 30, refillMs: 1e4 },
|
|
9847
10201
|
// Not a documented API limit at all — a self-imposed ceiling on the raw
|
|
9848
10202
|
// pg_stat_statements connection (see module header above).
|
|
9849
|
-
"supabase-postgres": { capacity: 20, refillMs: 3e3 }
|
|
10203
|
+
"supabase-postgres": { capacity: 20, refillMs: 3e3 },
|
|
10204
|
+
// Push provider (ADR-146): the Drains REST API is touched only by `neat
|
|
10205
|
+
// connector add/remove/test` (provision/deprovision/validate), never a poll
|
|
10206
|
+
// loop, so this bucket is exercised a handful of times per command. Kept
|
|
10207
|
+
// conservative pending a documented Drains-API rate limit.
|
|
10208
|
+
vercel: { capacity: 20, refillMs: 5e3 }
|
|
9850
10209
|
};
|
|
9851
10210
|
var JUNCTION_GENERIC_RATE_LIMIT = { capacity: 20, refillMs: 5e3 };
|
|
9852
10211
|
function defaultRateLimitFor(provider) {
|
|
@@ -10138,10 +10497,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
10138
10497
|
// src/connectors/supabase/map.ts
|
|
10139
10498
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
10140
10499
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
10141
|
-
function targetFromRestPath(
|
|
10142
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
10500
|
+
function targetFromRestPath(path54) {
|
|
10501
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path54);
|
|
10143
10502
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
10144
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
10503
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path54);
|
|
10145
10504
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
10146
10505
|
return null;
|
|
10147
10506
|
}
|
|
@@ -10250,23 +10609,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10250
10609
|
|
|
10251
10610
|
// src/connectors/supabase/resolve.ts
|
|
10252
10611
|
init_cjs_shims();
|
|
10253
|
-
var
|
|
10612
|
+
var import_types37 = require("@neat.is/types");
|
|
10254
10613
|
function createSupabaseResolveTarget(graph, config) {
|
|
10255
10614
|
return (signal, _ctx) => {
|
|
10256
10615
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10257
10616
|
return null;
|
|
10258
10617
|
}
|
|
10259
|
-
const subResourceId = (0,
|
|
10618
|
+
const subResourceId = (0, import_types37.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10260
10619
|
if (graph.hasNode(subResourceId)) {
|
|
10261
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10620
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10262
10621
|
}
|
|
10263
|
-
const bareResourceId = (0,
|
|
10622
|
+
const bareResourceId = (0, import_types37.infraId)(signal.targetKind, signal.targetName);
|
|
10264
10623
|
if (graph.hasNode(bareResourceId)) {
|
|
10265
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
10624
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10266
10625
|
}
|
|
10267
|
-
const projectLevelId = (0,
|
|
10626
|
+
const projectLevelId = (0, import_types37.infraId)("supabase", config.nodeRef);
|
|
10268
10627
|
if (graph.hasNode(projectLevelId)) {
|
|
10269
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10628
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types37.EdgeType.CALLS };
|
|
10270
10629
|
}
|
|
10271
10630
|
return null;
|
|
10272
10631
|
};
|
|
@@ -10359,7 +10718,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10359
10718
|
|
|
10360
10719
|
// src/connectors/railway/index.ts
|
|
10361
10720
|
init_cjs_shims();
|
|
10362
|
-
var
|
|
10721
|
+
var import_types41 = require("@neat.is/types");
|
|
10363
10722
|
|
|
10364
10723
|
// src/connectors/railway/client.ts
|
|
10365
10724
|
init_cjs_shims();
|
|
@@ -10485,7 +10844,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10485
10844
|
const out = [];
|
|
10486
10845
|
graph.forEachNode((_id, attrs) => {
|
|
10487
10846
|
const node = attrs;
|
|
10488
|
-
if (node.type !==
|
|
10847
|
+
if (node.type !== import_types41.NodeType.RouteNode) return;
|
|
10489
10848
|
const route = attrs;
|
|
10490
10849
|
if (route.service !== serviceName) return;
|
|
10491
10850
|
out.push({
|
|
@@ -10589,12 +10948,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10589
10948
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10590
10949
|
if (!serviceName) return null;
|
|
10591
10950
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10592
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
10951
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types41.EdgeType.CALLS };
|
|
10593
10952
|
}
|
|
10594
10953
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10595
10954
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10596
10955
|
if (!peerName) return null;
|
|
10597
|
-
return { targetNodeId: (0,
|
|
10956
|
+
return { targetNodeId: (0, import_types41.serviceId)(peerName), serviceName, edgeType: import_types41.EdgeType.CONNECTS_TO };
|
|
10598
10957
|
}
|
|
10599
10958
|
return null;
|
|
10600
10959
|
};
|
|
@@ -10718,9 +11077,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
10718
11077
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
10719
11078
|
if (secondSep === -1) return null;
|
|
10720
11079
|
const method = rest.slice(0, secondSep);
|
|
10721
|
-
const
|
|
10722
|
-
if (!resourceName || !method || !
|
|
10723
|
-
return { resourceName, method, path:
|
|
11080
|
+
const path54 = rest.slice(secondSep + 1);
|
|
11081
|
+
if (!resourceName || !method || !path54) return null;
|
|
11082
|
+
return { resourceName, method, path: path54 };
|
|
10724
11083
|
}
|
|
10725
11084
|
function resourceNameFor(type, labels) {
|
|
10726
11085
|
if (!labels) return null;
|
|
@@ -10758,14 +11117,14 @@ function mapLogEntryToSignal(entry2) {
|
|
|
10758
11117
|
if (!req2) return null;
|
|
10759
11118
|
if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
|
|
10760
11119
|
const method = req2.requestMethod.toUpperCase();
|
|
10761
|
-
const
|
|
10762
|
-
if (
|
|
11120
|
+
const path54 = pathFromRequestUrl(req2.requestUrl);
|
|
11121
|
+
if (path54 === null) return null;
|
|
10763
11122
|
const timestamp = entry2.timestamp;
|
|
10764
11123
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
10765
11124
|
const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
|
|
10766
11125
|
return {
|
|
10767
11126
|
targetKind: resourceType,
|
|
10768
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11127
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path54 }),
|
|
10769
11128
|
callCount: 1,
|
|
10770
11129
|
errorCount: isError ? 1 : 0,
|
|
10771
11130
|
lastObservedIso: timestamp
|
|
@@ -10782,7 +11141,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
10782
11141
|
|
|
10783
11142
|
// src/connectors/firebase/resolve.ts
|
|
10784
11143
|
init_cjs_shims();
|
|
10785
|
-
var
|
|
11144
|
+
var import_types42 = require("@neat.is/types");
|
|
10786
11145
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
10787
11146
|
switch (resourceType) {
|
|
10788
11147
|
case "cloud_function":
|
|
@@ -10797,7 +11156,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
10797
11156
|
const entries = [];
|
|
10798
11157
|
graph.forEachNode((_id, attrs) => {
|
|
10799
11158
|
const node = attrs;
|
|
10800
|
-
if (node.type !==
|
|
11159
|
+
if (node.type !== import_types42.NodeType.RouteNode) return;
|
|
10801
11160
|
const route = attrs;
|
|
10802
11161
|
if (route.service !== serviceName) return;
|
|
10803
11162
|
entries.push({
|
|
@@ -10829,7 +11188,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
10829
11188
|
return {
|
|
10830
11189
|
targetNodeId: match.routeNodeId,
|
|
10831
11190
|
serviceName,
|
|
10832
|
-
edgeType:
|
|
11191
|
+
edgeType: import_types42.EdgeType.CALLS
|
|
10833
11192
|
};
|
|
10834
11193
|
};
|
|
10835
11194
|
}
|
|
@@ -10856,7 +11215,7 @@ init_cjs_shims();
|
|
|
10856
11215
|
|
|
10857
11216
|
// src/connectors/cloudflare/connector.ts
|
|
10858
11217
|
init_cjs_shims();
|
|
10859
|
-
var
|
|
11218
|
+
var import_types44 = require("@neat.is/types");
|
|
10860
11219
|
|
|
10861
11220
|
// src/connectors/cloudflare/client.ts
|
|
10862
11221
|
init_cjs_shims();
|
|
@@ -10972,7 +11331,7 @@ function mapEventToSignal(event) {
|
|
|
10972
11331
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
10973
11332
|
const statusCode = metadata?.statusCode;
|
|
10974
11333
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
10975
|
-
const
|
|
11334
|
+
const path54 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
10976
11335
|
return {
|
|
10977
11336
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
10978
11337
|
targetName: scriptName,
|
|
@@ -10980,7 +11339,7 @@ function mapEventToSignal(event) {
|
|
|
10980
11339
|
errorCount: isError ? 1 : 0,
|
|
10981
11340
|
lastObservedIso: observedAt.toISOString(),
|
|
10982
11341
|
method,
|
|
10983
|
-
...
|
|
11342
|
+
...path54 ? { path: path54 } : {},
|
|
10984
11343
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
10985
11344
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
10986
11345
|
};
|
|
@@ -11020,19 +11379,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11020
11379
|
graph.forEachNode((id, attrs) => {
|
|
11021
11380
|
if (found) return;
|
|
11022
11381
|
const a = attrs;
|
|
11023
|
-
if (a.type ===
|
|
11382
|
+
if (a.type === import_types44.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
11024
11383
|
found = id;
|
|
11025
11384
|
}
|
|
11026
11385
|
});
|
|
11027
11386
|
return found;
|
|
11028
11387
|
}
|
|
11029
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11030
|
-
const normalizedPath = normalizePathTemplate(
|
|
11388
|
+
function findMatchingRouteNode(graph, serviceName, method, path54) {
|
|
11389
|
+
const normalizedPath = normalizePathTemplate(path54);
|
|
11031
11390
|
let found = null;
|
|
11032
11391
|
graph.forEachNode((id, attrs) => {
|
|
11033
11392
|
if (found) return;
|
|
11034
11393
|
const a = attrs;
|
|
11035
|
-
if (a.type !==
|
|
11394
|
+
if (a.type !== import_types44.NodeType.RouteNode || a.service !== serviceName) return;
|
|
11036
11395
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
11037
11396
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
11038
11397
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -11044,18 +11403,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11044
11403
|
return (signal) => {
|
|
11045
11404
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
11046
11405
|
const scriptName = signal.targetName;
|
|
11047
|
-
const { method, path:
|
|
11406
|
+
const { method, path: path54 } = signal;
|
|
11048
11407
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
11049
|
-
if (!method || !
|
|
11050
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
11408
|
+
if (!method || !path54) return wholeFileId;
|
|
11409
|
+
return findMatchingRouteNode(graph, serviceName, method, path54) ?? wholeFileId;
|
|
11051
11410
|
};
|
|
11052
11411
|
const mapping = config.workers?.[scriptName];
|
|
11053
11412
|
if (mapping) {
|
|
11054
|
-
const wholeFileId = (0,
|
|
11413
|
+
const wholeFileId = (0, import_types44.fileId)(mapping.service, mapping.entryFile);
|
|
11055
11414
|
return {
|
|
11056
11415
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
11057
11416
|
serviceName: mapping.service,
|
|
11058
|
-
edgeType:
|
|
11417
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11059
11418
|
};
|
|
11060
11419
|
}
|
|
11061
11420
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -11064,18 +11423,124 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
11064
11423
|
return {
|
|
11065
11424
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
11066
11425
|
serviceName: fileNode.service,
|
|
11067
|
-
edgeType:
|
|
11426
|
+
edgeType: import_types44.EdgeType.CALLS
|
|
11068
11427
|
};
|
|
11069
11428
|
}
|
|
11070
11429
|
return {
|
|
11071
|
-
targetNodeId: (0,
|
|
11430
|
+
targetNodeId: (0, import_types44.infraId)("cloudflare-worker", scriptName),
|
|
11072
11431
|
serviceName: scriptName,
|
|
11073
|
-
edgeType:
|
|
11432
|
+
edgeType: import_types44.EdgeType.CALLS,
|
|
11074
11433
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
11075
11434
|
};
|
|
11076
11435
|
};
|
|
11077
11436
|
}
|
|
11078
11437
|
|
|
11438
|
+
// src/connectors/vercel/index.ts
|
|
11439
|
+
init_cjs_shims();
|
|
11440
|
+
|
|
11441
|
+
// src/connectors/vercel/client.ts
|
|
11442
|
+
init_cjs_shims();
|
|
11443
|
+
var DEFAULT_API_BASE_URL = "https://api.vercel.com";
|
|
11444
|
+
var DEFAULT_DRAIN_NAME = "neat-otlp";
|
|
11445
|
+
var TRACE_SCHEMAS = { trace: { version: "v1" } };
|
|
11446
|
+
function apiBase(config) {
|
|
11447
|
+
return config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
11448
|
+
}
|
|
11449
|
+
function teamQuery(config) {
|
|
11450
|
+
return `?teamId=${encodeURIComponent(config.teamId)}`;
|
|
11451
|
+
}
|
|
11452
|
+
function drainDelivery(config, otelToken) {
|
|
11453
|
+
return {
|
|
11454
|
+
type: "http",
|
|
11455
|
+
endpoint: config.endpoint,
|
|
11456
|
+
encoding: "json",
|
|
11457
|
+
headers: bearerAuthHeader(otelToken),
|
|
11458
|
+
...config.secret ? { secret: config.secret } : {}
|
|
11459
|
+
};
|
|
11460
|
+
}
|
|
11461
|
+
async function describeError(res) {
|
|
11462
|
+
try {
|
|
11463
|
+
const data = await res.json();
|
|
11464
|
+
const message = data?.error?.message;
|
|
11465
|
+
return typeof message === "string" && message.length > 0 ? ` \u2014 ${message}` : "";
|
|
11466
|
+
} catch {
|
|
11467
|
+
return "";
|
|
11468
|
+
}
|
|
11469
|
+
}
|
|
11470
|
+
async function createVercelDrain(config, credentials, fetchImpl = fetch) {
|
|
11471
|
+
const projectIds = config.projectIds ?? [];
|
|
11472
|
+
const body = {
|
|
11473
|
+
name: config.drainName ?? DEFAULT_DRAIN_NAME,
|
|
11474
|
+
projects: projectIds.length > 0 ? "some" : "all",
|
|
11475
|
+
...projectIds.length > 0 ? { projectIds } : {},
|
|
11476
|
+
schemas: TRACE_SCHEMAS,
|
|
11477
|
+
delivery: drainDelivery(config, credentials.otelToken),
|
|
11478
|
+
source: { kind: "self-served" }
|
|
11479
|
+
};
|
|
11480
|
+
const res = await junctionFetch(
|
|
11481
|
+
`${apiBase(config)}/v1/drains${teamQuery(config)}`,
|
|
11482
|
+
{
|
|
11483
|
+
method: "POST",
|
|
11484
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11485
|
+
body: JSON.stringify(body)
|
|
11486
|
+
},
|
|
11487
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11488
|
+
);
|
|
11489
|
+
if (!res.ok) {
|
|
11490
|
+
throw new Error(
|
|
11491
|
+
`vercel connector: create drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11492
|
+
);
|
|
11493
|
+
}
|
|
11494
|
+
const payload = await res.json().catch(() => null);
|
|
11495
|
+
if (!payload || typeof payload.id !== "string" || payload.id.length === 0) {
|
|
11496
|
+
throw new Error(
|
|
11497
|
+
"vercel connector: create drain returned no drain id \u2014 the Drains API response shape may have changed"
|
|
11498
|
+
);
|
|
11499
|
+
}
|
|
11500
|
+
return {
|
|
11501
|
+
id: payload.id,
|
|
11502
|
+
...payload.status ? { status: payload.status } : {},
|
|
11503
|
+
...payload.disabledReason ? { disabledReason: payload.disabledReason } : {}
|
|
11504
|
+
};
|
|
11505
|
+
}
|
|
11506
|
+
async function deleteVercelDrain(config, drainId, credentials, fetchImpl = fetch) {
|
|
11507
|
+
const res = await junctionFetch(
|
|
11508
|
+
`${apiBase(config)}/v1/drains/${encodeURIComponent(drainId)}${teamQuery(config)}`,
|
|
11509
|
+
{ method: "DELETE", headers: { ...bearerAuthHeader(credentials.token) } },
|
|
11510
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11511
|
+
);
|
|
11512
|
+
if (res.ok || res.status === 404) return;
|
|
11513
|
+
throw new Error(
|
|
11514
|
+
`vercel connector: delete drain failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11515
|
+
);
|
|
11516
|
+
}
|
|
11517
|
+
async function testVercelDrainDelivery(config, credentials, fetchImpl = fetch) {
|
|
11518
|
+
const res = await junctionFetch(
|
|
11519
|
+
`${apiBase(config)}/v1/drains/test${teamQuery(config)}`,
|
|
11520
|
+
{
|
|
11521
|
+
method: "POST",
|
|
11522
|
+
headers: { "Content-Type": "application/json", ...bearerAuthHeader(credentials.token) },
|
|
11523
|
+
body: JSON.stringify({ schemas: TRACE_SCHEMAS, delivery: drainDelivery(config, credentials.otelToken) })
|
|
11524
|
+
},
|
|
11525
|
+
{ provider: "vercel", accountKey: config.teamId, fetchImpl }
|
|
11526
|
+
);
|
|
11527
|
+
if (res.status === 401 || res.status === 403) {
|
|
11528
|
+
return { status: "failure", error: `vercel rejected the API token (HTTP ${res.status})` };
|
|
11529
|
+
}
|
|
11530
|
+
if (!res.ok) {
|
|
11531
|
+
return {
|
|
11532
|
+
status: "failure",
|
|
11533
|
+
error: `vercel drain validation failed (${res.status} ${res.statusText}${await describeError(res)})`
|
|
11534
|
+
};
|
|
11535
|
+
}
|
|
11536
|
+
const payload = await res.json().catch(() => null);
|
|
11537
|
+
return {
|
|
11538
|
+
...payload?.status ? { status: payload.status } : {},
|
|
11539
|
+
...payload?.error ? { error: payload.error } : {},
|
|
11540
|
+
...payload?.endpoint ? { endpoint: payload.endpoint } : {}
|
|
11541
|
+
};
|
|
11542
|
+
}
|
|
11543
|
+
|
|
11079
11544
|
// src/connectors/registry.ts
|
|
11080
11545
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
11081
11546
|
async function authProbe(input) {
|
|
@@ -11212,21 +11677,112 @@ var PROVIDER_DISPATCH = {
|
|
|
11212
11677
|
resolveTarget: createCloudflareResolveTarget(config, graph)
|
|
11213
11678
|
};
|
|
11214
11679
|
},
|
|
11215
|
-
// GET /
|
|
11216
|
-
//
|
|
11680
|
+
// GET /accounts/{accountId}/tokens/verify — the *account-scoped* token-verify
|
|
11681
|
+
// endpoint. A Workers connector token is scoped to the account, and the
|
|
11682
|
+
// user-level `GET /user/tokens/verify` returns 401 "Invalid API Token" for
|
|
11683
|
+
// such a token even though it authenticates fine against the account's own
|
|
11684
|
+
// resources (confirmed live). Probing the account-scoped verify endpoint —
|
|
11685
|
+
// `accountId` is already required for this provider — returns 200
|
|
11686
|
+
// `{status:"active"}` for a working token and 401 for a bad one, so a valid
|
|
11687
|
+
// Workers token is no longer falsely rejected at `neat connector add`.
|
|
11217
11688
|
validate({ credentials, options, fetchImpl }) {
|
|
11218
11689
|
const cfg = options;
|
|
11219
11690
|
const baseUrl = cfg.baseUrl ?? CLOUDFLARE_API_BASE_URL;
|
|
11220
11691
|
return authProbe({
|
|
11221
11692
|
provider: "cloudflare",
|
|
11222
11693
|
accountKey: cfg.accountId ?? "validate",
|
|
11223
|
-
url: `${baseUrl}/
|
|
11694
|
+
url: `${baseUrl}/accounts/${cfg.accountId ?? ""}/tokens/verify`,
|
|
11224
11695
|
token: String(credentials.apiToken ?? ""),
|
|
11225
11696
|
...fetchImpl ? { fetchImpl } : {}
|
|
11226
11697
|
});
|
|
11227
11698
|
}
|
|
11228
11699
|
}
|
|
11229
11700
|
};
|
|
11701
|
+
function vercelCredsFrom(credentials) {
|
|
11702
|
+
return { token: String(credentials.token ?? ""), otelToken: String(credentials.otelToken ?? "") };
|
|
11703
|
+
}
|
|
11704
|
+
function vercelConfigFromOptions(options) {
|
|
11705
|
+
const raw = options.projectIds;
|
|
11706
|
+
let projectIds;
|
|
11707
|
+
if (Array.isArray(raw)) projectIds = raw.filter((p) => typeof p === "string");
|
|
11708
|
+
else if (typeof raw === "string" && raw.trim().length > 0) {
|
|
11709
|
+
projectIds = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
11710
|
+
}
|
|
11711
|
+
return {
|
|
11712
|
+
teamId: String(options.teamId ?? ""),
|
|
11713
|
+
endpoint: String(options.endpoint ?? ""),
|
|
11714
|
+
...projectIds && projectIds.length > 0 ? { projectIds } : {},
|
|
11715
|
+
...typeof options.drainId === "string" ? { drainId: options.drainId } : {},
|
|
11716
|
+
...typeof options.drainName === "string" ? { drainName: options.drainName } : {},
|
|
11717
|
+
...typeof options.apiBaseUrl === "string" ? { apiBaseUrl: options.apiBaseUrl } : {},
|
|
11718
|
+
...typeof options.secret === "string" ? { secret: options.secret } : {}
|
|
11719
|
+
};
|
|
11720
|
+
}
|
|
11721
|
+
var PUSH_PROVIDER_DISPATCH = {
|
|
11722
|
+
vercel: {
|
|
11723
|
+
provider: "vercel",
|
|
11724
|
+
// The Vercel access token is the "primary" secret a single `--token`
|
|
11725
|
+
// populates; `otelToken` (the daemon's OTLP bearer) is the second field.
|
|
11726
|
+
primaryCredentialKey: "token",
|
|
11727
|
+
requiredCredentialFields: ["token", "otelToken"],
|
|
11728
|
+
// teamId scopes every Drains call; endpoint is where the drain delivers.
|
|
11729
|
+
// projectIds is optional (absent → the drain covers the whole team).
|
|
11730
|
+
requiredOptionFields: ["teamId", "endpoint"],
|
|
11731
|
+
// POST /v1/drains/test — authenticates the token and pings the endpoint
|
|
11732
|
+
// with a sample event, so `success` means the credential is live *and* the
|
|
11733
|
+
// daemon's OTLP endpoint is reachable and accepted the drain's bearer.
|
|
11734
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
11735
|
+
const result = await testVercelDrainDelivery(
|
|
11736
|
+
vercelConfigFromOptions(options),
|
|
11737
|
+
vercelCredsFrom(credentials),
|
|
11738
|
+
fetchImpl
|
|
11739
|
+
);
|
|
11740
|
+
if (result.status === "success") return { ok: true };
|
|
11741
|
+
return {
|
|
11742
|
+
ok: false,
|
|
11743
|
+
reason: result.error ?? `vercel drain delivery test returned "${result.status ?? "no status"}"`
|
|
11744
|
+
};
|
|
11745
|
+
},
|
|
11746
|
+
// POST /v1/drains — creates the trace drain, returns its id to store in
|
|
11747
|
+
// `options.drainId`. A created-but-not-enabled drain is surfaced as a note,
|
|
11748
|
+
// not a failure (the entry still points at a real drain).
|
|
11749
|
+
async provision({ credentials, options, fetchImpl }) {
|
|
11750
|
+
try {
|
|
11751
|
+
const created = await createVercelDrain(
|
|
11752
|
+
vercelConfigFromOptions(options),
|
|
11753
|
+
vercelCredsFrom(credentials),
|
|
11754
|
+
fetchImpl
|
|
11755
|
+
);
|
|
11756
|
+
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;
|
|
11757
|
+
return { ok: true, options: { drainId: created.id }, ...note ? { note } : {} };
|
|
11758
|
+
} catch (err) {
|
|
11759
|
+
return { ok: false, reason: err.message };
|
|
11760
|
+
}
|
|
11761
|
+
},
|
|
11762
|
+
// DELETE /v1/drains/{id} — idempotent (deleteVercelDrain treats 404 as
|
|
11763
|
+
// success). No recorded drainId → nothing to delete, still a success.
|
|
11764
|
+
async deprovision({ credentials, options, fetchImpl }) {
|
|
11765
|
+
const drainId = typeof options.drainId === "string" ? options.drainId : "";
|
|
11766
|
+
if (!drainId) {
|
|
11767
|
+
return { ok: true, note: "no drain id was recorded \u2014 nothing to delete on the Vercel side" };
|
|
11768
|
+
}
|
|
11769
|
+
try {
|
|
11770
|
+
await deleteVercelDrain(
|
|
11771
|
+
vercelConfigFromOptions(options),
|
|
11772
|
+
drainId,
|
|
11773
|
+
vercelCredsFrom(credentials),
|
|
11774
|
+
fetchImpl
|
|
11775
|
+
);
|
|
11776
|
+
return { ok: true };
|
|
11777
|
+
} catch (err) {
|
|
11778
|
+
return { ok: false, reason: err.message };
|
|
11779
|
+
}
|
|
11780
|
+
}
|
|
11781
|
+
}
|
|
11782
|
+
};
|
|
11783
|
+
function isPushProvider(provider) {
|
|
11784
|
+
return provider in PUSH_PROVIDER_DISPATCH;
|
|
11785
|
+
}
|
|
11230
11786
|
function resolveEntryCredentials(dispatch, entry2, env) {
|
|
11231
11787
|
let credentials;
|
|
11232
11788
|
try {
|
|
@@ -11249,6 +11805,13 @@ function resolveEntryCredentials(dispatch, entry2, env) {
|
|
|
11249
11805
|
function buildRegistration(entry2, graph, env = process.env) {
|
|
11250
11806
|
const dispatch = PROVIDER_DISPATCH[entry2.provider];
|
|
11251
11807
|
if (!dispatch) {
|
|
11808
|
+
if (isPushProvider(entry2.provider)) {
|
|
11809
|
+
return {
|
|
11810
|
+
ok: false,
|
|
11811
|
+
push: true,
|
|
11812
|
+
reason: `push provider "${entry2.provider}" ingests via the OTLP receiver \u2014 nothing to poll`
|
|
11813
|
+
};
|
|
11814
|
+
}
|
|
11252
11815
|
return { ok: false, reason: `unknown provider "${entry2.provider}"` };
|
|
11253
11816
|
}
|
|
11254
11817
|
const creds = resolveEntryCredentials(dispatch, entry2, env);
|
|
@@ -11299,7 +11862,7 @@ async function loadConnectorRegistrations(input) {
|
|
|
11299
11862
|
if (!connectorMatchesProject(entry2, project)) continue;
|
|
11300
11863
|
const result = buildRegistration(entry2, graph, env);
|
|
11301
11864
|
if (result.ok) registrations.push(result.registration);
|
|
11302
|
-
else onSkip?.(entry2, result.reason);
|
|
11865
|
+
else if (!result.push) onSkip?.(entry2, result.reason);
|
|
11303
11866
|
}
|
|
11304
11867
|
return registrations;
|
|
11305
11868
|
}
|
|
@@ -11310,7 +11873,7 @@ init_auth();
|
|
|
11310
11873
|
// src/unrouted.ts
|
|
11311
11874
|
init_cjs_shims();
|
|
11312
11875
|
var import_node_fs30 = require("fs");
|
|
11313
|
-
var
|
|
11876
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11314
11877
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
11315
11878
|
return {
|
|
11316
11879
|
timestamp: now.toISOString(),
|
|
@@ -11320,34 +11883,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
11320
11883
|
};
|
|
11321
11884
|
}
|
|
11322
11885
|
async function appendUnroutedSpan(neatHome4, record) {
|
|
11323
|
-
const target =
|
|
11886
|
+
const target = import_node_path50.default.join(neatHome4, "errors.ndjson");
|
|
11324
11887
|
await import_node_fs30.promises.mkdir(neatHome4, { recursive: true });
|
|
11325
11888
|
await import_node_fs30.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
11326
11889
|
}
|
|
11327
11890
|
function unroutedErrorsPath(neatHome4) {
|
|
11328
|
-
return
|
|
11891
|
+
return import_node_path50.default.join(neatHome4, "errors.ndjson");
|
|
11329
11892
|
}
|
|
11330
11893
|
|
|
11331
11894
|
// src/daemon.ts
|
|
11332
|
-
var
|
|
11895
|
+
var import_types47 = require("@neat.is/types");
|
|
11333
11896
|
function daemonJsonPath(scanPath) {
|
|
11334
|
-
return
|
|
11897
|
+
return import_node_path51.default.join(scanPath, "neat-out", "daemon.json");
|
|
11335
11898
|
}
|
|
11336
11899
|
function daemonsDiscoveryDir(home) {
|
|
11337
11900
|
const base = home && home.length > 0 ? home : neatHomeFromEnv();
|
|
11338
|
-
return
|
|
11901
|
+
return import_node_path51.default.join(base, "daemons");
|
|
11339
11902
|
}
|
|
11340
11903
|
function daemonDiscoveryPath(project, home) {
|
|
11341
|
-
return
|
|
11904
|
+
return import_node_path51.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
|
|
11342
11905
|
}
|
|
11343
11906
|
function sanitizeDiscoveryName(project) {
|
|
11344
11907
|
return project.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
11345
11908
|
}
|
|
11346
11909
|
function neatHomeFromEnv() {
|
|
11347
11910
|
const env = process.env.NEAT_HOME;
|
|
11348
|
-
if (env && env.length > 0) return
|
|
11911
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11349
11912
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11350
|
-
return
|
|
11913
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11351
11914
|
}
|
|
11352
11915
|
function resolveNeatVersion() {
|
|
11353
11916
|
if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
|
|
@@ -11416,11 +11979,11 @@ function teardownSlot(slot) {
|
|
|
11416
11979
|
}
|
|
11417
11980
|
}
|
|
11418
11981
|
function neatHomeFor(opts) {
|
|
11419
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
11982
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path51.default.resolve(opts.neatHome);
|
|
11420
11983
|
const env = process.env.NEAT_HOME;
|
|
11421
|
-
if (env && env.length > 0) return
|
|
11984
|
+
if (env && env.length > 0) return import_node_path51.default.resolve(env);
|
|
11422
11985
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
11423
|
-
return
|
|
11986
|
+
return import_node_path51.default.join(home, ".neat");
|
|
11424
11987
|
}
|
|
11425
11988
|
function routeSpanToProject(serviceName, projects) {
|
|
11426
11989
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -11464,11 +12027,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
11464
12027
|
if (!serviceName) return true;
|
|
11465
12028
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
11466
12029
|
return graph.someNode(
|
|
11467
|
-
(_id, attrs) => attrs.type ===
|
|
12030
|
+
(_id, attrs) => attrs.type === import_types47.NodeType.ServiceNode && attrs.name === serviceName
|
|
11468
12031
|
);
|
|
11469
12032
|
}
|
|
11470
12033
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|
|
11471
|
-
const paths = pathsForProject(entry2.name,
|
|
12034
|
+
const paths = pathsForProject(entry2.name, import_node_path51.default.join(entry2.path, "neat-out"));
|
|
11472
12035
|
try {
|
|
11473
12036
|
const stat = await import_node_fs31.promises.stat(entry2.path);
|
|
11474
12037
|
if (!stat.isDirectory()) {
|
|
@@ -11599,7 +12162,7 @@ async function startDaemon(opts = {}) {
|
|
|
11599
12162
|
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
12163
|
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
12164
|
const singleProject = projectArg;
|
|
11602
|
-
const singleProjectPath = singleProject && projectPathArg ?
|
|
12165
|
+
const singleProjectPath = singleProject && projectPathArg ? import_node_path51.default.resolve(projectPathArg) : null;
|
|
11603
12166
|
if (singleProject && !singleProjectPath) {
|
|
11604
12167
|
throw new Error(
|
|
11605
12168
|
`neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
|
|
@@ -11614,7 +12177,7 @@ async function startDaemon(opts = {}) {
|
|
|
11614
12177
|
);
|
|
11615
12178
|
}
|
|
11616
12179
|
}
|
|
11617
|
-
const pidPath =
|
|
12180
|
+
const pidPath = import_node_path51.default.join(home, "neatd.pid");
|
|
11618
12181
|
await writeAtomically(pidPath, `${process.pid}
|
|
11619
12182
|
`);
|
|
11620
12183
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12021,8 +12584,8 @@ async function startDaemon(opts = {}) {
|
|
|
12021
12584
|
let registryWatcher = null;
|
|
12022
12585
|
let reloadTimer = null;
|
|
12023
12586
|
if (!singleProject) try {
|
|
12024
|
-
const regDir =
|
|
12025
|
-
const regBase =
|
|
12587
|
+
const regDir = import_node_path51.default.dirname(regPath);
|
|
12588
|
+
const regBase = import_node_path51.default.basename(regPath);
|
|
12026
12589
|
registryWatcher = (0, import_node_fs31.watch)(regDir, (_eventType, filename) => {
|
|
12027
12590
|
if (filename !== null && filename !== regBase) return;
|
|
12028
12591
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
@@ -12097,7 +12660,7 @@ init_cjs_shims();
|
|
|
12097
12660
|
var import_node_child_process2 = require("child_process");
|
|
12098
12661
|
var import_node_fs32 = require("fs");
|
|
12099
12662
|
var import_node_net = __toESM(require("net"), 1);
|
|
12100
|
-
var
|
|
12663
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
12101
12664
|
var DEFAULT_WEB_PORT = 6328;
|
|
12102
12665
|
var DEFAULT_REST_PORT = 8080;
|
|
12103
12666
|
function asValidPort(value) {
|
|
@@ -12106,11 +12669,11 @@ function asValidPort(value) {
|
|
|
12106
12669
|
}
|
|
12107
12670
|
function projectRoot() {
|
|
12108
12671
|
const fromEnv = process.env.NEAT_SCAN_PATH;
|
|
12109
|
-
return
|
|
12672
|
+
return import_node_path52.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
|
|
12110
12673
|
}
|
|
12111
12674
|
async function readDaemonPorts(root) {
|
|
12112
12675
|
try {
|
|
12113
|
-
const raw = await import_node_fs32.promises.readFile(
|
|
12676
|
+
const raw = await import_node_fs32.promises.readFile(import_node_path52.default.join(root, "neat-out", "daemon.json"), "utf8");
|
|
12114
12677
|
const parsed = JSON.parse(raw);
|
|
12115
12678
|
const ports = parsed?.ports ?? {};
|
|
12116
12679
|
return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
|
|
@@ -12155,10 +12718,10 @@ function resolveWebPackageDir() {
|
|
|
12155
12718
|
eval("require")
|
|
12156
12719
|
);
|
|
12157
12720
|
const pkgJsonPath = req.resolve("@neat.is/web/package.json");
|
|
12158
|
-
return
|
|
12721
|
+
return import_node_path52.default.dirname(pkgJsonPath);
|
|
12159
12722
|
}
|
|
12160
12723
|
function resolveStandaloneServerEntry(webDir) {
|
|
12161
|
-
return
|
|
12724
|
+
return import_node_path52.default.join(webDir, ".next/standalone/packages/web/server.js");
|
|
12162
12725
|
}
|
|
12163
12726
|
async function pickInternalPort() {
|
|
12164
12727
|
return new Promise((resolve, reject) => {
|
|
@@ -12211,7 +12774,7 @@ async function spawnWebUI(restPort, opts = {}) {
|
|
|
12211
12774
|
NEAT_API_URL: apiUrl
|
|
12212
12775
|
};
|
|
12213
12776
|
child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
|
|
12214
|
-
cwd:
|
|
12777
|
+
cwd: import_node_path52.default.dirname(serverEntry),
|
|
12215
12778
|
env,
|
|
12216
12779
|
stdio: ["ignore", "inherit", "inherit"],
|
|
12217
12780
|
detached: false
|
|
@@ -12387,14 +12950,14 @@ function localVersion() {
|
|
|
12387
12950
|
}
|
|
12388
12951
|
function neatHome3() {
|
|
12389
12952
|
if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
|
|
12390
|
-
return
|
|
12953
|
+
return import_node_path53.default.resolve(process.env.NEAT_HOME);
|
|
12391
12954
|
}
|
|
12392
12955
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12393
|
-
return
|
|
12956
|
+
return import_node_path53.default.join(home, ".neat");
|
|
12394
12957
|
}
|
|
12395
12958
|
async function readPid() {
|
|
12396
12959
|
try {
|
|
12397
|
-
const raw = await import_node_fs33.promises.readFile(
|
|
12960
|
+
const raw = await import_node_fs33.promises.readFile(import_node_path53.default.join(neatHome3(), "neatd.pid"), "utf8");
|
|
12398
12961
|
const n = Number.parseInt(raw.trim(), 10);
|
|
12399
12962
|
return Number.isFinite(n) ? n : null;
|
|
12400
12963
|
} catch {
|