@neat.is/core 0.7.2 → 0.7.3
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-MDBE23Y3.js → chunk-3SBEY67S.js} +2 -2
- package/dist/{chunk-RR4LWQQB.js → chunk-BY6ZP5WA.js} +267 -139
- package/dist/chunk-BY6ZP5WA.js.map +1 -0
- package/dist/cli.cjs +600 -471
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +425 -296
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +434 -305
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +357 -228
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-RR4LWQQB.js.map +0 -1
- /package/dist/{chunk-MDBE23Y3.js.map → chunk-3SBEY67S.js.map} +0 -0
|
@@ -3503,19 +3503,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
3503
3503
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
3504
3504
|
let best = { path: [start], edges: [] };
|
|
3505
3505
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
3506
|
-
function step(node,
|
|
3507
|
-
if (
|
|
3508
|
-
best = { path: [...
|
|
3506
|
+
function step(node, path56, edges) {
|
|
3507
|
+
if (path56.length > best.path.length) {
|
|
3508
|
+
best = { path: [...path56], edges: [...edges] };
|
|
3509
3509
|
}
|
|
3510
|
-
if (
|
|
3510
|
+
if (path56.length - 1 >= maxDepth) return;
|
|
3511
3511
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
3512
3512
|
for (const [srcId, edge] of incoming) {
|
|
3513
3513
|
if (visited.has(srcId)) continue;
|
|
3514
3514
|
visited.add(srcId);
|
|
3515
|
-
|
|
3515
|
+
path56.push(srcId);
|
|
3516
3516
|
edges.push(edge);
|
|
3517
|
-
step(srcId,
|
|
3518
|
-
|
|
3517
|
+
step(srcId, path56, edges);
|
|
3518
|
+
path56.pop();
|
|
3519
3519
|
edges.pop();
|
|
3520
3520
|
visited.delete(srcId);
|
|
3521
3521
|
}
|
|
@@ -3722,26 +3722,26 @@ function dominantFailingCall(graph, serviceId7, visited) {
|
|
|
3722
3722
|
return best;
|
|
3723
3723
|
}
|
|
3724
3724
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
3725
|
-
const
|
|
3725
|
+
const path56 = [originServiceId];
|
|
3726
3726
|
const edges = [];
|
|
3727
3727
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
3728
3728
|
let current = originServiceId;
|
|
3729
3729
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
3730
3730
|
const hop = dominantFailingCall(graph, current, visited);
|
|
3731
3731
|
if (!hop) break;
|
|
3732
|
-
|
|
3732
|
+
path56.push(hop.nextService);
|
|
3733
3733
|
edges.push(hop.edge);
|
|
3734
3734
|
visited.add(hop.nextService);
|
|
3735
3735
|
current = hop.nextService;
|
|
3736
3736
|
}
|
|
3737
3737
|
if (edges.length === 0) return null;
|
|
3738
|
-
return { path:
|
|
3738
|
+
return { path: path56, edges, culprit: current };
|
|
3739
3739
|
}
|
|
3740
3740
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
3741
3741
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
3742
3742
|
if (!chain) return null;
|
|
3743
3743
|
const culprit = chain.culprit;
|
|
3744
|
-
const
|
|
3744
|
+
const path56 = [...chain.path];
|
|
3745
3745
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
3746
3746
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
3747
3747
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -3749,14 +3749,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
3749
3749
|
if (loc) {
|
|
3750
3750
|
let rootCauseNode = culprit;
|
|
3751
3751
|
if (loc.fileNode) {
|
|
3752
|
-
|
|
3752
|
+
path56.push(loc.fileNode);
|
|
3753
3753
|
edgeProvenances.push(Provenance5.OBSERVED);
|
|
3754
3754
|
rootCauseNode = loc.fileNode;
|
|
3755
3755
|
}
|
|
3756
3756
|
return RootCauseResultSchema.parse({
|
|
3757
3757
|
rootCauseNode,
|
|
3758
3758
|
rootCauseReason: loc.rootCauseReason,
|
|
3759
|
-
traversalPath:
|
|
3759
|
+
traversalPath: path56,
|
|
3760
3760
|
edgeProvenances,
|
|
3761
3761
|
confidence,
|
|
3762
3762
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -3768,7 +3768,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
3768
3768
|
return RootCauseResultSchema.parse({
|
|
3769
3769
|
rootCauseNode: culprit,
|
|
3770
3770
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
3771
|
-
traversalPath:
|
|
3771
|
+
traversalPath: path56,
|
|
3772
3772
|
edgeProvenances,
|
|
3773
3773
|
confidence,
|
|
3774
3774
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -7692,11 +7692,138 @@ function drizzleEndpointsFromFile(file, serviceDir) {
|
|
|
7692
7692
|
return out;
|
|
7693
7693
|
}
|
|
7694
7694
|
|
|
7695
|
-
// src/extract/calls/
|
|
7695
|
+
// src/extract/calls/prisma.ts
|
|
7696
7696
|
import path38 from "path";
|
|
7697
|
+
import { infraId as infraId11 } from "@neat.is/types";
|
|
7698
|
+
var SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
7699
|
+
"Int",
|
|
7700
|
+
"String",
|
|
7701
|
+
"Boolean",
|
|
7702
|
+
"DateTime",
|
|
7703
|
+
"Float",
|
|
7704
|
+
"Decimal",
|
|
7705
|
+
"BigInt",
|
|
7706
|
+
"Bytes",
|
|
7707
|
+
"Json"
|
|
7708
|
+
]);
|
|
7709
|
+
function stripLineComment(line) {
|
|
7710
|
+
let inStr = false;
|
|
7711
|
+
for (let i = 0; i < line.length; i++) {
|
|
7712
|
+
const ch = line[i];
|
|
7713
|
+
if (ch === '"' && line[i - 1] !== "\\") inStr = !inStr;
|
|
7714
|
+
else if (!inStr && ch === "/" && line[i + 1] === "/") return line.slice(0, i);
|
|
7715
|
+
}
|
|
7716
|
+
return line;
|
|
7717
|
+
}
|
|
7718
|
+
function netBraces(line) {
|
|
7719
|
+
const s = stripLineComment(line).replace(/"(\\.|[^"\\])*"/g, "");
|
|
7720
|
+
let n = 0;
|
|
7721
|
+
for (const ch of s) {
|
|
7722
|
+
if (ch === "{") n++;
|
|
7723
|
+
else if (ch === "}") n--;
|
|
7724
|
+
}
|
|
7725
|
+
return n;
|
|
7726
|
+
}
|
|
7727
|
+
function prismaColumnsFromSchema(file, serviceDir) {
|
|
7728
|
+
const content = file.content;
|
|
7729
|
+
if (!/\bmodel\s+[A-Za-z_]\w*\s*\{/.test(content)) return [];
|
|
7730
|
+
const lines = content.split("\n");
|
|
7731
|
+
const modelNames = /* @__PURE__ */ new Set();
|
|
7732
|
+
const enumNames = /* @__PURE__ */ new Set();
|
|
7733
|
+
for (const line of lines) {
|
|
7734
|
+
const m = line.match(/^\s*model\s+([A-Za-z_]\w*)\s*\{/);
|
|
7735
|
+
if (m) modelNames.add(m[1]);
|
|
7736
|
+
const e = line.match(/^\s*enum\s+([A-Za-z_]\w*)\s*\{/);
|
|
7737
|
+
if (e) enumNames.add(e[1]);
|
|
7738
|
+
}
|
|
7739
|
+
const out = [];
|
|
7740
|
+
const seenTable = /* @__PURE__ */ new Set();
|
|
7741
|
+
const finalize = (b) => {
|
|
7742
|
+
if (seenTable.has(b.tableName)) return;
|
|
7743
|
+
seenTable.add(b.tableName);
|
|
7744
|
+
out.push({
|
|
7745
|
+
infraId: infraId11("sql-table", b.tableName),
|
|
7746
|
+
name: b.tableName,
|
|
7747
|
+
kind: "sql-table",
|
|
7748
|
+
edgeType: "CALLS",
|
|
7749
|
+
confidenceKind: "structural",
|
|
7750
|
+
columns: b.columns,
|
|
7751
|
+
evidence: {
|
|
7752
|
+
file: path38.relative(serviceDir, file.path),
|
|
7753
|
+
line: b.startLine,
|
|
7754
|
+
snippet: snippet(content, b.startLine)
|
|
7755
|
+
}
|
|
7756
|
+
});
|
|
7757
|
+
};
|
|
7758
|
+
let current = null;
|
|
7759
|
+
let depth = 0;
|
|
7760
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7761
|
+
const raw = lines[i];
|
|
7762
|
+
const lineNo = i + 1;
|
|
7763
|
+
if (current === null) {
|
|
7764
|
+
const header = raw.match(/^\s*(model|enum|datasource|generator|type)\s+([A-Za-z_]\w*)\b/);
|
|
7765
|
+
if (header && raw.includes("{")) {
|
|
7766
|
+
current = {
|
|
7767
|
+
kind: header[1],
|
|
7768
|
+
tableName: header[2],
|
|
7769
|
+
startLine: lineNo,
|
|
7770
|
+
columns: [],
|
|
7771
|
+
seenCol: /* @__PURE__ */ new Set()
|
|
7772
|
+
};
|
|
7773
|
+
depth = netBraces(raw);
|
|
7774
|
+
if (depth <= 0) {
|
|
7775
|
+
if (current.kind === "model") finalize(current);
|
|
7776
|
+
current = null;
|
|
7777
|
+
}
|
|
7778
|
+
}
|
|
7779
|
+
continue;
|
|
7780
|
+
}
|
|
7781
|
+
depth += netBraces(raw);
|
|
7782
|
+
if (depth <= 0) {
|
|
7783
|
+
if (current.kind === "model") finalize(current);
|
|
7784
|
+
current = null;
|
|
7785
|
+
continue;
|
|
7786
|
+
}
|
|
7787
|
+
if (current.kind !== "model") continue;
|
|
7788
|
+
const trimmed = stripLineComment(raw).trim();
|
|
7789
|
+
if (!trimmed) continue;
|
|
7790
|
+
if (trimmed.startsWith("@@")) {
|
|
7791
|
+
const map = trimmed.match(/@@map\(\s*"([^"]+)"\s*\)/);
|
|
7792
|
+
if (map) current.tableName = map[1];
|
|
7793
|
+
continue;
|
|
7794
|
+
}
|
|
7795
|
+
const fm = trimmed.match(/^([A-Za-z_]\w*)\s+([A-Za-z_]\w*)/);
|
|
7796
|
+
if (!fm) continue;
|
|
7797
|
+
const fieldName = fm[1];
|
|
7798
|
+
const baseType = fm[2];
|
|
7799
|
+
if (/@relation\b/.test(trimmed) || modelNames.has(baseType)) continue;
|
|
7800
|
+
if (!enumNames.has(baseType) && !SCALAR_TYPES.has(baseType)) continue;
|
|
7801
|
+
const mapM = trimmed.match(/(?<!@)@map\(\s*"([^"]+)"\s*\)/);
|
|
7802
|
+
const columnName = mapM ? mapM[1] : fieldName;
|
|
7803
|
+
if (!current.seenCol.has(columnName)) {
|
|
7804
|
+
current.seenCol.add(columnName);
|
|
7805
|
+
current.columns.push(columnName);
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7808
|
+
if (current && current.kind === "model") finalize(current);
|
|
7809
|
+
return out;
|
|
7810
|
+
}
|
|
7811
|
+
async function prismaColumnEndpoints(serviceDir) {
|
|
7812
|
+
const schemaPath = await findFirst(serviceDir, [
|
|
7813
|
+
path38.join("prisma", "schema.prisma"),
|
|
7814
|
+
"schema.prisma"
|
|
7815
|
+
]);
|
|
7816
|
+
if (!schemaPath) return [];
|
|
7817
|
+
const content = await readIfExists(schemaPath);
|
|
7818
|
+
if (!content) return [];
|
|
7819
|
+
return prismaColumnsFromSchema({ path: schemaPath, content }, serviceDir);
|
|
7820
|
+
}
|
|
7821
|
+
|
|
7822
|
+
// src/extract/calls/go.ts
|
|
7823
|
+
import path39 from "path";
|
|
7697
7824
|
import Parser10 from "tree-sitter";
|
|
7698
7825
|
import Go3 from "tree-sitter-go";
|
|
7699
|
-
import { infraId as
|
|
7826
|
+
import { infraId as infraId12 } from "@neat.is/types";
|
|
7700
7827
|
var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
|
|
7701
7828
|
var PARSE_CHUNK8 = 16384;
|
|
7702
7829
|
function walk5(node, visit) {
|
|
@@ -7707,7 +7834,7 @@ function walk5(node, visit) {
|
|
|
7707
7834
|
}
|
|
7708
7835
|
}
|
|
7709
7836
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
7710
|
-
if (
|
|
7837
|
+
if (path39.extname(file.path) !== ".go") return [];
|
|
7711
7838
|
const parser = new Parser10();
|
|
7712
7839
|
parser.setLanguage(Go3);
|
|
7713
7840
|
const tree = parser.parse(
|
|
@@ -7727,12 +7854,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
7727
7854
|
if (!table) return;
|
|
7728
7855
|
const line = node.startPosition.row + 1;
|
|
7729
7856
|
out.push({
|
|
7730
|
-
infraId:
|
|
7857
|
+
infraId: infraId12("sql-table", table),
|
|
7731
7858
|
name: table,
|
|
7732
7859
|
kind: "sql-table",
|
|
7733
7860
|
edgeType: "CALLS",
|
|
7734
7861
|
confidenceKind: "verified-call-site",
|
|
7735
|
-
evidence: { file: toPosix(
|
|
7862
|
+
evidence: { file: toPosix(path39.relative(serviceDir, file.path)), line, snippet: snippet(file.content, line) }
|
|
7736
7863
|
});
|
|
7737
7864
|
});
|
|
7738
7865
|
return out;
|
|
@@ -7781,6 +7908,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
7781
7908
|
}
|
|
7782
7909
|
endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
|
|
7783
7910
|
endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
|
|
7911
|
+
endpoints.push(...await prismaColumnEndpoints(service.dir));
|
|
7784
7912
|
if (endpoints.length === 0) continue;
|
|
7785
7913
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
7786
7914
|
for (const ep of endpoints) {
|
|
@@ -7865,14 +7993,14 @@ async function addCallEdges(graph, services) {
|
|
|
7865
7993
|
}
|
|
7866
7994
|
|
|
7867
7995
|
// src/extract/infra/docker-compose.ts
|
|
7868
|
-
import
|
|
7996
|
+
import path40 from "path";
|
|
7869
7997
|
import { EdgeType as EdgeType15, Provenance as Provenance16, confidenceForExtracted as confidenceForExtracted13 } from "@neat.is/types";
|
|
7870
7998
|
|
|
7871
7999
|
// src/extract/infra/shared.ts
|
|
7872
|
-
import { NodeType as NodeType16, Provenance as Provenance15, confidenceForExtracted as confidenceForExtracted12, infraId as
|
|
8000
|
+
import { NodeType as NodeType16, Provenance as Provenance15, confidenceForExtracted as confidenceForExtracted12, infraId as infraId13 } from "@neat.is/types";
|
|
7873
8001
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
7874
8002
|
return {
|
|
7875
|
-
id:
|
|
8003
|
+
id: infraId13(kind, name),
|
|
7876
8004
|
type: NodeType16.InfraNode,
|
|
7877
8005
|
name,
|
|
7878
8006
|
provider,
|
|
@@ -7935,7 +8063,7 @@ function dependsOnList(value) {
|
|
|
7935
8063
|
}
|
|
7936
8064
|
function serviceNameToServiceNode(name, services) {
|
|
7937
8065
|
for (const s of services) {
|
|
7938
|
-
if (s.node.name === name ||
|
|
8066
|
+
if (s.node.name === name || path40.basename(s.dir) === name) return s.node.id;
|
|
7939
8067
|
}
|
|
7940
8068
|
return null;
|
|
7941
8069
|
}
|
|
@@ -7944,7 +8072,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7944
8072
|
let edgesAdded = 0;
|
|
7945
8073
|
let composePath = null;
|
|
7946
8074
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
7947
|
-
const abs =
|
|
8075
|
+
const abs = path40.join(scanPath, name);
|
|
7948
8076
|
if (await exists(abs)) {
|
|
7949
8077
|
composePath = abs;
|
|
7950
8078
|
break;
|
|
@@ -7957,13 +8085,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
7957
8085
|
} catch (err) {
|
|
7958
8086
|
recordExtractionError(
|
|
7959
8087
|
"infra docker-compose",
|
|
7960
|
-
|
|
8088
|
+
path40.relative(scanPath, composePath),
|
|
7961
8089
|
err
|
|
7962
8090
|
);
|
|
7963
8091
|
return { nodesAdded, edgesAdded };
|
|
7964
8092
|
}
|
|
7965
8093
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
7966
|
-
const evidenceFile =
|
|
8094
|
+
const evidenceFile = path40.relative(scanPath, composePath).split(path40.sep).join("/");
|
|
7967
8095
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
7968
8096
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
7969
8097
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -8004,7 +8132,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
8004
8132
|
}
|
|
8005
8133
|
|
|
8006
8134
|
// src/extract/infra/dockerfile.ts
|
|
8007
|
-
import
|
|
8135
|
+
import path41 from "path";
|
|
8008
8136
|
import { promises as fs17 } from "fs";
|
|
8009
8137
|
import { EdgeType as EdgeType16, Provenance as Provenance17, confidenceForExtracted as confidenceForExtracted14 } from "@neat.is/types";
|
|
8010
8138
|
function readDockerfile(content) {
|
|
@@ -8035,7 +8163,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8035
8163
|
let nodesAdded = 0;
|
|
8036
8164
|
let edgesAdded = 0;
|
|
8037
8165
|
for (const service of services) {
|
|
8038
|
-
const dockerfilePath =
|
|
8166
|
+
const dockerfilePath = path41.join(service.dir, "Dockerfile");
|
|
8039
8167
|
if (!await exists(dockerfilePath)) continue;
|
|
8040
8168
|
let content;
|
|
8041
8169
|
try {
|
|
@@ -8043,7 +8171,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8043
8171
|
} catch (err) {
|
|
8044
8172
|
recordExtractionError(
|
|
8045
8173
|
"infra dockerfile",
|
|
8046
|
-
|
|
8174
|
+
path41.relative(scanPath, dockerfilePath),
|
|
8047
8175
|
err
|
|
8048
8176
|
);
|
|
8049
8177
|
continue;
|
|
@@ -8055,8 +8183,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8055
8183
|
graph.addNode(node.id, node);
|
|
8056
8184
|
nodesAdded++;
|
|
8057
8185
|
}
|
|
8058
|
-
const relDockerfile = toPosix(
|
|
8059
|
-
const evidenceFile = toPosix(
|
|
8186
|
+
const relDockerfile = toPosix(path41.relative(service.dir, dockerfilePath));
|
|
8187
|
+
const evidenceFile = toPosix(path41.relative(scanPath, dockerfilePath));
|
|
8060
8188
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
8061
8189
|
graph,
|
|
8062
8190
|
service.pkg.name,
|
|
@@ -8108,7 +8236,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
8108
8236
|
|
|
8109
8237
|
// src/extract/infra/terraform.ts
|
|
8110
8238
|
import { promises as fs18 } from "fs";
|
|
8111
|
-
import
|
|
8239
|
+
import path42 from "path";
|
|
8112
8240
|
import { EdgeType as EdgeType17, Provenance as Provenance18, confidenceForExtracted as confidenceForExtracted15 } from "@neat.is/types";
|
|
8113
8241
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
8114
8242
|
var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
|
|
@@ -8119,11 +8247,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
8119
8247
|
for (const entry of entries) {
|
|
8120
8248
|
if (entry.isDirectory()) {
|
|
8121
8249
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
8122
|
-
const child =
|
|
8250
|
+
const child = path42.join(start, entry.name);
|
|
8123
8251
|
if (await isPythonVenvDir(child)) continue;
|
|
8124
8252
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
8125
8253
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
8126
|
-
out.push(
|
|
8254
|
+
out.push(path42.join(start, entry.name));
|
|
8127
8255
|
}
|
|
8128
8256
|
}
|
|
8129
8257
|
return out;
|
|
@@ -8155,7 +8283,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8155
8283
|
const files = await walkTfFiles(scanPath);
|
|
8156
8284
|
for (const file of files) {
|
|
8157
8285
|
const content = await fs18.readFile(file, "utf8");
|
|
8158
|
-
const evidenceFile = toPosix(
|
|
8286
|
+
const evidenceFile = toPosix(path42.relative(scanPath, file));
|
|
8159
8287
|
const resources = [];
|
|
8160
8288
|
const byKey = /* @__PURE__ */ new Map();
|
|
8161
8289
|
RESOURCE_RE.lastIndex = 0;
|
|
@@ -8212,7 +8340,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
8212
8340
|
|
|
8213
8341
|
// src/extract/infra/k8s.ts
|
|
8214
8342
|
import { promises as fs19 } from "fs";
|
|
8215
|
-
import
|
|
8343
|
+
import path43 from "path";
|
|
8216
8344
|
import { parseAllDocuments as parseAllDocuments2 } from "yaml";
|
|
8217
8345
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
8218
8346
|
Service: "k8s-service",
|
|
@@ -8230,11 +8358,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
8230
8358
|
for (const entry of entries) {
|
|
8231
8359
|
if (entry.isDirectory()) {
|
|
8232
8360
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
8233
|
-
const child =
|
|
8361
|
+
const child = path43.join(start, entry.name);
|
|
8234
8362
|
if (await isPythonVenvDir(child)) continue;
|
|
8235
8363
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
8236
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
8237
|
-
out.push(
|
|
8364
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path43.extname(entry.name))) {
|
|
8365
|
+
out.push(path43.join(start, entry.name));
|
|
8238
8366
|
}
|
|
8239
8367
|
}
|
|
8240
8368
|
return out;
|
|
@@ -8267,13 +8395,13 @@ async function addK8sResources(graph, scanPath) {
|
|
|
8267
8395
|
|
|
8268
8396
|
// src/extract/infra/cloudflare.ts
|
|
8269
8397
|
import { promises as fs20 } from "fs";
|
|
8270
|
-
import
|
|
8398
|
+
import path44 from "path";
|
|
8271
8399
|
import { parse as parseToml2 } from "smol-toml";
|
|
8272
8400
|
import { EdgeType as EdgeType18, Provenance as Provenance19, confidenceForExtracted as confidenceForExtracted16 } from "@neat.is/types";
|
|
8273
8401
|
var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
8274
8402
|
async function readWranglerConfig(dir) {
|
|
8275
8403
|
for (const filename of WRANGLER_FILENAMES) {
|
|
8276
|
-
const abs =
|
|
8404
|
+
const abs = path44.join(dir, filename);
|
|
8277
8405
|
if (!await exists(abs)) continue;
|
|
8278
8406
|
const raw = await fs20.readFile(abs, "utf8");
|
|
8279
8407
|
const config = filename === "wrangler.toml" ? parseToml2(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -8336,11 +8464,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8336
8464
|
try {
|
|
8337
8465
|
read = await readWranglerConfig(service.dir);
|
|
8338
8466
|
} catch (err) {
|
|
8339
|
-
recordExtractionError("infra cloudflare",
|
|
8467
|
+
recordExtractionError("infra cloudflare", path44.relative(scanPath, service.dir), err);
|
|
8340
8468
|
continue;
|
|
8341
8469
|
}
|
|
8342
8470
|
if (!read || !read.config.name) continue;
|
|
8343
|
-
const evidenceFile = toPosix(
|
|
8471
|
+
const evidenceFile = toPosix(path44.relative(scanPath, path44.join(service.dir, read.relFile)));
|
|
8344
8472
|
discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
|
|
8345
8473
|
}
|
|
8346
8474
|
for (const worker of discovered) {
|
|
@@ -8352,7 +8480,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8352
8480
|
}
|
|
8353
8481
|
let anchorId = service.node.id;
|
|
8354
8482
|
if (config.main) {
|
|
8355
|
-
const entryRelPath = toPosix(
|
|
8483
|
+
const entryRelPath = toPosix(path44.normalize(config.main));
|
|
8356
8484
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
8357
8485
|
graph,
|
|
8358
8486
|
service.pkg.name,
|
|
@@ -8499,12 +8627,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
|
|
|
8499
8627
|
|
|
8500
8628
|
// src/extract/infra/vercel.ts
|
|
8501
8629
|
import { promises as fs21 } from "fs";
|
|
8502
|
-
import
|
|
8630
|
+
import path45 from "path";
|
|
8503
8631
|
import { EdgeType as EdgeType19 } from "@neat.is/types";
|
|
8504
8632
|
var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
|
|
8505
8633
|
async function readVercelConfig(dir) {
|
|
8506
8634
|
for (const filename of VERCEL_CONFIG_FILENAMES) {
|
|
8507
|
-
const abs =
|
|
8635
|
+
const abs = path45.join(dir, filename);
|
|
8508
8636
|
if (!await exists(abs)) continue;
|
|
8509
8637
|
const raw = await fs21.readFile(abs, "utf8");
|
|
8510
8638
|
const config = JSON.parse(maskCommentsInSource(raw));
|
|
@@ -8513,7 +8641,7 @@ async function readVercelConfig(dir) {
|
|
|
8513
8641
|
return null;
|
|
8514
8642
|
}
|
|
8515
8643
|
async function readLinkedProjectName(dir) {
|
|
8516
|
-
const abs =
|
|
8644
|
+
const abs = path45.join(dir, ".vercel", "project.json");
|
|
8517
8645
|
if (!await exists(abs)) return void 0;
|
|
8518
8646
|
const parsed = JSON.parse(await fs21.readFile(abs, "utf8"));
|
|
8519
8647
|
return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
|
|
@@ -8531,7 +8659,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8531
8659
|
read = await readVercelConfig(service.dir);
|
|
8532
8660
|
projectName = await readLinkedProjectName(service.dir);
|
|
8533
8661
|
} catch (err) {
|
|
8534
|
-
recordExtractionError("infra vercel",
|
|
8662
|
+
recordExtractionError("infra vercel", path45.relative(scanPath, service.dir), err);
|
|
8535
8663
|
continue;
|
|
8536
8664
|
}
|
|
8537
8665
|
if (!read && !projectName) continue;
|
|
@@ -8547,7 +8675,7 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8547
8675
|
const anchorId = service.node.id;
|
|
8548
8676
|
if (!read) continue;
|
|
8549
8677
|
const { config, relFile, raw } = read;
|
|
8550
|
-
const evidenceFile = toPosix(
|
|
8678
|
+
const evidenceFile = toPosix(path45.relative(scanPath, path45.join(service.dir, relFile)));
|
|
8551
8679
|
const add = (edgeType, kind, name) => {
|
|
8552
8680
|
if (!name) return;
|
|
8553
8681
|
const result = emitPlatformResourceEdge(
|
|
@@ -8576,13 +8704,13 @@ async function addVercelServices(graph, services, scanPath) {
|
|
|
8576
8704
|
|
|
8577
8705
|
// src/extract/infra/railway.ts
|
|
8578
8706
|
import { promises as fs22 } from "fs";
|
|
8579
|
-
import
|
|
8707
|
+
import path46 from "path";
|
|
8580
8708
|
import { parse as parseToml3 } from "smol-toml";
|
|
8581
8709
|
import { EdgeType as EdgeType20 } from "@neat.is/types";
|
|
8582
8710
|
var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
|
|
8583
8711
|
async function readRailwayConfig(dir) {
|
|
8584
8712
|
for (const filename of RAILWAY_FILENAMES) {
|
|
8585
|
-
const abs =
|
|
8713
|
+
const abs = path46.join(dir, filename);
|
|
8586
8714
|
if (!await exists(abs)) continue;
|
|
8587
8715
|
const raw = await fs22.readFile(abs, "utf8");
|
|
8588
8716
|
const config = filename === "railway.toml" ? parseToml3(raw) : JSON.parse(maskCommentsInSource(raw));
|
|
@@ -8598,7 +8726,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
8598
8726
|
try {
|
|
8599
8727
|
read = await readRailwayConfig(service.dir);
|
|
8600
8728
|
} catch (err) {
|
|
8601
|
-
recordExtractionError("infra railway",
|
|
8729
|
+
recordExtractionError("infra railway", path46.relative(scanPath, service.dir), err);
|
|
8602
8730
|
continue;
|
|
8603
8731
|
}
|
|
8604
8732
|
if (!read) continue;
|
|
@@ -8608,7 +8736,7 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
8608
8736
|
}
|
|
8609
8737
|
const anchorId = service.node.id;
|
|
8610
8738
|
const { config, relFile, raw } = read;
|
|
8611
|
-
const evidenceFile = toPosix(
|
|
8739
|
+
const evidenceFile = toPosix(path46.relative(scanPath, path46.join(service.dir, relFile)));
|
|
8612
8740
|
const add = (edgeType, kind, name) => {
|
|
8613
8741
|
if (!name) return;
|
|
8614
8742
|
const result = emitPlatformResourceEdge(
|
|
@@ -8633,12 +8761,12 @@ async function addRailwayServices(graph, services, scanPath) {
|
|
|
8633
8761
|
|
|
8634
8762
|
// src/extract/infra/supabase.ts
|
|
8635
8763
|
import { promises as fs23 } from "fs";
|
|
8636
|
-
import
|
|
8764
|
+
import path47 from "path";
|
|
8637
8765
|
import { parse as parseToml4 } from "smol-toml";
|
|
8638
8766
|
import { EdgeType as EdgeType21 } from "@neat.is/types";
|
|
8639
8767
|
async function readSupabaseConfig(dir) {
|
|
8640
|
-
const relFile =
|
|
8641
|
-
const abs =
|
|
8768
|
+
const relFile = path47.join("supabase", "config.toml");
|
|
8769
|
+
const abs = path47.join(dir, relFile);
|
|
8642
8770
|
if (!await exists(abs)) return null;
|
|
8643
8771
|
const raw = await fs23.readFile(abs, "utf8");
|
|
8644
8772
|
const config = parseToml4(raw);
|
|
@@ -8652,7 +8780,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
8652
8780
|
try {
|
|
8653
8781
|
read = await readSupabaseConfig(service.dir);
|
|
8654
8782
|
} catch (err) {
|
|
8655
|
-
recordExtractionError("infra supabase",
|
|
8783
|
+
recordExtractionError("infra supabase", path47.relative(scanPath, service.dir), err);
|
|
8656
8784
|
continue;
|
|
8657
8785
|
}
|
|
8658
8786
|
if (!read) continue;
|
|
@@ -8667,7 +8795,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
|
|
|
8667
8795
|
});
|
|
8668
8796
|
}
|
|
8669
8797
|
const anchorId = service.node.id;
|
|
8670
|
-
const evidenceFile = toPosix(
|
|
8798
|
+
const evidenceFile = toPosix(path47.relative(scanPath, path47.join(service.dir, relFile)));
|
|
8671
8799
|
const add = (edgeType, kind, name) => {
|
|
8672
8800
|
if (!name) return;
|
|
8673
8801
|
const result = emitPlatformResourceEdge(
|
|
@@ -8708,11 +8836,11 @@ async function addInfra(graph, scanPath, services) {
|
|
|
8708
8836
|
}
|
|
8709
8837
|
|
|
8710
8838
|
// src/extract/index.ts
|
|
8711
|
-
import
|
|
8839
|
+
import path49 from "path";
|
|
8712
8840
|
|
|
8713
8841
|
// src/extract/retire.ts
|
|
8714
8842
|
import { existsSync as existsSync2 } from "fs";
|
|
8715
|
-
import
|
|
8843
|
+
import path48 from "path";
|
|
8716
8844
|
import { NodeType as NodeType17, Provenance as Provenance20 } from "@neat.is/types";
|
|
8717
8845
|
function dropOrphanedFileNodes(graph) {
|
|
8718
8846
|
const orphans = [];
|
|
@@ -8746,11 +8874,11 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
8746
8874
|
if (edge.provenance !== Provenance20.EXTRACTED) return;
|
|
8747
8875
|
const evidenceFile = edge.evidence?.file;
|
|
8748
8876
|
if (!evidenceFile) return;
|
|
8749
|
-
if (
|
|
8877
|
+
if (path48.isAbsolute(evidenceFile)) {
|
|
8750
8878
|
if (!existsSync2(evidenceFile)) toDrop.push(id);
|
|
8751
8879
|
return;
|
|
8752
8880
|
}
|
|
8753
|
-
const found = bases.some((base) => existsSync2(
|
|
8881
|
+
const found = bases.some((base) => existsSync2(path48.join(base, evidenceFile)));
|
|
8754
8882
|
if (!found) toDrop.push(id);
|
|
8755
8883
|
});
|
|
8756
8884
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -8803,7 +8931,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
8803
8931
|
}
|
|
8804
8932
|
const droppedEntries = drainDroppedExtracted();
|
|
8805
8933
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
8806
|
-
const rejectedPath =
|
|
8934
|
+
const rejectedPath = path49.join(path49.dirname(opts.errorsPath), "rejected.ndjson");
|
|
8807
8935
|
try {
|
|
8808
8936
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
8809
8937
|
} catch (err) {
|
|
@@ -9173,7 +9301,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
9173
9301
|
|
|
9174
9302
|
// src/persist.ts
|
|
9175
9303
|
import { promises as fs24 } from "fs";
|
|
9176
|
-
import
|
|
9304
|
+
import path50 from "path";
|
|
9177
9305
|
import { NodeType as NodeType19, Provenance as Provenance22, observedEdgeId as observedEdgeId2 } from "@neat.is/types";
|
|
9178
9306
|
var SCHEMA_VERSION = 6;
|
|
9179
9307
|
function migrateV1ToV2(payload) {
|
|
@@ -9225,7 +9353,7 @@ function migrateV2ToV3(payload) {
|
|
|
9225
9353
|
return { ...payload, schemaVersion: 3 };
|
|
9226
9354
|
}
|
|
9227
9355
|
async function ensureDir(filePath) {
|
|
9228
|
-
await fs24.mkdir(
|
|
9356
|
+
await fs24.mkdir(path50.dirname(filePath), { recursive: true });
|
|
9229
9357
|
}
|
|
9230
9358
|
async function saveGraphToDisk(graph, outPath) {
|
|
9231
9359
|
await ensureDir(outPath);
|
|
@@ -9387,23 +9515,23 @@ function canonicalJson(value) {
|
|
|
9387
9515
|
}
|
|
9388
9516
|
|
|
9389
9517
|
// src/projects.ts
|
|
9390
|
-
import
|
|
9518
|
+
import path51 from "path";
|
|
9391
9519
|
function pathsForProject(project, baseDir) {
|
|
9392
9520
|
if (project === DEFAULT_PROJECT) {
|
|
9393
9521
|
return {
|
|
9394
|
-
snapshotPath:
|
|
9395
|
-
errorsPath:
|
|
9396
|
-
staleEventsPath:
|
|
9397
|
-
embeddingsCachePath:
|
|
9398
|
-
policyViolationsPath:
|
|
9522
|
+
snapshotPath: path51.join(baseDir, "graph.json"),
|
|
9523
|
+
errorsPath: path51.join(baseDir, "errors.ndjson"),
|
|
9524
|
+
staleEventsPath: path51.join(baseDir, "stale-events.ndjson"),
|
|
9525
|
+
embeddingsCachePath: path51.join(baseDir, "embeddings.json"),
|
|
9526
|
+
policyViolationsPath: path51.join(baseDir, "policy-violations.ndjson")
|
|
9399
9527
|
};
|
|
9400
9528
|
}
|
|
9401
9529
|
return {
|
|
9402
|
-
snapshotPath:
|
|
9403
|
-
errorsPath:
|
|
9404
|
-
staleEventsPath:
|
|
9405
|
-
embeddingsCachePath:
|
|
9406
|
-
policyViolationsPath:
|
|
9530
|
+
snapshotPath: path51.join(baseDir, `${project}.json`),
|
|
9531
|
+
errorsPath: path51.join(baseDir, `errors.${project}.ndjson`),
|
|
9532
|
+
staleEventsPath: path51.join(baseDir, `stale-events.${project}.ndjson`),
|
|
9533
|
+
embeddingsCachePath: path51.join(baseDir, `embeddings.${project}.json`),
|
|
9534
|
+
policyViolationsPath: path51.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
9407
9535
|
};
|
|
9408
9536
|
}
|
|
9409
9537
|
var Projects = class {
|
|
@@ -9444,7 +9572,7 @@ function parseExtraProjects(raw) {
|
|
|
9444
9572
|
// src/registry.ts
|
|
9445
9573
|
import { promises as fs26 } from "fs";
|
|
9446
9574
|
import os2 from "os";
|
|
9447
|
-
import
|
|
9575
|
+
import path52 from "path";
|
|
9448
9576
|
import {
|
|
9449
9577
|
RegistryFileSchema
|
|
9450
9578
|
} from "@neat.is/types";
|
|
@@ -9452,20 +9580,20 @@ var LOCK_TIMEOUT_MS = 5e3;
|
|
|
9452
9580
|
var LOCK_RETRY_MS = 50;
|
|
9453
9581
|
function neatHome() {
|
|
9454
9582
|
const override = process.env.NEAT_HOME;
|
|
9455
|
-
if (override && override.length > 0) return
|
|
9456
|
-
return
|
|
9583
|
+
if (override && override.length > 0) return path52.resolve(override);
|
|
9584
|
+
return path52.join(os2.homedir(), ".neat");
|
|
9457
9585
|
}
|
|
9458
9586
|
function registryPath() {
|
|
9459
|
-
return
|
|
9587
|
+
return path52.join(neatHome(), "projects.json");
|
|
9460
9588
|
}
|
|
9461
9589
|
function registryLockPath() {
|
|
9462
|
-
return
|
|
9590
|
+
return path52.join(neatHome(), "projects.json.lock");
|
|
9463
9591
|
}
|
|
9464
9592
|
function daemonPidPath() {
|
|
9465
|
-
return
|
|
9593
|
+
return path52.join(neatHome(), "neatd.pid");
|
|
9466
9594
|
}
|
|
9467
9595
|
function daemonsDir() {
|
|
9468
|
-
return
|
|
9596
|
+
return path52.join(neatHome(), "daemons");
|
|
9469
9597
|
}
|
|
9470
9598
|
function isFiniteInt(v) {
|
|
9471
9599
|
return typeof v === "number" && Number.isFinite(v);
|
|
@@ -9506,7 +9634,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
|
9506
9634
|
const out = [];
|
|
9507
9635
|
for (const name of names) {
|
|
9508
9636
|
if (!name.endsWith(".json")) continue;
|
|
9509
|
-
const file =
|
|
9637
|
+
const file = path52.join(dir, name);
|
|
9510
9638
|
let raw;
|
|
9511
9639
|
try {
|
|
9512
9640
|
raw = await fs26.readFile(file, "utf8");
|
|
@@ -9627,7 +9755,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
9627
9755
|
}
|
|
9628
9756
|
}
|
|
9629
9757
|
async function normalizeProjectPath(input) {
|
|
9630
|
-
const resolved =
|
|
9758
|
+
const resolved = path52.resolve(input);
|
|
9631
9759
|
try {
|
|
9632
9760
|
return await fs26.realpath(resolved);
|
|
9633
9761
|
} catch {
|
|
@@ -9635,7 +9763,7 @@ async function normalizeProjectPath(input) {
|
|
|
9635
9763
|
}
|
|
9636
9764
|
}
|
|
9637
9765
|
async function writeAtomically(target, contents) {
|
|
9638
|
-
await fs26.mkdir(
|
|
9766
|
+
await fs26.mkdir(path52.dirname(target), { recursive: true });
|
|
9639
9767
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
9640
9768
|
const fd = await fs26.open(tmp, "w");
|
|
9641
9769
|
try {
|
|
@@ -9648,7 +9776,7 @@ async function writeAtomically(target, contents) {
|
|
|
9648
9776
|
}
|
|
9649
9777
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
9650
9778
|
const deadline = Date.now() + timeoutMs;
|
|
9651
|
-
await fs26.mkdir(
|
|
9779
|
+
await fs26.mkdir(path52.dirname(lockPath), { recursive: true });
|
|
9652
9780
|
let probedHolder = false;
|
|
9653
9781
|
while (true) {
|
|
9654
9782
|
try {
|
|
@@ -9844,13 +9972,13 @@ import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } f
|
|
|
9844
9972
|
|
|
9845
9973
|
// src/extend/index.ts
|
|
9846
9974
|
import { promises as fs28 } from "fs";
|
|
9847
|
-
import
|
|
9975
|
+
import path54 from "path";
|
|
9848
9976
|
import os3 from "os";
|
|
9849
9977
|
import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
|
|
9850
9978
|
|
|
9851
9979
|
// src/installers/package-manager.ts
|
|
9852
9980
|
import { promises as fs27 } from "fs";
|
|
9853
|
-
import
|
|
9981
|
+
import path53 from "path";
|
|
9854
9982
|
import { spawn } from "child_process";
|
|
9855
9983
|
var LOCKFILE_PRIORITY = [
|
|
9856
9984
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -9872,22 +10000,22 @@ async function exists2(p) {
|
|
|
9872
10000
|
}
|
|
9873
10001
|
}
|
|
9874
10002
|
async function detectPackageManager(serviceDir) {
|
|
9875
|
-
let dir =
|
|
10003
|
+
let dir = path53.resolve(serviceDir);
|
|
9876
10004
|
const stops = /* @__PURE__ */ new Set();
|
|
9877
10005
|
for (let i = 0; i < 64; i++) {
|
|
9878
10006
|
if (stops.has(dir)) break;
|
|
9879
10007
|
stops.add(dir);
|
|
9880
10008
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
9881
|
-
const lockPath =
|
|
10009
|
+
const lockPath = path53.join(dir, candidate.lockfile);
|
|
9882
10010
|
if (await exists2(lockPath)) {
|
|
9883
10011
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
9884
10012
|
}
|
|
9885
10013
|
}
|
|
9886
|
-
const parent =
|
|
10014
|
+
const parent = path53.dirname(dir);
|
|
9887
10015
|
if (parent === dir) break;
|
|
9888
10016
|
dir = parent;
|
|
9889
10017
|
}
|
|
9890
|
-
return { pm: "npm", cwd:
|
|
10018
|
+
return { pm: "npm", cwd: path53.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
9891
10019
|
}
|
|
9892
10020
|
async function runPackageManagerInstall(cmd) {
|
|
9893
10021
|
return new Promise((resolve) => {
|
|
@@ -9936,7 +10064,7 @@ async function fileExists2(p) {
|
|
|
9936
10064
|
}
|
|
9937
10065
|
}
|
|
9938
10066
|
async function readPackageJson(scanPath) {
|
|
9939
|
-
const pkgPath =
|
|
10067
|
+
const pkgPath = path54.join(scanPath, "package.json");
|
|
9940
10068
|
const raw = await fs28.readFile(pkgPath, "utf8");
|
|
9941
10069
|
return JSON.parse(raw);
|
|
9942
10070
|
}
|
|
@@ -9955,11 +10083,11 @@ async function findHookFiles(scanPath) {
|
|
|
9955
10083
|
for (const entry of entries) {
|
|
9956
10084
|
if (entry.isDirectory()) {
|
|
9957
10085
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
9958
|
-
await walk6(
|
|
10086
|
+
await walk6(path54.join(dir, entry.name));
|
|
9959
10087
|
} else if (entry.isFile()) {
|
|
9960
10088
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
9961
|
-
const rel =
|
|
9962
|
-
found.push(rel.split(
|
|
10089
|
+
const rel = path54.relative(scanPath, path54.join(dir, entry.name));
|
|
10090
|
+
found.push(rel.split(path54.sep).join("/"));
|
|
9963
10091
|
}
|
|
9964
10092
|
}
|
|
9965
10093
|
}
|
|
@@ -9970,7 +10098,7 @@ async function findHookFiles(scanPath) {
|
|
|
9970
10098
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
9971
10099
|
let fallback = null;
|
|
9972
10100
|
for (const file of hookFiles) {
|
|
9973
|
-
const content = await fs28.readFile(
|
|
10101
|
+
const content = await fs28.readFile(path54.join(scanPath, file), "utf8");
|
|
9974
10102
|
const patched = splicedContent(content, snippet2);
|
|
9975
10103
|
if (patched !== null) return { file, content, patched };
|
|
9976
10104
|
if (fallback === null) fallback = { file, content };
|
|
@@ -9978,11 +10106,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
9978
10106
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
9979
10107
|
}
|
|
9980
10108
|
function extendLogPath() {
|
|
9981
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
10109
|
+
return process.env.NEAT_EXTEND_LOG ?? path54.join(os3.homedir(), ".neat", "extend-log.ndjson");
|
|
9982
10110
|
}
|
|
9983
10111
|
async function appendExtendLog(entry) {
|
|
9984
10112
|
const logPath = extendLogPath();
|
|
9985
|
-
await fs28.mkdir(
|
|
10113
|
+
await fs28.mkdir(path54.dirname(logPath), { recursive: true });
|
|
9986
10114
|
await fs28.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
9987
10115
|
}
|
|
9988
10116
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -10041,7 +10169,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
10041
10169
|
}
|
|
10042
10170
|
async function describeProjectInstrumentation(ctx) {
|
|
10043
10171
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
10044
|
-
const envNeat = await fileExists2(
|
|
10172
|
+
const envNeat = await fileExists2(path54.join(ctx.scanPath, ".env.neat"));
|
|
10045
10173
|
const registryInstrPackages = new Set(
|
|
10046
10174
|
registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
10047
10175
|
);
|
|
@@ -10063,7 +10191,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
10063
10191
|
);
|
|
10064
10192
|
}
|
|
10065
10193
|
for (const file of hookFiles) {
|
|
10066
|
-
const content = await fs28.readFile(
|
|
10194
|
+
const content = await fs28.readFile(path54.join(ctx.scanPath, file), "utf8");
|
|
10067
10195
|
if (content.includes(args.registration_snippet)) {
|
|
10068
10196
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
10069
10197
|
}
|
|
@@ -10075,10 +10203,10 @@ async function applyExtension(ctx, args, options) {
|
|
|
10075
10203
|
);
|
|
10076
10204
|
}
|
|
10077
10205
|
const primaryFile = primary.file;
|
|
10078
|
-
const primaryPath =
|
|
10206
|
+
const primaryPath = path54.join(ctx.scanPath, primaryFile);
|
|
10079
10207
|
const filesTouched = [];
|
|
10080
10208
|
const depsAdded = [];
|
|
10081
|
-
const pkgPath =
|
|
10209
|
+
const pkgPath = path54.join(ctx.scanPath, "package.json");
|
|
10082
10210
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
10083
10211
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
10084
10212
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -10117,7 +10245,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
10117
10245
|
};
|
|
10118
10246
|
}
|
|
10119
10247
|
for (const file of hookFiles) {
|
|
10120
|
-
const content = await fs28.readFile(
|
|
10248
|
+
const content = await fs28.readFile(path54.join(ctx.scanPath, file), "utf8");
|
|
10121
10249
|
if (content.includes(args.registration_snippet)) {
|
|
10122
10250
|
return {
|
|
10123
10251
|
library: args.library,
|
|
@@ -10158,7 +10286,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
10158
10286
|
if (!match) {
|
|
10159
10287
|
return { undone: false, message: "no apply found for library" };
|
|
10160
10288
|
}
|
|
10161
|
-
const pkgPath =
|
|
10289
|
+
const pkgPath = path54.join(ctx.scanPath, "package.json");
|
|
10162
10290
|
if (await fileExists2(pkgPath)) {
|
|
10163
10291
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
10164
10292
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -10169,7 +10297,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
10169
10297
|
}
|
|
10170
10298
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
10171
10299
|
for (const file of hookFiles) {
|
|
10172
|
-
const filePath =
|
|
10300
|
+
const filePath = path54.join(ctx.scanPath, file);
|
|
10173
10301
|
const content = await fs28.readFile(filePath, "utf8");
|
|
10174
10302
|
if (content.includes(match.registration_snippet)) {
|
|
10175
10303
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -10284,7 +10412,7 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
10284
10412
|
|
|
10285
10413
|
// src/connectors-config.ts
|
|
10286
10414
|
import os4 from "os";
|
|
10287
|
-
import
|
|
10415
|
+
import path55 from "path";
|
|
10288
10416
|
import { promises as fs29 } from "fs";
|
|
10289
10417
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
10290
10418
|
var EnvRefUnsetError = class extends Error {
|
|
@@ -10299,11 +10427,11 @@ var EnvRefUnsetError = class extends Error {
|
|
|
10299
10427
|
};
|
|
10300
10428
|
function neatHome2() {
|
|
10301
10429
|
const override = process.env.NEAT_HOME;
|
|
10302
|
-
if (override && override.length > 0) return
|
|
10303
|
-
return
|
|
10430
|
+
if (override && override.length > 0) return path55.resolve(override);
|
|
10431
|
+
return path55.join(os4.homedir(), ".neat");
|
|
10304
10432
|
}
|
|
10305
10433
|
function connectorsConfigPath(home = neatHome2()) {
|
|
10306
|
-
return
|
|
10434
|
+
return path55.join(home, "connectors.json");
|
|
10307
10435
|
}
|
|
10308
10436
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
10309
10437
|
async function warnIfModeLooserThan0600(file) {
|
|
@@ -10434,7 +10562,7 @@ function connectorMatchesProject(entry, project) {
|
|
|
10434
10562
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
10435
10563
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
10436
10564
|
function connectorsConfigLockPath(home = neatHome2()) {
|
|
10437
|
-
return
|
|
10565
|
+
return path55.join(home, "connectors.json.lock");
|
|
10438
10566
|
}
|
|
10439
10567
|
function isEnvRef(value) {
|
|
10440
10568
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -10447,7 +10575,7 @@ function redactCredentialRef(ref) {
|
|
|
10447
10575
|
return out;
|
|
10448
10576
|
}
|
|
10449
10577
|
async function writeConfigAtomically0600(file, contents) {
|
|
10450
|
-
await fs29.mkdir(
|
|
10578
|
+
await fs29.mkdir(path55.dirname(file), { recursive: true });
|
|
10451
10579
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
10452
10580
|
const fd = await fs29.open(tmp, "w", 384);
|
|
10453
10581
|
try {
|
|
@@ -10461,7 +10589,7 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
10461
10589
|
}
|
|
10462
10590
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
10463
10591
|
const deadline = Date.now() + timeoutMs;
|
|
10464
|
-
await fs29.mkdir(
|
|
10592
|
+
await fs29.mkdir(path55.dirname(lockPath), { recursive: true });
|
|
10465
10593
|
for (; ; ) {
|
|
10466
10594
|
try {
|
|
10467
10595
|
const fd = await fs29.open(lockPath, "wx");
|
|
@@ -11092,10 +11220,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
11092
11220
|
// src/connectors/supabase/map.ts
|
|
11093
11221
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
11094
11222
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
11095
|
-
function targetFromRestPath(
|
|
11096
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
11223
|
+
function targetFromRestPath(path56) {
|
|
11224
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path56);
|
|
11097
11225
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
11098
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
11226
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path56);
|
|
11099
11227
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
11100
11228
|
return null;
|
|
11101
11229
|
}
|
|
@@ -11204,21 +11332,21 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
11204
11332
|
}
|
|
11205
11333
|
|
|
11206
11334
|
// src/connectors/supabase/resolve.ts
|
|
11207
|
-
import { EdgeType as EdgeType23, infraId as
|
|
11335
|
+
import { EdgeType as EdgeType23, infraId as infraId14 } from "@neat.is/types";
|
|
11208
11336
|
function createSupabaseResolveTarget(graph, config) {
|
|
11209
11337
|
return (signal, _ctx) => {
|
|
11210
11338
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
11211
11339
|
return null;
|
|
11212
11340
|
}
|
|
11213
|
-
const subResourceId =
|
|
11341
|
+
const subResourceId = infraId14(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
11214
11342
|
if (graph.hasNode(subResourceId)) {
|
|
11215
11343
|
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: EdgeType23.CALLS };
|
|
11216
11344
|
}
|
|
11217
|
-
const bareResourceId =
|
|
11345
|
+
const bareResourceId = infraId14(signal.targetKind, signal.targetName);
|
|
11218
11346
|
if (graph.hasNode(bareResourceId)) {
|
|
11219
11347
|
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: EdgeType23.CALLS };
|
|
11220
11348
|
}
|
|
11221
|
-
const projectLevelId =
|
|
11349
|
+
const projectLevelId = infraId14("supabase", config.nodeRef);
|
|
11222
11350
|
if (graph.hasNode(projectLevelId)) {
|
|
11223
11351
|
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: EdgeType23.CALLS };
|
|
11224
11352
|
}
|
|
@@ -11690,9 +11818,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
11690
11818
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
11691
11819
|
if (secondSep === -1) return null;
|
|
11692
11820
|
const method = rest.slice(0, secondSep);
|
|
11693
|
-
const
|
|
11694
|
-
if (!resourceName || !method || !
|
|
11695
|
-
return { resourceName, method, path:
|
|
11821
|
+
const path56 = rest.slice(secondSep + 1);
|
|
11822
|
+
if (!resourceName || !method || !path56) return null;
|
|
11823
|
+
return { resourceName, method, path: path56 };
|
|
11696
11824
|
}
|
|
11697
11825
|
function resourceNameFor(type, labels) {
|
|
11698
11826
|
if (!labels) return null;
|
|
@@ -11730,14 +11858,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
11730
11858
|
if (!req) return null;
|
|
11731
11859
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
11732
11860
|
const method = req.requestMethod.toUpperCase();
|
|
11733
|
-
const
|
|
11734
|
-
if (
|
|
11861
|
+
const path56 = pathFromRequestUrl(req.requestUrl);
|
|
11862
|
+
if (path56 === null) return null;
|
|
11735
11863
|
const timestamp = entry.timestamp;
|
|
11736
11864
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
11737
11865
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
11738
11866
|
return {
|
|
11739
11867
|
targetKind: resourceType,
|
|
11740
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
11868
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path56 }),
|
|
11741
11869
|
callCount: 1,
|
|
11742
11870
|
errorCount: isError ? 1 : 0,
|
|
11743
11871
|
lastObservedIso: timestamp
|
|
@@ -11823,7 +11951,7 @@ function createFirebaseConnector(graph, serviceMap) {
|
|
|
11823
11951
|
}
|
|
11824
11952
|
|
|
11825
11953
|
// src/connectors/cloudflare/connector.ts
|
|
11826
|
-
import { EdgeType as EdgeType26, NodeType as NodeType23, fileId as fileId4, infraId as
|
|
11954
|
+
import { EdgeType as EdgeType26, NodeType as NodeType23, fileId as fileId4, infraId as infraId15 } from "@neat.is/types";
|
|
11827
11955
|
|
|
11828
11956
|
// src/connectors/cloudflare/client.ts
|
|
11829
11957
|
import { randomUUID } from "crypto";
|
|
@@ -11934,7 +12062,7 @@ function mapEventToSignal(event) {
|
|
|
11934
12062
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
11935
12063
|
const statusCode = metadata?.statusCode;
|
|
11936
12064
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
11937
|
-
const
|
|
12065
|
+
const path56 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
11938
12066
|
return {
|
|
11939
12067
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
11940
12068
|
targetName: scriptName,
|
|
@@ -11942,7 +12070,7 @@ function mapEventToSignal(event) {
|
|
|
11942
12070
|
errorCount: isError ? 1 : 0,
|
|
11943
12071
|
lastObservedIso: observedAt.toISOString(),
|
|
11944
12072
|
method,
|
|
11945
|
-
...
|
|
12073
|
+
...path56 ? { path: path56 } : {},
|
|
11946
12074
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
11947
12075
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
11948
12076
|
};
|
|
@@ -11988,8 +12116,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
11988
12116
|
});
|
|
11989
12117
|
return found;
|
|
11990
12118
|
}
|
|
11991
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
11992
|
-
const normalizedPath = normalizePathTemplate(
|
|
12119
|
+
function findMatchingRouteNode(graph, serviceName, method, path56) {
|
|
12120
|
+
const normalizedPath = normalizePathTemplate(path56);
|
|
11993
12121
|
let found = null;
|
|
11994
12122
|
graph.forEachNode((id, attrs) => {
|
|
11995
12123
|
if (found) return;
|
|
@@ -12006,10 +12134,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
12006
12134
|
return (signal) => {
|
|
12007
12135
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
12008
12136
|
const scriptName = signal.targetName;
|
|
12009
|
-
const { method, path:
|
|
12137
|
+
const { method, path: path56 } = signal;
|
|
12010
12138
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
12011
|
-
if (!method || !
|
|
12012
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
12139
|
+
if (!method || !path56) return wholeFileId;
|
|
12140
|
+
return findMatchingRouteNode(graph, serviceName, method, path56) ?? wholeFileId;
|
|
12013
12141
|
};
|
|
12014
12142
|
const mapping = config.workers?.[scriptName];
|
|
12015
12143
|
if (mapping) {
|
|
@@ -12030,7 +12158,7 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
12030
12158
|
};
|
|
12031
12159
|
}
|
|
12032
12160
|
return {
|
|
12033
|
-
targetNodeId:
|
|
12161
|
+
targetNodeId: infraId15("cloudflare-worker", scriptName),
|
|
12034
12162
|
serviceName: scriptName,
|
|
12035
12163
|
edgeType: EdgeType26.CALLS,
|
|
12036
12164
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
@@ -12214,12 +12342,12 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
12214
12342
|
}
|
|
12215
12343
|
|
|
12216
12344
|
// src/connectors/neon/resolve.ts
|
|
12217
|
-
import { EdgeType as EdgeType27, infraId as
|
|
12345
|
+
import { EdgeType as EdgeType27, infraId as infraId16 } from "@neat.is/types";
|
|
12218
12346
|
function createNeonResolveTarget(config) {
|
|
12219
12347
|
return (signal) => {
|
|
12220
12348
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
12221
12349
|
return {
|
|
12222
|
-
targetNodeId:
|
|
12350
|
+
targetNodeId: infraId16("sql-table", signal.targetName),
|
|
12223
12351
|
serviceName: config.serviceName,
|
|
12224
12352
|
edgeType: EdgeType27.CALLS,
|
|
12225
12353
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
@@ -13614,4 +13742,4 @@ export {
|
|
|
13614
13742
|
deprovisionConnector,
|
|
13615
13743
|
buildApi
|
|
13616
13744
|
};
|
|
13617
|
-
//# sourceMappingURL=chunk-
|
|
13745
|
+
//# sourceMappingURL=chunk-BY6ZP5WA.js.map
|