@neat.is/core 0.10.6-dev.20261007 → 0.10.6-dev.20261008
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-KTJK2PAH.js → chunk-6QMWJAWR.js} +204 -609
- package/dist/chunk-6QMWJAWR.js.map +1 -0
- package/dist/{chunk-VL7ANDPI.js → chunk-EMFX3V77.js} +10 -8
- package/dist/{chunk-VL7ANDPI.js.map → chunk-EMFX3V77.js.map} +1 -1
- package/dist/chunk-MR64ICXX.js +19 -0
- package/dist/chunk-MR64ICXX.js.map +1 -0
- package/dist/{chunk-EFBWQIKG.js → chunk-QPG2QXCW.js} +11 -27
- package/dist/chunk-QPG2QXCW.js.map +1 -0
- package/dist/chunk-RGYC5U2Q.js +511 -0
- package/dist/chunk-RGYC5U2Q.js.map +1 -0
- package/dist/{chunk-LXQCL4MP.js → chunk-TZXSWSHX.js} +2 -2
- package/dist/{chunk-BC53SCT7.js → chunk-VFZ6CXLI.js} +20 -6
- package/dist/chunk-VFZ6CXLI.js.map +1 -0
- package/dist/cli.cjs +300 -74
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -76
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +58 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -99
- package/dist/index.d.ts +3 -99
- package/dist/index.js +18 -15
- package/dist/neatd.cjs +58 -15
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +7 -4
- package/dist/neatd.js.map +1 -1
- package/dist/otel-grpc-CCCI772O.js +11 -0
- package/dist/registry.cjs +570 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.d.cts +220 -0
- package/dist/registry.d.ts +220 -0
- package/dist/registry.js +62 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.cjs +78 -21
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +10 -6
- package/dist/server.js.map +1 -1
- package/package.json +8 -3
- package/dist/chunk-BC53SCT7.js.map +0 -1
- package/dist/chunk-EFBWQIKG.js.map +0 -1
- package/dist/chunk-KTJK2PAH.js.map +0 -1
- package/dist/otel-grpc-B7ABHLV7.js +0 -10
- /package/dist/{chunk-LXQCL4MP.js.map → chunk-TZXSWSHX.js.map} +0 -0
- /package/dist/{otel-grpc-B7ABHLV7.js.map → otel-grpc-CCCI772O.js.map} +0 -0
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findDaemonByProject,
|
|
3
|
+
getProject,
|
|
4
|
+
listProjects
|
|
5
|
+
} from "./chunk-RGYC5U2Q.js";
|
|
1
6
|
import {
|
|
2
7
|
BEARER_DELEGATED,
|
|
3
8
|
columnsFromSqlStatement,
|
|
4
9
|
mountBearerAuth,
|
|
5
10
|
readAuthEnv,
|
|
6
11
|
tableFromSqlStatement
|
|
7
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-QPG2QXCW.js";
|
|
8
13
|
|
|
9
14
|
// src/graph.ts
|
|
10
15
|
import GraphDefault from "graphology";
|
|
@@ -2036,8 +2041,8 @@ function chiRoutesFromSource(source, parser) {
|
|
|
2036
2041
|
chiWalk(tree.rootNode, "", out);
|
|
2037
2042
|
return out;
|
|
2038
2043
|
}
|
|
2039
|
-
function stripChiRegex(
|
|
2040
|
-
return
|
|
2044
|
+
function stripChiRegex(path76) {
|
|
2045
|
+
return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
2041
2046
|
}
|
|
2042
2047
|
function chiWalk(node, prefix, out) {
|
|
2043
2048
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -2709,9 +2714,9 @@ function rubyRocketRoute(args) {
|
|
|
2709
2714
|
if (!pair || pair.type !== "pair") continue;
|
|
2710
2715
|
const k = pair.childForFieldName("key");
|
|
2711
2716
|
if (k?.type !== "string") continue;
|
|
2712
|
-
const
|
|
2713
|
-
if (
|
|
2714
|
-
return { path:
|
|
2717
|
+
const path76 = rubyLiteral(k);
|
|
2718
|
+
if (path76 === null) continue;
|
|
2719
|
+
return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
|
|
2715
2720
|
}
|
|
2716
2721
|
return null;
|
|
2717
2722
|
}
|
|
@@ -4658,7 +4663,29 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
4658
4663
|
graph.addNode(id, node);
|
|
4659
4664
|
return id;
|
|
4660
4665
|
}
|
|
4661
|
-
|
|
4666
|
+
var EDGE_ERROR_MESSAGE_MAX = 512;
|
|
4667
|
+
function buildEdgeErrorExemplar(detail, ts) {
|
|
4668
|
+
return {
|
|
4669
|
+
...detail.exceptionType !== void 0 ? { exceptionType: detail.exceptionType } : {},
|
|
4670
|
+
...detail.message !== void 0 ? { message: detail.message.slice(0, EDGE_ERROR_MESSAGE_MAX) } : {},
|
|
4671
|
+
...detail.httpStatusCode !== void 0 ? { httpStatusCode: detail.httpStatusCode } : {},
|
|
4672
|
+
at: ts
|
|
4673
|
+
};
|
|
4674
|
+
}
|
|
4675
|
+
function edgeErrorDetailFromSpan(span) {
|
|
4676
|
+
const exceptionType = span.exception?.type;
|
|
4677
|
+
const message = span.exception?.message;
|
|
4678
|
+
const httpStatusCode = httpResponseStatus(span);
|
|
4679
|
+
if (exceptionType === void 0 && message === void 0 && httpStatusCode === void 0) {
|
|
4680
|
+
return void 0;
|
|
4681
|
+
}
|
|
4682
|
+
return {
|
|
4683
|
+
...exceptionType !== void 0 ? { exceptionType } : {},
|
|
4684
|
+
...message !== void 0 ? { message } : {},
|
|
4685
|
+
...httpStatusCode !== void 0 ? { httpStatusCode } : {}
|
|
4686
|
+
};
|
|
4687
|
+
}
|
|
4688
|
+
function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence, durationMs, errorDetail) {
|
|
4662
4689
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
4663
4690
|
const grain = source.startsWith("file:") || source.startsWith("symbol:") ? "file" : "service";
|
|
4664
4691
|
const id = makeObservedEdgeId(type, source, target);
|
|
@@ -4668,13 +4695,15 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
4668
4695
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
4669
4696
|
const latencyHist2 = durationMs !== void 0 ? recordLatency({ ...existing.signal?.latencyHist ?? {} }, durationMs) : existing.signal?.latencyHist;
|
|
4670
4697
|
const latencyMs2 = latencyPercentiles(latencyHist2) ?? existing.signal?.latencyMs;
|
|
4698
|
+
const lastError2 = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : existing.signal?.lastError;
|
|
4671
4699
|
const newSignal = {
|
|
4672
4700
|
spanCount: newSpanCount,
|
|
4673
4701
|
errorCount: newErrorCount,
|
|
4674
4702
|
lastObservedAgeMs: 0,
|
|
4675
4703
|
...latencyHist2 ? { latencyHist: latencyHist2 } : {},
|
|
4676
4704
|
...latencyMs2 ? { latencyMs: latencyMs2 } : {},
|
|
4677
|
-
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {}
|
|
4705
|
+
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {},
|
|
4706
|
+
...lastError2 ? { lastError: lastError2 } : {}
|
|
4678
4707
|
};
|
|
4679
4708
|
const updated = {
|
|
4680
4709
|
...existing,
|
|
@@ -4691,12 +4720,14 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
4691
4720
|
}
|
|
4692
4721
|
const latencyHist = durationMs !== void 0 ? recordLatency({}, durationMs) : void 0;
|
|
4693
4722
|
const latencyMs = latencyHist ? latencyPercentiles(latencyHist) : void 0;
|
|
4723
|
+
const lastError = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : void 0;
|
|
4694
4724
|
const signal = {
|
|
4695
4725
|
spanCount: 1,
|
|
4696
4726
|
errorCount: isError ? 1 : 0,
|
|
4697
4727
|
lastObservedAgeMs: 0,
|
|
4698
4728
|
...latencyHist ? { latencyHist } : {},
|
|
4699
|
-
...latencyMs ? { latencyMs } : {}
|
|
4729
|
+
...latencyMs ? { latencyMs } : {},
|
|
4730
|
+
...lastError ? { lastError } : {}
|
|
4700
4731
|
};
|
|
4701
4732
|
const edge = {
|
|
4702
4733
|
id,
|
|
@@ -5037,6 +5068,7 @@ async function handleSpan(ctx, span) {
|
|
|
5037
5068
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
5038
5069
|
const isError = spanRecordsError(span);
|
|
5039
5070
|
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
5071
|
+
const errorDetail = isError ? edgeErrorDetailFromSpan(span) : void 0;
|
|
5040
5072
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
5041
5073
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
5042
5074
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -5076,7 +5108,8 @@ async function handleSpan(ctx, span) {
|
|
|
5076
5108
|
ts,
|
|
5077
5109
|
isError,
|
|
5078
5110
|
callSiteEvidence,
|
|
5079
|
-
durationMs
|
|
5111
|
+
durationMs,
|
|
5112
|
+
errorDetail
|
|
5080
5113
|
);
|
|
5081
5114
|
if (result) affectedNode = targetId;
|
|
5082
5115
|
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
@@ -5089,7 +5122,8 @@ async function handleSpan(ctx, span) {
|
|
|
5089
5122
|
ts,
|
|
5090
5123
|
isError,
|
|
5091
5124
|
callSiteEvidence,
|
|
5092
|
-
durationMs
|
|
5125
|
+
durationMs,
|
|
5126
|
+
errorDetail
|
|
5093
5127
|
);
|
|
5094
5128
|
}
|
|
5095
5129
|
if (span.dbTable) {
|
|
@@ -5102,7 +5136,8 @@ async function handleSpan(ctx, span) {
|
|
|
5102
5136
|
ts,
|
|
5103
5137
|
isError,
|
|
5104
5138
|
callSiteEvidence,
|
|
5105
|
-
durationMs
|
|
5139
|
+
durationMs,
|
|
5140
|
+
errorDetail
|
|
5106
5141
|
);
|
|
5107
5142
|
mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
|
|
5108
5143
|
}
|
|
@@ -5122,7 +5157,8 @@ async function handleSpan(ctx, span) {
|
|
|
5122
5157
|
ts,
|
|
5123
5158
|
isError,
|
|
5124
5159
|
callSiteEvidence,
|
|
5125
|
-
durationMs
|
|
5160
|
+
durationMs,
|
|
5161
|
+
errorDetail
|
|
5126
5162
|
);
|
|
5127
5163
|
if (result) affectedNode = targetId;
|
|
5128
5164
|
} else if (span.graphqlOperationName && span.graphqlOperationType && spanServesGraphqlOperation(span.kind)) {
|
|
@@ -5140,7 +5176,8 @@ async function handleSpan(ctx, span) {
|
|
|
5140
5176
|
ts,
|
|
5141
5177
|
isError,
|
|
5142
5178
|
callSiteEvidence,
|
|
5143
|
-
durationMs
|
|
5179
|
+
durationMs,
|
|
5180
|
+
errorDetail
|
|
5144
5181
|
);
|
|
5145
5182
|
if (result) affectedNode = targetId;
|
|
5146
5183
|
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
@@ -5153,7 +5190,8 @@ async function handleSpan(ctx, span) {
|
|
|
5153
5190
|
ts,
|
|
5154
5191
|
isError,
|
|
5155
5192
|
callSiteEvidence,
|
|
5156
|
-
durationMs
|
|
5193
|
+
durationMs,
|
|
5194
|
+
errorDetail
|
|
5157
5195
|
);
|
|
5158
5196
|
if (result) affectedNode = targetId;
|
|
5159
5197
|
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
@@ -5170,7 +5208,8 @@ async function handleSpan(ctx, span) {
|
|
|
5170
5208
|
ts,
|
|
5171
5209
|
isError,
|
|
5172
5210
|
callSiteEvidence,
|
|
5173
|
-
durationMs
|
|
5211
|
+
durationMs,
|
|
5212
|
+
errorDetail
|
|
5174
5213
|
);
|
|
5175
5214
|
if (result) affectedNode = targetId;
|
|
5176
5215
|
} else {
|
|
@@ -5187,7 +5226,8 @@ async function handleSpan(ctx, span) {
|
|
|
5187
5226
|
ts,
|
|
5188
5227
|
isError,
|
|
5189
5228
|
callSiteEvidence,
|
|
5190
|
-
durationMs
|
|
5229
|
+
durationMs,
|
|
5230
|
+
errorDetail
|
|
5191
5231
|
);
|
|
5192
5232
|
affectedNode = targetId;
|
|
5193
5233
|
resolvedViaAddress = true;
|
|
@@ -5201,7 +5241,8 @@ async function handleSpan(ctx, span) {
|
|
|
5201
5241
|
ts,
|
|
5202
5242
|
isError,
|
|
5203
5243
|
callSiteEvidence,
|
|
5204
|
-
durationMs
|
|
5244
|
+
durationMs,
|
|
5245
|
+
errorDetail
|
|
5205
5246
|
);
|
|
5206
5247
|
affectedNode = frontierNodeId;
|
|
5207
5248
|
resolvedViaAddress = true;
|
|
@@ -5228,7 +5269,8 @@ async function handleSpan(ctx, span) {
|
|
|
5228
5269
|
ts,
|
|
5229
5270
|
isError,
|
|
5230
5271
|
fallbackEvidence,
|
|
5231
|
-
durationMs
|
|
5272
|
+
durationMs,
|
|
5273
|
+
errorDetail
|
|
5232
5274
|
);
|
|
5233
5275
|
}
|
|
5234
5276
|
}
|
|
@@ -5243,7 +5285,7 @@ async function handleSpan(ctx, span) {
|
|
|
5243
5285
|
);
|
|
5244
5286
|
if (routeNodeId) {
|
|
5245
5287
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
5246
|
-
upsertObservedEdge(ctx.graph, EdgeType5.CONTAINS, serviceId(routeSvc), routeNodeId, ts, isError, void 0, durationMs);
|
|
5288
|
+
upsertObservedEdge(ctx.graph, EdgeType5.CONTAINS, serviceId(routeSvc), routeNodeId, ts, isError, void 0, durationMs, errorDetail);
|
|
5247
5289
|
}
|
|
5248
5290
|
}
|
|
5249
5291
|
if (span.statusCode === 2) {
|
|
@@ -5726,19 +5768,19 @@ function confidenceFromMix(edges, now = Date.now()) {
|
|
|
5726
5768
|
function longestIncomingWalk(graph, start, maxDepth) {
|
|
5727
5769
|
let best = { path: [start], edges: [] };
|
|
5728
5770
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
5729
|
-
function step(node,
|
|
5730
|
-
if (
|
|
5731
|
-
best = { path: [...
|
|
5771
|
+
function step(node, path76, edges) {
|
|
5772
|
+
if (path76.length > best.path.length) {
|
|
5773
|
+
best = { path: [...path76], edges: [...edges] };
|
|
5732
5774
|
}
|
|
5733
|
-
if (
|
|
5775
|
+
if (path76.length - 1 >= maxDepth) return;
|
|
5734
5776
|
const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
|
|
5735
5777
|
for (const [srcId, edge] of incoming) {
|
|
5736
5778
|
if (visited.has(srcId)) continue;
|
|
5737
5779
|
visited.add(srcId);
|
|
5738
|
-
|
|
5780
|
+
path76.push(srcId);
|
|
5739
5781
|
edges.push(edge);
|
|
5740
|
-
step(srcId,
|
|
5741
|
-
|
|
5782
|
+
step(srcId, path76, edges);
|
|
5783
|
+
path76.pop();
|
|
5742
5784
|
edges.pop();
|
|
5743
5785
|
visited.delete(srcId);
|
|
5744
5786
|
}
|
|
@@ -5953,20 +5995,20 @@ function dominantFailingCall(graph, serviceId17, visited) {
|
|
|
5953
5995
|
return best;
|
|
5954
5996
|
}
|
|
5955
5997
|
function followFailingCallChain(graph, originServiceId, maxDepth) {
|
|
5956
|
-
const
|
|
5998
|
+
const path76 = [originServiceId];
|
|
5957
5999
|
const edges = [];
|
|
5958
6000
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
5959
6001
|
let current = originServiceId;
|
|
5960
6002
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
5961
6003
|
const hop = dominantFailingCall(graph, current, visited);
|
|
5962
6004
|
if (!hop) break;
|
|
5963
|
-
|
|
6005
|
+
path76.push(hop.nextService);
|
|
5964
6006
|
edges.push(hop.edge);
|
|
5965
6007
|
visited.add(hop.nextService);
|
|
5966
6008
|
current = hop.nextService;
|
|
5967
6009
|
}
|
|
5968
6010
|
if (edges.length === 0) return null;
|
|
5969
|
-
return { path:
|
|
6011
|
+
return { path: path76, edges, culprit: current };
|
|
5970
6012
|
}
|
|
5971
6013
|
function isStaleCallEdge(e) {
|
|
5972
6014
|
return e.type === EdgeType6.CALLS && e.provenance === Provenance6.STALE;
|
|
@@ -6002,26 +6044,26 @@ function dominantStaleCall(graph, serviceId17, visited) {
|
|
|
6002
6044
|
return best;
|
|
6003
6045
|
}
|
|
6004
6046
|
function followStaleCallChain(graph, originServiceId, maxDepth) {
|
|
6005
|
-
const
|
|
6047
|
+
const path76 = [originServiceId];
|
|
6006
6048
|
const edges = [];
|
|
6007
6049
|
const visited = /* @__PURE__ */ new Set([originServiceId]);
|
|
6008
6050
|
let current = originServiceId;
|
|
6009
6051
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
6010
6052
|
const hop = dominantStaleCall(graph, current, visited);
|
|
6011
6053
|
if (!hop) break;
|
|
6012
|
-
|
|
6054
|
+
path76.push(hop.nextService);
|
|
6013
6055
|
edges.push(hop.edge);
|
|
6014
6056
|
visited.add(hop.nextService);
|
|
6015
6057
|
current = hop.nextService;
|
|
6016
6058
|
}
|
|
6017
6059
|
if (edges.length === 0) return null;
|
|
6018
|
-
return { path:
|
|
6060
|
+
return { path: path76, edges, culprit: current };
|
|
6019
6061
|
}
|
|
6020
6062
|
function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
6021
6063
|
const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
|
|
6022
6064
|
if (!chain) return null;
|
|
6023
6065
|
const culprit = chain.culprit;
|
|
6024
|
-
const
|
|
6066
|
+
const path76 = [...chain.path];
|
|
6025
6067
|
const edgeProvenances = chain.edges.map((e) => e.provenance);
|
|
6026
6068
|
const baseConfidence = confidenceFromMix(chain.edges);
|
|
6027
6069
|
const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
|
|
@@ -6029,14 +6071,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
6029
6071
|
if (loc) {
|
|
6030
6072
|
let rootCauseNode = culprit;
|
|
6031
6073
|
if (loc.fileNode) {
|
|
6032
|
-
|
|
6074
|
+
path76.push(loc.fileNode);
|
|
6033
6075
|
edgeProvenances.push(Provenance6.OBSERVED);
|
|
6034
6076
|
rootCauseNode = loc.fileNode;
|
|
6035
6077
|
}
|
|
6036
6078
|
return RootCauseResultSchema.parse({
|
|
6037
6079
|
rootCauseNode,
|
|
6038
6080
|
rootCauseReason: loc.rootCauseReason,
|
|
6039
|
-
traversalPath:
|
|
6081
|
+
traversalPath: path76,
|
|
6040
6082
|
edgeProvenances,
|
|
6041
6083
|
confidence,
|
|
6042
6084
|
...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
|
|
@@ -6048,7 +6090,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
|
|
|
6048
6090
|
return RootCauseResultSchema.parse({
|
|
6049
6091
|
rootCauseNode: culprit,
|
|
6050
6092
|
rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
|
|
6051
|
-
traversalPath:
|
|
6093
|
+
traversalPath: path76,
|
|
6052
6094
|
edgeProvenances,
|
|
6053
6095
|
confidence,
|
|
6054
6096
|
fixRecommendation: `Inspect ${culpritName}'s failing handler`
|
|
@@ -6701,10 +6743,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
|
|
|
6701
6743
|
traversalPath = staleChain.path;
|
|
6702
6744
|
edgeProvenances = staleChain.edges.map((e) => e.provenance);
|
|
6703
6745
|
} else if (top.node !== seedNode) {
|
|
6704
|
-
const
|
|
6705
|
-
if (
|
|
6706
|
-
traversalPath =
|
|
6707
|
-
edgeProvenances =
|
|
6746
|
+
const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
|
|
6747
|
+
if (path76) {
|
|
6748
|
+
traversalPath = path76.nodes;
|
|
6749
|
+
edgeProvenances = path76.edges.map((e) => e.provenance);
|
|
6708
6750
|
} else {
|
|
6709
6751
|
traversalPath = [errorNodeId, top.node];
|
|
6710
6752
|
edgeProvenances = [top.provenance ?? Provenance6.OBSERVED];
|
|
@@ -16320,6 +16362,11 @@ function detectObservedFailingEdge(graph, bucket) {
|
|
|
16320
16362
|
spanCount,
|
|
16321
16363
|
errorCount,
|
|
16322
16364
|
errorRate: clampConfidence(errorRate),
|
|
16365
|
+
// The bounded last-error exemplar rides the finding as evidence (ADR-236):
|
|
16366
|
+
// the error rate says HOW MUCH is failing, the exemplar says WHAT failed —
|
|
16367
|
+
// a timeout vs a deadlock vs a connection-refused, which `errorCount`
|
|
16368
|
+
// alone cannot name. Present only when the observed edge carries one.
|
|
16369
|
+
...signal.lastError ? { lastError: signal.lastError } : {},
|
|
16323
16370
|
confidence: OBSERVED_FAILING_CONFIDENCE,
|
|
16324
16371
|
reason: `Code declares ${bucket.source} \u2192 ${bucket.target} (${bucket.type}) and production observes it, but ${errorCount}/${spanCount} observed calls fail (${pct}% error rate) \u2014 the declared dependency is predominantly failing. The declared intent (EXTRACTED) and the observed behaviour (OBSERVED) diverge.`,
|
|
16325
16372
|
recommendation: "Treat this as a broken declared dependency, not a coverage gap: production runs the call the code declares and most calls error. Check the dependency\u2019s health, recent deploys, and the observed error responses on this edge."
|
|
@@ -16911,440 +16958,6 @@ function parseExtraProjects(raw) {
|
|
|
16911
16958
|
return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0 && p !== DEFAULT_PROJECT);
|
|
16912
16959
|
}
|
|
16913
16960
|
|
|
16914
|
-
// src/registry.ts
|
|
16915
|
-
import { promises as fs36 } from "fs";
|
|
16916
|
-
import os2 from "os";
|
|
16917
|
-
import path71 from "path";
|
|
16918
|
-
import {
|
|
16919
|
-
RegistryFileSchema
|
|
16920
|
-
} from "@neat.is/types";
|
|
16921
|
-
var LOCK_TIMEOUT_MS = 5e3;
|
|
16922
|
-
var LOCK_RETRY_MS = 50;
|
|
16923
|
-
function neatHome() {
|
|
16924
|
-
const override = process.env.NEAT_HOME;
|
|
16925
|
-
if (override && override.length > 0) return path71.resolve(override);
|
|
16926
|
-
return path71.join(os2.homedir(), ".neat");
|
|
16927
|
-
}
|
|
16928
|
-
function registryPath() {
|
|
16929
|
-
return path71.join(neatHome(), "projects.json");
|
|
16930
|
-
}
|
|
16931
|
-
function registryLockPath() {
|
|
16932
|
-
return path71.join(neatHome(), "projects.json.lock");
|
|
16933
|
-
}
|
|
16934
|
-
function daemonPidPath() {
|
|
16935
|
-
return path71.join(neatHome(), "neatd.pid");
|
|
16936
|
-
}
|
|
16937
|
-
function daemonsDir() {
|
|
16938
|
-
return path71.join(neatHome(), "daemons");
|
|
16939
|
-
}
|
|
16940
|
-
function isFiniteInt(v) {
|
|
16941
|
-
return typeof v === "number" && Number.isFinite(v);
|
|
16942
|
-
}
|
|
16943
|
-
function parseDaemonRecord(raw) {
|
|
16944
|
-
let obj;
|
|
16945
|
-
try {
|
|
16946
|
-
obj = JSON.parse(raw);
|
|
16947
|
-
} catch {
|
|
16948
|
-
return void 0;
|
|
16949
|
-
}
|
|
16950
|
-
if (typeof obj !== "object" || obj === null) return void 0;
|
|
16951
|
-
const r = obj;
|
|
16952
|
-
const ports = r.ports;
|
|
16953
|
-
if (typeof r.project !== "string" || typeof r.projectPath !== "string" || !isFiniteInt(r.pid) || r.status !== "running" && r.status !== "stopped" || typeof r.startedAt !== "string" || typeof r.neatVersion !== "string" || !ports || !isFiniteInt(ports.rest) || !isFiniteInt(ports.otlp) || !isFiniteInt(ports.web)) {
|
|
16954
|
-
return void 0;
|
|
16955
|
-
}
|
|
16956
|
-
return {
|
|
16957
|
-
project: r.project,
|
|
16958
|
-
projectPath: r.projectPath,
|
|
16959
|
-
pid: r.pid,
|
|
16960
|
-
status: r.status,
|
|
16961
|
-
ports: { rest: ports.rest, otlp: ports.otlp, web: ports.web },
|
|
16962
|
-
startedAt: r.startedAt,
|
|
16963
|
-
neatVersion: r.neatVersion
|
|
16964
|
-
};
|
|
16965
|
-
}
|
|
16966
|
-
var defaultDiscoveryProbe = { isPidAlive: isPidAliveDefault };
|
|
16967
|
-
async function discoverDaemons(probe = defaultDiscoveryProbe) {
|
|
16968
|
-
const dir = daemonsDir();
|
|
16969
|
-
let names;
|
|
16970
|
-
try {
|
|
16971
|
-
names = await fs36.readdir(dir);
|
|
16972
|
-
} catch (err) {
|
|
16973
|
-
if (err.code === "ENOENT") return [];
|
|
16974
|
-
throw err;
|
|
16975
|
-
}
|
|
16976
|
-
const out = [];
|
|
16977
|
-
for (const name of names) {
|
|
16978
|
-
if (!name.endsWith(".json")) continue;
|
|
16979
|
-
const file = path71.join(dir, name);
|
|
16980
|
-
let raw;
|
|
16981
|
-
try {
|
|
16982
|
-
raw = await fs36.readFile(file, "utf8");
|
|
16983
|
-
} catch {
|
|
16984
|
-
continue;
|
|
16985
|
-
}
|
|
16986
|
-
const record = parseDaemonRecord(raw);
|
|
16987
|
-
if (!record) continue;
|
|
16988
|
-
const live = record.status === "running" && probe.isPidAlive(record.pid);
|
|
16989
|
-
out.push({ record, live, source: file });
|
|
16990
|
-
}
|
|
16991
|
-
out.sort((a, b) => a.record.project.localeCompare(b.record.project));
|
|
16992
|
-
return out;
|
|
16993
|
-
}
|
|
16994
|
-
async function removeDaemonRecord(source) {
|
|
16995
|
-
await fs36.unlink(source).catch(() => {
|
|
16996
|
-
});
|
|
16997
|
-
}
|
|
16998
|
-
async function listMachineProjects(probe = defaultDiscoveryProbe) {
|
|
16999
|
-
const discovered = await discoverDaemons(probe);
|
|
17000
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
17001
|
-
for (const d of discovered) {
|
|
17002
|
-
const key = await normalizeProjectPath(d.record.projectPath);
|
|
17003
|
-
byPath.set(key, {
|
|
17004
|
-
project: d.record.project,
|
|
17005
|
-
projectPath: d.record.projectPath,
|
|
17006
|
-
state: d.live ? "running" : "stopped",
|
|
17007
|
-
ports: d.record.ports,
|
|
17008
|
-
pid: d.record.pid
|
|
17009
|
-
});
|
|
17010
|
-
}
|
|
17011
|
-
let legacy = [];
|
|
17012
|
-
try {
|
|
17013
|
-
legacy = (await readRegistry()).projects;
|
|
17014
|
-
} catch {
|
|
17015
|
-
legacy = [];
|
|
17016
|
-
}
|
|
17017
|
-
for (const entry of legacy) {
|
|
17018
|
-
const key = await normalizeProjectPath(entry.path);
|
|
17019
|
-
if (byPath.has(key)) continue;
|
|
17020
|
-
byPath.set(key, {
|
|
17021
|
-
project: entry.name,
|
|
17022
|
-
projectPath: entry.path,
|
|
17023
|
-
state: "registered",
|
|
17024
|
-
registryStatus: entry.status
|
|
17025
|
-
});
|
|
17026
|
-
}
|
|
17027
|
-
return [...byPath.values()].sort((a, b) => a.project.localeCompare(b.project));
|
|
17028
|
-
}
|
|
17029
|
-
async function findDaemonByProject(name, probe = defaultDiscoveryProbe) {
|
|
17030
|
-
const discovered = await discoverDaemons(probe);
|
|
17031
|
-
return discovered.find((d) => d.record.project === name);
|
|
17032
|
-
}
|
|
17033
|
-
function signalDaemonStop(pid) {
|
|
17034
|
-
try {
|
|
17035
|
-
process.kill(pid, "SIGTERM");
|
|
17036
|
-
return true;
|
|
17037
|
-
} catch (err) {
|
|
17038
|
-
if (err.code === "ESRCH") return false;
|
|
17039
|
-
throw err;
|
|
17040
|
-
}
|
|
17041
|
-
}
|
|
17042
|
-
function isPidAliveDefault(pid) {
|
|
17043
|
-
try {
|
|
17044
|
-
process.kill(pid, 0);
|
|
17045
|
-
return true;
|
|
17046
|
-
} catch (err) {
|
|
17047
|
-
return err.code === "EPERM";
|
|
17048
|
-
}
|
|
17049
|
-
}
|
|
17050
|
-
async function readPidFile(file) {
|
|
17051
|
-
try {
|
|
17052
|
-
const raw = await fs36.readFile(file, "utf8");
|
|
17053
|
-
const pid = Number.parseInt(raw.trim(), 10);
|
|
17054
|
-
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
17055
|
-
} catch {
|
|
17056
|
-
return void 0;
|
|
17057
|
-
}
|
|
17058
|
-
}
|
|
17059
|
-
var defaultLockHolderProbe = {
|
|
17060
|
-
isPidAlive: isPidAliveDefault,
|
|
17061
|
-
daemonPidFromFile: () => readPidFile(daemonPidPath()),
|
|
17062
|
-
async daemonResponds() {
|
|
17063
|
-
const base = process.env.NEAT_API_URL ?? "http://localhost:8080";
|
|
17064
|
-
try {
|
|
17065
|
-
await fetch(`${base}/health`, { signal: AbortSignal.timeout(750) });
|
|
17066
|
-
return true;
|
|
17067
|
-
} catch {
|
|
17068
|
-
return false;
|
|
17069
|
-
}
|
|
17070
|
-
}
|
|
17071
|
-
};
|
|
17072
|
-
async function readLockPid(lockPath) {
|
|
17073
|
-
return readPidFile(lockPath);
|
|
17074
|
-
}
|
|
17075
|
-
async function classifyLockHolder(lockPath, probe = defaultLockHolderProbe) {
|
|
17076
|
-
const lockPid = await readLockPid(lockPath);
|
|
17077
|
-
const daemonPid = await probe.daemonPidFromFile();
|
|
17078
|
-
if (lockPid !== void 0 && lockPid !== process.pid && probe.isPidAlive(lockPid)) {
|
|
17079
|
-
return { kind: lockPid === daemonPid ? "daemon" : "command", pid: lockPid };
|
|
17080
|
-
}
|
|
17081
|
-
if (daemonPid !== void 0 && daemonPid !== process.pid && probe.isPidAlive(daemonPid) && // Guards against a stale pidfile whose PID got reused.
|
|
17082
|
-
await probe.daemonResponds()) {
|
|
17083
|
-
return { kind: "daemon", pid: daemonPid };
|
|
17084
|
-
}
|
|
17085
|
-
return { kind: "stale" };
|
|
17086
|
-
}
|
|
17087
|
-
function lockHolderMessage(holder, lockPath, timeoutMs) {
|
|
17088
|
-
switch (holder.kind) {
|
|
17089
|
-
case "daemon":
|
|
17090
|
-
return `The neat daemon (pid ${holder.pid}) is holding the registry lock. Register this project through the daemon, or stop neatd and re-run \`neat init\`.`;
|
|
17091
|
-
case "command":
|
|
17092
|
-
return `Another neat command (pid ${holder.pid}) is holding the registry lock. Wait for it to finish, or check \`ps\` if you're not sure what's running.`;
|
|
17093
|
-
case "stale":
|
|
17094
|
-
return `timed out after ${timeoutMs}ms waiting for ${lockPath}. Another neat process is holding the lock; if no such process exists, remove the file by hand.`;
|
|
17095
|
-
}
|
|
17096
|
-
}
|
|
17097
|
-
async function normalizeProjectPath(input) {
|
|
17098
|
-
const resolved = path71.resolve(input);
|
|
17099
|
-
try {
|
|
17100
|
-
return await fs36.realpath(resolved);
|
|
17101
|
-
} catch {
|
|
17102
|
-
return resolved;
|
|
17103
|
-
}
|
|
17104
|
-
}
|
|
17105
|
-
async function writeAtomically(target, contents) {
|
|
17106
|
-
await fs36.mkdir(path71.dirname(target), { recursive: true });
|
|
17107
|
-
const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
17108
|
-
const fd = await fs36.open(tmp, "w");
|
|
17109
|
-
try {
|
|
17110
|
-
await fd.writeFile(contents, "utf8");
|
|
17111
|
-
await fd.sync();
|
|
17112
|
-
} finally {
|
|
17113
|
-
await fd.close();
|
|
17114
|
-
}
|
|
17115
|
-
await fs36.rename(tmp, target);
|
|
17116
|
-
}
|
|
17117
|
-
async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
|
|
17118
|
-
const deadline = Date.now() + timeoutMs;
|
|
17119
|
-
await fs36.mkdir(path71.dirname(lockPath), { recursive: true });
|
|
17120
|
-
let probedHolder = false;
|
|
17121
|
-
while (true) {
|
|
17122
|
-
try {
|
|
17123
|
-
const fd = await fs36.open(lockPath, "wx");
|
|
17124
|
-
try {
|
|
17125
|
-
await fd.writeFile(`${process.pid}
|
|
17126
|
-
`, "utf8");
|
|
17127
|
-
} finally {
|
|
17128
|
-
await fd.close();
|
|
17129
|
-
}
|
|
17130
|
-
return;
|
|
17131
|
-
} catch (err) {
|
|
17132
|
-
const code = err.code;
|
|
17133
|
-
if (code !== "EEXIST") throw err;
|
|
17134
|
-
if (!probedHolder) {
|
|
17135
|
-
probedHolder = true;
|
|
17136
|
-
const holder = await classifyLockHolder(lockPath, probe);
|
|
17137
|
-
if (holder.kind === "daemon") {
|
|
17138
|
-
throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
|
|
17139
|
-
}
|
|
17140
|
-
if (holder.kind === "stale" && await isReclaimableLock(lockPath)) {
|
|
17141
|
-
await fs36.unlink(lockPath).catch(() => {
|
|
17142
|
-
});
|
|
17143
|
-
continue;
|
|
17144
|
-
}
|
|
17145
|
-
}
|
|
17146
|
-
if (Date.now() >= deadline) {
|
|
17147
|
-
const holder = await classifyLockHolder(lockPath, probe);
|
|
17148
|
-
throw new RegistryLockError(lockHolderMessage(holder, lockPath, timeoutMs));
|
|
17149
|
-
}
|
|
17150
|
-
await new Promise((r) => setTimeout(r, LOCK_RETRY_MS));
|
|
17151
|
-
}
|
|
17152
|
-
}
|
|
17153
|
-
}
|
|
17154
|
-
var LOCK_UNSTAMPED_GRACE_MS = 5e3;
|
|
17155
|
-
async function isReclaimableLock(lockPath) {
|
|
17156
|
-
const pid = await readLockPid(lockPath);
|
|
17157
|
-
if (pid !== void 0) return true;
|
|
17158
|
-
try {
|
|
17159
|
-
const stat = await fs36.stat(lockPath);
|
|
17160
|
-
return Date.now() - stat.mtimeMs > LOCK_UNSTAMPED_GRACE_MS;
|
|
17161
|
-
} catch {
|
|
17162
|
-
return false;
|
|
17163
|
-
}
|
|
17164
|
-
}
|
|
17165
|
-
async function releaseLock(lockPath) {
|
|
17166
|
-
await fs36.unlink(lockPath).catch(() => {
|
|
17167
|
-
});
|
|
17168
|
-
}
|
|
17169
|
-
async function withLock(fn) {
|
|
17170
|
-
const lock = registryLockPath();
|
|
17171
|
-
await acquireLock(lock);
|
|
17172
|
-
try {
|
|
17173
|
-
return await fn();
|
|
17174
|
-
} finally {
|
|
17175
|
-
await releaseLock(lock);
|
|
17176
|
-
}
|
|
17177
|
-
}
|
|
17178
|
-
async function readRegistry() {
|
|
17179
|
-
const file = registryPath();
|
|
17180
|
-
let raw;
|
|
17181
|
-
try {
|
|
17182
|
-
raw = await fs36.readFile(file, "utf8");
|
|
17183
|
-
} catch (err) {
|
|
17184
|
-
if (err.code === "ENOENT") {
|
|
17185
|
-
return { version: 1, projects: [] };
|
|
17186
|
-
}
|
|
17187
|
-
throw err;
|
|
17188
|
-
}
|
|
17189
|
-
const parsed = JSON.parse(raw);
|
|
17190
|
-
return RegistryFileSchema.parse(parsed);
|
|
17191
|
-
}
|
|
17192
|
-
async function writeRegistry(reg) {
|
|
17193
|
-
const validated = RegistryFileSchema.parse(reg);
|
|
17194
|
-
await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
|
|
17195
|
-
}
|
|
17196
|
-
var RegistryError = class extends Error {
|
|
17197
|
-
constructor(message) {
|
|
17198
|
-
super(message);
|
|
17199
|
-
this.name = "RegistryError";
|
|
17200
|
-
}
|
|
17201
|
-
};
|
|
17202
|
-
var RegistryLockError = class extends RegistryError {
|
|
17203
|
-
constructor(message) {
|
|
17204
|
-
super(message);
|
|
17205
|
-
this.name = "RegistryLockError";
|
|
17206
|
-
}
|
|
17207
|
-
};
|
|
17208
|
-
var ProjectNameCollisionError = class extends RegistryError {
|
|
17209
|
-
projectName;
|
|
17210
|
-
constructor(name) {
|
|
17211
|
-
super(`a project named "${name}" is already registered`);
|
|
17212
|
-
this.name = "ProjectNameCollisionError";
|
|
17213
|
-
this.projectName = name;
|
|
17214
|
-
}
|
|
17215
|
-
};
|
|
17216
|
-
async function addProject(opts) {
|
|
17217
|
-
const resolvedPath = await normalizeProjectPath(opts.path);
|
|
17218
|
-
return withLock(async () => {
|
|
17219
|
-
const reg = await readRegistry();
|
|
17220
|
-
const byName = reg.projects.find((p) => p.name === opts.name);
|
|
17221
|
-
const byPath = reg.projects.find((p) => p.path === resolvedPath);
|
|
17222
|
-
if (byName && byName.path !== resolvedPath) {
|
|
17223
|
-
throw new ProjectNameCollisionError(opts.name);
|
|
17224
|
-
}
|
|
17225
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17226
|
-
if (byName && byName.path === resolvedPath) {
|
|
17227
|
-
byName.lastSeenAt = now;
|
|
17228
|
-
if (opts.languages) byName.languages = opts.languages;
|
|
17229
|
-
if (opts.status) byName.status = opts.status;
|
|
17230
|
-
await writeRegistry(reg);
|
|
17231
|
-
return byName;
|
|
17232
|
-
}
|
|
17233
|
-
if (byPath && byPath.name !== opts.name) {
|
|
17234
|
-
throw new ProjectNameCollisionError(byPath.name);
|
|
17235
|
-
}
|
|
17236
|
-
const entry = {
|
|
17237
|
-
name: opts.name,
|
|
17238
|
-
path: resolvedPath,
|
|
17239
|
-
registeredAt: now,
|
|
17240
|
-
languages: opts.languages ?? [],
|
|
17241
|
-
status: opts.status ?? "active"
|
|
17242
|
-
};
|
|
17243
|
-
reg.projects.push(entry);
|
|
17244
|
-
await writeRegistry(reg);
|
|
17245
|
-
return entry;
|
|
17246
|
-
});
|
|
17247
|
-
}
|
|
17248
|
-
async function getProject(name) {
|
|
17249
|
-
const reg = await readRegistry();
|
|
17250
|
-
return reg.projects.find((p) => p.name === name);
|
|
17251
|
-
}
|
|
17252
|
-
async function findProjectByPath(dir) {
|
|
17253
|
-
const here = await normalizeProjectPath(dir);
|
|
17254
|
-
const { projects } = await readRegistry();
|
|
17255
|
-
let best;
|
|
17256
|
-
for (const entry of projects) {
|
|
17257
|
-
const inside = here === entry.path || here.startsWith(entry.path + path71.sep);
|
|
17258
|
-
if (inside && (!best || entry.path.length > best.path.length)) best = entry;
|
|
17259
|
-
}
|
|
17260
|
-
return best;
|
|
17261
|
-
}
|
|
17262
|
-
async function listProjects() {
|
|
17263
|
-
const reg = await readRegistry();
|
|
17264
|
-
return reg.projects;
|
|
17265
|
-
}
|
|
17266
|
-
async function setStatus(name, status) {
|
|
17267
|
-
return withLock(async () => {
|
|
17268
|
-
const reg = await readRegistry();
|
|
17269
|
-
const entry = reg.projects.find((p) => p.name === name);
|
|
17270
|
-
if (!entry) throw new RegistryError(`no project named "${name}"`);
|
|
17271
|
-
entry.status = status;
|
|
17272
|
-
await writeRegistry(reg);
|
|
17273
|
-
return entry;
|
|
17274
|
-
});
|
|
17275
|
-
}
|
|
17276
|
-
async function touchLastSeen(name, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
17277
|
-
await withLock(async () => {
|
|
17278
|
-
const reg = await readRegistry();
|
|
17279
|
-
const entry = reg.projects.find((p) => p.name === name);
|
|
17280
|
-
if (!entry) return;
|
|
17281
|
-
entry.lastSeenAt = at;
|
|
17282
|
-
await writeRegistry(reg);
|
|
17283
|
-
});
|
|
17284
|
-
}
|
|
17285
|
-
async function removeProject(name) {
|
|
17286
|
-
return withLock(async () => {
|
|
17287
|
-
const reg = await readRegistry();
|
|
17288
|
-
const idx = reg.projects.findIndex((p) => p.name === name);
|
|
17289
|
-
if (idx < 0) return void 0;
|
|
17290
|
-
const [removed] = reg.projects.splice(idx, 1);
|
|
17291
|
-
await writeRegistry(reg);
|
|
17292
|
-
return removed;
|
|
17293
|
-
});
|
|
17294
|
-
}
|
|
17295
|
-
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
17296
|
-
var DEFAULT_PRUNE_TTL_MS = 7 * DAY_MS2;
|
|
17297
|
-
function pruneTtlMs() {
|
|
17298
|
-
const raw = process.env.NEAT_REGISTRY_PRUNE_TTL_MS;
|
|
17299
|
-
if (!raw) return DEFAULT_PRUNE_TTL_MS;
|
|
17300
|
-
const n = Number.parseInt(raw, 10);
|
|
17301
|
-
if (Number.isFinite(n) && n >= 0) return n;
|
|
17302
|
-
console.warn(
|
|
17303
|
-
`[neat] NEAT_REGISTRY_PRUNE_TTL_MS could not be parsed (${raw}); using default ${DEFAULT_PRUNE_TTL_MS}ms`
|
|
17304
|
-
);
|
|
17305
|
-
return DEFAULT_PRUNE_TTL_MS;
|
|
17306
|
-
}
|
|
17307
|
-
async function statPathStatus(p) {
|
|
17308
|
-
try {
|
|
17309
|
-
const stat = await fs36.stat(p);
|
|
17310
|
-
return stat.isDirectory() ? "present" : "unknown";
|
|
17311
|
-
} catch (err) {
|
|
17312
|
-
return err.code === "ENOENT" ? "gone" : "unknown";
|
|
17313
|
-
}
|
|
17314
|
-
}
|
|
17315
|
-
async function pruneRegistry(opts = {}) {
|
|
17316
|
-
const ttlMs = opts.ttlMs ?? pruneTtlMs();
|
|
17317
|
-
const statPath = opts.statPath ?? statPathStatus;
|
|
17318
|
-
const now = opts.now ?? Date.now;
|
|
17319
|
-
return withLock(async () => {
|
|
17320
|
-
const reg = await readRegistry();
|
|
17321
|
-
const removed = [];
|
|
17322
|
-
const kept = [];
|
|
17323
|
-
for (const entry of reg.projects) {
|
|
17324
|
-
const status = await statPath(entry.path);
|
|
17325
|
-
if (status !== "gone") {
|
|
17326
|
-
kept.push(entry);
|
|
17327
|
-
continue;
|
|
17328
|
-
}
|
|
17329
|
-
if (ttlMs <= 0) {
|
|
17330
|
-
removed.push(entry);
|
|
17331
|
-
continue;
|
|
17332
|
-
}
|
|
17333
|
-
const lastSeen = Date.parse(entry.lastSeenAt ?? entry.registeredAt);
|
|
17334
|
-
const age = now() - (Number.isFinite(lastSeen) ? lastSeen : 0);
|
|
17335
|
-
if (age > ttlMs) {
|
|
17336
|
-
removed.push(entry);
|
|
17337
|
-
} else {
|
|
17338
|
-
kept.push(entry);
|
|
17339
|
-
}
|
|
17340
|
-
}
|
|
17341
|
-
if (removed.length === 0) return [];
|
|
17342
|
-
reg.projects = kept;
|
|
17343
|
-
await writeRegistry(reg);
|
|
17344
|
-
return removed;
|
|
17345
|
-
});
|
|
17346
|
-
}
|
|
17347
|
-
|
|
17348
16961
|
// src/api.ts
|
|
17349
16962
|
import Fastify from "fastify";
|
|
17350
16963
|
import { timingSafeEqual } from "crypto";
|
|
@@ -17352,14 +16965,14 @@ import cors from "@fastify/cors";
|
|
|
17352
16965
|
import { DivergenceTypeSchema, PoliciesCheckBodySchema, PolicySeveritySchema } from "@neat.is/types";
|
|
17353
16966
|
|
|
17354
16967
|
// src/extend/index.ts
|
|
17355
|
-
import { promises as
|
|
17356
|
-
import
|
|
17357
|
-
import
|
|
16968
|
+
import { promises as fs37 } from "fs";
|
|
16969
|
+
import path72 from "path";
|
|
16970
|
+
import os2 from "os";
|
|
17358
16971
|
import { resolve as registryResolve, list as registryList } from "@neat.is/instrumentation-registry";
|
|
17359
16972
|
|
|
17360
16973
|
// src/installers/package-manager.ts
|
|
17361
|
-
import { promises as
|
|
17362
|
-
import
|
|
16974
|
+
import { promises as fs36 } from "fs";
|
|
16975
|
+
import path71 from "path";
|
|
17363
16976
|
import { spawn } from "child_process";
|
|
17364
16977
|
var LOCKFILE_PRIORITY = [
|
|
17365
16978
|
{ lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
|
|
@@ -17374,29 +16987,29 @@ var LOCKFILE_PRIORITY = [
|
|
|
17374
16987
|
var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
|
|
17375
16988
|
async function exists2(p) {
|
|
17376
16989
|
try {
|
|
17377
|
-
await
|
|
16990
|
+
await fs36.access(p);
|
|
17378
16991
|
return true;
|
|
17379
16992
|
} catch {
|
|
17380
16993
|
return false;
|
|
17381
16994
|
}
|
|
17382
16995
|
}
|
|
17383
16996
|
async function detectPackageManager(serviceDir) {
|
|
17384
|
-
let dir =
|
|
16997
|
+
let dir = path71.resolve(serviceDir);
|
|
17385
16998
|
const stops = /* @__PURE__ */ new Set();
|
|
17386
16999
|
for (let i = 0; i < 64; i++) {
|
|
17387
17000
|
if (stops.has(dir)) break;
|
|
17388
17001
|
stops.add(dir);
|
|
17389
17002
|
for (const candidate of LOCKFILE_PRIORITY) {
|
|
17390
|
-
const lockPath =
|
|
17003
|
+
const lockPath = path71.join(dir, candidate.lockfile);
|
|
17391
17004
|
if (await exists2(lockPath)) {
|
|
17392
17005
|
return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
|
|
17393
17006
|
}
|
|
17394
17007
|
}
|
|
17395
|
-
const parent =
|
|
17008
|
+
const parent = path71.dirname(dir);
|
|
17396
17009
|
if (parent === dir) break;
|
|
17397
17010
|
dir = parent;
|
|
17398
17011
|
}
|
|
17399
|
-
return { pm: "npm", cwd:
|
|
17012
|
+
return { pm: "npm", cwd: path71.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
|
|
17400
17013
|
}
|
|
17401
17014
|
async function runPackageManagerInstall(cmd) {
|
|
17402
17015
|
return new Promise((resolve) => {
|
|
@@ -17438,15 +17051,15 @@ ${err.message}`
|
|
|
17438
17051
|
// src/extend/index.ts
|
|
17439
17052
|
async function fileExists2(p) {
|
|
17440
17053
|
try {
|
|
17441
|
-
await
|
|
17054
|
+
await fs37.access(p);
|
|
17442
17055
|
return true;
|
|
17443
17056
|
} catch {
|
|
17444
17057
|
return false;
|
|
17445
17058
|
}
|
|
17446
17059
|
}
|
|
17447
17060
|
async function readPackageJson(scanPath) {
|
|
17448
|
-
const pkgPath =
|
|
17449
|
-
const raw = await
|
|
17061
|
+
const pkgPath = path72.join(scanPath, "package.json");
|
|
17062
|
+
const raw = await fs37.readFile(pkgPath, "utf8");
|
|
17450
17063
|
return JSON.parse(raw);
|
|
17451
17064
|
}
|
|
17452
17065
|
var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -17460,15 +17073,15 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
17460
17073
|
async function findHookFiles(scanPath) {
|
|
17461
17074
|
const found = [];
|
|
17462
17075
|
const walk11 = async (dir) => {
|
|
17463
|
-
const entries = await
|
|
17076
|
+
const entries = await fs37.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
17464
17077
|
for (const entry of entries) {
|
|
17465
17078
|
if (entry.isDirectory()) {
|
|
17466
17079
|
if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
|
|
17467
|
-
await walk11(
|
|
17080
|
+
await walk11(path72.join(dir, entry.name));
|
|
17468
17081
|
} else if (entry.isFile()) {
|
|
17469
17082
|
if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
|
|
17470
|
-
const rel =
|
|
17471
|
-
found.push(rel.split(
|
|
17083
|
+
const rel = path72.relative(scanPath, path72.join(dir, entry.name));
|
|
17084
|
+
found.push(rel.split(path72.sep).join("/"));
|
|
17472
17085
|
}
|
|
17473
17086
|
}
|
|
17474
17087
|
}
|
|
@@ -17479,7 +17092,7 @@ async function findHookFiles(scanPath) {
|
|
|
17479
17092
|
async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
17480
17093
|
let fallback = null;
|
|
17481
17094
|
for (const file of hookFiles) {
|
|
17482
|
-
const content = await
|
|
17095
|
+
const content = await fs37.readFile(path72.join(scanPath, file), "utf8");
|
|
17483
17096
|
const patched = splicedContent(content, snippet2);
|
|
17484
17097
|
if (patched !== null) return { file, content, patched };
|
|
17485
17098
|
if (fallback === null) fallback = { file, content };
|
|
@@ -17487,12 +17100,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
|
|
|
17487
17100
|
return { file: fallback.file, content: fallback.content, patched: null };
|
|
17488
17101
|
}
|
|
17489
17102
|
function extendLogPath() {
|
|
17490
|
-
return process.env.NEAT_EXTEND_LOG ??
|
|
17103
|
+
return process.env.NEAT_EXTEND_LOG ?? path72.join(os2.homedir(), ".neat", "extend-log.ndjson");
|
|
17491
17104
|
}
|
|
17492
17105
|
async function appendExtendLog(entry) {
|
|
17493
17106
|
const logPath = extendLogPath();
|
|
17494
|
-
await
|
|
17495
|
-
await
|
|
17107
|
+
await fs37.mkdir(path72.dirname(logPath), { recursive: true });
|
|
17108
|
+
await fs37.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
|
|
17496
17109
|
}
|
|
17497
17110
|
function splicedContent(fileContent, snippet2) {
|
|
17498
17111
|
if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
|
|
@@ -17550,7 +17163,7 @@ function lookupInstrumentation(library, installedVersion) {
|
|
|
17550
17163
|
}
|
|
17551
17164
|
async function describeProjectInstrumentation(ctx) {
|
|
17552
17165
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
17553
|
-
const envNeat = await fileExists2(
|
|
17166
|
+
const envNeat = await fileExists2(path72.join(ctx.scanPath, ".env.neat"));
|
|
17554
17167
|
const registryInstrPackages = new Set(
|
|
17555
17168
|
registryList().map((e) => e.instrumentation_package).filter((p) => !!p)
|
|
17556
17169
|
);
|
|
@@ -17572,7 +17185,7 @@ async function applyExtension(ctx, args, options) {
|
|
|
17572
17185
|
);
|
|
17573
17186
|
}
|
|
17574
17187
|
for (const file of hookFiles) {
|
|
17575
|
-
const content = await
|
|
17188
|
+
const content = await fs37.readFile(path72.join(ctx.scanPath, file), "utf8");
|
|
17576
17189
|
if (content.includes(args.registration_snippet)) {
|
|
17577
17190
|
return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
|
|
17578
17191
|
}
|
|
@@ -17584,18 +17197,18 @@ async function applyExtension(ctx, args, options) {
|
|
|
17584
17197
|
);
|
|
17585
17198
|
}
|
|
17586
17199
|
const primaryFile = primary.file;
|
|
17587
|
-
const primaryPath =
|
|
17200
|
+
const primaryPath = path72.join(ctx.scanPath, primaryFile);
|
|
17588
17201
|
const filesTouched = [];
|
|
17589
17202
|
const depsAdded = [];
|
|
17590
|
-
const pkgPath =
|
|
17203
|
+
const pkgPath = path72.join(ctx.scanPath, "package.json");
|
|
17591
17204
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
17592
17205
|
if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
|
|
17593
17206
|
pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
|
|
17594
|
-
await
|
|
17207
|
+
await fs37.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
17595
17208
|
filesTouched.push("package.json");
|
|
17596
17209
|
depsAdded.push(`${args.instrumentation_package}@${args.version}`);
|
|
17597
17210
|
}
|
|
17598
|
-
await
|
|
17211
|
+
await fs37.writeFile(primaryPath, primary.patched, "utf8");
|
|
17599
17212
|
filesTouched.push(primaryFile);
|
|
17600
17213
|
const cmd = await detectPackageManager(ctx.scanPath);
|
|
17601
17214
|
const installer = options?.runInstall ?? runPackageManagerInstall;
|
|
@@ -17626,7 +17239,7 @@ async function dryRunExtension(ctx, args) {
|
|
|
17626
17239
|
};
|
|
17627
17240
|
}
|
|
17628
17241
|
for (const file of hookFiles) {
|
|
17629
|
-
const content = await
|
|
17242
|
+
const content = await fs37.readFile(path72.join(ctx.scanPath, file), "utf8");
|
|
17630
17243
|
if (content.includes(args.registration_snippet)) {
|
|
17631
17244
|
return {
|
|
17632
17245
|
library: args.library,
|
|
@@ -17661,28 +17274,28 @@ async function rollbackExtension(ctx, args) {
|
|
|
17661
17274
|
if (!await fileExists2(logPath)) {
|
|
17662
17275
|
return { undone: false, message: "no apply found for library" };
|
|
17663
17276
|
}
|
|
17664
|
-
const raw = await
|
|
17277
|
+
const raw = await fs37.readFile(logPath, "utf8");
|
|
17665
17278
|
const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
17666
17279
|
const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
|
|
17667
17280
|
if (!match) {
|
|
17668
17281
|
return { undone: false, message: "no apply found for library" };
|
|
17669
17282
|
}
|
|
17670
|
-
const pkgPath =
|
|
17283
|
+
const pkgPath = path72.join(ctx.scanPath, "package.json");
|
|
17671
17284
|
if (await fileExists2(pkgPath)) {
|
|
17672
17285
|
const pkg = await readPackageJson(ctx.scanPath);
|
|
17673
17286
|
if (pkg.dependencies?.[match.instrumentation_package]) {
|
|
17674
17287
|
const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
|
|
17675
17288
|
pkg.dependencies = rest;
|
|
17676
|
-
await
|
|
17289
|
+
await fs37.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
17677
17290
|
}
|
|
17678
17291
|
}
|
|
17679
17292
|
const hookFiles = await findHookFiles(ctx.scanPath);
|
|
17680
17293
|
for (const file of hookFiles) {
|
|
17681
|
-
const filePath =
|
|
17682
|
-
const content = await
|
|
17294
|
+
const filePath = path72.join(ctx.scanPath, file);
|
|
17295
|
+
const content = await fs37.readFile(filePath, "utf8");
|
|
17683
17296
|
if (content.includes(match.registration_snippet)) {
|
|
17684
17297
|
const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
|
|
17685
|
-
await
|
|
17298
|
+
await fs37.writeFile(filePath, filtered, "utf8");
|
|
17686
17299
|
break;
|
|
17687
17300
|
}
|
|
17688
17301
|
}
|
|
@@ -18777,9 +18390,9 @@ data: ${JSON.stringify(envelope.payload)}
|
|
|
18777
18390
|
|
|
18778
18391
|
// src/pr-verdict.ts
|
|
18779
18392
|
import { createHash } from "crypto";
|
|
18780
|
-
import { promises as
|
|
18781
|
-
import
|
|
18782
|
-
import
|
|
18393
|
+
import { promises as fs38 } from "fs";
|
|
18394
|
+
import os4 from "os";
|
|
18395
|
+
import path74 from "path";
|
|
18783
18396
|
|
|
18784
18397
|
// ../action/src/graph.mjs
|
|
18785
18398
|
import { readFileSync as readFileSync3 } from "fs";
|
|
@@ -19054,8 +18667,8 @@ function renderVerdict({
|
|
|
19054
18667
|
import { PrVerdictBodySchema } from "@neat.is/types";
|
|
19055
18668
|
|
|
19056
18669
|
// src/connectors/hosted-repos.ts
|
|
19057
|
-
import
|
|
19058
|
-
import
|
|
18670
|
+
import os3 from "os";
|
|
18671
|
+
import path73 from "path";
|
|
19059
18672
|
import { mkdtemp, rm } from "fs/promises";
|
|
19060
18673
|
var CP_REQUEST_TIMEOUT_MS = 1e4;
|
|
19061
18674
|
var DEFAULT_SYNC_INTERVAL_MS = 5 * 6e4;
|
|
@@ -19084,7 +18697,7 @@ async function cpPostStatus(deps, owner, name, body) {
|
|
|
19084
18697
|
if (!res.ok && res.status !== 404) throw new Error(`control plane status POST \u2192 HTTP ${res.status}`);
|
|
19085
18698
|
}
|
|
19086
18699
|
var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
|
|
19087
|
-
const [{ default: git }, httpMod,
|
|
18700
|
+
const [{ default: git }, httpMod, fs40] = await Promise.all([
|
|
19088
18701
|
import("isomorphic-git"),
|
|
19089
18702
|
import("isomorphic-git/http/node"),
|
|
19090
18703
|
import("fs")
|
|
@@ -19095,7 +18708,7 @@ var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
|
|
|
19095
18708
|
const username = parsed.password ? parsed.username : "x-access-token";
|
|
19096
18709
|
const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
19097
18710
|
const clone = git.clone({
|
|
19098
|
-
fs:
|
|
18711
|
+
fs: fs40,
|
|
19099
18712
|
http,
|
|
19100
18713
|
dir: destDir,
|
|
19101
18714
|
url: cleanUrl,
|
|
@@ -19116,7 +18729,7 @@ var defaultCloneRepo = async (cloneUrl, ref, destDir) => {
|
|
|
19116
18729
|
}
|
|
19117
18730
|
};
|
|
19118
18731
|
var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
19119
|
-
const [{ default: git }, httpMod,
|
|
18732
|
+
const [{ default: git }, httpMod, fs40] = await Promise.all([
|
|
19120
18733
|
import("isomorphic-git"),
|
|
19121
18734
|
import("isomorphic-git/http/node"),
|
|
19122
18735
|
import("fs")
|
|
@@ -19127,10 +18740,10 @@ var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
|
19127
18740
|
const username = parsed.password ? parsed.username : "x-access-token";
|
|
19128
18741
|
const cleanUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
19129
18742
|
const clone = (async () => {
|
|
19130
|
-
await git.init({ fs:
|
|
19131
|
-
await git.addRemote({ fs:
|
|
18743
|
+
await git.init({ fs: fs40, dir: destDir });
|
|
18744
|
+
await git.addRemote({ fs: fs40, dir: destDir, remote: "origin", url: cleanUrl });
|
|
19132
18745
|
await git.fetch({
|
|
19133
|
-
fs:
|
|
18746
|
+
fs: fs40,
|
|
19134
18747
|
http,
|
|
19135
18748
|
dir: destDir,
|
|
19136
18749
|
remote: "origin",
|
|
@@ -19142,7 +18755,7 @@ var defaultCloneCommit = async (cloneUrl, sha, destDir) => {
|
|
|
19142
18755
|
tags: false,
|
|
19143
18756
|
...password ? { onAuth: () => ({ username, password }) } : {}
|
|
19144
18757
|
});
|
|
19145
|
-
await git.checkout({ fs:
|
|
18758
|
+
await git.checkout({ fs: fs40, dir: destDir, ref: sha, force: true });
|
|
19146
18759
|
})();
|
|
19147
18760
|
clone.catch(() => {
|
|
19148
18761
|
});
|
|
@@ -19167,10 +18780,10 @@ async function syncOneRepo(r, input) {
|
|
|
19167
18780
|
const cloneRepo = input.cloneRepo ?? defaultCloneRepo;
|
|
19168
18781
|
const extract = input.extract ?? extractFromDirectory;
|
|
19169
18782
|
const label = `${r.owner}/${r.name}`;
|
|
19170
|
-
const tmpRoot = input.tmpRoot ??
|
|
18783
|
+
const tmpRoot = input.tmpRoot ?? os3.tmpdir();
|
|
19171
18784
|
let dir;
|
|
19172
18785
|
try {
|
|
19173
|
-
dir = await mkdtemp(
|
|
18786
|
+
dir = await mkdtemp(path73.join(tmpRoot, "neat-repo-"));
|
|
19174
18787
|
await cpPostStatus(deps, r.owner, r.name, { syncStatus: "syncing", detail: "cloning" }).catch(() => {
|
|
19175
18788
|
});
|
|
19176
18789
|
await cloneRepo(r.cloneUrl, r.defaultBranch, dir);
|
|
@@ -19339,9 +18952,9 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19339
18952
|
const startedAt = now();
|
|
19340
18953
|
const cloneCommit = deps.cloneCommit ?? defaultCloneCommit;
|
|
19341
18954
|
const extract = deps.extract ?? extractFromDirectory;
|
|
19342
|
-
const root = await
|
|
19343
|
-
const baseDir =
|
|
19344
|
-
const headDir =
|
|
18955
|
+
const root = await fs38.mkdtemp(path74.join(deps.tmpRoot ?? os4.tmpdir(), "neat-pr-"));
|
|
18956
|
+
const baseDir = path74.join(root, "base");
|
|
18957
|
+
const headDir = path74.join(root, "head");
|
|
19345
18958
|
const stage = async (name, run) => {
|
|
19346
18959
|
const left = deadline - now();
|
|
19347
18960
|
if (left <= 0) throw new PrVerdictError(`verdict ran out of time before ${name}`, 504, name);
|
|
@@ -19364,8 +18977,8 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19364
18977
|
};
|
|
19365
18978
|
const extractOpts = { source: `${req.owner}/${req.name}`, announce: false };
|
|
19366
18979
|
try {
|
|
19367
|
-
await
|
|
19368
|
-
await
|
|
18980
|
+
await fs38.mkdir(baseDir);
|
|
18981
|
+
await fs38.mkdir(headDir);
|
|
19369
18982
|
await stage("clone-base", () => cloneCommit(req.cloneUrl, req.baseSha, baseDir));
|
|
19370
18983
|
await stage("clone-head", () => cloneCommit(req.cloneUrl, req.headSha, headDir));
|
|
19371
18984
|
const baseGraph = makeGraph();
|
|
@@ -19387,7 +19000,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19387
19000
|
const found = observedBreakFrom(node, getObservedDependencies(ctx.liveGraph, node.id));
|
|
19388
19001
|
if (found) observedBreaks.push(found);
|
|
19389
19002
|
}
|
|
19390
|
-
const { marker, body } = renderVerdict({
|
|
19003
|
+
const { marker, body: rendered } = renderVerdict({
|
|
19391
19004
|
graph: head,
|
|
19392
19005
|
delta,
|
|
19393
19006
|
changedFiles,
|
|
@@ -19395,6 +19008,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19395
19008
|
observedBreaks,
|
|
19396
19009
|
tone: req.tone ?? "loud"
|
|
19397
19010
|
});
|
|
19011
|
+
const body = rendered.startsWith(marker) ? rendered.slice(marker.length).replace(/^(?:\r?\n)+/, "") : rendered;
|
|
19398
19012
|
return {
|
|
19399
19013
|
marker,
|
|
19400
19014
|
body,
|
|
@@ -19406,7 +19020,7 @@ async function computePrVerdict(req, ctx, deps, deadline) {
|
|
|
19406
19020
|
durationMs: now() - startedAt
|
|
19407
19021
|
};
|
|
19408
19022
|
} finally {
|
|
19409
|
-
await
|
|
19023
|
+
await fs38.rm(root, { recursive: true, force: true }).catch(() => {
|
|
19410
19024
|
});
|
|
19411
19025
|
}
|
|
19412
19026
|
}
|
|
@@ -19414,13 +19028,13 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "neat-out"]);
|
|
|
19414
19028
|
async function hashTree(dir) {
|
|
19415
19029
|
const out = /* @__PURE__ */ new Map();
|
|
19416
19030
|
const walk11 = async (rel) => {
|
|
19417
|
-
const entries = await
|
|
19031
|
+
const entries = await fs38.readdir(path74.join(dir, rel), { withFileTypes: true }).catch(() => []);
|
|
19418
19032
|
for (const entry of entries) {
|
|
19419
19033
|
const child = rel ? `${rel}/${entry.name}` : entry.name;
|
|
19420
19034
|
if (entry.isDirectory()) {
|
|
19421
19035
|
if (!SKIP_DIRS.has(entry.name)) await walk11(child);
|
|
19422
19036
|
} else if (entry.isFile()) {
|
|
19423
|
-
const bytes = await
|
|
19037
|
+
const bytes = await fs38.readFile(path74.join(dir, child)).catch(() => null);
|
|
19424
19038
|
if (bytes) out.set(child, createHash("sha1").update(bytes).digest("hex"));
|
|
19425
19039
|
}
|
|
19426
19040
|
}
|
|
@@ -19437,9 +19051,9 @@ async function changedPathsBetween(baseDir, headDir) {
|
|
|
19437
19051
|
}
|
|
19438
19052
|
|
|
19439
19053
|
// src/connectors-config.ts
|
|
19440
|
-
import
|
|
19441
|
-
import
|
|
19442
|
-
import { promises as
|
|
19054
|
+
import os5 from "os";
|
|
19055
|
+
import path75 from "path";
|
|
19056
|
+
import { promises as fs39 } from "fs";
|
|
19443
19057
|
var CONNECTORS_CONFIG_VERSION = 1;
|
|
19444
19058
|
var EnvRefUnsetError = class extends Error {
|
|
19445
19059
|
ref;
|
|
@@ -19451,19 +19065,19 @@ var EnvRefUnsetError = class extends Error {
|
|
|
19451
19065
|
this.varName = varName;
|
|
19452
19066
|
}
|
|
19453
19067
|
};
|
|
19454
|
-
function
|
|
19068
|
+
function neatHome() {
|
|
19455
19069
|
const override = process.env.NEAT_HOME;
|
|
19456
|
-
if (override && override.length > 0) return
|
|
19457
|
-
return
|
|
19070
|
+
if (override && override.length > 0) return path75.resolve(override);
|
|
19071
|
+
return path75.join(os5.homedir(), ".neat");
|
|
19458
19072
|
}
|
|
19459
|
-
function connectorsConfigPath(home =
|
|
19460
|
-
return
|
|
19073
|
+
function connectorsConfigPath(home = neatHome()) {
|
|
19074
|
+
return path75.join(home, "connectors.json");
|
|
19461
19075
|
}
|
|
19462
19076
|
var MODE_MASK_LOOSER_THAN_0600 = 63;
|
|
19463
19077
|
async function warnIfModeLooserThan0600(file) {
|
|
19464
19078
|
if (process.platform === "win32") return;
|
|
19465
19079
|
try {
|
|
19466
|
-
const stat = await
|
|
19080
|
+
const stat = await fs39.stat(file);
|
|
19467
19081
|
if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
|
|
19468
19082
|
const mode = (stat.mode & 511).toString(8).padStart(3, "0");
|
|
19469
19083
|
console.warn(
|
|
@@ -19473,11 +19087,11 @@ async function warnIfModeLooserThan0600(file) {
|
|
|
19473
19087
|
} catch {
|
|
19474
19088
|
}
|
|
19475
19089
|
}
|
|
19476
|
-
async function readConnectorsConfig(home =
|
|
19090
|
+
async function readConnectorsConfig(home = neatHome()) {
|
|
19477
19091
|
const file = connectorsConfigPath(home);
|
|
19478
19092
|
let raw;
|
|
19479
19093
|
try {
|
|
19480
|
-
raw = await
|
|
19094
|
+
raw = await fs39.readFile(file, "utf8");
|
|
19481
19095
|
} catch (err) {
|
|
19482
19096
|
if (err.code === "ENOENT") {
|
|
19483
19097
|
return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
|
|
@@ -19587,8 +19201,8 @@ function connectorMatchesProject(entry, project) {
|
|
|
19587
19201
|
}
|
|
19588
19202
|
var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
|
|
19589
19203
|
var CONNECTORS_LOCK_RETRY_MS = 50;
|
|
19590
|
-
function connectorsConfigLockPath(home =
|
|
19591
|
-
return
|
|
19204
|
+
function connectorsConfigLockPath(home = neatHome()) {
|
|
19205
|
+
return path75.join(home, "connectors.json.lock");
|
|
19592
19206
|
}
|
|
19593
19207
|
function isEnvRef(value) {
|
|
19594
19208
|
return value.length > 1 && value.startsWith("$");
|
|
@@ -19601,9 +19215,9 @@ function redactCredentialRef(ref) {
|
|
|
19601
19215
|
return out;
|
|
19602
19216
|
}
|
|
19603
19217
|
async function writeConfigAtomically0600(file, contents) {
|
|
19604
|
-
await
|
|
19218
|
+
await fs39.mkdir(path75.dirname(file), { recursive: true });
|
|
19605
19219
|
const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
19606
|
-
const fd = await
|
|
19220
|
+
const fd = await fs39.open(tmp, "w", 384);
|
|
19607
19221
|
try {
|
|
19608
19222
|
await fd.writeFile(contents, "utf8");
|
|
19609
19223
|
await fd.chmod(384);
|
|
@@ -19611,14 +19225,14 @@ async function writeConfigAtomically0600(file, contents) {
|
|
|
19611
19225
|
} finally {
|
|
19612
19226
|
await fd.close();
|
|
19613
19227
|
}
|
|
19614
|
-
await
|
|
19228
|
+
await fs39.rename(tmp, file);
|
|
19615
19229
|
}
|
|
19616
19230
|
async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
|
|
19617
19231
|
const deadline = Date.now() + timeoutMs;
|
|
19618
|
-
await
|
|
19232
|
+
await fs39.mkdir(path75.dirname(lockPath), { recursive: true });
|
|
19619
19233
|
for (; ; ) {
|
|
19620
19234
|
try {
|
|
19621
|
-
const fd = await
|
|
19235
|
+
const fd = await fs39.open(lockPath, "wx");
|
|
19622
19236
|
try {
|
|
19623
19237
|
await fd.writeFile(`${process.pid}
|
|
19624
19238
|
`, "utf8");
|
|
@@ -19638,7 +19252,7 @@ async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEO
|
|
|
19638
19252
|
}
|
|
19639
19253
|
}
|
|
19640
19254
|
async function releaseConnectorsLock(lockPath) {
|
|
19641
|
-
await
|
|
19255
|
+
await fs39.unlink(lockPath).catch(() => {
|
|
19642
19256
|
});
|
|
19643
19257
|
}
|
|
19644
19258
|
async function withConnectorsLock(home, fn) {
|
|
@@ -19657,7 +19271,7 @@ function serializeConfig(config) {
|
|
|
19657
19271
|
2
|
|
19658
19272
|
) + "\n";
|
|
19659
19273
|
}
|
|
19660
|
-
async function upsertConnectorEntry(entry, home =
|
|
19274
|
+
async function upsertConnectorEntry(entry, home = neatHome()) {
|
|
19661
19275
|
const file = connectorsConfigPath(home);
|
|
19662
19276
|
return withConnectorsLock(home, async () => {
|
|
19663
19277
|
const config = await readConnectorsConfig(home);
|
|
@@ -19670,7 +19284,7 @@ async function upsertConnectorEntry(entry, home = neatHome2()) {
|
|
|
19670
19284
|
return { replaced };
|
|
19671
19285
|
});
|
|
19672
19286
|
}
|
|
19673
|
-
async function removeConnectorEntry(id, home =
|
|
19287
|
+
async function removeConnectorEntry(id, home = neatHome()) {
|
|
19674
19288
|
const file = connectorsConfigPath(home);
|
|
19675
19289
|
return withConnectorsLock(home, async () => {
|
|
19676
19290
|
const config = await readConnectorsConfig(home);
|
|
@@ -20378,10 +19992,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
|
|
|
20378
19992
|
// src/connectors/supabase/map.ts
|
|
20379
19993
|
var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
|
|
20380
19994
|
var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
|
|
20381
|
-
function targetFromRestPath(
|
|
20382
|
-
const rpcMatch = REST_RPC_PATH_RE.exec(
|
|
19995
|
+
function targetFromRestPath(path76) {
|
|
19996
|
+
const rpcMatch = REST_RPC_PATH_RE.exec(path76);
|
|
20383
19997
|
if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
|
|
20384
|
-
const tableMatch = REST_TABLE_PATH_RE.exec(
|
|
19998
|
+
const tableMatch = REST_TABLE_PATH_RE.exec(path76);
|
|
20385
19999
|
if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
|
|
20386
20000
|
return null;
|
|
20387
20001
|
}
|
|
@@ -20976,9 +20590,9 @@ function parseFirebaseTargetName(targetName) {
|
|
|
20976
20590
|
const secondSep = rest.indexOf(FIELD_SEP);
|
|
20977
20591
|
if (secondSep === -1) return null;
|
|
20978
20592
|
const method = rest.slice(0, secondSep);
|
|
20979
|
-
const
|
|
20980
|
-
if (!resourceName || !method || !
|
|
20981
|
-
return { resourceName, method, path:
|
|
20593
|
+
const path76 = rest.slice(secondSep + 1);
|
|
20594
|
+
if (!resourceName || !method || !path76) return null;
|
|
20595
|
+
return { resourceName, method, path: path76 };
|
|
20982
20596
|
}
|
|
20983
20597
|
function resourceNameFor(type, labels) {
|
|
20984
20598
|
if (!labels) return null;
|
|
@@ -21016,14 +20630,14 @@ function mapLogEntryToSignal(entry) {
|
|
|
21016
20630
|
if (!req) return null;
|
|
21017
20631
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21018
20632
|
const method = req.requestMethod.toUpperCase();
|
|
21019
|
-
const
|
|
21020
|
-
if (
|
|
20633
|
+
const path76 = pathFromRequestUrl(req.requestUrl);
|
|
20634
|
+
if (path76 === null) return null;
|
|
21021
20635
|
const timestamp = entry.timestamp;
|
|
21022
20636
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21023
20637
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
|
|
21024
20638
|
return {
|
|
21025
20639
|
targetKind: resourceType,
|
|
21026
|
-
targetName: packFirebaseTargetName({ resourceName, method, path:
|
|
20640
|
+
targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
|
|
21027
20641
|
callCount: 1,
|
|
21028
20642
|
errorCount: isError ? 1 : 0,
|
|
21029
20643
|
lastObservedIso: timestamp
|
|
@@ -21046,9 +20660,9 @@ import { NodeType as NodeType35 } from "@neat.is/types";
|
|
|
21046
20660
|
function normalizeName(name) {
|
|
21047
20661
|
return name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
21048
20662
|
}
|
|
21049
|
-
function inferServiceName(graph, resourceName, method,
|
|
20663
|
+
function inferServiceName(graph, resourceName, method, path76) {
|
|
21050
20664
|
const wantedMethod = method.toUpperCase();
|
|
21051
|
-
const wantedPath = normalizePathTemplate(
|
|
20665
|
+
const wantedPath = normalizePathTemplate(path76);
|
|
21052
20666
|
const services = [];
|
|
21053
20667
|
const owners = /* @__PURE__ */ new Set();
|
|
21054
20668
|
graph.forEachNode((_id, attrs) => {
|
|
@@ -21256,7 +20870,7 @@ function mapEventToSignal(event) {
|
|
|
21256
20870
|
if (Number.isNaN(observedAt.getTime())) return null;
|
|
21257
20871
|
const statusCode = metadata?.statusCode;
|
|
21258
20872
|
const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
|
|
21259
|
-
const
|
|
20873
|
+
const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
|
|
21260
20874
|
return {
|
|
21261
20875
|
targetKind: CLOUDFLARE_TARGET_KIND,
|
|
21262
20876
|
targetName: scriptName,
|
|
@@ -21264,7 +20878,7 @@ function mapEventToSignal(event) {
|
|
|
21264
20878
|
errorCount: isError ? 1 : 0,
|
|
21265
20879
|
lastObservedIso: observedAt.toISOString(),
|
|
21266
20880
|
method,
|
|
21267
|
-
...
|
|
20881
|
+
...path76 ? { path: path76 } : {},
|
|
21268
20882
|
...typeof statusCode === "number" ? { statusCode } : {},
|
|
21269
20883
|
...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
|
|
21270
20884
|
};
|
|
@@ -21310,8 +20924,8 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
21310
20924
|
});
|
|
21311
20925
|
return found;
|
|
21312
20926
|
}
|
|
21313
|
-
function findMatchingRouteNode(graph, serviceName, method,
|
|
21314
|
-
const normalizedPath = normalizePathTemplate(
|
|
20927
|
+
function findMatchingRouteNode(graph, serviceName, method, path76) {
|
|
20928
|
+
const normalizedPath = normalizePathTemplate(path76);
|
|
21315
20929
|
let found = null;
|
|
21316
20930
|
graph.forEachNode((id, attrs) => {
|
|
21317
20931
|
if (found) return;
|
|
@@ -21328,10 +20942,10 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
21328
20942
|
return (signal) => {
|
|
21329
20943
|
if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
|
|
21330
20944
|
const scriptName = signal.targetName;
|
|
21331
|
-
const { method, path:
|
|
20945
|
+
const { method, path: path76 } = signal;
|
|
21332
20946
|
const resolveRouteGrain = (serviceName, wholeFileId) => {
|
|
21333
|
-
if (!method || !
|
|
21334
|
-
return findMatchingRouteNode(graph, serviceName, method,
|
|
20947
|
+
if (!method || !path76) return wholeFileId;
|
|
20948
|
+
return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
|
|
21335
20949
|
};
|
|
21336
20950
|
const mapping = config.workers?.[scriptName];
|
|
21337
20951
|
if (mapping) {
|
|
@@ -21661,9 +21275,9 @@ function parseCloudRunTargetName(targetName) {
|
|
|
21661
21275
|
const secondSep = rest.indexOf(FIELD_SEP2);
|
|
21662
21276
|
if (secondSep === -1) return null;
|
|
21663
21277
|
const method = rest.slice(0, secondSep);
|
|
21664
|
-
const
|
|
21665
|
-
if (!serviceName || !method || !
|
|
21666
|
-
return { serviceName, method, path:
|
|
21278
|
+
const path76 = rest.slice(secondSep + 1);
|
|
21279
|
+
if (!serviceName || !method || !path76) return null;
|
|
21280
|
+
return { serviceName, method, path: path76 };
|
|
21667
21281
|
}
|
|
21668
21282
|
|
|
21669
21283
|
// src/connectors/cloud-run/map.ts
|
|
@@ -21692,14 +21306,14 @@ function mapLogEntryToSignal2(entry) {
|
|
|
21692
21306
|
if (!req) return null;
|
|
21693
21307
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21694
21308
|
const method = req.requestMethod.toUpperCase();
|
|
21695
|
-
const
|
|
21696
|
-
if (
|
|
21309
|
+
const path76 = pathFromRequestUrl2(req.requestUrl);
|
|
21310
|
+
if (path76 === null) return null;
|
|
21697
21311
|
const timestamp = entry.timestamp;
|
|
21698
21312
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21699
21313
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
|
|
21700
21314
|
return {
|
|
21701
21315
|
targetKind: CLOUD_RUN_TARGET_KIND,
|
|
21702
|
-
targetName: packCloudRunTargetName({ serviceName, method, path:
|
|
21316
|
+
targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
|
|
21703
21317
|
callCount: 1,
|
|
21704
21318
|
errorCount: isError ? 1 : 0,
|
|
21705
21319
|
lastObservedIso: timestamp
|
|
@@ -21737,14 +21351,14 @@ function createCloudRunResolveTarget(graph, config) {
|
|
|
21737
21351
|
if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
|
|
21738
21352
|
const identity = parseCloudRunTargetName(signal.targetName);
|
|
21739
21353
|
if (!identity) return null;
|
|
21740
|
-
const { serviceName: gcpServiceName, method, path:
|
|
21741
|
-
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method,
|
|
21354
|
+
const { serviceName: gcpServiceName, method, path: path76 } = identity;
|
|
21355
|
+
const mappedService = config.serviceMap?.[gcpServiceName] ?? (config.inferServices ? inferServiceName(graph, gcpServiceName, method, path76) ?? void 0 : void 0);
|
|
21742
21356
|
if (mappedService) {
|
|
21743
21357
|
const routeNodeId = findMatchingRouteNode2(
|
|
21744
21358
|
graph,
|
|
21745
21359
|
mappedService,
|
|
21746
21360
|
method,
|
|
21747
|
-
normalizePathTemplate(
|
|
21361
|
+
normalizePathTemplate(path76)
|
|
21748
21362
|
);
|
|
21749
21363
|
if (routeNodeId) {
|
|
21750
21364
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType32.CALLS };
|
|
@@ -21872,9 +21486,9 @@ function parseGcpLbTargetName(targetName) {
|
|
|
21872
21486
|
const secondSep = rest.indexOf(FIELD_SEP3);
|
|
21873
21487
|
if (secondSep === -1) return null;
|
|
21874
21488
|
const method = rest.slice(0, secondSep);
|
|
21875
|
-
const
|
|
21876
|
-
if (!backendServiceName || !method || !
|
|
21877
|
-
return { backendServiceName, method, path:
|
|
21489
|
+
const path76 = rest.slice(secondSep + 1);
|
|
21490
|
+
if (!backendServiceName || !method || !path76) return null;
|
|
21491
|
+
return { backendServiceName, method, path: path76 };
|
|
21878
21492
|
}
|
|
21879
21493
|
|
|
21880
21494
|
// src/connectors/gcp-lb/map.ts
|
|
@@ -21903,14 +21517,14 @@ function mapLogEntryToSignal3(entry) {
|
|
|
21903
21517
|
if (!req) return null;
|
|
21904
21518
|
if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
|
|
21905
21519
|
const method = req.requestMethod.toUpperCase();
|
|
21906
|
-
const
|
|
21907
|
-
if (
|
|
21520
|
+
const path76 = pathFromRequestUrl3(req.requestUrl);
|
|
21521
|
+
if (path76 === null) return null;
|
|
21908
21522
|
const timestamp = entry.timestamp;
|
|
21909
21523
|
if (typeof timestamp !== "string" || timestamp.length === 0) return null;
|
|
21910
21524
|
const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
|
|
21911
21525
|
return {
|
|
21912
21526
|
targetKind: GCP_LB_TARGET_KIND,
|
|
21913
|
-
targetName: packGcpLbTargetName({ backendServiceName, method, path:
|
|
21527
|
+
targetName: packGcpLbTargetName({ backendServiceName, method, path: path76 }),
|
|
21914
21528
|
callCount: 1,
|
|
21915
21529
|
errorCount: isError ? 1 : 0,
|
|
21916
21530
|
lastObservedIso: timestamp
|
|
@@ -21948,14 +21562,14 @@ function createGcpLbResolveTarget(graph, config) {
|
|
|
21948
21562
|
if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
|
|
21949
21563
|
const identity = parseGcpLbTargetName(signal.targetName);
|
|
21950
21564
|
if (!identity) return null;
|
|
21951
|
-
const { backendServiceName, method, path:
|
|
21952
|
-
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method,
|
|
21565
|
+
const { backendServiceName, method, path: path76 } = identity;
|
|
21566
|
+
const mappedService = config.backendServiceMap?.[backendServiceName] ?? (config.inferServices ? inferServiceName(graph, backendServiceName, method, path76) ?? void 0 : void 0);
|
|
21953
21567
|
if (mappedService) {
|
|
21954
21568
|
const routeNodeId = findMatchingRouteNode3(
|
|
21955
21569
|
graph,
|
|
21956
21570
|
mappedService,
|
|
21957
21571
|
method,
|
|
21958
|
-
normalizePathTemplate(
|
|
21572
|
+
normalizePathTemplate(path76)
|
|
21959
21573
|
);
|
|
21960
21574
|
if (routeNodeId) {
|
|
21961
21575
|
return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: EdgeType33.CALLS };
|
|
@@ -24416,25 +24030,6 @@ export {
|
|
|
24416
24030
|
pathsForProject,
|
|
24417
24031
|
Projects,
|
|
24418
24032
|
parseExtraProjects,
|
|
24419
|
-
registryPath,
|
|
24420
|
-
registryLockPath,
|
|
24421
|
-
removeDaemonRecord,
|
|
24422
|
-
listMachineProjects,
|
|
24423
|
-
findDaemonByProject,
|
|
24424
|
-
signalDaemonStop,
|
|
24425
|
-
normalizeProjectPath,
|
|
24426
|
-
writeAtomically,
|
|
24427
|
-
readRegistry,
|
|
24428
|
-
RegistryError,
|
|
24429
|
-
ProjectNameCollisionError,
|
|
24430
|
-
addProject,
|
|
24431
|
-
getProject,
|
|
24432
|
-
findProjectByPath,
|
|
24433
|
-
listProjects,
|
|
24434
|
-
setStatus,
|
|
24435
|
-
touchLastSeen,
|
|
24436
|
-
removeProject,
|
|
24437
|
-
pruneRegistry,
|
|
24438
24033
|
maybeStartRepoSync,
|
|
24439
24034
|
readConnectorsConfig,
|
|
24440
24035
|
resolveCredential,
|
|
@@ -24457,4 +24052,4 @@ export {
|
|
|
24457
24052
|
deprovisionConnector,
|
|
24458
24053
|
buildApi
|
|
24459
24054
|
};
|
|
24460
|
-
//# sourceMappingURL=chunk-
|
|
24055
|
+
//# sourceMappingURL=chunk-6QMWJAWR.js.map
|