@neat.is/core 0.4.15 → 0.4.17
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-VMLWUK7W.js → chunk-CEDXXMGO.js} +16 -2
- package/dist/chunk-CEDXXMGO.js.map +1 -0
- package/dist/{chunk-XS4CGNRO.js → chunk-LUDSPX5N.js} +293 -71
- package/dist/chunk-LUDSPX5N.js.map +1 -0
- package/dist/cli.cjs +923 -590
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +19 -4
- package/dist/cli.d.ts +19 -4
- package/dist/cli.js +291 -186
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +377 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +384 -149
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +269 -97
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +3 -4
- package/dist/chunk-VMLWUK7W.js.map +0 -1
- package/dist/chunk-XS4CGNRO.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -57,9 +57,9 @@ function mountBearerAuth(app, opts) {
|
|
|
57
57
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
58
58
|
const publicRead = opts.publicRead === true;
|
|
59
59
|
app.addHook("preHandler", (req, reply, done) => {
|
|
60
|
-
const
|
|
60
|
+
const path43 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
61
61
|
for (const suffix of suffixes) {
|
|
62
|
-
if (
|
|
62
|
+
if (path43 === suffix || path43.endsWith(suffix)) {
|
|
63
63
|
done();
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
@@ -187,8 +187,8 @@ function reshapeGrpcRequest(req) {
|
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
function resolveProtoRoot() {
|
|
190
|
-
const here =
|
|
191
|
-
return
|
|
190
|
+
const here = import_node_path39.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
191
|
+
return import_node_path39.default.resolve(here, "..", "proto");
|
|
192
192
|
}
|
|
193
193
|
function loadTraceService() {
|
|
194
194
|
const protoRoot = resolveProtoRoot();
|
|
@@ -256,13 +256,13 @@ async function startOtelGrpcReceiver(opts) {
|
|
|
256
256
|
})
|
|
257
257
|
};
|
|
258
258
|
}
|
|
259
|
-
var import_node_url,
|
|
259
|
+
var import_node_url, import_node_path39, import_node_crypto2, grpc, protoLoader;
|
|
260
260
|
var init_otel_grpc = __esm({
|
|
261
261
|
"src/otel-grpc.ts"() {
|
|
262
262
|
"use strict";
|
|
263
263
|
init_cjs_shims();
|
|
264
264
|
import_node_url = require("url");
|
|
265
|
-
|
|
265
|
+
import_node_path39 = __toESM(require("path"), 1);
|
|
266
266
|
import_node_crypto2 = require("crypto");
|
|
267
267
|
grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
268
268
|
protoLoader = __toESM(require("@grpc/proto-loader"), 1);
|
|
@@ -372,10 +372,10 @@ function parseOtlpRequest(body) {
|
|
|
372
372
|
return out;
|
|
373
373
|
}
|
|
374
374
|
function loadProtoRoot() {
|
|
375
|
-
const here =
|
|
376
|
-
const protoRoot =
|
|
375
|
+
const here = import_node_path40.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
376
|
+
const protoRoot = import_node_path40.default.resolve(here, "..", "proto");
|
|
377
377
|
const root = new import_protobufjs.default.Root();
|
|
378
|
-
root.resolvePath = (_origin, target) =>
|
|
378
|
+
root.resolvePath = (_origin, target) => import_node_path40.default.resolve(protoRoot, target);
|
|
379
379
|
root.loadSync(
|
|
380
380
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
381
381
|
{ keepCase: true }
|
|
@@ -579,12 +579,12 @@ function logSpanHandler(span) {
|
|
|
579
579
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
580
580
|
);
|
|
581
581
|
}
|
|
582
|
-
var
|
|
582
|
+
var import_node_path40, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
583
583
|
var init_otel = __esm({
|
|
584
584
|
"src/otel.ts"() {
|
|
585
585
|
"use strict";
|
|
586
586
|
init_cjs_shims();
|
|
587
|
-
|
|
587
|
+
import_node_path40 = __toESM(require("path"), 1);
|
|
588
588
|
import_node_url2 = require("url");
|
|
589
589
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
590
590
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
@@ -1172,19 +1172,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
1172
1172
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
1173
1173
|
let best = { path: [start], edges: [] };
|
|
1174
1174
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
1175
|
-
function step(node,
|
|
1176
|
-
if (
|
|
1177
|
-
best = { path: [...
|
|
1175
|
+
function step(node, path43, edges) {
|
|
1176
|
+
if (path43.length > best.path.length) {
|
|
1177
|
+
best = { path: [...path43], edges: [...edges] };
|
|
1178
1178
|
}
|
|
1179
|
-
if (
|
|
1179
|
+
if (path43.length - 1 >= maxDepth) return;
|
|
1180
1180
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
1181
1181
|
for (const [srcId, edge] of incoming) {
|
|
1182
1182
|
if (visited.has(srcId)) continue;
|
|
1183
1183
|
visited.add(srcId);
|
|
1184
|
-
|
|
1184
|
+
path43.push(srcId);
|
|
1185
1185
|
edges.push(edge);
|
|
1186
|
-
step(srcId,
|
|
1187
|
-
|
|
1186
|
+
step(srcId, path43, edges);
|
|
1187
|
+
path43.pop();
|
|
1188
1188
|
edges.pop();
|
|
1189
1189
|
visited.delete(srcId);
|
|
1190
1190
|
}
|
|
@@ -1724,6 +1724,41 @@ function thresholdForEdgeType(edgeType, overrides) {
|
|
|
1724
1724
|
const map = overrides ?? loadStaleThresholdsFromEnv();
|
|
1725
1725
|
return map[edgeType] ?? FALLBACK_STALE_THRESHOLD_MS;
|
|
1726
1726
|
}
|
|
1727
|
+
var DEFAULT_INCIDENT_THRESHOLDS = {
|
|
1728
|
+
threshold: 5,
|
|
1729
|
+
windowMs: 6e4
|
|
1730
|
+
};
|
|
1731
|
+
function loadIncidentThresholdsFromEnv() {
|
|
1732
|
+
const raw = process.env.NEAT_INCIDENT_THRESHOLDS;
|
|
1733
|
+
if (!raw) return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1734
|
+
try {
|
|
1735
|
+
const overrides = JSON.parse(raw);
|
|
1736
|
+
const merged = { ...DEFAULT_INCIDENT_THRESHOLDS };
|
|
1737
|
+
if (typeof overrides.threshold === "number" && Number.isFinite(overrides.threshold) && overrides.threshold >= 1) {
|
|
1738
|
+
merged.threshold = Math.floor(overrides.threshold);
|
|
1739
|
+
}
|
|
1740
|
+
if (typeof overrides.windowMs === "number" && Number.isFinite(overrides.windowMs) && overrides.windowMs >= 0) {
|
|
1741
|
+
merged.windowMs = overrides.windowMs;
|
|
1742
|
+
}
|
|
1743
|
+
return merged;
|
|
1744
|
+
} catch (err) {
|
|
1745
|
+
console.warn(
|
|
1746
|
+
`[neat] NEAT_INCIDENT_THRESHOLDS could not be parsed (${err.message}); using defaults`
|
|
1747
|
+
);
|
|
1748
|
+
return DEFAULT_INCIDENT_THRESHOLDS;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
function httpResponseStatus(span) {
|
|
1752
|
+
for (const key of ["http.response.status_code", "http.status_code"]) {
|
|
1753
|
+
const v = span.attributes[key];
|
|
1754
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
1755
|
+
if (typeof v === "string") {
|
|
1756
|
+
const n = Number(v);
|
|
1757
|
+
if (Number.isFinite(n)) return n;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return void 0;
|
|
1761
|
+
}
|
|
1727
1762
|
function nowIso(ctx) {
|
|
1728
1763
|
return new Date(ctx.now ? ctx.now() : Date.now()).toISOString();
|
|
1729
1764
|
}
|
|
@@ -2137,6 +2172,71 @@ function makeErrorSpanWriter(errorsPath) {
|
|
|
2137
2172
|
await import_node_fs3.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
2138
2173
|
};
|
|
2139
2174
|
}
|
|
2175
|
+
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
2176
|
+
const attrs = sanitizeAttributes(span.attributes);
|
|
2177
|
+
const first = firstTimestamp ?? timestamp;
|
|
2178
|
+
const peer = pickAddress(span);
|
|
2179
|
+
const message = count > 1 ? `${count} consecutive HTTP ${statusCode} responses` + (peer ? ` to ${peer}` : "") : `HTTP ${statusCode} response` + (peer ? ` from ${peer}` : "");
|
|
2180
|
+
const ev = {
|
|
2181
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
2182
|
+
timestamp,
|
|
2183
|
+
service: span.service,
|
|
2184
|
+
traceId: span.traceId,
|
|
2185
|
+
spanId: span.spanId,
|
|
2186
|
+
errorType: "http-failure",
|
|
2187
|
+
errorMessage: message,
|
|
2188
|
+
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
2189
|
+
affectedNode,
|
|
2190
|
+
httpStatusCode: statusCode,
|
|
2191
|
+
incidentCount: count,
|
|
2192
|
+
firstTimestamp: first,
|
|
2193
|
+
lastTimestamp: timestamp
|
|
2194
|
+
};
|
|
2195
|
+
await appendErrorEvent(ctx, ev);
|
|
2196
|
+
}
|
|
2197
|
+
async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
|
|
2198
|
+
const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
|
|
2199
|
+
if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
|
|
2200
|
+
const peer = pickAddress(span) ?? span.spanId;
|
|
2201
|
+
const key = `${span.service}->${peer}`;
|
|
2202
|
+
const existing = ctx.burstState.get(key);
|
|
2203
|
+
let state;
|
|
2204
|
+
if (existing && nowMs - existing.lastMs <= windowMs) {
|
|
2205
|
+
existing.count += 1;
|
|
2206
|
+
existing.lastTs = ts;
|
|
2207
|
+
existing.lastMs = nowMs;
|
|
2208
|
+
existing.codes.set(status2, (existing.codes.get(status2) ?? 0) + 1);
|
|
2209
|
+
state = existing;
|
|
2210
|
+
} else {
|
|
2211
|
+
state = {
|
|
2212
|
+
count: 1,
|
|
2213
|
+
firstTs: ts,
|
|
2214
|
+
lastTs: ts,
|
|
2215
|
+
lastMs: nowMs,
|
|
2216
|
+
codes: /* @__PURE__ */ new Map([[status2, 1]])
|
|
2217
|
+
};
|
|
2218
|
+
ctx.burstState.set(key, state);
|
|
2219
|
+
}
|
|
2220
|
+
if (state.count < threshold) return;
|
|
2221
|
+
let dominant = status2;
|
|
2222
|
+
let max = 0;
|
|
2223
|
+
for (const [code, n] of state.codes) {
|
|
2224
|
+
if (n > max) {
|
|
2225
|
+
max = n;
|
|
2226
|
+
dominant = code;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
await recordFailingResponseIncident(
|
|
2230
|
+
ctx,
|
|
2231
|
+
span,
|
|
2232
|
+
affectedNode,
|
|
2233
|
+
state.lastTs,
|
|
2234
|
+
dominant,
|
|
2235
|
+
state.count,
|
|
2236
|
+
state.firstTs
|
|
2237
|
+
);
|
|
2238
|
+
ctx.burstState.delete(key);
|
|
2239
|
+
}
|
|
2140
2240
|
async function handleSpan(ctx, span) {
|
|
2141
2241
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
2142
2242
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -2235,6 +2335,14 @@ async function handleSpan(ctx, span) {
|
|
|
2235
2335
|
await appendErrorEvent(ctx, ev);
|
|
2236
2336
|
}
|
|
2237
2337
|
}
|
|
2338
|
+
if (span.statusCode !== 2) {
|
|
2339
|
+
const status2 = httpResponseStatus(span);
|
|
2340
|
+
if (status2 !== void 0 && status2 >= 500) {
|
|
2341
|
+
await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
|
|
2342
|
+
} else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
|
|
2343
|
+
await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2238
2346
|
void affectedNode;
|
|
2239
2347
|
if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph);
|
|
2240
2348
|
}
|
|
@@ -4254,7 +4362,7 @@ async function addConfigNodes(graph, services, scanPath) {
|
|
|
4254
4362
|
|
|
4255
4363
|
// src/extract/calls/index.ts
|
|
4256
4364
|
init_cjs_shims();
|
|
4257
|
-
var
|
|
4365
|
+
var import_types17 = require("@neat.is/types");
|
|
4258
4366
|
|
|
4259
4367
|
// src/extract/calls/http.ts
|
|
4260
4368
|
init_cjs_shims();
|
|
@@ -4607,15 +4715,75 @@ function grpcEndpointsFromFile(file, serviceDir) {
|
|
|
4607
4715
|
return out;
|
|
4608
4716
|
}
|
|
4609
4717
|
|
|
4718
|
+
// src/extract/calls/supabase.ts
|
|
4719
|
+
init_cjs_shims();
|
|
4720
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
4721
|
+
var import_types16 = require("@neat.is/types");
|
|
4722
|
+
var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
|
|
4723
|
+
var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
|
|
4724
|
+
var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
|
|
4725
|
+
function hostFromLiteral(literal) {
|
|
4726
|
+
if (!literal) return null;
|
|
4727
|
+
const m = /^https?:\/\/([^/:'"`\s]+)/.exec(literal.trim());
|
|
4728
|
+
if (!m) return null;
|
|
4729
|
+
const host = m[1];
|
|
4730
|
+
if (!/\.supabase\.(co|in)$/i.test(host)) return null;
|
|
4731
|
+
return host;
|
|
4732
|
+
}
|
|
4733
|
+
function readImports2(content) {
|
|
4734
|
+
return {
|
|
4735
|
+
hasSupabaseJs: SUPABASE_JS_IMPORT_RE.test(content),
|
|
4736
|
+
hasSupabaseSsr: SUPABASE_SSR_IMPORT_RE.test(content)
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
function constructorMatchesImport(name, ctx) {
|
|
4740
|
+
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
4741
|
+
return ctx.hasSupabaseSsr;
|
|
4742
|
+
}
|
|
4743
|
+
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
4744
|
+
const ctx = readImports2(file.content);
|
|
4745
|
+
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
4746
|
+
const out = [];
|
|
4747
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4748
|
+
SUPABASE_CLIENT_RE.lastIndex = 0;
|
|
4749
|
+
let m;
|
|
4750
|
+
while ((m = SUPABASE_CLIENT_RE.exec(file.content)) !== null) {
|
|
4751
|
+
const ctor = m[1];
|
|
4752
|
+
if (!constructorMatchesImport(ctor, ctx)) continue;
|
|
4753
|
+
const host = hostFromLiteral(m[2]);
|
|
4754
|
+
const name = host ?? "env";
|
|
4755
|
+
if (seen.has(name)) continue;
|
|
4756
|
+
seen.add(name);
|
|
4757
|
+
const line = lineOf(file.content, m[0]);
|
|
4758
|
+
out.push({
|
|
4759
|
+
infraId: (0, import_types16.infraId)("supabase", name),
|
|
4760
|
+
name,
|
|
4761
|
+
kind: "supabase",
|
|
4762
|
+
edgeType: "CALLS",
|
|
4763
|
+
// `createClient(...)` from @supabase/supabase-js (or createServerClient /
|
|
4764
|
+
// createBrowserClient from @supabase/ssr) with the import in scope — a
|
|
4765
|
+
// framework-aware recognizer matched the SDK shape. Verified-call-site
|
|
4766
|
+
// tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
|
|
4767
|
+
confidenceKind: "verified-call-site",
|
|
4768
|
+
evidence: {
|
|
4769
|
+
file: import_node_path27.default.relative(serviceDir, file.path),
|
|
4770
|
+
line,
|
|
4771
|
+
snippet: snippet(file.content, line)
|
|
4772
|
+
}
|
|
4773
|
+
});
|
|
4774
|
+
}
|
|
4775
|
+
return out;
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4610
4778
|
// src/extract/calls/index.ts
|
|
4611
4779
|
function edgeTypeFromEndpoint(ep) {
|
|
4612
4780
|
switch (ep.edgeType) {
|
|
4613
4781
|
case "PUBLISHES_TO":
|
|
4614
|
-
return
|
|
4782
|
+
return import_types17.EdgeType.PUBLISHES_TO;
|
|
4615
4783
|
case "CONSUMES_FROM":
|
|
4616
|
-
return
|
|
4784
|
+
return import_types17.EdgeType.CONSUMES_FROM;
|
|
4617
4785
|
default:
|
|
4618
|
-
return
|
|
4786
|
+
return import_types17.EdgeType.CALLS;
|
|
4619
4787
|
}
|
|
4620
4788
|
}
|
|
4621
4789
|
function isAwsKind(kind) {
|
|
@@ -4635,6 +4803,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4635
4803
|
endpoints.push(...redisEndpointsFromFile(maskedFile, service.dir));
|
|
4636
4804
|
endpoints.push(...awsEndpointsFromFile(maskedFile, service.dir));
|
|
4637
4805
|
endpoints.push(...grpcEndpointsFromFile(maskedFile, service.dir));
|
|
4806
|
+
endpoints.push(...supabaseEndpointsFromFile(maskedFile, service.dir));
|
|
4638
4807
|
}
|
|
4639
4808
|
if (endpoints.length === 0) continue;
|
|
4640
4809
|
const seenEdges = /* @__PURE__ */ new Set();
|
|
@@ -4642,7 +4811,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4642
4811
|
if (!graph.hasNode(ep.infraId)) {
|
|
4643
4812
|
const node = {
|
|
4644
4813
|
id: ep.infraId,
|
|
4645
|
-
type:
|
|
4814
|
+
type: import_types17.NodeType.InfraNode,
|
|
4646
4815
|
name: ep.name,
|
|
4647
4816
|
// #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
|
|
4648
4817
|
// aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
|
|
@@ -4654,7 +4823,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4654
4823
|
nodesAdded++;
|
|
4655
4824
|
}
|
|
4656
4825
|
const edgeType = edgeTypeFromEndpoint(ep);
|
|
4657
|
-
const confidence = (0,
|
|
4826
|
+
const confidence = (0, import_types17.confidenceForExtracted)(ep.confidenceKind);
|
|
4658
4827
|
const relFile = toPosix2(ep.evidence.file);
|
|
4659
4828
|
const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
|
|
4660
4829
|
graph,
|
|
@@ -4664,7 +4833,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4664
4833
|
);
|
|
4665
4834
|
nodesAdded += n;
|
|
4666
4835
|
edgesAdded += e;
|
|
4667
|
-
if (!(0,
|
|
4836
|
+
if (!(0, import_types17.passesExtractedFloor)(confidence)) {
|
|
4668
4837
|
noteExtractedDropped({
|
|
4669
4838
|
source: fileNodeId,
|
|
4670
4839
|
target: ep.infraId,
|
|
@@ -4684,7 +4853,7 @@ async function addExternalEndpointEdges(graph, services) {
|
|
|
4684
4853
|
source: fileNodeId,
|
|
4685
4854
|
target: ep.infraId,
|
|
4686
4855
|
type: edgeType,
|
|
4687
|
-
provenance:
|
|
4856
|
+
provenance: import_types17.Provenance.EXTRACTED,
|
|
4688
4857
|
confidence,
|
|
4689
4858
|
evidence: ep.evidence
|
|
4690
4859
|
};
|
|
@@ -4709,16 +4878,16 @@ init_cjs_shims();
|
|
|
4709
4878
|
|
|
4710
4879
|
// src/extract/infra/docker-compose.ts
|
|
4711
4880
|
init_cjs_shims();
|
|
4712
|
-
var
|
|
4713
|
-
var
|
|
4881
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
4882
|
+
var import_types19 = require("@neat.is/types");
|
|
4714
4883
|
|
|
4715
4884
|
// src/extract/infra/shared.ts
|
|
4716
4885
|
init_cjs_shims();
|
|
4717
|
-
var
|
|
4886
|
+
var import_types18 = require("@neat.is/types");
|
|
4718
4887
|
function makeInfraNode(kind, name, provider = "self", extras) {
|
|
4719
4888
|
return {
|
|
4720
|
-
id: (0,
|
|
4721
|
-
type:
|
|
4889
|
+
id: (0, import_types18.infraId)(kind, name),
|
|
4890
|
+
type: import_types18.NodeType.InfraNode,
|
|
4722
4891
|
name,
|
|
4723
4892
|
provider,
|
|
4724
4893
|
kind,
|
|
@@ -4747,7 +4916,7 @@ function dependsOnList(value) {
|
|
|
4747
4916
|
}
|
|
4748
4917
|
function serviceNameToServiceNode(name, services) {
|
|
4749
4918
|
for (const s of services) {
|
|
4750
|
-
if (s.node.name === name ||
|
|
4919
|
+
if (s.node.name === name || import_node_path28.default.basename(s.dir) === name) return s.node.id;
|
|
4751
4920
|
}
|
|
4752
4921
|
return null;
|
|
4753
4922
|
}
|
|
@@ -4756,7 +4925,7 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4756
4925
|
let edgesAdded = 0;
|
|
4757
4926
|
let composePath = null;
|
|
4758
4927
|
for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
|
|
4759
|
-
const abs =
|
|
4928
|
+
const abs = import_node_path28.default.join(scanPath, name);
|
|
4760
4929
|
if (await exists(abs)) {
|
|
4761
4930
|
composePath = abs;
|
|
4762
4931
|
break;
|
|
@@ -4769,13 +4938,13 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4769
4938
|
} catch (err) {
|
|
4770
4939
|
recordExtractionError(
|
|
4771
4940
|
"infra docker-compose",
|
|
4772
|
-
|
|
4941
|
+
import_node_path28.default.relative(scanPath, composePath),
|
|
4773
4942
|
err
|
|
4774
4943
|
);
|
|
4775
4944
|
return { nodesAdded, edgesAdded };
|
|
4776
4945
|
}
|
|
4777
4946
|
if (!compose?.services) return { nodesAdded, edgesAdded };
|
|
4778
|
-
const evidenceFile =
|
|
4947
|
+
const evidenceFile = import_node_path28.default.relative(scanPath, composePath).split(import_node_path28.default.sep).join("/");
|
|
4779
4948
|
const composeNameToNodeId = /* @__PURE__ */ new Map();
|
|
4780
4949
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4781
4950
|
const matchedServiceId = serviceNameToServiceNode(composeName, services);
|
|
@@ -4797,15 +4966,15 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4797
4966
|
for (const dep of dependsOnList(svc.depends_on)) {
|
|
4798
4967
|
const targetId = composeNameToNodeId.get(dep);
|
|
4799
4968
|
if (!targetId) continue;
|
|
4800
|
-
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId,
|
|
4969
|
+
const edgeId = (0, import_types4.extractedEdgeId)(sourceId, targetId, import_types19.EdgeType.DEPENDS_ON);
|
|
4801
4970
|
if (graph.hasEdge(edgeId)) continue;
|
|
4802
4971
|
const edge = {
|
|
4803
4972
|
id: edgeId,
|
|
4804
4973
|
source: sourceId,
|
|
4805
4974
|
target: targetId,
|
|
4806
|
-
type:
|
|
4807
|
-
provenance:
|
|
4808
|
-
confidence: (0,
|
|
4975
|
+
type: import_types19.EdgeType.DEPENDS_ON,
|
|
4976
|
+
provenance: import_types19.Provenance.EXTRACTED,
|
|
4977
|
+
confidence: (0, import_types19.confidenceForExtracted)("structural"),
|
|
4809
4978
|
evidence: { file: evidenceFile }
|
|
4810
4979
|
};
|
|
4811
4980
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4817,9 +4986,9 @@ async function addComposeInfra(graph, scanPath, services) {
|
|
|
4817
4986
|
|
|
4818
4987
|
// src/extract/infra/dockerfile.ts
|
|
4819
4988
|
init_cjs_shims();
|
|
4820
|
-
var
|
|
4989
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
4821
4990
|
var import_node_fs15 = require("fs");
|
|
4822
|
-
var
|
|
4991
|
+
var import_types20 = require("@neat.is/types");
|
|
4823
4992
|
function runtimeImage(content) {
|
|
4824
4993
|
const lines = content.split("\n");
|
|
4825
4994
|
let last = null;
|
|
@@ -4838,7 +5007,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4838
5007
|
let nodesAdded = 0;
|
|
4839
5008
|
let edgesAdded = 0;
|
|
4840
5009
|
for (const service of services) {
|
|
4841
|
-
const dockerfilePath =
|
|
5010
|
+
const dockerfilePath = import_node_path29.default.join(service.dir, "Dockerfile");
|
|
4842
5011
|
if (!await exists(dockerfilePath)) continue;
|
|
4843
5012
|
let content;
|
|
4844
5013
|
try {
|
|
@@ -4846,7 +5015,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4846
5015
|
} catch (err) {
|
|
4847
5016
|
recordExtractionError(
|
|
4848
5017
|
"infra dockerfile",
|
|
4849
|
-
|
|
5018
|
+
import_node_path29.default.relative(scanPath, dockerfilePath),
|
|
4850
5019
|
err
|
|
4851
5020
|
);
|
|
4852
5021
|
continue;
|
|
@@ -4858,7 +5027,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4858
5027
|
graph.addNode(node.id, node);
|
|
4859
5028
|
nodesAdded++;
|
|
4860
5029
|
}
|
|
4861
|
-
const relDockerfile = toPosix2(
|
|
5030
|
+
const relDockerfile = toPosix2(import_node_path29.default.relative(service.dir, dockerfilePath));
|
|
4862
5031
|
const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
|
|
4863
5032
|
graph,
|
|
4864
5033
|
service.pkg.name,
|
|
@@ -4867,17 +5036,17 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4867
5036
|
);
|
|
4868
5037
|
nodesAdded += fn;
|
|
4869
5038
|
edgesAdded += fe;
|
|
4870
|
-
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id,
|
|
5039
|
+
const edgeId = (0, import_types4.extractedEdgeId)(fileNodeId, node.id, import_types20.EdgeType.RUNS_ON);
|
|
4871
5040
|
if (!graph.hasEdge(edgeId)) {
|
|
4872
5041
|
const edge = {
|
|
4873
5042
|
id: edgeId,
|
|
4874
5043
|
source: fileNodeId,
|
|
4875
5044
|
target: node.id,
|
|
4876
|
-
type:
|
|
4877
|
-
provenance:
|
|
4878
|
-
confidence: (0,
|
|
5045
|
+
type: import_types20.EdgeType.RUNS_ON,
|
|
5046
|
+
provenance: import_types20.Provenance.EXTRACTED,
|
|
5047
|
+
confidence: (0, import_types20.confidenceForExtracted)("structural"),
|
|
4879
5048
|
evidence: {
|
|
4880
|
-
file: toPosix2(
|
|
5049
|
+
file: toPosix2(import_node_path29.default.relative(scanPath, dockerfilePath))
|
|
4881
5050
|
}
|
|
4882
5051
|
};
|
|
4883
5052
|
graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
|
|
@@ -4890,7 +5059,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
|
|
|
4890
5059
|
// src/extract/infra/terraform.ts
|
|
4891
5060
|
init_cjs_shims();
|
|
4892
5061
|
var import_node_fs16 = require("fs");
|
|
4893
|
-
var
|
|
5062
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
4894
5063
|
var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
|
|
4895
5064
|
async function walkTfFiles(start, depth = 0, max = 5) {
|
|
4896
5065
|
if (depth > max) return [];
|
|
@@ -4899,11 +5068,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
4899
5068
|
for (const entry of entries) {
|
|
4900
5069
|
if (entry.isDirectory()) {
|
|
4901
5070
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
4902
|
-
const child =
|
|
5071
|
+
const child = import_node_path30.default.join(start, entry.name);
|
|
4903
5072
|
if (await isPythonVenvDir(child)) continue;
|
|
4904
5073
|
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
4905
5074
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
4906
|
-
out.push(
|
|
5075
|
+
out.push(import_node_path30.default.join(start, entry.name));
|
|
4907
5076
|
}
|
|
4908
5077
|
}
|
|
4909
5078
|
return out;
|
|
@@ -4931,7 +5100,7 @@ async function addTerraformResources(graph, scanPath) {
|
|
|
4931
5100
|
// src/extract/infra/k8s.ts
|
|
4932
5101
|
init_cjs_shims();
|
|
4933
5102
|
var import_node_fs17 = require("fs");
|
|
4934
|
-
var
|
|
5103
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
4935
5104
|
var import_yaml3 = require("yaml");
|
|
4936
5105
|
var K8S_KIND_TO_INFRA_KIND = {
|
|
4937
5106
|
Service: "k8s-service",
|
|
@@ -4949,11 +5118,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
4949
5118
|
for (const entry of entries) {
|
|
4950
5119
|
if (entry.isDirectory()) {
|
|
4951
5120
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
4952
|
-
const child =
|
|
5121
|
+
const child = import_node_path31.default.join(start, entry.name);
|
|
4953
5122
|
if (await isPythonVenvDir(child)) continue;
|
|
4954
5123
|
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
4955
|
-
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(
|
|
4956
|
-
out.push(
|
|
5124
|
+
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path31.default.extname(entry.name))) {
|
|
5125
|
+
out.push(import_node_path31.default.join(start, entry.name));
|
|
4957
5126
|
}
|
|
4958
5127
|
}
|
|
4959
5128
|
return out;
|
|
@@ -4997,17 +5166,17 @@ async function addInfra(graph, scanPath, services) {
|
|
|
4997
5166
|
}
|
|
4998
5167
|
|
|
4999
5168
|
// src/extract/index.ts
|
|
5000
|
-
var
|
|
5169
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
5001
5170
|
|
|
5002
5171
|
// src/extract/retire.ts
|
|
5003
5172
|
init_cjs_shims();
|
|
5004
5173
|
var import_node_fs18 = require("fs");
|
|
5005
|
-
var
|
|
5006
|
-
var
|
|
5174
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
5175
|
+
var import_types21 = require("@neat.is/types");
|
|
5007
5176
|
function dropOrphanedFileNodes(graph) {
|
|
5008
5177
|
const orphans = [];
|
|
5009
5178
|
graph.forEachNode((id, attrs) => {
|
|
5010
|
-
if (attrs.type !==
|
|
5179
|
+
if (attrs.type !== import_types21.NodeType.FileNode) return;
|
|
5011
5180
|
if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
|
|
5012
5181
|
orphans.push(id);
|
|
5013
5182
|
}
|
|
@@ -5020,14 +5189,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
|
|
|
5020
5189
|
const bases = [scanPath, ...serviceDirs];
|
|
5021
5190
|
graph.forEachEdge((id, attrs) => {
|
|
5022
5191
|
const edge = attrs;
|
|
5023
|
-
if (edge.provenance !==
|
|
5192
|
+
if (edge.provenance !== import_types21.Provenance.EXTRACTED) return;
|
|
5024
5193
|
const evidenceFile = edge.evidence?.file;
|
|
5025
5194
|
if (!evidenceFile) return;
|
|
5026
|
-
if (
|
|
5195
|
+
if (import_node_path32.default.isAbsolute(evidenceFile)) {
|
|
5027
5196
|
if (!(0, import_node_fs18.existsSync)(evidenceFile)) toDrop.push(id);
|
|
5028
5197
|
return;
|
|
5029
5198
|
}
|
|
5030
|
-
const found = bases.some((base) => (0, import_node_fs18.existsSync)(
|
|
5199
|
+
const found = bases.some((base) => (0, import_node_fs18.existsSync)(import_node_path32.default.join(base, evidenceFile)));
|
|
5031
5200
|
if (!found) toDrop.push(id);
|
|
5032
5201
|
});
|
|
5033
5202
|
for (const id of toDrop) graph.dropEdge(id);
|
|
@@ -5067,7 +5236,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5067
5236
|
}
|
|
5068
5237
|
const droppedEntries = drainDroppedExtracted();
|
|
5069
5238
|
if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
|
|
5070
|
-
const rejectedPath =
|
|
5239
|
+
const rejectedPath = import_node_path33.default.join(import_node_path33.default.dirname(opts.errorsPath), "rejected.ndjson");
|
|
5071
5240
|
try {
|
|
5072
5241
|
await writeRejectedExtracted(droppedEntries, rejectedPath);
|
|
5073
5242
|
} catch (err) {
|
|
@@ -5102,8 +5271,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
|
|
|
5102
5271
|
// src/persist.ts
|
|
5103
5272
|
init_cjs_shims();
|
|
5104
5273
|
var import_node_fs19 = require("fs");
|
|
5105
|
-
var
|
|
5106
|
-
var
|
|
5274
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
5275
|
+
var import_types22 = require("@neat.is/types");
|
|
5107
5276
|
var SCHEMA_VERSION = 4;
|
|
5108
5277
|
function migrateV1ToV2(payload) {
|
|
5109
5278
|
const nodes = payload.graph.nodes;
|
|
@@ -5125,12 +5294,12 @@ function migrateV2ToV3(payload) {
|
|
|
5125
5294
|
for (const edge of edges) {
|
|
5126
5295
|
const attrs = edge.attributes;
|
|
5127
5296
|
if (!attrs || attrs.provenance !== "FRONTIER") continue;
|
|
5128
|
-
attrs.provenance =
|
|
5297
|
+
attrs.provenance = import_types22.Provenance.OBSERVED;
|
|
5129
5298
|
const type = typeof attrs.type === "string" ? attrs.type : void 0;
|
|
5130
5299
|
const source = typeof attrs.source === "string" ? attrs.source : void 0;
|
|
5131
5300
|
const target = typeof attrs.target === "string" ? attrs.target : void 0;
|
|
5132
5301
|
if (type && source && target) {
|
|
5133
|
-
const newId = (0,
|
|
5302
|
+
const newId = (0, import_types22.observedEdgeId)(source, target, type);
|
|
5134
5303
|
attrs.id = newId;
|
|
5135
5304
|
if (edge.key) edge.key = newId;
|
|
5136
5305
|
}
|
|
@@ -5139,7 +5308,7 @@ function migrateV2ToV3(payload) {
|
|
|
5139
5308
|
return { ...payload, schemaVersion: 3 };
|
|
5140
5309
|
}
|
|
5141
5310
|
async function ensureDir(filePath) {
|
|
5142
|
-
await import_node_fs19.promises.mkdir(
|
|
5311
|
+
await import_node_fs19.promises.mkdir(import_node_path34.default.dirname(filePath), { recursive: true });
|
|
5143
5312
|
}
|
|
5144
5313
|
async function saveGraphToDisk(graph, outPath) {
|
|
5145
5314
|
await ensureDir(outPath);
|
|
@@ -5216,19 +5385,19 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5216
5385
|
init_cjs_shims();
|
|
5217
5386
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
5218
5387
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
5219
|
-
var
|
|
5388
|
+
var import_types25 = require("@neat.is/types");
|
|
5220
5389
|
|
|
5221
5390
|
// src/extend/index.ts
|
|
5222
5391
|
init_cjs_shims();
|
|
5223
5392
|
var import_node_fs21 = require("fs");
|
|
5224
|
-
var
|
|
5393
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
5225
5394
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
5226
5395
|
var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
|
|
5227
5396
|
|
|
5228
5397
|
// src/installers/package-manager.ts
|
|
5229
5398
|
init_cjs_shims();
|
|
5230
5399
|
var import_node_fs20 = require("fs");
|
|
5231
|
-
var
|
|
5400
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
5232
5401
|
var import_node_child_process = require("child_process");
|
|
5233
5402
|
var LOCKFILE_PRIORITY = [
|
|
5234
5403
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -5250,22 +5419,22 @@ async function exists2(p) {
|
|
|
5250
5419
|
}
|
|
5251
5420
|
}
|
|
5252
5421
|
async function detectPackageManager(serviceDir) {
|
|
5253
|
-
let dir =
|
|
5422
|
+
let dir = import_node_path35.default.resolve(serviceDir);
|
|
5254
5423
|
const stops = /* @__PURE__ */ new Set();
|
|
5255
5424
|
for (let i = 0; i < 64; i++) {
|
|
5256
5425
|
if (stops.has(dir)) break;
|
|
5257
5426
|
stops.add(dir);
|
|
5258
5427
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
5259
|
-
const lockPath =
|
|
5428
|
+
const lockPath = import_node_path35.default.join(dir, candidate.lockfile);
|
|
5260
5429
|
if (await exists2(lockPath)) {
|
|
5261
5430
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
5262
5431
|
}
|
|
5263
5432
|
}
|
|
5264
|
-
const parent =
|
|
5433
|
+
const parent = import_node_path35.default.dirname(dir);
|
|
5265
5434
|
if (parent === dir) break;
|
|
5266
5435
|
dir = parent;
|
|
5267
5436
|
}
|
|
5268
|
-
return { pm: "npm", cwd:
|
|
5437
|
+
return { pm: "npm", cwd: import_node_path35.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
5269
5438
|
}
|
|
5270
5439
|
async function runPackageManagerInstall(cmd) {
|
|
5271
5440
|
return new Promise((resolve) => {
|
|
@@ -5314,7 +5483,7 @@ async function fileExists2(p) {
|
|
|
5314
5483
|
}
|
|
5315
5484
|
}
|
|
5316
5485
|
async function readPackageJson(scanPath) {
|
|
5317
|
-
const pkgPath =
|
|
5486
|
+
const pkgPath = import_node_path36.default.join(scanPath, "package.json");
|
|
5318
5487
|
const raw = await import_node_fs21.promises.readFile(pkgPath, "utf8");
|
|
5319
5488
|
return JSON.parse(raw);
|
|
5320
5489
|
}
|
|
@@ -5325,11 +5494,11 @@ async function findHookFiles(scanPath) {
|
|
|
5325
5494
|
).sort();
|
|
5326
5495
|
}
|
|
5327
5496
|
function extendLogPath() {
|
|
5328
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
5497
|
+
return process.env.NEAT_EXTEND_LOG ?? import_node_path36.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
|
|
5329
5498
|
}
|
|
5330
5499
|
async function appendExtendLog(entry) {
|
|
5331
5500
|
const logPath = extendLogPath();
|
|
5332
|
-
await import_node_fs21.promises.mkdir(
|
|
5501
|
+
await import_node_fs21.promises.mkdir(import_node_path36.default.dirname(logPath), { recursive: true });
|
|
5333
5502
|
await import_node_fs21.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
5334
5503
|
}
|
|
5335
5504
|
function splicedContent(fileContent, snippet2) {
|
|
@@ -5388,7 +5557,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
5388
5557
|
}
|
|
5389
5558
|
async function describeProjectInstrumentation(ctx) {
|
|
5390
5559
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5391
|
-
const envNeat = await fileExists2(
|
|
5560
|
+
const envNeat = await fileExists2(import_node_path36.default.join(ctx.scanPath, ".env.neat"));
|
|
5392
5561
|
const registryInstrPackages = new Set(
|
|
5393
5562
|
(0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
5394
5563
|
);
|
|
@@ -5410,16 +5579,16 @@ async function applyExtension(ctx, args, options) {
|
|
|
5410
5579
|
);
|
|
5411
5580
|
}
|
|
5412
5581
|
for (const file of hookFiles) {
|
|
5413
|
-
const content = await import_node_fs21.promises.readFile(
|
|
5582
|
+
const content = await import_node_fs21.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
|
|
5414
5583
|
if (content.includes(args.registration_snippet)) {
|
|
5415
5584
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
5416
5585
|
}
|
|
5417
5586
|
}
|
|
5418
5587
|
const primaryFile = hookFiles[0];
|
|
5419
|
-
const primaryPath =
|
|
5588
|
+
const primaryPath = import_node_path36.default.join(ctx.scanPath, primaryFile);
|
|
5420
5589
|
const filesTouched = [];
|
|
5421
5590
|
const depsAdded = [];
|
|
5422
|
-
const pkgPath =
|
|
5591
|
+
const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
|
|
5423
5592
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5424
5593
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
5425
5594
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
@@ -5465,7 +5634,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5465
5634
|
};
|
|
5466
5635
|
}
|
|
5467
5636
|
for (const file of hookFiles) {
|
|
5468
|
-
const content = await import_node_fs21.promises.readFile(
|
|
5637
|
+
const content = await import_node_fs21.promises.readFile(import_node_path36.default.join(ctx.scanPath, file), "utf8");
|
|
5469
5638
|
if (content.includes(args.registration_snippet)) {
|
|
5470
5639
|
return {
|
|
5471
5640
|
library: args.library,
|
|
@@ -5487,7 +5656,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
5487
5656
|
depsToAdd.push(`${args.instrumentation_package}@${args.version}`);
|
|
5488
5657
|
filesTouched.push("package.json");
|
|
5489
5658
|
}
|
|
5490
|
-
const hookContent = await import_node_fs21.promises.readFile(
|
|
5659
|
+
const hookContent = await import_node_fs21.promises.readFile(import_node_path36.default.join(ctx.scanPath, primaryFile), "utf8");
|
|
5491
5660
|
const patched = splicedContent(hookContent, args.registration_snippet);
|
|
5492
5661
|
if (patched) {
|
|
5493
5662
|
filesTouched.push(primaryFile);
|
|
@@ -5508,7 +5677,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5508
5677
|
if (!match) {
|
|
5509
5678
|
return { undone: false, message: "no apply found for library" };
|
|
5510
5679
|
}
|
|
5511
|
-
const pkgPath =
|
|
5680
|
+
const pkgPath = import_node_path36.default.join(ctx.scanPath, "package.json");
|
|
5512
5681
|
if (await fileExists2(pkgPath)) {
|
|
5513
5682
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
5514
5683
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
@@ -5519,7 +5688,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5519
5688
|
}
|
|
5520
5689
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
5521
5690
|
for (const file of hookFiles) {
|
|
5522
|
-
const filePath =
|
|
5691
|
+
const filePath = import_node_path36.default.join(ctx.scanPath, file);
|
|
5523
5692
|
const content = await import_node_fs21.promises.readFile(filePath, "utf8");
|
|
5524
5693
|
if (content.includes(match.registration_snippet)) {
|
|
5525
5694
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
@@ -5535,7 +5704,7 @@ async function rollbackExtension(ctx, args) {
|
|
|
5535
5704
|
|
|
5536
5705
|
// src/divergences.ts
|
|
5537
5706
|
init_cjs_shims();
|
|
5538
|
-
var
|
|
5707
|
+
var import_types23 = require("@neat.is/types");
|
|
5539
5708
|
function bucketKey(source, target, type) {
|
|
5540
5709
|
return `${type}|${source}|${target}`;
|
|
5541
5710
|
}
|
|
@@ -5543,22 +5712,22 @@ function bucketEdges(graph) {
|
|
|
5543
5712
|
const buckets = /* @__PURE__ */ new Map();
|
|
5544
5713
|
graph.forEachEdge((id, attrs) => {
|
|
5545
5714
|
const e = attrs;
|
|
5546
|
-
const parsed = (0,
|
|
5715
|
+
const parsed = (0, import_types23.parseEdgeId)(id);
|
|
5547
5716
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
5548
5717
|
const key = bucketKey(e.source, e.target, e.type);
|
|
5549
5718
|
const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
|
|
5550
5719
|
switch (provenance) {
|
|
5551
|
-
case
|
|
5720
|
+
case import_types23.Provenance.EXTRACTED:
|
|
5552
5721
|
cur.extracted = e;
|
|
5553
5722
|
break;
|
|
5554
|
-
case
|
|
5723
|
+
case import_types23.Provenance.OBSERVED:
|
|
5555
5724
|
cur.observed = e;
|
|
5556
5725
|
break;
|
|
5557
|
-
case
|
|
5726
|
+
case import_types23.Provenance.INFERRED:
|
|
5558
5727
|
cur.inferred = e;
|
|
5559
5728
|
break;
|
|
5560
5729
|
default:
|
|
5561
|
-
if (e.provenance ===
|
|
5730
|
+
if (e.provenance === import_types23.Provenance.STALE) cur.stale = e;
|
|
5562
5731
|
}
|
|
5563
5732
|
buckets.set(key, cur);
|
|
5564
5733
|
});
|
|
@@ -5567,7 +5736,7 @@ function bucketEdges(graph) {
|
|
|
5567
5736
|
function nodeIsFrontier(graph, nodeId) {
|
|
5568
5737
|
if (!graph.hasNode(nodeId)) return false;
|
|
5569
5738
|
const attrs = graph.getNodeAttributes(nodeId);
|
|
5570
|
-
return attrs.type ===
|
|
5739
|
+
return attrs.type === import_types23.NodeType.FrontierNode;
|
|
5571
5740
|
}
|
|
5572
5741
|
function clampConfidence(n) {
|
|
5573
5742
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -5588,7 +5757,7 @@ function gradedConfidence(edge) {
|
|
|
5588
5757
|
}
|
|
5589
5758
|
function detectMissingDivergences(graph, bucket) {
|
|
5590
5759
|
const out = [];
|
|
5591
|
-
if (bucket.type ===
|
|
5760
|
+
if (bucket.type === import_types23.EdgeType.CONTAINS) return out;
|
|
5592
5761
|
if (bucket.extracted && !bucket.observed) {
|
|
5593
5762
|
if (!nodeIsFrontier(graph, bucket.target)) {
|
|
5594
5763
|
out.push({
|
|
@@ -5629,7 +5798,7 @@ function declaredHostFor(svc) {
|
|
|
5629
5798
|
function hasExtractedConfiguredBy(graph, svcId) {
|
|
5630
5799
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5631
5800
|
const e = graph.getEdgeAttributes(edgeId);
|
|
5632
|
-
if (e.type ===
|
|
5801
|
+
if (e.type === import_types23.EdgeType.CONFIGURED_BY && e.provenance === import_types23.Provenance.EXTRACTED) {
|
|
5633
5802
|
return true;
|
|
5634
5803
|
}
|
|
5635
5804
|
}
|
|
@@ -5642,10 +5811,10 @@ function detectHostMismatch(graph, svcId, svc) {
|
|
|
5642
5811
|
const out = [];
|
|
5643
5812
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5644
5813
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5645
|
-
if (edge.type !==
|
|
5646
|
-
if (edge.provenance !==
|
|
5814
|
+
if (edge.type !== import_types23.EdgeType.CONNECTS_TO) continue;
|
|
5815
|
+
if (edge.provenance !== import_types23.Provenance.OBSERVED) continue;
|
|
5647
5816
|
const target = graph.getNodeAttributes(edge.target);
|
|
5648
|
-
if (target.type !==
|
|
5817
|
+
if (target.type !== import_types23.NodeType.DatabaseNode) continue;
|
|
5649
5818
|
const observedHost = target.host?.trim();
|
|
5650
5819
|
if (!observedHost) continue;
|
|
5651
5820
|
if (observedHost === declaredHost) continue;
|
|
@@ -5667,10 +5836,10 @@ function detectCompatDivergences(graph, svcId, svc) {
|
|
|
5667
5836
|
const deps = svc.dependencies ?? {};
|
|
5668
5837
|
for (const edgeId of graph.outboundEdges(svcId)) {
|
|
5669
5838
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
5670
|
-
if (edge.type !==
|
|
5671
|
-
if (edge.provenance !==
|
|
5839
|
+
if (edge.type !== import_types23.EdgeType.CONNECTS_TO) continue;
|
|
5840
|
+
if (edge.provenance !== import_types23.Provenance.OBSERVED) continue;
|
|
5672
5841
|
const target = graph.getNodeAttributes(edge.target);
|
|
5673
|
-
if (target.type !==
|
|
5842
|
+
if (target.type !== import_types23.NodeType.DatabaseNode) continue;
|
|
5674
5843
|
for (const pair of compatPairs()) {
|
|
5675
5844
|
if (pair.engine !== target.engine) continue;
|
|
5676
5845
|
const declared = deps[pair.driver];
|
|
@@ -5731,7 +5900,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5731
5900
|
}
|
|
5732
5901
|
graph.forEachNode((nodeId, attrs) => {
|
|
5733
5902
|
const n = attrs;
|
|
5734
|
-
if (n.type !==
|
|
5903
|
+
if (n.type !== import_types23.NodeType.ServiceNode) return;
|
|
5735
5904
|
const svc = n;
|
|
5736
5905
|
for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
|
|
5737
5906
|
for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
|
|
@@ -5764,7 +5933,7 @@ function computeDivergences(graph, opts = {}) {
|
|
|
5764
5933
|
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
5765
5934
|
return a.target.localeCompare(b.target);
|
|
5766
5935
|
});
|
|
5767
|
-
return
|
|
5936
|
+
return import_types23.DivergenceResultSchema.parse({
|
|
5768
5937
|
divergences: filtered,
|
|
5769
5938
|
totalAffected: filtered.length,
|
|
5770
5939
|
computedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5850,23 +6019,23 @@ function canonicalJson(value) {
|
|
|
5850
6019
|
|
|
5851
6020
|
// src/projects.ts
|
|
5852
6021
|
init_cjs_shims();
|
|
5853
|
-
var
|
|
6022
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
5854
6023
|
function pathsForProject(project, baseDir) {
|
|
5855
6024
|
if (project === DEFAULT_PROJECT) {
|
|
5856
6025
|
return {
|
|
5857
|
-
snapshotPath:
|
|
5858
|
-
errorsPath:
|
|
5859
|
-
staleEventsPath:
|
|
5860
|
-
embeddingsCachePath:
|
|
5861
|
-
policyViolationsPath:
|
|
6026
|
+
snapshotPath: import_node_path37.default.join(baseDir, "graph.json"),
|
|
6027
|
+
errorsPath: import_node_path37.default.join(baseDir, "errors.ndjson"),
|
|
6028
|
+
staleEventsPath: import_node_path37.default.join(baseDir, "stale-events.ndjson"),
|
|
6029
|
+
embeddingsCachePath: import_node_path37.default.join(baseDir, "embeddings.json"),
|
|
6030
|
+
policyViolationsPath: import_node_path37.default.join(baseDir, "policy-violations.ndjson")
|
|
5862
6031
|
};
|
|
5863
6032
|
}
|
|
5864
6033
|
return {
|
|
5865
|
-
snapshotPath:
|
|
5866
|
-
errorsPath:
|
|
5867
|
-
staleEventsPath:
|
|
5868
|
-
embeddingsCachePath:
|
|
5869
|
-
policyViolationsPath:
|
|
6034
|
+
snapshotPath: import_node_path37.default.join(baseDir, `${project}.json`),
|
|
6035
|
+
errorsPath: import_node_path37.default.join(baseDir, `errors.${project}.ndjson`),
|
|
6036
|
+
staleEventsPath: import_node_path37.default.join(baseDir, `stale-events.${project}.ndjson`),
|
|
6037
|
+
embeddingsCachePath: import_node_path37.default.join(baseDir, `embeddings.${project}.json`),
|
|
6038
|
+
policyViolationsPath: import_node_path37.default.join(baseDir, `policy-violations.${project}.ndjson`)
|
|
5870
6039
|
};
|
|
5871
6040
|
}
|
|
5872
6041
|
var Projects = class {
|
|
@@ -5904,23 +6073,23 @@ var Projects = class {
|
|
|
5904
6073
|
init_cjs_shims();
|
|
5905
6074
|
var import_node_fs23 = require("fs");
|
|
5906
6075
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
5907
|
-
var
|
|
5908
|
-
var
|
|
6076
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
6077
|
+
var import_types24 = require("@neat.is/types");
|
|
5909
6078
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
5910
6079
|
var LOCK_RETRY_MS = 50;
|
|
5911
6080
|
function neatHome() {
|
|
5912
6081
|
const override = process.env.NEAT_HOME;
|
|
5913
|
-
if (override && override.length > 0) return
|
|
5914
|
-
return
|
|
6082
|
+
if (override && override.length > 0) return import_node_path38.default.resolve(override);
|
|
6083
|
+
return import_node_path38.default.join(import_node_os3.default.homedir(), ".neat");
|
|
5915
6084
|
}
|
|
5916
6085
|
function registryPath() {
|
|
5917
|
-
return
|
|
6086
|
+
return import_node_path38.default.join(neatHome(), "projects.json");
|
|
5918
6087
|
}
|
|
5919
6088
|
function registryLockPath() {
|
|
5920
|
-
return
|
|
6089
|
+
return import_node_path38.default.join(neatHome(), "projects.json.lock");
|
|
5921
6090
|
}
|
|
5922
6091
|
function daemonPidPath() {
|
|
5923
|
-
return
|
|
6092
|
+
return import_node_path38.default.join(neatHome(), "neatd.pid");
|
|
5924
6093
|
}
|
|
5925
6094
|
function isPidAliveDefault(pid) {
|
|
5926
6095
|
try {
|
|
@@ -5980,7 +6149,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
|
5980
6149
|
}
|
|
5981
6150
|
}
|
|
5982
6151
|
async function normalizeProjectPath(input) {
|
|
5983
|
-
const resolved =
|
|
6152
|
+
const resolved = import_node_path38.default.resolve(input);
|
|
5984
6153
|
try {
|
|
5985
6154
|
return await import_node_fs23.promises.realpath(resolved);
|
|
5986
6155
|
} catch {
|
|
@@ -5988,7 +6157,7 @@ async function normalizeProjectPath(input) {
|
|
|
5988
6157
|
}
|
|
5989
6158
|
}
|
|
5990
6159
|
async function writeAtomically(target, contents) {
|
|
5991
|
-
await import_node_fs23.promises.mkdir(
|
|
6160
|
+
await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(target), { recursive: true });
|
|
5992
6161
|
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
5993
6162
|
const fd = await import_node_fs23.promises.open(tmp, "w");
|
|
5994
6163
|
try {
|
|
@@ -6001,7 +6170,7 @@ async function writeAtomically(target, contents) {
|
|
|
6001
6170
|
}
|
|
6002
6171
|
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
6003
6172
|
const deadline = Date.now() + timeoutMs;
|
|
6004
|
-
await import_node_fs23.promises.mkdir(
|
|
6173
|
+
await import_node_fs23.promises.mkdir(import_node_path38.default.dirname(lockPath), { recursive: true });
|
|
6005
6174
|
let probedHolder = false;
|
|
6006
6175
|
while (true) {
|
|
6007
6176
|
try {
|
|
@@ -6054,10 +6223,10 @@ async function readRegistry() {
|
|
|
6054
6223
|
throw err;
|
|
6055
6224
|
}
|
|
6056
6225
|
const parsed = JSON.parse(raw);
|
|
6057
|
-
return
|
|
6226
|
+
return import_types24.RegistryFileSchema.parse(parsed);
|
|
6058
6227
|
}
|
|
6059
6228
|
async function writeRegistry(reg) {
|
|
6060
|
-
const validated =
|
|
6229
|
+
const validated = import_types24.RegistryFileSchema.parse(reg);
|
|
6061
6230
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
6062
6231
|
}
|
|
6063
6232
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -6137,6 +6306,58 @@ async function removeProject(name) {
|
|
|
6137
6306
|
return removed;
|
|
6138
6307
|
});
|
|
6139
6308
|
}
|
|
6309
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
6310
|
+
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
6311
|
+
function pruneTtlMs() {
|
|
6312
|
+
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
6313
|
+
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
6314
|
+
const n = Number.parseInt(raw, 10);
|
|
6315
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
6316
|
+
console.warn(
|
|
6317
|
+
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
6318
|
+
);
|
|
6319
|
+
return DEFAULT_PRUNE_TTL_MS;
|
|
6320
|
+
}
|
|
6321
|
+
async function statPathStatus(p) {
|
|
6322
|
+
try {
|
|
6323
|
+
const stat = await import_node_fs23.promises.stat(p);
|
|
6324
|
+
return stat.isDirectory() ? "present" : "unknown";
|
|
6325
|
+
} catch (err) {
|
|
6326
|
+
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
async function pruneRegistry(opts = {}) {
|
|
6330
|
+
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
6331
|
+
const statPath = opts.statPath ?? statPathStatus;
|
|
6332
|
+
const now = opts.now ?? Date.now;
|
|
6333
|
+
return withLock(async () => {
|
|
6334
|
+
const reg = await readRegistry();
|
|
6335
|
+
const removed = [];
|
|
6336
|
+
const kept = [];
|
|
6337
|
+
for (const entry of reg.projects) {
|
|
6338
|
+
const status2 = await statPath(entry.path);
|
|
6339
|
+
if (status2 !== "gone") {
|
|
6340
|
+
kept.push(entry);
|
|
6341
|
+
continue;
|
|
6342
|
+
}
|
|
6343
|
+
if (ttlMs <= 0) {
|
|
6344
|
+
removed.push(entry);
|
|
6345
|
+
continue;
|
|
6346
|
+
}
|
|
6347
|
+
const lastSeen = Date.parse(entry.lastSeenAt ?? entry.registeredAt);
|
|
6348
|
+
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
6349
|
+
if (age > ttlMs) {
|
|
6350
|
+
removed.push(entry);
|
|
6351
|
+
} else {
|
|
6352
|
+
kept.push(entry);
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
if (removed.length === 0) return [];
|
|
6356
|
+
reg.projects = kept;
|
|
6357
|
+
await writeRegistry(reg);
|
|
6358
|
+
return removed;
|
|
6359
|
+
});
|
|
6360
|
+
}
|
|
6140
6361
|
|
|
6141
6362
|
// src/streaming.ts
|
|
6142
6363
|
init_cjs_shims();
|
|
@@ -6150,6 +6371,7 @@ function handleSse(req, reply, opts) {
|
|
|
6150
6371
|
reply.raw.setHeader("Connection", "keep-alive");
|
|
6151
6372
|
reply.raw.setHeader("X-Accel-Buffering", "no");
|
|
6152
6373
|
reply.raw.flushHeaders?.();
|
|
6374
|
+
reply.raw.write(":open\n\n");
|
|
6153
6375
|
let pending = 0;
|
|
6154
6376
|
let dropped = false;
|
|
6155
6377
|
const closeConnection = () => {
|
|
@@ -6329,11 +6551,11 @@ function registerRoutes(scope, ctx) {
|
|
|
6329
6551
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
6330
6552
|
const parsed = [];
|
|
6331
6553
|
for (const c of candidates) {
|
|
6332
|
-
const r =
|
|
6554
|
+
const r = import_types25.DivergenceTypeSchema.safeParse(c);
|
|
6333
6555
|
if (!r.success) {
|
|
6334
6556
|
return reply.code(400).send({
|
|
6335
6557
|
error: `unknown divergence type "${c}"`,
|
|
6336
|
-
allowed:
|
|
6558
|
+
allowed: import_types25.DivergenceTypeSchema.options
|
|
6337
6559
|
});
|
|
6338
6560
|
}
|
|
6339
6561
|
parsed.push(r.data);
|
|
@@ -6546,7 +6768,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6546
6768
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6547
6769
|
let violations = await log.readAll();
|
|
6548
6770
|
if (req.query.severity) {
|
|
6549
|
-
const sev =
|
|
6771
|
+
const sev = import_types25.PolicySeveritySchema.safeParse(req.query.severity);
|
|
6550
6772
|
if (!sev.success) {
|
|
6551
6773
|
return reply.code(400).send({
|
|
6552
6774
|
error: "invalid severity",
|
|
@@ -6563,7 +6785,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6563
6785
|
scope.post("/policies/check", async (req, reply) => {
|
|
6564
6786
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6565
6787
|
if (!proj) return;
|
|
6566
|
-
const parsed =
|
|
6788
|
+
const parsed = import_types25.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6567
6789
|
if (!parsed.success) {
|
|
6568
6790
|
return reply.code(400).send({
|
|
6569
6791
|
error: "invalid /policies/check body",
|
|
@@ -6806,14 +7028,14 @@ init_otel_grpc();
|
|
|
6806
7028
|
// src/daemon.ts
|
|
6807
7029
|
init_cjs_shims();
|
|
6808
7030
|
var import_node_fs25 = require("fs");
|
|
6809
|
-
var
|
|
7031
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
6810
7032
|
init_otel();
|
|
6811
7033
|
init_auth();
|
|
6812
7034
|
|
|
6813
7035
|
// src/unrouted.ts
|
|
6814
7036
|
init_cjs_shims();
|
|
6815
7037
|
var import_node_fs24 = require("fs");
|
|
6816
|
-
var
|
|
7038
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
6817
7039
|
function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
|
|
6818
7040
|
return {
|
|
6819
7041
|
timestamp: now.toISOString(),
|
|
@@ -6823,12 +7045,12 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
|
|
|
6823
7045
|
};
|
|
6824
7046
|
}
|
|
6825
7047
|
async function appendUnroutedSpan(neatHome2, record) {
|
|
6826
|
-
const target =
|
|
7048
|
+
const target = import_node_path41.default.join(neatHome2, "errors.ndjson");
|
|
6827
7049
|
await import_node_fs24.promises.mkdir(neatHome2, { recursive: true });
|
|
6828
7050
|
await import_node_fs24.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
|
|
6829
7051
|
}
|
|
6830
7052
|
function unroutedErrorsPath(neatHome2) {
|
|
6831
|
-
return
|
|
7053
|
+
return import_node_path41.default.join(neatHome2, "errors.ndjson");
|
|
6832
7054
|
}
|
|
6833
7055
|
|
|
6834
7056
|
// src/daemon.ts
|
|
@@ -6843,11 +7065,11 @@ function teardownSlot(slot) {
|
|
|
6843
7065
|
}
|
|
6844
7066
|
}
|
|
6845
7067
|
function neatHomeFor(opts) {
|
|
6846
|
-
if (opts.neatHome && opts.neatHome.length > 0) return
|
|
7068
|
+
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path42.default.resolve(opts.neatHome);
|
|
6847
7069
|
const env = process.env.NEAT_HOME;
|
|
6848
|
-
if (env && env.length > 0) return
|
|
7070
|
+
if (env && env.length > 0) return import_node_path42.default.resolve(env);
|
|
6849
7071
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
6850
|
-
return
|
|
7072
|
+
return import_node_path42.default.join(home, ".neat");
|
|
6851
7073
|
}
|
|
6852
7074
|
function routeSpanToProject(serviceName, projects) {
|
|
6853
7075
|
if (!serviceName) return DEFAULT_PROJECT;
|
|
@@ -6882,7 +7104,7 @@ function isTokenContained(needle, haystack) {
|
|
|
6882
7104
|
return tokens.includes(needle);
|
|
6883
7105
|
}
|
|
6884
7106
|
async function bootstrapProject(entry) {
|
|
6885
|
-
const paths = pathsForProject(entry.name,
|
|
7107
|
+
const paths = pathsForProject(entry.name, import_node_path42.default.join(entry.path, "neat-out"));
|
|
6886
7108
|
try {
|
|
6887
7109
|
const stat = await import_node_fs25.promises.stat(entry.path);
|
|
6888
7110
|
if (!stat.isDirectory()) {
|
|
@@ -6965,7 +7187,7 @@ async function startDaemon(opts = {}) {
|
|
|
6965
7187
|
`neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
|
|
6966
7188
|
);
|
|
6967
7189
|
}
|
|
6968
|
-
const pidPath =
|
|
7190
|
+
const pidPath = import_node_path42.default.join(home, "neatd.pid");
|
|
6969
7191
|
await writeAtomically(pidPath, `${process.pid}
|
|
6970
7192
|
`);
|
|
6971
7193
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -7054,6 +7276,19 @@ async function startDaemon(opts = {}) {
|
|
|
7054
7276
|
}
|
|
7055
7277
|
}
|
|
7056
7278
|
async function loadAll() {
|
|
7279
|
+
try {
|
|
7280
|
+
const pruned = await pruneRegistry();
|
|
7281
|
+
for (const entry of pruned) {
|
|
7282
|
+
console.log(
|
|
7283
|
+
`neatd: pruned project "${entry.name}" \u2014 registered path ${entry.path} is gone`
|
|
7284
|
+
);
|
|
7285
|
+
slots.delete(entry.name);
|
|
7286
|
+
bootstrapStatus.delete(entry.name);
|
|
7287
|
+
bootstrapStartedAt.delete(entry.name);
|
|
7288
|
+
}
|
|
7289
|
+
} catch (err) {
|
|
7290
|
+
console.warn(`neatd: registry prune skipped \u2014 ${err.message}`);
|
|
7291
|
+
}
|
|
7057
7292
|
const projects = await listProjects();
|
|
7058
7293
|
const seen = /* @__PURE__ */ new Set();
|
|
7059
7294
|
const pending = [];
|
|
@@ -7270,8 +7505,8 @@ async function startDaemon(opts = {}) {
|
|
|
7270
7505
|
let registryWatcher = null;
|
|
7271
7506
|
let reloadTimer = null;
|
|
7272
7507
|
try {
|
|
7273
|
-
const regDir =
|
|
7274
|
-
const regBase =
|
|
7508
|
+
const regDir = import_node_path42.default.dirname(regPath);
|
|
7509
|
+
const regBase = import_node_path42.default.basename(regPath);
|
|
7275
7510
|
registryWatcher = (0, import_node_fs25.watch)(regDir, (_eventType, filename) => {
|
|
7276
7511
|
if (filename !== null && filename !== regBase) return;
|
|
7277
7512
|
if (reloadTimer) clearTimeout(reloadTimer);
|