@neat.is/core 0.7.8 → 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/neatd.js CHANGED
@@ -2,15 +2,15 @@
2
2
  import {
3
3
  reconcileDaemonRecordSync,
4
4
  startDaemon
5
- } from "./chunk-OVRVDSUO.js";
5
+ } from "./chunk-ILG3SMD5.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-LN75Z624.js";
9
+ } from "./chunk-N5TPODCX.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
13
- } from "./chunk-Y43UCVZS.js";
13
+ } from "./chunk-UUYCTH2E.js";
14
14
 
15
15
  // src/neatd.ts
16
16
  import { promises as fs } from "fs";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  reshapeGrpcRequest,
3
3
  startOtelGrpcReceiver
4
- } from "./chunk-4GCV46AP.js";
5
- import "./chunk-Y43UCVZS.js";
4
+ } from "./chunk-FCO5Z3RW.js";
5
+ import "./chunk-UUYCTH2E.js";
6
6
  export {
7
7
  reshapeGrpcRequest,
8
8
  startOtelGrpcReceiver
9
9
  };
10
- //# sourceMappingURL=otel-grpc-DPSAGRUU.js.map
10
+ //# sourceMappingURL=otel-grpc-CO47JRIN.js.map
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
- const msg = Type.create({});
509
- const encoded = Type.encode(msg).finish();
510
- cachedProtobufResponseBody = Buffer.from(encoded);
511
- return cachedProtobufResponseBody;
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) => {
@@ -4565,10 +4581,15 @@ function resolveDistToSrc(absFilepath, line) {
4565
4581
  }
4566
4582
  if (!entry) return null;
4567
4583
  try {
4568
- const pos = entry.consumer.originalPositionFor({
4569
- line: line !== void 0 && Number.isFinite(line) ? line : 1,
4570
- column: 0
4571
- });
4584
+ const queryLine = line !== void 0 && Number.isFinite(line) ? line : 1;
4585
+ let pos = entry.consumer.originalPositionFor({ line: queryLine, column: 0 });
4586
+ if (!pos || !pos.source) {
4587
+ pos = entry.consumer.originalPositionFor({
4588
+ line: queryLine,
4589
+ column: 0,
4590
+ bias: sourceMapJs.SourceMapConsumer.LEAST_UPPER_BOUND
4591
+ });
4592
+ }
4572
4593
  if (!pos || !pos.source) return null;
4573
4594
  const root = entry.consumer.sourceRoot ?? "";
4574
4595
  const resolved = import_node_path10.default.resolve(entry.dir, root, pos.source);
@@ -4577,6 +4598,9 @@ function resolveDistToSrc(absFilepath, line) {
4577
4598
  return null;
4578
4599
  }
4579
4600
  }
4601
+ function hasAdjacentSourceMap(absFilepath) {
4602
+ return sourceMapCache.get(absFilepath) != null;
4603
+ }
4580
4604
  function callSiteFromSpan(span, serviceNode, scanPath) {
4581
4605
  const filepath = codeFilepathOf(span.attributes);
4582
4606
  if (filepath === void 0) return null;
@@ -4592,7 +4616,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
4592
4616
  }
4593
4617
  const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath);
4594
4618
  if (!relPath) return null;
4595
- if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
4619
+ if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && !hasAdjacentSourceMap(abs) && serviceNode?.name) {
4596
4620
  warnNoSourceMaps(serviceNode.name);
4597
4621
  }
4598
4622
  const fn = codeFunctionOf(span.attributes);
@@ -4868,7 +4892,7 @@ function resolveServiceId(graph, host, env) {
4868
4892
  function frontierIdFor(host) {
4869
4893
  return (0, import_types7.frontierId)(host);
4870
4894
  }
4871
- function ensureServiceNode(graph, serviceName, env) {
4895
+ function resolveFusedServiceId(graph, serviceName, env) {
4872
4896
  const id = (0, import_types7.serviceId)(serviceName, env);
4873
4897
  if (graph.hasNode(id)) return id;
4874
4898
  const wanted = serviceName.toLowerCase();
@@ -4878,17 +4902,21 @@ function ensureServiceNode(graph, serviceName, env) {
4878
4902
  if (svc.discoveredVia === "otel") return false;
4879
4903
  return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
4880
4904
  });
4881
- if (extractedId) return extractedId;
4905
+ return extractedId ?? id;
4906
+ }
4907
+ function ensureServiceNode(graph, serviceName, env) {
4908
+ const resolved = resolveFusedServiceId(graph, serviceName, env);
4909
+ if (graph.hasNode(resolved)) return resolved;
4882
4910
  const node = {
4883
- id,
4911
+ id: resolved,
4884
4912
  type: import_types7.NodeType.ServiceNode,
4885
4913
  name: serviceName,
4886
4914
  language: "unknown",
4887
4915
  discoveredVia: "otel",
4888
4916
  ...env !== "unknown" ? { env } : {}
4889
4917
  };
4890
- graph.addNode(id, node);
4891
- return id;
4918
+ graph.addNode(resolved, node);
4919
+ return resolved;
4892
4920
  }
4893
4921
  function ensureInfraNode(graph, kind, name, provider) {
4894
4922
  const id = (0, import_types7.infraId)(kind, name);
@@ -5082,7 +5110,7 @@ async function appendErrorEvent(ctx, ev) {
5082
5110
  await import_node_fs9.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
5083
5111
  }
5084
5112
  function incidentAffectedNode(span, graph, scanPath) {
5085
- const sid = (0, import_types7.serviceId)(span.service, span.env);
5113
+ const sid = graph ? resolveFusedServiceId(graph, span.service, span.env) : (0, import_types7.serviceId)(span.service, span.env);
5086
5114
  const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
5087
5115
  const callSite = callSiteFromSpan(span, serviceNode, scanPath);
5088
5116
  if (callSite) {
@@ -7363,7 +7391,7 @@ function disambiguate(defs) {
7363
7391
  }
7364
7392
  async function addSymbols(graph, services) {
7365
7393
  const parsers = /* @__PURE__ */ new Map();
7366
- const parserForExt4 = (ext) => {
7394
+ const parserForExt5 = (ext) => {
7367
7395
  const grammar = GRAMMAR_BY_EXT[ext];
7368
7396
  if (!grammar) return null;
7369
7397
  let parser = parsers.get(ext);
@@ -7379,7 +7407,7 @@ async function addSymbols(graph, services) {
7379
7407
  for (const service of services) {
7380
7408
  const files = await loadSourceFiles(service.dir);
7381
7409
  for (const file of files) {
7382
- const parser = parserForExt4(import_node_path19.default.extname(file.path));
7410
+ const parser = parserForExt5(import_node_path19.default.extname(file.path));
7383
7411
  if (!parser) continue;
7384
7412
  const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
7385
7413
  let defs;
@@ -7531,7 +7559,7 @@ function stringInner(node) {
7531
7559
  }
7532
7560
  async function addSymbolEdges(graph, services) {
7533
7561
  const parsers = /* @__PURE__ */ new Map();
7534
- const parserForExt4 = (ext) => {
7562
+ const parserForExt5 = (ext) => {
7535
7563
  const grammar = GRAMMAR_BY_EXT[ext];
7536
7564
  if (!grammar) return null;
7537
7565
  let parser = parsers.get(ext);
@@ -7548,7 +7576,7 @@ async function addSymbolEdges(graph, services) {
7548
7576
  const tsPaths = await loadTsPathConfig(service.dir);
7549
7577
  const files = await loadSourceFiles(service.dir);
7550
7578
  for (const file of files) {
7551
- const parser = parserForExt4(import_node_path20.default.extname(file.path));
7579
+ const parser = parserForExt5(import_node_path20.default.extname(file.path));
7552
7580
  if (!parser) continue;
7553
7581
  const relPath = toPosix(import_node_path20.default.relative(service.dir, file.path));
7554
7582
  const fileDir = import_node_path20.default.dirname(file.path);
@@ -7810,7 +7838,7 @@ function firstReferenceLines(root, wanted) {
7810
7838
  }
7811
7839
  async function addServerActions(graph, services) {
7812
7840
  const parsers = /* @__PURE__ */ new Map();
7813
- const parserForExt4 = (ext) => {
7841
+ const parserForExt5 = (ext) => {
7814
7842
  const grammar = GRAMMAR_BY_EXT[ext];
7815
7843
  if (!grammar) return null;
7816
7844
  let parser = parsers.get(ext);
@@ -7833,7 +7861,7 @@ async function addServerActions(graph, services) {
7833
7861
  const files = await loadSourceFiles(service.dir);
7834
7862
  for (const file of files) {
7835
7863
  if (isTestPath(file.path)) continue;
7836
- const parser = parserForExt4(import_node_path21.default.extname(file.path));
7864
+ const parser = parserForExt5(import_node_path21.default.extname(file.path));
7837
7865
  if (!parser) continue;
7838
7866
  const relPath = toPosix(import_node_path21.default.relative(service.dir, file.path));
7839
7867
  let root;
@@ -7896,7 +7924,7 @@ async function addServerActions(graph, services) {
7896
7924
  }
7897
7925
  for (const file of files) {
7898
7926
  if (isTestPath(file.path)) continue;
7899
- const parser = parserForExt4(import_node_path21.default.extname(file.path));
7927
+ const parser = parserForExt5(import_node_path21.default.extname(file.path));
7900
7928
  if (!parser) continue;
7901
7929
  const relPath = toPosix(import_node_path21.default.relative(service.dir, file.path));
7902
7930
  const fileDir = import_node_path21.default.dirname(file.path);
@@ -8860,6 +8888,7 @@ init_cjs_shims();
8860
8888
  var import_node_path32 = __toESM(require("path"), 1);
8861
8889
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
8862
8890
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
8891
+ var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
8863
8892
  var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
8864
8893
  var import_types21 = require("@neat.is/types");
8865
8894
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
@@ -8910,19 +8939,27 @@ function callsFromSource(source, parser, knownHosts) {
8910
8939
  }
8911
8940
  return out;
8912
8941
  }
8913
- function makeJsParser3() {
8914
- const p = new import_tree_sitter6.default();
8915
- p.setLanguage(import_tree_sitter_javascript4.default);
8916
- return p;
8917
- }
8918
- function makePyParser3() {
8919
- const p = new import_tree_sitter6.default();
8920
- p.setLanguage(import_tree_sitter_python3.default);
8921
- return p;
8942
+ var GRAMMAR_BY_EXT2 = {
8943
+ ".ts": import_tree_sitter_typescript2.default.typescript,
8944
+ ".tsx": import_tree_sitter_typescript2.default.tsx,
8945
+ ".js": import_tree_sitter_javascript4.default,
8946
+ ".jsx": import_tree_sitter_javascript4.default,
8947
+ ".mjs": import_tree_sitter_javascript4.default,
8948
+ ".cjs": import_tree_sitter_javascript4.default,
8949
+ ".py": import_tree_sitter_python3.default
8950
+ };
8951
+ function parserForExt(ext, cache) {
8952
+ const grammar = GRAMMAR_BY_EXT2[ext] ?? import_tree_sitter_javascript4.default;
8953
+ let parser = cache.get(grammar);
8954
+ if (!parser) {
8955
+ parser = new import_tree_sitter6.default();
8956
+ parser.setLanguage(grammar);
8957
+ cache.set(grammar, parser);
8958
+ }
8959
+ return parser;
8922
8960
  }
8923
8961
  async function addHttpCallEdges(graph, services) {
8924
- const jsParser = makeJsParser3();
8925
- const pyParser = makePyParser3();
8962
+ const parserCache = /* @__PURE__ */ new Map();
8926
8963
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
8927
8964
  let nodesAdded = 0;
8928
8965
  let edgesAdded = 0;
@@ -8931,7 +8968,7 @@ async function addHttpCallEdges(graph, services) {
8931
8968
  const seen = /* @__PURE__ */ new Set();
8932
8969
  for (const file of files) {
8933
8970
  if (isTestPath(file.path)) continue;
8934
- const parser = import_node_path32.default.extname(file.path) === ".py" ? pyParser : jsParser;
8971
+ const parser = parserForExt(import_node_path32.default.extname(file.path), parserCache);
8935
8972
  let sites;
8936
8973
  try {
8937
8974
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9004,7 +9041,7 @@ function parseSource5(parser, source) {
9004
9041
  (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
9005
9042
  );
9006
9043
  }
9007
- function makeJsParser4() {
9044
+ function makeJsParser3() {
9008
9045
  const p = new import_tree_sitter7.default();
9009
9046
  p.setLanguage(import_tree_sitter_javascript5.default);
9010
9047
  return p;
@@ -9182,7 +9219,7 @@ function findRoute(entries, method, normalizedPath) {
9182
9219
  );
9183
9220
  }
9184
9221
  async function addRouteCallEdges(graph, services) {
9185
- const jsParser = makeJsParser4();
9222
+ const jsParser = makeJsParser3();
9186
9223
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
9187
9224
  const routeIndex = buildRouteIndex(graph);
9188
9225
  if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
@@ -9574,7 +9611,7 @@ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"),
9574
9611
  var import_types28 = require("@neat.is/types");
9575
9612
  var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
9576
9613
  var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
9577
- function parserForExt(ext) {
9614
+ function parserForExt2(ext) {
9578
9615
  const p = new import_tree_sitter8.default();
9579
9616
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
9580
9617
  return p;
@@ -9739,7 +9776,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9739
9776
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
9740
9777
  if (!hasClient && !hasAdmin) return [];
9741
9778
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
9742
- const tree = parseSource3(parserForExt(import_node_path39.default.extname(file.path)), file.content);
9779
+ const tree = parseSource3(parserForExt2(import_node_path39.default.extname(file.path)), file.content);
9743
9780
  const clientVars = firestoreClientVars(tree.rootNode);
9744
9781
  const collLine = /* @__PURE__ */ new Map();
9745
9782
  const writes = /* @__PURE__ */ new Map();
@@ -10156,7 +10193,7 @@ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
10156
10193
  var import_types30 = require("@neat.is/types");
10157
10194
  var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
10158
10195
  var PARSE_CHUNK6 = 16384;
10159
- function makePyParser4() {
10196
+ function makePyParser3() {
10160
10197
  const p = new import_tree_sitter9.default();
10161
10198
  p.setLanguage(import_tree_sitter_python4.default);
10162
10199
  return p;
@@ -10263,7 +10300,7 @@ function foreignKeyParentTable(call) {
10263
10300
  }
10264
10301
  function sqlalchemyForeignKeys(file, serviceDir) {
10265
10302
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
10266
- const tree = parseSource6(makePyParser4(), file.content);
10303
+ const tree = parseSource6(makePyParser3(), file.content);
10267
10304
  const out = [];
10268
10305
  const seen = /* @__PURE__ */ new Set();
10269
10306
  walk3(tree.rootNode, (node) => {
@@ -10300,7 +10337,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
10300
10337
  }
10301
10338
  function sqlalchemyEndpointsFromFile(file, serviceDir) {
10302
10339
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
10303
- const tree = parseSource6(makePyParser4(), file.content);
10340
+ const tree = parseSource6(makePyParser3(), file.content);
10304
10341
  const out = [];
10305
10342
  const seen = /* @__PURE__ */ new Set();
10306
10343
  const push = (name, line, columns) => {
@@ -10352,7 +10389,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
10352
10389
  function buildSqlalchemyModelRegistry(files) {
10353
10390
  const table = /* @__PURE__ */ new Map();
10354
10391
  const ambiguous = /* @__PURE__ */ new Set();
10355
- const parser = makePyParser4();
10392
+ const parser = makePyParser3();
10356
10393
  for (const file of files) {
10357
10394
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) continue;
10358
10395
  const tree = parseSource6(parser, file.content);
@@ -10401,7 +10438,7 @@ function importsModelName(content, name) {
10401
10438
  function pythonOrmCrossFileEndpoints(files, serviceDir) {
10402
10439
  const registry = buildSqlalchemyModelRegistry(files);
10403
10440
  if (registry.size === 0) return [];
10404
- const parser = makePyParser4();
10441
+ const parser = makePyParser3();
10405
10442
  const out = [];
10406
10443
  const seen = /* @__PURE__ */ new Set();
10407
10444
  for (const file of files) {
@@ -10439,7 +10476,7 @@ var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
10439
10476
  var import_types31 = require("@neat.is/types");
10440
10477
  var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
10441
10478
  var PARSE_CHUNK7 = 16384;
10442
- function makePyParser5() {
10479
+ function makePyParser4() {
10443
10480
  const p = new import_tree_sitter10.default();
10444
10481
  p.setLanguage(import_tree_sitter_python5.default);
10445
10482
  return p;
@@ -10500,7 +10537,7 @@ function readMeta(body) {
10500
10537
  }
10501
10538
  function djangoOrmEndpointsFromFile(file, serviceDir) {
10502
10539
  if (!DJANGO_IMPORT_RE.test(file.content)) return [];
10503
- const tree = parseSource7(makePyParser5(), file.content);
10540
+ const tree = parseSource7(makePyParser4(), file.content);
10504
10541
  const out = [];
10505
10542
  const seen = /* @__PURE__ */ new Set();
10506
10543
  const defaultAppLabel = import_node_path42.default.basename(import_node_path42.default.dirname(file.path));
@@ -10535,7 +10572,7 @@ var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"),
10535
10572
  var import_types32 = require("@neat.is/types");
10536
10573
  var DRIZZLE_IMPORT_RE = /drizzle-orm/;
10537
10574
  var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
10538
- function parserForExt2(ext) {
10575
+ function parserForExt3(ext) {
10539
10576
  const p = new import_tree_sitter11.default();
10540
10577
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
10541
10578
  return p;
@@ -10607,7 +10644,7 @@ function columnsFromObject(obj) {
10607
10644
  }
10608
10645
  function drizzleEndpointsFromFile(file, serviceDir) {
10609
10646
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
10610
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
10647
+ const tree = parseSource3(parserForExt3(import_node_path43.default.extname(file.path)), file.content);
10611
10648
  const out = [];
10612
10649
  const seen = /* @__PURE__ */ new Set();
10613
10650
  const walk9 = (node) => {
@@ -10696,7 +10733,7 @@ function referencesTargetVar(call) {
10696
10733
  }
10697
10734
  function drizzleForeignKeys(file, serviceDir) {
10698
10735
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
10699
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
10736
+ const tree = parseSource3(parserForExt3(import_node_path43.default.extname(file.path)), file.content);
10700
10737
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
10701
10738
  const out = [];
10702
10739
  const seen = /* @__PURE__ */ new Set();
@@ -13403,7 +13440,7 @@ var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"),
13403
13440
  var import_types48 = require("@neat.is/types");
13404
13441
  var ZOD_IMPORT_RE = /\bzod\b/;
13405
13442
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
13406
- function parserForExt3(ext) {
13443
+ function parserForExt4(ext) {
13407
13444
  const p = new import_tree_sitter16.default();
13408
13445
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
13409
13446
  return p;
@@ -13492,7 +13529,7 @@ function topLevelSchemas(root) {
13492
13529
  }
13493
13530
  function zodShapesFromFile(file, serviceDir) {
13494
13531
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
13495
- const tree = parseSource3(parserForExt3(import_node_path59.default.extname(file.path)), file.content);
13532
+ const tree = parseSource3(parserForExt4(import_node_path59.default.extname(file.path)), file.content);
13496
13533
  const out = [];
13497
13534
  const seen = /* @__PURE__ */ new Set();
13498
13535
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -18299,12 +18336,46 @@ var SubstringIndex = class {
18299
18336
  this.graph = graph;
18300
18337
  }
18301
18338
  };
18339
+ var DEFAULT_SEARCH_INIT_TIMEOUT_MS = 3e4;
18340
+ function searchInitTimeoutMs() {
18341
+ const env = process.env.NEAT_SEARCH_INIT_TIMEOUT_MS;
18342
+ if (env !== void 0 && env.length > 0) {
18343
+ const n = Number.parseInt(env, 10);
18344
+ if (Number.isFinite(n) && n >= 0) return n;
18345
+ }
18346
+ return DEFAULT_SEARCH_INIT_TIMEOUT_MS;
18347
+ }
18348
+ async function resolveEmbedderBounded(factory, timeoutMs) {
18349
+ if (timeoutMs <= 0) return factory();
18350
+ let timer;
18351
+ const TIMED_OUT = /* @__PURE__ */ Symbol("embedder-init-timeout");
18352
+ const timeout = new Promise((resolve) => {
18353
+ timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
18354
+ timer.unref?.();
18355
+ });
18356
+ try {
18357
+ const result = await Promise.race([factory(), timeout]);
18358
+ if (result === TIMED_OUT) {
18359
+ console.warn(
18360
+ `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).`
18361
+ );
18362
+ return null;
18363
+ }
18364
+ return result;
18365
+ } finally {
18366
+ if (timer) clearTimeout(timer);
18367
+ }
18368
+ }
18302
18369
  async function buildSearchIndex(graph, options = {}) {
18303
18370
  let embedder = null;
18304
18371
  if (options.embedder) {
18305
18372
  embedder = options.embedder;
18306
18373
  } else if (options.forceProvider !== "substring") {
18307
- embedder = await pickEmbedder();
18374
+ const factory = options.embedderFactory ?? pickEmbedder;
18375
+ embedder = await resolveEmbedderBounded(
18376
+ factory,
18377
+ options.initTimeoutMs ?? searchInitTimeoutMs()
18378
+ );
18308
18379
  if (options.forceProvider === "ollama" && embedder?.provider !== "ollama") {
18309
18380
  embedder = null;
18310
18381
  }