@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.d.cts
CHANGED
|
@@ -302,6 +302,10 @@ interface BuildOtelReceiverOptions {
|
|
|
302
302
|
onProjectSpan?: ProjectSpanHandler;
|
|
303
303
|
onProjectErrorSpanSync?: (project: string, span: ParsedSpan) => Promise<void>;
|
|
304
304
|
isProjectRegistered?: (project: string) => boolean;
|
|
305
|
+
classifyBareRoutability?: (spans: ParsedSpan[]) => {
|
|
306
|
+
rejected: number;
|
|
307
|
+
message?: string;
|
|
308
|
+
};
|
|
305
309
|
bodyLimit?: number;
|
|
306
310
|
authToken?: string;
|
|
307
311
|
trustProxy?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -302,6 +302,10 @@ interface BuildOtelReceiverOptions {
|
|
|
302
302
|
onProjectSpan?: ProjectSpanHandler;
|
|
303
303
|
onProjectErrorSpanSync?: (project: string, span: ParsedSpan) => Promise<void>;
|
|
304
304
|
isProjectRegistered?: (project: string) => boolean;
|
|
305
|
+
classifyBareRoutability?: (spans: ParsedSpan[]) => {
|
|
306
|
+
rejected: number;
|
|
307
|
+
message?: string;
|
|
308
|
+
};
|
|
305
309
|
bodyLimit?: number;
|
|
306
310
|
authToken?: string;
|
|
307
311
|
trustProxy?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeSpanToProject,
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ILG3SMD5.js";
|
|
5
5
|
import {
|
|
6
6
|
ProjectNameCollisionError,
|
|
7
7
|
addProject,
|
|
@@ -37,15 +37,15 @@ import {
|
|
|
37
37
|
thresholdForEdgeType,
|
|
38
38
|
touchLastSeen,
|
|
39
39
|
writeAtomically
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-N5TPODCX.js";
|
|
41
41
|
import {
|
|
42
42
|
startOtelGrpcReceiver
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-FCO5Z3RW.js";
|
|
44
44
|
import {
|
|
45
45
|
buildOtelReceiver,
|
|
46
46
|
logSpanHandler,
|
|
47
47
|
parseOtlpRequest
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-UUYCTH2E.js";
|
|
49
49
|
export {
|
|
50
50
|
ProjectNameCollisionError,
|
|
51
51
|
addProject,
|
package/dist/neatd.cjs
CHANGED
|
@@ -504,13 +504,18 @@ function loadProtobufResponseEncoder() {
|
|
|
504
504
|
);
|
|
505
505
|
return exportTraceServiceResponseType;
|
|
506
506
|
}
|
|
507
|
-
function encodeProtobufResponseBody() {
|
|
508
|
-
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
507
|
+
function encodeProtobufResponseBody(rejected, message) {
|
|
509
508
|
const Type = loadProtobufResponseEncoder();
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
509
|
+
if (!rejected) {
|
|
510
|
+
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
511
|
+
const msg2 = Type.create({});
|
|
512
|
+
cachedProtobufResponseBody = Buffer.from(Type.encode(msg2).finish());
|
|
513
|
+
return cachedProtobufResponseBody;
|
|
514
|
+
}
|
|
515
|
+
const msg = Type.fromObject({
|
|
516
|
+
partial_success: { rejected_spans: rejected, error_message: message ?? "" }
|
|
517
|
+
});
|
|
518
|
+
return Buffer.from(Type.encode(msg).finish());
|
|
514
519
|
}
|
|
515
520
|
async function decodeProtobufBody(buf) {
|
|
516
521
|
const Type = loadProtobufDecoder();
|
|
@@ -625,6 +630,13 @@ async function buildOtelReceiver(opts) {
|
|
|
625
630
|
}
|
|
626
631
|
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: {} });
|
|
627
632
|
}
|
|
633
|
+
function sendOtlpPartial(reply, flavor, rejected, message) {
|
|
634
|
+
if (flavor === "protobuf") {
|
|
635
|
+
const buf = encodeProtobufResponseBody(rejected, message);
|
|
636
|
+
return reply.code(200).header("content-type", "application/x-protobuf").send(buf);
|
|
637
|
+
}
|
|
638
|
+
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: { rejectedSpans: rejected, errorMessage: message } });
|
|
639
|
+
}
|
|
628
640
|
app.addContentTypeParser(
|
|
629
641
|
"application/x-protobuf",
|
|
630
642
|
{ parseAs: "buffer", bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024 },
|
|
@@ -652,6 +664,10 @@ async function buildOtelReceiver(opts) {
|
|
|
652
664
|
}
|
|
653
665
|
}
|
|
654
666
|
enqueue(spans);
|
|
667
|
+
if (opts.classifyBareRoutability) {
|
|
668
|
+
const { rejected, message } = opts.classifyBareRoutability(spans);
|
|
669
|
+
if (rejected > 0) return sendOtlpPartial(reply, result.flavor, rejected, message);
|
|
670
|
+
}
|
|
655
671
|
return sendOtlpSuccess(reply, result.flavor);
|
|
656
672
|
});
|
|
657
673
|
app.post("/projects/:project/v1/traces", async (req2, reply) => {
|
|
@@ -3008,6 +3024,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
|
|
|
3008
3024
|
"all"
|
|
3009
3025
|
]);
|
|
3010
3026
|
var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
3027
|
+
var NET_HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
3011
3028
|
var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
|
|
3012
3029
|
function goRouterRoutesFromSource(source, parser, framework) {
|
|
3013
3030
|
const tree = parseSource2(parser, source);
|
|
@@ -3061,6 +3078,101 @@ function echoRoutesFromSource(source, parser) {
|
|
|
3061
3078
|
function fiberRoutesFromSource(source, parser) {
|
|
3062
3079
|
return goRouterRoutesFromSource(source, parser, "fiber");
|
|
3063
3080
|
}
|
|
3081
|
+
function chiRoutesFromSource(source, parser) {
|
|
3082
|
+
const tree = parseSource2(parser, source);
|
|
3083
|
+
const out = [];
|
|
3084
|
+
chiWalk(tree.rootNode, "", out);
|
|
3085
|
+
return out;
|
|
3086
|
+
}
|
|
3087
|
+
function stripChiRegex(path70) {
|
|
3088
|
+
return path70.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
|
|
3089
|
+
}
|
|
3090
|
+
function chiWalk(node, prefix, out) {
|
|
3091
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3092
|
+
const child = node.namedChild(i);
|
|
3093
|
+
if (child) chiHandle(child, prefix, out);
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
function chiHandle(node, prefix, out) {
|
|
3097
|
+
if (node.type === "call_expression") {
|
|
3098
|
+
const fn = node.childForFieldName("function");
|
|
3099
|
+
if (fn?.type === "selector_expression") {
|
|
3100
|
+
const field = fn.childForFieldName("field")?.text;
|
|
3101
|
+
const args = node.childForFieldName("arguments");
|
|
3102
|
+
if (field === "Route") {
|
|
3103
|
+
const leaf = goStringLiteral(args?.namedChild(0));
|
|
3104
|
+
const closure = args?.namedChild(1);
|
|
3105
|
+
if (leaf !== null && closure?.type === "func_literal") {
|
|
3106
|
+
const body = closure.childForFieldName("body");
|
|
3107
|
+
if (body) chiWalk(body, prefix + leaf, out);
|
|
3108
|
+
}
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
if (field === "Group") {
|
|
3112
|
+
const closure = args?.namedChild(0);
|
|
3113
|
+
if (closure?.type === "func_literal") {
|
|
3114
|
+
const body = closure.childForFieldName("body");
|
|
3115
|
+
if (body) chiWalk(body, prefix, out);
|
|
3116
|
+
}
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
if (field === "Mount") {
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
if (field && ROUTER_METHODS.has(field.toLowerCase())) {
|
|
3123
|
+
const leaf = goStringLiteral(args?.namedChild(0));
|
|
3124
|
+
if (leaf !== null) {
|
|
3125
|
+
out.push({
|
|
3126
|
+
method: field.toUpperCase(),
|
|
3127
|
+
pathTemplate: canonicalizeTemplate(stripChiRegex(prefix + leaf)),
|
|
3128
|
+
line: node.startPosition.row + 1,
|
|
3129
|
+
framework: "chi"
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
chiWalk(node, prefix, out);
|
|
3137
|
+
}
|
|
3138
|
+
function netHttpRoutesFromSource(source, parser) {
|
|
3139
|
+
const tree = parseSource2(parser, source);
|
|
3140
|
+
if (!goImportsNetHttp(tree.rootNode)) return [];
|
|
3141
|
+
const out = [];
|
|
3142
|
+
walk(tree.rootNode, (node) => {
|
|
3143
|
+
if (node.type !== "call_expression") return;
|
|
3144
|
+
const fn = node.childForFieldName("function");
|
|
3145
|
+
if (fn?.type !== "selector_expression") return;
|
|
3146
|
+
const field = fn.childForFieldName("field")?.text;
|
|
3147
|
+
if (field !== "HandleFunc" && field !== "Handle") return;
|
|
3148
|
+
const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
|
|
3149
|
+
if (leaf === null) return;
|
|
3150
|
+
const sp = leaf.indexOf(" ");
|
|
3151
|
+
if (sp < 0) return;
|
|
3152
|
+
const method = leaf.slice(0, sp);
|
|
3153
|
+
const rest = leaf.slice(sp + 1);
|
|
3154
|
+
if (!NET_HTTP_METHODS.has(method)) return;
|
|
3155
|
+
if (!rest.startsWith("/")) return;
|
|
3156
|
+
out.push({
|
|
3157
|
+
method,
|
|
3158
|
+
pathTemplate: canonicalizeTemplate(rest),
|
|
3159
|
+
line: node.startPosition.row + 1,
|
|
3160
|
+
framework: "net/http"
|
|
3161
|
+
});
|
|
3162
|
+
});
|
|
3163
|
+
return out;
|
|
3164
|
+
}
|
|
3165
|
+
function goImportsNetHttp(root) {
|
|
3166
|
+
let found = false;
|
|
3167
|
+
walk(root, (node) => {
|
|
3168
|
+
if (found || node.type !== "import_spec") return;
|
|
3169
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
3170
|
+
const child = node.namedChild(i);
|
|
3171
|
+
if (goStringLiteral(child) === "net/http") found = true;
|
|
3172
|
+
}
|
|
3173
|
+
});
|
|
3174
|
+
return found;
|
|
3175
|
+
}
|
|
3064
3176
|
var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
|
|
3065
3177
|
var NESTJS_METHODS = /* @__PURE__ */ new Map([
|
|
3066
3178
|
["Get", "GET"],
|
|
@@ -4326,9 +4438,11 @@ async function addRoutes(graph, services) {
|
|
|
4326
4438
|
const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
|
|
4327
4439
|
const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
|
|
4328
4440
|
const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
|
|
4441
|
+
const hasChi = deps["github.com/go-chi/chi/v5"] !== void 0 || deps["github.com/go-chi/chi"] !== void 0;
|
|
4442
|
+
const isGoService = service.node.language === "go";
|
|
4329
4443
|
const hasRails = deps["rails"] !== void 0;
|
|
4330
4444
|
const hasLaravel = deps["laravel/framework"] !== void 0;
|
|
4331
|
-
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
|
|
4445
|
+
if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
|
|
4332
4446
|
continue;
|
|
4333
4447
|
const files = await loadSourceFiles(service.dir);
|
|
4334
4448
|
const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
|
|
@@ -4355,7 +4469,9 @@ async function addRoutes(graph, services) {
|
|
|
4355
4469
|
if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
|
|
4356
4470
|
else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
|
|
4357
4471
|
else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
|
|
4472
|
+
else if (hasChi) routes = chiRoutesFromSource(file.content, goParser);
|
|
4358
4473
|
else routes = [];
|
|
4474
|
+
routes = routes.concat(netHttpRoutesFromSource(file.content, goParser));
|
|
4359
4475
|
} else if (isPy) {
|
|
4360
4476
|
routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
|
|
4361
4477
|
if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
|
|
@@ -4728,10 +4844,15 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
4728
4844
|
}
|
|
4729
4845
|
if (!entry2) return null;
|
|
4730
4846
|
try {
|
|
4731
|
-
const
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4847
|
+
const queryLine = line !== void 0 && Number.isFinite(line) ? line : 1;
|
|
4848
|
+
let pos = entry2.consumer.originalPositionFor({ line: queryLine, column: 0 });
|
|
4849
|
+
if (!pos || !pos.source) {
|
|
4850
|
+
pos = entry2.consumer.originalPositionFor({
|
|
4851
|
+
line: queryLine,
|
|
4852
|
+
column: 0,
|
|
4853
|
+
bias: sourceMapJs.SourceMapConsumer.LEAST_UPPER_BOUND
|
|
4854
|
+
});
|
|
4855
|
+
}
|
|
4735
4856
|
if (!pos || !pos.source) return null;
|
|
4736
4857
|
const root = entry2.consumer.sourceRoot ?? "";
|
|
4737
4858
|
const resolved = import_node_path8.default.resolve(entry2.dir, root, pos.source);
|
|
@@ -4740,6 +4861,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
4740
4861
|
return null;
|
|
4741
4862
|
}
|
|
4742
4863
|
}
|
|
4864
|
+
function hasAdjacentSourceMap(absFilepath) {
|
|
4865
|
+
return sourceMapCache.get(absFilepath) != null;
|
|
4866
|
+
}
|
|
4743
4867
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
4744
4868
|
const filepath = codeFilepathOf(span.attributes);
|
|
4745
4869
|
if (filepath === void 0) return null;
|
|
@@ -4755,7 +4879,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
4755
4879
|
}
|
|
4756
4880
|
const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath);
|
|
4757
4881
|
if (!relPath) return null;
|
|
4758
|
-
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
4882
|
+
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && !hasAdjacentSourceMap(abs) && serviceNode?.name) {
|
|
4759
4883
|
warnNoSourceMaps(serviceNode.name);
|
|
4760
4884
|
}
|
|
4761
4885
|
const fn = codeFunctionOf(span.attributes);
|
|
@@ -5031,7 +5155,7 @@ function resolveServiceId(graph, host, env) {
|
|
|
5031
5155
|
function frontierIdFor(host) {
|
|
5032
5156
|
return (0, import_types8.frontierId)(host);
|
|
5033
5157
|
}
|
|
5034
|
-
function
|
|
5158
|
+
function resolveFusedServiceId(graph, serviceName, env) {
|
|
5035
5159
|
const id = (0, import_types8.serviceId)(serviceName, env);
|
|
5036
5160
|
if (graph.hasNode(id)) return id;
|
|
5037
5161
|
const wanted = serviceName.toLowerCase();
|
|
@@ -5041,17 +5165,21 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
5041
5165
|
if (svc.discoveredVia === "otel") return false;
|
|
5042
5166
|
return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
|
|
5043
5167
|
});
|
|
5044
|
-
|
|
5168
|
+
return extractedId ?? id;
|
|
5169
|
+
}
|
|
5170
|
+
function ensureServiceNode(graph, serviceName, env) {
|
|
5171
|
+
const resolved = resolveFusedServiceId(graph, serviceName, env);
|
|
5172
|
+
if (graph.hasNode(resolved)) return resolved;
|
|
5045
5173
|
const node = {
|
|
5046
|
-
id,
|
|
5174
|
+
id: resolved,
|
|
5047
5175
|
type: import_types8.NodeType.ServiceNode,
|
|
5048
5176
|
name: serviceName,
|
|
5049
5177
|
language: "unknown",
|
|
5050
5178
|
discoveredVia: "otel",
|
|
5051
5179
|
...env !== "unknown" ? { env } : {}
|
|
5052
5180
|
};
|
|
5053
|
-
graph.addNode(
|
|
5054
|
-
return
|
|
5181
|
+
graph.addNode(resolved, node);
|
|
5182
|
+
return resolved;
|
|
5055
5183
|
}
|
|
5056
5184
|
function ensureInfraNode(graph, kind, name, provider) {
|
|
5057
5185
|
const id = (0, import_types8.infraId)(kind, name);
|
|
@@ -5245,7 +5373,7 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
5245
5373
|
await import_node_fs7.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
5246
5374
|
}
|
|
5247
5375
|
function incidentAffectedNode(span, graph, scanPath) {
|
|
5248
|
-
const sid = (0, import_types8.serviceId)(span.service, span.env);
|
|
5376
|
+
const sid = graph ? resolveFusedServiceId(graph, span.service, span.env) : (0, import_types8.serviceId)(span.service, span.env);
|
|
5249
5377
|
const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
|
|
5250
5378
|
const callSite = callSiteFromSpan(span, serviceNode, scanPath);
|
|
5251
5379
|
if (callSite) {
|
|
@@ -5966,6 +6094,7 @@ function goFramework(deps) {
|
|
|
5966
6094
|
if (deps["github.com/gin-gonic/gin"]) return "gin";
|
|
5967
6095
|
if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
|
|
5968
6096
|
if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
|
|
6097
|
+
if (deps["github.com/go-chi/chi/v5"] || deps["github.com/go-chi/chi"]) return "chi";
|
|
5969
6098
|
return void 0;
|
|
5970
6099
|
}
|
|
5971
6100
|
async function discoverGoService(scanPath, dir) {
|
|
@@ -6643,7 +6772,7 @@ function disambiguate(defs) {
|
|
|
6643
6772
|
}
|
|
6644
6773
|
async function addSymbols(graph, services) {
|
|
6645
6774
|
const parsers = /* @__PURE__ */ new Map();
|
|
6646
|
-
const
|
|
6775
|
+
const parserForExt5 = (ext) => {
|
|
6647
6776
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6648
6777
|
if (!grammar) return null;
|
|
6649
6778
|
let parser = parsers.get(ext);
|
|
@@ -6659,7 +6788,7 @@ async function addSymbols(graph, services) {
|
|
|
6659
6788
|
for (const service of services) {
|
|
6660
6789
|
const files = await loadSourceFiles(service.dir);
|
|
6661
6790
|
for (const file of files) {
|
|
6662
|
-
const parser =
|
|
6791
|
+
const parser = parserForExt5(import_node_path17.default.extname(file.path));
|
|
6663
6792
|
if (!parser) continue;
|
|
6664
6793
|
const relPath = toPosix(import_node_path17.default.relative(service.dir, file.path));
|
|
6665
6794
|
let defs;
|
|
@@ -6811,7 +6940,7 @@ function stringInner(node) {
|
|
|
6811
6940
|
}
|
|
6812
6941
|
async function addSymbolEdges(graph, services) {
|
|
6813
6942
|
const parsers = /* @__PURE__ */ new Map();
|
|
6814
|
-
const
|
|
6943
|
+
const parserForExt5 = (ext) => {
|
|
6815
6944
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
6816
6945
|
if (!grammar) return null;
|
|
6817
6946
|
let parser = parsers.get(ext);
|
|
@@ -6828,7 +6957,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
6828
6957
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
6829
6958
|
const files = await loadSourceFiles(service.dir);
|
|
6830
6959
|
for (const file of files) {
|
|
6831
|
-
const parser =
|
|
6960
|
+
const parser = parserForExt5(import_node_path18.default.extname(file.path));
|
|
6832
6961
|
if (!parser) continue;
|
|
6833
6962
|
const relPath = toPosix(import_node_path18.default.relative(service.dir, file.path));
|
|
6834
6963
|
const fileDir = import_node_path18.default.dirname(file.path);
|
|
@@ -7090,7 +7219,7 @@ function firstReferenceLines(root, wanted) {
|
|
|
7090
7219
|
}
|
|
7091
7220
|
async function addServerActions(graph, services) {
|
|
7092
7221
|
const parsers = /* @__PURE__ */ new Map();
|
|
7093
|
-
const
|
|
7222
|
+
const parserForExt5 = (ext) => {
|
|
7094
7223
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
7095
7224
|
if (!grammar) return null;
|
|
7096
7225
|
let parser = parsers.get(ext);
|
|
@@ -7113,7 +7242,7 @@ async function addServerActions(graph, services) {
|
|
|
7113
7242
|
const files = await loadSourceFiles(service.dir);
|
|
7114
7243
|
for (const file of files) {
|
|
7115
7244
|
if (isTestPath(file.path)) continue;
|
|
7116
|
-
const parser =
|
|
7245
|
+
const parser = parserForExt5(import_node_path19.default.extname(file.path));
|
|
7117
7246
|
if (!parser) continue;
|
|
7118
7247
|
const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
|
|
7119
7248
|
let root;
|
|
@@ -7176,7 +7305,7 @@ async function addServerActions(graph, services) {
|
|
|
7176
7305
|
}
|
|
7177
7306
|
for (const file of files) {
|
|
7178
7307
|
if (isTestPath(file.path)) continue;
|
|
7179
|
-
const parser =
|
|
7308
|
+
const parser = parserForExt5(import_node_path19.default.extname(file.path));
|
|
7180
7309
|
if (!parser) continue;
|
|
7181
7310
|
const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
|
|
7182
7311
|
const fileDir = import_node_path19.default.dirname(file.path);
|
|
@@ -8140,6 +8269,7 @@ init_cjs_shims();
|
|
|
8140
8269
|
var import_node_path30 = __toESM(require("path"), 1);
|
|
8141
8270
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
8142
8271
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
8272
|
+
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
8143
8273
|
var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
|
|
8144
8274
|
var import_types20 = require("@neat.is/types");
|
|
8145
8275
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
@@ -8190,19 +8320,27 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
8190
8320
|
}
|
|
8191
8321
|
return out;
|
|
8192
8322
|
}
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8323
|
+
var GRAMMAR_BY_EXT2 = {
|
|
8324
|
+
".ts": import_tree_sitter_typescript2.default.typescript,
|
|
8325
|
+
".tsx": import_tree_sitter_typescript2.default.tsx,
|
|
8326
|
+
".js": import_tree_sitter_javascript4.default,
|
|
8327
|
+
".jsx": import_tree_sitter_javascript4.default,
|
|
8328
|
+
".mjs": import_tree_sitter_javascript4.default,
|
|
8329
|
+
".cjs": import_tree_sitter_javascript4.default,
|
|
8330
|
+
".py": import_tree_sitter_python3.default
|
|
8331
|
+
};
|
|
8332
|
+
function parserForExt(ext, cache) {
|
|
8333
|
+
const grammar = GRAMMAR_BY_EXT2[ext] ?? import_tree_sitter_javascript4.default;
|
|
8334
|
+
let parser = cache.get(grammar);
|
|
8335
|
+
if (!parser) {
|
|
8336
|
+
parser = new import_tree_sitter6.default();
|
|
8337
|
+
parser.setLanguage(grammar);
|
|
8338
|
+
cache.set(grammar, parser);
|
|
8339
|
+
}
|
|
8340
|
+
return parser;
|
|
8202
8341
|
}
|
|
8203
8342
|
async function addHttpCallEdges(graph, services) {
|
|
8204
|
-
const
|
|
8205
|
-
const pyParser = makePyParser3();
|
|
8343
|
+
const parserCache = /* @__PURE__ */ new Map();
|
|
8206
8344
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
8207
8345
|
let nodesAdded = 0;
|
|
8208
8346
|
let edgesAdded = 0;
|
|
@@ -8211,7 +8349,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
8211
8349
|
const seen = /* @__PURE__ */ new Set();
|
|
8212
8350
|
for (const file of files) {
|
|
8213
8351
|
if (isTestPath(file.path)) continue;
|
|
8214
|
-
const parser = import_node_path30.default.extname(file.path)
|
|
8352
|
+
const parser = parserForExt(import_node_path30.default.extname(file.path), parserCache);
|
|
8215
8353
|
let sites;
|
|
8216
8354
|
try {
|
|
8217
8355
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -8284,7 +8422,7 @@ function parseSource5(parser, source) {
|
|
|
8284
8422
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
8285
8423
|
);
|
|
8286
8424
|
}
|
|
8287
|
-
function
|
|
8425
|
+
function makeJsParser3() {
|
|
8288
8426
|
const p = new import_tree_sitter7.default();
|
|
8289
8427
|
p.setLanguage(import_tree_sitter_javascript5.default);
|
|
8290
8428
|
return p;
|
|
@@ -8462,7 +8600,7 @@ function findRoute(entries, method, normalizedPath) {
|
|
|
8462
8600
|
);
|
|
8463
8601
|
}
|
|
8464
8602
|
async function addRouteCallEdges(graph, services) {
|
|
8465
|
-
const jsParser =
|
|
8603
|
+
const jsParser = makeJsParser3();
|
|
8466
8604
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
8467
8605
|
const routeIndex = buildRouteIndex(graph);
|
|
8468
8606
|
if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
@@ -8854,7 +8992,7 @@ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"),
|
|
|
8854
8992
|
var import_types27 = require("@neat.is/types");
|
|
8855
8993
|
var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
|
|
8856
8994
|
var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
|
|
8857
|
-
function
|
|
8995
|
+
function parserForExt2(ext) {
|
|
8858
8996
|
const p = new import_tree_sitter8.default();
|
|
8859
8997
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
|
|
8860
8998
|
return p;
|
|
@@ -9019,7 +9157,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9019
9157
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
9020
9158
|
if (!hasClient && !hasAdmin) return [];
|
|
9021
9159
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
9022
|
-
const tree = parseSource3(
|
|
9160
|
+
const tree = parseSource3(parserForExt2(import_node_path37.default.extname(file.path)), file.content);
|
|
9023
9161
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
9024
9162
|
const collLine = /* @__PURE__ */ new Map();
|
|
9025
9163
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -9436,7 +9574,7 @@ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
|
9436
9574
|
var import_types29 = require("@neat.is/types");
|
|
9437
9575
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
9438
9576
|
var PARSE_CHUNK6 = 16384;
|
|
9439
|
-
function
|
|
9577
|
+
function makePyParser3() {
|
|
9440
9578
|
const p = new import_tree_sitter9.default();
|
|
9441
9579
|
p.setLanguage(import_tree_sitter_python4.default);
|
|
9442
9580
|
return p;
|
|
@@ -9543,7 +9681,7 @@ function foreignKeyParentTable(call) {
|
|
|
9543
9681
|
}
|
|
9544
9682
|
function sqlalchemyForeignKeys(file, serviceDir) {
|
|
9545
9683
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
9546
|
-
const tree = parseSource6(
|
|
9684
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
9547
9685
|
const out = [];
|
|
9548
9686
|
const seen = /* @__PURE__ */ new Set();
|
|
9549
9687
|
walk3(tree.rootNode, (node) => {
|
|
@@ -9580,7 +9718,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
9580
9718
|
}
|
|
9581
9719
|
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
9582
9720
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
9583
|
-
const tree = parseSource6(
|
|
9721
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
9584
9722
|
const out = [];
|
|
9585
9723
|
const seen = /* @__PURE__ */ new Set();
|
|
9586
9724
|
const push = (name, line, columns) => {
|
|
@@ -9632,7 +9770,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
9632
9770
|
function buildSqlalchemyModelRegistry(files) {
|
|
9633
9771
|
const table = /* @__PURE__ */ new Map();
|
|
9634
9772
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
9635
|
-
const parser =
|
|
9773
|
+
const parser = makePyParser3();
|
|
9636
9774
|
for (const file of files) {
|
|
9637
9775
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) continue;
|
|
9638
9776
|
const tree = parseSource6(parser, file.content);
|
|
@@ -9681,7 +9819,7 @@ function importsModelName(content, name) {
|
|
|
9681
9819
|
function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
9682
9820
|
const registry = buildSqlalchemyModelRegistry(files);
|
|
9683
9821
|
if (registry.size === 0) return [];
|
|
9684
|
-
const parser =
|
|
9822
|
+
const parser = makePyParser3();
|
|
9685
9823
|
const out = [];
|
|
9686
9824
|
const seen = /* @__PURE__ */ new Set();
|
|
9687
9825
|
for (const file of files) {
|
|
@@ -9719,7 +9857,7 @@ var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
|
9719
9857
|
var import_types30 = require("@neat.is/types");
|
|
9720
9858
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
9721
9859
|
var PARSE_CHUNK7 = 16384;
|
|
9722
|
-
function
|
|
9860
|
+
function makePyParser4() {
|
|
9723
9861
|
const p = new import_tree_sitter10.default();
|
|
9724
9862
|
p.setLanguage(import_tree_sitter_python5.default);
|
|
9725
9863
|
return p;
|
|
@@ -9780,7 +9918,7 @@ function readMeta(body) {
|
|
|
9780
9918
|
}
|
|
9781
9919
|
function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
9782
9920
|
if (!DJANGO_IMPORT_RE.test(file.content)) return [];
|
|
9783
|
-
const tree = parseSource7(
|
|
9921
|
+
const tree = parseSource7(makePyParser4(), file.content);
|
|
9784
9922
|
const out = [];
|
|
9785
9923
|
const seen = /* @__PURE__ */ new Set();
|
|
9786
9924
|
const defaultAppLabel = import_node_path40.default.basename(import_node_path40.default.dirname(file.path));
|
|
@@ -9815,7 +9953,7 @@ var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"),
|
|
|
9815
9953
|
var import_types31 = require("@neat.is/types");
|
|
9816
9954
|
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
9817
9955
|
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
9818
|
-
function
|
|
9956
|
+
function parserForExt3(ext) {
|
|
9819
9957
|
const p = new import_tree_sitter11.default();
|
|
9820
9958
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
|
|
9821
9959
|
return p;
|
|
@@ -9887,7 +10025,7 @@ function columnsFromObject(obj) {
|
|
|
9887
10025
|
}
|
|
9888
10026
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
9889
10027
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9890
|
-
const tree = parseSource3(
|
|
10028
|
+
const tree = parseSource3(parserForExt3(import_node_path41.default.extname(file.path)), file.content);
|
|
9891
10029
|
const out = [];
|
|
9892
10030
|
const seen = /* @__PURE__ */ new Set();
|
|
9893
10031
|
const walk9 = (node) => {
|
|
@@ -9976,7 +10114,7 @@ function referencesTargetVar(call) {
|
|
|
9976
10114
|
}
|
|
9977
10115
|
function drizzleForeignKeys(file, serviceDir) {
|
|
9978
10116
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
9979
|
-
const tree = parseSource3(
|
|
10117
|
+
const tree = parseSource3(parserForExt3(import_node_path41.default.extname(file.path)), file.content);
|
|
9980
10118
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
9981
10119
|
const out = [];
|
|
9982
10120
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -12683,7 +12821,7 @@ var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"),
|
|
|
12683
12821
|
var import_types47 = require("@neat.is/types");
|
|
12684
12822
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
12685
12823
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
12686
|
-
function
|
|
12824
|
+
function parserForExt4(ext) {
|
|
12687
12825
|
const p = new import_tree_sitter16.default();
|
|
12688
12826
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
12689
12827
|
return p;
|
|
@@ -12772,7 +12910,7 @@ function topLevelSchemas(root) {
|
|
|
12772
12910
|
}
|
|
12773
12911
|
function zodShapesFromFile(file, serviceDir) {
|
|
12774
12912
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
12775
|
-
const tree = parseSource3(
|
|
12913
|
+
const tree = parseSource3(parserForExt4(import_node_path57.default.extname(file.path)), file.content);
|
|
12776
12914
|
const out = [];
|
|
12777
12915
|
const seen = /* @__PURE__ */ new Set();
|
|
12778
12916
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -18738,6 +18876,19 @@ async function startDaemon(opts = {}) {
|
|
|
18738
18876
|
let otlpAddress = "";
|
|
18739
18877
|
let daemonRecord = null;
|
|
18740
18878
|
if (bind) {
|
|
18879
|
+
let bareSpanIsRoutable2 = function(serviceName) {
|
|
18880
|
+
if (singleProject) {
|
|
18881
|
+
const slot = slots.get(singleProject);
|
|
18882
|
+
if (!slot) {
|
|
18883
|
+
return !serviceName || serviceNameMatchesProject(serviceName, singleProject);
|
|
18884
|
+
}
|
|
18885
|
+
return spanBelongsToSingleProject(slot.graph, singleProject, serviceName);
|
|
18886
|
+
}
|
|
18887
|
+
const entries = [...slots.values()].map((s) => s.entry);
|
|
18888
|
+
const target = routeSpanToProject(serviceName, entries);
|
|
18889
|
+
return slots.has(target) || slots.has(DEFAULT_PROJECT);
|
|
18890
|
+
};
|
|
18891
|
+
var bareSpanIsRoutable = bareSpanIsRoutable2;
|
|
18741
18892
|
const auth = readAuthEnv();
|
|
18742
18893
|
const host = resolveHost(opts, Boolean(auth.authToken));
|
|
18743
18894
|
const restPort = resolveRestPort(opts);
|
|
@@ -18835,6 +18986,20 @@ async function startDaemon(opts = {}) {
|
|
|
18835
18986
|
const liveEntries = await listProjects().catch(() => []);
|
|
18836
18987
|
let slot = slots.get(project);
|
|
18837
18988
|
if (!slot) {
|
|
18989
|
+
if (singleProject && project === singleProject) {
|
|
18990
|
+
slot = await tryRecoverSlot({
|
|
18991
|
+
name: singleProject,
|
|
18992
|
+
path: singleProjectPath,
|
|
18993
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18994
|
+
languages: [],
|
|
18995
|
+
status: "active"
|
|
18996
|
+
});
|
|
18997
|
+
if (!slot || slot.status !== "active") {
|
|
18998
|
+
warnDroppedSpan(singleProject, slot?.errorReason ?? "unknown");
|
|
18999
|
+
return null;
|
|
19000
|
+
}
|
|
19001
|
+
return slot;
|
|
19002
|
+
}
|
|
18838
19003
|
await recordUnroutedSpan(serviceName, traceId);
|
|
18839
19004
|
return null;
|
|
18840
19005
|
}
|
|
@@ -18901,7 +19066,30 @@ async function startDaemon(opts = {}) {
|
|
|
18901
19066
|
// host, rather than accepting it and dropping the batch. `slots` covers
|
|
18902
19067
|
// active/recovering projects, `bootstrapStatus` the ones still
|
|
18903
19068
|
// extracting; a foreign or wrong-cased project name matches neither.
|
|
18904
|
-
|
|
19069
|
+
// A single-project daemon owns exactly one project by definition, so its
|
|
19070
|
+
// own name always counts as registered even before loadAll populates the
|
|
19071
|
+
// slot — otherwise a scoped span arriving during cold-start would 404 and
|
|
19072
|
+
// be lost (OTLP does not retry 4xx). resolveSlotByName then builds it (#879).
|
|
19073
|
+
isProjectRegistered: (project) => slots.has(project) || bootstrapStatus.has(project) || singleProject !== void 0 && project === singleProject,
|
|
19074
|
+
// #881 — the bare `/v1/traces` route replies before the span is routed
|
|
19075
|
+
// (off the queue), so tell the receiver, per batch, how many spans will
|
|
19076
|
+
// land on no project. It keeps the 200 but reports those as
|
|
19077
|
+
// partialSuccess.rejectedSpans instead of an empty partialSuccess that an
|
|
19078
|
+
// exporter reads as full acceptance. Pure — the drop + unrouted-ledger
|
|
19079
|
+
// write still happen on the async onSpan path.
|
|
19080
|
+
classifyBareRoutability: (spans) => {
|
|
19081
|
+
let rejected = 0;
|
|
19082
|
+
for (const span of spans) {
|
|
19083
|
+
if (!bareSpanIsRoutable2(span.service)) rejected++;
|
|
19084
|
+
}
|
|
19085
|
+
if (rejected === 0) return { rejected: 0 };
|
|
19086
|
+
const noun = rejected === 1 ? "span" : "spans";
|
|
19087
|
+
const verb = rejected === 1 ? "was" : "were";
|
|
19088
|
+
return {
|
|
19089
|
+
rejected,
|
|
19090
|
+
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.`
|
|
19091
|
+
};
|
|
19092
|
+
}
|
|
18905
19093
|
});
|
|
18906
19094
|
otlpAddress = await listenSteppingOtlp(otlpApp, otlpPort, host);
|
|
18907
19095
|
console.log(`neatd: OTLP listening on ${otlpAddress}/v1/traces`);
|