@neat.is/core 0.7.6 → 0.7.7
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-6H757ZNM.js → chunk-4GCV46AP.js} +2 -2
- package/dist/{chunk-XT4NNFH6.js → chunk-UI3AFJAF.js} +765 -216
- package/dist/chunk-UI3AFJAF.js.map +1 -0
- package/dist/{chunk-P2ZEKJ35.js → chunk-Y43UCVZS.js} +9 -6
- package/dist/chunk-Y43UCVZS.js.map +1 -0
- package/dist/{chunk-XHSK4BGL.js → chunk-YVZRBT4C.js} +3 -3
- package/dist/cli.cjs +1143 -590
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.cjs +945 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +954 -401
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-APVZSB6W.js → otel-grpc-DPSAGRUU.js} +3 -3
- package/dist/server.cjs +877 -324
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-P2ZEKJ35.js.map +0 -1
- package/dist/chunk-XT4NNFH6.js.map +0 -1
- /package/dist/{chunk-6H757ZNM.js.map → chunk-4GCV46AP.js.map} +0 -0
- /package/dist/{chunk-XHSK4BGL.js.map → chunk-YVZRBT4C.js.map} +0 -0
- /package/dist/{otel-grpc-APVZSB6W.js.map → otel-grpc-DPSAGRUU.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 path68 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
63
|
+
if (exactUnauthPaths.has(path68) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path68)) {
|
|
64
64
|
done();
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -341,7 +341,7 @@ function pickEnv(spanAttrs, resourceAttrs) {
|
|
|
341
341
|
return ENV_FALLBACK;
|
|
342
342
|
}
|
|
343
343
|
function normalizeDbSystem(attrs) {
|
|
344
|
-
const raw = attrs["db.system"];
|
|
344
|
+
const raw = attrs["db.system"] ?? attrs["db.system.name"];
|
|
345
345
|
if (typeof raw !== "string") return void 0;
|
|
346
346
|
return raw === "mongoose" ? "mongodb" : raw;
|
|
347
347
|
}
|
|
@@ -413,8 +413,8 @@ function websocketChannelPathOf(attrs) {
|
|
|
413
413
|
const v = attrs[key];
|
|
414
414
|
if (typeof v === "string" && v.length > 0) {
|
|
415
415
|
const q = v.indexOf("?");
|
|
416
|
-
const
|
|
417
|
-
if (
|
|
416
|
+
const path68 = q === -1 ? v : v.slice(0, q);
|
|
417
|
+
if (path68.length > 0) return path68;
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
return void 0;
|
|
@@ -433,6 +433,9 @@ function parseOtlpRequest(body) {
|
|
|
433
433
|
for (const ss of rs.scopeSpans ?? []) {
|
|
434
434
|
for (const span of ss.spans ?? []) {
|
|
435
435
|
const attrs = attrsToRecord(span.attributes);
|
|
436
|
+
const dbSqlText = typeof attrs["db.statement"] === "string" ? attrs["db.statement"] : typeof attrs["db.query.text"] === "string" ? attrs["db.query.text"] : void 0;
|
|
437
|
+
const dbSystemName = normalizeDbSystem(attrs);
|
|
438
|
+
const directDbTable = typeof attrs["db.sql.table"] === "string" ? attrs["db.sql.table"] : dbSystemName !== "mongodb" && typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : void 0;
|
|
436
439
|
const parsed = {
|
|
437
440
|
service,
|
|
438
441
|
resourceServiceNamePresent,
|
|
@@ -447,11 +450,11 @@ function parseOtlpRequest(body) {
|
|
|
447
450
|
durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
|
|
448
451
|
env: pickEnv(attrs, resourceAttrs),
|
|
449
452
|
attributes: attrs,
|
|
450
|
-
dbSystem:
|
|
453
|
+
dbSystem: dbSystemName,
|
|
451
454
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
452
455
|
dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
|
|
453
|
-
dbTable:
|
|
454
|
-
dbColumns:
|
|
456
|
+
dbTable: directDbTable ?? (dbSqlText ? tableFromSqlStatement(dbSqlText) ?? void 0 : void 0),
|
|
457
|
+
dbColumns: dbSqlText ? columnsFromSqlStatement(dbSqlText) : void 0,
|
|
455
458
|
httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
|
|
456
459
|
httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
|
|
457
460
|
messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
|
|
@@ -712,7 +715,7 @@ var init_otel = __esm({
|
|
|
712
715
|
|
|
713
716
|
// src/server.ts
|
|
714
717
|
init_cjs_shims();
|
|
715
|
-
var
|
|
718
|
+
var import_node_path67 = __toESM(require("path"), 1);
|
|
716
719
|
|
|
717
720
|
// src/graph.ts
|
|
718
721
|
init_cjs_shims();
|
|
@@ -736,7 +739,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
736
739
|
init_cjs_shims();
|
|
737
740
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
738
741
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
739
|
-
var
|
|
742
|
+
var import_types80 = require("@neat.is/types");
|
|
740
743
|
|
|
741
744
|
// src/extend/index.ts
|
|
742
745
|
init_cjs_shims();
|
|
@@ -848,12 +851,12 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
848
851
|
]);
|
|
849
852
|
async function findHookFiles(scanPath) {
|
|
850
853
|
const found = [];
|
|
851
|
-
const
|
|
854
|
+
const walk9 = async (dir) => {
|
|
852
855
|
const entries = await import_node_fs2.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
853
856
|
for (const entry of entries) {
|
|
854
857
|
if (entry.isDirectory()) {
|
|
855
858
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
856
|
-
await
|
|
859
|
+
await walk9(import_node_path2.default.join(dir, entry.name));
|
|
857
860
|
} else if (entry.isFile()) {
|
|
858
861
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
859
862
|
const rel = import_node_path2.default.relative(scanPath, import_node_path2.default.join(dir, entry.name));
|
|
@@ -862,7 +865,7 @@ async function findHookFiles(scanPath) {
|
|
|
862
865
|
}
|
|
863
866
|
}
|
|
864
867
|
};
|
|
865
|
-
await
|
|
868
|
+
await walk9(scanPath);
|
|
866
869
|
return found.sort();
|
|
867
870
|
}
|
|
868
871
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
@@ -2265,14 +2268,14 @@ function buildServiceHostIndex(services) {
|
|
|
2265
2268
|
}
|
|
2266
2269
|
async function walkSourceFiles(dir) {
|
|
2267
2270
|
const out = [];
|
|
2268
|
-
async function
|
|
2271
|
+
async function walk9(current) {
|
|
2269
2272
|
const entries = await import_node_fs7.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
2270
2273
|
for (const entry of entries) {
|
|
2271
2274
|
const full = import_node_path7.default.join(current, entry.name);
|
|
2272
2275
|
if (entry.isDirectory()) {
|
|
2273
2276
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2274
2277
|
if (await isPythonVenvDir(full)) continue;
|
|
2275
|
-
await
|
|
2278
|
+
await walk9(full);
|
|
2276
2279
|
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path7.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
|
|
2277
2280
|
// would attribute our instrumentation imports to the user's service.
|
|
2278
2281
|
!isNeatAuthoredSourceFile(entry.name)) {
|
|
@@ -2280,7 +2283,7 @@ async function walkSourceFiles(dir) {
|
|
|
2280
2283
|
}
|
|
2281
2284
|
}
|
|
2282
2285
|
}
|
|
2283
|
-
await
|
|
2286
|
+
await walk9(dir);
|
|
2284
2287
|
return out;
|
|
2285
2288
|
}
|
|
2286
2289
|
async function loadSourceFiles(dir) {
|
|
@@ -2794,7 +2797,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
|
2794
2797
|
]);
|
|
2795
2798
|
var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
2796
2799
|
var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
2797
|
-
function
|
|
2800
|
+
function goRouterRoutesFromSource(source, parser, framework) {
|
|
2798
2801
|
const tree = parseSource2(parser, source);
|
|
2799
2802
|
const prefixes = /* @__PURE__ */ new Map();
|
|
2800
2803
|
const out = [];
|
|
@@ -2804,10 +2807,12 @@ function ginRoutesFromSource(source, parser) {
|
|
|
2804
2807
|
const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
|
|
2805
2808
|
if (name && value?.type === "call_expression") {
|
|
2806
2809
|
const fn2 = value.childForFieldName("function");
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2810
|
+
if (fn2?.childForFieldName("field")?.text === "Group") {
|
|
2811
|
+
const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
|
|
2812
|
+
if (leaf2 !== null) {
|
|
2813
|
+
const parent = fn2.childForFieldName("operand")?.text ?? "";
|
|
2814
|
+
prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
|
|
2815
|
+
}
|
|
2811
2816
|
}
|
|
2812
2817
|
}
|
|
2813
2818
|
return;
|
|
@@ -2818,18 +2823,32 @@ function ginRoutesFromSource(source, parser) {
|
|
|
2818
2823
|
const method = fn.childForFieldName("field")?.text?.toUpperCase();
|
|
2819
2824
|
if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
|
|
2820
2825
|
const receiver = fn.childForFieldName("operand")?.text ?? "";
|
|
2821
|
-
const
|
|
2822
|
-
if (
|
|
2823
|
-
const leaf = first.text.slice(1, -1);
|
|
2826
|
+
const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
2827
|
+
if (leaf === null) return;
|
|
2824
2828
|
out.push({
|
|
2825
|
-
method
|
|
2829
|
+
method,
|
|
2826
2830
|
pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
|
|
2827
2831
|
line: node.startPosition.row + 1,
|
|
2828
|
-
framework
|
|
2832
|
+
framework
|
|
2829
2833
|
});
|
|
2830
2834
|
});
|
|
2831
2835
|
return out;
|
|
2832
2836
|
}
|
|
2837
|
+
function goStringLiteral(node) {
|
|
2838
|
+
if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
|
|
2839
|
+
return node.text.slice(1, -1);
|
|
2840
|
+
}
|
|
2841
|
+
return null;
|
|
2842
|
+
}
|
|
2843
|
+
function ginRoutesFromSource(source, parser) {
|
|
2844
|
+
return goRouterRoutesFromSource(source, parser, "gin");
|
|
2845
|
+
}
|
|
2846
|
+
function echoRoutesFromSource(source, parser) {
|
|
2847
|
+
return goRouterRoutesFromSource(source, parser, "echo");
|
|
2848
|
+
}
|
|
2849
|
+
function fiberRoutesFromSource(source, parser) {
|
|
2850
|
+
return goRouterRoutesFromSource(source, parser, "fiber");
|
|
2851
|
+
}
|
|
2833
2852
|
var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
|
|
2834
2853
|
var NESTJS_METHODS = /* @__PURE__ */ new Map([
|
|
2835
2854
|
["Get", "GET"],
|
|
@@ -3414,9 +3433,9 @@ function rubyRocketRoute(args) {
|
|
|
3414
3433
|
if (!pair || pair.type !== "pair") continue;
|
|
3415
3434
|
const k = pair.childForFieldName("key");
|
|
3416
3435
|
if (k?.type !== "string") continue;
|
|
3417
|
-
const
|
|
3418
|
-
if (
|
|
3419
|
-
return { path:
|
|
3436
|
+
const path68 = rubyLiteral(k);
|
|
3437
|
+
if (path68 === null) continue;
|
|
3438
|
+
return { path: path68, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
3420
3439
|
}
|
|
3421
3440
|
return null;
|
|
3422
3441
|
}
|
|
@@ -4093,9 +4112,11 @@ async function addRoutes(graph, services) {
|
|
|
4093
4112
|
const hasFlask = deps["flask"] !== void 0;
|
|
4094
4113
|
const hasDjango = deps["django"] !== void 0;
|
|
4095
4114
|
const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
|
|
4115
|
+
const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
|
|
4116
|
+
const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
|
|
4096
4117
|
const hasRails = deps["rails"] !== void 0;
|
|
4097
4118
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4098
|
-
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
|
|
4119
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
|
|
4099
4120
|
continue;
|
|
4100
4121
|
const files = await loadSourceFiles(service.dir);
|
|
4101
4122
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4119,7 +4140,10 @@ async function addRoutes(graph, services) {
|
|
|
4119
4140
|
} else if (isRb) {
|
|
4120
4141
|
routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
|
|
4121
4142
|
} else if (isGo) {
|
|
4122
|
-
routes =
|
|
4143
|
+
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4144
|
+
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
4145
|
+
else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
|
|
4146
|
+
else routes = [];
|
|
4123
4147
|
} else if (isPy) {
|
|
4124
4148
|
routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
|
|
4125
4149
|
if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
|
|
@@ -5669,19 +5693,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
5669
5693
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
5670
5694
|
let best = { path: [start], edges: [] };
|
|
5671
5695
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
5672
|
-
function step(node,
|
|
5673
|
-
if (
|
|
5674
|
-
best = { path: [...
|
|
5696
|
+
function step(node, path68, edges) {
|
|
5697
|
+
if (path68.length > best.path.length) {
|
|
5698
|
+
best = { path: [...path68], edges: [...edges] };
|
|
5675
5699
|
}
|
|
5676
|
-
if (
|
|
5700
|
+
if (path68.length - 1 >= maxDepth) return;
|
|
5677
5701
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
5678
5702
|
for (const [srcId, edge] of incoming) {
|
|
5679
5703
|
if (visited.has(srcId)) continue;
|
|
5680
5704
|
visited.add(srcId);
|
|
5681
|
-
|
|
5705
|
+
path68.push(srcId);
|
|
5682
5706
|
edges.push(edge);
|
|
5683
|
-
step(srcId,
|
|
5684
|
-
|
|
5707
|
+
step(srcId, path68, edges);
|
|
5708
|
+
path68.pop();
|
|
5685
5709
|
edges.pop();
|
|
5686
5710
|
visited.delete(srcId);
|
|
5687
5711
|
}
|
|
@@ -5689,11 +5713,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
|
|
|
5689
5713
|
step(start, [start], []);
|
|
5690
5714
|
return best;
|
|
5691
5715
|
}
|
|
5692
|
-
function databaseRootCauseShape(graph, origin,
|
|
5716
|
+
function databaseRootCauseShape(graph, origin, walk9) {
|
|
5693
5717
|
const targetDb = origin;
|
|
5694
5718
|
const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
|
|
5695
5719
|
if (candidatePairs.length === 0) return null;
|
|
5696
|
-
for (const id of
|
|
5720
|
+
for (const id of walk9.path) {
|
|
5697
5721
|
const owner = resolveOwningService(graph, id);
|
|
5698
5722
|
if (!owner) continue;
|
|
5699
5723
|
const { id: serviceId9, svc } = owner;
|
|
@@ -5720,8 +5744,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
|
|
|
5720
5744
|
}
|
|
5721
5745
|
return null;
|
|
5722
5746
|
}
|
|
5723
|
-
function serviceRootCauseShape(graph, _origin,
|
|
5724
|
-
for (const id of
|
|
5747
|
+
function serviceRootCauseShape(graph, _origin, walk9) {
|
|
5748
|
+
for (const id of walk9.path) {
|
|
5725
5749
|
const owner = resolveOwningService(graph, id);
|
|
5726
5750
|
if (!owner) continue;
|
|
5727
5751
|
const { id: serviceId9, svc } = owner;
|
|
@@ -5757,15 +5781,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
|
|
|
5757
5781
|
}
|
|
5758
5782
|
return null;
|
|
5759
5783
|
}
|
|
5760
|
-
function fileRootCauseShape(graph, origin,
|
|
5784
|
+
function fileRootCauseShape(graph, origin, walk9) {
|
|
5761
5785
|
const owner = resolveOwningService(graph, origin.id);
|
|
5762
5786
|
if (!owner) return null;
|
|
5763
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
5787
|
+
return serviceRootCauseShape(graph, owner.svc, walk9);
|
|
5764
5788
|
}
|
|
5765
|
-
function symbolRootCauseShape(graph, origin,
|
|
5789
|
+
function symbolRootCauseShape(graph, origin, walk9) {
|
|
5766
5790
|
const owner = resolveOwningService(graph, origin.id);
|
|
5767
5791
|
if (!owner) return null;
|
|
5768
|
-
return serviceRootCauseShape(graph, owner.svc,
|
|
5792
|
+
return serviceRootCauseShape(graph, owner.svc, walk9);
|
|
5769
5793
|
}
|
|
5770
5794
|
var rootCauseShapes = {
|
|
5771
5795
|
[import_types8.NodeType.DatabaseNode]: databaseRootCauseShape,
|
|
@@ -5778,16 +5802,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
|
|
|
5778
5802
|
const origin = graph.getNodeAttributes(errorNodeId);
|
|
5779
5803
|
const shape = rootCauseShapes[origin.type];
|
|
5780
5804
|
if (shape) {
|
|
5781
|
-
const
|
|
5782
|
-
const match = shape(graph, origin,
|
|
5805
|
+
const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
|
|
5806
|
+
const match = shape(graph, origin, walk9);
|
|
5783
5807
|
if (match) {
|
|
5784
5808
|
const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
|
|
5785
5809
|
return import_types8.RootCauseResultSchema.parse({
|
|
5786
5810
|
rootCauseNode: match.rootCauseNode,
|
|
5787
5811
|
rootCauseReason: reason,
|
|
5788
|
-
traversalPath:
|
|
5789
|
-
edgeProvenances:
|
|
5790
|
-
confidence: confidenceFromMix(
|
|
5812
|
+
traversalPath: walk9.path,
|
|
5813
|
+
edgeProvenances: walk9.edges.map((e) => e.provenance),
|
|
5814
|
+
confidence: confidenceFromMix(walk9.edges),
|
|
5791
5815
|
fixRecommendation: match.fixRecommendation
|
|
5792
5816
|
});
|
|
5793
5817
|
}
|
|
@@ -5888,26 +5912,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
|
|
|
5888
5912
|
return best;
|
|
5889
5913
|
}
|
|
5890
5914
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
5891
|
-
const
|
|
5915
|
+
const path68 = [originServiceId];
|
|
5892
5916
|
const edges = [];
|
|
5893
5917
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
5894
5918
|
let current = originServiceId;
|
|
5895
5919
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
5896
5920
|
const hop = dominantFailingCall(graph, current, visited);
|
|
5897
5921
|
if (!hop) break;
|
|
5898
|
-
|
|
5922
|
+
path68.push(hop.nextService);
|
|
5899
5923
|
edges.push(hop.edge);
|
|
5900
5924
|
visited.add(hop.nextService);
|
|
5901
5925
|
current = hop.nextService;
|
|
5902
5926
|
}
|
|
5903
5927
|
if (edges.length === 0) return null;
|
|
5904
|
-
return { path:
|
|
5928
|
+
return { path: path68, edges, culprit: current };
|
|
5905
5929
|
}
|
|
5906
5930
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
5907
5931
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
5908
5932
|
if (!chain) return null;
|
|
5909
5933
|
const culprit = chain.culprit;
|
|
5910
|
-
const
|
|
5934
|
+
const path68 = [...chain.path];
|
|
5911
5935
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
5912
5936
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
5913
5937
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -5915,14 +5939,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
5915
5939
|
if (loc) {
|
|
5916
5940
|
let rootCauseNode = culprit;
|
|
5917
5941
|
if (loc.fileNode) {
|
|
5918
|
-
|
|
5942
|
+
path68.push(loc.fileNode);
|
|
5919
5943
|
edgeProvenances.push(import_types8.Provenance.OBSERVED);
|
|
5920
5944
|
rootCauseNode = loc.fileNode;
|
|
5921
5945
|
}
|
|
5922
5946
|
return import_types8.RootCauseResultSchema.parse({
|
|
5923
5947
|
rootCauseNode,
|
|
5924
5948
|
rootCauseReason: loc.rootCauseReason,
|
|
5925
|
-
traversalPath:
|
|
5949
|
+
traversalPath: path68,
|
|
5926
5950
|
edgeProvenances,
|
|
5927
5951
|
confidence,
|
|
5928
5952
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -5934,7 +5958,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
5934
5958
|
return import_types8.RootCauseResultSchema.parse({
|
|
5935
5959
|
rootCauseNode: culprit,
|
|
5936
5960
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
5937
|
-
traversalPath:
|
|
5961
|
+
traversalPath: path68,
|
|
5938
5962
|
edgeProvenances,
|
|
5939
5963
|
confidence,
|
|
5940
5964
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -6557,6 +6581,12 @@ function parseGoMod(source) {
|
|
|
6557
6581
|
}
|
|
6558
6582
|
return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
|
|
6559
6583
|
}
|
|
6584
|
+
function goFramework(deps) {
|
|
6585
|
+
if (deps["github.com/gin-gonic/gin"]) return "gin";
|
|
6586
|
+
if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
|
|
6587
|
+
if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
|
|
6588
|
+
return void 0;
|
|
6589
|
+
}
|
|
6560
6590
|
async function discoverGoService(scanPath, dir) {
|
|
6561
6591
|
let raw;
|
|
6562
6592
|
try {
|
|
@@ -6568,6 +6598,7 @@ async function discoverGoService(scanPath, dir) {
|
|
|
6568
6598
|
if (!mod) return null;
|
|
6569
6599
|
const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
|
|
6570
6600
|
const pkg = { name, dependencies: mod.dependencies };
|
|
6601
|
+
const framework = goFramework(mod.dependencies);
|
|
6571
6602
|
const node = {
|
|
6572
6603
|
id: (0, import_types10.serviceId)(name),
|
|
6573
6604
|
type: import_types10.NodeType.ServiceNode,
|
|
@@ -6575,7 +6606,7 @@ async function discoverGoService(scanPath, dir) {
|
|
|
6575
6606
|
language: "go",
|
|
6576
6607
|
dependencies: mod.dependencies,
|
|
6577
6608
|
repoPath: import_node_path12.default.relative(scanPath, dir),
|
|
6578
|
-
...
|
|
6609
|
+
...framework ? { framework } : {}
|
|
6579
6610
|
};
|
|
6580
6611
|
return { pkg, dir, node };
|
|
6581
6612
|
}
|
|
@@ -7475,7 +7506,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
7475
7506
|
return best;
|
|
7476
7507
|
};
|
|
7477
7508
|
const requests = [];
|
|
7478
|
-
const
|
|
7509
|
+
const walk9 = (node) => {
|
|
7479
7510
|
if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
|
|
7480
7511
|
const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
|
|
7481
7512
|
if (self && self.kind === "class") {
|
|
@@ -7521,10 +7552,10 @@ async function addSymbolEdges(graph, services) {
|
|
|
7521
7552
|
}
|
|
7522
7553
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
7523
7554
|
const child = node.namedChild(i);
|
|
7524
|
-
if (child)
|
|
7555
|
+
if (child) walk9(child);
|
|
7525
7556
|
}
|
|
7526
7557
|
};
|
|
7527
|
-
|
|
7558
|
+
walk9(root);
|
|
7528
7559
|
for (const req of requests) {
|
|
7529
7560
|
const targetSid = resolveTarget(req.targetName, req.wantKind);
|
|
7530
7561
|
if (!targetSid) continue;
|
|
@@ -8537,20 +8568,20 @@ var import_node_path30 = __toESM(require("path"), 1);
|
|
|
8537
8568
|
var import_types19 = require("@neat.is/types");
|
|
8538
8569
|
async function walkConfigFiles(dir) {
|
|
8539
8570
|
const out = [];
|
|
8540
|
-
async function
|
|
8571
|
+
async function walk9(current) {
|
|
8541
8572
|
const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true });
|
|
8542
8573
|
for (const entry of entries) {
|
|
8543
8574
|
const full = import_node_path30.default.join(current, entry.name);
|
|
8544
8575
|
if (entry.isDirectory()) {
|
|
8545
8576
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
8546
8577
|
if (await isPythonVenvDir(full)) continue;
|
|
8547
|
-
await
|
|
8578
|
+
await walk9(full);
|
|
8548
8579
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
8549
8580
|
out.push(full);
|
|
8550
8581
|
}
|
|
8551
8582
|
}
|
|
8552
8583
|
}
|
|
8553
|
-
await
|
|
8584
|
+
await walk9(dir);
|
|
8554
8585
|
return out;
|
|
8555
8586
|
}
|
|
8556
8587
|
async function addConfigNodes(graph, services, scanPath) {
|
|
@@ -8640,20 +8671,20 @@ function grpcMethodsFromProto(content, fqPackage) {
|
|
|
8640
8671
|
}
|
|
8641
8672
|
async function walkProtoFiles(dir) {
|
|
8642
8673
|
const out = [];
|
|
8643
|
-
async function
|
|
8674
|
+
async function walk9(current) {
|
|
8644
8675
|
const entries = await import_node_fs19.promises.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
8645
8676
|
for (const entry of entries) {
|
|
8646
8677
|
const full = import_node_path31.default.join(current, entry.name);
|
|
8647
8678
|
if (entry.isDirectory()) {
|
|
8648
8679
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
8649
8680
|
if (await isPythonVenvDir(full)) continue;
|
|
8650
|
-
await
|
|
8681
|
+
await walk9(full);
|
|
8651
8682
|
} else if (entry.isFile() && import_node_path31.default.extname(entry.name) === PROTO_EXTENSION) {
|
|
8652
8683
|
out.push(full);
|
|
8653
8684
|
}
|
|
8654
8685
|
}
|
|
8655
8686
|
}
|
|
8656
|
-
await
|
|
8687
|
+
await walk9(dir);
|
|
8657
8688
|
return out;
|
|
8658
8689
|
}
|
|
8659
8690
|
async function addGrpcMethods(graph, services) {
|
|
@@ -8721,7 +8752,7 @@ async function addGrpcMethods(graph, services) {
|
|
|
8721
8752
|
|
|
8722
8753
|
// src/extract/calls/index.ts
|
|
8723
8754
|
init_cjs_shims();
|
|
8724
|
-
var
|
|
8755
|
+
var import_types38 = require("@neat.is/types");
|
|
8725
8756
|
|
|
8726
8757
|
// src/extract/calls/http.ts
|
|
8727
8758
|
init_cjs_shims();
|
|
@@ -9475,7 +9506,7 @@ function isFirestoreClientFactory(node) {
|
|
|
9475
9506
|
}
|
|
9476
9507
|
function firestoreClientVars(root) {
|
|
9477
9508
|
const vars = /* @__PURE__ */ new Set();
|
|
9478
|
-
const
|
|
9509
|
+
const walk9 = (node) => {
|
|
9479
9510
|
if (node.type === "variable_declarator") {
|
|
9480
9511
|
const name = node.childForFieldName("name");
|
|
9481
9512
|
let value = node.childForFieldName("value");
|
|
@@ -9484,9 +9515,9 @@ function firestoreClientVars(root) {
|
|
|
9484
9515
|
vars.add(name.text);
|
|
9485
9516
|
}
|
|
9486
9517
|
}
|
|
9487
|
-
for (const c of namedChildren(node))
|
|
9518
|
+
for (const c of namedChildren(node)) walk9(c);
|
|
9488
9519
|
};
|
|
9489
|
-
|
|
9520
|
+
walk9(root);
|
|
9490
9521
|
return vars;
|
|
9491
9522
|
}
|
|
9492
9523
|
function isClientExpr(node, clientVars) {
|
|
@@ -9641,7 +9672,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9641
9672
|
}
|
|
9642
9673
|
s.add(field);
|
|
9643
9674
|
};
|
|
9644
|
-
const
|
|
9675
|
+
const walk9 = (node) => {
|
|
9645
9676
|
if (node.type === "call_expression") {
|
|
9646
9677
|
const fn = node.childForFieldName("function");
|
|
9647
9678
|
const line = node.startPosition.row + 1;
|
|
@@ -9681,9 +9712,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9681
9712
|
}
|
|
9682
9713
|
}
|
|
9683
9714
|
}
|
|
9684
|
-
for (const c of namedChildren(node))
|
|
9715
|
+
for (const c of namedChildren(node)) walk9(c);
|
|
9685
9716
|
};
|
|
9686
|
-
|
|
9717
|
+
walk9(tree.rootNode);
|
|
9687
9718
|
const out = [];
|
|
9688
9719
|
for (const [collPath, line] of collLine) {
|
|
9689
9720
|
const byField = writes.get(collPath);
|
|
@@ -10478,7 +10509,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
10478
10509
|
const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
|
|
10479
10510
|
const out = [];
|
|
10480
10511
|
const seen = /* @__PURE__ */ new Set();
|
|
10481
|
-
const
|
|
10512
|
+
const walk9 = (node) => {
|
|
10482
10513
|
if (node.type === "call_expression") {
|
|
10483
10514
|
const fn = node.childForFieldName("function");
|
|
10484
10515
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -10506,9 +10537,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
10506
10537
|
}
|
|
10507
10538
|
}
|
|
10508
10539
|
}
|
|
10509
|
-
for (const c of namedChildren4(node))
|
|
10540
|
+
for (const c of namedChildren4(node)) walk9(c);
|
|
10510
10541
|
};
|
|
10511
|
-
|
|
10542
|
+
walk9(tree.rootNode);
|
|
10512
10543
|
return out;
|
|
10513
10544
|
}
|
|
10514
10545
|
function enclosingVarName(call) {
|
|
@@ -10530,7 +10561,7 @@ function enclosingVarName(call) {
|
|
|
10530
10561
|
function collectDrizzleTables(root) {
|
|
10531
10562
|
const tables = [];
|
|
10532
10563
|
const varToTable = /* @__PURE__ */ new Map();
|
|
10533
|
-
const
|
|
10564
|
+
const walk9 = (node) => {
|
|
10534
10565
|
if (node.type === "call_expression") {
|
|
10535
10566
|
const fn = node.childForFieldName("function");
|
|
10536
10567
|
if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
|
|
@@ -10545,9 +10576,9 @@ function collectDrizzleTables(root) {
|
|
|
10545
10576
|
}
|
|
10546
10577
|
}
|
|
10547
10578
|
}
|
|
10548
|
-
for (const c of namedChildren4(node))
|
|
10579
|
+
for (const c of namedChildren4(node)) walk9(c);
|
|
10549
10580
|
};
|
|
10550
|
-
|
|
10581
|
+
walk9(root);
|
|
10551
10582
|
return { tables, varToTable };
|
|
10552
10583
|
}
|
|
10553
10584
|
function referencesTargetVar(call) {
|
|
@@ -10570,7 +10601,7 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
10570
10601
|
const seen = /* @__PURE__ */ new Set();
|
|
10571
10602
|
for (const table of tables) {
|
|
10572
10603
|
if (!table.object) continue;
|
|
10573
|
-
const
|
|
10604
|
+
const walk9 = (node) => {
|
|
10574
10605
|
if (node.type === "call_expression") {
|
|
10575
10606
|
const targetVar = referencesTargetVar(node);
|
|
10576
10607
|
const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
|
|
@@ -10591,9 +10622,9 @@ function drizzleForeignKeys(file, serviceDir) {
|
|
|
10591
10622
|
}
|
|
10592
10623
|
}
|
|
10593
10624
|
}
|
|
10594
|
-
for (const c of namedChildren4(node))
|
|
10625
|
+
for (const c of namedChildren4(node)) walk9(c);
|
|
10595
10626
|
};
|
|
10596
|
-
|
|
10627
|
+
walk9(table.object);
|
|
10597
10628
|
}
|
|
10598
10629
|
return out;
|
|
10599
10630
|
}
|
|
@@ -11673,15 +11704,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
11673
11704
|
return out;
|
|
11674
11705
|
}
|
|
11675
11706
|
|
|
11707
|
+
// src/extract/calls/gorm.ts
|
|
11708
|
+
init_cjs_shims();
|
|
11709
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
11710
|
+
var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
|
|
11711
|
+
var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
11712
|
+
var import_types37 = require("@neat.is/types");
|
|
11713
|
+
var GORM_IMPORT_RE = /gorm\.io\/gorm/;
|
|
11714
|
+
var PARSE_CHUNK11 = 16384;
|
|
11715
|
+
function makeGoParser3() {
|
|
11716
|
+
const p = new import_tree_sitter15.default();
|
|
11717
|
+
p.setLanguage(import_tree_sitter_go4.default);
|
|
11718
|
+
return p;
|
|
11719
|
+
}
|
|
11720
|
+
function parseSource10(parser, source) {
|
|
11721
|
+
return parser.parse(
|
|
11722
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
|
|
11723
|
+
);
|
|
11724
|
+
}
|
|
11725
|
+
function walk8(node, visit) {
|
|
11726
|
+
visit(node);
|
|
11727
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
11728
|
+
const c = node.namedChild(i);
|
|
11729
|
+
if (c) walk8(c, visit);
|
|
11730
|
+
}
|
|
11731
|
+
}
|
|
11732
|
+
var COMMON_INITIALISMS = [
|
|
11733
|
+
"ASCII",
|
|
11734
|
+
"HTTPS",
|
|
11735
|
+
"UTF8",
|
|
11736
|
+
"XSRF",
|
|
11737
|
+
"HTML",
|
|
11738
|
+
"HTTP",
|
|
11739
|
+
"JSON",
|
|
11740
|
+
"UUID",
|
|
11741
|
+
"XMPP",
|
|
11742
|
+
"ACL",
|
|
11743
|
+
"API",
|
|
11744
|
+
"CPU",
|
|
11745
|
+
"CSS",
|
|
11746
|
+
"DNS",
|
|
11747
|
+
"EOF",
|
|
11748
|
+
"GUID",
|
|
11749
|
+
"LHS",
|
|
11750
|
+
"QPS",
|
|
11751
|
+
"RAM",
|
|
11752
|
+
"RHS",
|
|
11753
|
+
"RPC",
|
|
11754
|
+
"SLA",
|
|
11755
|
+
"SQL",
|
|
11756
|
+
"SSH",
|
|
11757
|
+
"TCP",
|
|
11758
|
+
"TLS",
|
|
11759
|
+
"TTL",
|
|
11760
|
+
"UDP",
|
|
11761
|
+
"UID",
|
|
11762
|
+
"URI",
|
|
11763
|
+
"URL",
|
|
11764
|
+
"UID",
|
|
11765
|
+
"XSS",
|
|
11766
|
+
"ID",
|
|
11767
|
+
"IP",
|
|
11768
|
+
"UI",
|
|
11769
|
+
"VM",
|
|
11770
|
+
"XML"
|
|
11771
|
+
].sort((a, b) => b.length - a.length);
|
|
11772
|
+
function titleCase(word) {
|
|
11773
|
+
return word.charAt(0) + word.slice(1).toLowerCase();
|
|
11774
|
+
}
|
|
11775
|
+
function replaceInitialisms(name) {
|
|
11776
|
+
let out = "";
|
|
11777
|
+
let i = 0;
|
|
11778
|
+
while (i < name.length) {
|
|
11779
|
+
let matched = false;
|
|
11780
|
+
for (const init of COMMON_INITIALISMS) {
|
|
11781
|
+
if (name.startsWith(init, i)) {
|
|
11782
|
+
out += titleCase(init);
|
|
11783
|
+
i += init.length;
|
|
11784
|
+
matched = true;
|
|
11785
|
+
break;
|
|
11786
|
+
}
|
|
11787
|
+
}
|
|
11788
|
+
if (!matched) {
|
|
11789
|
+
out += name[i];
|
|
11790
|
+
i++;
|
|
11791
|
+
}
|
|
11792
|
+
}
|
|
11793
|
+
return out;
|
|
11794
|
+
}
|
|
11795
|
+
var isUpper = (c) => c >= "A" && c <= "Z";
|
|
11796
|
+
var isDigit = (c) => c >= "0" && c <= "9";
|
|
11797
|
+
function toDBName(name) {
|
|
11798
|
+
if (name === "") return "";
|
|
11799
|
+
const value = replaceInitialisms(name);
|
|
11800
|
+
if (value.length === 1) return value.toLowerCase();
|
|
11801
|
+
let buf = "";
|
|
11802
|
+
let lastCase = false;
|
|
11803
|
+
let curCase = isUpper(value[0]);
|
|
11804
|
+
for (let i = 0; i < value.length - 1; i++) {
|
|
11805
|
+
const v = value[i];
|
|
11806
|
+
const nextCase = isUpper(value[i + 1]);
|
|
11807
|
+
const nextNumber = isDigit(value[i + 1]);
|
|
11808
|
+
if (curCase) {
|
|
11809
|
+
if (lastCase && (nextCase || nextNumber)) {
|
|
11810
|
+
buf += v.toLowerCase();
|
|
11811
|
+
} else {
|
|
11812
|
+
if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
|
|
11813
|
+
buf += v.toLowerCase();
|
|
11814
|
+
}
|
|
11815
|
+
} else {
|
|
11816
|
+
buf += v;
|
|
11817
|
+
}
|
|
11818
|
+
lastCase = curCase;
|
|
11819
|
+
curCase = nextCase;
|
|
11820
|
+
}
|
|
11821
|
+
const last = value[value.length - 1];
|
|
11822
|
+
if (curCase) {
|
|
11823
|
+
if (!lastCase && value.length > 1) buf += "_";
|
|
11824
|
+
buf += last.toLowerCase();
|
|
11825
|
+
} else {
|
|
11826
|
+
buf += last;
|
|
11827
|
+
}
|
|
11828
|
+
return buf;
|
|
11829
|
+
}
|
|
11830
|
+
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
11831
|
+
"equipment",
|
|
11832
|
+
"information",
|
|
11833
|
+
"rice",
|
|
11834
|
+
"money",
|
|
11835
|
+
"species",
|
|
11836
|
+
"series",
|
|
11837
|
+
"fish",
|
|
11838
|
+
"sheep",
|
|
11839
|
+
"jeans",
|
|
11840
|
+
"police"
|
|
11841
|
+
]);
|
|
11842
|
+
var IRREGULAR = [
|
|
11843
|
+
["person", "people"],
|
|
11844
|
+
["man", "men"],
|
|
11845
|
+
["child", "children"],
|
|
11846
|
+
["sex", "sexes"],
|
|
11847
|
+
["move", "moves"]
|
|
11848
|
+
];
|
|
11849
|
+
var PLURAL_RULES = [
|
|
11850
|
+
[/(quiz)$/i, "$1zes"],
|
|
11851
|
+
[/^(ox)$/i, "$1en"],
|
|
11852
|
+
[/([ml])ouse$/i, "$1ice"],
|
|
11853
|
+
[/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
|
|
11854
|
+
[/(x|ch|ss|sh)$/i, "$1es"],
|
|
11855
|
+
[/([^aeiouy]|qu)y$/i, "$1ies"],
|
|
11856
|
+
[/(hive)$/i, "$1s"],
|
|
11857
|
+
[/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
|
|
11858
|
+
[/sis$/i, "ses"],
|
|
11859
|
+
[/([ti])um$/i, "$1a"],
|
|
11860
|
+
[/([ti])a$/i, "$1a"],
|
|
11861
|
+
[/(buffal|tomat)o$/i, "$1oes"],
|
|
11862
|
+
[/(bu)s$/i, "$1ses"],
|
|
11863
|
+
[/(alias|status)$/i, "$1es"],
|
|
11864
|
+
[/(octop|vir)i$/i, "$1i"],
|
|
11865
|
+
[/(octop|vir)us$/i, "$1i"],
|
|
11866
|
+
[/(ax|test)is$/i, "$1es"],
|
|
11867
|
+
[/s$/i, "s"]
|
|
11868
|
+
];
|
|
11869
|
+
function pluralize3(word) {
|
|
11870
|
+
if (word === "") return word;
|
|
11871
|
+
const lower = word.toLowerCase();
|
|
11872
|
+
for (const u of UNCOUNTABLE) {
|
|
11873
|
+
if (lower === u || lower.endsWith("_" + u)) return word;
|
|
11874
|
+
}
|
|
11875
|
+
for (const [sing, plur] of IRREGULAR) {
|
|
11876
|
+
const re = new RegExp(sing + "$", "i");
|
|
11877
|
+
if (re.test(word)) return word.replace(re, plur);
|
|
11878
|
+
}
|
|
11879
|
+
for (const [re, rep] of PLURAL_RULES) {
|
|
11880
|
+
if (re.test(word)) return word.replace(re, rep);
|
|
11881
|
+
}
|
|
11882
|
+
return word + "s";
|
|
11883
|
+
}
|
|
11884
|
+
function deriveTableName(structName) {
|
|
11885
|
+
return pluralize3(toDBName(structName));
|
|
11886
|
+
}
|
|
11887
|
+
function stringLiteralValue(node) {
|
|
11888
|
+
if (!node) return null;
|
|
11889
|
+
if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
|
|
11890
|
+
const t = node.text;
|
|
11891
|
+
return t.length >= 2 ? t.slice(1, -1) : "";
|
|
11892
|
+
}
|
|
11893
|
+
return null;
|
|
11894
|
+
}
|
|
11895
|
+
function parseGormTag(tagNode) {
|
|
11896
|
+
const tag = {};
|
|
11897
|
+
if (!tagNode) return tag;
|
|
11898
|
+
let inner = tagNode.text;
|
|
11899
|
+
if (inner.length >= 2) inner = inner.slice(1, -1);
|
|
11900
|
+
if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
|
|
11901
|
+
const m = inner.match(/gorm:"([^"]*)"/);
|
|
11902
|
+
if (!m) return tag;
|
|
11903
|
+
for (const part of m[1].split(";")) {
|
|
11904
|
+
if (part === "") continue;
|
|
11905
|
+
const idx = part.indexOf(":");
|
|
11906
|
+
const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
|
|
11907
|
+
const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
|
|
11908
|
+
if (key === "-") tag.skip = true;
|
|
11909
|
+
else if (key === "column") tag.column = value;
|
|
11910
|
+
else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
|
|
11911
|
+
else if (key === "foreignkey") tag.foreignKey = value;
|
|
11912
|
+
else if (key === "many2many") tag.many2many = value;
|
|
11913
|
+
else if (key === "embedded") tag.embedded = true;
|
|
11914
|
+
else if (key === "embeddedprefix") tag.embeddedPrefix = value;
|
|
11915
|
+
}
|
|
11916
|
+
return tag;
|
|
11917
|
+
}
|
|
11918
|
+
function unwrapType(typeNode) {
|
|
11919
|
+
let isSlice = false;
|
|
11920
|
+
let isPointer = false;
|
|
11921
|
+
let n = typeNode;
|
|
11922
|
+
while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
|
|
11923
|
+
if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
|
|
11924
|
+
if (n.type === "pointer_type") isPointer = true;
|
|
11925
|
+
n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
|
|
11926
|
+
}
|
|
11927
|
+
if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
|
|
11928
|
+
if (n.type === "type_identifier") {
|
|
11929
|
+
return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
|
|
11930
|
+
}
|
|
11931
|
+
if (n.type === "qualified_type") {
|
|
11932
|
+
const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
|
|
11933
|
+
const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
|
|
11934
|
+
return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
|
|
11935
|
+
}
|
|
11936
|
+
return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
|
|
11937
|
+
}
|
|
11938
|
+
function readField(fieldDecl) {
|
|
11939
|
+
const names = [];
|
|
11940
|
+
let tagNode = null;
|
|
11941
|
+
for (let i = 0; i < fieldDecl.namedChildCount; i++) {
|
|
11942
|
+
const c = fieldDecl.namedChild(i);
|
|
11943
|
+
if (!c) continue;
|
|
11944
|
+
if (c.type === "field_identifier") names.push(c.text);
|
|
11945
|
+
else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
|
|
11946
|
+
}
|
|
11947
|
+
const typeNode = fieldDecl.childForFieldName("type");
|
|
11948
|
+
const t = unwrapType(typeNode);
|
|
11949
|
+
return {
|
|
11950
|
+
names,
|
|
11951
|
+
typeName: t.name,
|
|
11952
|
+
qualifier: t.qualifier,
|
|
11953
|
+
isSlice: t.isSlice,
|
|
11954
|
+
isPointer: t.isPointer,
|
|
11955
|
+
isQualified: t.isQualified,
|
|
11956
|
+
tag: parseGormTag(tagNode),
|
|
11957
|
+
line: fieldDecl.startPosition.row + 1
|
|
11958
|
+
};
|
|
11959
|
+
}
|
|
11960
|
+
function collectStructs(tree) {
|
|
11961
|
+
const structs = /* @__PURE__ */ new Map();
|
|
11962
|
+
walk8(tree.rootNode, (node) => {
|
|
11963
|
+
if (node.type !== "type_spec") return;
|
|
11964
|
+
const nameNode = node.childForFieldName("name");
|
|
11965
|
+
const typeNode = node.childForFieldName("type");
|
|
11966
|
+
if (!nameNode || typeNode?.type !== "struct_type") return;
|
|
11967
|
+
const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
|
|
11968
|
+
const fields = [];
|
|
11969
|
+
if (list && list.type === "field_declaration_list") {
|
|
11970
|
+
for (let i = 0; i < list.namedChildCount; i++) {
|
|
11971
|
+
const fd = list.namedChild(i);
|
|
11972
|
+
if (fd?.type === "field_declaration") fields.push(readField(fd));
|
|
11973
|
+
}
|
|
11974
|
+
}
|
|
11975
|
+
structs.set(nameNode.text, {
|
|
11976
|
+
name: nameNode.text,
|
|
11977
|
+
fields,
|
|
11978
|
+
line: node.startPosition.row + 1
|
|
11979
|
+
});
|
|
11980
|
+
});
|
|
11981
|
+
return structs;
|
|
11982
|
+
}
|
|
11983
|
+
var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
|
|
11984
|
+
"AutoMigrate",
|
|
11985
|
+
"Model",
|
|
11986
|
+
"Create",
|
|
11987
|
+
"Find",
|
|
11988
|
+
"First",
|
|
11989
|
+
"Take",
|
|
11990
|
+
"Last",
|
|
11991
|
+
"Save",
|
|
11992
|
+
"Delete",
|
|
11993
|
+
"Where",
|
|
11994
|
+
"FirstOrCreate",
|
|
11995
|
+
"FirstOrInit"
|
|
11996
|
+
]);
|
|
11997
|
+
function compositeStructName(arg) {
|
|
11998
|
+
let n = arg;
|
|
11999
|
+
if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
|
|
12000
|
+
if (!n || n.type !== "composite_literal") return null;
|
|
12001
|
+
const typeNode = n.childForFieldName("type");
|
|
12002
|
+
if (!typeNode) return null;
|
|
12003
|
+
if (typeNode.type === "type_identifier") return typeNode.text;
|
|
12004
|
+
if (typeNode.type === "qualified_type") {
|
|
12005
|
+
return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
|
|
12006
|
+
}
|
|
12007
|
+
return null;
|
|
12008
|
+
}
|
|
12009
|
+
function collectCallModels(tree) {
|
|
12010
|
+
const models = /* @__PURE__ */ new Set();
|
|
12011
|
+
walk8(tree.rootNode, (node) => {
|
|
12012
|
+
if (node.type !== "call_expression") return;
|
|
12013
|
+
const fn = node.childForFieldName("function");
|
|
12014
|
+
if (fn?.type !== "selector_expression") return;
|
|
12015
|
+
const method = fn.childForFieldName("field")?.text;
|
|
12016
|
+
if (!method || !GORM_MODEL_METHODS.has(method)) return;
|
|
12017
|
+
const args = node.childForFieldName("arguments");
|
|
12018
|
+
if (!args) return;
|
|
12019
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
12020
|
+
const arg = args.namedChild(i);
|
|
12021
|
+
if (!arg) continue;
|
|
12022
|
+
const name = compositeStructName(arg);
|
|
12023
|
+
if (name) models.add(name);
|
|
12024
|
+
}
|
|
12025
|
+
});
|
|
12026
|
+
return models;
|
|
12027
|
+
}
|
|
12028
|
+
function collectTableNameOverrides(tree) {
|
|
12029
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
12030
|
+
const declarers = /* @__PURE__ */ new Set();
|
|
12031
|
+
walk8(tree.rootNode, (node) => {
|
|
12032
|
+
if (node.type !== "method_declaration") return;
|
|
12033
|
+
if (node.childForFieldName("name")?.text !== "TableName") return;
|
|
12034
|
+
const receiver = node.childForFieldName("receiver");
|
|
12035
|
+
if (!receiver) return;
|
|
12036
|
+
let recvType = null;
|
|
12037
|
+
for (let i = 0; i < receiver.namedChildCount; i++) {
|
|
12038
|
+
const pd = receiver.namedChild(i);
|
|
12039
|
+
if (pd?.type !== "parameter_declaration") continue;
|
|
12040
|
+
const t = unwrapType(pd.childForFieldName("type"));
|
|
12041
|
+
recvType = t.name;
|
|
12042
|
+
}
|
|
12043
|
+
if (!recvType) return;
|
|
12044
|
+
declarers.add(recvType);
|
|
12045
|
+
const body = node.childForFieldName("body");
|
|
12046
|
+
if (!body) return;
|
|
12047
|
+
let literal = null;
|
|
12048
|
+
walk8(body, (n) => {
|
|
12049
|
+
if (literal !== null) return;
|
|
12050
|
+
if (n.type !== "return_statement") return;
|
|
12051
|
+
const exprList = n.namedChild(0);
|
|
12052
|
+
const first = exprList?.namedChild(0) ?? exprList;
|
|
12053
|
+
const v = stringLiteralValue(first);
|
|
12054
|
+
if (v) literal = v;
|
|
12055
|
+
});
|
|
12056
|
+
if (literal !== null) overrides.set(recvType, literal);
|
|
12057
|
+
});
|
|
12058
|
+
return { overrides, declarers };
|
|
12059
|
+
}
|
|
12060
|
+
function isRelationField(field, structs) {
|
|
12061
|
+
if (field.names.length === 0) return false;
|
|
12062
|
+
if (field.isQualified) return false;
|
|
12063
|
+
if (!field.typeName) return false;
|
|
12064
|
+
return structs.has(field.typeName);
|
|
12065
|
+
}
|
|
12066
|
+
function isGormModelEmbed(field) {
|
|
12067
|
+
return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
|
|
12068
|
+
}
|
|
12069
|
+
function analyze(tree) {
|
|
12070
|
+
const structs = collectStructs(tree);
|
|
12071
|
+
const { overrides, declarers } = collectTableNameOverrides(tree);
|
|
12072
|
+
const callModels = collectCallModels(tree);
|
|
12073
|
+
const models = /* @__PURE__ */ new Set();
|
|
12074
|
+
for (const [name, info] of structs) {
|
|
12075
|
+
if (info.fields.some(isGormModelEmbed)) models.add(name);
|
|
12076
|
+
}
|
|
12077
|
+
for (const name of callModels) if (structs.has(name)) models.add(name);
|
|
12078
|
+
for (const name of declarers) if (structs.has(name)) models.add(name);
|
|
12079
|
+
let grew = true;
|
|
12080
|
+
while (grew) {
|
|
12081
|
+
grew = false;
|
|
12082
|
+
for (const name of Array.from(models)) {
|
|
12083
|
+
const info = structs.get(name);
|
|
12084
|
+
if (!info) continue;
|
|
12085
|
+
for (const field of info.fields) {
|
|
12086
|
+
if (!isRelationField(field, structs)) continue;
|
|
12087
|
+
const target = field.typeName;
|
|
12088
|
+
if (!models.has(target) && structs.has(target)) {
|
|
12089
|
+
models.add(target);
|
|
12090
|
+
grew = true;
|
|
12091
|
+
}
|
|
12092
|
+
}
|
|
12093
|
+
}
|
|
12094
|
+
}
|
|
12095
|
+
const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
|
|
12096
|
+
return { structs, models, tableFor };
|
|
12097
|
+
}
|
|
12098
|
+
function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
12099
|
+
if (seen.has(struct.name)) return;
|
|
12100
|
+
seen.add(struct.name);
|
|
12101
|
+
const add = (col) => {
|
|
12102
|
+
const full = prefix + col;
|
|
12103
|
+
if (!emitted.has(full)) {
|
|
12104
|
+
emitted.add(full);
|
|
12105
|
+
out.push(full);
|
|
12106
|
+
}
|
|
12107
|
+
};
|
|
12108
|
+
for (const field of struct.fields) {
|
|
12109
|
+
if (field.tag.skip) continue;
|
|
12110
|
+
if (field.names.length === 0) {
|
|
12111
|
+
if (isGormModelEmbed(field)) {
|
|
12112
|
+
add("id");
|
|
12113
|
+
add("created_at");
|
|
12114
|
+
add("updated_at");
|
|
12115
|
+
add("deleted_at");
|
|
12116
|
+
} else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
|
|
12117
|
+
collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
|
|
12118
|
+
}
|
|
12119
|
+
continue;
|
|
12120
|
+
}
|
|
12121
|
+
if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
|
|
12122
|
+
collectColumns(
|
|
12123
|
+
structs.get(field.typeName),
|
|
12124
|
+
structs,
|
|
12125
|
+
seen,
|
|
12126
|
+
prefix + (field.tag.embeddedPrefix ?? ""),
|
|
12127
|
+
out,
|
|
12128
|
+
emitted
|
|
12129
|
+
);
|
|
12130
|
+
continue;
|
|
12131
|
+
}
|
|
12132
|
+
if (isRelationField(field, structs)) continue;
|
|
12133
|
+
if (field.names.length === 1 && field.tag.column) {
|
|
12134
|
+
add(field.tag.column);
|
|
12135
|
+
} else {
|
|
12136
|
+
for (const n of field.names) add(toDBName(n));
|
|
12137
|
+
}
|
|
12138
|
+
}
|
|
12139
|
+
seen.delete(struct.name);
|
|
12140
|
+
}
|
|
12141
|
+
function gormEndpointsFromFile(file, serviceDir) {
|
|
12142
|
+
if (import_node_path50.default.extname(file.path) !== ".go") return [];
|
|
12143
|
+
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
12144
|
+
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12145
|
+
const { structs, models, tableFor } = analyze(tree);
|
|
12146
|
+
const out = [];
|
|
12147
|
+
const seenTables = /* @__PURE__ */ new Set();
|
|
12148
|
+
for (const name of models) {
|
|
12149
|
+
const struct = structs.get(name);
|
|
12150
|
+
if (!struct) continue;
|
|
12151
|
+
const table = tableFor(name);
|
|
12152
|
+
if (seenTables.has(table)) continue;
|
|
12153
|
+
seenTables.add(table);
|
|
12154
|
+
const columns = [];
|
|
12155
|
+
collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
|
|
12156
|
+
out.push({
|
|
12157
|
+
infraId: (0, import_types37.infraId)("sql-table", table),
|
|
12158
|
+
name: table,
|
|
12159
|
+
kind: "sql-table",
|
|
12160
|
+
edgeType: "CALLS",
|
|
12161
|
+
confidenceKind: "structural",
|
|
12162
|
+
...columns.length > 0 ? { columns } : {},
|
|
12163
|
+
evidence: {
|
|
12164
|
+
file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
|
|
12165
|
+
line: struct.line,
|
|
12166
|
+
snippet: snippet(file.content, struct.line)
|
|
12167
|
+
}
|
|
12168
|
+
});
|
|
12169
|
+
}
|
|
12170
|
+
return out;
|
|
12171
|
+
}
|
|
12172
|
+
function gormForeignKeys(file, serviceDir) {
|
|
12173
|
+
if (import_node_path50.default.extname(file.path) !== ".go") return [];
|
|
12174
|
+
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
12175
|
+
const tree = parseSource10(makeGoParser3(), file.content);
|
|
12176
|
+
const { structs, models, tableFor } = analyze(tree);
|
|
12177
|
+
const out = [];
|
|
12178
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12179
|
+
const emit = (childTable, parentTable, line) => {
|
|
12180
|
+
if (!childTable || !parentTable || childTable === parentTable) return;
|
|
12181
|
+
const key = `${childTable}->${parentTable}`;
|
|
12182
|
+
if (seen.has(key)) return;
|
|
12183
|
+
seen.add(key);
|
|
12184
|
+
out.push({
|
|
12185
|
+
childTable,
|
|
12186
|
+
parentTable,
|
|
12187
|
+
evidence: {
|
|
12188
|
+
file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
|
|
12189
|
+
line,
|
|
12190
|
+
snippet: snippet(file.content, line)
|
|
12191
|
+
}
|
|
12192
|
+
});
|
|
12193
|
+
};
|
|
12194
|
+
for (const name of models) {
|
|
12195
|
+
const struct = structs.get(name);
|
|
12196
|
+
if (!struct) continue;
|
|
12197
|
+
const thisTable = tableFor(name);
|
|
12198
|
+
const scalarNames = new Set(
|
|
12199
|
+
struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
|
|
12200
|
+
);
|
|
12201
|
+
for (const field of struct.fields) {
|
|
12202
|
+
if (field.tag.skip) continue;
|
|
12203
|
+
if (!isRelationField(field, structs)) continue;
|
|
12204
|
+
const relTable = tableFor(field.typeName);
|
|
12205
|
+
if (field.tag.many2many) {
|
|
12206
|
+
emit(field.tag.many2many, thisTable, field.line);
|
|
12207
|
+
emit(field.tag.many2many, relTable, field.line);
|
|
12208
|
+
continue;
|
|
12209
|
+
}
|
|
12210
|
+
if (field.isSlice) {
|
|
12211
|
+
emit(relTable, thisTable, field.line);
|
|
12212
|
+
continue;
|
|
12213
|
+
}
|
|
12214
|
+
const convFk = field.names[0] + "ID";
|
|
12215
|
+
const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
|
|
12216
|
+
if (belongsTo) emit(thisTable, relTable, field.line);
|
|
12217
|
+
else emit(relTable, thisTable, field.line);
|
|
12218
|
+
}
|
|
12219
|
+
}
|
|
12220
|
+
return out;
|
|
12221
|
+
}
|
|
12222
|
+
|
|
11676
12223
|
// src/extract/calls/index.ts
|
|
11677
12224
|
function edgeTypeFromEndpoint(ep) {
|
|
11678
12225
|
switch (ep.edgeType) {
|
|
11679
12226
|
case "PUBLISHES_TO":
|
|
11680
|
-
return
|
|
12227
|
+
return import_types38.EdgeType.PUBLISHES_TO;
|
|
11681
12228
|
case "CONSUMES_FROM":
|
|
11682
|
-
return
|
|
12229
|
+
return import_types38.EdgeType.CONSUMES_FROM;
|
|
11683
12230
|
default:
|
|
11684
|
-
return
|
|
12231
|
+
return import_types38.EdgeType.CALLS;
|
|
11685
12232
|
}
|
|
11686
12233
|
}
|
|
11687
12234
|
function isAwsKind(kind) {
|
|
@@ -11714,6 +12261,11 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11714
12261
|
} catch (err) {
|
|
11715
12262
|
recordExtractionError("go SQL call extraction", file.path, err);
|
|
11716
12263
|
}
|
|
12264
|
+
try {
|
|
12265
|
+
endpoints.push(...gormEndpointsFromFile(file, service.dir));
|
|
12266
|
+
} catch (err) {
|
|
12267
|
+
recordExtractionError("gorm data-axis extraction", file.path, err);
|
|
12268
|
+
}
|
|
11717
12269
|
try {
|
|
11718
12270
|
endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
|
|
11719
12271
|
endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
|
|
@@ -11736,7 +12288,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11736
12288
|
if (!graph.hasNode(ep.infraId)) {
|
|
11737
12289
|
const node = {
|
|
11738
12290
|
id: ep.infraId,
|
|
11739
|
-
type:
|
|
12291
|
+
type: import_types38.NodeType.InfraNode,
|
|
11740
12292
|
name: ep.name,
|
|
11741
12293
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
11742
12294
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -11749,21 +12301,21 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11749
12301
|
}
|
|
11750
12302
|
if (ep.columns && ep.columns.length > 0) {
|
|
11751
12303
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
11752
|
-
if (node.type ===
|
|
12304
|
+
if (node.type === import_types38.NodeType.InfraNode) {
|
|
11753
12305
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
11754
12306
|
...node,
|
|
11755
12307
|
columns: foldColumns(
|
|
11756
12308
|
node.columns,
|
|
11757
12309
|
ep.columns,
|
|
11758
|
-
|
|
11759
|
-
(0,
|
|
12310
|
+
import_types38.Provenance.EXTRACTED,
|
|
12311
|
+
(0, import_types38.confidenceForExtracted)(ep.confidenceKind)
|
|
11760
12312
|
)
|
|
11761
12313
|
});
|
|
11762
12314
|
}
|
|
11763
12315
|
}
|
|
11764
12316
|
if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
|
|
11765
12317
|
const node = graph.getNodeAttributes(ep.infraId);
|
|
11766
|
-
if (node.type ===
|
|
12318
|
+
if (node.type === import_types38.NodeType.InfraNode) {
|
|
11767
12319
|
graph.replaceNodeAttributes(ep.infraId, {
|
|
11768
12320
|
...node,
|
|
11769
12321
|
columns: foldSdkWrites(node.columns, ep.sdkWrites)
|
|
@@ -11771,7 +12323,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11771
12323
|
}
|
|
11772
12324
|
}
|
|
11773
12325
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
11774
|
-
const confidence = (0,
|
|
12326
|
+
const confidence = (0, import_types38.confidenceForExtracted)(ep.confidenceKind);
|
|
11775
12327
|
const relFile = toPosix(ep.evidence.file);
|
|
11776
12328
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
11777
12329
|
graph,
|
|
@@ -11781,7 +12333,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11781
12333
|
);
|
|
11782
12334
|
nodesAdded += n;
|
|
11783
12335
|
edgesAdded += e;
|
|
11784
|
-
if (!(0,
|
|
12336
|
+
if (!(0, import_types38.passesExtractedFloor)(confidence)) {
|
|
11785
12337
|
noteExtractedDropped({
|
|
11786
12338
|
source: fileNodeId,
|
|
11787
12339
|
target: ep.infraId,
|
|
@@ -11801,7 +12353,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
11801
12353
|
source: fileNodeId,
|
|
11802
12354
|
target: ep.infraId,
|
|
11803
12355
|
type: edgeType,
|
|
11804
|
-
provenance:
|
|
12356
|
+
provenance: import_types38.Provenance.EXTRACTED,
|
|
11805
12357
|
confidence,
|
|
11806
12358
|
evidence: ep.evidence
|
|
11807
12359
|
};
|
|
@@ -11824,7 +12376,7 @@ async function addCallEdges(graph, services) {
|
|
|
11824
12376
|
|
|
11825
12377
|
// src/extract/table-edges.ts
|
|
11826
12378
|
init_cjs_shims();
|
|
11827
|
-
var
|
|
12379
|
+
var import_types39 = require("@neat.is/types");
|
|
11828
12380
|
async function addTableEdges(graph, services) {
|
|
11829
12381
|
let nodesAdded = 0;
|
|
11830
12382
|
let edgesAdded = 0;
|
|
@@ -11838,6 +12390,7 @@ async function addTableEdges(graph, services) {
|
|
|
11838
12390
|
refs.push(...sqlalchemyForeignKeys(file, service.dir));
|
|
11839
12391
|
refs.push(...railsSchemaForeignKeys(file, service.dir));
|
|
11840
12392
|
refs.push(...laravelMigrationForeignKeys(file, service.dir));
|
|
12393
|
+
refs.push(...gormForeignKeys(file, service.dir));
|
|
11841
12394
|
modelRefs.push(...railsModelForeignKeys(file, service.dir));
|
|
11842
12395
|
modelRefs.push(...laravelModelForeignKeys(file, service.dir));
|
|
11843
12396
|
} catch (err) {
|
|
@@ -11851,20 +12404,20 @@ async function addTableEdges(graph, services) {
|
|
|
11851
12404
|
}
|
|
11852
12405
|
refs.push(...modelRefs);
|
|
11853
12406
|
for (const ref of refs) {
|
|
11854
|
-
const childId = (0,
|
|
11855
|
-
const parentId = (0,
|
|
12407
|
+
const childId = (0, import_types39.infraId)("sql-table", ref.childTable);
|
|
12408
|
+
const parentId = (0, import_types39.infraId)("sql-table", ref.parentTable);
|
|
11856
12409
|
if (childId === parentId) continue;
|
|
11857
12410
|
nodesAdded += ensureTableNode(graph, childId, ref.childTable);
|
|
11858
12411
|
nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
|
|
11859
|
-
const edgeId = (0,
|
|
12412
|
+
const edgeId = (0, import_types39.extractedEdgeId)(childId, parentId, import_types39.EdgeType.REFERENCES);
|
|
11860
12413
|
if (graph.hasEdge(edgeId)) continue;
|
|
11861
12414
|
const edge = {
|
|
11862
12415
|
id: edgeId,
|
|
11863
12416
|
source: childId,
|
|
11864
12417
|
target: parentId,
|
|
11865
|
-
type:
|
|
11866
|
-
provenance:
|
|
11867
|
-
confidence: (0,
|
|
12418
|
+
type: import_types39.EdgeType.REFERENCES,
|
|
12419
|
+
provenance: import_types39.Provenance.EXTRACTED,
|
|
12420
|
+
confidence: (0, import_types39.confidenceForExtracted)("structural"),
|
|
11868
12421
|
evidence: ref.evidence
|
|
11869
12422
|
};
|
|
11870
12423
|
graph.addEdgeWithKey(edgeId, childId, parentId, edge);
|
|
@@ -11877,7 +12430,7 @@ function ensureTableNode(graph, id, name) {
|
|
|
11877
12430
|
if (graph.hasNode(id)) return 0;
|
|
11878
12431
|
const node = {
|
|
11879
12432
|
id,
|
|
11880
|
-
type:
|
|
12433
|
+
type: import_types39.NodeType.InfraNode,
|
|
11881
12434
|
name,
|
|
11882
12435
|
provider: "self",
|
|
11883
12436
|
kind: "sql-table"
|
|
@@ -11891,16 +12444,16 @@ init_cjs_shims();
|
|
|
11891
12444
|
|
|
11892
12445
|
// src/extract/infra/docker-compose.ts
|
|
11893
12446
|
init_cjs_shims();
|
|
11894
|
-
var
|
|
11895
|
-
var
|
|
12447
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
12448
|
+
var import_types41 = require("@neat.is/types");
|
|
11896
12449
|
|
|
11897
12450
|
// src/extract/infra/shared.ts
|
|
11898
12451
|
init_cjs_shims();
|
|
11899
|
-
var
|
|
12452
|
+
var import_types40 = require("@neat.is/types");
|
|
11900
12453
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
11901
12454
|
return {
|
|
11902
|
-
id: (0,
|
|
11903
|
-
type:
|
|
12455
|
+
id: (0, import_types40.infraId)(kind, name),
|
|
12456
|
+
type: import_types40.NodeType.InfraNode,
|
|
11904
12457
|
name,
|
|
11905
12458
|
provider,
|
|
11906
12459
|
kind,
|
|
@@ -11944,8 +12497,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
|
|
|
11944
12497
|
source: anchorId,
|
|
11945
12498
|
target: node.id,
|
|
11946
12499
|
type: edgeType,
|
|
11947
|
-
provenance:
|
|
11948
|
-
confidence: (0,
|
|
12500
|
+
provenance: import_types40.Provenance.EXTRACTED,
|
|
12501
|
+
confidence: (0, import_types40.confidenceForExtracted)("structural"),
|
|
11949
12502
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
11950
12503
|
};
|
|
11951
12504
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -11962,7 +12515,7 @@ function dependsOnList(value) {
|
|
|
11962
12515
|
}
|
|
11963
12516
|
function serviceNameToServiceNode(name, services) {
|
|
11964
12517
|
for (const s of services) {
|
|
11965
|
-
if (s.node.name === name ||
|
|
12518
|
+
if (s.node.name === name || import_node_path51.default.basename(s.dir) === name) return s.node.id;
|
|
11966
12519
|
}
|
|
11967
12520
|
return null;
|
|
11968
12521
|
}
|
|
@@ -11971,7 +12524,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
11971
12524
|
let edgesAdded = 0;
|
|
11972
12525
|
let composePath = null;
|
|
11973
12526
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
11974
|
-
const abs =
|
|
12527
|
+
const abs = import_node_path51.default.join(scanPath, name);
|
|
11975
12528
|
if (await exists2(abs)) {
|
|
11976
12529
|
composePath = abs;
|
|
11977
12530
|
break;
|
|
@@ -11984,13 +12537,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
11984
12537
|
} catch (err) {
|
|
11985
12538
|
recordExtractionError(
|
|
11986
12539
|
"infra docker-compose",
|
|
11987
|
-
|
|
12540
|
+
import_node_path51.default.relative(scanPath, composePath),
|
|
11988
12541
|
err
|
|
11989
12542
|
);
|
|
11990
12543
|
return { nodesAdded, edgesAdded };
|
|
11991
12544
|
}
|
|
11992
12545
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
11993
|
-
const evidenceFile =
|
|
12546
|
+
const evidenceFile = import_node_path51.default.relative(scanPath, composePath).split(import_node_path51.default.sep).join("/");
|
|
11994
12547
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
11995
12548
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
11996
12549
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -12012,15 +12565,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
12012
12565
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
12013
12566
|
const targetId = composeNameToNodeId.get(dep);
|
|
12014
12567
|
if (!targetId) continue;
|
|
12015
|
-
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId,
|
|
12568
|
+
const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types41.EdgeType.DEPENDS_ON);
|
|
12016
12569
|
if (graph.hasEdge(edgeId)) continue;
|
|
12017
12570
|
const edge = {
|
|
12018
12571
|
id: edgeId,
|
|
12019
12572
|
source: sourceId,
|
|
12020
12573
|
target: targetId,
|
|
12021
|
-
type:
|
|
12022
|
-
provenance:
|
|
12023
|
-
confidence: (0,
|
|
12574
|
+
type: import_types41.EdgeType.DEPENDS_ON,
|
|
12575
|
+
provenance: import_types41.Provenance.EXTRACTED,
|
|
12576
|
+
confidence: (0, import_types41.confidenceForExtracted)("structural"),
|
|
12024
12577
|
evidence: { file: evidenceFile }
|
|
12025
12578
|
};
|
|
12026
12579
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -12032,9 +12585,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
12032
12585
|
|
|
12033
12586
|
// src/extract/infra/dockerfile.ts
|
|
12034
12587
|
init_cjs_shims();
|
|
12035
|
-
var
|
|
12588
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
12036
12589
|
var import_node_fs20 = require("fs");
|
|
12037
|
-
var
|
|
12590
|
+
var import_types42 = require("@neat.is/types");
|
|
12038
12591
|
function readDockerfile(content) {
|
|
12039
12592
|
let image = null;
|
|
12040
12593
|
const ports = [];
|
|
@@ -12063,7 +12616,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12063
12616
|
let nodesAdded = 0;
|
|
12064
12617
|
let edgesAdded = 0;
|
|
12065
12618
|
for (const service of services) {
|
|
12066
|
-
const dockerfilePath =
|
|
12619
|
+
const dockerfilePath = import_node_path52.default.join(service.dir, "Dockerfile");
|
|
12067
12620
|
if (!await exists2(dockerfilePath)) continue;
|
|
12068
12621
|
let content;
|
|
12069
12622
|
try {
|
|
@@ -12071,7 +12624,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12071
12624
|
} catch (err) {
|
|
12072
12625
|
recordExtractionError(
|
|
12073
12626
|
"infra dockerfile",
|
|
12074
|
-
|
|
12627
|
+
import_node_path52.default.relative(scanPath, dockerfilePath),
|
|
12075
12628
|
err
|
|
12076
12629
|
);
|
|
12077
12630
|
continue;
|
|
@@ -12083,8 +12636,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12083
12636
|
graph.addNode(node.id, node);
|
|
12084
12637
|
nodesAdded++;
|
|
12085
12638
|
}
|
|
12086
|
-
const relDockerfile = toPosix(
|
|
12087
|
-
const evidenceFile = toPosix(
|
|
12639
|
+
const relDockerfile = toPosix(import_node_path52.default.relative(service.dir, dockerfilePath));
|
|
12640
|
+
const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, dockerfilePath));
|
|
12088
12641
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
12089
12642
|
graph,
|
|
12090
12643
|
service.pkg.name,
|
|
@@ -12093,15 +12646,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12093
12646
|
);
|
|
12094
12647
|
nodesAdded += fn;
|
|
12095
12648
|
edgesAdded += fe;
|
|
12096
|
-
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id,
|
|
12649
|
+
const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types42.EdgeType.RUNS_ON);
|
|
12097
12650
|
if (!graph.hasEdge(edgeId)) {
|
|
12098
12651
|
const edge = {
|
|
12099
12652
|
id: edgeId,
|
|
12100
12653
|
source: fileNodeId,
|
|
12101
12654
|
target: node.id,
|
|
12102
|
-
type:
|
|
12103
|
-
provenance:
|
|
12104
|
-
confidence: (0,
|
|
12655
|
+
type: import_types42.EdgeType.RUNS_ON,
|
|
12656
|
+
provenance: import_types42.Provenance.EXTRACTED,
|
|
12657
|
+
confidence: (0, import_types42.confidenceForExtracted)("structural"),
|
|
12105
12658
|
evidence: {
|
|
12106
12659
|
file: evidenceFile,
|
|
12107
12660
|
...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
|
|
@@ -12116,15 +12669,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12116
12669
|
graph.addNode(portNode.id, portNode);
|
|
12117
12670
|
nodesAdded++;
|
|
12118
12671
|
}
|
|
12119
|
-
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id,
|
|
12672
|
+
const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types42.EdgeType.CONNECTS_TO);
|
|
12120
12673
|
if (graph.hasEdge(portEdgeId)) continue;
|
|
12121
12674
|
const portEdge = {
|
|
12122
12675
|
id: portEdgeId,
|
|
12123
12676
|
source: fileNodeId,
|
|
12124
12677
|
target: portNode.id,
|
|
12125
|
-
type:
|
|
12126
|
-
provenance:
|
|
12127
|
-
confidence: (0,
|
|
12678
|
+
type: import_types42.EdgeType.CONNECTS_TO,
|
|
12679
|
+
provenance: import_types42.Provenance.EXTRACTED,
|
|
12680
|
+
confidence: (0, import_types42.confidenceForExtracted)("structural"),
|
|
12128
12681
|
evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
|
|
12129
12682
|
};
|
|
12130
12683
|
graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
|
|
@@ -12137,8 +12690,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
12137
12690
|
// src/extract/infra/terraform.ts
|
|
12138
12691
|
init_cjs_shims();
|
|
12139
12692
|
var import_node_fs21 = require("fs");
|
|
12140
|
-
var
|
|
12141
|
-
var
|
|
12693
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
12694
|
+
var import_types43 = require("@neat.is/types");
|
|
12142
12695
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
12143
12696
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
12144
12697
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
@@ -12148,11 +12701,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
12148
12701
|
for (const entry of entries) {
|
|
12149
12702
|
if (entry.isDirectory()) {
|
|
12150
12703
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
12151
|
-
const child =
|
|
12704
|
+
const child = import_node_path53.default.join(start, entry.name);
|
|
12152
12705
|
if (await isPythonVenvDir(child)) continue;
|
|
12153
12706
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
12154
12707
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
12155
|
-
out.push(
|
|
12708
|
+
out.push(import_node_path53.default.join(start, entry.name));
|
|
12156
12709
|
}
|
|
12157
12710
|
}
|
|
12158
12711
|
return out;
|
|
@@ -12184,7 +12737,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
12184
12737
|
const files = await walkTfFiles(scanPath);
|
|
12185
12738
|
for (const file of files) {
|
|
12186
12739
|
const content = await import_node_fs21.promises.readFile(file, "utf8");
|
|
12187
|
-
const evidenceFile = toPosix(
|
|
12740
|
+
const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, file));
|
|
12188
12741
|
const resources = [];
|
|
12189
12742
|
const byKey = /* @__PURE__ */ new Map();
|
|
12190
12743
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -12219,16 +12772,16 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
12219
12772
|
if (!target) continue;
|
|
12220
12773
|
if (seen.has(target.nodeId)) continue;
|
|
12221
12774
|
seen.add(target.nodeId);
|
|
12222
|
-
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId,
|
|
12775
|
+
const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types43.EdgeType.DEPENDS_ON);
|
|
12223
12776
|
if (graph.hasEdge(edgeId)) continue;
|
|
12224
12777
|
const line = lineAt2(content, resource.bodyOffset + ref.index);
|
|
12225
12778
|
const edge = {
|
|
12226
12779
|
id: edgeId,
|
|
12227
12780
|
source: resource.nodeId,
|
|
12228
12781
|
target: target.nodeId,
|
|
12229
|
-
type:
|
|
12230
|
-
provenance:
|
|
12231
|
-
confidence: (0,
|
|
12782
|
+
type: import_types43.EdgeType.DEPENDS_ON,
|
|
12783
|
+
provenance: import_types43.Provenance.EXTRACTED,
|
|
12784
|
+
confidence: (0, import_types43.confidenceForExtracted)("structural"),
|
|
12232
12785
|
evidence: { file: evidenceFile, line, snippet: key }
|
|
12233
12786
|
};
|
|
12234
12787
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -12242,7 +12795,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
12242
12795
|
// src/extract/infra/k8s.ts
|
|
12243
12796
|
init_cjs_shims();
|
|
12244
12797
|
var import_node_fs22 = require("fs");
|
|
12245
|
-
var
|
|
12798
|
+
var import_node_path54 = __toESM(require("path"), 1);
|
|
12246
12799
|
var import_yaml3 = require("yaml");
|
|
12247
12800
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
12248
12801
|
Service: "k8s-service",
|
|
@@ -12260,11 +12813,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
12260
12813
|
for (const entry of entries) {
|
|
12261
12814
|
if (entry.isDirectory()) {
|
|
12262
12815
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
12263
|
-
const child =
|
|
12816
|
+
const child = import_node_path54.default.join(start, entry.name);
|
|
12264
12817
|
if (await isPythonVenvDir(child)) continue;
|
|
12265
12818
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
12266
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
12267
|
-
out.push(
|
|
12819
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path54.default.extname(entry.name))) {
|
|
12820
|
+
out.push(import_node_path54.default.join(start, entry.name));
|
|
12268
12821
|
}
|
|
12269
12822
|
}
|
|
12270
12823
|
return out;
|
|
@@ -12298,13 +12851,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
12298
12851
|
// src/extract/infra/cloudflare.ts
|
|
12299
12852
|
init_cjs_shims();
|
|
12300
12853
|
var import_node_fs23 = require("fs");
|
|
12301
|
-
var
|
|
12854
|
+
var import_node_path55 = __toESM(require("path"), 1);
|
|
12302
12855
|
var import_smol_toml2 = require("smol-toml");
|
|
12303
|
-
var
|
|
12856
|
+
var import_types44 = require("@neat.is/types");
|
|
12304
12857
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
12305
12858
|
async function readWranglerConfig(dir) {
|
|
12306
12859
|
for (const filename of WRANGLER_FILENAMES) {
|
|
12307
|
-
const abs =
|
|
12860
|
+
const abs = import_node_path55.default.join(dir, filename);
|
|
12308
12861
|
if (!await exists2(abs)) continue;
|
|
12309
12862
|
const raw = await import_node_fs23.promises.readFile(abs, "utf8");
|
|
12310
12863
|
const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -12348,8 +12901,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
|
|
|
12348
12901
|
source: anchorId,
|
|
12349
12902
|
target: node.id,
|
|
12350
12903
|
type: edgeType,
|
|
12351
|
-
provenance:
|
|
12352
|
-
confidence: (0,
|
|
12904
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
12905
|
+
confidence: (0, import_types44.confidenceForExtracted)("structural"),
|
|
12353
12906
|
evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
|
|
12354
12907
|
};
|
|
12355
12908
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -12367,11 +12920,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12367
12920
|
try {
|
|
12368
12921
|
read = await readWranglerConfig(service.dir);
|
|
12369
12922
|
} catch (err) {
|
|
12370
|
-
recordExtractionError("infra cloudflare",
|
|
12923
|
+
recordExtractionError("infra cloudflare", import_node_path55.default.relative(scanPath, service.dir), err);
|
|
12371
12924
|
continue;
|
|
12372
12925
|
}
|
|
12373
12926
|
if (!read || !read.config.name) continue;
|
|
12374
|
-
const evidenceFile = toPosix(
|
|
12927
|
+
const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, read.relFile)));
|
|
12375
12928
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
12376
12929
|
}
|
|
12377
12930
|
for (const worker of discovered) {
|
|
@@ -12383,7 +12936,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12383
12936
|
}
|
|
12384
12937
|
let anchorId = service.node.id;
|
|
12385
12938
|
if (config.main) {
|
|
12386
|
-
const entryRelPath = toPosix(
|
|
12939
|
+
const entryRelPath = toPosix(import_node_path55.default.normalize(config.main));
|
|
12387
12940
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
12388
12941
|
graph,
|
|
12389
12942
|
service.pkg.name,
|
|
@@ -12410,15 +12963,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12410
12963
|
nodesAdded++;
|
|
12411
12964
|
}
|
|
12412
12965
|
if (runtimeNode.id !== anchorId) {
|
|
12413
|
-
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id,
|
|
12966
|
+
const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types44.EdgeType.RUNS_ON);
|
|
12414
12967
|
if (!graph.hasEdge(runsOnId)) {
|
|
12415
12968
|
const edge = {
|
|
12416
12969
|
id: runsOnId,
|
|
12417
12970
|
source: anchorId,
|
|
12418
12971
|
target: runtimeNode.id,
|
|
12419
|
-
type:
|
|
12420
|
-
provenance:
|
|
12421
|
-
confidence: (0,
|
|
12972
|
+
type: import_types44.EdgeType.RUNS_ON,
|
|
12973
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
12974
|
+
confidence: (0, import_types44.confidenceForExtracted)("structural"),
|
|
12422
12975
|
evidence: {
|
|
12423
12976
|
file: evidenceFile,
|
|
12424
12977
|
...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
|
|
@@ -12432,7 +12985,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12432
12985
|
const result = addResourceEdge(
|
|
12433
12986
|
graph,
|
|
12434
12987
|
anchorId,
|
|
12435
|
-
|
|
12988
|
+
import_types44.EdgeType.CONNECTS_TO,
|
|
12436
12989
|
"cloudflare-route",
|
|
12437
12990
|
route,
|
|
12438
12991
|
evidenceFile,
|
|
@@ -12456,7 +13009,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12456
13009
|
const result = addResourceEdge(
|
|
12457
13010
|
graph,
|
|
12458
13011
|
anchorId,
|
|
12459
|
-
|
|
13012
|
+
import_types44.EdgeType.DEPENDS_ON,
|
|
12460
13013
|
group.kind,
|
|
12461
13014
|
name,
|
|
12462
13015
|
evidenceFile,
|
|
@@ -12470,7 +13023,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12470
13023
|
const result = addResourceEdge(
|
|
12471
13024
|
graph,
|
|
12472
13025
|
anchorId,
|
|
12473
|
-
|
|
13026
|
+
import_types44.EdgeType.DEPENDS_ON,
|
|
12474
13027
|
"cloudflare-cron",
|
|
12475
13028
|
cron,
|
|
12476
13029
|
evidenceFile,
|
|
@@ -12483,7 +13036,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12483
13036
|
const result = addResourceEdge(
|
|
12484
13037
|
graph,
|
|
12485
13038
|
anchorId,
|
|
12486
|
-
|
|
13039
|
+
import_types44.EdgeType.DEPENDS_ON,
|
|
12487
13040
|
"cloudflare-env-var",
|
|
12488
13041
|
varName,
|
|
12489
13042
|
evidenceFile,
|
|
@@ -12496,15 +13049,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12496
13049
|
if (!svc.service) continue;
|
|
12497
13050
|
const target = workerIndex.get(svc.service);
|
|
12498
13051
|
if (target && target.anchorId !== anchorId) {
|
|
12499
|
-
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId,
|
|
13052
|
+
const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types44.EdgeType.CALLS);
|
|
12500
13053
|
if (!graph.hasEdge(edgeId)) {
|
|
12501
13054
|
const edge = {
|
|
12502
13055
|
id: edgeId,
|
|
12503
13056
|
source: anchorId,
|
|
12504
13057
|
target: target.anchorId,
|
|
12505
|
-
type:
|
|
12506
|
-
provenance:
|
|
12507
|
-
confidence: (0,
|
|
13058
|
+
type: import_types44.EdgeType.CALLS,
|
|
13059
|
+
provenance: import_types44.Provenance.EXTRACTED,
|
|
13060
|
+
confidence: (0, import_types44.confidenceForExtracted)("structural"),
|
|
12508
13061
|
evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
|
|
12509
13062
|
};
|
|
12510
13063
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -12515,7 +13068,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12515
13068
|
const result = addResourceEdge(
|
|
12516
13069
|
graph,
|
|
12517
13070
|
anchorId,
|
|
12518
|
-
|
|
13071
|
+
import_types44.EdgeType.DEPENDS_ON,
|
|
12519
13072
|
"cloudflare-service-binding",
|
|
12520
13073
|
svc.service,
|
|
12521
13074
|
evidenceFile,
|
|
@@ -12531,12 +13084,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
12531
13084
|
// src/extract/infra/vercel.ts
|
|
12532
13085
|
init_cjs_shims();
|
|
12533
13086
|
var import_node_fs24 = require("fs");
|
|
12534
|
-
var
|
|
12535
|
-
var
|
|
13087
|
+
var import_node_path56 = __toESM(require("path"), 1);
|
|
13088
|
+
var import_types45 = require("@neat.is/types");
|
|
12536
13089
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
12537
13090
|
async function readVercelConfig(dir) {
|
|
12538
13091
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
12539
|
-
const abs =
|
|
13092
|
+
const abs = import_node_path56.default.join(dir, filename);
|
|
12540
13093
|
if (!await exists2(abs)) continue;
|
|
12541
13094
|
const raw = await import_node_fs24.promises.readFile(abs, "utf8");
|
|
12542
13095
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -12545,7 +13098,7 @@ async function readVercelConfig(dir) {
|
|
|
12545
13098
|
return null;
|
|
12546
13099
|
}
|
|
12547
13100
|
async function readLinkedProjectName(dir) {
|
|
12548
|
-
const abs =
|
|
13101
|
+
const abs = import_node_path56.default.join(dir, ".vercel", "project.json");
|
|
12549
13102
|
if (!await exists2(abs)) return void 0;
|
|
12550
13103
|
const parsed = JSON.parse(await import_node_fs24.promises.readFile(abs, "utf8"));
|
|
12551
13104
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -12563,7 +13116,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12563
13116
|
read = await readVercelConfig(service.dir);
|
|
12564
13117
|
projectName = await readLinkedProjectName(service.dir);
|
|
12565
13118
|
} catch (err) {
|
|
12566
|
-
recordExtractionError("infra vercel",
|
|
13119
|
+
recordExtractionError("infra vercel", import_node_path56.default.relative(scanPath, service.dir), err);
|
|
12567
13120
|
continue;
|
|
12568
13121
|
}
|
|
12569
13122
|
if (!read && !projectName) continue;
|
|
@@ -12579,7 +13132,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12579
13132
|
const anchorId = service.node.id;
|
|
12580
13133
|
if (!read) continue;
|
|
12581
13134
|
const { config, relFile, raw } = read;
|
|
12582
|
-
const evidenceFile = toPosix(
|
|
13135
|
+
const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
|
|
12583
13136
|
const add = (edgeType, kind, name) => {
|
|
12584
13137
|
if (!name) return;
|
|
12585
13138
|
const result = emitPlatformResourceEdge(
|
|
@@ -12595,12 +13148,12 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12595
13148
|
nodesAdded += result.nodesAdded;
|
|
12596
13149
|
edgesAdded += result.edgesAdded;
|
|
12597
13150
|
};
|
|
12598
|
-
add(
|
|
12599
|
-
for (const cron of config.crons ?? []) add(
|
|
12600
|
-
for (const varName of Object.keys(config.env ?? {})) add(
|
|
12601
|
-
for (const varName of Object.keys(config.build?.env ?? {})) add(
|
|
13151
|
+
add(import_types45.EdgeType.RUNS_ON, "vercel", "vercel");
|
|
13152
|
+
for (const cron of config.crons ?? []) add(import_types45.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
|
|
13153
|
+
for (const varName of Object.keys(config.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
13154
|
+
for (const varName of Object.keys(config.build?.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
|
|
12602
13155
|
for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
|
|
12603
|
-
add(
|
|
13156
|
+
add(import_types45.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
|
|
12604
13157
|
}
|
|
12605
13158
|
}
|
|
12606
13159
|
return { nodesAdded, edgesAdded };
|
|
@@ -12609,13 +13162,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
12609
13162
|
// src/extract/infra/railway.ts
|
|
12610
13163
|
init_cjs_shims();
|
|
12611
13164
|
var import_node_fs25 = require("fs");
|
|
12612
|
-
var
|
|
13165
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
12613
13166
|
var import_smol_toml3 = require("smol-toml");
|
|
12614
|
-
var
|
|
13167
|
+
var import_types46 = require("@neat.is/types");
|
|
12615
13168
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
12616
13169
|
async function readRailwayConfig(dir) {
|
|
12617
13170
|
for (const filename of RAILWAY_FILENAMES) {
|
|
12618
|
-
const abs =
|
|
13171
|
+
const abs = import_node_path57.default.join(dir, filename);
|
|
12619
13172
|
if (!await exists2(abs)) continue;
|
|
12620
13173
|
const raw = await import_node_fs25.promises.readFile(abs, "utf8");
|
|
12621
13174
|
const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -12631,7 +13184,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12631
13184
|
try {
|
|
12632
13185
|
read = await readRailwayConfig(service.dir);
|
|
12633
13186
|
} catch (err) {
|
|
12634
|
-
recordExtractionError("infra railway",
|
|
13187
|
+
recordExtractionError("infra railway", import_node_path57.default.relative(scanPath, service.dir), err);
|
|
12635
13188
|
continue;
|
|
12636
13189
|
}
|
|
12637
13190
|
if (!read) continue;
|
|
@@ -12641,7 +13194,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12641
13194
|
}
|
|
12642
13195
|
const anchorId = service.node.id;
|
|
12643
13196
|
const { config, relFile, raw } = read;
|
|
12644
|
-
const evidenceFile = toPosix(
|
|
13197
|
+
const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
|
|
12645
13198
|
const add = (edgeType, kind, name) => {
|
|
12646
13199
|
if (!name) return;
|
|
12647
13200
|
const result = emitPlatformResourceEdge(
|
|
@@ -12657,9 +13210,9 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12657
13210
|
nodesAdded += result.nodesAdded;
|
|
12658
13211
|
edgesAdded += result.edgesAdded;
|
|
12659
13212
|
};
|
|
12660
|
-
add(
|
|
12661
|
-
add(
|
|
12662
|
-
add(
|
|
13213
|
+
add(import_types46.EdgeType.RUNS_ON, "railway", "railway");
|
|
13214
|
+
add(import_types46.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
|
|
13215
|
+
add(import_types46.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
|
|
12663
13216
|
}
|
|
12664
13217
|
return { nodesAdded, edgesAdded };
|
|
12665
13218
|
}
|
|
@@ -12667,12 +13220,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
12667
13220
|
// src/extract/infra/supabase.ts
|
|
12668
13221
|
init_cjs_shims();
|
|
12669
13222
|
var import_node_fs26 = require("fs");
|
|
12670
|
-
var
|
|
13223
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
12671
13224
|
var import_smol_toml4 = require("smol-toml");
|
|
12672
|
-
var
|
|
13225
|
+
var import_types47 = require("@neat.is/types");
|
|
12673
13226
|
async function readSupabaseConfig(dir) {
|
|
12674
|
-
const relFile =
|
|
12675
|
-
const abs =
|
|
13227
|
+
const relFile = import_node_path58.default.join("supabase", "config.toml");
|
|
13228
|
+
const abs = import_node_path58.default.join(dir, relFile);
|
|
12676
13229
|
if (!await exists2(abs)) return null;
|
|
12677
13230
|
const raw = await import_node_fs26.promises.readFile(abs, "utf8");
|
|
12678
13231
|
const config = (0, import_smol_toml4.parse)(raw);
|
|
@@ -12686,7 +13239,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
12686
13239
|
try {
|
|
12687
13240
|
read = await readSupabaseConfig(service.dir);
|
|
12688
13241
|
} catch (err) {
|
|
12689
|
-
recordExtractionError("infra supabase",
|
|
13242
|
+
recordExtractionError("infra supabase", import_node_path58.default.relative(scanPath, service.dir), err);
|
|
12690
13243
|
continue;
|
|
12691
13244
|
}
|
|
12692
13245
|
if (!read) continue;
|
|
@@ -12701,7 +13254,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
12701
13254
|
});
|
|
12702
13255
|
}
|
|
12703
13256
|
const anchorId = service.node.id;
|
|
12704
|
-
const evidenceFile = toPosix(
|
|
13257
|
+
const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, import_node_path58.default.join(service.dir, relFile)));
|
|
12705
13258
|
const add = (edgeType, kind, name) => {
|
|
12706
13259
|
if (!name) return;
|
|
12707
13260
|
const result = emitPlatformResourceEdge(
|
|
@@ -12717,10 +13270,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
12717
13270
|
nodesAdded += result.nodesAdded;
|
|
12718
13271
|
edgesAdded += result.edgesAdded;
|
|
12719
13272
|
};
|
|
12720
|
-
add(
|
|
12721
|
-
for (const fn of Object.keys(config.functions ?? {})) add(
|
|
12722
|
-
if (config.storage) add(
|
|
12723
|
-
if (config.auth) add(
|
|
13273
|
+
add(import_types47.EdgeType.RUNS_ON, "supabase", "supabase");
|
|
13274
|
+
for (const fn of Object.keys(config.functions ?? {})) add(import_types47.EdgeType.DEPENDS_ON, "supabase-function", fn);
|
|
13275
|
+
if (config.storage) add(import_types47.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
|
|
13276
|
+
if (config.auth) add(import_types47.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
|
|
12724
13277
|
}
|
|
12725
13278
|
return { nodesAdded, edgesAdded };
|
|
12726
13279
|
}
|
|
@@ -12743,14 +13296,14 @@ async function addInfra(graph, scanPath, services) {
|
|
|
12743
13296
|
|
|
12744
13297
|
// src/extract/zod-shapes.ts
|
|
12745
13298
|
init_cjs_shims();
|
|
12746
|
-
var
|
|
12747
|
-
var
|
|
13299
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
13300
|
+
var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
|
|
12748
13301
|
var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
|
|
12749
|
-
var
|
|
13302
|
+
var import_types48 = require("@neat.is/types");
|
|
12750
13303
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
12751
13304
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
12752
13305
|
function parserForExt3(ext) {
|
|
12753
|
-
const p = new
|
|
13306
|
+
const p = new import_tree_sitter16.default();
|
|
12754
13307
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
12755
13308
|
return p;
|
|
12756
13309
|
}
|
|
@@ -12838,7 +13391,7 @@ function topLevelSchemas(root) {
|
|
|
12838
13391
|
}
|
|
12839
13392
|
function zodShapesFromFile(file, serviceDir) {
|
|
12840
13393
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
12841
|
-
const tree = parseSource3(parserForExt3(
|
|
13394
|
+
const tree = parseSource3(parserForExt3(import_node_path59.default.extname(file.path)), file.content);
|
|
12842
13395
|
const out = [];
|
|
12843
13396
|
const seen = /* @__PURE__ */ new Set();
|
|
12844
13397
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -12852,11 +13405,11 @@ function zodShapesFromFile(file, serviceDir) {
|
|
|
12852
13405
|
seen.add(name);
|
|
12853
13406
|
const line = call.startPosition.row + 1;
|
|
12854
13407
|
out.push({
|
|
12855
|
-
infraId: (0,
|
|
13408
|
+
infraId: (0, import_types48.infraId)("zod-schema", name),
|
|
12856
13409
|
name,
|
|
12857
13410
|
fields,
|
|
12858
13411
|
evidence: {
|
|
12859
|
-
file:
|
|
13412
|
+
file: import_node_path59.default.relative(serviceDir, file.path),
|
|
12860
13413
|
line,
|
|
12861
13414
|
snippet: snippet(file.content, line)
|
|
12862
13415
|
}
|
|
@@ -12887,7 +13440,7 @@ async function addZodShapes(graph, services) {
|
|
|
12887
13440
|
if (!graph.hasNode(shape.infraId)) {
|
|
12888
13441
|
const node = {
|
|
12889
13442
|
id: shape.infraId,
|
|
12890
|
-
type:
|
|
13443
|
+
type: import_types48.NodeType.InfraNode,
|
|
12891
13444
|
name: shape.name,
|
|
12892
13445
|
provider: "self",
|
|
12893
13446
|
kind: "zod-schema"
|
|
@@ -12897,14 +13450,14 @@ async function addZodShapes(graph, services) {
|
|
|
12897
13450
|
}
|
|
12898
13451
|
if (shape.fields.length > 0) {
|
|
12899
13452
|
const node = graph.getNodeAttributes(shape.infraId);
|
|
12900
|
-
if (node.type ===
|
|
13453
|
+
if (node.type === import_types48.NodeType.InfraNode) {
|
|
12901
13454
|
graph.replaceNodeAttributes(shape.infraId, {
|
|
12902
13455
|
...node,
|
|
12903
13456
|
columns: foldColumns(
|
|
12904
13457
|
node.columns,
|
|
12905
13458
|
shape.fields,
|
|
12906
|
-
|
|
12907
|
-
(0,
|
|
13459
|
+
import_types48.Provenance.EXTRACTED,
|
|
13460
|
+
(0, import_types48.confidenceForExtracted)("structural")
|
|
12908
13461
|
)
|
|
12909
13462
|
});
|
|
12910
13463
|
}
|
|
@@ -12918,15 +13471,15 @@ async function addZodShapes(graph, services) {
|
|
|
12918
13471
|
);
|
|
12919
13472
|
nodesAdded += n;
|
|
12920
13473
|
edgesAdded += e;
|
|
12921
|
-
const edgeId = (0,
|
|
13474
|
+
const edgeId = (0, import_types48.extractedEdgeId)(fileNodeId, shape.infraId, import_types48.EdgeType.CONTAINS);
|
|
12922
13475
|
if (!graph.hasEdge(edgeId)) {
|
|
12923
13476
|
const edge = {
|
|
12924
13477
|
id: edgeId,
|
|
12925
13478
|
source: fileNodeId,
|
|
12926
13479
|
target: shape.infraId,
|
|
12927
|
-
type:
|
|
12928
|
-
provenance:
|
|
12929
|
-
confidence: (0,
|
|
13480
|
+
type: import_types48.EdgeType.CONTAINS,
|
|
13481
|
+
provenance: import_types48.Provenance.EXTRACTED,
|
|
13482
|
+
confidence: (0, import_types48.confidenceForExtracted)("structural"),
|
|
12930
13483
|
evidence: shape.evidence
|
|
12931
13484
|
};
|
|
12932
13485
|
graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
|
|
@@ -12940,7 +13493,7 @@ async function addZodShapes(graph, services) {
|
|
|
12940
13493
|
|
|
12941
13494
|
// src/extract/firestore-rules.ts
|
|
12942
13495
|
init_cjs_shims();
|
|
12943
|
-
var
|
|
13496
|
+
var import_types49 = require("@neat.is/types");
|
|
12944
13497
|
var FIRESTORE_COLLECTION_KIND = "firestore-collection";
|
|
12945
13498
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
|
|
12946
13499
|
function stripComments(src) {
|
|
@@ -13080,7 +13633,7 @@ async function addFirestoreRules(graph, services) {
|
|
|
13080
13633
|
if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
13081
13634
|
graph.forEachNode((id, attrs) => {
|
|
13082
13635
|
const node = attrs;
|
|
13083
|
-
if (node.type !==
|
|
13636
|
+
if (node.type !== import_types49.NodeType.InfraNode) return;
|
|
13084
13637
|
if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
|
|
13085
13638
|
const fields = guards.get(collectionKeyFromName(node.name));
|
|
13086
13639
|
if (!fields || fields.size === 0) return;
|
|
@@ -13093,17 +13646,17 @@ async function addFirestoreRules(graph, services) {
|
|
|
13093
13646
|
}
|
|
13094
13647
|
|
|
13095
13648
|
// src/extract/index.ts
|
|
13096
|
-
var
|
|
13649
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
13097
13650
|
|
|
13098
13651
|
// src/extract/retire.ts
|
|
13099
13652
|
init_cjs_shims();
|
|
13100
13653
|
var import_node_fs27 = require("fs");
|
|
13101
|
-
var
|
|
13102
|
-
var
|
|
13654
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
13655
|
+
var import_types50 = require("@neat.is/types");
|
|
13103
13656
|
function dropOrphanedFileNodes(graph) {
|
|
13104
13657
|
const orphans = [];
|
|
13105
13658
|
graph.forEachNode((id, attrs) => {
|
|
13106
|
-
if (attrs.type !==
|
|
13659
|
+
if (attrs.type !== import_types50.NodeType.FileNode) return;
|
|
13107
13660
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
13108
13661
|
orphans.push(id);
|
|
13109
13662
|
}
|
|
@@ -13116,14 +13669,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
13116
13669
|
const bases = [scanPath, ...serviceDirs];
|
|
13117
13670
|
graph.forEachEdge((id, attrs) => {
|
|
13118
13671
|
const edge = attrs;
|
|
13119
|
-
if (edge.provenance !==
|
|
13672
|
+
if (edge.provenance !== import_types50.Provenance.EXTRACTED) return;
|
|
13120
13673
|
const evidenceFile = edge.evidence?.file;
|
|
13121
13674
|
if (!evidenceFile) return;
|
|
13122
|
-
if (
|
|
13675
|
+
if (import_node_path60.default.isAbsolute(evidenceFile)) {
|
|
13123
13676
|
if (!(0, import_node_fs27.existsSync)(evidenceFile)) toDrop.push(id);
|
|
13124
13677
|
return;
|
|
13125
13678
|
}
|
|
13126
|
-
const found = bases.some((base) => (0, import_node_fs27.existsSync)(
|
|
13679
|
+
const found = bases.some((base) => (0, import_node_fs27.existsSync)(import_node_path60.default.join(base, evidenceFile)));
|
|
13127
13680
|
if (!found) toDrop.push(id);
|
|
13128
13681
|
});
|
|
13129
13682
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -13180,7 +13733,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
13180
13733
|
}
|
|
13181
13734
|
const droppedEntries = drainDroppedExtracted();
|
|
13182
13735
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
13183
|
-
const rejectedPath =
|
|
13736
|
+
const rejectedPath = import_node_path61.default.join(import_node_path61.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
13184
13737
|
try {
|
|
13185
13738
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
13186
13739
|
} catch (err) {
|
|
@@ -13292,8 +13845,8 @@ function canonicalJson(value) {
|
|
|
13292
13845
|
// src/persist.ts
|
|
13293
13846
|
init_cjs_shims();
|
|
13294
13847
|
var import_node_fs29 = require("fs");
|
|
13295
|
-
var
|
|
13296
|
-
var
|
|
13848
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
13849
|
+
var import_types51 = require("@neat.is/types");
|
|
13297
13850
|
var SCHEMA_VERSION = 7;
|
|
13298
13851
|
function migrateV1ToV2(payload) {
|
|
13299
13852
|
const nodes = payload.graph.nodes;
|
|
@@ -13317,7 +13870,7 @@ function migrateV5ToV6(payload) {
|
|
|
13317
13870
|
if (Array.isArray(nodes)) {
|
|
13318
13871
|
for (const node of nodes) {
|
|
13319
13872
|
const attrs = node.attributes;
|
|
13320
|
-
if (!attrs || attrs.type !==
|
|
13873
|
+
if (!attrs || attrs.type !== import_types51.NodeType.InfraNode) continue;
|
|
13321
13874
|
if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
|
|
13322
13875
|
if (!Array.isArray(attrs.columns)) attrs.columns = [];
|
|
13323
13876
|
}
|
|
@@ -13333,12 +13886,12 @@ function migrateV2ToV3(payload) {
|
|
|
13333
13886
|
for (const edge of edges) {
|
|
13334
13887
|
const attrs = edge.attributes;
|
|
13335
13888
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
13336
|
-
attrs.provenance =
|
|
13889
|
+
attrs.provenance = import_types51.Provenance.OBSERVED;
|
|
13337
13890
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
13338
13891
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
13339
13892
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
13340
13893
|
if (type && source && target) {
|
|
13341
|
-
const newId = (0,
|
|
13894
|
+
const newId = (0, import_types51.observedEdgeId)(source, target, type);
|
|
13342
13895
|
attrs.id = newId;
|
|
13343
13896
|
if (edge.key) edge.key = newId;
|
|
13344
13897
|
}
|
|
@@ -13347,7 +13900,7 @@ function migrateV2ToV3(payload) {
|
|
|
13347
13900
|
return { ...payload, schemaVersion: 3 };
|
|
13348
13901
|
}
|
|
13349
13902
|
async function ensureDir(filePath) {
|
|
13350
|
-
await import_node_fs29.promises.mkdir(
|
|
13903
|
+
await import_node_fs29.promises.mkdir(import_node_path62.default.dirname(filePath), { recursive: true });
|
|
13351
13904
|
}
|
|
13352
13905
|
async function saveGraphToDisk(graph, outPath) {
|
|
13353
13906
|
await ensureDir(outPath);
|
|
@@ -13437,23 +13990,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
13437
13990
|
|
|
13438
13991
|
// src/projects.ts
|
|
13439
13992
|
init_cjs_shims();
|
|
13440
|
-
var
|
|
13993
|
+
var import_node_path63 = __toESM(require("path"), 1);
|
|
13441
13994
|
function pathsForProject(project, baseDir) {
|
|
13442
13995
|
if (project === DEFAULT_PROJECT) {
|
|
13443
13996
|
return {
|
|
13444
|
-
snapshotPath:
|
|
13445
|
-
errorsPath:
|
|
13446
|
-
staleEventsPath:
|
|
13447
|
-
embeddingsCachePath:
|
|
13448
|
-
policyViolationsPath:
|
|
13997
|
+
snapshotPath: import_node_path63.default.join(baseDir, "graph.json"),
|
|
13998
|
+
errorsPath: import_node_path63.default.join(baseDir, "errors.ndjson"),
|
|
13999
|
+
staleEventsPath: import_node_path63.default.join(baseDir, "stale-events.ndjson"),
|
|
14000
|
+
embeddingsCachePath: import_node_path63.default.join(baseDir, "embeddings.json"),
|
|
14001
|
+
policyViolationsPath: import_node_path63.default.join(baseDir, "policy-violations.ndjson")
|
|
13449
14002
|
};
|
|
13450
14003
|
}
|
|
13451
14004
|
return {
|
|
13452
|
-
snapshotPath:
|
|
13453
|
-
errorsPath:
|
|
13454
|
-
staleEventsPath:
|
|
13455
|
-
embeddingsCachePath:
|
|
13456
|
-
policyViolationsPath:
|
|
14005
|
+
snapshotPath: import_node_path63.default.join(baseDir, `${project}.json`),
|
|
14006
|
+
errorsPath: import_node_path63.default.join(baseDir, `errors.${project}.ndjson`),
|
|
14007
|
+
staleEventsPath: import_node_path63.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
14008
|
+
embeddingsCachePath: import_node_path63.default.join(baseDir, `embeddings.${project}.json`),
|
|
14009
|
+
policyViolationsPath: import_node_path63.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
13457
14010
|
};
|
|
13458
14011
|
}
|
|
13459
14012
|
var Projects = class {
|
|
@@ -13495,18 +14048,18 @@ function parseExtraProjects(raw) {
|
|
|
13495
14048
|
init_cjs_shims();
|
|
13496
14049
|
var import_node_fs30 = require("fs");
|
|
13497
14050
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
13498
|
-
var
|
|
13499
|
-
var
|
|
14051
|
+
var import_node_path64 = __toESM(require("path"), 1);
|
|
14052
|
+
var import_types52 = require("@neat.is/types");
|
|
13500
14053
|
function neatHome() {
|
|
13501
14054
|
const override = process.env.NEAT_HOME;
|
|
13502
|
-
if (override && override.length > 0) return
|
|
13503
|
-
return
|
|
14055
|
+
if (override && override.length > 0) return import_node_path64.default.resolve(override);
|
|
14056
|
+
return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
|
|
13504
14057
|
}
|
|
13505
14058
|
function registryPath() {
|
|
13506
|
-
return
|
|
14059
|
+
return import_node_path64.default.join(neatHome(), "projects.json");
|
|
13507
14060
|
}
|
|
13508
14061
|
function daemonsDir() {
|
|
13509
|
-
return
|
|
14062
|
+
return import_node_path64.default.join(neatHome(), "daemons");
|
|
13510
14063
|
}
|
|
13511
14064
|
function isFiniteInt(v) {
|
|
13512
14065
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -13547,7 +14100,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
13547
14100
|
const out = [];
|
|
13548
14101
|
for (const name of names) {
|
|
13549
14102
|
if (!name.endsWith(".json")) continue;
|
|
13550
|
-
const file =
|
|
14103
|
+
const file = import_node_path64.default.join(dir, name);
|
|
13551
14104
|
let raw;
|
|
13552
14105
|
try {
|
|
13553
14106
|
raw = await import_node_fs30.promises.readFile(file, "utf8");
|
|
@@ -13586,7 +14139,7 @@ async function readRegistry() {
|
|
|
13586
14139
|
throw err;
|
|
13587
14140
|
}
|
|
13588
14141
|
const parsed = JSON.parse(raw);
|
|
13589
|
-
return
|
|
14142
|
+
return import_types52.RegistryFileSchema.parse(parsed);
|
|
13590
14143
|
}
|
|
13591
14144
|
async function getProject(name) {
|
|
13592
14145
|
const reg = await readRegistry();
|
|
@@ -13661,7 +14214,7 @@ init_auth();
|
|
|
13661
14214
|
// src/connectors-config.ts
|
|
13662
14215
|
init_cjs_shims();
|
|
13663
14216
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
13664
|
-
var
|
|
14217
|
+
var import_node_path65 = __toESM(require("path"), 1);
|
|
13665
14218
|
var import_node_fs31 = require("fs");
|
|
13666
14219
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
13667
14220
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -13676,11 +14229,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
13676
14229
|
};
|
|
13677
14230
|
function neatHome2() {
|
|
13678
14231
|
const override = process.env.NEAT_HOME;
|
|
13679
|
-
if (override && override.length > 0) return
|
|
13680
|
-
return
|
|
14232
|
+
if (override && override.length > 0) return import_node_path65.default.resolve(override);
|
|
14233
|
+
return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
|
|
13681
14234
|
}
|
|
13682
14235
|
function connectorsConfigPath(home = neatHome2()) {
|
|
13683
|
-
return
|
|
14236
|
+
return import_node_path65.default.join(home, "connectors.json");
|
|
13684
14237
|
}
|
|
13685
14238
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
13686
14239
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -13867,15 +14420,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
13867
14420
|
|
|
13868
14421
|
// src/connectors/index.ts
|
|
13869
14422
|
init_cjs_shims();
|
|
13870
|
-
var
|
|
14423
|
+
var import_types53 = require("@neat.is/types");
|
|
13871
14424
|
var NO_ENV = "unknown";
|
|
13872
14425
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
13873
14426
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
13874
14427
|
const sites = [];
|
|
13875
14428
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
13876
14429
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
13877
|
-
if (edge.provenance !==
|
|
13878
|
-
const parsed = (0,
|
|
14430
|
+
if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
|
|
14431
|
+
const parsed = (0, import_types53.parseFileId)(edge.source);
|
|
13879
14432
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
13880
14433
|
const site = { relPath: edge.evidence.file };
|
|
13881
14434
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -13886,7 +14439,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
13886
14439
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
13887
14440
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
13888
14441
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
13889
|
-
if (attrs.type !==
|
|
14442
|
+
if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
|
|
13890
14443
|
const site = { relPath: attrs.path };
|
|
13891
14444
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
13892
14445
|
return site;
|
|
@@ -14326,10 +14879,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
14326
14879
|
// src/connectors/supabase/map.ts
|
|
14327
14880
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
14328
14881
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
14329
|
-
function targetFromRestPath(
|
|
14330
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
14882
|
+
function targetFromRestPath(path68) {
|
|
14883
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path68);
|
|
14331
14884
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
14332
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
14885
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path68);
|
|
14333
14886
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
14334
14887
|
return null;
|
|
14335
14888
|
}
|
|
@@ -14440,23 +14993,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
14440
14993
|
|
|
14441
14994
|
// src/connectors/supabase/resolve.ts
|
|
14442
14995
|
init_cjs_shims();
|
|
14443
|
-
var
|
|
14996
|
+
var import_types55 = require("@neat.is/types");
|
|
14444
14997
|
function createSupabaseResolveTarget(graph, config) {
|
|
14445
14998
|
return (signal, _ctx) => {
|
|
14446
14999
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
14447
15000
|
return null;
|
|
14448
15001
|
}
|
|
14449
|
-
const subResourceId = (0,
|
|
15002
|
+
const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
14450
15003
|
if (graph.hasNode(subResourceId)) {
|
|
14451
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
15004
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
|
|
14452
15005
|
}
|
|
14453
|
-
const bareResourceId = (0,
|
|
15006
|
+
const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
|
|
14454
15007
|
if (graph.hasNode(bareResourceId)) {
|
|
14455
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
15008
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
|
|
14456
15009
|
}
|
|
14457
|
-
const projectLevelId = (0,
|
|
15010
|
+
const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
|
|
14458
15011
|
if (graph.hasNode(projectLevelId)) {
|
|
14459
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
15012
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
|
|
14460
15013
|
}
|
|
14461
15014
|
return null;
|
|
14462
15015
|
};
|
|
@@ -14549,7 +15102,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
14549
15102
|
|
|
14550
15103
|
// src/connectors/railway/index.ts
|
|
14551
15104
|
init_cjs_shims();
|
|
14552
|
-
var
|
|
15105
|
+
var import_types59 = require("@neat.is/types");
|
|
14553
15106
|
|
|
14554
15107
|
// src/connectors/railway/client.ts
|
|
14555
15108
|
init_cjs_shims();
|
|
@@ -14700,7 +15253,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
14700
15253
|
const out = [];
|
|
14701
15254
|
graph.forEachNode((_id, attrs) => {
|
|
14702
15255
|
const node = attrs;
|
|
14703
|
-
if (node.type !==
|
|
15256
|
+
if (node.type !== import_types59.NodeType.RouteNode) return;
|
|
14704
15257
|
const route = attrs;
|
|
14705
15258
|
if (route.service !== serviceName) return;
|
|
14706
15259
|
out.push({
|
|
@@ -14804,12 +15357,12 @@ function createRailwayResolveTarget(config) {
|
|
|
14804
15357
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
14805
15358
|
if (!serviceName) return null;
|
|
14806
15359
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
14807
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
15360
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
|
|
14808
15361
|
}
|
|
14809
15362
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
14810
15363
|
const peerName = config.serviceNameById[signal.targetName];
|
|
14811
15364
|
if (!peerName) return null;
|
|
14812
|
-
return { targetNodeId: (0,
|
|
15365
|
+
return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
|
|
14813
15366
|
}
|
|
14814
15367
|
return null;
|
|
14815
15368
|
};
|
|
@@ -14933,9 +15486,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
14933
15486
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
14934
15487
|
if (secondSep === -1) return null;
|
|
14935
15488
|
const method = rest.slice(0, secondSep);
|
|
14936
|
-
const
|
|
14937
|
-
if (!resourceName || !method || !
|
|
14938
|
-
return { resourceName, method, path:
|
|
15489
|
+
const path68 = rest.slice(secondSep + 1);
|
|
15490
|
+
if (!resourceName || !method || !path68) return null;
|
|
15491
|
+
return { resourceName, method, path: path68 };
|
|
14939
15492
|
}
|
|
14940
15493
|
function resourceNameFor(type, labels) {
|
|
14941
15494
|
if (!labels) return null;
|
|
@@ -14973,14 +15526,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
14973
15526
|
if (!req) return null;
|
|
14974
15527
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
14975
15528
|
const method = req.requestMethod.toUpperCase();
|
|
14976
|
-
const
|
|
14977
|
-
if (
|
|
15529
|
+
const path68 = pathFromRequestUrl(req.requestUrl);
|
|
15530
|
+
if (path68 === null) return null;
|
|
14978
15531
|
const timestamp = entry.timestamp;
|
|
14979
15532
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
14980
15533
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
14981
15534
|
return {
|
|
14982
15535
|
targetKind: resourceType,
|
|
14983
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
15536
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path68 }),
|
|
14984
15537
|
callCount: 1,
|
|
14985
15538
|
errorCount: isError ? 1 : 0,
|
|
14986
15539
|
lastObservedIso: timestamp
|
|
@@ -14997,7 +15550,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
14997
15550
|
|
|
14998
15551
|
// src/connectors/firebase/resolve.ts
|
|
14999
15552
|
init_cjs_shims();
|
|
15000
|
-
var
|
|
15553
|
+
var import_types60 = require("@neat.is/types");
|
|
15001
15554
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
15002
15555
|
switch (resourceType) {
|
|
15003
15556
|
case "cloud_function":
|
|
@@ -15012,7 +15565,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
15012
15565
|
const entries = [];
|
|
15013
15566
|
graph.forEachNode((_id, attrs) => {
|
|
15014
15567
|
const node = attrs;
|
|
15015
|
-
if (node.type !==
|
|
15568
|
+
if (node.type !== import_types60.NodeType.RouteNode) return;
|
|
15016
15569
|
const route = attrs;
|
|
15017
15570
|
if (route.service !== serviceName) return;
|
|
15018
15571
|
entries.push({
|
|
@@ -15044,7 +15597,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
15044
15597
|
return {
|
|
15045
15598
|
targetNodeId: match.routeNodeId,
|
|
15046
15599
|
serviceName,
|
|
15047
|
-
edgeType:
|
|
15600
|
+
edgeType: import_types60.EdgeType.CALLS
|
|
15048
15601
|
};
|
|
15049
15602
|
};
|
|
15050
15603
|
}
|
|
@@ -15071,7 +15624,7 @@ init_cjs_shims();
|
|
|
15071
15624
|
|
|
15072
15625
|
// src/connectors/cloudflare/connector.ts
|
|
15073
15626
|
init_cjs_shims();
|
|
15074
|
-
var
|
|
15627
|
+
var import_types62 = require("@neat.is/types");
|
|
15075
15628
|
|
|
15076
15629
|
// src/connectors/cloudflare/client.ts
|
|
15077
15630
|
init_cjs_shims();
|
|
@@ -15187,7 +15740,7 @@ function mapEventToSignal(event) {
|
|
|
15187
15740
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
15188
15741
|
const statusCode = metadata?.statusCode;
|
|
15189
15742
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
15190
|
-
const
|
|
15743
|
+
const path68 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
15191
15744
|
return {
|
|
15192
15745
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
15193
15746
|
targetName: scriptName,
|
|
@@ -15195,7 +15748,7 @@ function mapEventToSignal(event) {
|
|
|
15195
15748
|
errorCount: isError ? 1 : 0,
|
|
15196
15749
|
lastObservedIso: observedAt.toISOString(),
|
|
15197
15750
|
method,
|
|
15198
|
-
...
|
|
15751
|
+
...path68 ? { path: path68 } : {},
|
|
15199
15752
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
15200
15753
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
15201
15754
|
};
|
|
@@ -15235,19 +15788,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
15235
15788
|
graph.forEachNode((id, attrs) => {
|
|
15236
15789
|
if (found) return;
|
|
15237
15790
|
const a = attrs;
|
|
15238
|
-
if (a.type ===
|
|
15791
|
+
if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
15239
15792
|
found = id;
|
|
15240
15793
|
}
|
|
15241
15794
|
});
|
|
15242
15795
|
return found;
|
|
15243
15796
|
}
|
|
15244
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
15245
|
-
const normalizedPath = normalizePathTemplate(
|
|
15797
|
+
function findMatchingRouteNode(graph, serviceName, method, path68) {
|
|
15798
|
+
const normalizedPath = normalizePathTemplate(path68);
|
|
15246
15799
|
let found = null;
|
|
15247
15800
|
graph.forEachNode((id, attrs) => {
|
|
15248
15801
|
if (found) return;
|
|
15249
15802
|
const a = attrs;
|
|
15250
|
-
if (a.type !==
|
|
15803
|
+
if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
|
|
15251
15804
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
15252
15805
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
15253
15806
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -15259,18 +15812,18 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
15259
15812
|
return (signal) => {
|
|
15260
15813
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
15261
15814
|
const scriptName = signal.targetName;
|
|
15262
|
-
const { method, path:
|
|
15815
|
+
const { method, path: path68 } = signal;
|
|
15263
15816
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
15264
|
-
if (!method || !
|
|
15265
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
15817
|
+
if (!method || !path68) return wholeFileId;
|
|
15818
|
+
return findMatchingRouteNode(graph, serviceName, method, path68) ?? wholeFileId;
|
|
15266
15819
|
};
|
|
15267
15820
|
const mapping = config.workers?.[scriptName];
|
|
15268
15821
|
if (mapping) {
|
|
15269
|
-
const wholeFileId = (0,
|
|
15822
|
+
const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
|
|
15270
15823
|
return {
|
|
15271
15824
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
15272
15825
|
serviceName: mapping.service,
|
|
15273
|
-
edgeType:
|
|
15826
|
+
edgeType: import_types62.EdgeType.CALLS
|
|
15274
15827
|
};
|
|
15275
15828
|
}
|
|
15276
15829
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -15279,13 +15832,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
15279
15832
|
return {
|
|
15280
15833
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
15281
15834
|
serviceName: fileNode.service,
|
|
15282
|
-
edgeType:
|
|
15835
|
+
edgeType: import_types62.EdgeType.CALLS
|
|
15283
15836
|
};
|
|
15284
15837
|
}
|
|
15285
15838
|
return {
|
|
15286
|
-
targetNodeId: (0,
|
|
15839
|
+
targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
|
|
15287
15840
|
serviceName: scriptName,
|
|
15288
|
-
edgeType:
|
|
15841
|
+
edgeType: import_types62.EdgeType.CALLS,
|
|
15289
15842
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
15290
15843
|
};
|
|
15291
15844
|
};
|
|
@@ -15481,14 +16034,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
15481
16034
|
|
|
15482
16035
|
// src/connectors/neon/resolve.ts
|
|
15483
16036
|
init_cjs_shims();
|
|
15484
|
-
var
|
|
16037
|
+
var import_types66 = require("@neat.is/types");
|
|
15485
16038
|
function createNeonResolveTarget(config) {
|
|
15486
16039
|
return (signal) => {
|
|
15487
16040
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
15488
16041
|
return {
|
|
15489
|
-
targetNodeId: (0,
|
|
16042
|
+
targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
|
|
15490
16043
|
serviceName: config.serviceName,
|
|
15491
|
-
edgeType:
|
|
16044
|
+
edgeType: import_types66.EdgeType.CALLS,
|
|
15492
16045
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
15493
16046
|
};
|
|
15494
16047
|
};
|
|
@@ -15614,9 +16167,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
15614
16167
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
15615
16168
|
if (secondSep === -1) return null;
|
|
15616
16169
|
const method = rest.slice(0, secondSep);
|
|
15617
|
-
const
|
|
15618
|
-
if (!serviceName || !method || !
|
|
15619
|
-
return { serviceName, method, path:
|
|
16170
|
+
const path68 = rest.slice(secondSep + 1);
|
|
16171
|
+
if (!serviceName || !method || !path68) return null;
|
|
16172
|
+
return { serviceName, method, path: path68 };
|
|
15620
16173
|
}
|
|
15621
16174
|
|
|
15622
16175
|
// src/connectors/cloud-run/map.ts
|
|
@@ -15645,14 +16198,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
15645
16198
|
if (!req) return null;
|
|
15646
16199
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
15647
16200
|
const method = req.requestMethod.toUpperCase();
|
|
15648
|
-
const
|
|
15649
|
-
if (
|
|
16201
|
+
const path68 = pathFromRequestUrl2(req.requestUrl);
|
|
16202
|
+
if (path68 === null) return null;
|
|
15650
16203
|
const timestamp = entry.timestamp;
|
|
15651
16204
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
15652
16205
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
15653
16206
|
return {
|
|
15654
16207
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
15655
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
16208
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path68 }),
|
|
15656
16209
|
callCount: 1,
|
|
15657
16210
|
errorCount: isError ? 1 : 0,
|
|
15658
16211
|
lastObservedIso: timestamp
|
|
@@ -15669,14 +16222,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
15669
16222
|
|
|
15670
16223
|
// src/connectors/cloud-run/resolve.ts
|
|
15671
16224
|
init_cjs_shims();
|
|
15672
|
-
var
|
|
16225
|
+
var import_types70 = require("@neat.is/types");
|
|
15673
16226
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
15674
16227
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
15675
16228
|
let found = null;
|
|
15676
16229
|
graph.forEachNode((_id, attrs) => {
|
|
15677
16230
|
if (found) return;
|
|
15678
16231
|
const node = attrs;
|
|
15679
|
-
if (node.type !==
|
|
16232
|
+
if (node.type !== import_types70.NodeType.RouteNode) return;
|
|
15680
16233
|
const route = attrs;
|
|
15681
16234
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
15682
16235
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -15691,23 +16244,23 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
15691
16244
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
15692
16245
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
15693
16246
|
if (!identity) return null;
|
|
15694
|
-
const { serviceName: gcpServiceName, method, path:
|
|
16247
|
+
const { serviceName: gcpServiceName, method, path: path68 } = identity;
|
|
15695
16248
|
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
15696
16249
|
if (mappedService) {
|
|
15697
16250
|
const routeNodeId = findMatchingRouteNode2(
|
|
15698
16251
|
graph,
|
|
15699
16252
|
mappedService,
|
|
15700
16253
|
method,
|
|
15701
|
-
normalizePathTemplate(
|
|
16254
|
+
normalizePathTemplate(path68)
|
|
15702
16255
|
);
|
|
15703
16256
|
if (routeNodeId) {
|
|
15704
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
16257
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
|
|
15705
16258
|
}
|
|
15706
16259
|
}
|
|
15707
16260
|
return {
|
|
15708
|
-
targetNodeId: (0,
|
|
16261
|
+
targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
15709
16262
|
serviceName: mappedService ?? gcpServiceName,
|
|
15710
|
-
edgeType:
|
|
16263
|
+
edgeType: import_types70.EdgeType.CALLS,
|
|
15711
16264
|
ensureInfraNode: {
|
|
15712
16265
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
15713
16266
|
name: gcpServiceName,
|
|
@@ -15748,7 +16301,7 @@ function createCloudRunConnector(graph, config = {}) {
|
|
|
15748
16301
|
|
|
15749
16302
|
// src/connectors/render/index.ts
|
|
15750
16303
|
init_cjs_shims();
|
|
15751
|
-
var
|
|
16304
|
+
var import_types73 = require("@neat.is/types");
|
|
15752
16305
|
|
|
15753
16306
|
// src/connectors/render/types.ts
|
|
15754
16307
|
init_cjs_shims();
|
|
@@ -15826,7 +16379,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
15826
16379
|
const out = [];
|
|
15827
16380
|
graph.forEachNode((_id, attrs) => {
|
|
15828
16381
|
const node = attrs;
|
|
15829
|
-
if (node.type !==
|
|
16382
|
+
if (node.type !== import_types73.NodeType.RouteNode) return;
|
|
15830
16383
|
const route = attrs;
|
|
15831
16384
|
if (route.service !== serviceName) return;
|
|
15832
16385
|
out.push({
|
|
@@ -15911,7 +16464,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
15911
16464
|
function createRenderResolveTarget(config) {
|
|
15912
16465
|
return (signal) => {
|
|
15913
16466
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
15914
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
16467
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
|
|
15915
16468
|
}
|
|
15916
16469
|
return null;
|
|
15917
16470
|
};
|
|
@@ -16049,21 +16602,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
16049
16602
|
|
|
16050
16603
|
// src/connectors/planetscale/resolve.ts
|
|
16051
16604
|
init_cjs_shims();
|
|
16052
|
-
var
|
|
16605
|
+
var import_types77 = require("@neat.is/types");
|
|
16053
16606
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
16054
16607
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
16055
16608
|
const databaseName = `${config.organization}/${config.database}`;
|
|
16056
16609
|
return (signal, _ctx) => {
|
|
16057
16610
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
16058
|
-
const tableId = (0,
|
|
16611
|
+
const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
|
|
16059
16612
|
if (graph.hasNode(tableId)) {
|
|
16060
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
16613
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
|
|
16061
16614
|
}
|
|
16062
|
-
const providerId = (0,
|
|
16615
|
+
const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
16063
16616
|
return {
|
|
16064
16617
|
targetNodeId: providerId,
|
|
16065
16618
|
serviceName: config.serviceName,
|
|
16066
|
-
edgeType:
|
|
16619
|
+
edgeType: import_types77.EdgeType.CALLS,
|
|
16067
16620
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
16068
16621
|
};
|
|
16069
16622
|
};
|
|
@@ -16691,11 +17244,11 @@ function registerRoutes(scope, ctx) {
|
|
|
16691
17244
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
16692
17245
|
const parsed = [];
|
|
16693
17246
|
for (const c of candidates) {
|
|
16694
|
-
const r =
|
|
17247
|
+
const r = import_types80.DivergenceTypeSchema.safeParse(c);
|
|
16695
17248
|
if (!r.success) {
|
|
16696
17249
|
return reply.code(400).send({
|
|
16697
17250
|
error: `unknown divergence type "${c}"`,
|
|
16698
|
-
allowed:
|
|
17251
|
+
allowed: import_types80.DivergenceTypeSchema.options
|
|
16699
17252
|
});
|
|
16700
17253
|
}
|
|
16701
17254
|
parsed.push(r.data);
|
|
@@ -17004,7 +17557,7 @@ function registerRoutes(scope, ctx) {
|
|
|
17004
17557
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
17005
17558
|
let violations = await log.readAll();
|
|
17006
17559
|
if (req.query.severity) {
|
|
17007
|
-
const sev =
|
|
17560
|
+
const sev = import_types80.PolicySeveritySchema.safeParse(req.query.severity);
|
|
17008
17561
|
if (!sev.success) {
|
|
17009
17562
|
return reply.code(400).send({
|
|
17010
17563
|
error: "invalid severity",
|
|
@@ -17043,7 +17596,7 @@ function registerRoutes(scope, ctx) {
|
|
|
17043
17596
|
scope.post("/policies/check", async (req, reply) => {
|
|
17044
17597
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
17045
17598
|
if (!proj) return;
|
|
17046
|
-
const parsed =
|
|
17599
|
+
const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
17047
17600
|
if (!parsed.success) {
|
|
17048
17601
|
return reply.code(400).send({
|
|
17049
17602
|
error: "invalid /policies/check body",
|
|
@@ -17365,7 +17918,7 @@ init_otel_grpc();
|
|
|
17365
17918
|
// src/search.ts
|
|
17366
17919
|
init_cjs_shims();
|
|
17367
17920
|
var import_node_fs32 = require("fs");
|
|
17368
|
-
var
|
|
17921
|
+
var import_node_path66 = __toESM(require("path"), 1);
|
|
17369
17922
|
var import_node_crypto4 = require("crypto");
|
|
17370
17923
|
var DEFAULT_LIMIT = 10;
|
|
17371
17924
|
var NOMIC_DIM = 768;
|
|
@@ -17528,7 +18081,7 @@ async function readCache(cachePath) {
|
|
|
17528
18081
|
}
|
|
17529
18082
|
}
|
|
17530
18083
|
async function writeCache(cachePath, cache) {
|
|
17531
|
-
await import_node_fs32.promises.mkdir(
|
|
18084
|
+
await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(cachePath), { recursive: true });
|
|
17532
18085
|
await import_node_fs32.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
17533
18086
|
}
|
|
17534
18087
|
var VectorIndex = class {
|
|
@@ -17709,14 +18262,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
17709
18262
|
async function main() {
|
|
17710
18263
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
17711
18264
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
17712
|
-
const baseDir = baseDirEnv ?
|
|
17713
|
-
const defaultScanPath =
|
|
18265
|
+
const baseDir = baseDirEnv ? import_node_path67.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path67.default.resolve(import_node_path67.default.dirname(legacyOutPath)) : import_node_path67.default.resolve("./neat-out");
|
|
18266
|
+
const defaultScanPath = import_node_path67.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
17714
18267
|
const registry = new Projects();
|
|
17715
18268
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
17716
18269
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
17717
18270
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
17718
18271
|
const projectScan = process.env[envKey];
|
|
17719
|
-
await bootProject(registry, name, projectScan ?
|
|
18272
|
+
await bootProject(registry, name, projectScan ? import_node_path67.default.resolve(projectScan) : void 0, baseDir);
|
|
17720
18273
|
}
|
|
17721
18274
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
17722
18275
|
const port = Number(process.env.PORT ?? 8080);
|