@neat.is/core 0.3.3 → 0.3.5
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-VABXPLDT.js → chunk-BUB3ASD5.js} +268 -142
- package/dist/chunk-BUB3ASD5.js.map +1 -0
- package/dist/{chunk-ICFH326H.js → chunk-KCEZSFU2.js} +2 -2
- package/dist/cli.cjs +309 -204
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +244 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +251 -151
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +213 -115
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-VABXPLDT.js.map +0 -1
- /package/dist/{chunk-ICFH326H.js.map → chunk-KCEZSFU2.js.map} +0 -0
package/dist/server.cjs
CHANGED
|
@@ -99,8 +99,8 @@ function reshapeGrpcRequest(req) {
|
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
101
|
function resolveProtoRoot() {
|
|
102
|
-
const here =
|
|
103
|
-
return
|
|
102
|
+
const here = import_node_path34.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
103
|
+
return import_node_path34.default.resolve(here, "..", "proto");
|
|
104
104
|
}
|
|
105
105
|
function loadTraceService() {
|
|
106
106
|
const protoRoot = resolveProtoRoot();
|
|
@@ -155,13 +155,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
155
155
|
})
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
var import_node_url,
|
|
158
|
+
var import_node_url, import_node_path34, grpc, protoLoader;
|
|
159
159
|
var init_otel_grpc = __esm({
|
|
160
160
|
"src/otel-grpc.ts"() {
|
|
161
161
|
"use strict";
|
|
162
162
|
init_cjs_shims();
|
|
163
163
|
import_node_url = require("url");
|
|
164
|
-
|
|
164
|
+
import_node_path34 = __toESM(require("path"), 1);
|
|
165
165
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
166
166
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
167
167
|
init_otel();
|
|
@@ -258,10 +258,10 @@ function parseOtlpRequest(body) {
|
|
|
258
258
|
}
|
|
259
259
|
function loadProtobufDecoder() {
|
|
260
260
|
if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
|
|
261
|
-
const here =
|
|
262
|
-
const protoRoot =
|
|
261
|
+
const here = import_node_path35.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
262
|
+
const protoRoot = import_node_path35.default.resolve(here, "..", "proto");
|
|
263
263
|
const root = new import_protobufjs.default.Root();
|
|
264
|
-
root.resolvePath = (_origin, target) =>
|
|
264
|
+
root.resolvePath = (_origin, target) => import_node_path35.default.resolve(protoRoot, target);
|
|
265
265
|
root.loadSync(
|
|
266
266
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
267
267
|
{ keepCase: true }
|
|
@@ -353,12 +353,12 @@ async function buildOtelReceiver(opts) {
|
|
|
353
353
|
};
|
|
354
354
|
return decorated;
|
|
355
355
|
}
|
|
356
|
-
var
|
|
356
|
+
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
|
|
357
357
|
var init_otel = __esm({
|
|
358
358
|
"src/otel.ts"() {
|
|
359
359
|
"use strict";
|
|
360
360
|
init_cjs_shims();
|
|
361
|
-
|
|
361
|
+
import_node_path35 = __toESM(require("path"), 1);
|
|
362
362
|
import_node_url2 = require("url");
|
|
363
363
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
364
364
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -368,7 +368,7 @@ var init_otel = __esm({
|
|
|
368
368
|
|
|
369
369
|
// src/server.ts
|
|
370
370
|
init_cjs_shims();
|
|
371
|
-
var
|
|
371
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
372
372
|
|
|
373
373
|
// src/graph.ts
|
|
374
374
|
init_cjs_shims();
|
|
@@ -716,11 +716,16 @@ init_cjs_shims();
|
|
|
716
716
|
var import_types = require("@neat.is/types");
|
|
717
717
|
var ROOT_CAUSE_MAX_DEPTH = 5;
|
|
718
718
|
var BLAST_RADIUS_DEFAULT_DEPTH = 10;
|
|
719
|
+
function isFrontierNode(graph, nodeId) {
|
|
720
|
+
if (!graph.hasNode(nodeId)) return false;
|
|
721
|
+
const attrs = graph.getNodeAttributes(nodeId);
|
|
722
|
+
return attrs.type === import_types.NodeType.FrontierNode;
|
|
723
|
+
}
|
|
719
724
|
function bestEdgeBySource(graph, edgeIds) {
|
|
720
725
|
const best = /* @__PURE__ */ new Map();
|
|
721
726
|
for (const id of edgeIds) {
|
|
722
727
|
const e = graph.getEdgeAttributes(id);
|
|
723
|
-
if (e.
|
|
728
|
+
if (isFrontierNode(graph, e.source)) continue;
|
|
724
729
|
const cur = best.get(e.source);
|
|
725
730
|
if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
|
|
726
731
|
best.set(e.source, e);
|
|
@@ -732,7 +737,7 @@ function bestEdgeByTarget(graph, edgeIds) {
|
|
|
732
737
|
const best = /* @__PURE__ */ new Map();
|
|
733
738
|
for (const id of edgeIds) {
|
|
734
739
|
const e = graph.getEdgeAttributes(id);
|
|
735
|
-
if (e.
|
|
740
|
+
if (isFrontierNode(graph, e.target)) continue;
|
|
736
741
|
const cur = best.get(e.target);
|
|
737
742
|
if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
|
|
738
743
|
best.set(e.target, e);
|
|
@@ -744,8 +749,7 @@ var PROVENANCE_CEILING = {
|
|
|
744
749
|
OBSERVED: 1,
|
|
745
750
|
INFERRED: 0.7,
|
|
746
751
|
EXTRACTED: 0.5,
|
|
747
|
-
STALE: 0.3
|
|
748
|
-
FRONTIER: 0.3
|
|
752
|
+
STALE: 0.3
|
|
749
753
|
};
|
|
750
754
|
function volumeWeight(spanCount) {
|
|
751
755
|
if (!spanCount || spanCount <= 0) return 0.5;
|
|
@@ -798,19 +802,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
798
802
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
799
803
|
let best = { path: [start], edges: [] };
|
|
800
804
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
801
|
-
function step(node,
|
|
802
|
-
if (
|
|
803
|
-
best = { path: [...
|
|
805
|
+
function step(node, path38, edges) {
|
|
806
|
+
if (path38.length > best.path.length) {
|
|
807
|
+
best = { path: [...path38], edges: [...edges] };
|
|
804
808
|
}
|
|
805
|
-
if (
|
|
809
|
+
if (path38.length - 1 >= maxDepth) return;
|
|
806
810
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
807
811
|
for (const [srcId, edge] of incoming) {
|
|
808
812
|
if (visited.has(srcId)) continue;
|
|
809
813
|
visited.add(srcId);
|
|
810
|
-
|
|
814
|
+
path38.push(srcId);
|
|
811
815
|
edges.push(edge);
|
|
812
|
-
step(srcId,
|
|
813
|
-
|
|
816
|
+
step(srcId, path38, edges);
|
|
817
|
+
path38.pop();
|
|
814
818
|
edges.pop();
|
|
815
819
|
visited.delete(srcId);
|
|
816
820
|
}
|
|
@@ -1014,9 +1018,6 @@ function bucketEdges(graph) {
|
|
|
1014
1018
|
case import_types2.Provenance.INFERRED:
|
|
1015
1019
|
cur.inferred = e;
|
|
1016
1020
|
break;
|
|
1017
|
-
case import_types2.Provenance.FRONTIER:
|
|
1018
|
-
cur.frontier = e;
|
|
1019
|
-
break;
|
|
1020
1021
|
default:
|
|
1021
1022
|
if (e.provenance === import_types2.Provenance.STALE) cur.stale = e;
|
|
1022
1023
|
}
|
|
@@ -1029,14 +1030,6 @@ function nodeIsFrontier(graph, nodeId) {
|
|
|
1029
1030
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
1030
1031
|
return attrs.type === import_types2.NodeType.FrontierNode;
|
|
1031
1032
|
}
|
|
1032
|
-
function hasAnyObservedFromSource(graph, sourceId) {
|
|
1033
|
-
if (!graph.hasNode(sourceId)) return false;
|
|
1034
|
-
for (const edgeId of graph.outboundEdges(sourceId)) {
|
|
1035
|
-
const e = graph.getEdgeAttributes(edgeId);
|
|
1036
|
-
if (e.provenance === import_types2.Provenance.OBSERVED) return true;
|
|
1037
|
-
}
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
1033
|
function clampConfidence(n) {
|
|
1041
1034
|
if (!Number.isFinite(n)) return 0;
|
|
1042
1035
|
return Math.max(0, Math.min(1, n));
|
|
@@ -1050,32 +1043,34 @@ function reasonForMissingExtracted(source, target, type) {
|
|
|
1050
1043
|
var RECOMMENDATION_MISSING_OBSERVED = "Verify the code path is exercised in production; check feature flags or conditional branches that might gate the call.";
|
|
1051
1044
|
var RECOMMENDATION_MISSING_EXTRACTED = "Likely dynamic dispatch, reflection, or a coverage gap in tree-sitter extraction. Consider an `aliases` entry on the source service or file an extractor issue.";
|
|
1052
1045
|
var RECOMMENDATION_HOST_MISMATCH = "Check environment-specific config overrides \u2014 the runtime host differs from what static configuration declares.";
|
|
1046
|
+
function gradedConfidence(edge) {
|
|
1047
|
+
if (typeof edge.confidence === "number") return clampConfidence(edge.confidence);
|
|
1048
|
+
return clampConfidence(confidenceForEdge(edge));
|
|
1049
|
+
}
|
|
1053
1050
|
function detectMissingDivergences(graph, bucket) {
|
|
1054
1051
|
const out = [];
|
|
1055
1052
|
if (bucket.extracted && !bucket.observed) {
|
|
1056
1053
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
1057
|
-
const sourceHasTraffic = hasAnyObservedFromSource(graph, bucket.source);
|
|
1058
1054
|
out.push({
|
|
1059
1055
|
type: "missing-observed",
|
|
1060
1056
|
source: bucket.source,
|
|
1061
1057
|
target: bucket.target,
|
|
1062
1058
|
edgeType: bucket.type,
|
|
1063
1059
|
extracted: bucket.extracted,
|
|
1064
|
-
confidence:
|
|
1060
|
+
confidence: gradedConfidence(bucket.extracted),
|
|
1065
1061
|
reason: reasonForMissingObserved(bucket.source, bucket.target, bucket.type),
|
|
1066
1062
|
recommendation: RECOMMENDATION_MISSING_OBSERVED
|
|
1067
1063
|
});
|
|
1068
1064
|
}
|
|
1069
1065
|
}
|
|
1070
1066
|
if (bucket.observed && !bucket.extracted) {
|
|
1071
|
-
const cascaded = clampConfidence(confidenceForEdge(bucket.observed));
|
|
1072
1067
|
out.push({
|
|
1073
1068
|
type: "missing-extracted",
|
|
1074
1069
|
source: bucket.source,
|
|
1075
1070
|
target: bucket.target,
|
|
1076
1071
|
edgeType: bucket.type,
|
|
1077
1072
|
observed: bucket.observed,
|
|
1078
|
-
confidence:
|
|
1073
|
+
confidence: gradedConfidence(bucket.observed),
|
|
1079
1074
|
reason: reasonForMissingExtracted(bucket.source, bucket.target, bucket.type),
|
|
1080
1075
|
recommendation: RECOMMENDATION_MISSING_EXTRACTED
|
|
1081
1076
|
});
|
|
@@ -1214,8 +1209,17 @@ function computeDivergences(graph, opts = {}) {
|
|
|
1214
1209
|
const target = opts.node;
|
|
1215
1210
|
filtered = filtered.filter((d) => involvesNode(d, target));
|
|
1216
1211
|
}
|
|
1212
|
+
const TYPE_LEADERSHIP = {
|
|
1213
|
+
"missing-extracted": 0,
|
|
1214
|
+
"missing-observed": 1,
|
|
1215
|
+
"version-mismatch": 2,
|
|
1216
|
+
"host-mismatch": 3,
|
|
1217
|
+
"compat-violation": 4
|
|
1218
|
+
};
|
|
1217
1219
|
filtered.sort((a, b) => {
|
|
1218
1220
|
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
1221
|
+
const lead = TYPE_LEADERSHIP[a.type] - TYPE_LEADERSHIP[b.type];
|
|
1222
|
+
if (lead !== 0) return lead;
|
|
1219
1223
|
if (a.type !== b.type) return a.type.localeCompare(b.type);
|
|
1220
1224
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
1221
1225
|
return a.target.localeCompare(b.target);
|
|
@@ -1282,8 +1286,8 @@ var evaluateStructural = ({
|
|
|
1282
1286
|
for (const edgeId of graph.outboundEdges(id)) {
|
|
1283
1287
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1284
1288
|
if (e.type !== rule.edgeType) continue;
|
|
1285
|
-
if (e.provenance === import_types3.Provenance.FRONTIER) continue;
|
|
1286
1289
|
const target = graph.getNodeAttributes(e.target);
|
|
1290
|
+
if (target.type === import_types3.NodeType.FrontierNode) continue;
|
|
1287
1291
|
if (target.type === rule.toNodeType) {
|
|
1288
1292
|
satisfied = true;
|
|
1289
1293
|
break;
|
|
@@ -1402,8 +1406,8 @@ var evaluateCompatibility = ({
|
|
|
1402
1406
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
1403
1407
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1404
1408
|
if (e.type !== import_types3.EdgeType.CONNECTS_TO) continue;
|
|
1405
|
-
if (e.provenance === import_types3.Provenance.FRONTIER) continue;
|
|
1406
1409
|
const dbAttrs = graph.getNodeAttributes(e.target);
|
|
1410
|
+
if (dbAttrs.type === import_types3.NodeType.FrontierNode) continue;
|
|
1407
1411
|
if (dbAttrs.type !== import_types3.NodeType.DatabaseNode) continue;
|
|
1408
1412
|
const db = dbAttrs;
|
|
1409
1413
|
for (const pair of compatPairs()) {
|
|
@@ -1729,31 +1733,6 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
1729
1733
|
graph.addNode(id, node);
|
|
1730
1734
|
return id;
|
|
1731
1735
|
}
|
|
1732
|
-
function upsertFrontierEdge(graph, type, source, target, ts) {
|
|
1733
|
-
const id = (0, import_types4.frontierEdgeId)(source, target, type);
|
|
1734
|
-
if (graph.hasEdge(id)) {
|
|
1735
|
-
const existing = graph.getEdgeAttributes(id);
|
|
1736
|
-
const updated = {
|
|
1737
|
-
...existing,
|
|
1738
|
-
provenance: import_types4.Provenance.FRONTIER,
|
|
1739
|
-
lastObserved: ts,
|
|
1740
|
-
callCount: (existing.callCount ?? 0) + 1
|
|
1741
|
-
};
|
|
1742
|
-
graph.replaceEdgeAttributes(id, updated);
|
|
1743
|
-
return;
|
|
1744
|
-
}
|
|
1745
|
-
const edge = {
|
|
1746
|
-
id,
|
|
1747
|
-
source,
|
|
1748
|
-
target,
|
|
1749
|
-
type,
|
|
1750
|
-
provenance: import_types4.Provenance.FRONTIER,
|
|
1751
|
-
confidence: 1,
|
|
1752
|
-
lastObserved: ts,
|
|
1753
|
-
callCount: 1
|
|
1754
|
-
};
|
|
1755
|
-
graph.addEdgeWithKey(id, source, target, edge);
|
|
1756
|
-
}
|
|
1757
1736
|
function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
1758
1737
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
1759
1738
|
const id = makeObservedEdgeId(type, source, target);
|
|
@@ -1761,35 +1740,37 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false) {
|
|
|
1761
1740
|
const existing = graph.getEdgeAttributes(id);
|
|
1762
1741
|
const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1;
|
|
1763
1742
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
1743
|
+
const newSignal = {
|
|
1744
|
+
spanCount: newSpanCount,
|
|
1745
|
+
errorCount: newErrorCount,
|
|
1746
|
+
lastObservedAgeMs: 0
|
|
1747
|
+
};
|
|
1764
1748
|
const updated = {
|
|
1765
1749
|
...existing,
|
|
1766
1750
|
provenance: import_types4.Provenance.OBSERVED,
|
|
1767
1751
|
lastObserved: ts,
|
|
1768
1752
|
callCount: newSpanCount,
|
|
1769
|
-
signal:
|
|
1770
|
-
|
|
1771
|
-
errorCount: newErrorCount,
|
|
1772
|
-
lastObservedAgeMs: 0
|
|
1773
|
-
},
|
|
1774
|
-
confidence: 1
|
|
1753
|
+
signal: newSignal,
|
|
1754
|
+
confidence: (0, import_types4.confidenceForObservedSignal)(newSignal)
|
|
1775
1755
|
};
|
|
1776
1756
|
graph.replaceEdgeAttributes(id, updated);
|
|
1777
1757
|
return { edge: updated, created: false };
|
|
1778
1758
|
}
|
|
1759
|
+
const signal = {
|
|
1760
|
+
spanCount: 1,
|
|
1761
|
+
errorCount: isError ? 1 : 0,
|
|
1762
|
+
lastObservedAgeMs: 0
|
|
1763
|
+
};
|
|
1779
1764
|
const edge = {
|
|
1780
1765
|
id,
|
|
1781
1766
|
source,
|
|
1782
1767
|
target,
|
|
1783
1768
|
type,
|
|
1784
1769
|
provenance: import_types4.Provenance.OBSERVED,
|
|
1785
|
-
confidence:
|
|
1770
|
+
confidence: (0, import_types4.confidenceForObservedSignal)(signal),
|
|
1786
1771
|
lastObserved: ts,
|
|
1787
1772
|
callCount: 1,
|
|
1788
|
-
signal
|
|
1789
|
-
spanCount: 1,
|
|
1790
|
-
errorCount: isError ? 1 : 0,
|
|
1791
|
-
lastObservedAgeMs: 0
|
|
1792
|
-
}
|
|
1773
|
+
signal
|
|
1793
1774
|
};
|
|
1794
1775
|
graph.addEdgeWithKey(id, source, target, edge);
|
|
1795
1776
|
return { edge, created: true };
|
|
@@ -1837,6 +1818,14 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
1837
1818
|
await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(ctx.errorsPath), { recursive: true });
|
|
1838
1819
|
await import_node_fs3.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
1839
1820
|
}
|
|
1821
|
+
function sanitizeAttributes(attrs) {
|
|
1822
|
+
const out = {};
|
|
1823
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
1824
|
+
if (typeof v === "bigint") out[k] = v.toString();
|
|
1825
|
+
else out[k] = v;
|
|
1826
|
+
}
|
|
1827
|
+
return out;
|
|
1828
|
+
}
|
|
1840
1829
|
async function handleSpan(ctx, span) {
|
|
1841
1830
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
1842
1831
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -1876,11 +1865,16 @@ async function handleSpan(ctx, span) {
|
|
|
1876
1865
|
affectedNode = targetId;
|
|
1877
1866
|
resolvedViaAddress = true;
|
|
1878
1867
|
} else if (!targetId) {
|
|
1879
|
-
const
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1868
|
+
const frontierNodeId = ensureFrontierNode(ctx.graph, host, ts);
|
|
1869
|
+
upsertObservedEdge(
|
|
1870
|
+
ctx.graph,
|
|
1871
|
+
import_types4.EdgeType.CALLS,
|
|
1872
|
+
sourceId,
|
|
1873
|
+
frontierNodeId,
|
|
1874
|
+
ts,
|
|
1875
|
+
isError
|
|
1876
|
+
);
|
|
1877
|
+
affectedNode = frontierNodeId;
|
|
1884
1878
|
resolvedViaAddress = true;
|
|
1885
1879
|
}
|
|
1886
1880
|
}
|
|
@@ -1902,15 +1896,17 @@ async function handleSpan(ctx, span) {
|
|
|
1902
1896
|
if (span.statusCode === 2) {
|
|
1903
1897
|
stitchTrace(ctx.graph, sourceId, ts);
|
|
1904
1898
|
if (ctx.writeErrorEventInline !== false) {
|
|
1899
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
1905
1900
|
const ev = {
|
|
1906
1901
|
id: `${span.traceId}:${span.spanId}`,
|
|
1907
1902
|
timestamp: ts,
|
|
1908
1903
|
service: span.service,
|
|
1909
1904
|
traceId: span.traceId,
|
|
1910
1905
|
spanId: span.spanId,
|
|
1911
|
-
errorMessage: span.exception?.message ?? span.
|
|
1906
|
+
errorMessage: span.exception?.message ?? span.name ?? "unknown error",
|
|
1912
1907
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1913
1908
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1909
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
1914
1910
|
affectedNode
|
|
1915
1911
|
};
|
|
1916
1912
|
await appendErrorEvent(ctx, ev);
|
|
@@ -1966,8 +1962,7 @@ function rewireFrontierEdges(graph, frontierId2, serviceId3) {
|
|
|
1966
1962
|
}
|
|
1967
1963
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
1968
1964
|
graph.dropEdge(oldEdgeId);
|
|
1969
|
-
const
|
|
1970
|
-
const newId = promotedProvenance === import_types4.Provenance.OBSERVED ? (0, import_types4.observedEdgeId)(newSource, newTarget, edge.type) : promotedProvenance === import_types4.Provenance.INFERRED ? (0, import_types4.inferredEdgeId)(newSource, newTarget, edge.type) : promotedProvenance === import_types4.Provenance.EXTRACTED ? (0, import_types4.extractedEdgeId)(newSource, newTarget, edge.type) : (0, import_types4.frontierEdgeId)(newSource, newTarget, edge.type);
|
|
1965
|
+
const newId = edge.provenance === import_types4.Provenance.OBSERVED ? (0, import_types4.observedEdgeId)(newSource, newTarget, edge.type) : edge.provenance === import_types4.Provenance.INFERRED ? (0, import_types4.inferredEdgeId)(newSource, newTarget, edge.type) : (0, import_types4.extractedEdgeId)(newSource, newTarget, edge.type);
|
|
1971
1966
|
if (graph.hasEdge(newId)) {
|
|
1972
1967
|
const existing = graph.getEdgeAttributes(newId);
|
|
1973
1968
|
const merged = {
|
|
@@ -1982,8 +1977,7 @@ function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
|
1982
1977
|
...edge,
|
|
1983
1978
|
id: newId,
|
|
1984
1979
|
source: newSource,
|
|
1985
|
-
target: newTarget
|
|
1986
|
-
provenance: promotedProvenance
|
|
1980
|
+
target: newTarget
|
|
1987
1981
|
};
|
|
1988
1982
|
graph.addEdgeWithKey(newId, newSource, newTarget, rebuilt);
|
|
1989
1983
|
}
|
|
@@ -2393,6 +2387,23 @@ async function writeExtractionErrors(errors, errorsPath) {
|
|
|
2393
2387
|
const lines = errors.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
2394
2388
|
await import_node_fs7.promises.appendFile(errorsPath, lines, "utf8");
|
|
2395
2389
|
}
|
|
2390
|
+
var droppedSink = [];
|
|
2391
|
+
function noteExtractedDropped(edge) {
|
|
2392
|
+
droppedSink.push(edge);
|
|
2393
|
+
}
|
|
2394
|
+
function drainDroppedExtracted() {
|
|
2395
|
+
return droppedSink.splice(0, droppedSink.length);
|
|
2396
|
+
}
|
|
2397
|
+
function isRejectedLogEnabled() {
|
|
2398
|
+
const raw = process.env.NEAT_EXTRACTED_REJECTED_LOG;
|
|
2399
|
+
return raw === "1" || raw === "true";
|
|
2400
|
+
}
|
|
2401
|
+
async function writeRejectedExtracted(drops, rejectedPath) {
|
|
2402
|
+
if (drops.length === 0) return;
|
|
2403
|
+
await import_node_fs7.promises.mkdir(import_node_path7.default.dirname(rejectedPath), { recursive: true });
|
|
2404
|
+
const lines = drops.map((d) => JSON.stringify({ ...d, ts: (/* @__PURE__ */ new Date()).toISOString() })).join("\n") + "\n";
|
|
2405
|
+
await import_node_fs7.promises.appendFile(rejectedPath, lines, "utf8");
|
|
2406
|
+
}
|
|
2396
2407
|
|
|
2397
2408
|
// src/extract/services.ts
|
|
2398
2409
|
var DEFAULT_SCAN_DEPTH = 5;
|
|
@@ -3331,6 +3342,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
|
|
|
3331
3342
|
target: dbNode.id,
|
|
3332
3343
|
type: import_types8.EdgeType.CONNECTS_TO,
|
|
3333
3344
|
provenance: import_types8.Provenance.EXTRACTED,
|
|
3345
|
+
confidence: (0, import_types8.confidenceForExtracted)("structural"),
|
|
3334
3346
|
// ADR-032 / #140 — every EXTRACTED edge carries evidence.file.
|
|
3335
3347
|
// Ghost-edge cleanup keys retirement on this; the conditional
|
|
3336
3348
|
// sourceFile spread that used to live here was a v0.1.x leftover.
|
|
@@ -3405,6 +3417,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
3405
3417
|
target: node.id,
|
|
3406
3418
|
type: import_types9.EdgeType.CONFIGURED_BY,
|
|
3407
3419
|
provenance: import_types9.Provenance.EXTRACTED,
|
|
3420
|
+
confidence: (0, import_types9.confidenceForExtracted)("structural"),
|
|
3408
3421
|
evidence: { file: relPath.split(import_node_path18.default.sep).join("/") }
|
|
3409
3422
|
};
|
|
3410
3423
|
if (!graph.hasEdge(edge.id)) {
|
|
@@ -3559,17 +3572,32 @@ async function addHttpCallEdges(graph, services) {
|
|
|
3559
3572
|
for (const [targetId, evidenceFile] of seenTargets) {
|
|
3560
3573
|
const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? "";
|
|
3561
3574
|
const line = lineOf(fileContent, `//${evidenceFile.host}`);
|
|
3575
|
+
const confidence = (0, import_types10.confidenceForExtracted)("hostname-shape-match");
|
|
3576
|
+
const ev = {
|
|
3577
|
+
file: import_node_path20.default.relative(service.dir, evidenceFile.file),
|
|
3578
|
+
line,
|
|
3579
|
+
snippet: snippet(fileContent, line)
|
|
3580
|
+
};
|
|
3581
|
+
const edgeId = (0, import_types5.extractedEdgeId)(service.node.id, targetId, import_types10.EdgeType.CALLS);
|
|
3582
|
+
if (!(0, import_types10.passesExtractedFloor)(confidence)) {
|
|
3583
|
+
noteExtractedDropped({
|
|
3584
|
+
source: service.node.id,
|
|
3585
|
+
target: targetId,
|
|
3586
|
+
type: import_types10.EdgeType.CALLS,
|
|
3587
|
+
confidence,
|
|
3588
|
+
confidenceKind: "hostname-shape-match",
|
|
3589
|
+
evidence: ev
|
|
3590
|
+
});
|
|
3591
|
+
continue;
|
|
3592
|
+
}
|
|
3562
3593
|
const edge = {
|
|
3563
|
-
id:
|
|
3594
|
+
id: edgeId,
|
|
3564
3595
|
source: service.node.id,
|
|
3565
3596
|
target: targetId,
|
|
3566
3597
|
type: import_types10.EdgeType.CALLS,
|
|
3567
3598
|
provenance: import_types10.Provenance.EXTRACTED,
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
line,
|
|
3571
|
-
snippet: snippet(fileContent, line)
|
|
3572
|
-
}
|
|
3599
|
+
confidence,
|
|
3600
|
+
evidence: ev
|
|
3573
3601
|
};
|
|
3574
3602
|
if (!graph.hasEdge(edge.id)) {
|
|
3575
3603
|
graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
|
|
@@ -3608,6 +3636,10 @@ function kafkaEndpointsFromFile(file, serviceDir) {
|
|
|
3608
3636
|
name: topic,
|
|
3609
3637
|
kind: "kafka-topic",
|
|
3610
3638
|
edgeType,
|
|
3639
|
+
// `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})` —
|
|
3640
|
+
// framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
|
|
3641
|
+
// tier (ADR-066).
|
|
3642
|
+
confidenceKind: "verified-call-site",
|
|
3611
3643
|
evidence: {
|
|
3612
3644
|
file: import_node_path21.default.relative(serviceDir, file.path),
|
|
3613
3645
|
line,
|
|
@@ -3640,6 +3672,10 @@ function redisEndpointsFromFile(file, serviceDir) {
|
|
|
3640
3672
|
name: host,
|
|
3641
3673
|
kind: "redis",
|
|
3642
3674
|
edgeType: "CALLS",
|
|
3675
|
+
// `redis://host` URL literal — the scheme is structural support, but no
|
|
3676
|
+
// call expression is verified to wire it through. URL-with-structural-
|
|
3677
|
+
// support tier (ADR-066).
|
|
3678
|
+
confidenceKind: "url-with-structural-support",
|
|
3643
3679
|
evidence: {
|
|
3644
3680
|
file: import_node_path22.default.relative(serviceDir, file.path),
|
|
3645
3681
|
line,
|
|
@@ -3681,6 +3717,10 @@ function awsEndpointsFromFile(file, serviceDir) {
|
|
|
3681
3717
|
name,
|
|
3682
3718
|
kind,
|
|
3683
3719
|
edgeType: "CALLS",
|
|
3720
|
+
// SDK marker (S3Client, GetCommand, etc.) plus a Bucket/TableName
|
|
3721
|
+
// literal — framework-aware recognizer, verified-call-site tier
|
|
3722
|
+
// (ADR-066).
|
|
3723
|
+
confidenceKind: "verified-call-site",
|
|
3684
3724
|
evidence: {
|
|
3685
3725
|
file: import_node_path23.default.relative(serviceDir, file.path),
|
|
3686
3726
|
line,
|
|
@@ -3760,6 +3800,10 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
3760
3800
|
name,
|
|
3761
3801
|
kind,
|
|
3762
3802
|
edgeType: "CALLS",
|
|
3803
|
+
// `new <Name>Client(...)` with @aws-sdk/* or @grpc/grpc-js import
|
|
3804
|
+
// context — import-aware classification per #238. Verified-call-site
|
|
3805
|
+
// tier (ADR-066).
|
|
3806
|
+
confidenceKind: "verified-call-site",
|
|
3763
3807
|
evidence: {
|
|
3764
3808
|
file: import_node_path24.default.relative(serviceDir, file.path),
|
|
3765
3809
|
line,
|
|
@@ -3820,6 +3864,18 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3820
3864
|
const edgeId = (0, import_types5.extractedEdgeId)(service.node.id, ep.infraId, edgeType);
|
|
3821
3865
|
if (seenEdges.has(edgeId)) continue;
|
|
3822
3866
|
seenEdges.add(edgeId);
|
|
3867
|
+
const confidence = (0, import_types15.confidenceForExtracted)(ep.confidenceKind);
|
|
3868
|
+
if (!(0, import_types15.passesExtractedFloor)(confidence)) {
|
|
3869
|
+
noteExtractedDropped({
|
|
3870
|
+
source: service.node.id,
|
|
3871
|
+
target: ep.infraId,
|
|
3872
|
+
type: edgeType,
|
|
3873
|
+
confidence,
|
|
3874
|
+
confidenceKind: ep.confidenceKind,
|
|
3875
|
+
evidence: ep.evidence
|
|
3876
|
+
});
|
|
3877
|
+
continue;
|
|
3878
|
+
}
|
|
3823
3879
|
if (!graph.hasEdge(edgeId)) {
|
|
3824
3880
|
const edge = {
|
|
3825
3881
|
id: edgeId,
|
|
@@ -3827,6 +3883,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
3827
3883
|
target: ep.infraId,
|
|
3828
3884
|
type: edgeType,
|
|
3829
3885
|
provenance: import_types15.Provenance.EXTRACTED,
|
|
3886
|
+
confidence,
|
|
3830
3887
|
evidence: ep.evidence
|
|
3831
3888
|
};
|
|
3832
3889
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -3946,6 +4003,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
3946
4003
|
target: targetId,
|
|
3947
4004
|
type: import_types17.EdgeType.DEPENDS_ON,
|
|
3948
4005
|
provenance: import_types17.Provenance.EXTRACTED,
|
|
4006
|
+
confidence: (0, import_types17.confidenceForExtracted)("structural"),
|
|
3949
4007
|
evidence: { file: evidenceFile }
|
|
3950
4008
|
};
|
|
3951
4009
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4006,6 +4064,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4006
4064
|
target: node.id,
|
|
4007
4065
|
type: import_types18.EdgeType.RUNS_ON,
|
|
4008
4066
|
provenance: import_types18.Provenance.EXTRACTED,
|
|
4067
|
+
confidence: (0, import_types18.confidenceForExtracted)("structural"),
|
|
4009
4068
|
evidence: {
|
|
4010
4069
|
file: import_node_path26.default.relative(scanPath, dockerfilePath).split(import_node_path26.default.sep).join("/")
|
|
4011
4070
|
}
|
|
@@ -4122,6 +4181,9 @@ async function addInfra(graph, scanPath, services) {
|
|
|
4122
4181
|
};
|
|
4123
4182
|
}
|
|
4124
4183
|
|
|
4184
|
+
// src/extract/index.ts
|
|
4185
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4186
|
+
|
|
4125
4187
|
// src/extract/retire.ts
|
|
4126
4188
|
init_cjs_shims();
|
|
4127
4189
|
var import_node_fs17 = require("fs");
|
|
@@ -4174,13 +4236,26 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
4174
4236
|
);
|
|
4175
4237
|
}
|
|
4176
4238
|
}
|
|
4239
|
+
const droppedEntries = drainDroppedExtracted();
|
|
4240
|
+
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
4241
|
+
const rejectedPath = import_node_path30.default.join(import_node_path30.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
4242
|
+
try {
|
|
4243
|
+
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
4244
|
+
} catch (err) {
|
|
4245
|
+
console.warn(
|
|
4246
|
+
`[neat] failed to write rejected extracted edges to ${rejectedPath}: ${err.message}`
|
|
4247
|
+
);
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4177
4250
|
const result = {
|
|
4178
4251
|
nodesAdded: phase1Nodes + phase2.nodesAdded + phase3.nodesAdded + phase4.nodesAdded + phase5.nodesAdded,
|
|
4179
4252
|
edgesAdded: phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,
|
|
4180
4253
|
frontiersPromoted,
|
|
4181
4254
|
extractionErrors: errorEntries.length,
|
|
4182
4255
|
errorEntries,
|
|
4183
|
-
ghostsRetired
|
|
4256
|
+
ghostsRetired,
|
|
4257
|
+
extractedDropped: droppedEntries.length,
|
|
4258
|
+
droppedEntries
|
|
4184
4259
|
};
|
|
4185
4260
|
emitNeatEvent({
|
|
4186
4261
|
type: "extraction-complete",
|
|
@@ -4274,23 +4349,23 @@ function canonicalJson(value) {
|
|
|
4274
4349
|
|
|
4275
4350
|
// src/projects.ts
|
|
4276
4351
|
init_cjs_shims();
|
|
4277
|
-
var
|
|
4352
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4278
4353
|
function pathsForProject(project, baseDir) {
|
|
4279
4354
|
if (project === DEFAULT_PROJECT) {
|
|
4280
4355
|
return {
|
|
4281
|
-
snapshotPath:
|
|
4282
|
-
errorsPath:
|
|
4283
|
-
staleEventsPath:
|
|
4284
|
-
embeddingsCachePath:
|
|
4285
|
-
policyViolationsPath:
|
|
4356
|
+
snapshotPath: import_node_path31.default.join(baseDir, "graph.json"),
|
|
4357
|
+
errorsPath: import_node_path31.default.join(baseDir, "errors.ndjson"),
|
|
4358
|
+
staleEventsPath: import_node_path31.default.join(baseDir, "stale-events.ndjson"),
|
|
4359
|
+
embeddingsCachePath: import_node_path31.default.join(baseDir, "embeddings.json"),
|
|
4360
|
+
policyViolationsPath: import_node_path31.default.join(baseDir, "policy-violations.ndjson")
|
|
4286
4361
|
};
|
|
4287
4362
|
}
|
|
4288
4363
|
return {
|
|
4289
|
-
snapshotPath:
|
|
4290
|
-
errorsPath:
|
|
4291
|
-
staleEventsPath:
|
|
4292
|
-
embeddingsCachePath:
|
|
4293
|
-
policyViolationsPath:
|
|
4364
|
+
snapshotPath: import_node_path31.default.join(baseDir, `${project}.json`),
|
|
4365
|
+
errorsPath: import_node_path31.default.join(baseDir, `errors.${project}.ndjson`),
|
|
4366
|
+
staleEventsPath: import_node_path31.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
4367
|
+
embeddingsCachePath: import_node_path31.default.join(baseDir, `embeddings.${project}.json`),
|
|
4368
|
+
policyViolationsPath: import_node_path31.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
4294
4369
|
};
|
|
4295
4370
|
}
|
|
4296
4371
|
var Projects = class {
|
|
@@ -4332,15 +4407,15 @@ function parseExtraProjects(raw) {
|
|
|
4332
4407
|
init_cjs_shims();
|
|
4333
4408
|
var import_node_fs19 = require("fs");
|
|
4334
4409
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
4335
|
-
var
|
|
4410
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
4336
4411
|
var import_types20 = require("@neat.is/types");
|
|
4337
4412
|
function neatHome() {
|
|
4338
4413
|
const override = process.env.NEAT_HOME;
|
|
4339
|
-
if (override && override.length > 0) return
|
|
4340
|
-
return
|
|
4414
|
+
if (override && override.length > 0) return import_node_path32.default.resolve(override);
|
|
4415
|
+
return import_node_path32.default.join(import_node_os2.default.homedir(), ".neat");
|
|
4341
4416
|
}
|
|
4342
4417
|
function registryPath() {
|
|
4343
|
-
return
|
|
4418
|
+
return import_node_path32.default.join(neatHome(), "projects.json");
|
|
4344
4419
|
}
|
|
4345
4420
|
async function readRegistry() {
|
|
4346
4421
|
const file = registryPath();
|
|
@@ -4850,8 +4925,9 @@ async function buildApi(opts) {
|
|
|
4850
4925
|
// src/persist.ts
|
|
4851
4926
|
init_cjs_shims();
|
|
4852
4927
|
var import_node_fs20 = require("fs");
|
|
4853
|
-
var
|
|
4854
|
-
var
|
|
4928
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
4929
|
+
var import_types22 = require("@neat.is/types");
|
|
4930
|
+
var SCHEMA_VERSION = 3;
|
|
4855
4931
|
function migrateV1ToV2(payload) {
|
|
4856
4932
|
const nodes = payload.graph.nodes;
|
|
4857
4933
|
if (Array.isArray(nodes)) {
|
|
@@ -4863,8 +4939,27 @@ function migrateV1ToV2(payload) {
|
|
|
4863
4939
|
}
|
|
4864
4940
|
return { ...payload, schemaVersion: 2 };
|
|
4865
4941
|
}
|
|
4942
|
+
function migrateV2ToV3(payload) {
|
|
4943
|
+
const edges = payload.graph.edges;
|
|
4944
|
+
if (Array.isArray(edges)) {
|
|
4945
|
+
for (const edge of edges) {
|
|
4946
|
+
const attrs = edge.attributes;
|
|
4947
|
+
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
4948
|
+
attrs.provenance = import_types22.Provenance.OBSERVED;
|
|
4949
|
+
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
4950
|
+
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
4951
|
+
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
4952
|
+
if (type && source && target) {
|
|
4953
|
+
const newId = (0, import_types22.observedEdgeId)(source, target, type);
|
|
4954
|
+
attrs.id = newId;
|
|
4955
|
+
if (edge.key) edge.key = newId;
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
return { ...payload, schemaVersion: 3 };
|
|
4960
|
+
}
|
|
4866
4961
|
async function ensureDir(filePath) {
|
|
4867
|
-
await import_node_fs20.promises.mkdir(
|
|
4962
|
+
await import_node_fs20.promises.mkdir(import_node_path33.default.dirname(filePath), { recursive: true });
|
|
4868
4963
|
}
|
|
4869
4964
|
async function saveGraphToDisk(graph, outPath) {
|
|
4870
4965
|
await ensureDir(outPath);
|
|
@@ -4889,6 +4984,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
4889
4984
|
if (payload.schemaVersion === 1) {
|
|
4890
4985
|
payload = migrateV1ToV2(payload);
|
|
4891
4986
|
}
|
|
4987
|
+
if (payload.schemaVersion === 2) {
|
|
4988
|
+
payload = migrateV2ToV3(payload);
|
|
4989
|
+
}
|
|
4892
4990
|
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
4893
4991
|
throw new Error(
|
|
4894
4992
|
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
@@ -4938,7 +5036,7 @@ init_otel_grpc();
|
|
|
4938
5036
|
// src/search.ts
|
|
4939
5037
|
init_cjs_shims();
|
|
4940
5038
|
var import_node_fs21 = require("fs");
|
|
4941
|
-
var
|
|
5039
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
4942
5040
|
var import_node_crypto = require("crypto");
|
|
4943
5041
|
var DEFAULT_LIMIT = 10;
|
|
4944
5042
|
var NOMIC_DIM = 768;
|
|
@@ -5077,7 +5175,7 @@ async function readCache(cachePath) {
|
|
|
5077
5175
|
}
|
|
5078
5176
|
}
|
|
5079
5177
|
async function writeCache(cachePath, cache) {
|
|
5080
|
-
await import_node_fs21.promises.mkdir(
|
|
5178
|
+
await import_node_fs21.promises.mkdir(import_node_path36.default.dirname(cachePath), { recursive: true });
|
|
5081
5179
|
await import_node_fs21.promises.writeFile(cachePath, JSON.stringify(cache));
|
|
5082
5180
|
}
|
|
5083
5181
|
var VectorIndex = class {
|
|
@@ -5257,14 +5355,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
|
|
|
5257
5355
|
async function main() {
|
|
5258
5356
|
const baseDirEnv = process.env.NEAT_OUT_DIR;
|
|
5259
5357
|
const legacyOutPath = process.env.NEAT_OUT_PATH;
|
|
5260
|
-
const baseDir = baseDirEnv ?
|
|
5261
|
-
const defaultScanPath =
|
|
5358
|
+
const baseDir = baseDirEnv ? import_node_path37.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path37.default.resolve(import_node_path37.default.dirname(legacyOutPath)) : import_node_path37.default.resolve("./neat-out");
|
|
5359
|
+
const defaultScanPath = import_node_path37.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
|
|
5262
5360
|
const registry = new Projects();
|
|
5263
5361
|
await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
|
|
5264
5362
|
for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
|
|
5265
5363
|
const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
5266
5364
|
const projectScan = process.env[envKey];
|
|
5267
|
-
await bootProject(registry, name, projectScan ?
|
|
5365
|
+
await bootProject(registry, name, projectScan ? import_node_path37.default.resolve(projectScan) : void 0, baseDir);
|
|
5268
5366
|
}
|
|
5269
5367
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
5270
5368
|
const port = Number(process.env.PORT ?? 8080);
|