@neat.is/core 0.9.10-dev.20260830 → 0.9.11
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-B6KJG5KN.js → chunk-3UAUMPIY.js} +114 -22
- package/dist/chunk-3UAUMPIY.js.map +1 -0
- package/dist/{chunk-QETJIA2Q.js → chunk-UNO3X6ZV.js} +2 -2
- package/dist/cli.cjs +113 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +113 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +113 -21
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +131 -21
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-B6KJG5KN.js.map +0 -1
- /package/dist/{chunk-QETJIA2Q.js.map → chunk-UNO3X6ZV.js.map} +0 -0
|
@@ -4694,6 +4694,7 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
|
|
|
4694
4694
|
const ts = span.startTimeIso ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4695
4695
|
const locus = incidentLocus(span, graph, scanPath);
|
|
4696
4696
|
const attrs = withRecoveredCodeAttrs(sanitizeAttributes(span.attributes), locus);
|
|
4697
|
+
const httpStatus = httpResponseStatus(span);
|
|
4697
4698
|
return {
|
|
4698
4699
|
id: `${span.traceId}:${span.spanId}`,
|
|
4699
4700
|
timestamp: ts,
|
|
@@ -4703,6 +4704,7 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
|
|
|
4703
4704
|
errorMessage: incidentMessage(span),
|
|
4704
4705
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
4705
4706
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
4707
|
+
...httpStatus !== void 0 ? { httpStatusCode: httpStatus } : {},
|
|
4706
4708
|
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
4707
4709
|
affectedNode: locus.affectedNode
|
|
4708
4710
|
};
|
|
@@ -5054,26 +5056,8 @@ async function handleSpan(ctx, span) {
|
|
|
5054
5056
|
if (span.statusCode === 2) {
|
|
5055
5057
|
stitchTrace(ctx.graph, sourceId, ts);
|
|
5056
5058
|
if (ctx.writeErrorEventInline !== false) {
|
|
5057
|
-
const
|
|
5058
|
-
|
|
5059
|
-
const ev = {
|
|
5060
|
-
id: `${span.traceId}:${span.spanId}`,
|
|
5061
|
-
timestamp: ts,
|
|
5062
|
-
service: span.service,
|
|
5063
|
-
traceId: span.traceId,
|
|
5064
|
-
spanId: span.spanId,
|
|
5065
|
-
errorMessage: incidentMessage(span),
|
|
5066
|
-
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
5067
|
-
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
5068
|
-
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
5069
|
-
// Attribute to where the failure originated — the symbol / file / service
|
|
5070
|
-
// the throwing span named (incidentAffectedNode / ADR-191, extended to
|
|
5071
|
-
// recover the locus from the stacktrace when the span stamped no code.*
|
|
5072
|
-
// attrs, ADR-216) — the same source-based attribution the durable
|
|
5073
|
-
// receiver write uses, not the outbound edge target this span minted.
|
|
5074
|
-
affectedNode: locus.affectedNode
|
|
5075
|
-
};
|
|
5076
|
-
await appendErrorEvent(ctx, ev);
|
|
5059
|
+
const ev = buildErrorEventForReceiver(span, ctx.graph, ctx.scanPath);
|
|
5060
|
+
if (ev) await appendErrorEvent(ctx, ev);
|
|
5077
5061
|
}
|
|
5078
5062
|
}
|
|
5079
5063
|
if (span.statusCode !== 2) {
|
|
@@ -5987,6 +5971,24 @@ function incidentCountForNode(nodeId, incidents) {
|
|
|
5987
5971
|
if (!incidents || incidents.length === 0) return 0;
|
|
5988
5972
|
return incidents.filter((ev) => incidentMatchesNode(ev, nodeId)).length;
|
|
5989
5973
|
}
|
|
5974
|
+
var DEP_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
5975
|
+
EdgeType6.CALLS,
|
|
5976
|
+
EdgeType6.CONNECTS_TO,
|
|
5977
|
+
EdgeType6.PUBLISHES_TO,
|
|
5978
|
+
EdgeType6.CONSUMES_FROM
|
|
5979
|
+
]);
|
|
5980
|
+
var TIMEOUT_ERROR_PATTERNS = [
|
|
5981
|
+
/\bDEADLINE_EXCEEDED\b/i,
|
|
5982
|
+
/\bdeadline exceeded\b/i,
|
|
5983
|
+
/\bETIMEDOUT\b/i,
|
|
5984
|
+
/\btimed[\s-]?out\b/i,
|
|
5985
|
+
/\btimeout\b/i
|
|
5986
|
+
];
|
|
5987
|
+
function isBoundaryTimeoutError(ev) {
|
|
5988
|
+
if (ev.httpStatusCode === 504) return true;
|
|
5989
|
+
const haystack = [ev.errorType, ev.exceptionType, ev.errorMessage].filter((s) => typeof s === "string").join(" \n ");
|
|
5990
|
+
return TIMEOUT_ERROR_PATTERNS.some((re) => re.test(haystack));
|
|
5991
|
+
}
|
|
5990
5992
|
function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
5991
5993
|
const scope = nodeScope(graph, nodeId);
|
|
5992
5994
|
let errorsFromCallers = 0;
|
|
@@ -5996,6 +5998,8 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
5996
5998
|
let latestInboundMs;
|
|
5997
5999
|
let latencyP95Ms;
|
|
5998
6000
|
let stale = false;
|
|
6001
|
+
let observedErroringDownstream = false;
|
|
6002
|
+
let hasOutboundDeps = false;
|
|
5999
6003
|
for (const n of scope) {
|
|
6000
6004
|
if (!graph.hasNode(n)) continue;
|
|
6001
6005
|
for (const edgeId of graph.inboundEdges(n)) {
|
|
@@ -6017,10 +6021,17 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
6017
6021
|
outboundVolume += e.callCount ?? e.signal?.spanCount ?? 0;
|
|
6018
6022
|
if (e.type === EdgeType6.CALLS) outboundErrors += e.signal?.errorCount ?? 0;
|
|
6019
6023
|
if (e.provenance === Provenance6.STALE) stale = true;
|
|
6024
|
+
if (DEP_EDGE_TYPES.has(e.type)) {
|
|
6025
|
+
hasOutboundDeps = true;
|
|
6026
|
+
if ((e.signal?.errorCount ?? 0) > 0) observedErroringDownstream = true;
|
|
6027
|
+
}
|
|
6020
6028
|
}
|
|
6021
6029
|
}
|
|
6022
6030
|
const errorsEmittedHere = incidentCountForNode(nodeId, incidents) + outboundErrors;
|
|
6023
6031
|
const lastObservedAgeMs = latestInboundMs !== void 0 ? Math.max(0, now - latestInboundMs) : void 0;
|
|
6032
|
+
const boundaryTimeout = (incidents ?? []).some(
|
|
6033
|
+
(ev) => incidentMatchesNode(ev, nodeId) && isBoundaryTimeoutError(ev)
|
|
6034
|
+
);
|
|
6024
6035
|
return {
|
|
6025
6036
|
errorsEmittedHere,
|
|
6026
6037
|
errorsFromCallers,
|
|
@@ -6028,17 +6039,24 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
6028
6039
|
outboundVolume,
|
|
6029
6040
|
...lastObservedAgeMs !== void 0 ? { lastObservedAgeMs } : {},
|
|
6030
6041
|
...latencyP95Ms !== void 0 ? { latencyP95Ms } : {},
|
|
6031
|
-
stale
|
|
6042
|
+
stale,
|
|
6043
|
+
boundaryTimeout,
|
|
6044
|
+
observedErroringDownstream,
|
|
6045
|
+
hasOutboundDeps
|
|
6032
6046
|
};
|
|
6033
6047
|
}
|
|
6034
6048
|
function isSaturated(ctx) {
|
|
6035
6049
|
return ctx.latencyP95Ms !== void 0 && ctx.latencyP95Ms >= SATURATION_P95_MS;
|
|
6036
6050
|
}
|
|
6051
|
+
function isBoundaryTimeoutSymptom(ctx) {
|
|
6052
|
+
return ctx.errorsEmittedHere > 0 && ctx.boundaryTimeout === true && ctx.errorsFromCallers === 0 && ctx.hasOutboundDeps === true && ctx.observedErroringDownstream !== true;
|
|
6053
|
+
}
|
|
6037
6054
|
function classifyNode(ctx) {
|
|
6038
6055
|
if (ctx.errorsEmittedHere > 0) {
|
|
6039
6056
|
if ((ctx.stale || isSaturated(ctx)) && ctx.errorsFromCallers >= ctx.errorsEmittedHere) {
|
|
6040
6057
|
return "symptom-only";
|
|
6041
6058
|
}
|
|
6059
|
+
if (isBoundaryTimeoutSymptom(ctx)) return "symptom-only";
|
|
6042
6060
|
return "primary-failure";
|
|
6043
6061
|
}
|
|
6044
6062
|
if (ctx.errorsFromCallers > 0) return "symptom-only";
|
|
@@ -6243,6 +6261,57 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
|
|
|
6243
6261
|
if (!navigation) return tagged.result;
|
|
6244
6262
|
return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
|
|
6245
6263
|
}
|
|
6264
|
+
function boundaryIncidentRoute(nodeId, incidents) {
|
|
6265
|
+
for (const ev of incidents ?? []) {
|
|
6266
|
+
if (!incidentMatchesNode(ev, nodeId)) continue;
|
|
6267
|
+
const r = ev.attributes?.["http.route"] ?? ev.attributes?.["http.target"] ?? ev.attributes?.["url.path"];
|
|
6268
|
+
if (typeof r === "string" && r.length > 0) return r;
|
|
6269
|
+
}
|
|
6270
|
+
return void 0;
|
|
6271
|
+
}
|
|
6272
|
+
function declaredOutboundCallees(graph, nodeId) {
|
|
6273
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
6274
|
+
for (const n of nodeScope(graph, nodeId)) {
|
|
6275
|
+
if (!graph.hasNode(n)) continue;
|
|
6276
|
+
for (const edgeId of graph.outboundEdges(n)) {
|
|
6277
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
6278
|
+
if (!DEP_EDGE_TYPES.has(e.type)) continue;
|
|
6279
|
+
if (e.provenance !== Provenance6.EXTRACTED) continue;
|
|
6280
|
+
if (e.target === nodeId || byTarget.has(e.target)) continue;
|
|
6281
|
+
const route = e.evidence?.pathTemplate;
|
|
6282
|
+
byTarget.set(
|
|
6283
|
+
e.target,
|
|
6284
|
+
route !== void 0 ? { target: e.target, route } : { target: e.target }
|
|
6285
|
+
);
|
|
6286
|
+
}
|
|
6287
|
+
}
|
|
6288
|
+
return [...byTarget.values()];
|
|
6289
|
+
}
|
|
6290
|
+
function normalizeRoute(s) {
|
|
6291
|
+
return s.replace(/\/+$/, "").toLowerCase();
|
|
6292
|
+
}
|
|
6293
|
+
function routeMatches(declared, incident) {
|
|
6294
|
+
const a = normalizeRoute(declared);
|
|
6295
|
+
const b = normalizeRoute(incident);
|
|
6296
|
+
return a === b || a.startsWith(b) || b.startsWith(a);
|
|
6297
|
+
}
|
|
6298
|
+
function structuralUpstreamPointer(graph, boundaryNode, incidents) {
|
|
6299
|
+
const route = boundaryIncidentRoute(boundaryNode, incidents);
|
|
6300
|
+
const callees = declaredOutboundCallees(graph, boundaryNode);
|
|
6301
|
+
if (callees.length === 0) return null;
|
|
6302
|
+
const narrowed = route !== void 0 ? callees.filter((c) => c.route !== void 0 && routeMatches(c.route, route)) : callees;
|
|
6303
|
+
const chosen = narrowed.length === 1 ? narrowed[0] : callees.length === 1 ? callees[0] : null;
|
|
6304
|
+
if (!chosen) return null;
|
|
6305
|
+
let node = chosen.target;
|
|
6306
|
+
const seen = /* @__PURE__ */ new Set([boundaryNode, node]);
|
|
6307
|
+
for (let depth = 0; depth < ROOT_CAUSE_MAX_DEPTH; depth++) {
|
|
6308
|
+
const next = declaredOutboundCallees(graph, node);
|
|
6309
|
+
if (next.length !== 1 || seen.has(next[0].target)) break;
|
|
6310
|
+
node = next[0].target;
|
|
6311
|
+
seen.add(node);
|
|
6312
|
+
}
|
|
6313
|
+
return route !== void 0 ? { node, route } : { node };
|
|
6314
|
+
}
|
|
6246
6315
|
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
6247
6316
|
const legacy = tagged.result;
|
|
6248
6317
|
const seedNode = legacy.rootCauseNode;
|
|
@@ -6276,6 +6345,29 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
6276
6345
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
6277
6346
|
...lastProv ? { provenance: lastProv } : {}
|
|
6278
6347
|
});
|
|
6348
|
+
} else if (seedCtx && isBoundaryTimeoutSymptom(seedCtx)) {
|
|
6349
|
+
const pointer = structuralUpstreamPointer(graph, seedNode, incidents);
|
|
6350
|
+
const seedName = displayNameOf(seedNode);
|
|
6351
|
+
const routeNote = pointer?.route ? ` serving ${pointer.route}` : "";
|
|
6352
|
+
if (pointer) {
|
|
6353
|
+
const causeName = displayNameOf(pointer.node);
|
|
6354
|
+
candidates.push({
|
|
6355
|
+
node: pointer.node,
|
|
6356
|
+
classification: "primary-failure",
|
|
6357
|
+
reason: `${causeName} is the likely root cause (structural, INFERRED \u2014 not observed): the boundary ${seedName} only timed out waiting, and the actual culprit hung without exporting a span, so this is walked from the declared call graph${routeNote}, not from runtime signal. Restore instrumentation / inspect ${causeName} to confirm.`,
|
|
6358
|
+
context: nodeContext(graph, pointer.node, incidents, now),
|
|
6359
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.EXTRACTED),
|
|
6360
|
+
provenance: Provenance6.INFERRED
|
|
6361
|
+
});
|
|
6362
|
+
}
|
|
6363
|
+
candidates.push({
|
|
6364
|
+
node: seedNode,
|
|
6365
|
+
classification: "symptom-only",
|
|
6366
|
+
reason: pointer ? `${seedName} timed out waiting on a downstream that hung and exported nothing \u2014 a boundary reporting an upstream fault, not the fault itself.` : `${seedName} timed out but nothing it can see downstream is erroring \u2014 the cause is downstream and unobserved (the culprit hung and exported no span). No single declared callee${routeNote} resolves, so no confident cause is named; inspect ${seedName}'s declared downstream.`,
|
|
6367
|
+
context: seedCtx,
|
|
6368
|
+
confidence: Math.min(legacy.confidence, 0.3),
|
|
6369
|
+
...lastProv ? { provenance: lastProv } : {}
|
|
6370
|
+
});
|
|
6279
6371
|
} else if (staleChain) {
|
|
6280
6372
|
const culprit = staleChain.culprit;
|
|
6281
6373
|
const culpritName = displayNameOf(culprit);
|
|
@@ -22510,4 +22602,4 @@ export {
|
|
|
22510
22602
|
deprovisionConnector,
|
|
22511
22603
|
buildApi
|
|
22512
22604
|
};
|
|
22513
|
-
//# sourceMappingURL=chunk-
|
|
22605
|
+
//# sourceMappingURL=chunk-3UAUMPIY.js.map
|