@neat.is/core 0.9.21-dev.20260924 → 0.9.21
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-NFJ4W5T6.js → chunk-J3VVEB45.js} +200 -50
- package/dist/chunk-J3VVEB45.js.map +1 -0
- package/dist/{chunk-SK2FEJKB.js → chunk-KVJQQUUY.js} +186 -40
- package/dist/chunk-KVJQQUUY.js.map +1 -0
- package/dist/cli.cjs +494 -345
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +470 -177
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +482 -189
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +225 -78
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-NFJ4W5T6.js.map +0 -1
- package/dist/chunk-SK2FEJKB.js.map +0 -1
|
@@ -17474,7 +17474,7 @@ var INTENT_RULES = [
|
|
|
17474
17474
|
},
|
|
17475
17475
|
{
|
|
17476
17476
|
intent: "blast-radius",
|
|
17477
|
-
test: /\b(blast|break[\s-]?if|breaks[\s-]?if|impact|downstream|dependents?|redeploy|who\s+(?:uses|calls|depends)|what\s+depends\s+on|affect(?:s|ed)?)\b/
|
|
17477
|
+
test: /\b(blast|break[\s-]?if|breaks[\s-]?if|impact|downstream|dependents?|redeploy|who\s+(?:uses|calls|depends(?:\s+on)?)|what\s+depends\s+on|consumers?\s+of|callers?\s+of|affect(?:s|ed)?)\b/
|
|
17478
17478
|
},
|
|
17479
17479
|
{
|
|
17480
17480
|
intent: "divergence",
|
|
@@ -17486,11 +17486,11 @@ var INTENT_RULES = [
|
|
|
17486
17486
|
},
|
|
17487
17487
|
{
|
|
17488
17488
|
intent: "observed",
|
|
17489
|
-
test: /\b(at\s+runtime|in\s+prod(?:uction)?|actually
|
|
17489
|
+
test: /\b(at\s+runtime|in\s+prod(?:uction)?|actually|really\s+call\w*|observed|runtime\s+traffic|slow|latency|p95|timing)\b/
|
|
17490
17490
|
},
|
|
17491
17491
|
{
|
|
17492
17492
|
intent: "dependencies",
|
|
17493
|
-
test: /\b(depend\w*|calls?|uses?|imports?|relies\s+on|needs
|
|
17493
|
+
test: /\b(depend\w*|calls?|uses?|imports?|relies\s+on|needs?|talks?\s+to|connects?\s+to|hits?|reads?\s+from|writes?\s+to)\b/
|
|
17494
17494
|
}
|
|
17495
17495
|
];
|
|
17496
17496
|
function classifyIntent(question) {
|
|
@@ -17657,7 +17657,25 @@ var INTENT_WORDS = /* @__PURE__ */ new Set([
|
|
|
17657
17657
|
"rely",
|
|
17658
17658
|
"needs",
|
|
17659
17659
|
"need",
|
|
17660
|
-
"sync"
|
|
17660
|
+
"sync",
|
|
17661
|
+
"talk",
|
|
17662
|
+
"talks",
|
|
17663
|
+
"connect",
|
|
17664
|
+
"connects",
|
|
17665
|
+
"hit",
|
|
17666
|
+
"hits",
|
|
17667
|
+
"read",
|
|
17668
|
+
"reads",
|
|
17669
|
+
"write",
|
|
17670
|
+
"writes",
|
|
17671
|
+
"consumer",
|
|
17672
|
+
"consumers",
|
|
17673
|
+
"caller",
|
|
17674
|
+
"callers",
|
|
17675
|
+
"slow",
|
|
17676
|
+
"latency",
|
|
17677
|
+
"p95",
|
|
17678
|
+
"timing"
|
|
17661
17679
|
]);
|
|
17662
17680
|
function tokens(input) {
|
|
17663
17681
|
const spaced = input.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
@@ -17681,6 +17699,13 @@ function idBody(id) {
|
|
|
17681
17699
|
const colon = id.indexOf(":");
|
|
17682
17700
|
return colon >= 0 ? id.slice(colon + 1) : id;
|
|
17683
17701
|
}
|
|
17702
|
+
var TYPE_NOUNS = [
|
|
17703
|
+
{ nouns: ["database", "db", "table"], type: NodeType32.DatabaseNode },
|
|
17704
|
+
{ nouns: ["service", "api", "backend"], type: NodeType32.ServiceNode },
|
|
17705
|
+
{ nouns: ["route", "endpoint"], type: NodeType32.RouteNode },
|
|
17706
|
+
{ nouns: ["file"], type: NodeType32.FileNode },
|
|
17707
|
+
{ nouns: ["config"], type: NodeType32.ConfigNode }
|
|
17708
|
+
];
|
|
17684
17709
|
var EMBED_MIN_SCORE = 0.35;
|
|
17685
17710
|
async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
17686
17711
|
const qTokens = queryTokens(question);
|
|
@@ -17688,6 +17713,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17688
17713
|
const best = /* @__PURE__ */ new Map();
|
|
17689
17714
|
const consider = (cand) => {
|
|
17690
17715
|
const cur = best.get(cand.nodeId);
|
|
17716
|
+
if (cur?.via === "id" && cand.via === "embedding") return;
|
|
17691
17717
|
if (!cur || cand.score > cur.score) best.set(cand.nodeId, cand);
|
|
17692
17718
|
};
|
|
17693
17719
|
graph.forEachNode((id, attrs) => {
|
|
@@ -17697,12 +17723,12 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17697
17723
|
const body = idBody(id);
|
|
17698
17724
|
const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
|
|
17699
17725
|
if (labelTokens.size === 0) return;
|
|
17700
|
-
let
|
|
17701
|
-
for (const t of qTokens) if (labelTokens.has(t))
|
|
17726
|
+
let matched2 = 0;
|
|
17727
|
+
for (const t of qTokens) if (labelTokens.has(t)) matched2 += 1;
|
|
17702
17728
|
const named = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
|
|
17703
|
-
if (
|
|
17704
|
-
const coverage = qTokens.length > 0 ?
|
|
17705
|
-
const via = named ? "id" :
|
|
17729
|
+
if (matched2 === 0 && !named) return;
|
|
17730
|
+
const coverage = qTokens.length > 0 ? matched2 / qTokens.length : 0;
|
|
17731
|
+
const via = named ? "id" : matched2 > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
|
|
17706
17732
|
const score = named ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
|
|
17707
17733
|
consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
|
|
17708
17734
|
});
|
|
@@ -17712,6 +17738,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17712
17738
|
if (res.provider !== "substring") {
|
|
17713
17739
|
for (const m of res.matches) {
|
|
17714
17740
|
if (m.node.type === NodeType32.FrontierNode) continue;
|
|
17741
|
+
if (!graph.hasNode(m.node.id)) continue;
|
|
17715
17742
|
const already = best.get(m.node.id);
|
|
17716
17743
|
if (!already && m.score < EMBED_MIN_SCORE) continue;
|
|
17717
17744
|
consider({
|
|
@@ -17725,10 +17752,31 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
|
|
|
17725
17752
|
} catch {
|
|
17726
17753
|
}
|
|
17727
17754
|
}
|
|
17728
|
-
|
|
17729
|
-
|
|
17730
|
-
|
|
17755
|
+
if (best.size === 0) {
|
|
17756
|
+
const words = new Set(tokens(question));
|
|
17757
|
+
const kind = TYPE_NOUNS.find((entry) => entry.nouns.some((noun) => words.has(noun)));
|
|
17758
|
+
if (kind) {
|
|
17759
|
+
const ids = graph.filterNodes((_id, attrs) => attrs.type === kind.type).sort();
|
|
17760
|
+
if (ids.length > 1) {
|
|
17761
|
+
return { matched: [], ambiguousType: { noun: kind.nouns.find((noun) => words.has(noun)), ids } };
|
|
17762
|
+
}
|
|
17763
|
+
if (ids.length === 1) {
|
|
17764
|
+
const id = ids[0];
|
|
17765
|
+
return { matched: [{ nodeId: id, label: nodeName(graph.getNodeAttributes(id)), via: "type", score: 0.6 }].slice(0, maxNodes) };
|
|
17766
|
+
}
|
|
17767
|
+
return { matched: [] };
|
|
17768
|
+
}
|
|
17769
|
+
}
|
|
17770
|
+
const viaRank = { id: 4, label: 3, token: 2, type: 1, embedding: 0 };
|
|
17771
|
+
const ranked = [...best.values()].sort(
|
|
17772
|
+
(a, b) => Number(b.via === "id") - Number(a.via === "id") || b.score - a.score || viaRank[b.via] - viaRank[a.via] || a.nodeId.localeCompare(b.nodeId)
|
|
17773
|
+
);
|
|
17774
|
+
const primary = ranked[0];
|
|
17775
|
+
const primaryType = primary && graph.getNodeAttributes(primary.nodeId).type;
|
|
17776
|
+
const matched = ranked.filter(
|
|
17777
|
+
(candidate) => !primary || primary.via !== "id" || primary.score < 0.85 || candidate === primary || graph.getNodeAttributes(candidate.nodeId).type === primaryType || candidate.via !== "embedding" && candidate.score >= primary.score - 0.2
|
|
17731
17778
|
).slice(0, maxNodes).map((s) => ({ nodeId: s.nodeId, label: s.label, via: s.via, score: Number(s.score.toFixed(3)) }));
|
|
17779
|
+
return { matched };
|
|
17732
17780
|
}
|
|
17733
17781
|
function incidentsForNode(nodeId, incidents) {
|
|
17734
17782
|
if (!incidents || incidents.length === 0) return [];
|
|
@@ -17820,6 +17868,23 @@ function buildBlastSection(graph, node) {
|
|
|
17820
17868
|
facts
|
|
17821
17869
|
};
|
|
17822
17870
|
}
|
|
17871
|
+
function buildDatabaseCallersSection(graph, node) {
|
|
17872
|
+
const callers = getBlastRadius(graph, node).affectedNodes.filter((item) => {
|
|
17873
|
+
if (graph.getNodeAttributes(item.nodeId).type !== NodeType32.ServiceNode) return false;
|
|
17874
|
+
return item.path.slice(1, -1).every((id) => {
|
|
17875
|
+
const type = graph.getNodeAttributes(id).type;
|
|
17876
|
+
return type === NodeType32.FileNode || type === NodeType32.SymbolNode;
|
|
17877
|
+
});
|
|
17878
|
+
});
|
|
17879
|
+
return {
|
|
17880
|
+
heading: `Services calling ${node} \u2014 ${callers.length}`,
|
|
17881
|
+
facts: callers.length > 0 ? callers.slice(0, MAX_FACTS_PER_SECTION).map((item) => ({
|
|
17882
|
+
text: `${item.nodeId} (distance ${item.distance})`,
|
|
17883
|
+
provenance: item.edgeProvenance,
|
|
17884
|
+
confidence: item.confidence
|
|
17885
|
+
})) : [{ text: `No inbound service callers found for ${node}.` }]
|
|
17886
|
+
};
|
|
17887
|
+
}
|
|
17823
17888
|
function buildIncidentsSection(node, incidents) {
|
|
17824
17889
|
const relevant = incidentsForNode(node, incidents);
|
|
17825
17890
|
if (relevant.length === 0) return null;
|
|
@@ -17846,6 +17911,29 @@ function divergenceLine(d) {
|
|
|
17846
17911
|
}
|
|
17847
17912
|
return `[${d.type}] ${d.source} \u2192 ${d.target} \u2014 ${d.reason}`;
|
|
17848
17913
|
}
|
|
17914
|
+
function runtimeEdgeState(graph) {
|
|
17915
|
+
let observed = false;
|
|
17916
|
+
let stale = false;
|
|
17917
|
+
graph.forEachEdge((_id, attrs) => {
|
|
17918
|
+
if (attrs.provenance === Provenance27.OBSERVED) observed = true;
|
|
17919
|
+
if (attrs.provenance === Provenance27.STALE) stale = true;
|
|
17920
|
+
});
|
|
17921
|
+
return { observed, stale };
|
|
17922
|
+
}
|
|
17923
|
+
function pendingRuntimeMessage(graph, divergences) {
|
|
17924
|
+
const count = divergences.filter((d) => d.type === "missing-observed").length;
|
|
17925
|
+
if (count === 0) return void 0;
|
|
17926
|
+
const { observed, stale } = runtimeEdgeState(graph);
|
|
17927
|
+
if (observed) return void 0;
|
|
17928
|
+
const prefix = stale ? "No current runtime observations \u2014 earlier runtime evidence is stale" : "No runtime observed yet";
|
|
17929
|
+
return `${prefix} \u2014 ${count} declared dependenc${count === 1 ? "y is" : "ies are"} waiting to be confirmed. Send traces (or connect a provider) and ask again.`;
|
|
17930
|
+
}
|
|
17931
|
+
function noDivergenceMessage(graph) {
|
|
17932
|
+
if (graph.order === 0) return "Graph is empty \u2014 no declared or observed dependencies to compare.";
|
|
17933
|
+
const { observed, stale } = runtimeEdgeState(graph);
|
|
17934
|
+
if (!observed) return stale ? "No current runtime dependency observations to compare \u2014 earlier runtime evidence is stale." : "No runtime dependency observations to compare with declared code.";
|
|
17935
|
+
return "None \u2014 declared code and observed runtime agree across the graph.";
|
|
17936
|
+
}
|
|
17849
17937
|
function buildDivergenceSection(graph, node, incidents) {
|
|
17850
17938
|
const result = computeDivergences(graph, { node, ...incidents ? { incidents } : {} });
|
|
17851
17939
|
if (result.totalAffected === 0) return null;
|
|
@@ -17862,19 +17950,28 @@ function buildDivergenceSection(graph, node, incidents) {
|
|
|
17862
17950
|
}
|
|
17863
17951
|
function buildGlobalDivergenceSection(graph, incidents) {
|
|
17864
17952
|
const result = computeDivergences(graph, incidents ? { incidents } : {});
|
|
17953
|
+
const pending = pendingRuntimeMessage(graph, result.divergences);
|
|
17954
|
+
const visible = pending ? result.divergences.filter((d) => d.type !== "missing-observed") : result.divergences;
|
|
17955
|
+
if (visible.length === 0 && pending) {
|
|
17956
|
+
return {
|
|
17957
|
+
heading: "Divergences (EXTRACTED vs OBSERVED)",
|
|
17958
|
+
facts: [{ text: pending }]
|
|
17959
|
+
};
|
|
17960
|
+
}
|
|
17865
17961
|
if (result.totalAffected === 0) {
|
|
17866
17962
|
return {
|
|
17867
17963
|
heading: "Divergences (EXTRACTED vs OBSERVED)",
|
|
17868
|
-
facts: [{ text:
|
|
17964
|
+
facts: [{ text: noDivergenceMessage(graph) }]
|
|
17869
17965
|
};
|
|
17870
17966
|
}
|
|
17871
|
-
const facts =
|
|
17967
|
+
const facts = visible.slice(0, MAX_FACTS_PER_SECTION).map((d) => ({
|
|
17872
17968
|
text: divergenceLine(d),
|
|
17873
17969
|
confidence: d.confidence
|
|
17874
17970
|
// Composite by construction (EXTRACTED vs OBSERVED), so no single provenance.
|
|
17875
17971
|
}));
|
|
17972
|
+
if (pending && facts.length < MAX_FACTS_PER_SECTION) facts.push({ text: pending });
|
|
17876
17973
|
return {
|
|
17877
|
-
heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${
|
|
17974
|
+
heading: `Divergences (EXTRACTED vs OBSERVED) \u2014 ${visible.length}`,
|
|
17878
17975
|
facts
|
|
17879
17976
|
};
|
|
17880
17977
|
}
|
|
@@ -17960,13 +18057,12 @@ function buildOverviewSections(graph, incidents) {
|
|
|
17960
18057
|
});
|
|
17961
18058
|
}
|
|
17962
18059
|
const div = computeDivergences(graph, incidents ? { incidents } : {});
|
|
18060
|
+
const pending = pendingRuntimeMessage(graph, div.divergences);
|
|
18061
|
+
const visibleCount = pending ? div.divergences.filter((d) => d.type !== "missing-observed").length : div.totalAffected;
|
|
18062
|
+
const divergenceText = visibleCount > 0 ? `${visibleCount} divergence${visibleCount === 1 ? "" : "s"} between declared code and observed runtime \u2014 ask "are there any divergences?" for the list.${pending ? ` ${pending}` : ""}` : pending ?? noDivergenceMessage(graph);
|
|
17963
18063
|
sections.push({
|
|
17964
18064
|
heading: "Divergences",
|
|
17965
|
-
facts: [
|
|
17966
|
-
{
|
|
17967
|
-
text: div.totalAffected === 0 ? "None \u2014 declared code and observed runtime agree across the graph." : `${div.totalAffected} divergence${div.totalAffected === 1 ? "" : "s"} between declared code and observed runtime \u2014 ask "are there any divergences?" for the list.`
|
|
17968
|
-
}
|
|
17969
|
-
]
|
|
18065
|
+
facts: [{ text: divergenceText }]
|
|
17970
18066
|
});
|
|
17971
18067
|
return sections;
|
|
17972
18068
|
}
|
|
@@ -18011,6 +18107,8 @@ function summarizeGlobal(intent, sections) {
|
|
|
18011
18107
|
const lead = sections[0];
|
|
18012
18108
|
switch (intent) {
|
|
18013
18109
|
case "divergence": {
|
|
18110
|
+
const pending = lead?.facts[0]?.text;
|
|
18111
|
+
if (pending?.startsWith("No runtime") || pending?.startsWith("No current runtime") || pending?.startsWith("Graph is empty")) return pending;
|
|
18014
18112
|
const none = lead?.facts[0]?.text.startsWith("None") ?? true;
|
|
18015
18113
|
return none ? "No divergences across the graph \u2014 declared code and observed runtime agree." : `${lead.heading} across the graph, highest-confidence first below.`;
|
|
18016
18114
|
}
|
|
@@ -18023,11 +18121,16 @@ function summarizeGlobal(intent, sections) {
|
|
|
18023
18121
|
return `System overview across the whole graph: ${sections.length} view${sections.length === 1 ? "" : "s"} below \u2014 shape (nodes/edges by provenance), busiest services, incidents, and divergences.`;
|
|
18024
18122
|
}
|
|
18025
18123
|
}
|
|
18026
|
-
function summarize(question, intent, matched, primary, sections, scope) {
|
|
18124
|
+
function summarize(question, intent, matched, primary, sections, scope, ambiguousType) {
|
|
18027
18125
|
if (scope === "global") {
|
|
18028
18126
|
return summarizeGlobal(intent, sections);
|
|
18029
18127
|
}
|
|
18030
18128
|
if (!primary) {
|
|
18129
|
+
if (ambiguousType) {
|
|
18130
|
+
const shown = ambiguousType.ids.slice(0, 10);
|
|
18131
|
+
const more = ambiguousType.ids.length - shown.length;
|
|
18132
|
+
return `"${ambiguousType.noun}" matches ${ambiguousType.ids.length} nodes. Name one by ID: ${shown.join(", ")}${more > 0 ? `, and ${more} more` : ""}.`;
|
|
18133
|
+
}
|
|
18031
18134
|
return `Nothing in "${question}" resolved to a node in the graph. Name a service, file, route, or table \u2014 e.g. \`ask "what does <service> depend on?"\`. For a graph-wide look, ask for an overview, divergences, or incidents.`;
|
|
18032
18135
|
}
|
|
18033
18136
|
const lead = sections[0];
|
|
@@ -18050,10 +18153,13 @@ function summarize(question, intent, matched, primary, sections, scope) {
|
|
|
18050
18153
|
core = lead ? `${lead.heading} of ${primary}.` : `${primary} has no dependents \u2014 nothing else would break if it failed.`;
|
|
18051
18154
|
break;
|
|
18052
18155
|
case "dependencies":
|
|
18053
|
-
core = lead ? `${primary}: ${lead.heading.toLowerCase()} listed below.` : `${primary} has no declared dependencies in the graph.`;
|
|
18156
|
+
core = lead?.heading.startsWith("Services calling") ? `${lead.heading}.` : lead ? `${primary}: ${lead.heading.toLowerCase()} listed below.` : `${primary} has no declared dependencies in the graph.`;
|
|
18054
18157
|
break;
|
|
18055
18158
|
case "observed":
|
|
18056
|
-
|
|
18159
|
+
{
|
|
18160
|
+
const observed = sections.find((s) => s.heading === "Runtime dependencies (OBSERVED)");
|
|
18161
|
+
core = observed ? `${primary} at runtime: ${observed.facts.length} OBSERVED fact${observed.facts.length === 1 ? "" : "s"}.` : `No runtime traffic OBSERVED for ${primary}.`;
|
|
18162
|
+
}
|
|
18057
18163
|
break;
|
|
18058
18164
|
case "incidents":
|
|
18059
18165
|
core = lead ? `${primary}: ${lead.heading.toLowerCase()}.` : `No incidents recorded against ${primary}.`;
|
|
@@ -18070,24 +18176,28 @@ async function askGraph(graph, question, opts = {}) {
|
|
|
18070
18176
|
const now = opts.now ?? Date.now();
|
|
18071
18177
|
const maxNodes = opts.maxNodes ?? DEFAULT_MAX_NODES;
|
|
18072
18178
|
const intent = classifyIntent(question);
|
|
18073
|
-
const
|
|
18179
|
+
const resolution = await resolveEntities(graph, question, opts.searchIndex, maxNodes);
|
|
18180
|
+
const matched = resolution.matched;
|
|
18074
18181
|
const primary = matched[0]?.nodeId;
|
|
18075
18182
|
const sections = [];
|
|
18076
18183
|
let scope;
|
|
18077
18184
|
if (primary) {
|
|
18078
18185
|
scope = "node";
|
|
18186
|
+
const callersQuestion = intent === "dependencies" && graph.getNodeAttributes(primary).type === NodeType32.DatabaseNode && /\b(?:which|what)\s+services?\s+(?:talks?\s+to|connects?\s+to|uses?|hits?|calls?|reads?\s+from|writes?\s+to)\b/i.test(question);
|
|
18187
|
+
if (callersQuestion) sections.push(buildDatabaseCallersSection(graph, primary));
|
|
18079
18188
|
for (const kind of SECTION_ORDER[intent]) {
|
|
18189
|
+
if (callersQuestion && kind === "blast") continue;
|
|
18080
18190
|
const s = buildSection(kind, graph, primary, opts.incidents, now);
|
|
18081
18191
|
if (s && s.facts.length > 0) sections.push(s);
|
|
18082
18192
|
}
|
|
18083
18193
|
} else {
|
|
18084
|
-
const global = buildGlobalSections(intent, graph, opts.incidents);
|
|
18194
|
+
const global = resolution.ambiguousType ? null : buildGlobalSections(intent, graph, opts.incidents);
|
|
18085
18195
|
if (global) {
|
|
18086
18196
|
scope = "global";
|
|
18087
18197
|
for (const s of global) if (s.facts.length > 0) sections.push(s);
|
|
18088
18198
|
}
|
|
18089
18199
|
}
|
|
18090
|
-
const answer = summarize(question, intent, matched, primary, sections, scope);
|
|
18200
|
+
const answer = summarize(question, intent, matched, primary, sections, scope, resolution.ambiguousType);
|
|
18091
18201
|
const provSet = /* @__PURE__ */ new Set();
|
|
18092
18202
|
for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
|
|
18093
18203
|
const confidence = sections[0]?.facts[0]?.confidence;
|
|
@@ -19637,9 +19747,9 @@ var RESOURCE_TYPES = [
|
|
|
19637
19747
|
function isFirebaseResourceType(value) {
|
|
19638
19748
|
return RESOURCE_TYPES.includes(value);
|
|
19639
19749
|
}
|
|
19640
|
-
function buildEntriesFilter(sinceIso) {
|
|
19750
|
+
function buildEntriesFilter(sinceIso, resourceTypes = RESOURCE_TYPES) {
|
|
19641
19751
|
return [
|
|
19642
|
-
|
|
19752
|
+
`resource.type = (${resourceTypes.map((t) => `"${t}"`).join(" OR ")})`,
|
|
19643
19753
|
"httpRequest:*",
|
|
19644
19754
|
`timestamp >= "${sinceIso}"`
|
|
19645
19755
|
].join(" AND ");
|
|
@@ -19648,8 +19758,8 @@ var DEFAULT_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
|
|
|
19648
19758
|
var ENTRIES_LIST_URL = "https://logging.googleapis.com/v2/entries:list";
|
|
19649
19759
|
var PAGE_SIZE = 1e3;
|
|
19650
19760
|
var MAX_PAGES = 20;
|
|
19651
|
-
async function fetchHttpRequestLogEntries(creds, sinceIso) {
|
|
19652
|
-
const filter = buildEntriesFilter(sinceIso);
|
|
19761
|
+
async function fetchHttpRequestLogEntries(creds, sinceIso, resourceTypes) {
|
|
19762
|
+
const filter = buildEntriesFilter(sinceIso, resourceTypes);
|
|
19653
19763
|
const out = [];
|
|
19654
19764
|
let pageToken;
|
|
19655
19765
|
for (let page = 0; page < MAX_PAGES; page++) {
|
|
@@ -19762,7 +19872,37 @@ function mapLogEntriesToSignals(entries) {
|
|
|
19762
19872
|
}
|
|
19763
19873
|
|
|
19764
19874
|
// src/connectors/firebase/resolve.ts
|
|
19765
|
-
import { NodeType as
|
|
19875
|
+
import { NodeType as NodeType36, EdgeType as EdgeType29 } from "@neat.is/types";
|
|
19876
|
+
|
|
19877
|
+
// src/connectors/infer-service.ts
|
|
19878
|
+
import { NodeType as NodeType35 } from "@neat.is/types";
|
|
19879
|
+
function normalizeName(name) {
|
|
19880
|
+
return name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
19881
|
+
}
|
|
19882
|
+
function inferServiceName(graph, resourceName, method, path73) {
|
|
19883
|
+
const wantedMethod = method.toUpperCase();
|
|
19884
|
+
const wantedPath = normalizePathTemplate(path73);
|
|
19885
|
+
const services = [];
|
|
19886
|
+
const owners = /* @__PURE__ */ new Set();
|
|
19887
|
+
graph.forEachNode((_id, attrs) => {
|
|
19888
|
+
const node = attrs;
|
|
19889
|
+
if (node.type === NodeType35.ServiceNode && typeof node.name === "string") {
|
|
19890
|
+
services.push(node.name);
|
|
19891
|
+
return;
|
|
19892
|
+
}
|
|
19893
|
+
if (node.type !== NodeType35.RouteNode) return;
|
|
19894
|
+
const route = attrs;
|
|
19895
|
+
if (!route.pathTemplate || normalizePathTemplate(route.pathTemplate) !== wantedPath) return;
|
|
19896
|
+
const routeMethod = route.method.toUpperCase();
|
|
19897
|
+
if (routeMethod === "ALL" || routeMethod === wantedMethod) owners.add(route.service);
|
|
19898
|
+
});
|
|
19899
|
+
const wanted = normalizeName(resourceName);
|
|
19900
|
+
const byName = services.find((s) => normalizeName(s) === wanted);
|
|
19901
|
+
if (byName) return byName;
|
|
19902
|
+
return owners.size === 1 ? [...owners][0] : null;
|
|
19903
|
+
}
|
|
19904
|
+
|
|
19905
|
+
// src/connectors/firebase/resolve.ts
|
|
19766
19906
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
19767
19907
|
switch (resourceType) {
|
|
19768
19908
|
case "cloud_function":
|
|
@@ -19777,7 +19917,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
19777
19917
|
const entries = [];
|
|
19778
19918
|
graph.forEachNode((_id, attrs) => {
|
|
19779
19919
|
const node = attrs;
|
|
19780
|
-
if (node.type !==
|
|
19920
|
+
if (node.type !== NodeType36.RouteNode) return;
|
|
19781
19921
|
const route = attrs;
|
|
19782
19922
|
if (route.service !== serviceName) return;
|
|
19783
19923
|
entries.push({
|
|
@@ -19801,9 +19941,9 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
19801
19941
|
}
|
|
19802
19942
|
const identity = parseFirebaseTargetName(signal.targetName);
|
|
19803
19943
|
if (!identity) return null;
|
|
19804
|
-
const serviceName = neatServiceNameFor(resourceType, identity.resourceName, serviceMap);
|
|
19805
|
-
if (!serviceName) return null;
|
|
19806
19944
|
const normalizedPath = normalizePathTemplate(identity.path);
|
|
19945
|
+
const serviceName = neatServiceNameFor(resourceType, identity.resourceName, serviceMap) ?? (serviceMap.inferServices ? inferServiceName(graph, identity.resourceName, identity.method, identity.path) : null);
|
|
19946
|
+
if (!serviceName) return null;
|
|
19807
19947
|
const match = findRoute2(routeEntriesFor(graph, serviceName), identity.method, normalizedPath);
|
|
19808
19948
|
if (!match) return null;
|
|
19809
19949
|
return {
|
|
@@ -19816,23 +19956,29 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
19816
19956
|
|
|
19817
19957
|
// src/connectors/firebase/index.ts
|
|
19818
19958
|
var FirebaseConnector = class {
|
|
19959
|
+
constructor(opts = {}) {
|
|
19960
|
+
this.opts = opts;
|
|
19961
|
+
}
|
|
19962
|
+
opts;
|
|
19819
19963
|
provider = "firebase";
|
|
19820
19964
|
async poll(ctx) {
|
|
19821
19965
|
const creds = readFirebaseCredentials(ctx.credentials);
|
|
19822
19966
|
const sinceIso = ctx.since ?? new Date(Date.now() - DEFAULT_LOOKBACK_MS).toISOString();
|
|
19823
|
-
const entries = await fetchHttpRequestLogEntries(creds, sinceIso);
|
|
19967
|
+
const entries = await fetchHttpRequestLogEntries(creds, sinceIso, this.opts.resourceTypes);
|
|
19824
19968
|
return mapLogEntriesToSignals(entries);
|
|
19825
19969
|
}
|
|
19826
19970
|
};
|
|
19827
19971
|
function createFirebaseConnector(graph, serviceMap) {
|
|
19828
19972
|
return {
|
|
19829
|
-
connector: new FirebaseConnector(
|
|
19973
|
+
connector: new FirebaseConnector(
|
|
19974
|
+
serviceMap.excludeCloudRun ? { resourceTypes: ["cloud_function", "firebase_domain"] } : {}
|
|
19975
|
+
),
|
|
19830
19976
|
resolveTarget: createFirebaseResolveTarget(graph, serviceMap)
|
|
19831
19977
|
};
|
|
19832
19978
|
}
|
|
19833
19979
|
|
|
19834
19980
|
// src/connectors/cloudflare/connector.ts
|
|
19835
|
-
import { EdgeType as EdgeType30, NodeType as
|
|
19981
|
+
import { EdgeType as EdgeType30, NodeType as NodeType37, fileId as fileId5, infraId as infraId23 } from "@neat.is/types";
|
|
19836
19982
|
|
|
19837
19983
|
// src/connectors/cloudflare/client.ts
|
|
19838
19984
|
import { randomUUID } from "crypto";
|
|
@@ -19991,7 +20137,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
19991
20137
|
graph.forEachNode((id, attrs) => {
|
|
19992
20138
|
if (found) return;
|
|
19993
20139
|
const a = attrs;
|
|
19994
|
-
if (a.type ===
|
|
20140
|
+
if (a.type === NodeType37.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
19995
20141
|
found = id;
|
|
19996
20142
|
}
|
|
19997
20143
|
});
|
|
@@ -20003,7 +20149,7 @@ function findMatchingRouteNode(graph, serviceName, method, path73) {
|
|
|
20003
20149
|
graph.forEachNode((id, attrs) => {
|
|
20004
20150
|
if (found) return;
|
|
20005
20151
|
const a = attrs;
|
|
20006
|
-
if (a.type !==
|
|
20152
|
+
if (a.type !== NodeType37.RouteNode || a.service !== serviceName) return;
|
|
20007
20153
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
20008
20154
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
20009
20155
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -20402,14 +20548,14 @@ function mapLogEntriesToSignals2(entries) {
|
|
|
20402
20548
|
}
|
|
20403
20549
|
|
|
20404
20550
|
// src/connectors/cloud-run/resolve.ts
|
|
20405
|
-
import { EdgeType as EdgeType32, NodeType as
|
|
20551
|
+
import { EdgeType as EdgeType32, NodeType as NodeType38, infraId as infraId25 } from "@neat.is/types";
|
|
20406
20552
|
var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
|
|
20407
20553
|
function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
|
|
20408
20554
|
let found = null;
|
|
20409
20555
|
graph.forEachNode((_id, attrs) => {
|
|
20410
20556
|
if (found) return;
|
|
20411
20557
|
const node = attrs;
|
|
20412
|
-
if (node.type !==
|
|
20558
|
+
if (node.type !== NodeType38.RouteNode) return;
|
|
20413
20559
|
const route = attrs;
|
|
20414
20560
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20415
20561
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20425,7 +20571,7 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
20425
20571
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
20426
20572
|
if (!identity) return null;
|
|
20427
20573
|
const { serviceName: gcpServiceName, method, path: path73 } = identity;
|
|
20428
|
-
const mappedService = config.serviceMap?.[gcpServiceName];
|
|
20574
|
+
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path73) ?? void 0 : void 0);
|
|
20429
20575
|
if (mappedService) {
|
|
20430
20576
|
const routeNodeId = findMatchingRouteNode2(
|
|
20431
20577
|
graph,
|
|
@@ -20613,14 +20759,14 @@ function mapLogEntriesToSignals3(entries) {
|
|
|
20613
20759
|
}
|
|
20614
20760
|
|
|
20615
20761
|
// src/connectors/gcp-lb/resolve.ts
|
|
20616
|
-
import { EdgeType as EdgeType33, NodeType as
|
|
20762
|
+
import { EdgeType as EdgeType33, NodeType as NodeType39, infraId as infraId26 } from "@neat.is/types";
|
|
20617
20763
|
var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
|
|
20618
20764
|
function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
|
|
20619
20765
|
let found = null;
|
|
20620
20766
|
graph.forEachNode((_id, attrs) => {
|
|
20621
20767
|
if (found) return;
|
|
20622
20768
|
const node = attrs;
|
|
20623
|
-
if (node.type !==
|
|
20769
|
+
if (node.type !== NodeType39.RouteNode) return;
|
|
20624
20770
|
const route = attrs;
|
|
20625
20771
|
if (route.service !== serviceName || !route.pathTemplate) return;
|
|
20626
20772
|
if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
|
|
@@ -20636,7 +20782,7 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
20636
20782
|
const identity = parseGcpLbTargetName(signal.targetName);
|
|
20637
20783
|
if (!identity) return null;
|
|
20638
20784
|
const { backendServiceName, method, path: path73 } = identity;
|
|
20639
|
-
const mappedService = config.backendServiceMap?.[backendServiceName];
|
|
20785
|
+
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path73) ?? void 0 : void 0);
|
|
20640
20786
|
if (mappedService) {
|
|
20641
20787
|
const routeNodeId = findMatchingRouteNode3(
|
|
20642
20788
|
graph,
|
|
@@ -20691,7 +20837,7 @@ function createGcpLbConnector(graph, config = {}) {
|
|
|
20691
20837
|
}
|
|
20692
20838
|
|
|
20693
20839
|
// src/connectors/render/index.ts
|
|
20694
|
-
import { EdgeType as EdgeType34, NodeType as
|
|
20840
|
+
import { EdgeType as EdgeType34, NodeType as NodeType40 } from "@neat.is/types";
|
|
20695
20841
|
|
|
20696
20842
|
// src/connectors/render/types.ts
|
|
20697
20843
|
function readRenderToken(credentials) {
|
|
@@ -20767,7 +20913,7 @@ function buildRenderRouteIndex(graph, serviceName) {
|
|
|
20767
20913
|
const out = [];
|
|
20768
20914
|
graph.forEachNode((_id, attrs) => {
|
|
20769
20915
|
const node = attrs;
|
|
20770
|
-
if (node.type !==
|
|
20916
|
+
if (node.type !== NodeType40.RouteNode) return;
|
|
20771
20917
|
const route = attrs;
|
|
20772
20918
|
if (route.service !== serviceName) return;
|
|
20773
20919
|
out.push({
|
|
@@ -21251,7 +21397,7 @@ function mapBuildsToSignals(builds, serviceName) {
|
|
|
21251
21397
|
}
|
|
21252
21398
|
|
|
21253
21399
|
// src/connectors/eas/resolve.ts
|
|
21254
|
-
import { EdgeType as EdgeType36, NodeType as
|
|
21400
|
+
import { EdgeType as EdgeType36, NodeType as NodeType41, parseFileId as parseFileId3 } from "@neat.is/types";
|
|
21255
21401
|
var NO_ENV2 = "unknown";
|
|
21256
21402
|
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
21257
21403
|
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
@@ -21279,7 +21425,7 @@ function findConfigNode(graph, basenames, serviceName) {
|
|
|
21279
21425
|
graph.forEachNode((id, attrs) => {
|
|
21280
21426
|
if (scoped) return;
|
|
21281
21427
|
const node = attrs;
|
|
21282
|
-
if (node.type !==
|
|
21428
|
+
if (node.type !== NodeType41.ConfigNode) return;
|
|
21283
21429
|
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
21284
21430
|
if (anyMatch === null) anyMatch = id;
|
|
21285
21431
|
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
@@ -21790,6 +21936,9 @@ var PUSH_PROVIDER_DISPATCH = {
|
|
|
21790
21936
|
}
|
|
21791
21937
|
}
|
|
21792
21938
|
};
|
|
21939
|
+
function getPushProviderDispatch(provider) {
|
|
21940
|
+
return PUSH_PROVIDER_DISPATCH[provider];
|
|
21941
|
+
}
|
|
21793
21942
|
function isPushProvider(provider) {
|
|
21794
21943
|
return provider in PUSH_PROVIDER_DISPATCH;
|
|
21795
21944
|
}
|
|
@@ -23011,6 +23160,7 @@ export {
|
|
|
23011
23160
|
bearerAuthHeader,
|
|
23012
23161
|
junctionFetch,
|
|
23013
23162
|
PROVIDER_DISPATCH,
|
|
23163
|
+
getPushProviderDispatch,
|
|
23014
23164
|
isPushProvider,
|
|
23015
23165
|
getProviderFieldSchema,
|
|
23016
23166
|
knownProviderNames,
|
|
@@ -23020,4 +23170,4 @@ export {
|
|
|
23020
23170
|
deprovisionConnector,
|
|
23021
23171
|
buildApi
|
|
23022
23172
|
};
|
|
23023
|
-
//# sourceMappingURL=chunk-
|
|
23173
|
+
//# sourceMappingURL=chunk-J3VVEB45.js.map
|