@neat.is/core 0.9.10-dev.20260830 → 0.9.10
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-C3LHXC55.js} +110 -2
- package/dist/chunk-C3LHXC55.js.map +1 -0
- package/dist/{chunk-QETJIA2Q.js → chunk-UHNE7LGF.js} +2 -2
- package/dist/cli.cjs +109 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +109 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +109 -1
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +109 -1
- 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-UHNE7LGF.js.map} +0 -0
package/dist/neatd.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
reconcileDaemonRecordSync,
|
|
4
4
|
startDaemon
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UHNE7LGF.js";
|
|
6
6
|
import {
|
|
7
7
|
listProjects,
|
|
8
8
|
registryPath
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-C3LHXC55.js";
|
|
10
10
|
import {
|
|
11
11
|
BindAuthorityError,
|
|
12
12
|
__require
|
package/dist/server.cjs
CHANGED
|
@@ -6962,6 +6962,24 @@ function incidentCountForNode(nodeId, incidents) {
|
|
|
6962
6962
|
if (!incidents || incidents.length === 0) return 0;
|
|
6963
6963
|
return incidents.filter((ev) => incidentMatchesNode(ev, nodeId)).length;
|
|
6964
6964
|
}
|
|
6965
|
+
var DEP_EDGE_TYPES = /* @__PURE__ */ new Set([
|
|
6966
|
+
import_types8.EdgeType.CALLS,
|
|
6967
|
+
import_types8.EdgeType.CONNECTS_TO,
|
|
6968
|
+
import_types8.EdgeType.PUBLISHES_TO,
|
|
6969
|
+
import_types8.EdgeType.CONSUMES_FROM
|
|
6970
|
+
]);
|
|
6971
|
+
var TIMEOUT_ERROR_PATTERNS = [
|
|
6972
|
+
/\bDEADLINE_EXCEEDED\b/i,
|
|
6973
|
+
/\bdeadline exceeded\b/i,
|
|
6974
|
+
/\bETIMEDOUT\b/i,
|
|
6975
|
+
/\btimed[\s-]?out\b/i,
|
|
6976
|
+
/\btimeout\b/i
|
|
6977
|
+
];
|
|
6978
|
+
function isBoundaryTimeoutError(ev) {
|
|
6979
|
+
if (ev.httpStatusCode === 504) return true;
|
|
6980
|
+
const haystack = [ev.errorType, ev.exceptionType, ev.errorMessage].filter((s) => typeof s === "string").join(" \n ");
|
|
6981
|
+
return TIMEOUT_ERROR_PATTERNS.some((re) => re.test(haystack));
|
|
6982
|
+
}
|
|
6965
6983
|
function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
6966
6984
|
const scope = nodeScope(graph, nodeId);
|
|
6967
6985
|
let errorsFromCallers = 0;
|
|
@@ -6971,6 +6989,8 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
6971
6989
|
let latestInboundMs;
|
|
6972
6990
|
let latencyP95Ms;
|
|
6973
6991
|
let stale = false;
|
|
6992
|
+
let observedErroringDownstream = false;
|
|
6993
|
+
let hasOutboundDeps = false;
|
|
6974
6994
|
for (const n of scope) {
|
|
6975
6995
|
if (!graph.hasNode(n)) continue;
|
|
6976
6996
|
for (const edgeId of graph.inboundEdges(n)) {
|
|
@@ -6992,10 +7012,17 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
6992
7012
|
outboundVolume += e.callCount ?? e.signal?.spanCount ?? 0;
|
|
6993
7013
|
if (e.type === import_types8.EdgeType.CALLS) outboundErrors += e.signal?.errorCount ?? 0;
|
|
6994
7014
|
if (e.provenance === import_types8.Provenance.STALE) stale = true;
|
|
7015
|
+
if (DEP_EDGE_TYPES.has(e.type)) {
|
|
7016
|
+
hasOutboundDeps = true;
|
|
7017
|
+
if ((e.signal?.errorCount ?? 0) > 0) observedErroringDownstream = true;
|
|
7018
|
+
}
|
|
6995
7019
|
}
|
|
6996
7020
|
}
|
|
6997
7021
|
const errorsEmittedHere = incidentCountForNode(nodeId, incidents) + outboundErrors;
|
|
6998
7022
|
const lastObservedAgeMs = latestInboundMs !== void 0 ? Math.max(0, now - latestInboundMs) : void 0;
|
|
7023
|
+
const boundaryTimeout = (incidents ?? []).some(
|
|
7024
|
+
(ev) => incidentMatchesNode(ev, nodeId) && isBoundaryTimeoutError(ev)
|
|
7025
|
+
);
|
|
6999
7026
|
return {
|
|
7000
7027
|
errorsEmittedHere,
|
|
7001
7028
|
errorsFromCallers,
|
|
@@ -7003,17 +7030,24 @@ function nodeContext(graph, nodeId, incidents, now = Date.now()) {
|
|
|
7003
7030
|
outboundVolume,
|
|
7004
7031
|
...lastObservedAgeMs !== void 0 ? { lastObservedAgeMs } : {},
|
|
7005
7032
|
...latencyP95Ms !== void 0 ? { latencyP95Ms } : {},
|
|
7006
|
-
stale
|
|
7033
|
+
stale,
|
|
7034
|
+
boundaryTimeout,
|
|
7035
|
+
observedErroringDownstream,
|
|
7036
|
+
hasOutboundDeps
|
|
7007
7037
|
};
|
|
7008
7038
|
}
|
|
7009
7039
|
function isSaturated(ctx) {
|
|
7010
7040
|
return ctx.latencyP95Ms !== void 0 && ctx.latencyP95Ms >= SATURATION_P95_MS;
|
|
7011
7041
|
}
|
|
7042
|
+
function isBoundaryTimeoutSymptom(ctx) {
|
|
7043
|
+
return ctx.errorsEmittedHere > 0 && ctx.boundaryTimeout === true && ctx.errorsFromCallers === 0 && ctx.hasOutboundDeps === true && ctx.observedErroringDownstream !== true;
|
|
7044
|
+
}
|
|
7012
7045
|
function classifyNode(ctx) {
|
|
7013
7046
|
if (ctx.errorsEmittedHere > 0) {
|
|
7014
7047
|
if ((ctx.stale || isSaturated(ctx)) && ctx.errorsFromCallers >= ctx.errorsEmittedHere) {
|
|
7015
7048
|
return "symptom-only";
|
|
7016
7049
|
}
|
|
7050
|
+
if (isBoundaryTimeoutSymptom(ctx)) return "symptom-only";
|
|
7017
7051
|
return "primary-failure";
|
|
7018
7052
|
}
|
|
7019
7053
|
if (ctx.errorsFromCallers > 0) return "symptom-only";
|
|
@@ -7218,6 +7252,57 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
|
|
|
7218
7252
|
if (!navigation) return tagged.result;
|
|
7219
7253
|
return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
|
|
7220
7254
|
}
|
|
7255
|
+
function boundaryIncidentRoute(nodeId, incidents) {
|
|
7256
|
+
for (const ev of incidents ?? []) {
|
|
7257
|
+
if (!incidentMatchesNode(ev, nodeId)) continue;
|
|
7258
|
+
const r = ev.attributes?.["http.route"] ?? ev.attributes?.["http.target"] ?? ev.attributes?.["url.path"];
|
|
7259
|
+
if (typeof r === "string" && r.length > 0) return r;
|
|
7260
|
+
}
|
|
7261
|
+
return void 0;
|
|
7262
|
+
}
|
|
7263
|
+
function declaredOutboundCallees(graph, nodeId) {
|
|
7264
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
7265
|
+
for (const n of nodeScope(graph, nodeId)) {
|
|
7266
|
+
if (!graph.hasNode(n)) continue;
|
|
7267
|
+
for (const edgeId of graph.outboundEdges(n)) {
|
|
7268
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
7269
|
+
if (!DEP_EDGE_TYPES.has(e.type)) continue;
|
|
7270
|
+
if (e.provenance !== import_types8.Provenance.EXTRACTED) continue;
|
|
7271
|
+
if (e.target === nodeId || byTarget.has(e.target)) continue;
|
|
7272
|
+
const route = e.evidence?.pathTemplate;
|
|
7273
|
+
byTarget.set(
|
|
7274
|
+
e.target,
|
|
7275
|
+
route !== void 0 ? { target: e.target, route } : { target: e.target }
|
|
7276
|
+
);
|
|
7277
|
+
}
|
|
7278
|
+
}
|
|
7279
|
+
return [...byTarget.values()];
|
|
7280
|
+
}
|
|
7281
|
+
function normalizeRoute(s) {
|
|
7282
|
+
return s.replace(/\/+$/, "").toLowerCase();
|
|
7283
|
+
}
|
|
7284
|
+
function routeMatches(declared, incident) {
|
|
7285
|
+
const a = normalizeRoute(declared);
|
|
7286
|
+
const b = normalizeRoute(incident);
|
|
7287
|
+
return a === b || a.startsWith(b) || b.startsWith(a);
|
|
7288
|
+
}
|
|
7289
|
+
function structuralUpstreamPointer(graph, boundaryNode, incidents) {
|
|
7290
|
+
const route = boundaryIncidentRoute(boundaryNode, incidents);
|
|
7291
|
+
const callees = declaredOutboundCallees(graph, boundaryNode);
|
|
7292
|
+
if (callees.length === 0) return null;
|
|
7293
|
+
const narrowed = route !== void 0 ? callees.filter((c) => c.route !== void 0 && routeMatches(c.route, route)) : callees;
|
|
7294
|
+
const chosen = narrowed.length === 1 ? narrowed[0] : callees.length === 1 ? callees[0] : null;
|
|
7295
|
+
if (!chosen) return null;
|
|
7296
|
+
let node = chosen.target;
|
|
7297
|
+
const seen = /* @__PURE__ */ new Set([boundaryNode, node]);
|
|
7298
|
+
for (let depth = 0; depth < ROOT_CAUSE_MAX_DEPTH; depth++) {
|
|
7299
|
+
const next = declaredOutboundCallees(graph, node);
|
|
7300
|
+
if (next.length !== 1 || seen.has(next[0].target)) break;
|
|
7301
|
+
node = next[0].target;
|
|
7302
|
+
seen.add(node);
|
|
7303
|
+
}
|
|
7304
|
+
return route !== void 0 ? { node, route } : { node };
|
|
7305
|
+
}
|
|
7221
7306
|
function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
7222
7307
|
const legacy = tagged.result;
|
|
7223
7308
|
const seedNode = legacy.rootCauseNode;
|
|
@@ -7251,6 +7336,29 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
7251
7336
|
confidence: Math.min(legacy.confidence, 0.4),
|
|
7252
7337
|
...lastProv ? { provenance: lastProv } : {}
|
|
7253
7338
|
});
|
|
7339
|
+
} else if (seedCtx && isBoundaryTimeoutSymptom(seedCtx)) {
|
|
7340
|
+
const pointer = structuralUpstreamPointer(graph, seedNode, incidents);
|
|
7341
|
+
const seedName = displayNameOf(seedNode);
|
|
7342
|
+
const routeNote = pointer?.route ? ` serving ${pointer.route}` : "";
|
|
7343
|
+
if (pointer) {
|
|
7344
|
+
const causeName = displayNameOf(pointer.node);
|
|
7345
|
+
candidates.push({
|
|
7346
|
+
node: pointer.node,
|
|
7347
|
+
classification: "primary-failure",
|
|
7348
|
+
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.`,
|
|
7349
|
+
context: nodeContext(graph, pointer.node, incidents, now),
|
|
7350
|
+
confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.EXTRACTED),
|
|
7351
|
+
provenance: import_types8.Provenance.INFERRED
|
|
7352
|
+
});
|
|
7353
|
+
}
|
|
7354
|
+
candidates.push({
|
|
7355
|
+
node: seedNode,
|
|
7356
|
+
classification: "symptom-only",
|
|
7357
|
+
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.`,
|
|
7358
|
+
context: seedCtx,
|
|
7359
|
+
confidence: Math.min(legacy.confidence, 0.3),
|
|
7360
|
+
...lastProv ? { provenance: lastProv } : {}
|
|
7361
|
+
});
|
|
7254
7362
|
} else if (staleChain) {
|
|
7255
7363
|
const culprit = staleChain.culprit;
|
|
7256
7364
|
const culpritName = displayNameOf(culprit);
|