@neat.is/core 0.9.8-dev.20260829 → 0.9.9
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-KZBFP7L6.js → chunk-B6KJG5KN.js} +219 -13
- package/dist/chunk-B6KJG5KN.js.map +1 -0
- package/dist/{chunk-JSUPWMH3.js → chunk-QETJIA2Q.js} +14 -4
- package/dist/{chunk-JSUPWMH3.js.map → chunk-QETJIA2Q.js.map} +1 -1
- package/dist/cli.cjs +343 -98
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +46 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +294 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +294 -82
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +280 -80
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-KZBFP7L6.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -6346,11 +6346,25 @@ function upsertInferredEdge(graph, type, source, target, ts) {
|
|
|
6346
6346
|
};
|
|
6347
6347
|
graph.addEdgeWithKey(id, source, target, edge);
|
|
6348
6348
|
}
|
|
6349
|
+
function emitIncidentEvent(project, ev) {
|
|
6350
|
+
emitNeatEvent({
|
|
6351
|
+
type: "incident",
|
|
6352
|
+
project,
|
|
6353
|
+
payload: {
|
|
6354
|
+
incidentId: ev.id,
|
|
6355
|
+
affectedNode: ev.affectedNode,
|
|
6356
|
+
service: ev.service,
|
|
6357
|
+
incidentKind: (0, import_types8.incidentKindOf)(ev),
|
|
6358
|
+
at: ev.timestamp
|
|
6359
|
+
}
|
|
6360
|
+
});
|
|
6361
|
+
}
|
|
6349
6362
|
async function appendErrorEvent(ctx, ev) {
|
|
6350
6363
|
await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(ctx.errorsPath), { recursive: true });
|
|
6351
6364
|
await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
6365
|
+
emitIncidentEvent(ctx.project ?? DEFAULT_PROJECT, ev);
|
|
6352
6366
|
}
|
|
6353
|
-
async function appendConnectorIncident(errorsPath, input) {
|
|
6367
|
+
async function appendConnectorIncident(errorsPath, input, project) {
|
|
6354
6368
|
const ev = {
|
|
6355
6369
|
id: input.id,
|
|
6356
6370
|
timestamp: input.timestamp,
|
|
@@ -6364,6 +6378,7 @@ async function appendConnectorIncident(errorsPath, input) {
|
|
|
6364
6378
|
};
|
|
6365
6379
|
await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
|
|
6366
6380
|
await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
6381
|
+
if (project) emitIncidentEvent(project, ev);
|
|
6367
6382
|
}
|
|
6368
6383
|
function landIncidentCallSite(span, callSite, trusted, graph) {
|
|
6369
6384
|
const relPath = graph ? reconcileObservedRelPath(graph, span.service, callSite.relPath) : callSite.relPath;
|
|
@@ -6457,12 +6472,13 @@ function buildErrorEventForReceiver(span, graph, scanPath) {
|
|
|
6457
6472
|
affectedNode: locus.affectedNode
|
|
6458
6473
|
};
|
|
6459
6474
|
}
|
|
6460
|
-
function makeErrorSpanWriter(errorsPath, graph, scanPath) {
|
|
6475
|
+
function makeErrorSpanWriter(errorsPath, graph, scanPath, project = DEFAULT_PROJECT) {
|
|
6461
6476
|
return async (span) => {
|
|
6462
6477
|
const ev = buildErrorEventForReceiver(span, graph, scanPath);
|
|
6463
6478
|
if (!ev) return;
|
|
6464
6479
|
await import_node_fs7.promises.mkdir(import_node_path8.default.dirname(errorsPath), { recursive: true });
|
|
6465
6480
|
await import_node_fs7.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
6481
|
+
emitIncidentEvent(project, ev);
|
|
6466
6482
|
};
|
|
6467
6483
|
}
|
|
6468
6484
|
async function recordFailingResponseIncident(ctx, span, affectedNode, timestamp, statusCode, count, firstTimestamp) {
|
|
@@ -16120,7 +16136,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
|
|
|
16120
16136
|
init_cjs_shims();
|
|
16121
16137
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
16122
16138
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
16123
|
-
var
|
|
16139
|
+
var import_types98 = require("@neat.is/types");
|
|
16124
16140
|
|
|
16125
16141
|
// src/extend/index.ts
|
|
16126
16142
|
init_cjs_shims();
|
|
@@ -17162,9 +17178,160 @@ function queryLogEntries(opts) {
|
|
|
17162
17178
|
return merged;
|
|
17163
17179
|
}
|
|
17164
17180
|
|
|
17165
|
-
// src/
|
|
17181
|
+
// src/goodybag.ts
|
|
17166
17182
|
init_cjs_shims();
|
|
17167
17183
|
var import_types59 = require("@neat.is/types");
|
|
17184
|
+
var CODE_FILEPATH_ATTR2 = "code.filepath";
|
|
17185
|
+
var CODE_LINENO_ATTR2 = "code.lineno";
|
|
17186
|
+
var BLAST_NEAREST_LIMIT = 5;
|
|
17187
|
+
function grainOf2(graph, nodeId) {
|
|
17188
|
+
if (graph.hasNode(nodeId)) {
|
|
17189
|
+
const t = graph.getNodeAttributes(nodeId).type;
|
|
17190
|
+
if (typeof t === "string" && t.length > 0) {
|
|
17191
|
+
return (t.endsWith("Node") ? t.slice(0, -4) : t).toLowerCase();
|
|
17192
|
+
}
|
|
17193
|
+
}
|
|
17194
|
+
const colon = nodeId.indexOf(":");
|
|
17195
|
+
return colon > 0 ? nodeId.slice(0, colon) : "unknown";
|
|
17196
|
+
}
|
|
17197
|
+
function locusOf(graph, ev) {
|
|
17198
|
+
const file = ev.attributes?.[CODE_FILEPATH_ATTR2];
|
|
17199
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
17200
|
+
const rawLine = ev.attributes?.[CODE_LINENO_ATTR2];
|
|
17201
|
+
const line = typeof rawLine === "number" ? rawLine : Number(rawLine);
|
|
17202
|
+
const node = graph.hasNode(ev.affectedNode) ? graph.getNodeAttributes(ev.affectedNode) : void 0;
|
|
17203
|
+
return {
|
|
17204
|
+
file,
|
|
17205
|
+
...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
|
|
17206
|
+
...node?.name ? { symbol: node.name } : {},
|
|
17207
|
+
service: node?.service ?? ev.service,
|
|
17208
|
+
provenance: import_types59.Provenance.OBSERVED
|
|
17209
|
+
};
|
|
17210
|
+
}
|
|
17211
|
+
function locusFromNode(graph, nodeId) {
|
|
17212
|
+
if (!graph.hasNode(nodeId)) return null;
|
|
17213
|
+
const n = graph.getNodeAttributes(nodeId);
|
|
17214
|
+
const file = n.relPath ?? n.path;
|
|
17215
|
+
if (typeof file !== "string" || file.length === 0) return null;
|
|
17216
|
+
const start = n.span?.startLine;
|
|
17217
|
+
const end = n.span?.endLine;
|
|
17218
|
+
return {
|
|
17219
|
+
file,
|
|
17220
|
+
...typeof start === "number" ? { lineStart: start } : {},
|
|
17221
|
+
...typeof end === "number" ? { lineEnd: end } : {},
|
|
17222
|
+
...n.qualname ? { symbol: shortLabel(graph, nodeId) } : {},
|
|
17223
|
+
...n.service ? { service: n.service } : {},
|
|
17224
|
+
provenance: import_types59.Provenance.INFERRED
|
|
17225
|
+
};
|
|
17226
|
+
}
|
|
17227
|
+
function promoteCauseLocus(graph, causeNode, incidents) {
|
|
17228
|
+
const native = incidents.find(
|
|
17229
|
+
(e) => e.affectedNode === causeNode && typeof e.attributes?.[CODE_FILEPATH_ATTR2] === "string"
|
|
17230
|
+
);
|
|
17231
|
+
if (native) {
|
|
17232
|
+
const l = locusOf(graph, native);
|
|
17233
|
+
if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types59.Provenance.INFERRED };
|
|
17234
|
+
}
|
|
17235
|
+
return locusFromNode(graph, causeNode);
|
|
17236
|
+
}
|
|
17237
|
+
function divergenceSummary(d) {
|
|
17238
|
+
const column = "column" in d && d.column ? `.${d.column}` : "";
|
|
17239
|
+
const label = "table" in d && d.table ? `${d.table}${column}` : `${d.source} \u2192 ${d.target}`;
|
|
17240
|
+
return label;
|
|
17241
|
+
}
|
|
17242
|
+
function baseName(p) {
|
|
17243
|
+
const parts = p.split(/[\\/]/);
|
|
17244
|
+
return parts[parts.length - 1] || p;
|
|
17245
|
+
}
|
|
17246
|
+
function shortLabel(graph, nodeId) {
|
|
17247
|
+
if (graph.hasNode(nodeId)) {
|
|
17248
|
+
const name = graph.getNodeAttributes(nodeId).name;
|
|
17249
|
+
if (typeof name === "string" && name.length > 0) return name;
|
|
17250
|
+
}
|
|
17251
|
+
const afterHash = nodeId.includes("#") ? nodeId.slice(nodeId.lastIndexOf("#") + 1) : nodeId;
|
|
17252
|
+
return afterHash.includes(":") ? afterHash.slice(afterHash.lastIndexOf(":") + 1) : afterHash;
|
|
17253
|
+
}
|
|
17254
|
+
function renderHeadline(graph, ev, locus, causeNode) {
|
|
17255
|
+
const what = ev.exceptionType ? `raised ${ev.exceptionType}` : ev.errorMessage || "failed";
|
|
17256
|
+
const causeLabel = causeNode && causeNode !== ev.affectedNode ? shortLabel(graph, causeNode) : "";
|
|
17257
|
+
if (locus) {
|
|
17258
|
+
const base = baseName(locus.file);
|
|
17259
|
+
const lines = locus.lineStart != null ? locus.lineEnd && locus.lineEnd !== locus.lineStart ? `LINES ${locus.lineStart}-${locus.lineEnd}` : `LINE ${locus.lineStart}` : "";
|
|
17260
|
+
const symbol = locus.symbol ?? (grainOf2(graph, ev.affectedNode) === "symbol" ? shortLabel(graph, ev.affectedNode) : void 0);
|
|
17261
|
+
const subject = symbol ? `SYMBOL ${symbol}` : `FILE ${base}`;
|
|
17262
|
+
const at = symbol ? `${lines ? `${lines} in ` : ""}${base}` : lines;
|
|
17263
|
+
const where = at ? ` at ${at}` : "";
|
|
17264
|
+
const svc = locus.service ?? ev.service;
|
|
17265
|
+
const cause2 = causeLabel && causeLabel !== symbol ? ` \u2192 root cause ${causeLabel}` : "";
|
|
17266
|
+
return `${subject}${where} (SERVICE ${svc}) ${what} at ${ev.timestamp}${cause2}`;
|
|
17267
|
+
}
|
|
17268
|
+
const cause = causeLabel ? ` \u2192 root cause ${causeLabel}` : "";
|
|
17269
|
+
return `SERVICE ${ev.service} ${what} at ${ev.timestamp}${cause}`;
|
|
17270
|
+
}
|
|
17271
|
+
function buildIncidentCard(graph, errorEvent, incidents, policies) {
|
|
17272
|
+
const affected = errorEvent.affectedNode;
|
|
17273
|
+
let locus = locusOf(graph, errorEvent);
|
|
17274
|
+
const inGraph = graph.hasNode(affected);
|
|
17275
|
+
const rc = inGraph ? getRootCause(graph, affected, errorEvent, incidents) : null;
|
|
17276
|
+
let rootCause = null;
|
|
17277
|
+
if (rc) {
|
|
17278
|
+
const provs = rc.edgeProvenances ?? [];
|
|
17279
|
+
const chain = (rc.traversalPath ?? []).map((node, i) => ({
|
|
17280
|
+
node,
|
|
17281
|
+
grain: grainOf2(graph, node),
|
|
17282
|
+
provenance: provs[i] ?? provs[provs.length - 1] ?? import_types59.Provenance.INFERRED
|
|
17283
|
+
}));
|
|
17284
|
+
rootCause = {
|
|
17285
|
+
node: rc.rootCauseNode,
|
|
17286
|
+
...rc.candidates?.[0]?.classification ? { classification: rc.candidates[0].classification } : {},
|
|
17287
|
+
reason: rc.rootCauseReason,
|
|
17288
|
+
confidence: rc.confidence,
|
|
17289
|
+
fix: rc.fixRecommendation ?? null,
|
|
17290
|
+
chain
|
|
17291
|
+
};
|
|
17292
|
+
}
|
|
17293
|
+
if (locus === null && rootCause) {
|
|
17294
|
+
locus = promoteCauseLocus(graph, rootCause.node, incidents);
|
|
17295
|
+
}
|
|
17296
|
+
const blast = inGraph ? getBlastRadius(graph, affected) : { origin: affected, affectedNodes: [], totalAffected: 0 };
|
|
17297
|
+
const nearest = [...blast.affectedNodes].sort((a, b) => a.distance - b.distance).slice(0, BLAST_NEAREST_LIMIT).map((n) => ({ node: n.nodeId, distance: n.distance, provenance: n.edgeProvenance }));
|
|
17298
|
+
const applicable = inGraph ? selectApplicablePolicies(graph, policies, affected) : [];
|
|
17299
|
+
const policyCards = applicable.map((p) => ({
|
|
17300
|
+
policyName: p.policyName,
|
|
17301
|
+
severity: p.severity,
|
|
17302
|
+
message: p.reason
|
|
17303
|
+
}));
|
|
17304
|
+
const divergences = inGraph ? computeDivergences(graph, { node: affected, incidents }).divergences.map((d) => ({
|
|
17305
|
+
type: d.type,
|
|
17306
|
+
summary: divergenceSummary(d)
|
|
17307
|
+
})) : [];
|
|
17308
|
+
const card = {
|
|
17309
|
+
kind: "incident",
|
|
17310
|
+
id: errorEvent.id,
|
|
17311
|
+
at: errorEvent.timestamp,
|
|
17312
|
+
incidentKind: (0, import_types59.incidentKindOf)(errorEvent),
|
|
17313
|
+
service: errorEvent.service,
|
|
17314
|
+
affectedNode: affected,
|
|
17315
|
+
message: errorEvent.errorMessage,
|
|
17316
|
+
...errorEvent.exceptionType ? { exceptionType: errorEvent.exceptionType } : {},
|
|
17317
|
+
...errorEvent.httpStatusCode !== void 0 ? { httpStatusCode: errorEvent.httpStatusCode } : {},
|
|
17318
|
+
...errorEvent.incidentCount !== void 0 ? { count: errorEvent.incidentCount } : {},
|
|
17319
|
+
...errorEvent.firstTimestamp && errorEvent.lastTimestamp ? { window: { first: errorEvent.firstTimestamp, last: errorEvent.lastTimestamp } } : {},
|
|
17320
|
+
...errorEvent.traceId ? { traceId: errorEvent.traceId } : {},
|
|
17321
|
+
...errorEvent.spanId ? { spanId: errorEvent.spanId } : {},
|
|
17322
|
+
locus,
|
|
17323
|
+
rootCause,
|
|
17324
|
+
...nearest.length > 0 ? { blastRadius: { totalAffected: blast.totalAffected, nearest } } : {},
|
|
17325
|
+
...policyCards.length > 0 ? { policies: policyCards } : {},
|
|
17326
|
+
...divergences.length > 0 ? { divergence: divergences } : {},
|
|
17327
|
+
headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
|
|
17328
|
+
};
|
|
17329
|
+
return import_types59.IncidentCardSchema.parse(card);
|
|
17330
|
+
}
|
|
17331
|
+
|
|
17332
|
+
// src/ask.ts
|
|
17333
|
+
init_cjs_shims();
|
|
17334
|
+
var import_types60 = require("@neat.is/types");
|
|
17168
17335
|
var DEFAULT_MAX_NODES = 3;
|
|
17169
17336
|
var MAX_FACTS_PER_SECTION = 6;
|
|
17170
17337
|
var INTENT_RULES = [
|
|
@@ -17392,7 +17559,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17392
17559
|
};
|
|
17393
17560
|
graph.forEachNode((id, attrs) => {
|
|
17394
17561
|
const node = attrs;
|
|
17395
|
-
if (node.type ===
|
|
17562
|
+
if (node.type === import_types60.NodeType.FrontierNode) return;
|
|
17396
17563
|
const name = nodeName(node);
|
|
17397
17564
|
const body = idBody(id);
|
|
17398
17565
|
const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
|
|
@@ -17411,7 +17578,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17411
17578
|
const res = await searchIndex.search(question, 10);
|
|
17412
17579
|
if (res.provider !== "substring") {
|
|
17413
17580
|
for (const m of res.matches) {
|
|
17414
|
-
if (m.node.type ===
|
|
17581
|
+
if (m.node.type === import_types60.NodeType.FrontierNode) continue;
|
|
17415
17582
|
const already = best.get(m.node.id);
|
|
17416
17583
|
if (!already && m.score < EMBED_MIN_SCORE) continue;
|
|
17417
17584
|
consider({
|
|
@@ -17449,7 +17616,7 @@ function edgeSignalNote(e) {
|
|
|
17449
17616
|
function buildRootCauseSection(graph, node, incidents, now) {
|
|
17450
17617
|
const result = getRootCause(graph, node, void 0, incidents, { now });
|
|
17451
17618
|
if (!result) return null;
|
|
17452
|
-
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ??
|
|
17619
|
+
const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types60.Provenance.OBSERVED;
|
|
17453
17620
|
const facts = [
|
|
17454
17621
|
{
|
|
17455
17622
|
text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
|
|
@@ -17499,7 +17666,7 @@ function buildObservedSection(graph, node) {
|
|
|
17499
17666
|
if (result.observed && result.inboundObservedCount > 0) {
|
|
17500
17667
|
facts.push({
|
|
17501
17668
|
text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
|
|
17502
|
-
provenance:
|
|
17669
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17503
17670
|
});
|
|
17504
17671
|
} else {
|
|
17505
17672
|
return null;
|
|
@@ -17527,7 +17694,7 @@ function buildIncidentsSection(node, incidents) {
|
|
|
17527
17694
|
const facts = ordered.map((ev) => ({
|
|
17528
17695
|
text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
|
|
17529
17696
|
// ErrorEvents are observation records — OBSERVED by definition.
|
|
17530
|
-
provenance:
|
|
17697
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17531
17698
|
}));
|
|
17532
17699
|
return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
|
|
17533
17700
|
}
|
|
@@ -17587,7 +17754,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17587
17754
|
}
|
|
17588
17755
|
const byKey = /* @__PURE__ */ new Map();
|
|
17589
17756
|
for (const ev of incidents) {
|
|
17590
|
-
const key = ev.affectedNode || (0,
|
|
17757
|
+
const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
|
|
17591
17758
|
const cur = byKey.get(key);
|
|
17592
17759
|
if (!cur) {
|
|
17593
17760
|
byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
|
|
@@ -17602,7 +17769,7 @@ function buildGlobalIncidentsSection(incidents) {
|
|
|
17602
17769
|
const rows = [...byKey.values()].sort((a, b) => b.count - a.count || b.latest.localeCompare(a.latest) || a.key.localeCompare(b.key)).slice(0, MAX_FACTS_PER_SECTION);
|
|
17603
17770
|
const facts = rows.map((r) => ({
|
|
17604
17771
|
text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
|
|
17605
|
-
provenance:
|
|
17772
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17606
17773
|
}));
|
|
17607
17774
|
return {
|
|
17608
17775
|
heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
|
|
@@ -17615,7 +17782,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17615
17782
|
graph.forEachNode((_id, attrs) => {
|
|
17616
17783
|
const node = attrs;
|
|
17617
17784
|
nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
|
|
17618
|
-
if (node.type ===
|
|
17785
|
+
if (node.type === import_types60.NodeType.ServiceNode) services.push(node.id);
|
|
17619
17786
|
});
|
|
17620
17787
|
const edgeByProv = /* @__PURE__ */ new Map();
|
|
17621
17788
|
graph.forEachEdge((_id, attrs) => {
|
|
@@ -17627,10 +17794,10 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17627
17794
|
const shapeFacts = [
|
|
17628
17795
|
{ text: `${graph.order} nodes, ${graph.size} edges` },
|
|
17629
17796
|
{
|
|
17630
|
-
text: `${count(
|
|
17797
|
+
text: `${count(import_types60.NodeType.ServiceNode)} services, ${count(import_types60.NodeType.FileNode)} files, ${count(import_types60.NodeType.SymbolNode)} symbols, ${count(import_types60.NodeType.DatabaseNode)} databases`
|
|
17631
17798
|
}
|
|
17632
17799
|
];
|
|
17633
|
-
for (const p of [
|
|
17800
|
+
for (const p of [import_types60.Provenance.EXTRACTED, import_types60.Provenance.OBSERVED, import_types60.Provenance.INFERRED, import_types60.Provenance.STALE]) {
|
|
17634
17801
|
const n = edgeByProv.get(p) ?? 0;
|
|
17635
17802
|
if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
|
|
17636
17803
|
}
|
|
@@ -17647,7 +17814,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17647
17814
|
if (incidents && incidents.length > 0) {
|
|
17648
17815
|
const incCount = /* @__PURE__ */ new Map();
|
|
17649
17816
|
for (const ev of incidents) {
|
|
17650
|
-
const key = ev.affectedNode || (0,
|
|
17817
|
+
const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
|
|
17651
17818
|
incCount.set(key, (incCount.get(key) ?? 0) + 1);
|
|
17652
17819
|
}
|
|
17653
17820
|
const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
|
|
@@ -17655,7 +17822,7 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17655
17822
|
heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
|
|
17656
17823
|
facts: top.map(([k, n]) => ({
|
|
17657
17824
|
text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
|
|
17658
|
-
provenance:
|
|
17825
|
+
provenance: import_types60.Provenance.OBSERVED
|
|
17659
17826
|
}))
|
|
17660
17827
|
});
|
|
17661
17828
|
}
|
|
@@ -17791,7 +17958,7 @@ async function askGraph(graph, question, opts = {}) {
|
|
|
17791
17958
|
const provSet = /* @__PURE__ */ new Set();
|
|
17792
17959
|
for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
|
|
17793
17960
|
const confidence = sections[0]?.facts[0]?.confidence;
|
|
17794
|
-
return
|
|
17961
|
+
return import_types60.AskResultSchema.parse({
|
|
17795
17962
|
question,
|
|
17796
17963
|
intent,
|
|
17797
17964
|
matched,
|
|
@@ -17938,7 +18105,7 @@ init_cjs_shims();
|
|
|
17938
18105
|
var import_node_fs37 = require("fs");
|
|
17939
18106
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
17940
18107
|
var import_node_path72 = __toESM(require("path"), 1);
|
|
17941
|
-
var
|
|
18108
|
+
var import_types61 = require("@neat.is/types");
|
|
17942
18109
|
var LOCK_TIMEOUT_MS = 5e3;
|
|
17943
18110
|
var LOCK_RETRY_MS = 50;
|
|
17944
18111
|
function neatHome() {
|
|
@@ -18148,10 +18315,10 @@ async function readRegistry() {
|
|
|
18148
18315
|
throw err;
|
|
18149
18316
|
}
|
|
18150
18317
|
const parsed = JSON.parse(raw);
|
|
18151
|
-
return
|
|
18318
|
+
return import_types61.RegistryFileSchema.parse(parsed);
|
|
18152
18319
|
}
|
|
18153
18320
|
async function writeRegistry(reg) {
|
|
18154
|
-
const validated =
|
|
18321
|
+
const validated = import_types61.RegistryFileSchema.parse(reg);
|
|
18155
18322
|
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
18156
18323
|
}
|
|
18157
18324
|
var ProjectNameCollisionError = class extends Error {
|
|
@@ -18552,15 +18719,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
|
|
|
18552
18719
|
|
|
18553
18720
|
// src/connectors/index.ts
|
|
18554
18721
|
init_cjs_shims();
|
|
18555
|
-
var
|
|
18722
|
+
var import_types62 = require("@neat.is/types");
|
|
18556
18723
|
var NO_ENV = "unknown";
|
|
18557
18724
|
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
18558
18725
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
18559
18726
|
const sites = [];
|
|
18560
18727
|
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
18561
18728
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
18562
|
-
if (edge.provenance !==
|
|
18563
|
-
const parsed = (0,
|
|
18729
|
+
if (edge.provenance !== import_types62.Provenance.EXTRACTED) continue;
|
|
18730
|
+
const parsed = (0, import_types62.parseFileId)(edge.source);
|
|
18564
18731
|
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
18565
18732
|
const site = { relPath: edge.evidence.file };
|
|
18566
18733
|
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
@@ -18571,7 +18738,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
|
18571
18738
|
function routeCallSiteFor(graph, targetNodeId) {
|
|
18572
18739
|
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
18573
18740
|
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
18574
|
-
if (attrs.type !==
|
|
18741
|
+
if (attrs.type !== import_types62.NodeType.RouteNode || !attrs.path) return void 0;
|
|
18575
18742
|
const site = { relPath: attrs.path };
|
|
18576
18743
|
if (attrs.line !== void 0) site.line = attrs.line;
|
|
18577
18744
|
return site;
|
|
@@ -18601,7 +18768,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
18601
18768
|
errorMessage: signal.incident.errorMessage,
|
|
18602
18769
|
...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
|
|
18603
18770
|
affectedNode: resolved.targetNodeId
|
|
18604
|
-
});
|
|
18771
|
+
}, ctx.project);
|
|
18605
18772
|
continue;
|
|
18606
18773
|
}
|
|
18607
18774
|
if (resolved.ensureInfraNode) {
|
|
@@ -19183,23 +19350,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
19183
19350
|
|
|
19184
19351
|
// src/connectors/supabase/resolve.ts
|
|
19185
19352
|
init_cjs_shims();
|
|
19186
|
-
var
|
|
19353
|
+
var import_types64 = require("@neat.is/types");
|
|
19187
19354
|
function createSupabaseResolveTarget(graph, config) {
|
|
19188
19355
|
return (signal, _ctx) => {
|
|
19189
19356
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
19190
19357
|
return null;
|
|
19191
19358
|
}
|
|
19192
|
-
const subResourceId = (0,
|
|
19359
|
+
const subResourceId = (0, import_types64.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
19193
19360
|
if (graph.hasNode(subResourceId)) {
|
|
19194
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
19361
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
|
|
19195
19362
|
}
|
|
19196
|
-
const bareResourceId = (0,
|
|
19363
|
+
const bareResourceId = (0, import_types64.infraId)(signal.targetKind, signal.targetName);
|
|
19197
19364
|
if (graph.hasNode(bareResourceId)) {
|
|
19198
|
-
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType:
|
|
19365
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
|
|
19199
19366
|
}
|
|
19200
|
-
const projectLevelId = (0,
|
|
19367
|
+
const projectLevelId = (0, import_types64.infraId)("supabase", config.nodeRef);
|
|
19201
19368
|
if (graph.hasNode(projectLevelId)) {
|
|
19202
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
19369
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
|
|
19203
19370
|
}
|
|
19204
19371
|
return null;
|
|
19205
19372
|
};
|
|
@@ -19292,7 +19459,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
19292
19459
|
|
|
19293
19460
|
// src/connectors/railway/index.ts
|
|
19294
19461
|
init_cjs_shims();
|
|
19295
|
-
var
|
|
19462
|
+
var import_types68 = require("@neat.is/types");
|
|
19296
19463
|
|
|
19297
19464
|
// src/connectors/railway/client.ts
|
|
19298
19465
|
init_cjs_shims();
|
|
@@ -19443,7 +19610,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
19443
19610
|
const out = [];
|
|
19444
19611
|
graph.forEachNode((_id, attrs) => {
|
|
19445
19612
|
const node = attrs;
|
|
19446
|
-
if (node.type !==
|
|
19613
|
+
if (node.type !== import_types68.NodeType.RouteNode) return;
|
|
19447
19614
|
const route = attrs;
|
|
19448
19615
|
if (route.service !== serviceName) return;
|
|
19449
19616
|
out.push({
|
|
@@ -19547,12 +19714,12 @@ function createRailwayResolveTarget(config) {
|
|
|
19547
19714
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
19548
19715
|
if (!serviceName) return null;
|
|
19549
19716
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
19550
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
19717
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types68.EdgeType.CALLS };
|
|
19551
19718
|
}
|
|
19552
19719
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
19553
19720
|
const peerName = config.serviceNameById[signal.targetName];
|
|
19554
19721
|
if (!peerName) return null;
|
|
19555
|
-
return { targetNodeId: (0,
|
|
19722
|
+
return { targetNodeId: (0, import_types68.serviceId)(peerName), serviceName, edgeType: import_types68.EdgeType.CONNECTS_TO };
|
|
19556
19723
|
}
|
|
19557
19724
|
return null;
|
|
19558
19725
|
};
|
|
@@ -19740,7 +19907,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
19740
19907
|
|
|
19741
19908
|
// src/connectors/firebase/resolve.ts
|
|
19742
19909
|
init_cjs_shims();
|
|
19743
|
-
var
|
|
19910
|
+
var import_types69 = require("@neat.is/types");
|
|
19744
19911
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
19745
19912
|
switch (resourceType) {
|
|
19746
19913
|
case "cloud_function":
|
|
@@ -19755,7 +19922,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
19755
19922
|
const entries = [];
|
|
19756
19923
|
graph.forEachNode((_id, attrs) => {
|
|
19757
19924
|
const node = attrs;
|
|
19758
|
-
if (node.type !==
|
|
19925
|
+
if (node.type !== import_types69.NodeType.RouteNode) return;
|
|
19759
19926
|
const route = attrs;
|
|
19760
19927
|
if (route.service !== serviceName) return;
|
|
19761
19928
|
entries.push({
|
|
@@ -19787,7 +19954,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
19787
19954
|
return {
|
|
19788
19955
|
targetNodeId: match.routeNodeId,
|
|
19789
19956
|
serviceName,
|
|
19790
|
-
edgeType:
|
|
19957
|
+
edgeType: import_types69.EdgeType.CALLS
|
|
19791
19958
|
};
|
|
19792
19959
|
};
|
|
19793
19960
|
}
|
|
@@ -19814,7 +19981,7 @@ init_cjs_shims();
|
|
|
19814
19981
|
|
|
19815
19982
|
// src/connectors/cloudflare/connector.ts
|
|
19816
19983
|
init_cjs_shims();
|
|
19817
|
-
var
|
|
19984
|
+
var import_types71 = require("@neat.is/types");
|
|
19818
19985
|
|
|
19819
19986
|
// src/connectors/cloudflare/client.ts
|
|
19820
19987
|
init_cjs_shims();
|
|
@@ -19978,7 +20145,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
19978
20145
|
graph.forEachNode((id, attrs) => {
|
|
19979
20146
|
if (found) return;
|
|
19980
20147
|
const a = attrs;
|
|
19981
|
-
if (a.type ===
|
|
20148
|
+
if (a.type === import_types71.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
19982
20149
|
found = id;
|
|
19983
20150
|
}
|
|
19984
20151
|
});
|
|
@@ -19990,7 +20157,7 @@ function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
|
19990
20157
|
graph.forEachNode((id, attrs) => {
|
|
19991
20158
|
if (found) return;
|
|
19992
20159
|
const a = attrs;
|
|
19993
|
-
if (a.type !==
|
|
20160
|
+
if (a.type !== import_types71.NodeType.RouteNode || a.service !== serviceName) return;
|
|
19994
20161
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
19995
20162
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
19996
20163
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -20009,11 +20176,11 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20009
20176
|
};
|
|
20010
20177
|
const mapping = config.workers?.[scriptName];
|
|
20011
20178
|
if (mapping) {
|
|
20012
|
-
const wholeFileId = (0,
|
|
20179
|
+
const wholeFileId = (0, import_types71.fileId)(mapping.service, mapping.entryFile);
|
|
20013
20180
|
return {
|
|
20014
20181
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
20015
20182
|
serviceName: mapping.service,
|
|
20016
|
-
edgeType:
|
|
20183
|
+
edgeType: import_types71.EdgeType.CALLS
|
|
20017
20184
|
};
|
|
20018
20185
|
}
|
|
20019
20186
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -20022,13 +20189,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
20022
20189
|
return {
|
|
20023
20190
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
20024
20191
|
serviceName: fileNode.service,
|
|
20025
|
-
edgeType:
|
|
20192
|
+
edgeType: import_types71.EdgeType.CALLS
|
|
20026
20193
|
};
|
|
20027
20194
|
}
|
|
20028
20195
|
return {
|
|
20029
|
-
targetNodeId: (0,
|
|
20196
|
+
targetNodeId: (0, import_types71.infraId)("cloudflare-worker", scriptName),
|
|
20030
20197
|
serviceName: scriptName,
|
|
20031
|
-
edgeType:
|
|
20198
|
+
edgeType: import_types71.EdgeType.CALLS,
|
|
20032
20199
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
20033
20200
|
};
|
|
20034
20201
|
};
|
|
@@ -20224,14 +20391,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
|
|
|
20224
20391
|
|
|
20225
20392
|
// src/connectors/neon/resolve.ts
|
|
20226
20393
|
init_cjs_shims();
|
|
20227
|
-
var
|
|
20394
|
+
var import_types75 = require("@neat.is/types");
|
|
20228
20395
|
function createNeonResolveTarget(config) {
|
|
20229
20396
|
return (signal) => {
|
|
20230
20397
|
if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
20231
20398
|
return {
|
|
20232
|
-
targetNodeId: (0,
|
|
20399
|
+
targetNodeId: (0, import_types75.infraId)("sql-table", signal.targetName),
|
|
20233
20400
|
serviceName: config.serviceName,
|
|
20234
|
-
edgeType:
|
|
20401
|
+
edgeType: import_types75.EdgeType.CALLS,
|
|
20235
20402
|
ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
|
|
20236
20403
|
};
|
|
20237
20404
|
};
|
|
@@ -20412,14 +20579,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
20412
20579
|
|
|
20413
20580
|
// src/connectors/cloud-run/resolve.ts
|
|
20414
20581
|
init_cjs_shims();
|
|
20415
|
-
var
|
|
20582
|
+
var import_types79 = require("@neat.is/types");
|
|
20416
20583
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
20417
20584
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
20418
20585
|
let found = null;
|
|
20419
20586
|
graph.forEachNode((_id, attrs) => {
|
|
20420
20587
|
if (found) return;
|
|
20421
20588
|
const node = attrs;
|
|
20422
|
-
if (node.type !==
|
|
20589
|
+
if (node.type !== import_types79.NodeType.RouteNode) return;
|
|
20423
20590
|
const route = attrs;
|
|
20424
20591
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20425
20592
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20444,13 +20611,13 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
20444
20611
|
normalizePathTemplate(path76)
|
|
20445
20612
|
);
|
|
20446
20613
|
if (routeNodeId) {
|
|
20447
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
20614
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types79.EdgeType.CALLS };
|
|
20448
20615
|
}
|
|
20449
20616
|
}
|
|
20450
20617
|
return {
|
|
20451
|
-
targetNodeId: (0,
|
|
20618
|
+
targetNodeId: (0, import_types79.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
|
|
20452
20619
|
serviceName: mappedService ?? gcpServiceName,
|
|
20453
|
-
edgeType:
|
|
20620
|
+
edgeType: import_types79.EdgeType.CALLS,
|
|
20454
20621
|
ensureInfraNode: {
|
|
20455
20622
|
kind: CLOUD_RUN_SERVICE_INFRA_KIND,
|
|
20456
20623
|
name: gcpServiceName,
|
|
@@ -20632,14 +20799,14 @@ function mapLogEntriesToSignals3(entries) {
|
|
|
20632
20799
|
|
|
20633
20800
|
// src/connectors/gcp-lb/resolve.ts
|
|
20634
20801
|
init_cjs_shims();
|
|
20635
|
-
var
|
|
20802
|
+
var import_types83 = require("@neat.is/types");
|
|
20636
20803
|
var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
|
|
20637
20804
|
function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
|
|
20638
20805
|
let found = null;
|
|
20639
20806
|
graph.forEachNode((_id, attrs) => {
|
|
20640
20807
|
if (found) return;
|
|
20641
20808
|
const node = attrs;
|
|
20642
|
-
if (node.type !==
|
|
20809
|
+
if (node.type !== import_types83.NodeType.RouteNode) return;
|
|
20643
20810
|
const route = attrs;
|
|
20644
20811
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20645
20812
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20664,13 +20831,13 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
20664
20831
|
normalizePathTemplate(path76)
|
|
20665
20832
|
);
|
|
20666
20833
|
if (routeNodeId) {
|
|
20667
|
-
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType:
|
|
20834
|
+
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
|
|
20668
20835
|
}
|
|
20669
20836
|
}
|
|
20670
20837
|
return {
|
|
20671
|
-
targetNodeId: (0,
|
|
20838
|
+
targetNodeId: (0, import_types83.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
|
|
20672
20839
|
serviceName: mappedService ?? backendServiceName,
|
|
20673
|
-
edgeType:
|
|
20840
|
+
edgeType: import_types83.EdgeType.CALLS,
|
|
20674
20841
|
ensureInfraNode: {
|
|
20675
20842
|
kind: GCP_LB_BACKEND_INFRA_KIND,
|
|
20676
20843
|
name: backendServiceName,
|
|
@@ -20711,7 +20878,7 @@ function createGcpLbConnector(graph, config = {}) {
|
|
|
20711
20878
|
|
|
20712
20879
|
// src/connectors/render/index.ts
|
|
20713
20880
|
init_cjs_shims();
|
|
20714
|
-
var
|
|
20881
|
+
var import_types86 = require("@neat.is/types");
|
|
20715
20882
|
|
|
20716
20883
|
// src/connectors/render/types.ts
|
|
20717
20884
|
init_cjs_shims();
|
|
@@ -20789,7 +20956,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
20789
20956
|
const out = [];
|
|
20790
20957
|
graph.forEachNode((_id, attrs) => {
|
|
20791
20958
|
const node = attrs;
|
|
20792
|
-
if (node.type !==
|
|
20959
|
+
if (node.type !== import_types86.NodeType.RouteNode) return;
|
|
20793
20960
|
const route = attrs;
|
|
20794
20961
|
if (route.service !== serviceName) return;
|
|
20795
20962
|
out.push({
|
|
@@ -20874,7 +21041,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
|
|
|
20874
21041
|
function createRenderResolveTarget(config) {
|
|
20875
21042
|
return (signal) => {
|
|
20876
21043
|
if (signal.targetKind === ROUTE_TARGET_KIND2) {
|
|
20877
|
-
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType:
|
|
21044
|
+
return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types86.EdgeType.CALLS };
|
|
20878
21045
|
}
|
|
20879
21046
|
return null;
|
|
20880
21047
|
};
|
|
@@ -21012,21 +21179,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
|
|
|
21012
21179
|
|
|
21013
21180
|
// src/connectors/planetscale/resolve.ts
|
|
21014
21181
|
init_cjs_shims();
|
|
21015
|
-
var
|
|
21182
|
+
var import_types90 = require("@neat.is/types");
|
|
21016
21183
|
var PLANETSCALE_DATABASE_KIND = "planetscale-database";
|
|
21017
21184
|
function createPlanetscaleResolveTarget(graph, config) {
|
|
21018
21185
|
const databaseName = `${config.organization}/${config.database}`;
|
|
21019
21186
|
return (signal, _ctx) => {
|
|
21020
21187
|
if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
|
|
21021
|
-
const tableId = (0,
|
|
21188
|
+
const tableId = (0, import_types90.infraId)("sql-table", signal.targetName);
|
|
21022
21189
|
if (graph.hasNode(tableId)) {
|
|
21023
|
-
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType:
|
|
21190
|
+
return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
|
|
21024
21191
|
}
|
|
21025
|
-
const providerId = (0,
|
|
21192
|
+
const providerId = (0, import_types90.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
|
|
21026
21193
|
return {
|
|
21027
21194
|
targetNodeId: providerId,
|
|
21028
21195
|
serviceName: config.serviceName,
|
|
21029
|
-
edgeType:
|
|
21196
|
+
edgeType: import_types90.EdgeType.CALLS,
|
|
21030
21197
|
ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
|
|
21031
21198
|
};
|
|
21032
21199
|
};
|
|
@@ -21291,7 +21458,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
21291
21458
|
|
|
21292
21459
|
// src/connectors/eas/resolve.ts
|
|
21293
21460
|
init_cjs_shims();
|
|
21294
|
-
var
|
|
21461
|
+
var import_types95 = require("@neat.is/types");
|
|
21295
21462
|
var NO_ENV2 = "unknown";
|
|
21296
21463
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
21297
21464
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -21307,8 +21474,8 @@ function configBasenamesForPhase(phase) {
|
|
|
21307
21474
|
function configNodeService(graph, configNodeId) {
|
|
21308
21475
|
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
21309
21476
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
21310
|
-
if (edge.type !==
|
|
21311
|
-
const parsed = (0,
|
|
21477
|
+
if (edge.type !== import_types95.EdgeType.CONFIGURED_BY) continue;
|
|
21478
|
+
const parsed = (0, import_types95.parseFileId)(edge.source);
|
|
21312
21479
|
if (parsed) return parsed.service;
|
|
21313
21480
|
}
|
|
21314
21481
|
return null;
|
|
@@ -21319,7 +21486,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
21319
21486
|
graph.forEachNode((id, attrs) => {
|
|
21320
21487
|
if (scoped) return;
|
|
21321
21488
|
const node = attrs;
|
|
21322
|
-
if (node.type !==
|
|
21489
|
+
if (node.type !== import_types95.NodeType.ConfigNode) return;
|
|
21323
21490
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
21324
21491
|
if (anyMatch === null) anyMatch = id;
|
|
21325
21492
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -21336,13 +21503,13 @@ function createEasResolveTarget(graph) {
|
|
|
21336
21503
|
if (basenames.length > 0) {
|
|
21337
21504
|
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
21338
21505
|
if (configNodeId) {
|
|
21339
|
-
return { targetNodeId: configNodeId, serviceName, edgeType:
|
|
21506
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types95.EdgeType.CALLS };
|
|
21340
21507
|
}
|
|
21341
21508
|
}
|
|
21342
21509
|
return {
|
|
21343
21510
|
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
21344
21511
|
serviceName,
|
|
21345
|
-
edgeType:
|
|
21512
|
+
edgeType: import_types95.EdgeType.CALLS
|
|
21346
21513
|
};
|
|
21347
21514
|
};
|
|
21348
21515
|
}
|
|
@@ -21929,6 +22096,7 @@ async function startConnectorPolling(input) {
|
|
|
21929
22096
|
registration.connector,
|
|
21930
22097
|
{
|
|
21931
22098
|
projectDir: input.projectDir,
|
|
22099
|
+
project: input.project,
|
|
21932
22100
|
credentials: registration.credentials,
|
|
21933
22101
|
...input.errorsPath ? { errorsPath: input.errorsPath } : {}
|
|
21934
22102
|
},
|
|
@@ -22103,11 +22271,11 @@ function registerRoutes(scope, ctx) {
|
|
|
22103
22271
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
22104
22272
|
const parsed = [];
|
|
22105
22273
|
for (const c of candidates) {
|
|
22106
|
-
const r =
|
|
22274
|
+
const r = import_types98.DivergenceTypeSchema.safeParse(c);
|
|
22107
22275
|
if (!r.success) {
|
|
22108
22276
|
return reply.code(400).send({
|
|
22109
22277
|
error: `unknown divergence type "${c}"`,
|
|
22110
|
-
allowed:
|
|
22278
|
+
allowed: import_types98.DivergenceTypeSchema.options
|
|
22111
22279
|
});
|
|
22112
22280
|
}
|
|
22113
22281
|
parsed.push(r.data);
|
|
@@ -22224,6 +22392,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22224
22392
|
reg.connector,
|
|
22225
22393
|
{
|
|
22226
22394
|
projectDir: proj.scanPath ?? "",
|
|
22395
|
+
project: proj.name,
|
|
22227
22396
|
credentials: reg.credentials,
|
|
22228
22397
|
...incidentsPath ? { errorsPath: incidentsPath } : {}
|
|
22229
22398
|
},
|
|
@@ -22287,6 +22456,39 @@ function registerRoutes(scope, ctx) {
|
|
|
22287
22456
|
if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
|
|
22288
22457
|
return result;
|
|
22289
22458
|
});
|
|
22459
|
+
scope.get("/graph/incident-card/:nodeId", async (req, reply) => {
|
|
22460
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
22461
|
+
if (!proj) return;
|
|
22462
|
+
const { nodeId } = req.params;
|
|
22463
|
+
if (!proj.graph.hasNode(nodeId)) {
|
|
22464
|
+
return reply.code(404).send({ error: "node not found", id: nodeId });
|
|
22465
|
+
}
|
|
22466
|
+
const epath = errorsPathFor(proj);
|
|
22467
|
+
const incidents = epath ? await readErrorEvents(epath) : [];
|
|
22468
|
+
let errorEvent;
|
|
22469
|
+
if (req.query.errorId) {
|
|
22470
|
+
errorEvent = incidents.find((e) => e.id === req.query.errorId);
|
|
22471
|
+
if (!errorEvent) {
|
|
22472
|
+
return reply.code(404).send({ error: "error event not found", id: req.query.errorId });
|
|
22473
|
+
}
|
|
22474
|
+
} else {
|
|
22475
|
+
const svc = nodeId.replace(/^service:/, "");
|
|
22476
|
+
errorEvent = [...incidents].filter((e) => e.affectedNode === nodeId || e.service === svc).sort((a, b) => (b.timestamp ?? "").localeCompare(a.timestamp ?? ""))[0];
|
|
22477
|
+
if (!errorEvent) {
|
|
22478
|
+
return reply.code(404).send({ error: "no incident found for node", id: nodeId });
|
|
22479
|
+
}
|
|
22480
|
+
}
|
|
22481
|
+
const policyPath = ctx.policyFilePathFor(proj);
|
|
22482
|
+
let policies = [];
|
|
22483
|
+
if (policyPath) {
|
|
22484
|
+
try {
|
|
22485
|
+
policies = await loadPolicyFile(policyPath);
|
|
22486
|
+
} catch {
|
|
22487
|
+
policies = [];
|
|
22488
|
+
}
|
|
22489
|
+
}
|
|
22490
|
+
return buildIncidentCard(proj.graph, errorEvent, incidents, policies);
|
|
22491
|
+
});
|
|
22290
22492
|
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
22291
22493
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
22292
22494
|
if (!proj) return;
|
|
@@ -22469,7 +22671,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22469
22671
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
22470
22672
|
let violations = await log.readAll();
|
|
22471
22673
|
if (req.query.severity) {
|
|
22472
|
-
const sev =
|
|
22674
|
+
const sev = import_types98.PolicySeveritySchema.safeParse(req.query.severity);
|
|
22473
22675
|
if (!sev.success) {
|
|
22474
22676
|
return reply.code(400).send({
|
|
22475
22677
|
error: "invalid severity",
|
|
@@ -22508,7 +22710,7 @@ function registerRoutes(scope, ctx) {
|
|
|
22508
22710
|
scope.post("/policies/check", async (req, reply) => {
|
|
22509
22711
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
22510
22712
|
if (!proj) return;
|
|
22511
|
-
const parsed =
|
|
22713
|
+
const parsed = import_types98.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
22512
22714
|
if (!parsed.success) {
|
|
22513
22715
|
return reply.code(400).send({
|
|
22514
22716
|
error: "invalid /policies/check body",
|
|
@@ -22857,7 +23059,7 @@ function unroutedErrorsPath(neatHome3) {
|
|
|
22857
23059
|
}
|
|
22858
23060
|
|
|
22859
23061
|
// src/daemon.ts
|
|
22860
|
-
var
|
|
23062
|
+
var import_types99 = require("@neat.is/types");
|
|
22861
23063
|
function daemonJsonPath(scanPath) {
|
|
22862
23064
|
return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
|
|
22863
23065
|
}
|
|
@@ -22982,7 +23184,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
22982
23184
|
if (!serviceName) return true;
|
|
22983
23185
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
22984
23186
|
return graph.someNode(
|
|
22985
|
-
(_id, attrs) => attrs.type ===
|
|
23187
|
+
(_id, attrs) => attrs.type === import_types99.NodeType.ServiceNode && attrs.name === serviceName
|
|
22986
23188
|
);
|
|
22987
23189
|
}
|
|
22988
23190
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|
|
@@ -23437,7 +23639,12 @@ async function startDaemon(opts = {}) {
|
|
|
23437
23639
|
onErrorSpanSync: async (span) => {
|
|
23438
23640
|
const slot = await resolveTargetSlot(span.service, span.traceId);
|
|
23439
23641
|
if (!slot) return;
|
|
23440
|
-
await makeErrorSpanWriter(
|
|
23642
|
+
await makeErrorSpanWriter(
|
|
23643
|
+
slot.paths.errorsPath,
|
|
23644
|
+
slot.graph,
|
|
23645
|
+
slot.entry.path,
|
|
23646
|
+
slot.entry.name
|
|
23647
|
+
)(span);
|
|
23441
23648
|
},
|
|
23442
23649
|
// Project-scoped route (issue #367) — the URL already named the
|
|
23443
23650
|
// project. Resolution is a direct slot lookup; service.name resolves
|
|
@@ -23460,7 +23667,12 @@ async function startDaemon(opts = {}) {
|
|
|
23460
23667
|
onProjectErrorSpanSync: async (project, span) => {
|
|
23461
23668
|
const slot = await resolveSlotByName(project, span.service, span.traceId);
|
|
23462
23669
|
if (!slot) return;
|
|
23463
|
-
await makeErrorSpanWriter(
|
|
23670
|
+
await makeErrorSpanWriter(
|
|
23671
|
+
slot.paths.errorsPath,
|
|
23672
|
+
slot.graph,
|
|
23673
|
+
slot.entry.path,
|
|
23674
|
+
slot.entry.name
|
|
23675
|
+
)(span);
|
|
23464
23676
|
},
|
|
23465
23677
|
// #881 — 404 a project-scoped POST for a project this daemon doesn't
|
|
23466
23678
|
// host, rather than accepting it and dropping the batch. `slots` covers
|