@neat.is/core 0.5.3 → 0.5.4-dev.20260721
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-I72HTUOG.js → chunk-4RU3AAOI.js} +2 -2
- package/dist/{chunk-CFDPIMRP.js → chunk-I4NZ7PSN.js} +3 -2
- package/dist/chunk-I4NZ7PSN.js.map +1 -0
- package/dist/{chunk-GJHEZC5K.js → chunk-PEFX3DBR.js} +422 -111
- package/dist/chunk-PEFX3DBR.js.map +1 -0
- package/dist/{chunk-X2AMX3QZ.js → chunk-VR73QNJD.js} +263 -10
- package/dist/chunk-VR73QNJD.js.map +1 -0
- package/dist/cli.cjs +1094 -448
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +95 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +798 -282
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +807 -291
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-IDMIH6ZY.js → otel-grpc-TEGOXE6T.js} +3 -3
- package/dist/server.cjs +498 -172
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-CFDPIMRP.js.map +0 -1
- package/dist/chunk-GJHEZC5K.js.map +0 -1
- package/dist/chunk-X2AMX3QZ.js.map +0 -1
- /package/dist/{chunk-I72HTUOG.js.map → chunk-4RU3AAOI.js.map} +0 -0
- /package/dist/{otel-grpc-IDMIH6ZY.js.map → otel-grpc-TEGOXE6T.js.map} +0 -0
package/dist/server.cjs
CHANGED
|
@@ -59,8 +59,8 @@ function mountBearerAuth(app, opts) {
|
|
|
59
59
|
]);
|
|
60
60
|
const publicRead = opts.publicRead === true;
|
|
61
61
|
app.addHook("preHandler", (req, reply, done) => {
|
|
62
|
-
const
|
|
63
|
-
if (exactUnauthPaths.has(
|
|
62
|
+
const path52 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
63
|
+
if (exactUnauthPaths.has(path52) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path52)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -192,8 +192,8 @@ function reshapeGrpcRequest(req) {
|
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
194
|
function resolveProtoRoot() {
|
|
195
|
-
const here =
|
|
196
|
-
return
|
|
195
|
+
const here = import_node_path48.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
196
|
+
return import_node_path48.default.resolve(here, "..", "proto");
|
|
197
197
|
}
|
|
198
198
|
function loadTraceService() {
|
|
199
199
|
const protoRoot = resolveProtoRoot();
|
|
@@ -261,13 +261,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
261
261
|
})
|
|
262
262
|
};
|
|
263
263
|
}
|
|
264
|
-
var import_node_url,
|
|
264
|
+
var import_node_url, import_node_path48, import_node_crypto2, grpc, protoLoader;
|
|
265
265
|
var init_otel_grpc = __esm({
|
|
266
266
|
"src/otel-grpc.ts"() {
|
|
267
267
|
"use strict";
|
|
268
268
|
init_cjs_shims();
|
|
269
269
|
import_node_url = require("url");
|
|
270
|
-
|
|
270
|
+
import_node_path48 = __toESM(require("path"), 1);
|
|
271
271
|
import_node_crypto2 = require("crypto");
|
|
272
272
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
273
273
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -360,8 +360,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
360
360
|
const v = attrs[key];
|
|
361
361
|
if (typeof v === "string" && v.length > 0) {
|
|
362
362
|
const q = v.indexOf("?");
|
|
363
|
-
const
|
|
364
|
-
if (
|
|
363
|
+
const path52 = q === -1 ? v : v.slice(0, q);
|
|
364
|
+
if (path52.length > 0) return path52;
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
return void 0;
|
|
@@ -396,6 +396,7 @@ function parseOtlpRequest(body) {
|
|
|
396
396
|
attributes: attrs,
|
|
397
397
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
398
398
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
399
|
+
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
399
400
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
400
401
|
messagingDestination: messagingDestinationOf(attrs),
|
|
401
402
|
graphqlOperationName: typeof attrs["graphql.operation.name"] === "string" && attrs["graphql.operation.name"].length > 0 ? attrs["graphql.operation.name"] : void 0,
|
|
@@ -415,10 +416,10 @@ function parseOtlpRequest(body) {
|
|
|
415
416
|
return out;
|
|
416
417
|
}
|
|
417
418
|
function loadProtoRoot() {
|
|
418
|
-
const here =
|
|
419
|
-
const protoRoot =
|
|
419
|
+
const here = import_node_path49.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
420
|
+
const protoRoot = import_node_path49.default.resolve(here, "..", "proto");
|
|
420
421
|
const root = new import_protobufjs.default.Root();
|
|
421
|
-
root.resolvePath = (_origin, target) =>
|
|
422
|
+
root.resolvePath = (_origin, target) => import_node_path49.default.resolve(protoRoot, target);
|
|
422
423
|
root.loadSync(
|
|
423
424
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
424
425
|
{ keepCase: true }
|
|
@@ -630,12 +631,12 @@ async function buildOtelReceiver(opts) {
|
|
|
630
631
|
};
|
|
631
632
|
return decorated;
|
|
632
633
|
}
|
|
633
|
-
var
|
|
634
|
+
var import_node_path49, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
634
635
|
var init_otel = __esm({
|
|
635
636
|
"src/otel.ts"() {
|
|
636
637
|
"use strict";
|
|
637
638
|
init_cjs_shims();
|
|
638
|
-
|
|
639
|
+
import_node_path49 = __toESM(require("path"), 1);
|
|
639
640
|
import_node_url2 = require("url");
|
|
640
641
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
641
642
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -651,7 +652,7 @@ var init_otel = __esm({
|
|
|
651
652
|
|
|
652
653
|
// src/server.ts
|
|
653
654
|
init_cjs_shims();
|
|
654
|
-
var
|
|
655
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
655
656
|
|
|
656
657
|
// src/graph.ts
|
|
657
658
|
init_cjs_shims();
|
|
@@ -675,7 +676,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
675
676
|
init_cjs_shims();
|
|
676
677
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
677
678
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
678
|
-
var
|
|
679
|
+
var import_types34 = require("@neat.is/types");
|
|
679
680
|
|
|
680
681
|
// src/extend/index.ts
|
|
681
682
|
init_cjs_shims();
|
|
@@ -1446,19 +1447,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1446
1447
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1447
1448
|
let best = { path: [start], edges: [] };
|
|
1448
1449
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1449
|
-
function step(node,
|
|
1450
|
-
if (
|
|
1451
|
-
best = { path: [...
|
|
1450
|
+
function step(node, path52, edges) {
|
|
1451
|
+
if (path52.length > best.path.length) {
|
|
1452
|
+
best = { path: [...path52], edges: [...edges] };
|
|
1452
1453
|
}
|
|
1453
|
-
if (
|
|
1454
|
+
if (path52.length - 1 >= maxDepth) return;
|
|
1454
1455
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1455
1456
|
for (const [srcId, edge] of incoming) {
|
|
1456
1457
|
if (visited.has(srcId)) continue;
|
|
1457
1458
|
visited.add(srcId);
|
|
1458
|
-
|
|
1459
|
+
path52.push(srcId);
|
|
1459
1460
|
edges.push(edge);
|
|
1460
|
-
step(srcId,
|
|
1461
|
-
|
|
1461
|
+
step(srcId, path52, edges);
|
|
1462
|
+
path52.pop();
|
|
1462
1463
|
edges.pop();
|
|
1463
1464
|
visited.delete(srcId);
|
|
1464
1465
|
}
|
|
@@ -1652,26 +1653,26 @@ function dominantFailingCall(graph, serviceId5, visited) {
|
|
|
1652
1653
|
return best;
|
|
1653
1654
|
}
|
|
1654
1655
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
1655
|
-
const
|
|
1656
|
+
const path52 = [originServiceId];
|
|
1656
1657
|
const edges = [];
|
|
1657
1658
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
1658
1659
|
let current = originServiceId;
|
|
1659
1660
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1660
1661
|
const hop = dominantFailingCall(graph, current, visited);
|
|
1661
1662
|
if (!hop) break;
|
|
1662
|
-
|
|
1663
|
+
path52.push(hop.nextService);
|
|
1663
1664
|
edges.push(hop.edge);
|
|
1664
1665
|
visited.add(hop.nextService);
|
|
1665
1666
|
current = hop.nextService;
|
|
1666
1667
|
}
|
|
1667
1668
|
if (edges.length === 0) return null;
|
|
1668
|
-
return { path:
|
|
1669
|
+
return { path: path52, edges, culprit: current };
|
|
1669
1670
|
}
|
|
1670
1671
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
1671
1672
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
1672
1673
|
if (!chain) return null;
|
|
1673
1674
|
const culprit = chain.culprit;
|
|
1674
|
-
const
|
|
1675
|
+
const path52 = [...chain.path];
|
|
1675
1676
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
1676
1677
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
1677
1678
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -1679,14 +1680,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1679
1680
|
if (loc) {
|
|
1680
1681
|
let rootCauseNode = culprit;
|
|
1681
1682
|
if (loc.fileNode) {
|
|
1682
|
-
|
|
1683
|
+
path52.push(loc.fileNode);
|
|
1683
1684
|
edgeProvenances.push(import_types.Provenance.OBSERVED);
|
|
1684
1685
|
rootCauseNode = loc.fileNode;
|
|
1685
1686
|
}
|
|
1686
1687
|
return import_types.RootCauseResultSchema.parse({
|
|
1687
1688
|
rootCauseNode,
|
|
1688
1689
|
rootCauseReason: loc.rootCauseReason,
|
|
1689
|
-
traversalPath:
|
|
1690
|
+
traversalPath: path52,
|
|
1690
1691
|
edgeProvenances,
|
|
1691
1692
|
confidence,
|
|
1692
1693
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -1698,7 +1699,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
1698
1699
|
return import_types.RootCauseResultSchema.parse({
|
|
1699
1700
|
rootCauseNode: culprit,
|
|
1700
1701
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
1701
|
-
traversalPath:
|
|
1702
|
+
traversalPath: path52,
|
|
1702
1703
|
edgeProvenances,
|
|
1703
1704
|
confidence,
|
|
1704
1705
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -3122,6 +3123,19 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
3122
3123
|
graph.addNode(id, node);
|
|
3123
3124
|
return id;
|
|
3124
3125
|
}
|
|
3126
|
+
function ensureInfraNode(graph, kind, name, provider) {
|
|
3127
|
+
const id = (0, import_types4.infraId)(kind, name);
|
|
3128
|
+
if (graph.hasNode(id)) return id;
|
|
3129
|
+
const node = {
|
|
3130
|
+
id,
|
|
3131
|
+
type: import_types4.NodeType.InfraNode,
|
|
3132
|
+
name,
|
|
3133
|
+
provider,
|
|
3134
|
+
kind
|
|
3135
|
+
};
|
|
3136
|
+
graph.addNode(id, node);
|
|
3137
|
+
return id;
|
|
3138
|
+
}
|
|
3125
3139
|
function ensureDatabaseNode(graph, host, engine) {
|
|
3126
3140
|
const id = (0, import_types4.databaseId)(host);
|
|
3127
3141
|
if (graph.hasNode(id)) return id;
|
|
@@ -3434,6 +3448,18 @@ async function handleSpan(ctx, span) {
|
|
|
3434
3448
|
callSiteEvidence
|
|
3435
3449
|
);
|
|
3436
3450
|
if (result) affectedNode = targetId;
|
|
3451
|
+
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
3452
|
+
const collectionId = ensureInfraNode(ctx.graph, "mongodb-collection", span.dbCollection, "self");
|
|
3453
|
+
upsertObservedEdge(
|
|
3454
|
+
ctx.graph,
|
|
3455
|
+
import_types4.EdgeType.CALLS,
|
|
3456
|
+
observedSource(),
|
|
3457
|
+
collectionId,
|
|
3458
|
+
ts,
|
|
3459
|
+
isError,
|
|
3460
|
+
callSiteEvidence
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3437
3463
|
}
|
|
3438
3464
|
} else if (span.messagingSystem && span.messagingDestination && spanMintsMessagingEdge(span.kind)) {
|
|
3439
3465
|
const targetId = ensureMessagingDestinationNode(
|
|
@@ -6281,7 +6307,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
6281
6307
|
|
|
6282
6308
|
// src/extract/calls/index.ts
|
|
6283
6309
|
init_cjs_shims();
|
|
6284
|
-
var
|
|
6310
|
+
var import_types22 = require("@neat.is/types");
|
|
6285
6311
|
|
|
6286
6312
|
// src/extract/calls/http.ts
|
|
6287
6313
|
init_cjs_shims();
|
|
@@ -6994,15 +7020,311 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6994
7020
|
return out;
|
|
6995
7021
|
}
|
|
6996
7022
|
|
|
7023
|
+
// src/extract/calls/mongoose.ts
|
|
7024
|
+
init_cjs_shims();
|
|
7025
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
7026
|
+
var import_types21 = require("@neat.is/types");
|
|
7027
|
+
var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
|
|
7028
|
+
var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
|
|
7029
|
+
var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
|
|
7030
|
+
var UNCOUNTABLES = /* @__PURE__ */ new Set([
|
|
7031
|
+
"advice",
|
|
7032
|
+
"energy",
|
|
7033
|
+
"excretion",
|
|
7034
|
+
"digestion",
|
|
7035
|
+
"cooperation",
|
|
7036
|
+
"health",
|
|
7037
|
+
"justice",
|
|
7038
|
+
"labour",
|
|
7039
|
+
"machinery",
|
|
7040
|
+
"equipment",
|
|
7041
|
+
"information",
|
|
7042
|
+
"pollution",
|
|
7043
|
+
"sewage",
|
|
7044
|
+
"paper",
|
|
7045
|
+
"money",
|
|
7046
|
+
"species",
|
|
7047
|
+
"series",
|
|
7048
|
+
"rain",
|
|
7049
|
+
"rice",
|
|
7050
|
+
"fish",
|
|
7051
|
+
"sheep",
|
|
7052
|
+
"moose",
|
|
7053
|
+
"deer",
|
|
7054
|
+
"news",
|
|
7055
|
+
"expertise",
|
|
7056
|
+
"status",
|
|
7057
|
+
"media"
|
|
7058
|
+
]);
|
|
7059
|
+
var PLURALIZE_RULES = [
|
|
7060
|
+
[/human$/gi, "humans"],
|
|
7061
|
+
[/(m)an$/gi, "$1en"],
|
|
7062
|
+
[/(pe)rson$/gi, "$1ople"],
|
|
7063
|
+
[/(child)$/gi, "$1ren"],
|
|
7064
|
+
[/^(ox)$/gi, "$1en"],
|
|
7065
|
+
[/(ax|test)is$/gi, "$1es"],
|
|
7066
|
+
[/(octop|vir)us$/gi, "$1i"],
|
|
7067
|
+
[/(alias|status)$/gi, "$1es"],
|
|
7068
|
+
[/(bu)s$/gi, "$1ses"],
|
|
7069
|
+
[/(buffal|tomat|potat)o$/gi, "$1oes"],
|
|
7070
|
+
[/([ti])um$/gi, "$1a"],
|
|
7071
|
+
[/sis$/gi, "ses"],
|
|
7072
|
+
[/(?:([^f])fe|([lr])f)$/gi, "$1$2ves"],
|
|
7073
|
+
[/(hive)$/gi, "$1s"],
|
|
7074
|
+
[/([^aeiouy]|qu)y$/gi, "$1ies"],
|
|
7075
|
+
[/(x|ch|ss|sh)$/gi, "$1es"],
|
|
7076
|
+
[/(matr|vert|ind)ix|ex$/gi, "$1ices"],
|
|
7077
|
+
[/([m|l])ouse$/gi, "$1ice"],
|
|
7078
|
+
[/(kn|w|l)ife$/gi, "$1ives"],
|
|
7079
|
+
[/(quiz)$/gi, "$1zes"],
|
|
7080
|
+
[/s$/gi, "s"],
|
|
7081
|
+
[/([^a-z])$/, "$1"],
|
|
7082
|
+
[/$/gi, "s"]
|
|
7083
|
+
];
|
|
7084
|
+
function pluralizeCollection(name) {
|
|
7085
|
+
const str = name.toLowerCase();
|
|
7086
|
+
if (UNCOUNTABLES.has(str)) return str;
|
|
7087
|
+
for (const [re, repl] of PLURALIZE_RULES) {
|
|
7088
|
+
if (str.match(re)) return str.replace(re, repl);
|
|
7089
|
+
}
|
|
7090
|
+
return str;
|
|
7091
|
+
}
|
|
7092
|
+
var MODEL_METHODS = [
|
|
7093
|
+
"find",
|
|
7094
|
+
"findOne",
|
|
7095
|
+
"findById",
|
|
7096
|
+
"findByIdAndUpdate",
|
|
7097
|
+
"findByIdAndDelete",
|
|
7098
|
+
"findOneAndUpdate",
|
|
7099
|
+
"findOneAndDelete",
|
|
7100
|
+
"findOneAndReplace",
|
|
7101
|
+
"countDocuments",
|
|
7102
|
+
"estimatedDocumentCount",
|
|
7103
|
+
"distinct",
|
|
7104
|
+
"aggregate",
|
|
7105
|
+
"exists",
|
|
7106
|
+
"where",
|
|
7107
|
+
"populate",
|
|
7108
|
+
"watch",
|
|
7109
|
+
"hydrate",
|
|
7110
|
+
"create",
|
|
7111
|
+
"insertMany",
|
|
7112
|
+
"insertOne",
|
|
7113
|
+
"updateOne",
|
|
7114
|
+
"updateMany",
|
|
7115
|
+
"replaceOne",
|
|
7116
|
+
"deleteOne",
|
|
7117
|
+
"deleteMany",
|
|
7118
|
+
"bulkWrite",
|
|
7119
|
+
"bulkSave",
|
|
7120
|
+
"save",
|
|
7121
|
+
// pre-v7, removed in current mongoose but present in older codebases:
|
|
7122
|
+
"count",
|
|
7123
|
+
"update",
|
|
7124
|
+
"remove",
|
|
7125
|
+
"findOneAndRemove",
|
|
7126
|
+
"findByIdAndRemove"
|
|
7127
|
+
];
|
|
7128
|
+
var MODEL_METHODS_ALT = MODEL_METHODS.join("|");
|
|
7129
|
+
function schemaCollectionVars(content) {
|
|
7130
|
+
const out = /* @__PURE__ */ new Map();
|
|
7131
|
+
const re = /(?:const|let|var)\s+(\w+)\s*=\s*new\s+(?:mongoose\s*\.\s*)?Schema\s*\(/g;
|
|
7132
|
+
let m;
|
|
7133
|
+
while ((m = re.exec(content)) !== null) {
|
|
7134
|
+
const windowText = content.slice(m.index, m.index + 2e3);
|
|
7135
|
+
const opt = /\bcollection\s*:\s*['"`]([\w.-]+)['"`]/.exec(windowText);
|
|
7136
|
+
if (opt) out.set(m[1], opt[1]);
|
|
7137
|
+
}
|
|
7138
|
+
return out;
|
|
7139
|
+
}
|
|
7140
|
+
function resolveModel(modelName, rest, schemaColl, pluralizeOn) {
|
|
7141
|
+
const trimmed = rest.trim();
|
|
7142
|
+
const literalThird = /,\s*['"`]([\w.-]+)['"`]\s*$/.exec(trimmed);
|
|
7143
|
+
if (literalThird) return { name: literalThird[1], kind: "mongodb-collection" };
|
|
7144
|
+
const firstIdent = /^([A-Za-z_$][\w$]*)/.exec(trimmed)?.[1];
|
|
7145
|
+
if (firstIdent && schemaColl.has(firstIdent)) {
|
|
7146
|
+
return { name: schemaColl.get(firstIdent), kind: "mongodb-collection" };
|
|
7147
|
+
}
|
|
7148
|
+
if (/,\s*[A-Za-z_$][\w$]*\s*$/.test(trimmed)) {
|
|
7149
|
+
return { name: modelName, kind: "mongodb-model" };
|
|
7150
|
+
}
|
|
7151
|
+
return { name: pluralizeOn ? pluralizeCollection(modelName) : modelName, kind: "mongodb-collection" };
|
|
7152
|
+
}
|
|
7153
|
+
function collectModelDefs(content, pluralizeOn) {
|
|
7154
|
+
const schemaColl = schemaCollectionVars(content);
|
|
7155
|
+
const out = [];
|
|
7156
|
+
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;
|
|
7157
|
+
let m;
|
|
7158
|
+
while ((m = re.exec(content)) !== null) {
|
|
7159
|
+
out.push({
|
|
7160
|
+
modelName: m[2],
|
|
7161
|
+
varName: m[1] ?? null,
|
|
7162
|
+
resolved: resolveModel(m[2], m[3] ?? "", schemaColl, pluralizeOn),
|
|
7163
|
+
matchText: m[0]
|
|
7164
|
+
});
|
|
7165
|
+
}
|
|
7166
|
+
return out;
|
|
7167
|
+
}
|
|
7168
|
+
function endpoint(r, file, serviceDir, matchText) {
|
|
7169
|
+
const line = lineOf(file.content, matchText);
|
|
7170
|
+
return {
|
|
7171
|
+
infraId: (0, import_types21.infraId)(r.kind, r.name),
|
|
7172
|
+
name: r.name,
|
|
7173
|
+
kind: r.kind,
|
|
7174
|
+
edgeType: "CALLS",
|
|
7175
|
+
confidenceKind: "verified-call-site",
|
|
7176
|
+
evidence: { file: import_node_path33.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
|
|
7177
|
+
};
|
|
7178
|
+
}
|
|
7179
|
+
function mongooseEndpointsFromFile(file, serviceDir) {
|
|
7180
|
+
const hasMongoose = MONGOOSE_IMPORT_RE.test(file.content);
|
|
7181
|
+
const hasMongodb = MONGODB_IMPORT_RE.test(file.content);
|
|
7182
|
+
if (!hasMongoose && !hasMongodb) return [];
|
|
7183
|
+
const content = file.content;
|
|
7184
|
+
const out = [];
|
|
7185
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7186
|
+
const push = (r, matchText) => {
|
|
7187
|
+
const key = `${r.kind}/${r.name}`;
|
|
7188
|
+
if (seen.has(key)) return;
|
|
7189
|
+
seen.add(key);
|
|
7190
|
+
out.push(endpoint(r, file, serviceDir, matchText));
|
|
7191
|
+
};
|
|
7192
|
+
if (hasMongoose) {
|
|
7193
|
+
const pluralizeOn = !PLURALIZE_DISABLED_RE.test(content);
|
|
7194
|
+
for (const def of collectModelDefs(content, pluralizeOn)) push(def.resolved, def.matchText);
|
|
7195
|
+
}
|
|
7196
|
+
const collRe = /\.\s*collection\s*\(\s*['"`]([\w.$-]+)['"`]\s*\)/g;
|
|
7197
|
+
let c;
|
|
7198
|
+
while ((c = collRe.exec(content)) !== null) {
|
|
7199
|
+
push({ name: c[1], kind: "mongodb-collection" }, c[0]);
|
|
7200
|
+
}
|
|
7201
|
+
return out;
|
|
7202
|
+
}
|
|
7203
|
+
function parseDestructure(inner, specifier, out) {
|
|
7204
|
+
for (const raw of inner.split(",")) {
|
|
7205
|
+
const part = raw.trim();
|
|
7206
|
+
if (!part) continue;
|
|
7207
|
+
const asM = /^(\w+)\s+as\s+(\w+)$/.exec(part);
|
|
7208
|
+
const colonM = /^(\w+)\s*:\s*(\w+)$/.exec(part);
|
|
7209
|
+
if (asM) out.push({ local: asM[2], kind: "named", exportName: asM[1], specifier });
|
|
7210
|
+
else if (colonM) out.push({ local: colonM[2], kind: "named", exportName: colonM[1], specifier });
|
|
7211
|
+
else {
|
|
7212
|
+
const id = /^(\w+)$/.exec(part);
|
|
7213
|
+
if (id) out.push({ local: id[1], kind: "named", exportName: id[1], specifier });
|
|
7214
|
+
}
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
function parseImportBindings(content) {
|
|
7218
|
+
const out = [];
|
|
7219
|
+
const esm = /import\s+([^;'"`\n]+?)\s+from\s*['"`]([^'"`]+)['"`]/g;
|
|
7220
|
+
let m;
|
|
7221
|
+
while ((m = esm.exec(content)) !== null) {
|
|
7222
|
+
const clause = m[1].trim();
|
|
7223
|
+
const spec = m[2];
|
|
7224
|
+
const ns = /^\*\s+as\s+(\w+)$/.exec(clause);
|
|
7225
|
+
if (ns) {
|
|
7226
|
+
out.push({ local: ns[1], kind: "namespace", specifier: spec });
|
|
7227
|
+
continue;
|
|
7228
|
+
}
|
|
7229
|
+
const named = /\{([^}]*)\}/.exec(clause);
|
|
7230
|
+
if (named) parseDestructure(named[1], spec, out);
|
|
7231
|
+
const def = /^(\w+)\s*(?:,|$)/.exec(clause);
|
|
7232
|
+
if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
|
|
7233
|
+
}
|
|
7234
|
+
const cjs = /(?:const|let|var)\s+(\{[^}]*\}|\w+)\s*=\s*require\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
7235
|
+
while ((m = cjs.exec(content)) !== null) {
|
|
7236
|
+
const lhs = m[1];
|
|
7237
|
+
const spec = m[2];
|
|
7238
|
+
if (lhs.startsWith("{")) parseDestructure(lhs.slice(1, -1), spec, out);
|
|
7239
|
+
else out.push({ local: lhs, kind: "whole", specifier: spec });
|
|
7240
|
+
}
|
|
7241
|
+
return out;
|
|
7242
|
+
}
|
|
7243
|
+
function fileExportsOf(content, pluralizeOn) {
|
|
7244
|
+
const defs = collectModelDefs(content, pluralizeOn).filter((d) => d.resolved.kind === "mongodb-collection");
|
|
7245
|
+
if (defs.length === 0) return null;
|
|
7246
|
+
const byVar = /* @__PURE__ */ new Map();
|
|
7247
|
+
for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
|
|
7248
|
+
const byName = new Map(byVar);
|
|
7249
|
+
let def;
|
|
7250
|
+
const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
|
|
7251
|
+
if (named && byVar.has(named[1])) def = byVar.get(named[1]);
|
|
7252
|
+
if (!def) {
|
|
7253
|
+
const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
|
|
7254
|
+
if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
|
|
7255
|
+
}
|
|
7256
|
+
if (!def && byVar.size === 1) def = [...byVar.values()][0];
|
|
7257
|
+
return { byName, ...def ? { default: def } : {} };
|
|
7258
|
+
}
|
|
7259
|
+
async function mongooseCrossFileEndpoints(files, serviceDir) {
|
|
7260
|
+
const mongooseFiles = files.filter((f) => MONGOOSE_IMPORT_RE.test(f.content));
|
|
7261
|
+
if (mongooseFiles.length === 0) return [];
|
|
7262
|
+
const pluralizeOn = !files.some((f) => PLURALIZE_DISABLED_RE.test(f.content));
|
|
7263
|
+
const registry = /* @__PURE__ */ new Map();
|
|
7264
|
+
for (const f of mongooseFiles) {
|
|
7265
|
+
const fx = fileExportsOf(f.content, pluralizeOn);
|
|
7266
|
+
if (fx) registry.set(toPosix2(import_node_path33.default.relative(serviceDir, f.path)), fx);
|
|
7267
|
+
}
|
|
7268
|
+
if (registry.size === 0) return [];
|
|
7269
|
+
const out = [];
|
|
7270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7271
|
+
for (const f of files) {
|
|
7272
|
+
const bindings = parseImportBindings(f.content);
|
|
7273
|
+
if (bindings.length === 0) continue;
|
|
7274
|
+
const directColl = /* @__PURE__ */ new Map();
|
|
7275
|
+
const nsExports = /* @__PURE__ */ new Map();
|
|
7276
|
+
for (const b of bindings) {
|
|
7277
|
+
const resolvedRel = await resolveJsImport(b.specifier, import_node_path33.default.dirname(f.path), serviceDir, null);
|
|
7278
|
+
if (!resolvedRel) continue;
|
|
7279
|
+
const fx = registry.get(resolvedRel);
|
|
7280
|
+
if (!fx) continue;
|
|
7281
|
+
if (b.kind === "named" && b.exportName) {
|
|
7282
|
+
const coll = fx.byName.get(b.exportName);
|
|
7283
|
+
if (coll) directColl.set(b.local, coll);
|
|
7284
|
+
} else if (b.kind === "default") {
|
|
7285
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
7286
|
+
} else {
|
|
7287
|
+
if (fx.default) directColl.set(b.local, fx.default);
|
|
7288
|
+
nsExports.set(b.local, fx);
|
|
7289
|
+
}
|
|
7290
|
+
}
|
|
7291
|
+
if (directColl.size === 0 && nsExports.size === 0) continue;
|
|
7292
|
+
const localDefs = new Set(
|
|
7293
|
+
collectModelDefs(f.content, pluralizeOn).map((d) => d.varName).filter((v) => v !== null)
|
|
7294
|
+
);
|
|
7295
|
+
const emit = (collection, matchText) => {
|
|
7296
|
+
const key = `${f.path}::${collection}`;
|
|
7297
|
+
if (seen.has(key)) return;
|
|
7298
|
+
seen.add(key);
|
|
7299
|
+
out.push(endpoint({ name: collection, kind: "mongodb-collection" }, f, serviceDir, matchText));
|
|
7300
|
+
};
|
|
7301
|
+
for (const [local, collection] of directColl) {
|
|
7302
|
+
if (localDefs.has(local)) continue;
|
|
7303
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
7304
|
+
let qm;
|
|
7305
|
+
while ((qm = qre.exec(f.content)) !== null) emit(collection, qm[0]);
|
|
7306
|
+
}
|
|
7307
|
+
for (const [local, fx] of nsExports) {
|
|
7308
|
+
const qre = new RegExp(`\\b${local}\\s*\\.\\s*(\\w+)\\s*\\.\\s*(?:${MODEL_METHODS_ALT})\\s*\\(`, "g");
|
|
7309
|
+
let qm;
|
|
7310
|
+
while ((qm = qre.exec(f.content)) !== null) {
|
|
7311
|
+
const coll = fx.byName.get(qm[1]);
|
|
7312
|
+
if (coll) emit(coll, qm[0]);
|
|
7313
|
+
}
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
return out;
|
|
7317
|
+
}
|
|
7318
|
+
|
|
6997
7319
|
// src/extract/calls/index.ts
|
|
6998
7320
|
function edgeTypeFromEndpoint(ep) {
|
|
6999
7321
|
switch (ep.edgeType) {
|
|
7000
7322
|
case "PUBLISHES_TO":
|
|
7001
|
-
return
|
|
7323
|
+
return import_types22.EdgeType.PUBLISHES_TO;
|
|
7002
7324
|
case "CONSUMES_FROM":
|
|
7003
|
-
return
|
|
7325
|
+
return import_types22.EdgeType.CONSUMES_FROM;
|
|
7004
7326
|
default:
|
|
7005
|
-
return
|
|
7327
|
+
return import_types22.EdgeType.CALLS;
|
|
7006
7328
|
}
|
|
7007
7329
|
}
|
|
7008
7330
|
function isAwsKind(kind) {
|
|
@@ -7014,23 +7336,27 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
7014
7336
|
for (const service of services) {
|
|
7015
7337
|
const files = await loadSourceFiles(service.dir);
|
|
7016
7338
|
const endpoints = [];
|
|
7339
|
+
const maskedFiles = [];
|
|
7017
7340
|
for (const file of files) {
|
|
7018
7341
|
if (isTestPath(file.path)) continue;
|
|
7019
7342
|
const masked = maskCommentsInSource(file.content);
|
|
7020
7343
|
const maskedFile = { path: file.path, content: masked };
|
|
7344
|
+
maskedFiles.push(maskedFile);
|
|
7021
7345
|
endpoints.push(...kafkaEndpointsFromFile(maskedFile, service.dir));
|
|
7022
7346
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
7023
7347
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
7024
7348
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
7025
7349
|
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
7350
|
+
endpoints.push(...mongooseEndpointsFromFile(maskedFile, service.dir));
|
|
7026
7351
|
}
|
|
7352
|
+
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
7027
7353
|
if (endpoints.length === 0) continue;
|
|
7028
7354
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
7029
7355
|
for (const ep of endpoints) {
|
|
7030
7356
|
if (!graph.hasNode(ep.infraId)) {
|
|
7031
7357
|
const node = {
|
|
7032
7358
|
id: ep.infraId,
|
|
7033
|
-
type:
|
|
7359
|
+
type: import_types22.NodeType.InfraNode,
|
|
7034
7360
|
name: ep.name,
|
|
7035
7361
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
7036
7362
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -7042,7 +7368,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
7042
7368
|
nodesAdded++;
|
|
7043
7369
|
}
|
|
7044
7370
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
7045
|
-
const confidence = (0,
|
|
7371
|
+
const confidence = (0, import_types22.confidenceForExtracted)(ep.confidenceKind);
|
|
7046
7372
|
const relFile = toPosix2(ep.evidence.file);
|
|
7047
7373
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
7048
7374
|
graph,
|
|
@@ -7052,7 +7378,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
7052
7378
|
);
|
|
7053
7379
|
nodesAdded += n;
|
|
7054
7380
|
edgesAdded += e;
|
|
7055
|
-
if (!(0,
|
|
7381
|
+
if (!(0, import_types22.passesExtractedFloor)(confidence)) {
|
|
7056
7382
|
noteExtractedDropped({
|
|
7057
7383
|
source: fileNodeId,
|
|
7058
7384
|
target: ep.infraId,
|
|
@@ -7072,7 +7398,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
7072
7398
|
source: fileNodeId,
|
|
7073
7399
|
target: ep.infraId,
|
|
7074
7400
|
type: edgeType,
|
|
7075
|
-
provenance:
|
|
7401
|
+
provenance: import_types22.Provenance.EXTRACTED,
|
|
7076
7402
|
confidence,
|
|
7077
7403
|
evidence: ep.evidence
|
|
7078
7404
|
};
|
|
@@ -7098,16 +7424,16 @@ init_cjs_shims();
|
|
|
7098
7424
|
|
|
7099
7425
|
// src/extract/infra/docker-compose.ts
|
|
7100
7426
|
init_cjs_shims();
|
|
7101
|
-
var
|
|
7102
|
-
var
|
|
7427
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
7428
|
+
var import_types24 = require("@neat.is/types");
|
|
7103
7429
|
|
|
7104
7430
|
// src/extract/infra/shared.ts
|
|
7105
7431
|
init_cjs_shims();
|
|
7106
|
-
var
|
|
7432
|
+
var import_types23 = require("@neat.is/types");
|
|
7107
7433
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
7108
7434
|
return {
|
|
7109
|
-
id: (0,
|
|
7110
|
-
type:
|
|
7435
|
+
id: (0, import_types23.infraId)(kind, name),
|
|
7436
|
+
type: import_types23.NodeType.InfraNode,
|
|
7111
7437
|
name,
|
|
7112
7438
|
provider,
|
|
7113
7439
|
kind,
|
|
@@ -7151,8 +7477,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
7151
7477
|
source: anchorId,
|
|
7152
7478
|
target: node.id,
|
|
7153
7479
|
type: edgeType,
|
|
7154
|
-
provenance:
|
|
7155
|
-
confidence: (0,
|
|
7480
|
+
provenance: import_types23.Provenance.EXTRACTED,
|
|
7481
|
+
confidence: (0, import_types23.confidenceForExtracted)("structural"),
|
|
7156
7482
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7157
7483
|
};
|
|
7158
7484
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7169,7 +7495,7 @@ function dependsOnList(value) {
|
|
|
7169
7495
|
}
|
|
7170
7496
|
function serviceNameToServiceNode(name, services) {
|
|
7171
7497
|
for (const s of services) {
|
|
7172
|
-
if (s.node.name === name ||
|
|
7498
|
+
if (s.node.name === name || import_node_path34.default.basename(s.dir) === name) return s.node.id;
|
|
7173
7499
|
}
|
|
7174
7500
|
return null;
|
|
7175
7501
|
}
|
|
@@ -7178,7 +7504,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7178
7504
|
let edgesAdded = 0;
|
|
7179
7505
|
let composePath = null;
|
|
7180
7506
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
7181
|
-
const abs =
|
|
7507
|
+
const abs = import_node_path34.default.join(scanPath, name);
|
|
7182
7508
|
if (await exists2(abs)) {
|
|
7183
7509
|
composePath = abs;
|
|
7184
7510
|
break;
|
|
@@ -7191,13 +7517,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7191
7517
|
} catch (err) {
|
|
7192
7518
|
recordExtractionError(
|
|
7193
7519
|
"infra docker-compose",
|
|
7194
|
-
|
|
7520
|
+
import_node_path34.default.relative(scanPath, composePath),
|
|
7195
7521
|
err
|
|
7196
7522
|
);
|
|
7197
7523
|
return { nodesAdded, edgesAdded };
|
|
7198
7524
|
}
|
|
7199
7525
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
7200
|
-
const evidenceFile =
|
|
7526
|
+
const evidenceFile = import_node_path34.default.relative(scanPath, composePath).split(import_node_path34.default.sep).join("/");
|
|
7201
7527
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
7202
7528
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
7203
7529
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -7219,15 +7545,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7219
7545
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
7220
7546
|
const targetId = composeNameToNodeId.get(dep);
|
|
7221
7547
|
if (!targetId) continue;
|
|
7222
|
-
const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId,
|
|
7548
|
+
const edgeId = (0, import_types5.extractedEdgeId)(sourceId, targetId, import_types24.EdgeType.DEPENDS_ON);
|
|
7223
7549
|
if (graph.hasEdge(edgeId)) continue;
|
|
7224
7550
|
const edge = {
|
|
7225
7551
|
id: edgeId,
|
|
7226
7552
|
source: sourceId,
|
|
7227
7553
|
target: targetId,
|
|
7228
|
-
type:
|
|
7229
|
-
provenance:
|
|
7230
|
-
confidence: (0,
|
|
7554
|
+
type: import_types24.EdgeType.DEPENDS_ON,
|
|
7555
|
+
provenance: import_types24.Provenance.EXTRACTED,
|
|
7556
|
+
confidence: (0, import_types24.confidenceForExtracted)("structural"),
|
|
7231
7557
|
evidence: { file: evidenceFile }
|
|
7232
7558
|
};
|
|
7233
7559
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7239,9 +7565,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7239
7565
|
|
|
7240
7566
|
// src/extract/infra/dockerfile.ts
|
|
7241
7567
|
init_cjs_shims();
|
|
7242
|
-
var
|
|
7568
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
7243
7569
|
var import_node_fs18 = require("fs");
|
|
7244
|
-
var
|
|
7570
|
+
var import_types25 = require("@neat.is/types");
|
|
7245
7571
|
function readDockerfile(content) {
|
|
7246
7572
|
let image = null;
|
|
7247
7573
|
const ports = [];
|
|
@@ -7270,7 +7596,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7270
7596
|
let nodesAdded = 0;
|
|
7271
7597
|
let edgesAdded = 0;
|
|
7272
7598
|
for (const service of services) {
|
|
7273
|
-
const dockerfilePath =
|
|
7599
|
+
const dockerfilePath = import_node_path35.default.join(service.dir, "Dockerfile");
|
|
7274
7600
|
if (!await exists2(dockerfilePath)) continue;
|
|
7275
7601
|
let content;
|
|
7276
7602
|
try {
|
|
@@ -7278,7 +7604,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7278
7604
|
} catch (err) {
|
|
7279
7605
|
recordExtractionError(
|
|
7280
7606
|
"infra dockerfile",
|
|
7281
|
-
|
|
7607
|
+
import_node_path35.default.relative(scanPath, dockerfilePath),
|
|
7282
7608
|
err
|
|
7283
7609
|
);
|
|
7284
7610
|
continue;
|
|
@@ -7290,8 +7616,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7290
7616
|
graph.addNode(node.id, node);
|
|
7291
7617
|
nodesAdded++;
|
|
7292
7618
|
}
|
|
7293
|
-
const relDockerfile = toPosix2(
|
|
7294
|
-
const evidenceFile = toPosix2(
|
|
7619
|
+
const relDockerfile = toPosix2(import_node_path35.default.relative(service.dir, dockerfilePath));
|
|
7620
|
+
const evidenceFile = toPosix2(import_node_path35.default.relative(scanPath, dockerfilePath));
|
|
7295
7621
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7296
7622
|
graph,
|
|
7297
7623
|
service.pkg.name,
|
|
@@ -7300,15 +7626,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7300
7626
|
);
|
|
7301
7627
|
nodesAdded += fn;
|
|
7302
7628
|
edgesAdded += fe;
|
|
7303
|
-
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id,
|
|
7629
|
+
const edgeId = (0, import_types5.extractedEdgeId)(fileNodeId, node.id, import_types25.EdgeType.RUNS_ON);
|
|
7304
7630
|
if (!graph.hasEdge(edgeId)) {
|
|
7305
7631
|
const edge = {
|
|
7306
7632
|
id: edgeId,
|
|
7307
7633
|
source: fileNodeId,
|
|
7308
7634
|
target: node.id,
|
|
7309
|
-
type:
|
|
7310
|
-
provenance:
|
|
7311
|
-
confidence: (0,
|
|
7635
|
+
type: import_types25.EdgeType.RUNS_ON,
|
|
7636
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7637
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7312
7638
|
evidence: {
|
|
7313
7639
|
file: evidenceFile,
|
|
7314
7640
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -7323,15 +7649,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7323
7649
|
graph.addNode(portNode.id, portNode);
|
|
7324
7650
|
nodesAdded++;
|
|
7325
7651
|
}
|
|
7326
|
-
const portEdgeId = (0, import_types5.extractedEdgeId)(fileNodeId, portNode.id,
|
|
7652
|
+
const portEdgeId = (0, import_types5.extractedEdgeId)(fileNodeId, portNode.id, import_types25.EdgeType.CONNECTS_TO);
|
|
7327
7653
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
7328
7654
|
const portEdge = {
|
|
7329
7655
|
id: portEdgeId,
|
|
7330
7656
|
source: fileNodeId,
|
|
7331
7657
|
target: portNode.id,
|
|
7332
|
-
type:
|
|
7333
|
-
provenance:
|
|
7334
|
-
confidence: (0,
|
|
7658
|
+
type: import_types25.EdgeType.CONNECTS_TO,
|
|
7659
|
+
provenance: import_types25.Provenance.EXTRACTED,
|
|
7660
|
+
confidence: (0, import_types25.confidenceForExtracted)("structural"),
|
|
7335
7661
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
7336
7662
|
};
|
|
7337
7663
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -7344,8 +7670,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
7344
7670
|
// src/extract/infra/terraform.ts
|
|
7345
7671
|
init_cjs_shims();
|
|
7346
7672
|
var import_node_fs19 = require("fs");
|
|
7347
|
-
var
|
|
7348
|
-
var
|
|
7673
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
7674
|
+
var import_types26 = require("@neat.is/types");
|
|
7349
7675
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
7350
7676
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
7351
7677
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -7355,11 +7681,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
7355
7681
|
for (const entry of entries) {
|
|
7356
7682
|
if (entry.isDirectory()) {
|
|
7357
7683
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
7358
|
-
const child =
|
|
7684
|
+
const child = import_node_path36.default.join(start, entry.name);
|
|
7359
7685
|
if (await isPythonVenvDir(child)) continue;
|
|
7360
7686
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
7361
7687
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
7362
|
-
out.push(
|
|
7688
|
+
out.push(import_node_path36.default.join(start, entry.name));
|
|
7363
7689
|
}
|
|
7364
7690
|
}
|
|
7365
7691
|
return out;
|
|
@@ -7391,7 +7717,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7391
7717
|
const files = await walkTfFiles(scanPath);
|
|
7392
7718
|
for (const file of files) {
|
|
7393
7719
|
const content = await import_node_fs19.promises.readFile(file, "utf8");
|
|
7394
|
-
const evidenceFile = toPosix2(
|
|
7720
|
+
const evidenceFile = toPosix2(import_node_path36.default.relative(scanPath, file));
|
|
7395
7721
|
const resources = [];
|
|
7396
7722
|
const byKey = /* @__PURE__ */ new Map();
|
|
7397
7723
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -7426,16 +7752,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7426
7752
|
if (!target) continue;
|
|
7427
7753
|
if (seen.has(target.nodeId)) continue;
|
|
7428
7754
|
seen.add(target.nodeId);
|
|
7429
|
-
const edgeId = (0, import_types5.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
7755
|
+
const edgeId = (0, import_types5.extractedEdgeId)(resource.nodeId, target.nodeId, import_types26.EdgeType.DEPENDS_ON);
|
|
7430
7756
|
if (graph.hasEdge(edgeId)) continue;
|
|
7431
7757
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
7432
7758
|
const edge = {
|
|
7433
7759
|
id: edgeId,
|
|
7434
7760
|
source: resource.nodeId,
|
|
7435
7761
|
target: target.nodeId,
|
|
7436
|
-
type:
|
|
7437
|
-
provenance:
|
|
7438
|
-
confidence: (0,
|
|
7762
|
+
type: import_types26.EdgeType.DEPENDS_ON,
|
|
7763
|
+
provenance: import_types26.Provenance.EXTRACTED,
|
|
7764
|
+
confidence: (0, import_types26.confidenceForExtracted)("structural"),
|
|
7439
7765
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
7440
7766
|
};
|
|
7441
7767
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7449,7 +7775,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
7449
7775
|
// src/extract/infra/k8s.ts
|
|
7450
7776
|
init_cjs_shims();
|
|
7451
7777
|
var import_node_fs20 = require("fs");
|
|
7452
|
-
var
|
|
7778
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
7453
7779
|
var import_yaml3 = require("yaml");
|
|
7454
7780
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
7455
7781
|
Service: "k8s-service",
|
|
@@ -7467,11 +7793,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
7467
7793
|
for (const entry of entries) {
|
|
7468
7794
|
if (entry.isDirectory()) {
|
|
7469
7795
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7470
|
-
const child =
|
|
7796
|
+
const child = import_node_path37.default.join(start, entry.name);
|
|
7471
7797
|
if (await isPythonVenvDir(child)) continue;
|
|
7472
7798
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
7473
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
7474
|
-
out.push(
|
|
7799
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path37.default.extname(entry.name))) {
|
|
7800
|
+
out.push(import_node_path37.default.join(start, entry.name));
|
|
7475
7801
|
}
|
|
7476
7802
|
}
|
|
7477
7803
|
return out;
|
|
@@ -7505,13 +7831,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
7505
7831
|
// src/extract/infra/cloudflare.ts
|
|
7506
7832
|
init_cjs_shims();
|
|
7507
7833
|
var import_node_fs21 = require("fs");
|
|
7508
|
-
var
|
|
7834
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
7509
7835
|
var import_smol_toml2 = require("smol-toml");
|
|
7510
|
-
var
|
|
7836
|
+
var import_types27 = require("@neat.is/types");
|
|
7511
7837
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
7512
7838
|
async function readWranglerConfig(dir) {
|
|
7513
7839
|
for (const filename of WRANGLER_FILENAMES) {
|
|
7514
|
-
const abs =
|
|
7840
|
+
const abs = import_node_path38.default.join(dir, filename);
|
|
7515
7841
|
if (!await exists2(abs)) continue;
|
|
7516
7842
|
const raw = await import_node_fs21.promises.readFile(abs, "utf8");
|
|
7517
7843
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7555,8 +7881,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
7555
7881
|
source: anchorId,
|
|
7556
7882
|
target: node.id,
|
|
7557
7883
|
type: edgeType,
|
|
7558
|
-
provenance:
|
|
7559
|
-
confidence: (0,
|
|
7884
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7885
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7560
7886
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
7561
7887
|
};
|
|
7562
7888
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7574,11 +7900,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7574
7900
|
try {
|
|
7575
7901
|
read = await readWranglerConfig(service.dir);
|
|
7576
7902
|
} catch (err) {
|
|
7577
|
-
recordExtractionError("infra cloudflare",
|
|
7903
|
+
recordExtractionError("infra cloudflare", import_node_path38.default.relative(scanPath, service.dir), err);
|
|
7578
7904
|
continue;
|
|
7579
7905
|
}
|
|
7580
7906
|
if (!read || !read.config.name) continue;
|
|
7581
|
-
const evidenceFile = toPosix2(
|
|
7907
|
+
const evidenceFile = toPosix2(import_node_path38.default.relative(scanPath, import_node_path38.default.join(service.dir, read.relFile)));
|
|
7582
7908
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
7583
7909
|
}
|
|
7584
7910
|
for (const worker of discovered) {
|
|
@@ -7590,7 +7916,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7590
7916
|
}
|
|
7591
7917
|
let anchorId = service.node.id;
|
|
7592
7918
|
if (config.main) {
|
|
7593
|
-
const entryRelPath = toPosix2(
|
|
7919
|
+
const entryRelPath = toPosix2(import_node_path38.default.normalize(config.main));
|
|
7594
7920
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
7595
7921
|
graph,
|
|
7596
7922
|
service.pkg.name,
|
|
@@ -7617,15 +7943,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7617
7943
|
nodesAdded++;
|
|
7618
7944
|
}
|
|
7619
7945
|
if (runtimeNode.id !== anchorId) {
|
|
7620
|
-
const runsOnId = (0, import_types5.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
7946
|
+
const runsOnId = (0, import_types5.extractedEdgeId)(anchorId, runtimeNode.id, import_types27.EdgeType.RUNS_ON);
|
|
7621
7947
|
if (!graph.hasEdge(runsOnId)) {
|
|
7622
7948
|
const edge = {
|
|
7623
7949
|
id: runsOnId,
|
|
7624
7950
|
source: anchorId,
|
|
7625
7951
|
target: runtimeNode.id,
|
|
7626
|
-
type:
|
|
7627
|
-
provenance:
|
|
7628
|
-
confidence: (0,
|
|
7952
|
+
type: import_types27.EdgeType.RUNS_ON,
|
|
7953
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
7954
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7629
7955
|
evidence: {
|
|
7630
7956
|
file: evidenceFile,
|
|
7631
7957
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -7639,7 +7965,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7639
7965
|
const result = addResourceEdge(
|
|
7640
7966
|
graph,
|
|
7641
7967
|
anchorId,
|
|
7642
|
-
|
|
7968
|
+
import_types27.EdgeType.CONNECTS_TO,
|
|
7643
7969
|
"cloudflare-route",
|
|
7644
7970
|
route,
|
|
7645
7971
|
evidenceFile,
|
|
@@ -7663,7 +7989,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7663
7989
|
const result = addResourceEdge(
|
|
7664
7990
|
graph,
|
|
7665
7991
|
anchorId,
|
|
7666
|
-
|
|
7992
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7667
7993
|
group.kind,
|
|
7668
7994
|
name,
|
|
7669
7995
|
evidenceFile,
|
|
@@ -7677,7 +8003,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7677
8003
|
const result = addResourceEdge(
|
|
7678
8004
|
graph,
|
|
7679
8005
|
anchorId,
|
|
7680
|
-
|
|
8006
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7681
8007
|
"cloudflare-cron",
|
|
7682
8008
|
cron,
|
|
7683
8009
|
evidenceFile,
|
|
@@ -7690,7 +8016,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7690
8016
|
const result = addResourceEdge(
|
|
7691
8017
|
graph,
|
|
7692
8018
|
anchorId,
|
|
7693
|
-
|
|
8019
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7694
8020
|
"cloudflare-env-var",
|
|
7695
8021
|
varName,
|
|
7696
8022
|
evidenceFile,
|
|
@@ -7703,15 +8029,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7703
8029
|
if (!svc.service) continue;
|
|
7704
8030
|
const target = workerIndex.get(svc.service);
|
|
7705
8031
|
if (target && target.anchorId !== anchorId) {
|
|
7706
|
-
const edgeId = (0, import_types5.extractedEdgeId)(anchorId, target.anchorId,
|
|
8032
|
+
const edgeId = (0, import_types5.extractedEdgeId)(anchorId, target.anchorId, import_types27.EdgeType.CALLS);
|
|
7707
8033
|
if (!graph.hasEdge(edgeId)) {
|
|
7708
8034
|
const edge = {
|
|
7709
8035
|
id: edgeId,
|
|
7710
8036
|
source: anchorId,
|
|
7711
8037
|
target: target.anchorId,
|
|
7712
|
-
type:
|
|
7713
|
-
provenance:
|
|
7714
|
-
confidence: (0,
|
|
8038
|
+
type: import_types27.EdgeType.CALLS,
|
|
8039
|
+
provenance: import_types27.Provenance.EXTRACTED,
|
|
8040
|
+
confidence: (0, import_types27.confidenceForExtracted)("structural"),
|
|
7715
8041
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
7716
8042
|
};
|
|
7717
8043
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -7722,7 +8048,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7722
8048
|
const result = addResourceEdge(
|
|
7723
8049
|
graph,
|
|
7724
8050
|
anchorId,
|
|
7725
|
-
|
|
8051
|
+
import_types27.EdgeType.DEPENDS_ON,
|
|
7726
8052
|
"cloudflare-service-binding",
|
|
7727
8053
|
svc.service,
|
|
7728
8054
|
evidenceFile,
|
|
@@ -7738,12 +8064,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
7738
8064
|
// src/extract/infra/vercel.ts
|
|
7739
8065
|
init_cjs_shims();
|
|
7740
8066
|
var import_node_fs22 = require("fs");
|
|
7741
|
-
var
|
|
7742
|
-
var
|
|
8067
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
8068
|
+
var import_types28 = require("@neat.is/types");
|
|
7743
8069
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
7744
8070
|
async function readVercelConfig(dir) {
|
|
7745
8071
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
7746
|
-
const abs =
|
|
8072
|
+
const abs = import_node_path39.default.join(dir, filename);
|
|
7747
8073
|
if (!await exists2(abs)) continue;
|
|
7748
8074
|
const raw = await import_node_fs22.promises.readFile(abs, "utf8");
|
|
7749
8075
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7752,7 +8078,7 @@ async function readVercelConfig(dir) {
|
|
|
7752
8078
|
return null;
|
|
7753
8079
|
}
|
|
7754
8080
|
async function readLinkedProjectName(dir) {
|
|
7755
|
-
const abs =
|
|
8081
|
+
const abs = import_node_path39.default.join(dir, ".vercel", "project.json");
|
|
7756
8082
|
if (!await exists2(abs)) return void 0;
|
|
7757
8083
|
const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
|
|
7758
8084
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -7770,7 +8096,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7770
8096
|
read = await readVercelConfig(service.dir);
|
|
7771
8097
|
projectName = await readLinkedProjectName(service.dir);
|
|
7772
8098
|
} catch (err) {
|
|
7773
|
-
recordExtractionError("infra vercel",
|
|
8099
|
+
recordExtractionError("infra vercel", import_node_path39.default.relative(scanPath, service.dir), err);
|
|
7774
8100
|
continue;
|
|
7775
8101
|
}
|
|
7776
8102
|
if (!read && !projectName) continue;
|
|
@@ -7786,7 +8112,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7786
8112
|
const anchorId = service.node.id;
|
|
7787
8113
|
if (!read) continue;
|
|
7788
8114
|
const { config, relFile, raw } = read;
|
|
7789
|
-
const evidenceFile = toPosix2(
|
|
8115
|
+
const evidenceFile = toPosix2(import_node_path39.default.relative(scanPath, import_node_path39.default.join(service.dir, relFile)));
|
|
7790
8116
|
const add = (edgeType, kind, name) => {
|
|
7791
8117
|
if (!name) return;
|
|
7792
8118
|
const result = emitPlatformResourceEdge(
|
|
@@ -7802,12 +8128,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7802
8128
|
nodesAdded += result.nodesAdded;
|
|
7803
8129
|
edgesAdded += result.edgesAdded;
|
|
7804
8130
|
};
|
|
7805
|
-
add(
|
|
7806
|
-
for (const cron of config.crons ?? []) add(
|
|
7807
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
7808
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
8131
|
+
add(import_types28.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
8132
|
+
for (const cron of config.crons ?? []) add(import_types28.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
8133
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
8134
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types28.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
7809
8135
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
7810
|
-
add(
|
|
8136
|
+
add(import_types28.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
7811
8137
|
}
|
|
7812
8138
|
}
|
|
7813
8139
|
return { nodesAdded, edgesAdded };
|
|
@@ -7816,13 +8142,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
7816
8142
|
// src/extract/infra/railway.ts
|
|
7817
8143
|
init_cjs_shims();
|
|
7818
8144
|
var import_node_fs23 = require("fs");
|
|
7819
|
-
var
|
|
8145
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
7820
8146
|
var import_smol_toml3 = require("smol-toml");
|
|
7821
|
-
var
|
|
8147
|
+
var import_types29 = require("@neat.is/types");
|
|
7822
8148
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
7823
8149
|
async function readRailwayConfig(dir) {
|
|
7824
8150
|
for (const filename of RAILWAY_FILENAMES) {
|
|
7825
|
-
const abs =
|
|
8151
|
+
const abs = import_node_path40.default.join(dir, filename);
|
|
7826
8152
|
if (!await exists2(abs)) continue;
|
|
7827
8153
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
7828
8154
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -7838,7 +8164,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7838
8164
|
try {
|
|
7839
8165
|
read = await readRailwayConfig(service.dir);
|
|
7840
8166
|
} catch (err) {
|
|
7841
|
-
recordExtractionError("infra railway",
|
|
8167
|
+
recordExtractionError("infra railway", import_node_path40.default.relative(scanPath, service.dir), err);
|
|
7842
8168
|
continue;
|
|
7843
8169
|
}
|
|
7844
8170
|
if (!read) continue;
|
|
@@ -7848,7 +8174,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7848
8174
|
}
|
|
7849
8175
|
const anchorId = service.node.id;
|
|
7850
8176
|
const { config, relFile, raw } = read;
|
|
7851
|
-
const evidenceFile = toPosix2(
|
|
8177
|
+
const evidenceFile = toPosix2(import_node_path40.default.relative(scanPath, import_node_path40.default.join(service.dir, relFile)));
|
|
7852
8178
|
const add = (edgeType, kind, name) => {
|
|
7853
8179
|
if (!name) return;
|
|
7854
8180
|
const result = emitPlatformResourceEdge(
|
|
@@ -7864,9 +8190,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7864
8190
|
nodesAdded += result.nodesAdded;
|
|
7865
8191
|
edgesAdded += result.edgesAdded;
|
|
7866
8192
|
};
|
|
7867
|
-
add(
|
|
7868
|
-
add(
|
|
7869
|
-
add(
|
|
8193
|
+
add(import_types29.EdgeType.RUNS_ON, "railway", "railway");
|
|
8194
|
+
add(import_types29.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
8195
|
+
add(import_types29.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
7870
8196
|
}
|
|
7871
8197
|
return { nodesAdded, edgesAdded };
|
|
7872
8198
|
}
|
|
@@ -7874,12 +8200,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
7874
8200
|
// src/extract/infra/supabase.ts
|
|
7875
8201
|
init_cjs_shims();
|
|
7876
8202
|
var import_node_fs24 = require("fs");
|
|
7877
|
-
var
|
|
8203
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
7878
8204
|
var import_smol_toml4 = require("smol-toml");
|
|
7879
|
-
var
|
|
8205
|
+
var import_types30 = require("@neat.is/types");
|
|
7880
8206
|
async function readSupabaseConfig(dir) {
|
|
7881
|
-
const relFile =
|
|
7882
|
-
const abs =
|
|
8207
|
+
const relFile = import_node_path41.default.join("supabase", "config.toml");
|
|
8208
|
+
const abs = import_node_path41.default.join(dir, relFile);
|
|
7883
8209
|
if (!await exists2(abs)) return null;
|
|
7884
8210
|
const raw = await import_node_fs24.promises.readFile(abs, "utf8");
|
|
7885
8211
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -7893,7 +8219,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7893
8219
|
try {
|
|
7894
8220
|
read = await readSupabaseConfig(service.dir);
|
|
7895
8221
|
} catch (err) {
|
|
7896
|
-
recordExtractionError("infra supabase",
|
|
8222
|
+
recordExtractionError("infra supabase", import_node_path41.default.relative(scanPath, service.dir), err);
|
|
7897
8223
|
continue;
|
|
7898
8224
|
}
|
|
7899
8225
|
if (!read) continue;
|
|
@@ -7908,7 +8234,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7908
8234
|
});
|
|
7909
8235
|
}
|
|
7910
8236
|
const anchorId = service.node.id;
|
|
7911
|
-
const evidenceFile = toPosix2(
|
|
8237
|
+
const evidenceFile = toPosix2(import_node_path41.default.relative(scanPath, import_node_path41.default.join(service.dir, relFile)));
|
|
7912
8238
|
const add = (edgeType, kind, name) => {
|
|
7913
8239
|
if (!name) return;
|
|
7914
8240
|
const result = emitPlatformResourceEdge(
|
|
@@ -7924,10 +8250,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
7924
8250
|
nodesAdded += result.nodesAdded;
|
|
7925
8251
|
edgesAdded += result.edgesAdded;
|
|
7926
8252
|
};
|
|
7927
|
-
add(
|
|
7928
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
7929
|
-
if (config.storage) add(
|
|
7930
|
-
if (config.auth) add(
|
|
8253
|
+
add(import_types30.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
8254
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types30.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
8255
|
+
if (config.storage) add(import_types30.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
8256
|
+
if (config.auth) add(import_types30.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
7931
8257
|
}
|
|
7932
8258
|
return { nodesAdded, edgesAdded };
|
|
7933
8259
|
}
|
|
@@ -7949,17 +8275,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
7949
8275
|
}
|
|
7950
8276
|
|
|
7951
8277
|
// src/extract/index.ts
|
|
7952
|
-
var
|
|
8278
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
7953
8279
|
|
|
7954
8280
|
// src/extract/retire.ts
|
|
7955
8281
|
init_cjs_shims();
|
|
7956
8282
|
var import_node_fs25 = require("fs");
|
|
7957
|
-
var
|
|
7958
|
-
var
|
|
8283
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
8284
|
+
var import_types31 = require("@neat.is/types");
|
|
7959
8285
|
function dropOrphanedFileNodes(graph) {
|
|
7960
8286
|
const orphans = [];
|
|
7961
8287
|
graph.forEachNode((id, attrs) => {
|
|
7962
|
-
if (attrs.type !==
|
|
8288
|
+
if (attrs.type !== import_types31.NodeType.FileNode) return;
|
|
7963
8289
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
7964
8290
|
orphans.push(id);
|
|
7965
8291
|
}
|
|
@@ -7972,14 +8298,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
7972
8298
|
const bases = [scanPath, ...serviceDirs];
|
|
7973
8299
|
graph.forEachEdge((id, attrs) => {
|
|
7974
8300
|
const edge = attrs;
|
|
7975
|
-
if (edge.provenance !==
|
|
8301
|
+
if (edge.provenance !== import_types31.Provenance.EXTRACTED) return;
|
|
7976
8302
|
const evidenceFile = edge.evidence?.file;
|
|
7977
8303
|
if (!evidenceFile) return;
|
|
7978
|
-
if (
|
|
8304
|
+
if (import_node_path42.default.isAbsolute(evidenceFile)) {
|
|
7979
8305
|
if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
|
|
7980
8306
|
return;
|
|
7981
8307
|
}
|
|
7982
|
-
const found = bases.some((base) => (0, import_node_fs25.existsSync)(
|
|
8308
|
+
const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path42.default.join(base, evidenceFile)));
|
|
7983
8309
|
if (!found) toDrop.push(id);
|
|
7984
8310
|
});
|
|
7985
8311
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -8021,7 +8347,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
8021
8347
|
}
|
|
8022
8348
|
const droppedEntries = drainDroppedExtracted();
|
|
8023
8349
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
8024
|
-
const rejectedPath =
|
|
8350
|
+
const rejectedPath = import_node_path43.default.join(import_node_path43.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
8025
8351
|
try {
|
|
8026
8352
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
8027
8353
|
} catch (err) {
|
|
@@ -8133,8 +8459,8 @@ function canonicalJson(value) {
|
|
|
8133
8459
|
// src/persist.ts
|
|
8134
8460
|
init_cjs_shims();
|
|
8135
8461
|
var import_node_fs27 = require("fs");
|
|
8136
|
-
var
|
|
8137
|
-
var
|
|
8462
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
8463
|
+
var import_types32 = require("@neat.is/types");
|
|
8138
8464
|
var SCHEMA_VERSION = 4;
|
|
8139
8465
|
function migrateV1ToV2(payload) {
|
|
8140
8466
|
const nodes = payload.graph.nodes;
|
|
@@ -8156,12 +8482,12 @@ function migrateV2ToV3(payload) {
|
|
|
8156
8482
|
for (const edge of edges) {
|
|
8157
8483
|
const attrs = edge.attributes;
|
|
8158
8484
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
8159
|
-
attrs.provenance =
|
|
8485
|
+
attrs.provenance = import_types32.Provenance.OBSERVED;
|
|
8160
8486
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
8161
8487
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
8162
8488
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
8163
8489
|
if (type && source && target) {
|
|
8164
|
-
const newId = (0,
|
|
8490
|
+
const newId = (0, import_types32.observedEdgeId)(source, target, type);
|
|
8165
8491
|
attrs.id = newId;
|
|
8166
8492
|
if (edge.key) edge.key = newId;
|
|
8167
8493
|
}
|
|
@@ -8170,7 +8496,7 @@ function migrateV2ToV3(payload) {
|
|
|
8170
8496
|
return { ...payload, schemaVersion: 3 };
|
|
8171
8497
|
}
|
|
8172
8498
|
async function ensureDir(filePath) {
|
|
8173
|
-
await import_node_fs27.promises.mkdir(
|
|
8499
|
+
await import_node_fs27.promises.mkdir(import_node_path44.default.dirname(filePath), { recursive: true });
|
|
8174
8500
|
}
|
|
8175
8501
|
async function saveGraphToDisk(graph, outPath) {
|
|
8176
8502
|
await ensureDir(outPath);
|
|
@@ -8251,23 +8577,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
8251
8577
|
|
|
8252
8578
|
// src/projects.ts
|
|
8253
8579
|
init_cjs_shims();
|
|
8254
|
-
var
|
|
8580
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
8255
8581
|
function pathsForProject(project, baseDir) {
|
|
8256
8582
|
if (project === DEFAULT_PROJECT) {
|
|
8257
8583
|
return {
|
|
8258
|
-
snapshotPath:
|
|
8259
|
-
errorsPath:
|
|
8260
|
-
staleEventsPath:
|
|
8261
|
-
embeddingsCachePath:
|
|
8262
|
-
policyViolationsPath:
|
|
8584
|
+
snapshotPath: import_node_path45.default.join(baseDir, "graph.json"),
|
|
8585
|
+
errorsPath: import_node_path45.default.join(baseDir, "errors.ndjson"),
|
|
8586
|
+
staleEventsPath: import_node_path45.default.join(baseDir, "stale-events.ndjson"),
|
|
8587
|
+
embeddingsCachePath: import_node_path45.default.join(baseDir, "embeddings.json"),
|
|
8588
|
+
policyViolationsPath: import_node_path45.default.join(baseDir, "policy-violations.ndjson")
|
|
8263
8589
|
};
|
|
8264
8590
|
}
|
|
8265
8591
|
return {
|
|
8266
|
-
snapshotPath:
|
|
8267
|
-
errorsPath:
|
|
8268
|
-
staleEventsPath:
|
|
8269
|
-
embeddingsCachePath:
|
|
8270
|
-
policyViolationsPath:
|
|
8592
|
+
snapshotPath: import_node_path45.default.join(baseDir, `${project}.json`),
|
|
8593
|
+
errorsPath: import_node_path45.default.join(baseDir, `errors.${project}.ndjson`),
|
|
8594
|
+
staleEventsPath: import_node_path45.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
8595
|
+
embeddingsCachePath: import_node_path45.default.join(baseDir, `embeddings.${project}.json`),
|
|
8596
|
+
policyViolationsPath: import_node_path45.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
8271
8597
|
};
|
|
8272
8598
|
}
|
|
8273
8599
|
var Projects = class {
|
|
@@ -8309,15 +8635,15 @@ function parseExtraProjects(raw) {
|
|
|
8309
8635
|
init_cjs_shims();
|
|
8310
8636
|
var import_node_fs28 = require("fs");
|
|
8311
8637
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
8312
|
-
var
|
|
8313
|
-
var
|
|
8638
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
8639
|
+
var import_types33 = require("@neat.is/types");
|
|
8314
8640
|
function neatHome() {
|
|
8315
8641
|
const override = process.env.NEAT_HOME;
|
|
8316
|
-
if (override && override.length > 0) return
|
|
8317
|
-
return
|
|
8642
|
+
if (override && override.length > 0) return import_node_path46.default.resolve(override);
|
|
8643
|
+
return import_node_path46.default.join(import_node_os3.default.homedir(), ".neat");
|
|
8318
8644
|
}
|
|
8319
8645
|
function registryPath() {
|
|
8320
|
-
return
|
|
8646
|
+
return import_node_path46.default.join(neatHome(), "projects.json");
|
|
8321
8647
|
}
|
|
8322
8648
|
async function readRegistry() {
|
|
8323
8649
|
const file = registryPath();
|
|
@@ -8331,7 +8657,7 @@ async function readRegistry() {
|
|
|
8331
8657
|
throw err;
|
|
8332
8658
|
}
|
|
8333
8659
|
const parsed = JSON.parse(raw);
|
|
8334
|
-
return
|
|
8660
|
+
return import_types33.RegistryFileSchema.parse(parsed);
|
|
8335
8661
|
}
|
|
8336
8662
|
async function getProject(name) {
|
|
8337
8663
|
const reg = await readRegistry();
|
|
@@ -8406,16 +8732,16 @@ init_auth();
|
|
|
8406
8732
|
// src/connectors-config.ts
|
|
8407
8733
|
init_cjs_shims();
|
|
8408
8734
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
8409
|
-
var
|
|
8735
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
8410
8736
|
var import_node_fs29 = require("fs");
|
|
8411
8737
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
8412
8738
|
function neatHome2() {
|
|
8413
8739
|
const override = process.env.NEAT_HOME;
|
|
8414
|
-
if (override && override.length > 0) return
|
|
8415
|
-
return
|
|
8740
|
+
if (override && override.length > 0) return import_node_path47.default.resolve(override);
|
|
8741
|
+
return import_node_path47.default.join(import_node_os4.default.homedir(), ".neat");
|
|
8416
8742
|
}
|
|
8417
8743
|
function connectorsConfigPath(home = neatHome2()) {
|
|
8418
|
-
return
|
|
8744
|
+
return import_node_path47.default.join(home, "connectors.json");
|
|
8419
8745
|
}
|
|
8420
8746
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
8421
8747
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -8714,11 +9040,11 @@ function registerRoutes(scope, ctx) {
|
|
|
8714
9040
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
8715
9041
|
const parsed = [];
|
|
8716
9042
|
for (const c of candidates) {
|
|
8717
|
-
const r =
|
|
9043
|
+
const r = import_types34.DivergenceTypeSchema.safeParse(c);
|
|
8718
9044
|
if (!r.success) {
|
|
8719
9045
|
return reply.code(400).send({
|
|
8720
9046
|
error: `unknown divergence type "${c}"`,
|
|
8721
|
-
allowed:
|
|
9047
|
+
allowed: import_types34.DivergenceTypeSchema.options
|
|
8722
9048
|
});
|
|
8723
9049
|
}
|
|
8724
9050
|
parsed.push(r.data);
|
|
@@ -8989,7 +9315,7 @@ function registerRoutes(scope, ctx) {
|
|
|
8989
9315
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
8990
9316
|
let violations = await log.readAll();
|
|
8991
9317
|
if (req.query.severity) {
|
|
8992
|
-
const sev =
|
|
9318
|
+
const sev = import_types34.PolicySeveritySchema.safeParse(req.query.severity);
|
|
8993
9319
|
if (!sev.success) {
|
|
8994
9320
|
return reply.code(400).send({
|
|
8995
9321
|
error: "invalid severity",
|
|
@@ -9028,7 +9354,7 @@ function registerRoutes(scope, ctx) {
|
|
|
9028
9354
|
scope.post("/policies/check", async (req, reply) => {
|
|
9029
9355
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
9030
9356
|
if (!proj) return;
|
|
9031
|
-
const parsed =
|
|
9357
|
+
const parsed = import_types34.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
9032
9358
|
if (!parsed.success) {
|
|
9033
9359
|
return reply.code(400).send({
|
|
9034
9360
|
error: "invalid /policies/check body",
|
|
@@ -9294,7 +9620,7 @@ init_otel_grpc();
|
|
|
9294
9620
|
// src/search.ts
|
|
9295
9621
|
init_cjs_shims();
|
|
9296
9622
|
var import_node_fs30 = require("fs");
|
|
9297
|
-
var
|
|
9623
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
9298
9624
|
var import_node_crypto3 = require("crypto");
|
|
9299
9625
|
var DEFAULT_LIMIT = 10;
|
|
9300
9626
|
var NOMIC_DIM = 768;
|
|
@@ -9457,7 +9783,7 @@ async function readCache(cachePath) {
|
|
|
9457
9783
|
}
|
|
9458
9784
|
}
|
|
9459
9785
|
async function writeCache(cachePath, cache) {
|
|
9460
|
-
await import_node_fs30.promises.mkdir(
|
|
9786
|
+
await import_node_fs30.promises.mkdir(import_node_path50.default.dirname(cachePath), { recursive: true });
|
|
9461
9787
|
await import_node_fs30.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
9462
9788
|
}
|
|
9463
9789
|
var VectorIndex = class {
|
|
@@ -9638,14 +9964,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
9638
9964
|
async function main() {
|
|
9639
9965
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
9640
9966
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
9641
|
-
const baseDir = baseDirEnv ?
|
|
9642
|
-
const defaultScanPath =
|
|
9967
|
+
const baseDir = baseDirEnv ? import_node_path51.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path51.default.resolve(import_node_path51.default.dirname(legacyOutPath)) : import_node_path51.default.resolve("./neat-out");
|
|
9968
|
+
const defaultScanPath = import_node_path51.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
9643
9969
|
const registry = new Projects();
|
|
9644
9970
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
9645
9971
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
9646
9972
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
9647
9973
|
const projectScan = process.env[envKey];
|
|
9648
|
-
await bootProject(registry, name, projectScan ?
|
|
9974
|
+
await bootProject(registry, name, projectScan ? import_node_path51.default.resolve(projectScan) : void 0, baseDir);
|
|
9649
9975
|
}
|
|
9650
9976
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
9651
9977
|
const port = Number(process.env.PORT ?? 8080);
|