@neat.is/core 0.7.7 → 0.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-BIY46Q6U.js → chunk-BC53SCT7.js} +36 -2
- package/dist/chunk-BC53SCT7.js.map +1 -0
- package/dist/{chunk-4GCV46AP.js → chunk-FCO5Z3RW.js} +2 -2
- package/dist/{chunk-YVZRBT4C.js → chunk-ILG3SMD5.js} +54 -4
- package/dist/{chunk-YVZRBT4C.js.map → chunk-ILG3SMD5.js.map} +1 -1
- package/dist/{chunk-UI3AFJAF.js → chunk-N5TPODCX.js} +171 -49
- package/dist/chunk-N5TPODCX.js.map +1 -0
- package/dist/{chunk-Y43UCVZS.js → chunk-UUYCTH2E.js} +24 -8
- package/dist/chunk-UUYCTH2E.js.map +1 -0
- package/dist/cli.cjs +238 -63
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +17 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +242 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +242 -54
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-DPSAGRUU.js → otel-grpc-CO47JRIN.js} +3 -3
- package/dist/server.cjs +226 -54
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-BIY46Q6U.js.map +0 -1
- package/dist/chunk-UI3AFJAF.js.map +0 -1
- package/dist/chunk-Y43UCVZS.js.map +0 -1
- /package/dist/{chunk-4GCV46AP.js.map → chunk-FCO5Z3RW.js.map} +0 -0
- /package/dist/{otel-grpc-DPSAGRUU.js.map → otel-grpc-CO47JRIN.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -503,13 +503,18 @@ function loadProtobufResponseEncoder() {
|
|
|
503
503
|
);
|
|
504
504
|
return exportTraceServiceResponseType;
|
|
505
505
|
}
|
|
506
|
-
function encodeProtobufResponseBody() {
|
|
507
|
-
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
506
|
+
function encodeProtobufResponseBody(rejected, message) {
|
|
508
507
|
const Type = loadProtobufResponseEncoder();
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
508
|
+
if (!rejected) {
|
|
509
|
+
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
510
|
+
const msg2 = Type.create({});
|
|
511
|
+
cachedProtobufResponseBody = Buffer.from(Type.encode(msg2).finish());
|
|
512
|
+
return cachedProtobufResponseBody;
|
|
513
|
+
}
|
|
514
|
+
const msg = Type.fromObject({
|
|
515
|
+
partial_success: { rejected_spans: rejected, error_message: message ?? "" }
|
|
516
|
+
});
|
|
517
|
+
return Buffer.from(Type.encode(msg).finish());
|
|
513
518
|
}
|
|
514
519
|
async function decodeProtobufBody(buf) {
|
|
515
520
|
const Type = loadProtobufDecoder();
|
|
@@ -624,6 +629,13 @@ async function buildOtelReceiver(opts) {
|
|
|
624
629
|
}
|
|
625
630
|
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: {} });
|
|
626
631
|
}
|
|
632
|
+
function sendOtlpPartial(reply, flavor, rejected, message) {
|
|
633
|
+
if (flavor === "protobuf") {
|
|
634
|
+
const buf = encodeProtobufResponseBody(rejected, message);
|
|
635
|
+
return reply.code(200).header("content-type", "application/x-protobuf").send(buf);
|
|
636
|
+
}
|
|
637
|
+
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: { rejectedSpans: rejected, errorMessage: message } });
|
|
638
|
+
}
|
|
627
639
|
app.addContentTypeParser(
|
|
628
640
|
"application/x-protobuf",
|
|
629
641
|
{ parseAs: "buffer", bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024 },
|
|
@@ -651,6 +663,10 @@ async function buildOtelReceiver(opts) {
|
|
|
651
663
|
}
|
|
652
664
|
}
|
|
653
665
|
enqueue(spans);
|
|
666
|
+
if (opts.classifyBareRoutability) {
|
|
667
|
+
const { rejected, message } = opts.classifyBareRoutability(spans);
|
|
668
|
+
if (rejected > 0) return sendOtlpPartial(reply, result.flavor, rejected, message);
|
|
669
|
+
}
|
|
654
670
|
return sendOtlpSuccess(reply, result.flavor);
|
|
655
671
|
});
|
|
656
672
|
app.post("/projects/:project/v1/traces", async (req, reply) => {
|
|
@@ -3045,6 +3061,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
|
3045
3061
|
"all"
|
|
3046
3062
|
]);
|
|
3047
3063
|
var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
3064
|
+
var NET_HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
3048
3065
|
var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
3049
3066
|
function goRouterRoutesFromSource(source, parser, framework) {
|
|
3050
3067
|
const tree = parseSource2(parser, source);
|
|
@@ -3098,6 +3115,101 @@ function echoRoutesFromSource(source, parser) {
|
|
|
3098
3115
|
function fiberRoutesFromSource(source, parser) {
|
|
3099
3116
|
return goRouterRoutesFromSource(source, parser, "fiber");
|
|
3100
3117
|
}
|
|
3118
|
+
function chiRoutesFromSource(source, parser) {
|
|
3119
|
+
const tree = parseSource2(parser, source);
|
|
3120
|
+
const out = [];
|
|
3121
|
+
chiWalk(tree.rootNode, "", out);
|
|
3122
|
+
return out;
|
|
3123
|
+
}
|
|
3124
|
+
function stripChiRegex(path68) {
|
|
3125
|
+
return path68.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3126
|
+
}
|
|
3127
|
+
function chiWalk(node, prefix, out) {
|
|
3128
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3129
|
+
const child = node.namedChild(i);
|
|
3130
|
+
if (child) chiHandle(child, prefix, out);
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
function chiHandle(node, prefix, out) {
|
|
3134
|
+
if (node.type === "call_expression") {
|
|
3135
|
+
const fn = node.childForFieldName("function");
|
|
3136
|
+
if (fn?.type === "selector_expression") {
|
|
3137
|
+
const field = fn.childForFieldName("field")?.text;
|
|
3138
|
+
const args = node.childForFieldName("arguments");
|
|
3139
|
+
if (field === "Route") {
|
|
3140
|
+
const leaf = goStringLiteral(args?.namedChild(0));
|
|
3141
|
+
const closure = args?.namedChild(1);
|
|
3142
|
+
if (leaf !== null && closure?.type === "func_literal") {
|
|
3143
|
+
const body = closure.childForFieldName("body");
|
|
3144
|
+
if (body) chiWalk(body, prefix + leaf, out);
|
|
3145
|
+
}
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
if (field === "Group") {
|
|
3149
|
+
const closure = args?.namedChild(0);
|
|
3150
|
+
if (closure?.type === "func_literal") {
|
|
3151
|
+
const body = closure.childForFieldName("body");
|
|
3152
|
+
if (body) chiWalk(body, prefix, out);
|
|
3153
|
+
}
|
|
3154
|
+
return;
|
|
3155
|
+
}
|
|
3156
|
+
if (field === "Mount") {
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
if (field && ROUTER_METHODS.has(field.toLowerCase())) {
|
|
3160
|
+
const leaf = goStringLiteral(args?.namedChild(0));
|
|
3161
|
+
if (leaf !== null) {
|
|
3162
|
+
out.push({
|
|
3163
|
+
method: field.toUpperCase(),
|
|
3164
|
+
pathTemplate: canonicalizeTemplate(stripChiRegex(prefix + leaf)),
|
|
3165
|
+
line: node.startPosition.row + 1,
|
|
3166
|
+
framework: "chi"
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
chiWalk(node, prefix, out);
|
|
3174
|
+
}
|
|
3175
|
+
function netHttpRoutesFromSource(source, parser) {
|
|
3176
|
+
const tree = parseSource2(parser, source);
|
|
3177
|
+
if (!goImportsNetHttp(tree.rootNode)) return [];
|
|
3178
|
+
const out = [];
|
|
3179
|
+
walk(tree.rootNode, (node) => {
|
|
3180
|
+
if (node.type !== "call_expression") return;
|
|
3181
|
+
const fn = node.childForFieldName("function");
|
|
3182
|
+
if (fn?.type !== "selector_expression") return;
|
|
3183
|
+
const field = fn.childForFieldName("field")?.text;
|
|
3184
|
+
if (field !== "HandleFunc" && field !== "Handle") return;
|
|
3185
|
+
const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
3186
|
+
if (leaf === null) return;
|
|
3187
|
+
const sp = leaf.indexOf(" ");
|
|
3188
|
+
if (sp < 0) return;
|
|
3189
|
+
const method = leaf.slice(0, sp);
|
|
3190
|
+
const rest = leaf.slice(sp + 1);
|
|
3191
|
+
if (!NET_HTTP_METHODS.has(method)) return;
|
|
3192
|
+
if (!rest.startsWith("/")) return;
|
|
3193
|
+
out.push({
|
|
3194
|
+
method,
|
|
3195
|
+
pathTemplate: canonicalizeTemplate(rest),
|
|
3196
|
+
line: node.startPosition.row + 1,
|
|
3197
|
+
framework: "net/http"
|
|
3198
|
+
});
|
|
3199
|
+
});
|
|
3200
|
+
return out;
|
|
3201
|
+
}
|
|
3202
|
+
function goImportsNetHttp(root) {
|
|
3203
|
+
let found = false;
|
|
3204
|
+
walk(root, (node) => {
|
|
3205
|
+
if (found || node.type !== "import_spec") return;
|
|
3206
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3207
|
+
const child = node.namedChild(i);
|
|
3208
|
+
if (goStringLiteral(child) === "net/http") found = true;
|
|
3209
|
+
}
|
|
3210
|
+
});
|
|
3211
|
+
return found;
|
|
3212
|
+
}
|
|
3101
3213
|
var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
|
|
3102
3214
|
var NESTJS_METHODS = /* @__PURE__ */ new Map([
|
|
3103
3215
|
["Get", "GET"],
|
|
@@ -4363,9 +4475,11 @@ async function addRoutes(graph, services) {
|
|
|
4363
4475
|
const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
|
|
4364
4476
|
const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
|
|
4365
4477
|
const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
|
|
4478
|
+
const hasChi = deps["github.com/go-chi/chi/v5"] !== void 0 || deps["github.com/go-chi/chi"] !== void 0;
|
|
4479
|
+
const isGoService = service.node.language === "go";
|
|
4366
4480
|
const hasRails = deps["rails"] !== void 0;
|
|
4367
4481
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4368
|
-
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
|
|
4482
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
|
|
4369
4483
|
continue;
|
|
4370
4484
|
const files = await loadSourceFiles(service.dir);
|
|
4371
4485
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4392,7 +4506,9 @@ async function addRoutes(graph, services) {
|
|
|
4392
4506
|
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4393
4507
|
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
4394
4508
|
else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
|
|
4509
|
+
else if (hasChi) routes = chiRoutesFromSource(file.content, goParser);
|
|
4395
4510
|
else routes = [];
|
|
4511
|
+
routes = routes.concat(netHttpRoutesFromSource(file.content, goParser));
|
|
4396
4512
|
} else if (isPy) {
|
|
4397
4513
|
routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
|
|
4398
4514
|
if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
|
|
@@ -4765,10 +4881,15 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
4765
4881
|
}
|
|
4766
4882
|
if (!entry) return null;
|
|
4767
4883
|
try {
|
|
4768
|
-
const
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4884
|
+
const queryLine = line !== void 0 && Number.isFinite(line) ? line : 1;
|
|
4885
|
+
let pos = entry.consumer.originalPositionFor({ line: queryLine, column: 0 });
|
|
4886
|
+
if (!pos || !pos.source) {
|
|
4887
|
+
pos = entry.consumer.originalPositionFor({
|
|
4888
|
+
line: queryLine,
|
|
4889
|
+
column: 0,
|
|
4890
|
+
bias: sourceMapJs.SourceMapConsumer.LEAST_UPPER_BOUND
|
|
4891
|
+
});
|
|
4892
|
+
}
|
|
4772
4893
|
if (!pos || !pos.source) return null;
|
|
4773
4894
|
const root = entry.consumer.sourceRoot ?? "";
|
|
4774
4895
|
const resolved = import_node_path8.default.resolve(entry.dir, root, pos.source);
|
|
@@ -4777,6 +4898,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
4777
4898
|
return null;
|
|
4778
4899
|
}
|
|
4779
4900
|
}
|
|
4901
|
+
function hasAdjacentSourceMap(absFilepath) {
|
|
4902
|
+
return sourceMapCache.get(absFilepath) != null;
|
|
4903
|
+
}
|
|
4780
4904
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
4781
4905
|
const filepath = codeFilepathOf(span.attributes);
|
|
4782
4906
|
if (filepath === void 0) return null;
|
|
@@ -4792,7 +4916,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
4792
4916
|
}
|
|
4793
4917
|
const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath);
|
|
4794
4918
|
if (!relPath) return null;
|
|
4795
|
-
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
4919
|
+
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && !hasAdjacentSourceMap(abs) && serviceNode?.name) {
|
|
4796
4920
|
warnNoSourceMaps(serviceNode.name);
|
|
4797
4921
|
}
|
|
4798
4922
|
const fn = codeFunctionOf(span.attributes);
|
|
@@ -5068,7 +5192,7 @@ function resolveServiceId(graph, host, env) {
|
|
|
5068
5192
|
function frontierIdFor(host) {
|
|
5069
5193
|
return (0, import_types8.frontierId)(host);
|
|
5070
5194
|
}
|
|
5071
|
-
function
|
|
5195
|
+
function resolveFusedServiceId(graph, serviceName, env) {
|
|
5072
5196
|
const id = (0, import_types8.serviceId)(serviceName, env);
|
|
5073
5197
|
if (graph.hasNode(id)) return id;
|
|
5074
5198
|
const wanted = serviceName.toLowerCase();
|
|
@@ -5078,17 +5202,21 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
5078
5202
|
if (svc.discoveredVia === "otel") return false;
|
|
5079
5203
|
return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
|
|
5080
5204
|
});
|
|
5081
|
-
|
|
5205
|
+
return extractedId ?? id;
|
|
5206
|
+
}
|
|
5207
|
+
function ensureServiceNode(graph, serviceName, env) {
|
|
5208
|
+
const resolved = resolveFusedServiceId(graph, serviceName, env);
|
|
5209
|
+
if (graph.hasNode(resolved)) return resolved;
|
|
5082
5210
|
const node = {
|
|
5083
|
-
id,
|
|
5211
|
+
id: resolved,
|
|
5084
5212
|
type: import_types8.NodeType.ServiceNode,
|
|
5085
5213
|
name: serviceName,
|
|
5086
5214
|
language: "unknown",
|
|
5087
5215
|
discoveredVia: "otel",
|
|
5088
5216
|
...env !== "unknown" ? { env } : {}
|
|
5089
5217
|
};
|
|
5090
|
-
graph.addNode(
|
|
5091
|
-
return
|
|
5218
|
+
graph.addNode(resolved, node);
|
|
5219
|
+
return resolved;
|
|
5092
5220
|
}
|
|
5093
5221
|
function ensureInfraNode(graph, kind, name, provider) {
|
|
5094
5222
|
const id = (0, import_types8.infraId)(kind, name);
|
|
@@ -5282,7 +5410,7 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
5282
5410
|
await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
5283
5411
|
}
|
|
5284
5412
|
function incidentAffectedNode(span, graph, scanPath) {
|
|
5285
|
-
const sid = (0, import_types8.serviceId)(span.service, span.env);
|
|
5413
|
+
const sid = graph ? resolveFusedServiceId(graph, span.service, span.env) : (0, import_types8.serviceId)(span.service, span.env);
|
|
5286
5414
|
const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
|
|
5287
5415
|
const callSite = callSiteFromSpan(span, serviceNode, scanPath);
|
|
5288
5416
|
if (callSite) {
|
|
@@ -6006,6 +6134,7 @@ function goFramework(deps) {
|
|
|
6006
6134
|
if (deps["github.com/gin-gonic/gin"]) return "gin";
|
|
6007
6135
|
if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
|
|
6008
6136
|
if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
|
|
6137
|
+
if (deps["github.com/go-chi/chi/v5"] || deps["github.com/go-chi/chi"]) return "chi";
|
|
6009
6138
|
return void 0;
|
|
6010
6139
|
}
|
|
6011
6140
|
async function discoverGoService(scanPath, dir) {
|
|
@@ -6683,7 +6812,7 @@ function disambiguate(defs) {
|
|
|
6683
6812
|
}
|
|
6684
6813
|
async function addSymbols(graph, services) {
|
|
6685
6814
|
const parsers = /* @__PURE__ */ new Map();
|
|
6686
|
-
const
|
|
6815
|
+
const parserForExt5 = (ext) => {
|
|
6687
6816
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6688
6817
|
if (!grammar) return null;
|
|
6689
6818
|
let parser = parsers.get(ext);
|
|
@@ -6699,7 +6828,7 @@ async function addSymbols(graph, services) {
|
|
|
6699
6828
|
for (const service of services) {
|
|
6700
6829
|
const files = await loadSourceFiles(service.dir);
|
|
6701
6830
|
for (const file of files) {
|
|
6702
|
-
const parser =
|
|
6831
|
+
const parser = parserForExt5(import_node_path17.default.extname(file.path));
|
|
6703
6832
|
if (!parser) continue;
|
|
6704
6833
|
const relPath = toPosix(import_node_path17.default.relative(service.dir, file.path));
|
|
6705
6834
|
let defs;
|
|
@@ -6851,7 +6980,7 @@ function stringInner(node) {
|
|
|
6851
6980
|
}
|
|
6852
6981
|
async function addSymbolEdges(graph, services) {
|
|
6853
6982
|
const parsers = /* @__PURE__ */ new Map();
|
|
6854
|
-
const
|
|
6983
|
+
const parserForExt5 = (ext) => {
|
|
6855
6984
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6856
6985
|
if (!grammar) return null;
|
|
6857
6986
|
let parser = parsers.get(ext);
|
|
@@ -6868,7 +6997,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6868
6997
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
6869
6998
|
const files = await loadSourceFiles(service.dir);
|
|
6870
6999
|
for (const file of files) {
|
|
6871
|
-
const parser =
|
|
7000
|
+
const parser = parserForExt5(import_node_path18.default.extname(file.path));
|
|
6872
7001
|
if (!parser) continue;
|
|
6873
7002
|
const relPath = toPosix(import_node_path18.default.relative(service.dir, file.path));
|
|
6874
7003
|
const fileDir = import_node_path18.default.dirname(file.path);
|
|
@@ -7130,7 +7259,7 @@ function firstReferenceLines(root, wanted) {
|
|
|
7130
7259
|
}
|
|
7131
7260
|
async function addServerActions(graph, services) {
|
|
7132
7261
|
const parsers = /* @__PURE__ */ new Map();
|
|
7133
|
-
const
|
|
7262
|
+
const parserForExt5 = (ext) => {
|
|
7134
7263
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
7135
7264
|
if (!grammar) return null;
|
|
7136
7265
|
let parser = parsers.get(ext);
|
|
@@ -7153,7 +7282,7 @@ async function addServerActions(graph, services) {
|
|
|
7153
7282
|
const files = await loadSourceFiles(service.dir);
|
|
7154
7283
|
for (const file of files) {
|
|
7155
7284
|
if (isTestPath(file.path)) continue;
|
|
7156
|
-
const parser =
|
|
7285
|
+
const parser = parserForExt5(import_node_path19.default.extname(file.path));
|
|
7157
7286
|
if (!parser) continue;
|
|
7158
7287
|
const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
|
|
7159
7288
|
let root;
|
|
@@ -7216,7 +7345,7 @@ async function addServerActions(graph, services) {
|
|
|
7216
7345
|
}
|
|
7217
7346
|
for (const file of files) {
|
|
7218
7347
|
if (isTestPath(file.path)) continue;
|
|
7219
|
-
const parser =
|
|
7348
|
+
const parser = parserForExt5(import_node_path19.default.extname(file.path));
|
|
7220
7349
|
if (!parser) continue;
|
|
7221
7350
|
const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
|
|
7222
7351
|
const fileDir = import_node_path19.default.dirname(file.path);
|
|
@@ -8180,6 +8309,7 @@ init_cjs_shims();
|
|
|
8180
8309
|
var import_node_path30 = __toESM(require("path"), 1);
|
|
8181
8310
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
8182
8311
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
8312
|
+
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
8183
8313
|
var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
|
|
8184
8314
|
var import_types20 = require("@neat.is/types");
|
|
8185
8315
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
@@ -8230,19 +8360,27 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
8230
8360
|
}
|
|
8231
8361
|
return out;
|
|
8232
8362
|
}
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8363
|
+
var GRAMMAR_BY_EXT2 = {
|
|
8364
|
+
".ts": import_tree_sitter_typescript2.default.typescript,
|
|
8365
|
+
".tsx": import_tree_sitter_typescript2.default.tsx,
|
|
8366
|
+
".js": import_tree_sitter_javascript4.default,
|
|
8367
|
+
".jsx": import_tree_sitter_javascript4.default,
|
|
8368
|
+
".mjs": import_tree_sitter_javascript4.default,
|
|
8369
|
+
".cjs": import_tree_sitter_javascript4.default,
|
|
8370
|
+
".py": import_tree_sitter_python3.default
|
|
8371
|
+
};
|
|
8372
|
+
function parserForExt(ext, cache) {
|
|
8373
|
+
const grammar = GRAMMAR_BY_EXT2[ext] ?? import_tree_sitter_javascript4.default;
|
|
8374
|
+
let parser = cache.get(grammar);
|
|
8375
|
+
if (!parser) {
|
|
8376
|
+
parser = new import_tree_sitter6.default();
|
|
8377
|
+
parser.setLanguage(grammar);
|
|
8378
|
+
cache.set(grammar, parser);
|
|
8379
|
+
}
|
|
8380
|
+
return parser;
|
|
8242
8381
|
}
|
|
8243
8382
|
async function addHttpCallEdges(graph, services) {
|
|
8244
|
-
const
|
|
8245
|
-
const pyParser = makePyParser3();
|
|
8383
|
+
const parserCache = /* @__PURE__ */ new Map();
|
|
8246
8384
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
8247
8385
|
let nodesAdded = 0;
|
|
8248
8386
|
let edgesAdded = 0;
|
|
@@ -8251,7 +8389,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
8251
8389
|
const seen = /* @__PURE__ */ new Set();
|
|
8252
8390
|
for (const file of files) {
|
|
8253
8391
|
if (isTestPath(file.path)) continue;
|
|
8254
|
-
const parser = import_node_path30.default.extname(file.path)
|
|
8392
|
+
const parser = parserForExt(import_node_path30.default.extname(file.path), parserCache);
|
|
8255
8393
|
let sites;
|
|
8256
8394
|
try {
|
|
8257
8395
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -8324,7 +8462,7 @@ function parseSource5(parser, source) {
|
|
|
8324
8462
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
8325
8463
|
);
|
|
8326
8464
|
}
|
|
8327
|
-
function
|
|
8465
|
+
function makeJsParser3() {
|
|
8328
8466
|
const p = new import_tree_sitter7.default();
|
|
8329
8467
|
p.setLanguage(import_tree_sitter_javascript5.default);
|
|
8330
8468
|
return p;
|
|
@@ -8502,7 +8640,7 @@ function findRoute(entries, method, normalizedPath) {
|
|
|
8502
8640
|
);
|
|
8503
8641
|
}
|
|
8504
8642
|
async function addRouteCallEdges(graph, services) {
|
|
8505
|
-
const jsParser =
|
|
8643
|
+
const jsParser = makeJsParser3();
|
|
8506
8644
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
8507
8645
|
const routeIndex = buildRouteIndex(graph);
|
|
8508
8646
|
if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
@@ -8894,7 +9032,7 @@ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"),
|
|
|
8894
9032
|
var import_types27 = require("@neat.is/types");
|
|
8895
9033
|
var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
|
|
8896
9034
|
var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
|
|
8897
|
-
function
|
|
9035
|
+
function parserForExt2(ext) {
|
|
8898
9036
|
const p = new import_tree_sitter8.default();
|
|
8899
9037
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
|
|
8900
9038
|
return p;
|
|
@@ -9059,7 +9197,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9059
9197
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
9060
9198
|
if (!hasClient && !hasAdmin) return [];
|
|
9061
9199
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
9062
|
-
const tree = parseSource3(
|
|
9200
|
+
const tree = parseSource3(parserForExt2(import_node_path37.default.extname(file.path)), file.content);
|
|
9063
9201
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
9064
9202
|
const collLine = /* @__PURE__ */ new Map();
|
|
9065
9203
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -9476,7 +9614,7 @@ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
|
9476
9614
|
var import_types29 = require("@neat.is/types");
|
|
9477
9615
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
9478
9616
|
var PARSE_CHUNK6 = 16384;
|
|
9479
|
-
function
|
|
9617
|
+
function makePyParser3() {
|
|
9480
9618
|
const p = new import_tree_sitter9.default();
|
|
9481
9619
|
p.setLanguage(import_tree_sitter_python4.default);
|
|
9482
9620
|
return p;
|
|
@@ -9583,7 +9721,7 @@ function foreignKeyParentTable(call) {
|
|
|
9583
9721
|
}
|
|
9584
9722
|
function sqlalchemyForeignKeys(file, serviceDir) {
|
|
9585
9723
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
9586
|
-
const tree = parseSource6(
|
|
9724
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
9587
9725
|
const out = [];
|
|
9588
9726
|
const seen = /* @__PURE__ */ new Set();
|
|
9589
9727
|
walk3(tree.rootNode, (node) => {
|
|
@@ -9620,7 +9758,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
9620
9758
|
}
|
|
9621
9759
|
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
9622
9760
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
9623
|
-
const tree = parseSource6(
|
|
9761
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
9624
9762
|
const out = [];
|
|
9625
9763
|
const seen = /* @__PURE__ */ new Set();
|
|
9626
9764
|
const push = (name, line, columns) => {
|
|
@@ -9672,7 +9810,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
9672
9810
|
function buildSqlalchemyModelRegistry(files) {
|
|
9673
9811
|
const table = /* @__PURE__ */ new Map();
|
|
9674
9812
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
9675
|
-
const parser =
|
|
9813
|
+
const parser = makePyParser3();
|
|
9676
9814
|
for (const file of files) {
|
|
9677
9815
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) continue;
|
|
9678
9816
|
const tree = parseSource6(parser, file.content);
|
|
@@ -9721,7 +9859,7 @@ function importsModelName(content, name) {
|
|
|
9721
9859
|
function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
9722
9860
|
const registry = buildSqlalchemyModelRegistry(files);
|
|
9723
9861
|
if (registry.size === 0) return [];
|
|
9724
|
-
const parser =
|
|
9862
|
+
const parser = makePyParser3();
|
|
9725
9863
|
const out = [];
|
|
9726
9864
|
const seen = /* @__PURE__ */ new Set();
|
|
9727
9865
|
for (const file of files) {
|
|
@@ -9759,7 +9897,7 @@ var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
|
9759
9897
|
var import_types30 = require("@neat.is/types");
|
|
9760
9898
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
9761
9899
|
var PARSE_CHUNK7 = 16384;
|
|
9762
|
-
function
|
|
9900
|
+
function makePyParser4() {
|
|
9763
9901
|
const p = new import_tree_sitter10.default();
|
|
9764
9902
|
p.setLanguage(import_tree_sitter_python5.default);
|
|
9765
9903
|
return p;
|
|
@@ -9820,7 +9958,7 @@ function readMeta(body) {
|
|
|
9820
9958
|
}
|
|
9821
9959
|
function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
9822
9960
|
if (!DJANGO_IMPORT_RE.test(file.content)) return [];
|
|
9823
|
-
const tree = parseSource7(
|
|
9961
|
+
const tree = parseSource7(makePyParser4(), file.content);
|
|
9824
9962
|
const out = [];
|
|
9825
9963
|
const seen = /* @__PURE__ */ new Set();
|
|
9826
9964
|
const defaultAppLabel = import_node_path40.default.basename(import_node_path40.default.dirname(file.path));
|
|
@@ -9855,7 +9993,7 @@ var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"),
|
|
|
9855
9993
|
var import_types31 = require("@neat.is/types");
|
|
9856
9994
|
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
9857
9995
|
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
9858
|
-
function
|
|
9996
|
+
function parserForExt3(ext) {
|
|
9859
9997
|
const p = new import_tree_sitter11.default();
|
|
9860
9998
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
|
|
9861
9999
|
return p;
|
|
@@ -9927,7 +10065,7 @@ function columnsFromObject(obj) {
|
|
|
9927
10065
|
}
|
|
9928
10066
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
9929
10067
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9930
|
-
const tree = parseSource3(
|
|
10068
|
+
const tree = parseSource3(parserForExt3(import_node_path41.default.extname(file.path)), file.content);
|
|
9931
10069
|
const out = [];
|
|
9932
10070
|
const seen = /* @__PURE__ */ new Set();
|
|
9933
10071
|
const walk9 = (node) => {
|
|
@@ -10016,7 +10154,7 @@ function referencesTargetVar(call) {
|
|
|
10016
10154
|
}
|
|
10017
10155
|
function drizzleForeignKeys(file, serviceDir) {
|
|
10018
10156
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
10019
|
-
const tree = parseSource3(
|
|
10157
|
+
const tree = parseSource3(parserForExt3(import_node_path41.default.extname(file.path)), file.content);
|
|
10020
10158
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
10021
10159
|
const out = [];
|
|
10022
10160
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -12723,7 +12861,7 @@ var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"),
|
|
|
12723
12861
|
var import_types47 = require("@neat.is/types");
|
|
12724
12862
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
12725
12863
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
12726
|
-
function
|
|
12864
|
+
function parserForExt4(ext) {
|
|
12727
12865
|
const p = new import_tree_sitter16.default();
|
|
12728
12866
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
12729
12867
|
return p;
|
|
@@ -12812,7 +12950,7 @@ function topLevelSchemas(root) {
|
|
|
12812
12950
|
}
|
|
12813
12951
|
function zodShapesFromFile(file, serviceDir) {
|
|
12814
12952
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
12815
|
-
const tree = parseSource3(
|
|
12953
|
+
const tree = parseSource3(parserForExt4(import_node_path57.default.extname(file.path)), file.content);
|
|
12816
12954
|
const out = [];
|
|
12817
12955
|
const seen = /* @__PURE__ */ new Set();
|
|
12818
12956
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -18830,6 +18968,19 @@ async function startDaemon(opts = {}) {
|
|
|
18830
18968
|
let otlpAddress = "";
|
|
18831
18969
|
let daemonRecord = null;
|
|
18832
18970
|
if (bind) {
|
|
18971
|
+
let bareSpanIsRoutable2 = function(serviceName) {
|
|
18972
|
+
if (singleProject) {
|
|
18973
|
+
const slot = slots.get(singleProject);
|
|
18974
|
+
if (!slot) {
|
|
18975
|
+
return !serviceName || serviceNameMatchesProject(serviceName, singleProject);
|
|
18976
|
+
}
|
|
18977
|
+
return spanBelongsToSingleProject(slot.graph, singleProject, serviceName);
|
|
18978
|
+
}
|
|
18979
|
+
const entries = [...slots.values()].map((s) => s.entry);
|
|
18980
|
+
const target = routeSpanToProject(serviceName, entries);
|
|
18981
|
+
return slots.has(target) || slots.has(DEFAULT_PROJECT);
|
|
18982
|
+
};
|
|
18983
|
+
var bareSpanIsRoutable = bareSpanIsRoutable2;
|
|
18833
18984
|
const auth = readAuthEnv();
|
|
18834
18985
|
const host = resolveHost(opts, Boolean(auth.authToken));
|
|
18835
18986
|
const restPort = resolveRestPort(opts);
|
|
@@ -18927,6 +19078,20 @@ async function startDaemon(opts = {}) {
|
|
|
18927
19078
|
const liveEntries = await listProjects().catch(() => []);
|
|
18928
19079
|
let slot = slots.get(project);
|
|
18929
19080
|
if (!slot) {
|
|
19081
|
+
if (singleProject && project === singleProject) {
|
|
19082
|
+
slot = await tryRecoverSlot({
|
|
19083
|
+
name: singleProject,
|
|
19084
|
+
path: singleProjectPath,
|
|
19085
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19086
|
+
languages: [],
|
|
19087
|
+
status: "active"
|
|
19088
|
+
});
|
|
19089
|
+
if (!slot || slot.status !== "active") {
|
|
19090
|
+
warnDroppedSpan(singleProject, slot?.errorReason ?? "unknown");
|
|
19091
|
+
return null;
|
|
19092
|
+
}
|
|
19093
|
+
return slot;
|
|
19094
|
+
}
|
|
18930
19095
|
await recordUnroutedSpan(serviceName, traceId);
|
|
18931
19096
|
return null;
|
|
18932
19097
|
}
|
|
@@ -18993,7 +19158,30 @@ async function startDaemon(opts = {}) {
|
|
|
18993
19158
|
// host, rather than accepting it and dropping the batch. `slots` covers
|
|
18994
19159
|
// active/recovering projects, `bootstrapStatus` the ones still
|
|
18995
19160
|
// extracting; a foreign or wrong-cased project name matches neither.
|
|
18996
|
-
|
|
19161
|
+
// A single-project daemon owns exactly one project by definition, so its
|
|
19162
|
+
// own name always counts as registered even before loadAll populates the
|
|
19163
|
+
// slot — otherwise a scoped span arriving during cold-start would 404 and
|
|
19164
|
+
// be lost (OTLP does not retry 4xx). resolveSlotByName then builds it (#879).
|
|
19165
|
+
isProjectRegistered: (project) => slots.has(project) || bootstrapStatus.has(project) || singleProject !== void 0 && project === singleProject,
|
|
19166
|
+
// #881 — the bare `/v1/traces` route replies before the span is routed
|
|
19167
|
+
// (off the queue), so tell the receiver, per batch, how many spans will
|
|
19168
|
+
// land on no project. It keeps the 200 but reports those as
|
|
19169
|
+
// partialSuccess.rejectedSpans instead of an empty partialSuccess that an
|
|
19170
|
+
// exporter reads as full acceptance. Pure — the drop + unrouted-ledger
|
|
19171
|
+
// write still happen on the async onSpan path.
|
|
19172
|
+
classifyBareRoutability: (spans) => {
|
|
19173
|
+
let rejected = 0;
|
|
19174
|
+
for (const span of spans) {
|
|
19175
|
+
if (!bareSpanIsRoutable2(span.service)) rejected++;
|
|
19176
|
+
}
|
|
19177
|
+
if (rejected === 0) return { rejected: 0 };
|
|
19178
|
+
const noun = rejected === 1 ? "span" : "spans";
|
|
19179
|
+
const verb = rejected === 1 ? "was" : "were";
|
|
19180
|
+
return {
|
|
19181
|
+
rejected,
|
|
19182
|
+
message: `${rejected} ${noun} matched no project on this daemon and ${verb} dropped. Export to /projects/<project>/v1/traces, or check the exporter's service.name.`
|
|
19183
|
+
};
|
|
19184
|
+
}
|
|
18997
19185
|
});
|
|
18998
19186
|
otlpAddress = await listenSteppingOtlp(otlpApp, otlpPort, host);
|
|
18999
19187
|
console.log(`neatd: OTLP listening on ${otlpAddress}/v1/traces`);
|