@neat.is/core 0.7.8 → 0.7.10
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-LN75Z624.js → chunk-6T7ZHODF.js} +554 -65
- package/dist/chunk-6T7ZHODF.js.map +1 -0
- package/dist/{chunk-BIY46Q6U.js → chunk-BC53SCT7.js} +36 -2
- package/dist/chunk-BC53SCT7.js.map +1 -0
- package/dist/{chunk-OVRVDSUO.js → chunk-D4OX6MUX.js} +58 -4
- package/dist/chunk-D4OX6MUX.js.map +1 -0
- package/dist/{chunk-4GCV46AP.js → chunk-FCO5Z3RW.js} +2 -2
- package/dist/{chunk-Y43UCVZS.js → chunk-UUYCTH2E.js} +24 -8
- package/dist/chunk-UUYCTH2E.js.map +1 -0
- package/dist/cli.cjs +1392 -314
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +704 -176
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +645 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +645 -77
- 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 +618 -74
- 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-LN75Z624.js.map +0 -1
- package/dist/chunk-OVRVDSUO.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/server.cjs
CHANGED
|
@@ -502,13 +502,18 @@ function loadProtobufResponseEncoder() {
|
|
|
502
502
|
);
|
|
503
503
|
return exportTraceServiceResponseType;
|
|
504
504
|
}
|
|
505
|
-
function encodeProtobufResponseBody() {
|
|
506
|
-
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
505
|
+
function encodeProtobufResponseBody(rejected, message) {
|
|
507
506
|
const Type = loadProtobufResponseEncoder();
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
507
|
+
if (!rejected) {
|
|
508
|
+
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
509
|
+
const msg2 = Type.create({});
|
|
510
|
+
cachedProtobufResponseBody = Buffer.from(Type.encode(msg2).finish());
|
|
511
|
+
return cachedProtobufResponseBody;
|
|
512
|
+
}
|
|
513
|
+
const msg = Type.fromObject({
|
|
514
|
+
partial_success: { rejected_spans: rejected, error_message: message ?? "" }
|
|
515
|
+
});
|
|
516
|
+
return Buffer.from(Type.encode(msg).finish());
|
|
512
517
|
}
|
|
513
518
|
async function decodeProtobufBody(buf) {
|
|
514
519
|
const Type = loadProtobufDecoder();
|
|
@@ -623,6 +628,13 @@ async function buildOtelReceiver(opts) {
|
|
|
623
628
|
}
|
|
624
629
|
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: {} });
|
|
625
630
|
}
|
|
631
|
+
function sendOtlpPartial(reply, flavor, rejected, message) {
|
|
632
|
+
if (flavor === "protobuf") {
|
|
633
|
+
const buf = encodeProtobufResponseBody(rejected, message);
|
|
634
|
+
return reply.code(200).header("content-type", "application/x-protobuf").send(buf);
|
|
635
|
+
}
|
|
636
|
+
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: { rejectedSpans: rejected, errorMessage: message } });
|
|
637
|
+
}
|
|
626
638
|
app.addContentTypeParser(
|
|
627
639
|
"application/x-protobuf",
|
|
628
640
|
{ parseAs: "buffer", bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024 },
|
|
@@ -650,6 +662,10 @@ async function buildOtelReceiver(opts) {
|
|
|
650
662
|
}
|
|
651
663
|
}
|
|
652
664
|
enqueue(spans);
|
|
665
|
+
if (opts.classifyBareRoutability) {
|
|
666
|
+
const { rejected, message } = opts.classifyBareRoutability(spans);
|
|
667
|
+
if (rejected > 0) return sendOtlpPartial(reply, result.flavor, rejected, message);
|
|
668
|
+
}
|
|
653
669
|
return sendOtlpSuccess(reply, result.flavor);
|
|
654
670
|
});
|
|
655
671
|
app.post("/projects/:project/v1/traces", async (req, reply) => {
|
|
@@ -739,7 +755,7 @@ function getGraph(project = DEFAULT_PROJECT) {
|
|
|
739
755
|
init_cjs_shims();
|
|
740
756
|
var import_fastify2 = __toESM(require("fastify"), 1);
|
|
741
757
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
742
|
-
var
|
|
758
|
+
var import_types85 = require("@neat.is/types");
|
|
743
759
|
|
|
744
760
|
// src/extend/index.ts
|
|
745
761
|
init_cjs_shims();
|
|
@@ -2001,6 +2017,7 @@ var import_yaml = require("yaml");
|
|
|
2001
2017
|
var import_types3 = require("@neat.is/types");
|
|
2002
2018
|
var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php"]);
|
|
2003
2019
|
var CONFIG_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
|
|
2020
|
+
var JSON_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["app.json", "app.config.json", "eas.json"]);
|
|
2004
2021
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
2005
2022
|
"node_modules",
|
|
2006
2023
|
".git",
|
|
@@ -2040,6 +2057,7 @@ async function isPythonVenvDir(dir) {
|
|
|
2040
2057
|
function isConfigFile(name) {
|
|
2041
2058
|
const ext = import_node_path5.default.extname(name);
|
|
2042
2059
|
if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) };
|
|
2060
|
+
if (JSON_CONFIG_FILENAMES.has(name)) return { match: true, fileType: "json" };
|
|
2043
2061
|
if (name === ".env" || name.startsWith(".env.")) {
|
|
2044
2062
|
if (isEnvTemplateFile(name)) return { match: false, fileType: "" };
|
|
2045
2063
|
if (isNeatAuthoredEnvFile(name)) return { match: false, fileType: "" };
|
|
@@ -4565,10 +4583,15 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
4565
4583
|
}
|
|
4566
4584
|
if (!entry) return null;
|
|
4567
4585
|
try {
|
|
4568
|
-
const
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4586
|
+
const queryLine = line !== void 0 && Number.isFinite(line) ? line : 1;
|
|
4587
|
+
let pos = entry.consumer.originalPositionFor({ line: queryLine, column: 0 });
|
|
4588
|
+
if (!pos || !pos.source) {
|
|
4589
|
+
pos = entry.consumer.originalPositionFor({
|
|
4590
|
+
line: queryLine,
|
|
4591
|
+
column: 0,
|
|
4592
|
+
bias: sourceMapJs.SourceMapConsumer.LEAST_UPPER_BOUND
|
|
4593
|
+
});
|
|
4594
|
+
}
|
|
4572
4595
|
if (!pos || !pos.source) return null;
|
|
4573
4596
|
const root = entry.consumer.sourceRoot ?? "";
|
|
4574
4597
|
const resolved = import_node_path10.default.resolve(entry.dir, root, pos.source);
|
|
@@ -4577,6 +4600,9 @@ function resolveDistToSrc(absFilepath, line) {
|
|
|
4577
4600
|
return null;
|
|
4578
4601
|
}
|
|
4579
4602
|
}
|
|
4603
|
+
function hasAdjacentSourceMap(absFilepath) {
|
|
4604
|
+
return sourceMapCache.get(absFilepath) != null;
|
|
4605
|
+
}
|
|
4580
4606
|
function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
4581
4607
|
const filepath = codeFilepathOf(span.attributes);
|
|
4582
4608
|
if (filepath === void 0) return null;
|
|
@@ -4592,7 +4618,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
|
|
|
4592
4618
|
}
|
|
4593
4619
|
const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath);
|
|
4594
4620
|
if (!relPath) return null;
|
|
4595
|
-
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
|
|
4621
|
+
if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && !hasAdjacentSourceMap(abs) && serviceNode?.name) {
|
|
4596
4622
|
warnNoSourceMaps(serviceNode.name);
|
|
4597
4623
|
}
|
|
4598
4624
|
const fn = codeFunctionOf(span.attributes);
|
|
@@ -4868,7 +4894,7 @@ function resolveServiceId(graph, host, env) {
|
|
|
4868
4894
|
function frontierIdFor(host) {
|
|
4869
4895
|
return (0, import_types7.frontierId)(host);
|
|
4870
4896
|
}
|
|
4871
|
-
function
|
|
4897
|
+
function resolveFusedServiceId(graph, serviceName, env) {
|
|
4872
4898
|
const id = (0, import_types7.serviceId)(serviceName, env);
|
|
4873
4899
|
if (graph.hasNode(id)) return id;
|
|
4874
4900
|
const wanted = serviceName.toLowerCase();
|
|
@@ -4878,17 +4904,21 @@ function ensureServiceNode(graph, serviceName, env) {
|
|
|
4878
4904
|
if (svc.discoveredVia === "otel") return false;
|
|
4879
4905
|
return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
|
|
4880
4906
|
});
|
|
4881
|
-
|
|
4907
|
+
return extractedId ?? id;
|
|
4908
|
+
}
|
|
4909
|
+
function ensureServiceNode(graph, serviceName, env) {
|
|
4910
|
+
const resolved = resolveFusedServiceId(graph, serviceName, env);
|
|
4911
|
+
if (graph.hasNode(resolved)) return resolved;
|
|
4882
4912
|
const node = {
|
|
4883
|
-
id,
|
|
4913
|
+
id: resolved,
|
|
4884
4914
|
type: import_types7.NodeType.ServiceNode,
|
|
4885
4915
|
name: serviceName,
|
|
4886
4916
|
language: "unknown",
|
|
4887
4917
|
discoveredVia: "otel",
|
|
4888
4918
|
...env !== "unknown" ? { env } : {}
|
|
4889
4919
|
};
|
|
4890
|
-
graph.addNode(
|
|
4891
|
-
return
|
|
4920
|
+
graph.addNode(resolved, node);
|
|
4921
|
+
return resolved;
|
|
4892
4922
|
}
|
|
4893
4923
|
function ensureInfraNode(graph, kind, name, provider) {
|
|
4894
4924
|
const id = (0, import_types7.infraId)(kind, name);
|
|
@@ -5081,8 +5111,23 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
5081
5111
|
await import_node_fs9.promises.mkdir(import_node_path10.default.dirname(ctx.errorsPath), { recursive: true });
|
|
5082
5112
|
await import_node_fs9.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
5083
5113
|
}
|
|
5114
|
+
async function appendConnectorIncident(errorsPath, input) {
|
|
5115
|
+
const ev = {
|
|
5116
|
+
id: input.id,
|
|
5117
|
+
timestamp: input.timestamp,
|
|
5118
|
+
service: input.service,
|
|
5119
|
+
traceId: input.id,
|
|
5120
|
+
spanId: input.id,
|
|
5121
|
+
errorType: input.errorType,
|
|
5122
|
+
errorMessage: input.errorMessage,
|
|
5123
|
+
...input.attributes && Object.keys(input.attributes).length > 0 ? { attributes: input.attributes } : {},
|
|
5124
|
+
affectedNode: input.affectedNode
|
|
5125
|
+
};
|
|
5126
|
+
await import_node_fs9.promises.mkdir(import_node_path10.default.dirname(errorsPath), { recursive: true });
|
|
5127
|
+
await import_node_fs9.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
5128
|
+
}
|
|
5084
5129
|
function incidentAffectedNode(span, graph, scanPath) {
|
|
5085
|
-
const sid = (0, import_types7.serviceId)(span.service, span.env);
|
|
5130
|
+
const sid = graph ? resolveFusedServiceId(graph, span.service, span.env) : (0, import_types7.serviceId)(span.service, span.env);
|
|
5086
5131
|
const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
|
|
5087
5132
|
const callSite = callSiteFromSpan(span, serviceNode, scanPath);
|
|
5088
5133
|
if (callSite) {
|
|
@@ -7363,7 +7408,7 @@ function disambiguate(defs) {
|
|
|
7363
7408
|
}
|
|
7364
7409
|
async function addSymbols(graph, services) {
|
|
7365
7410
|
const parsers = /* @__PURE__ */ new Map();
|
|
7366
|
-
const
|
|
7411
|
+
const parserForExt5 = (ext) => {
|
|
7367
7412
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
7368
7413
|
if (!grammar) return null;
|
|
7369
7414
|
let parser = parsers.get(ext);
|
|
@@ -7379,7 +7424,7 @@ async function addSymbols(graph, services) {
|
|
|
7379
7424
|
for (const service of services) {
|
|
7380
7425
|
const files = await loadSourceFiles(service.dir);
|
|
7381
7426
|
for (const file of files) {
|
|
7382
|
-
const parser =
|
|
7427
|
+
const parser = parserForExt5(import_node_path19.default.extname(file.path));
|
|
7383
7428
|
if (!parser) continue;
|
|
7384
7429
|
const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
|
|
7385
7430
|
let defs;
|
|
@@ -7531,7 +7576,7 @@ function stringInner(node) {
|
|
|
7531
7576
|
}
|
|
7532
7577
|
async function addSymbolEdges(graph, services) {
|
|
7533
7578
|
const parsers = /* @__PURE__ */ new Map();
|
|
7534
|
-
const
|
|
7579
|
+
const parserForExt5 = (ext) => {
|
|
7535
7580
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
7536
7581
|
if (!grammar) return null;
|
|
7537
7582
|
let parser = parsers.get(ext);
|
|
@@ -7548,7 +7593,7 @@ async function addSymbolEdges(graph, services) {
|
|
|
7548
7593
|
const tsPaths = await loadTsPathConfig(service.dir);
|
|
7549
7594
|
const files = await loadSourceFiles(service.dir);
|
|
7550
7595
|
for (const file of files) {
|
|
7551
|
-
const parser =
|
|
7596
|
+
const parser = parserForExt5(import_node_path20.default.extname(file.path));
|
|
7552
7597
|
if (!parser) continue;
|
|
7553
7598
|
const relPath = toPosix(import_node_path20.default.relative(service.dir, file.path));
|
|
7554
7599
|
const fileDir = import_node_path20.default.dirname(file.path);
|
|
@@ -7810,7 +7855,7 @@ function firstReferenceLines(root, wanted) {
|
|
|
7810
7855
|
}
|
|
7811
7856
|
async function addServerActions(graph, services) {
|
|
7812
7857
|
const parsers = /* @__PURE__ */ new Map();
|
|
7813
|
-
const
|
|
7858
|
+
const parserForExt5 = (ext) => {
|
|
7814
7859
|
const grammar = GRAMMAR_BY_EXT[ext];
|
|
7815
7860
|
if (!grammar) return null;
|
|
7816
7861
|
let parser = parsers.get(ext);
|
|
@@ -7833,7 +7878,7 @@ async function addServerActions(graph, services) {
|
|
|
7833
7878
|
const files = await loadSourceFiles(service.dir);
|
|
7834
7879
|
for (const file of files) {
|
|
7835
7880
|
if (isTestPath(file.path)) continue;
|
|
7836
|
-
const parser =
|
|
7881
|
+
const parser = parserForExt5(import_node_path21.default.extname(file.path));
|
|
7837
7882
|
if (!parser) continue;
|
|
7838
7883
|
const relPath = toPosix(import_node_path21.default.relative(service.dir, file.path));
|
|
7839
7884
|
let root;
|
|
@@ -7896,7 +7941,7 @@ async function addServerActions(graph, services) {
|
|
|
7896
7941
|
}
|
|
7897
7942
|
for (const file of files) {
|
|
7898
7943
|
if (isTestPath(file.path)) continue;
|
|
7899
|
-
const parser =
|
|
7944
|
+
const parser = parserForExt5(import_node_path21.default.extname(file.path));
|
|
7900
7945
|
if (!parser) continue;
|
|
7901
7946
|
const relPath = toPosix(import_node_path21.default.relative(service.dir, file.path));
|
|
7902
7947
|
const fileDir = import_node_path21.default.dirname(file.path);
|
|
@@ -8860,6 +8905,7 @@ init_cjs_shims();
|
|
|
8860
8905
|
var import_node_path32 = __toESM(require("path"), 1);
|
|
8861
8906
|
var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
|
|
8862
8907
|
var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
|
|
8908
|
+
var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
|
|
8863
8909
|
var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
|
|
8864
8910
|
var import_types21 = require("@neat.is/types");
|
|
8865
8911
|
var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
|
|
@@ -8910,19 +8956,27 @@ function callsFromSource(source, parser, knownHosts) {
|
|
|
8910
8956
|
}
|
|
8911
8957
|
return out;
|
|
8912
8958
|
}
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8959
|
+
var GRAMMAR_BY_EXT2 = {
|
|
8960
|
+
".ts": import_tree_sitter_typescript2.default.typescript,
|
|
8961
|
+
".tsx": import_tree_sitter_typescript2.default.tsx,
|
|
8962
|
+
".js": import_tree_sitter_javascript4.default,
|
|
8963
|
+
".jsx": import_tree_sitter_javascript4.default,
|
|
8964
|
+
".mjs": import_tree_sitter_javascript4.default,
|
|
8965
|
+
".cjs": import_tree_sitter_javascript4.default,
|
|
8966
|
+
".py": import_tree_sitter_python3.default
|
|
8967
|
+
};
|
|
8968
|
+
function parserForExt(ext, cache) {
|
|
8969
|
+
const grammar = GRAMMAR_BY_EXT2[ext] ?? import_tree_sitter_javascript4.default;
|
|
8970
|
+
let parser = cache.get(grammar);
|
|
8971
|
+
if (!parser) {
|
|
8972
|
+
parser = new import_tree_sitter6.default();
|
|
8973
|
+
parser.setLanguage(grammar);
|
|
8974
|
+
cache.set(grammar, parser);
|
|
8975
|
+
}
|
|
8976
|
+
return parser;
|
|
8922
8977
|
}
|
|
8923
8978
|
async function addHttpCallEdges(graph, services) {
|
|
8924
|
-
const
|
|
8925
|
-
const pyParser = makePyParser3();
|
|
8979
|
+
const parserCache = /* @__PURE__ */ new Map();
|
|
8926
8980
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
8927
8981
|
let nodesAdded = 0;
|
|
8928
8982
|
let edgesAdded = 0;
|
|
@@ -8931,7 +8985,7 @@ async function addHttpCallEdges(graph, services) {
|
|
|
8931
8985
|
const seen = /* @__PURE__ */ new Set();
|
|
8932
8986
|
for (const file of files) {
|
|
8933
8987
|
if (isTestPath(file.path)) continue;
|
|
8934
|
-
const parser = import_node_path32.default.extname(file.path)
|
|
8988
|
+
const parser = parserForExt(import_node_path32.default.extname(file.path), parserCache);
|
|
8935
8989
|
let sites;
|
|
8936
8990
|
try {
|
|
8937
8991
|
sites = callsFromSource(file.content, parser, knownHosts);
|
|
@@ -9004,7 +9058,7 @@ function parseSource5(parser, source) {
|
|
|
9004
9058
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
|
|
9005
9059
|
);
|
|
9006
9060
|
}
|
|
9007
|
-
function
|
|
9061
|
+
function makeJsParser3() {
|
|
9008
9062
|
const p = new import_tree_sitter7.default();
|
|
9009
9063
|
p.setLanguage(import_tree_sitter_javascript5.default);
|
|
9010
9064
|
return p;
|
|
@@ -9182,7 +9236,7 @@ function findRoute(entries, method, normalizedPath) {
|
|
|
9182
9236
|
);
|
|
9183
9237
|
}
|
|
9184
9238
|
async function addRouteCallEdges(graph, services) {
|
|
9185
|
-
const jsParser =
|
|
9239
|
+
const jsParser = makeJsParser3();
|
|
9186
9240
|
const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
|
|
9187
9241
|
const routeIndex = buildRouteIndex(graph);
|
|
9188
9242
|
if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
|
|
@@ -9574,7 +9628,7 @@ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"),
|
|
|
9574
9628
|
var import_types28 = require("@neat.is/types");
|
|
9575
9629
|
var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
|
|
9576
9630
|
var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
|
|
9577
|
-
function
|
|
9631
|
+
function parserForExt2(ext) {
|
|
9578
9632
|
const p = new import_tree_sitter8.default();
|
|
9579
9633
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
|
|
9580
9634
|
return p;
|
|
@@ -9739,7 +9793,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
|
|
|
9739
9793
|
const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
|
|
9740
9794
|
if (!hasClient && !hasAdmin) return [];
|
|
9741
9795
|
const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
|
|
9742
|
-
const tree = parseSource3(
|
|
9796
|
+
const tree = parseSource3(parserForExt2(import_node_path39.default.extname(file.path)), file.content);
|
|
9743
9797
|
const clientVars = firestoreClientVars(tree.rootNode);
|
|
9744
9798
|
const collLine = /* @__PURE__ */ new Map();
|
|
9745
9799
|
const writes = /* @__PURE__ */ new Map();
|
|
@@ -10156,7 +10210,7 @@ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
|
|
|
10156
10210
|
var import_types30 = require("@neat.is/types");
|
|
10157
10211
|
var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
|
|
10158
10212
|
var PARSE_CHUNK6 = 16384;
|
|
10159
|
-
function
|
|
10213
|
+
function makePyParser3() {
|
|
10160
10214
|
const p = new import_tree_sitter9.default();
|
|
10161
10215
|
p.setLanguage(import_tree_sitter_python4.default);
|
|
10162
10216
|
return p;
|
|
@@ -10263,7 +10317,7 @@ function foreignKeyParentTable(call) {
|
|
|
10263
10317
|
}
|
|
10264
10318
|
function sqlalchemyForeignKeys(file, serviceDir) {
|
|
10265
10319
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
10266
|
-
const tree = parseSource6(
|
|
10320
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
10267
10321
|
const out = [];
|
|
10268
10322
|
const seen = /* @__PURE__ */ new Set();
|
|
10269
10323
|
walk3(tree.rootNode, (node) => {
|
|
@@ -10300,7 +10354,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
|
|
|
10300
10354
|
}
|
|
10301
10355
|
function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
10302
10356
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
|
|
10303
|
-
const tree = parseSource6(
|
|
10357
|
+
const tree = parseSource6(makePyParser3(), file.content);
|
|
10304
10358
|
const out = [];
|
|
10305
10359
|
const seen = /* @__PURE__ */ new Set();
|
|
10306
10360
|
const push = (name, line, columns) => {
|
|
@@ -10352,7 +10406,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
|
|
|
10352
10406
|
function buildSqlalchemyModelRegistry(files) {
|
|
10353
10407
|
const table = /* @__PURE__ */ new Map();
|
|
10354
10408
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
10355
|
-
const parser =
|
|
10409
|
+
const parser = makePyParser3();
|
|
10356
10410
|
for (const file of files) {
|
|
10357
10411
|
if (!SQLALCHEMY_IMPORT_RE.test(file.content)) continue;
|
|
10358
10412
|
const tree = parseSource6(parser, file.content);
|
|
@@ -10401,7 +10455,7 @@ function importsModelName(content, name) {
|
|
|
10401
10455
|
function pythonOrmCrossFileEndpoints(files, serviceDir) {
|
|
10402
10456
|
const registry = buildSqlalchemyModelRegistry(files);
|
|
10403
10457
|
if (registry.size === 0) return [];
|
|
10404
|
-
const parser =
|
|
10458
|
+
const parser = makePyParser3();
|
|
10405
10459
|
const out = [];
|
|
10406
10460
|
const seen = /* @__PURE__ */ new Set();
|
|
10407
10461
|
for (const file of files) {
|
|
@@ -10439,7 +10493,7 @@ var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
|
|
|
10439
10493
|
var import_types31 = require("@neat.is/types");
|
|
10440
10494
|
var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
|
|
10441
10495
|
var PARSE_CHUNK7 = 16384;
|
|
10442
|
-
function
|
|
10496
|
+
function makePyParser4() {
|
|
10443
10497
|
const p = new import_tree_sitter10.default();
|
|
10444
10498
|
p.setLanguage(import_tree_sitter_python5.default);
|
|
10445
10499
|
return p;
|
|
@@ -10500,7 +10554,7 @@ function readMeta(body) {
|
|
|
10500
10554
|
}
|
|
10501
10555
|
function djangoOrmEndpointsFromFile(file, serviceDir) {
|
|
10502
10556
|
if (!DJANGO_IMPORT_RE.test(file.content)) return [];
|
|
10503
|
-
const tree = parseSource7(
|
|
10557
|
+
const tree = parseSource7(makePyParser4(), file.content);
|
|
10504
10558
|
const out = [];
|
|
10505
10559
|
const seen = /* @__PURE__ */ new Set();
|
|
10506
10560
|
const defaultAppLabel = import_node_path42.default.basename(import_node_path42.default.dirname(file.path));
|
|
@@ -10535,7 +10589,7 @@ var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"),
|
|
|
10535
10589
|
var import_types32 = require("@neat.is/types");
|
|
10536
10590
|
var DRIZZLE_IMPORT_RE = /drizzle-orm/;
|
|
10537
10591
|
var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
|
|
10538
|
-
function
|
|
10592
|
+
function parserForExt3(ext) {
|
|
10539
10593
|
const p = new import_tree_sitter11.default();
|
|
10540
10594
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
|
|
10541
10595
|
return p;
|
|
@@ -10607,7 +10661,7 @@ function columnsFromObject(obj) {
|
|
|
10607
10661
|
}
|
|
10608
10662
|
function drizzleEndpointsFromFile(file, serviceDir) {
|
|
10609
10663
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
10610
|
-
const tree = parseSource3(
|
|
10664
|
+
const tree = parseSource3(parserForExt3(import_node_path43.default.extname(file.path)), file.content);
|
|
10611
10665
|
const out = [];
|
|
10612
10666
|
const seen = /* @__PURE__ */ new Set();
|
|
10613
10667
|
const walk9 = (node) => {
|
|
@@ -10696,7 +10750,7 @@ function referencesTargetVar(call) {
|
|
|
10696
10750
|
}
|
|
10697
10751
|
function drizzleForeignKeys(file, serviceDir) {
|
|
10698
10752
|
if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
|
|
10699
|
-
const tree = parseSource3(
|
|
10753
|
+
const tree = parseSource3(parserForExt3(import_node_path43.default.extname(file.path)), file.content);
|
|
10700
10754
|
const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
|
|
10701
10755
|
const out = [];
|
|
10702
10756
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -11764,8 +11818,41 @@ var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
|
|
|
11764
11818
|
var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
|
|
11765
11819
|
var import_types36 = require("@neat.is/types");
|
|
11766
11820
|
init_otel();
|
|
11767
|
-
var SQL_METHODS = /* @__PURE__ */ new Set(["Exec", "ExecContext", "Query", "QueryContext", "QueryRow", "QueryRowContext"]);
|
|
11768
11821
|
var PARSE_CHUNK10 = 16384;
|
|
11822
|
+
var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
|
|
11823
|
+
"Query",
|
|
11824
|
+
"QueryContext",
|
|
11825
|
+
"QueryRow",
|
|
11826
|
+
"QueryRowContext",
|
|
11827
|
+
"Exec",
|
|
11828
|
+
"ExecContext",
|
|
11829
|
+
"Prepare",
|
|
11830
|
+
"PrepareContext"
|
|
11831
|
+
]);
|
|
11832
|
+
var SQLX_METHODS = /* @__PURE__ */ new Set([
|
|
11833
|
+
"Get",
|
|
11834
|
+
"Select",
|
|
11835
|
+
"Queryx",
|
|
11836
|
+
"QueryRowx",
|
|
11837
|
+
"NamedExec",
|
|
11838
|
+
"NamedQuery",
|
|
11839
|
+
"MustExec",
|
|
11840
|
+
"Preparex",
|
|
11841
|
+
"GetContext",
|
|
11842
|
+
"SelectContext"
|
|
11843
|
+
]);
|
|
11844
|
+
var DATABASE_SQL_IMPORT = "database/sql";
|
|
11845
|
+
var SQLX_IMPORT = "github.com/jmoiron/sqlx";
|
|
11846
|
+
function makeGoParser3() {
|
|
11847
|
+
const p = new import_tree_sitter14.default();
|
|
11848
|
+
p.setLanguage(import_tree_sitter_go3.default);
|
|
11849
|
+
return p;
|
|
11850
|
+
}
|
|
11851
|
+
function parseSource10(parser, source) {
|
|
11852
|
+
return parser.parse(
|
|
11853
|
+
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
|
|
11854
|
+
);
|
|
11855
|
+
}
|
|
11769
11856
|
function walk7(node, visit) {
|
|
11770
11857
|
visit(node);
|
|
11771
11858
|
for (let i = 0; i < node.namedChildCount; i++) {
|
|
@@ -11773,25 +11860,54 @@ function walk7(node, visit) {
|
|
|
11773
11860
|
if (child) walk7(child, visit);
|
|
11774
11861
|
}
|
|
11775
11862
|
}
|
|
11863
|
+
function goStringLiteralValue(node) {
|
|
11864
|
+
if (!node) return null;
|
|
11865
|
+
if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
|
|
11866
|
+
const t = node.text;
|
|
11867
|
+
return t.length >= 2 ? t.slice(1, -1) : "";
|
|
11868
|
+
}
|
|
11869
|
+
return null;
|
|
11870
|
+
}
|
|
11871
|
+
function goImportsAny(root, names) {
|
|
11872
|
+
let found = false;
|
|
11873
|
+
walk7(root, (node) => {
|
|
11874
|
+
if (found || node.type !== "import_spec") return;
|
|
11875
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
11876
|
+
const value = goStringLiteralValue(node.namedChild(i));
|
|
11877
|
+
if (value !== null && names.has(value)) found = true;
|
|
11878
|
+
}
|
|
11879
|
+
});
|
|
11880
|
+
return found;
|
|
11881
|
+
}
|
|
11882
|
+
function firstStringLiteralArg(argsNode) {
|
|
11883
|
+
if (!argsNode) return null;
|
|
11884
|
+
for (let i = 0; i < argsNode.namedChildCount; i++) {
|
|
11885
|
+
const value = goStringLiteralValue(argsNode.namedChild(i));
|
|
11886
|
+
if (value !== null) return value;
|
|
11887
|
+
}
|
|
11888
|
+
return null;
|
|
11889
|
+
}
|
|
11776
11890
|
function goSqlEndpointsFromFile(file, serviceDir) {
|
|
11777
11891
|
if (import_node_path49.default.extname(file.path) !== ".go") return [];
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
const
|
|
11781
|
-
|
|
11782
|
-
);
|
|
11892
|
+
if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
|
|
11893
|
+
const tree = parseSource10(makeGoParser3(), file.content);
|
|
11894
|
+
const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
|
|
11895
|
+
const importsSqlx = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([SQLX_IMPORT]));
|
|
11896
|
+
if (!importsDatabaseSql && !importsSqlx) return [];
|
|
11783
11897
|
const out = [];
|
|
11784
11898
|
walk7(tree.rootNode, (node) => {
|
|
11785
11899
|
if (node.type !== "call_expression") return;
|
|
11786
11900
|
const fn = node.childForFieldName("function");
|
|
11787
11901
|
if (fn?.type !== "selector_expression") return;
|
|
11788
11902
|
const method = fn.childForFieldName("field")?.text;
|
|
11789
|
-
if (!method
|
|
11790
|
-
const
|
|
11791
|
-
if (!
|
|
11792
|
-
const sql =
|
|
11903
|
+
if (!method) return;
|
|
11904
|
+
const recognized = DATABASE_SQL_METHODS.has(method) || importsSqlx && SQLX_METHODS.has(method);
|
|
11905
|
+
if (!recognized) return;
|
|
11906
|
+
const sql = firstStringLiteralArg(node.childForFieldName("arguments"));
|
|
11907
|
+
if (sql === null) return;
|
|
11793
11908
|
const table = tableFromSqlStatement(sql);
|
|
11794
11909
|
if (!table) return;
|
|
11910
|
+
const columns = columnsFromSqlStatement(sql);
|
|
11795
11911
|
const line = node.startPosition.row + 1;
|
|
11796
11912
|
out.push({
|
|
11797
11913
|
infraId: (0, import_types36.infraId)("sql-table", table),
|
|
@@ -11799,7 +11915,12 @@ function goSqlEndpointsFromFile(file, serviceDir) {
|
|
|
11799
11915
|
kind: "sql-table",
|
|
11800
11916
|
edgeType: "CALLS",
|
|
11801
11917
|
confidenceKind: "verified-call-site",
|
|
11802
|
-
|
|
11918
|
+
...columns.length > 0 ? { columns } : {},
|
|
11919
|
+
evidence: {
|
|
11920
|
+
file: toPosix(import_node_path49.default.relative(serviceDir, file.path)),
|
|
11921
|
+
line,
|
|
11922
|
+
snippet: snippet(file.content, line)
|
|
11923
|
+
}
|
|
11803
11924
|
});
|
|
11804
11925
|
});
|
|
11805
11926
|
return out;
|
|
@@ -11813,12 +11934,12 @@ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
|
|
|
11813
11934
|
var import_types37 = require("@neat.is/types");
|
|
11814
11935
|
var GORM_IMPORT_RE = /gorm\.io\/gorm/;
|
|
11815
11936
|
var PARSE_CHUNK11 = 16384;
|
|
11816
|
-
function
|
|
11937
|
+
function makeGoParser4() {
|
|
11817
11938
|
const p = new import_tree_sitter15.default();
|
|
11818
11939
|
p.setLanguage(import_tree_sitter_go4.default);
|
|
11819
11940
|
return p;
|
|
11820
11941
|
}
|
|
11821
|
-
function
|
|
11942
|
+
function parseSource11(parser, source) {
|
|
11822
11943
|
return parser.parse(
|
|
11823
11944
|
(index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
|
|
11824
11945
|
);
|
|
@@ -12242,7 +12363,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
|
|
|
12242
12363
|
function gormEndpointsFromFile(file, serviceDir) {
|
|
12243
12364
|
if (import_node_path50.default.extname(file.path) !== ".go") return [];
|
|
12244
12365
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
12245
|
-
const tree =
|
|
12366
|
+
const tree = parseSource11(makeGoParser4(), file.content);
|
|
12246
12367
|
const { structs, models, tableFor } = analyze(tree);
|
|
12247
12368
|
const out = [];
|
|
12248
12369
|
const seenTables = /* @__PURE__ */ new Set();
|
|
@@ -12273,7 +12394,7 @@ function gormEndpointsFromFile(file, serviceDir) {
|
|
|
12273
12394
|
function gormForeignKeys(file, serviceDir) {
|
|
12274
12395
|
if (import_node_path50.default.extname(file.path) !== ".go") return [];
|
|
12275
12396
|
if (!GORM_IMPORT_RE.test(file.content)) return [];
|
|
12276
|
-
const tree =
|
|
12397
|
+
const tree = parseSource11(makeGoParser4(), file.content);
|
|
12277
12398
|
const { structs, models, tableFor } = analyze(tree);
|
|
12278
12399
|
const out = [];
|
|
12279
12400
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -13403,7 +13524,7 @@ var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"),
|
|
|
13403
13524
|
var import_types48 = require("@neat.is/types");
|
|
13404
13525
|
var ZOD_IMPORT_RE = /\bzod\b/;
|
|
13405
13526
|
var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
|
|
13406
|
-
function
|
|
13527
|
+
function parserForExt4(ext) {
|
|
13407
13528
|
const p = new import_tree_sitter16.default();
|
|
13408
13529
|
p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
|
|
13409
13530
|
return p;
|
|
@@ -13492,7 +13613,7 @@ function topLevelSchemas(root) {
|
|
|
13492
13613
|
}
|
|
13493
13614
|
function zodShapesFromFile(file, serviceDir) {
|
|
13494
13615
|
if (!ZOD_IMPORT_RE.test(file.content)) return [];
|
|
13495
|
-
const tree = parseSource3(
|
|
13616
|
+
const tree = parseSource3(parserForExt4(import_node_path59.default.extname(file.path)), file.content);
|
|
13496
13617
|
const out = [];
|
|
13497
13618
|
const seen = /* @__PURE__ */ new Set();
|
|
13498
13619
|
for (const { name, call } of topLevelSchemas(tree.rootNode)) {
|
|
@@ -14556,6 +14677,23 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
14556
14677
|
unresolved++;
|
|
14557
14678
|
continue;
|
|
14558
14679
|
}
|
|
14680
|
+
if (signal.incident) {
|
|
14681
|
+
ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
14682
|
+
if (!ctx.errorsPath || !graph.hasNode(resolved.targetNodeId)) {
|
|
14683
|
+
unresolved++;
|
|
14684
|
+
continue;
|
|
14685
|
+
}
|
|
14686
|
+
await appendConnectorIncident(ctx.errorsPath, {
|
|
14687
|
+
id: signal.incident.id,
|
|
14688
|
+
timestamp: signal.incident.timestamp,
|
|
14689
|
+
service: signal.incident.service,
|
|
14690
|
+
errorType: signal.incident.errorType,
|
|
14691
|
+
errorMessage: signal.incident.errorMessage,
|
|
14692
|
+
...signal.incident.attributes ? { attributes: signal.incident.attributes } : {},
|
|
14693
|
+
affectedNode: resolved.targetNodeId
|
|
14694
|
+
});
|
|
14695
|
+
continue;
|
|
14696
|
+
}
|
|
14559
14697
|
if (resolved.ensureInfraNode) {
|
|
14560
14698
|
const { kind, name, provider } = resolved.ensureInfraNode;
|
|
14561
14699
|
ensureInfraNode(graph, kind, name, provider);
|
|
@@ -16749,6 +16887,338 @@ function createPlanetscaleConnector(graph, config, deps = {}) {
|
|
|
16749
16887
|
};
|
|
16750
16888
|
}
|
|
16751
16889
|
|
|
16890
|
+
// src/connectors/eas/index.ts
|
|
16891
|
+
init_cjs_shims();
|
|
16892
|
+
|
|
16893
|
+
// src/connectors/eas/client.ts
|
|
16894
|
+
init_cjs_shims();
|
|
16895
|
+
|
|
16896
|
+
// src/connectors/eas/types.ts
|
|
16897
|
+
init_cjs_shims();
|
|
16898
|
+
function readEasCredentials(raw) {
|
|
16899
|
+
const token = raw["token"];
|
|
16900
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
16901
|
+
throw new Error("eas connector: credentials.token (EXPO_TOKEN) must be a non-empty string");
|
|
16902
|
+
}
|
|
16903
|
+
return { token };
|
|
16904
|
+
}
|
|
16905
|
+
var EAS_STATUS_ERRORED = "ERRORED";
|
|
16906
|
+
var TRANSIENT_BUILD_PHASES = /* @__PURE__ */ new Set([
|
|
16907
|
+
"SPIN_UP_BUILDER",
|
|
16908
|
+
"PREPARE_CREDENTIALS",
|
|
16909
|
+
"RESTORE_CACHE",
|
|
16910
|
+
"UPLOAD_APPLICATION_ARCHIVE"
|
|
16911
|
+
]);
|
|
16912
|
+
var INTERNAL_ERROR_CODE = /INTERNAL_SERVER_ERROR|EAS_BUILD_.*INTERNAL|_INTERNAL_ERROR|UNKNOWN_ERROR/i;
|
|
16913
|
+
function isTransientFailure(err) {
|
|
16914
|
+
if (!err) return false;
|
|
16915
|
+
const phase = typeof err.buildPhase === "string" ? err.buildPhase : void 0;
|
|
16916
|
+
if (phase) {
|
|
16917
|
+
if (TRANSIENT_BUILD_PHASES.has(phase)) return true;
|
|
16918
|
+
if (/^SPIN_UP|_CREDENTIALS$|^RESTORE_CACHE/i.test(phase)) return true;
|
|
16919
|
+
}
|
|
16920
|
+
const code = typeof err.errorCode === "string" ? err.errorCode : void 0;
|
|
16921
|
+
if (code && INTERNAL_ERROR_CODE.test(code)) return true;
|
|
16922
|
+
return false;
|
|
16923
|
+
}
|
|
16924
|
+
var FIELD_SEP3 = "\0";
|
|
16925
|
+
var EAS_TARGET_KIND = "eas-build";
|
|
16926
|
+
function packEasTargetName(identity) {
|
|
16927
|
+
return [identity.serviceName, identity.phase].join(FIELD_SEP3);
|
|
16928
|
+
}
|
|
16929
|
+
function parseEasTargetName(targetName) {
|
|
16930
|
+
const sep = targetName.indexOf(FIELD_SEP3);
|
|
16931
|
+
if (sep === -1) return null;
|
|
16932
|
+
const serviceName = targetName.slice(0, sep);
|
|
16933
|
+
const phase = targetName.slice(sep + 1);
|
|
16934
|
+
if (!serviceName) return null;
|
|
16935
|
+
return { serviceName, phase };
|
|
16936
|
+
}
|
|
16937
|
+
|
|
16938
|
+
// src/connectors/eas/client.ts
|
|
16939
|
+
var DEFAULT_EAS_API_URL = "https://api.expo.dev/graphql";
|
|
16940
|
+
var DEFAULT_PAGE_SIZE = 50;
|
|
16941
|
+
var DEFAULT_MAX_PAGES = 10;
|
|
16942
|
+
var DEFAULT_MAX_LOG_BYTES = 16 * 1024;
|
|
16943
|
+
var DEFAULT_MAX_LOOKBACK_MS6 = 7 * 24 * 60 * 60 * 1e3;
|
|
16944
|
+
var BUILDS_QUERY = `
|
|
16945
|
+
query NeatEasErroredBuilds($appId: String!, $offset: Int!, $limit: Int!) {
|
|
16946
|
+
app {
|
|
16947
|
+
byId(appId: $appId) {
|
|
16948
|
+
builds(offset: $offset, limit: $limit, filter: { status: ERRORED }) {
|
|
16949
|
+
id
|
|
16950
|
+
status
|
|
16951
|
+
platform
|
|
16952
|
+
buildProfile
|
|
16953
|
+
gitCommitHash
|
|
16954
|
+
gitCommitMessage
|
|
16955
|
+
gitRef
|
|
16956
|
+
isGitWorkingTreeDirty
|
|
16957
|
+
createdAt
|
|
16958
|
+
completedAt
|
|
16959
|
+
error {
|
|
16960
|
+
buildPhase
|
|
16961
|
+
errorCode
|
|
16962
|
+
message
|
|
16963
|
+
docsUrl
|
|
16964
|
+
}
|
|
16965
|
+
logFileUrls
|
|
16966
|
+
}
|
|
16967
|
+
}
|
|
16968
|
+
}
|
|
16969
|
+
}
|
|
16970
|
+
`;
|
|
16971
|
+
async function easGraphQL(apiUrl, token, query, variables, accountKey, fetchImpl) {
|
|
16972
|
+
const res = await junctionFetch(
|
|
16973
|
+
apiUrl,
|
|
16974
|
+
{
|
|
16975
|
+
method: "POST",
|
|
16976
|
+
headers: {
|
|
16977
|
+
"Content-Type": "application/json",
|
|
16978
|
+
...bearerAuthHeader(token)
|
|
16979
|
+
},
|
|
16980
|
+
body: JSON.stringify({ query, variables })
|
|
16981
|
+
},
|
|
16982
|
+
// accountKey: the Expo app id — the per-(provider, accountKey) rate-limit
|
|
16983
|
+
// bucket (ADR-131), the closest thing this connector carries to "one account".
|
|
16984
|
+
{ provider: "eas", accountKey, ...fetchImpl ? { fetchImpl } : {} }
|
|
16985
|
+
);
|
|
16986
|
+
if (!res.ok) {
|
|
16987
|
+
throw new Error(`Expo GraphQL request failed: ${res.status} ${res.statusText}`);
|
|
16988
|
+
}
|
|
16989
|
+
const body = await res.json();
|
|
16990
|
+
if (body.errors && body.errors.length > 0) {
|
|
16991
|
+
throw new Error(`Expo GraphQL errors: ${body.errors.map((e) => e.message).join("; ")}`);
|
|
16992
|
+
}
|
|
16993
|
+
if (!body.data) throw new Error("Expo GraphQL response carried no data");
|
|
16994
|
+
return body.data;
|
|
16995
|
+
}
|
|
16996
|
+
async function fetchErroredBuilds(token, config, fetchImpl) {
|
|
16997
|
+
const apiUrl = config.apiUrl ?? DEFAULT_EAS_API_URL;
|
|
16998
|
+
const pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
16999
|
+
const maxPages = config.maxPages ?? DEFAULT_MAX_PAGES;
|
|
17000
|
+
const out = [];
|
|
17001
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17002
|
+
for (let page = 0; page < maxPages; page++) {
|
|
17003
|
+
const data = await easGraphQL(
|
|
17004
|
+
apiUrl,
|
|
17005
|
+
token,
|
|
17006
|
+
BUILDS_QUERY,
|
|
17007
|
+
{ appId: config.appId, offset: page * pageSize, limit: pageSize },
|
|
17008
|
+
config.appId,
|
|
17009
|
+
fetchImpl
|
|
17010
|
+
);
|
|
17011
|
+
const builds = data.app?.byId?.builds;
|
|
17012
|
+
if (!Array.isArray(builds)) break;
|
|
17013
|
+
let added = 0;
|
|
17014
|
+
for (const b of builds) {
|
|
17015
|
+
if (!b || typeof b.id !== "string" || b.id.length === 0) continue;
|
|
17016
|
+
if (b.status !== EAS_STATUS_ERRORED) continue;
|
|
17017
|
+
if (seen.has(b.id)) continue;
|
|
17018
|
+
seen.add(b.id);
|
|
17019
|
+
out.push(b);
|
|
17020
|
+
added++;
|
|
17021
|
+
}
|
|
17022
|
+
if (builds.length < pageSize) break;
|
|
17023
|
+
if (added === 0) break;
|
|
17024
|
+
}
|
|
17025
|
+
return out;
|
|
17026
|
+
}
|
|
17027
|
+
async function fetchBuildLogs(logFileUrls, maxBytes = DEFAULT_MAX_LOG_BYTES, fetchImpl) {
|
|
17028
|
+
if (!Array.isArray(logFileUrls) || logFileUrls.length === 0) return "";
|
|
17029
|
+
const doFetch = fetchImpl ?? fetch;
|
|
17030
|
+
const chunks = [];
|
|
17031
|
+
for (const url of logFileUrls) {
|
|
17032
|
+
if (typeof url !== "string" || url.length === 0) continue;
|
|
17033
|
+
try {
|
|
17034
|
+
const res = await doFetch(url);
|
|
17035
|
+
if (!res.ok) continue;
|
|
17036
|
+
chunks.push(await res.text());
|
|
17037
|
+
} catch {
|
|
17038
|
+
}
|
|
17039
|
+
}
|
|
17040
|
+
const joined = chunks.join("\n");
|
|
17041
|
+
return joined.length > maxBytes ? joined.slice(joined.length - maxBytes) : joined;
|
|
17042
|
+
}
|
|
17043
|
+
|
|
17044
|
+
// src/connectors/eas/map.ts
|
|
17045
|
+
init_cjs_shims();
|
|
17046
|
+
function buildEventTime(build) {
|
|
17047
|
+
if (typeof build.completedAt === "string" && build.completedAt.length > 0) return build.completedAt;
|
|
17048
|
+
if (typeof build.createdAt === "string" && build.createdAt.length > 0) return build.createdAt;
|
|
17049
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
17050
|
+
}
|
|
17051
|
+
function incidentMessage2(build) {
|
|
17052
|
+
const err = build.error ?? {};
|
|
17053
|
+
const phase = typeof err.buildPhase === "string" && err.buildPhase.length > 0 ? ` at ${err.buildPhase}` : "";
|
|
17054
|
+
const detail = typeof err.message === "string" && err.message.trim().length > 0 && err.message.trim() || typeof err.errorCode === "string" && err.errorCode.length > 0 && err.errorCode || "no error detail reported";
|
|
17055
|
+
let msg = `EAS build failed${phase}: ${detail}`;
|
|
17056
|
+
if (build.isGitWorkingTreeDirty === true) {
|
|
17057
|
+
msg += " (built from a dirty working tree \u2014 the commit may not represent what built)";
|
|
17058
|
+
}
|
|
17059
|
+
return msg;
|
|
17060
|
+
}
|
|
17061
|
+
function incidentAttributes(build) {
|
|
17062
|
+
const attrs = {};
|
|
17063
|
+
const err = build.error ?? {};
|
|
17064
|
+
const put = (k, v) => {
|
|
17065
|
+
if (typeof v === "string" && v.length === 0) return;
|
|
17066
|
+
if (v !== void 0 && v !== null) attrs[k] = v;
|
|
17067
|
+
};
|
|
17068
|
+
put("eas.buildId", build.id);
|
|
17069
|
+
put("eas.platform", build.platform ?? void 0);
|
|
17070
|
+
put("eas.buildProfile", build.buildProfile ?? void 0);
|
|
17071
|
+
put("eas.buildPhase", err.buildPhase ?? void 0);
|
|
17072
|
+
put("eas.errorCode", err.errorCode ?? void 0);
|
|
17073
|
+
put("eas.docsUrl", err.docsUrl ?? void 0);
|
|
17074
|
+
put("eas.gitCommitHash", build.gitCommitHash ?? void 0);
|
|
17075
|
+
put("eas.gitRef", build.gitRef ?? void 0);
|
|
17076
|
+
put("eas.gitCommitMessage", build.gitCommitMessage ?? void 0);
|
|
17077
|
+
if (typeof build.isGitWorkingTreeDirty === "boolean") {
|
|
17078
|
+
attrs["eas.gitWorkingTreeDirty"] = build.isGitWorkingTreeDirty;
|
|
17079
|
+
if (build.isGitWorkingTreeDirty) attrs["eas.confidence"] = "low";
|
|
17080
|
+
}
|
|
17081
|
+
put("eas.createdAt", build.createdAt ?? void 0);
|
|
17082
|
+
put("eas.completedAt", build.completedAt ?? void 0);
|
|
17083
|
+
if (typeof build.logsText === "string" && build.logsText.length > 0) {
|
|
17084
|
+
attrs["eas.logs"] = build.logsText;
|
|
17085
|
+
}
|
|
17086
|
+
return attrs;
|
|
17087
|
+
}
|
|
17088
|
+
function mapBuildToSignal(build, serviceName) {
|
|
17089
|
+
if (!build || typeof build !== "object") return null;
|
|
17090
|
+
if (build.status !== EAS_STATUS_ERRORED) return null;
|
|
17091
|
+
if (!build.error) return null;
|
|
17092
|
+
if (isTransientFailure(build.error)) return null;
|
|
17093
|
+
const timestamp = buildEventTime(build);
|
|
17094
|
+
const phase = typeof build.error.buildPhase === "string" ? build.error.buildPhase : "";
|
|
17095
|
+
return {
|
|
17096
|
+
targetKind: EAS_TARGET_KIND,
|
|
17097
|
+
targetName: packEasTargetName({ serviceName, phase }),
|
|
17098
|
+
// Incident-only — no edge, so no call/error count to replay.
|
|
17099
|
+
callCount: 0,
|
|
17100
|
+
errorCount: 0,
|
|
17101
|
+
lastObservedIso: timestamp,
|
|
17102
|
+
incident: {
|
|
17103
|
+
id: `eas:build:${build.id}`,
|
|
17104
|
+
timestamp,
|
|
17105
|
+
service: serviceName,
|
|
17106
|
+
errorType: "eas-build-failure",
|
|
17107
|
+
errorMessage: incidentMessage2(build),
|
|
17108
|
+
attributes: incidentAttributes(build)
|
|
17109
|
+
}
|
|
17110
|
+
};
|
|
17111
|
+
}
|
|
17112
|
+
function mapBuildsToSignals(builds, serviceName) {
|
|
17113
|
+
const out = [];
|
|
17114
|
+
for (const build of builds) {
|
|
17115
|
+
const signal = mapBuildToSignal(build, serviceName);
|
|
17116
|
+
if (signal) out.push(signal);
|
|
17117
|
+
}
|
|
17118
|
+
return out;
|
|
17119
|
+
}
|
|
17120
|
+
|
|
17121
|
+
// src/connectors/eas/resolve.ts
|
|
17122
|
+
init_cjs_shims();
|
|
17123
|
+
var import_types82 = require("@neat.is/types");
|
|
17124
|
+
var NO_ENV2 = "unknown";
|
|
17125
|
+
var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
|
|
17126
|
+
var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
|
|
17127
|
+
"READ_APP_CONFIG",
|
|
17128
|
+
"CONFIGURE_EXPO_UPDATES",
|
|
17129
|
+
"CALCULATE_EXPO_UPDATES_RUNTIME_VERSION"
|
|
17130
|
+
]);
|
|
17131
|
+
function configBasenamesForPhase(phase) {
|
|
17132
|
+
if (EAS_JSON_PHASES.has(phase)) return ["eas.json"];
|
|
17133
|
+
if (APP_CONFIG_PHASES.has(phase)) return ["app.json", "app.config.json"];
|
|
17134
|
+
return [];
|
|
17135
|
+
}
|
|
17136
|
+
function configNodeService(graph, configNodeId) {
|
|
17137
|
+
for (const edgeId of graph.inboundEdges(configNodeId)) {
|
|
17138
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
17139
|
+
if (edge.type !== import_types82.EdgeType.CONFIGURED_BY) continue;
|
|
17140
|
+
const parsed = (0, import_types82.parseFileId)(edge.source);
|
|
17141
|
+
if (parsed) return parsed.service;
|
|
17142
|
+
}
|
|
17143
|
+
return null;
|
|
17144
|
+
}
|
|
17145
|
+
function findConfigNode(graph, basenames, serviceName) {
|
|
17146
|
+
let scoped = null;
|
|
17147
|
+
let anyMatch = null;
|
|
17148
|
+
graph.forEachNode((id, attrs) => {
|
|
17149
|
+
if (scoped) return;
|
|
17150
|
+
const node = attrs;
|
|
17151
|
+
if (node.type !== import_types82.NodeType.ConfigNode) return;
|
|
17152
|
+
if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
|
|
17153
|
+
if (anyMatch === null) anyMatch = id;
|
|
17154
|
+
if (configNodeService(graph, id) === serviceName) scoped = id;
|
|
17155
|
+
});
|
|
17156
|
+
return scoped ?? anyMatch;
|
|
17157
|
+
}
|
|
17158
|
+
function createEasResolveTarget(graph) {
|
|
17159
|
+
return (signal) => {
|
|
17160
|
+
if (signal.targetKind !== EAS_TARGET_KIND) return null;
|
|
17161
|
+
const identity = parseEasTargetName(signal.targetName);
|
|
17162
|
+
if (!identity) return null;
|
|
17163
|
+
const { serviceName, phase } = identity;
|
|
17164
|
+
const basenames = configBasenamesForPhase(phase);
|
|
17165
|
+
if (basenames.length > 0) {
|
|
17166
|
+
const configNodeId = findConfigNode(graph, basenames, serviceName);
|
|
17167
|
+
if (configNodeId) {
|
|
17168
|
+
return { targetNodeId: configNodeId, serviceName, edgeType: import_types82.EdgeType.CALLS };
|
|
17169
|
+
}
|
|
17170
|
+
}
|
|
17171
|
+
return {
|
|
17172
|
+
targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
|
|
17173
|
+
serviceName,
|
|
17174
|
+
edgeType: import_types82.EdgeType.CALLS
|
|
17175
|
+
};
|
|
17176
|
+
};
|
|
17177
|
+
}
|
|
17178
|
+
|
|
17179
|
+
// src/connectors/eas/index.ts
|
|
17180
|
+
function isBuildSince(build, sinceIso) {
|
|
17181
|
+
const t = Date.parse(buildEventTime(build));
|
|
17182
|
+
const s = Date.parse(sinceIso);
|
|
17183
|
+
if (Number.isNaN(t) || Number.isNaN(s)) return true;
|
|
17184
|
+
return t > s;
|
|
17185
|
+
}
|
|
17186
|
+
function boundedSinceIso2(since, now, maxLookbackMs) {
|
|
17187
|
+
const floor = new Date(now.getTime() - maxLookbackMs);
|
|
17188
|
+
if (!since) return floor.toISOString();
|
|
17189
|
+
const sinceMs = new Date(since).getTime();
|
|
17190
|
+
if (Number.isNaN(sinceMs)) return floor.toISOString();
|
|
17191
|
+
return sinceMs < floor.getTime() ? floor.toISOString() : new Date(sinceMs).toISOString();
|
|
17192
|
+
}
|
|
17193
|
+
var EasConnector = class {
|
|
17194
|
+
constructor(config, fetchImpl) {
|
|
17195
|
+
this.config = config;
|
|
17196
|
+
this.fetchImpl = fetchImpl;
|
|
17197
|
+
}
|
|
17198
|
+
config;
|
|
17199
|
+
fetchImpl;
|
|
17200
|
+
provider = "eas";
|
|
17201
|
+
async poll(ctx) {
|
|
17202
|
+
const creds = readEasCredentials(ctx.credentials);
|
|
17203
|
+
const serviceName = this.config.serviceName ?? this.config.appId;
|
|
17204
|
+
const maxLookbackMs = this.config.maxLookbackMs ?? DEFAULT_MAX_LOOKBACK_MS6;
|
|
17205
|
+
const sinceIso = boundedSinceIso2(ctx.since, /* @__PURE__ */ new Date(), maxLookbackMs);
|
|
17206
|
+
const builds = await fetchErroredBuilds(creds.token, this.config, this.fetchImpl);
|
|
17207
|
+
const fresh = builds.filter((b) => isBuildSince(b, sinceIso));
|
|
17208
|
+
const maxLogBytes = this.config.maxLogBytes ?? DEFAULT_MAX_LOG_BYTES;
|
|
17209
|
+
for (const build of fresh) {
|
|
17210
|
+
build.logsText = await fetchBuildLogs(build.logFileUrls, maxLogBytes, this.fetchImpl);
|
|
17211
|
+
}
|
|
17212
|
+
return mapBuildsToSignals(fresh, serviceName);
|
|
17213
|
+
}
|
|
17214
|
+
};
|
|
17215
|
+
function createEasConnector(graph, config, fetchImpl) {
|
|
17216
|
+
return {
|
|
17217
|
+
connector: new EasConnector(config, fetchImpl),
|
|
17218
|
+
resolveTarget: createEasResolveTarget(graph)
|
|
17219
|
+
};
|
|
17220
|
+
}
|
|
17221
|
+
|
|
16752
17222
|
// src/connectors/registry.ts
|
|
16753
17223
|
var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
|
|
16754
17224
|
async function authProbe(input) {
|
|
@@ -17036,6 +17506,41 @@ var PROVIDER_DISPATCH = {
|
|
|
17036
17506
|
...fetchImpl ? { fetchImpl } : {}
|
|
17037
17507
|
});
|
|
17038
17508
|
}
|
|
17509
|
+
},
|
|
17510
|
+
eas: {
|
|
17511
|
+
provider: "eas",
|
|
17512
|
+
// The secret is a single robot-user EXPO_TOKEN; a single-string credential
|
|
17513
|
+
// maps to `token`. `appId` is non-secret config (connector-config.md §7.1).
|
|
17514
|
+
primaryCredentialKey: "token",
|
|
17515
|
+
requiredCredentialFields: ["token"],
|
|
17516
|
+
requiredOptionFields: ["appId"],
|
|
17517
|
+
build(graph, options) {
|
|
17518
|
+
return createEasConnector(graph, options);
|
|
17519
|
+
},
|
|
17520
|
+
// Runs the connector's real `builds` query at limit 1 — the exact read poll()
|
|
17521
|
+
// performs, minus the pages — so the probe checks both that the EXPO_TOKEN
|
|
17522
|
+
// authenticates and that this app id is reachable, the same probe-the-real-
|
|
17523
|
+
// query discipline Railway and Cloud Run use over a trivial `{ __typename }`.
|
|
17524
|
+
// A bad or wrong-scoped token comes back as an Expo GraphQL error, which
|
|
17525
|
+
// `fetchErroredBuilds` throws on, so it fails honestly here rather than
|
|
17526
|
+
// silently at the first poll.
|
|
17527
|
+
async validate({ credentials, options, fetchImpl }) {
|
|
17528
|
+
const cfg = options;
|
|
17529
|
+
const appId = String(cfg.appId ?? "");
|
|
17530
|
+
if (!appId) return { ok: false, reason: "eas: appId is required to validate" };
|
|
17531
|
+
const probeConfig = {
|
|
17532
|
+
appId,
|
|
17533
|
+
pageSize: 1,
|
|
17534
|
+
maxPages: 1,
|
|
17535
|
+
...cfg.apiUrl ? { apiUrl: cfg.apiUrl } : {}
|
|
17536
|
+
};
|
|
17537
|
+
try {
|
|
17538
|
+
await fetchErroredBuilds(String(credentials.token ?? ""), probeConfig, fetchImpl);
|
|
17539
|
+
return { ok: true };
|
|
17540
|
+
} catch (err) {
|
|
17541
|
+
return { ok: false, reason: `eas auth check failed: ${err.message}` };
|
|
17542
|
+
}
|
|
17543
|
+
}
|
|
17039
17544
|
}
|
|
17040
17545
|
};
|
|
17041
17546
|
function vercelCredsFrom(credentials) {
|
|
@@ -17345,11 +17850,11 @@ function registerRoutes(scope, ctx) {
|
|
|
17345
17850
|
const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
17346
17851
|
const parsed = [];
|
|
17347
17852
|
for (const c of candidates) {
|
|
17348
|
-
const r =
|
|
17853
|
+
const r = import_types85.DivergenceTypeSchema.safeParse(c);
|
|
17349
17854
|
if (!r.success) {
|
|
17350
17855
|
return reply.code(400).send({
|
|
17351
17856
|
error: `unknown divergence type "${c}"`,
|
|
17352
|
-
allowed:
|
|
17857
|
+
allowed: import_types85.DivergenceTypeSchema.options
|
|
17353
17858
|
});
|
|
17354
17859
|
}
|
|
17355
17860
|
parsed.push(r.data);
|
|
@@ -17456,10 +17961,15 @@ function registerRoutes(scope, ctx) {
|
|
|
17456
17961
|
}
|
|
17457
17962
|
const reg = built.registration;
|
|
17458
17963
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
17964
|
+
const incidentsPath = errorsPathFor(proj);
|
|
17459
17965
|
try {
|
|
17460
17966
|
const result = await ctx.runPoll(
|
|
17461
17967
|
reg.connector,
|
|
17462
|
-
{
|
|
17968
|
+
{
|
|
17969
|
+
projectDir: proj.scanPath ?? "",
|
|
17970
|
+
credentials: reg.credentials,
|
|
17971
|
+
...incidentsPath ? { errorsPath: incidentsPath } : {}
|
|
17972
|
+
},
|
|
17463
17973
|
proj.graph,
|
|
17464
17974
|
reg.resolveTarget
|
|
17465
17975
|
);
|
|
@@ -17658,7 +18168,7 @@ function registerRoutes(scope, ctx) {
|
|
|
17658
18168
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
17659
18169
|
let violations = await log.readAll();
|
|
17660
18170
|
if (req.query.severity) {
|
|
17661
|
-
const sev =
|
|
18171
|
+
const sev = import_types85.PolicySeveritySchema.safeParse(req.query.severity);
|
|
17662
18172
|
if (!sev.success) {
|
|
17663
18173
|
return reply.code(400).send({
|
|
17664
18174
|
error: "invalid severity",
|
|
@@ -17697,7 +18207,7 @@ function registerRoutes(scope, ctx) {
|
|
|
17697
18207
|
scope.post("/policies/check", async (req, reply) => {
|
|
17698
18208
|
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
17699
18209
|
if (!proj) return;
|
|
17700
|
-
const parsed =
|
|
18210
|
+
const parsed = import_types85.PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
17701
18211
|
if (!parsed.success) {
|
|
17702
18212
|
return reply.code(400).send({
|
|
17703
18213
|
error: "invalid /policies/check body",
|
|
@@ -18299,12 +18809,46 @@ var SubstringIndex = class {
|
|
|
18299
18809
|
this.graph = graph;
|
|
18300
18810
|
}
|
|
18301
18811
|
};
|
|
18812
|
+
var DEFAULT_SEARCH_INIT_TIMEOUT_MS = 3e4;
|
|
18813
|
+
function searchInitTimeoutMs() {
|
|
18814
|
+
const env = process.env.NEAT_SEARCH_INIT_TIMEOUT_MS;
|
|
18815
|
+
if (env !== void 0 && env.length > 0) {
|
|
18816
|
+
const n = Number.parseInt(env, 10);
|
|
18817
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
18818
|
+
}
|
|
18819
|
+
return DEFAULT_SEARCH_INIT_TIMEOUT_MS;
|
|
18820
|
+
}
|
|
18821
|
+
async function resolveEmbedderBounded(factory, timeoutMs) {
|
|
18822
|
+
if (timeoutMs <= 0) return factory();
|
|
18823
|
+
let timer;
|
|
18824
|
+
const TIMED_OUT = /* @__PURE__ */ Symbol("embedder-init-timeout");
|
|
18825
|
+
const timeout = new Promise((resolve) => {
|
|
18826
|
+
timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
|
|
18827
|
+
timer.unref?.();
|
|
18828
|
+
});
|
|
18829
|
+
try {
|
|
18830
|
+
const result = await Promise.race([factory(), timeout]);
|
|
18831
|
+
if (result === TIMED_OUT) {
|
|
18832
|
+
console.warn(
|
|
18833
|
+
`semantic_search: embedder init exceeded ${timeoutMs}ms; falling back to substring search. Set NEAT_SEARCH_INIT_TIMEOUT_MS to raise the bound (or 0 to wait indefinitely).`
|
|
18834
|
+
);
|
|
18835
|
+
return null;
|
|
18836
|
+
}
|
|
18837
|
+
return result;
|
|
18838
|
+
} finally {
|
|
18839
|
+
if (timer) clearTimeout(timer);
|
|
18840
|
+
}
|
|
18841
|
+
}
|
|
18302
18842
|
async function buildSearchIndex(graph, options = {}) {
|
|
18303
18843
|
let embedder = null;
|
|
18304
18844
|
if (options.embedder) {
|
|
18305
18845
|
embedder = options.embedder;
|
|
18306
18846
|
} else if (options.forceProvider !== "substring") {
|
|
18307
|
-
|
|
18847
|
+
const factory = options.embedderFactory ?? pickEmbedder;
|
|
18848
|
+
embedder = await resolveEmbedderBounded(
|
|
18849
|
+
factory,
|
|
18850
|
+
options.initTimeoutMs ?? searchInitTimeoutMs()
|
|
18851
|
+
);
|
|
18308
18852
|
if (options.forceProvider === "ollama" && embedder?.provider !== "ollama") {
|
|
18309
18853
|
embedder = null;
|
|
18310
18854
|
}
|