@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/cli.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
- const msg = Type.create({});
511
- const encoded = Type.encode(msg).finish();
512
- cachedProtobufResponseBody = Buffer.from(encoded);
513
- return cachedProtobufResponseBody;
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 (req, reply) => {
@@ -4885,10 +4901,15 @@ function resolveDistToSrc(absFilepath, line) {
4885
4901
  }
4886
4902
  if (!entry2) return null;
4887
4903
  try {
4888
- const pos = entry2.consumer.originalPositionFor({
4889
- line: line !== void 0 && Number.isFinite(line) ? line : 1,
4890
- column: 0
4891
- });
4904
+ const queryLine = line !== void 0 && Number.isFinite(line) ? line : 1;
4905
+ let pos = entry2.consumer.originalPositionFor({ line: queryLine, column: 0 });
4906
+ if (!pos || !pos.source) {
4907
+ pos = entry2.consumer.originalPositionFor({
4908
+ line: queryLine,
4909
+ column: 0,
4910
+ bias: sourceMapJs.SourceMapConsumer.LEAST_UPPER_BOUND
4911
+ });
4912
+ }
4892
4913
  if (!pos || !pos.source) return null;
4893
4914
  const root = entry2.consumer.sourceRoot ?? "";
4894
4915
  const resolved = import_node_path9.default.resolve(entry2.dir, root, pos.source);
@@ -4897,6 +4918,9 @@ function resolveDistToSrc(absFilepath, line) {
4897
4918
  return null;
4898
4919
  }
4899
4920
  }
4921
+ function hasAdjacentSourceMap(absFilepath) {
4922
+ return sourceMapCache.get(absFilepath) != null;
4923
+ }
4900
4924
  function callSiteFromSpan(span, serviceNode, scanPath) {
4901
4925
  const filepath = codeFilepathOf(span.attributes);
4902
4926
  if (filepath === void 0) return null;
@@ -4912,7 +4936,7 @@ function callSiteFromSpan(span, serviceNode, scanPath) {
4912
4936
  }
4913
4937
  const relPath = relPathForRuntimeFile(effectivePath, serviceNode, scanPath);
4914
4938
  if (!relPath) return null;
4915
- if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && serviceNode?.name) {
4939
+ if (!resolved && abs.endsWith(".js") && relPath.startsWith("dist/") && !hasAdjacentSourceMap(abs) && serviceNode?.name) {
4916
4940
  warnNoSourceMaps(serviceNode.name);
4917
4941
  }
4918
4942
  const fn = codeFunctionOf(span.attributes);
@@ -5188,7 +5212,7 @@ function resolveServiceId(graph, host, env) {
5188
5212
  function frontierIdFor(host) {
5189
5213
  return (0, import_types8.frontierId)(host);
5190
5214
  }
5191
- function ensureServiceNode(graph, serviceName, env) {
5215
+ function resolveFusedServiceId(graph, serviceName, env) {
5192
5216
  const id = (0, import_types8.serviceId)(serviceName, env);
5193
5217
  if (graph.hasNode(id)) return id;
5194
5218
  const wanted = serviceName.toLowerCase();
@@ -5198,17 +5222,21 @@ function ensureServiceNode(graph, serviceName, env) {
5198
5222
  if (svc.discoveredVia === "otel") return false;
5199
5223
  return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
5200
5224
  });
5201
- if (extractedId) return extractedId;
5225
+ return extractedId ?? id;
5226
+ }
5227
+ function ensureServiceNode(graph, serviceName, env) {
5228
+ const resolved = resolveFusedServiceId(graph, serviceName, env);
5229
+ if (graph.hasNode(resolved)) return resolved;
5202
5230
  const node = {
5203
- id,
5231
+ id: resolved,
5204
5232
  type: import_types8.NodeType.ServiceNode,
5205
5233
  name: serviceName,
5206
5234
  language: "unknown",
5207
5235
  discoveredVia: "otel",
5208
5236
  ...env !== "unknown" ? { env } : {}
5209
5237
  };
5210
- graph.addNode(id, node);
5211
- return id;
5238
+ graph.addNode(resolved, node);
5239
+ return resolved;
5212
5240
  }
5213
5241
  function ensureInfraNode(graph, kind, name, provider) {
5214
5242
  const id = (0, import_types8.infraId)(kind, name);
@@ -5402,7 +5430,7 @@ async function appendErrorEvent(ctx, ev) {
5402
5430
  await import_node_fs8.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
5403
5431
  }
5404
5432
  function incidentAffectedNode(span, graph, scanPath) {
5405
- const sid = (0, import_types8.serviceId)(span.service, span.env);
5433
+ const sid = graph ? resolveFusedServiceId(graph, span.service, span.env) : (0, import_types8.serviceId)(span.service, span.env);
5406
5434
  const serviceNode = graph && graph.hasNode(sid) ? graph.getNodeAttributes(sid) : void 0;
5407
5435
  const callSite = callSiteFromSpan(span, serviceNode, scanPath);
5408
5436
  if (callSite) {
@@ -6804,7 +6832,7 @@ function disambiguate(defs) {
6804
6832
  }
6805
6833
  async function addSymbols(graph, services) {
6806
6834
  const parsers = /* @__PURE__ */ new Map();
6807
- const parserForExt4 = (ext) => {
6835
+ const parserForExt5 = (ext) => {
6808
6836
  const grammar = GRAMMAR_BY_EXT[ext];
6809
6837
  if (!grammar) return null;
6810
6838
  let parser = parsers.get(ext);
@@ -6820,7 +6848,7 @@ async function addSymbols(graph, services) {
6820
6848
  for (const service of services) {
6821
6849
  const files = await loadSourceFiles(service.dir);
6822
6850
  for (const file of files) {
6823
- const parser = parserForExt4(import_node_path18.default.extname(file.path));
6851
+ const parser = parserForExt5(import_node_path18.default.extname(file.path));
6824
6852
  if (!parser) continue;
6825
6853
  const relPath = toPosix(import_node_path18.default.relative(service.dir, file.path));
6826
6854
  let defs;
@@ -6972,7 +7000,7 @@ function stringInner(node) {
6972
7000
  }
6973
7001
  async function addSymbolEdges(graph, services) {
6974
7002
  const parsers = /* @__PURE__ */ new Map();
6975
- const parserForExt4 = (ext) => {
7003
+ const parserForExt5 = (ext) => {
6976
7004
  const grammar = GRAMMAR_BY_EXT[ext];
6977
7005
  if (!grammar) return null;
6978
7006
  let parser = parsers.get(ext);
@@ -6989,7 +7017,7 @@ async function addSymbolEdges(graph, services) {
6989
7017
  const tsPaths = await loadTsPathConfig(service.dir);
6990
7018
  const files = await loadSourceFiles(service.dir);
6991
7019
  for (const file of files) {
6992
- const parser = parserForExt4(import_node_path19.default.extname(file.path));
7020
+ const parser = parserForExt5(import_node_path19.default.extname(file.path));
6993
7021
  if (!parser) continue;
6994
7022
  const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
6995
7023
  const fileDir = import_node_path19.default.dirname(file.path);
@@ -7251,7 +7279,7 @@ function firstReferenceLines(root, wanted) {
7251
7279
  }
7252
7280
  async function addServerActions(graph, services) {
7253
7281
  const parsers = /* @__PURE__ */ new Map();
7254
- const parserForExt4 = (ext) => {
7282
+ const parserForExt5 = (ext) => {
7255
7283
  const grammar = GRAMMAR_BY_EXT[ext];
7256
7284
  if (!grammar) return null;
7257
7285
  let parser = parsers.get(ext);
@@ -7274,7 +7302,7 @@ async function addServerActions(graph, services) {
7274
7302
  const files = await loadSourceFiles(service.dir);
7275
7303
  for (const file of files) {
7276
7304
  if (isTestPath(file.path)) continue;
7277
- const parser = parserForExt4(import_node_path20.default.extname(file.path));
7305
+ const parser = parserForExt5(import_node_path20.default.extname(file.path));
7278
7306
  if (!parser) continue;
7279
7307
  const relPath = toPosix(import_node_path20.default.relative(service.dir, file.path));
7280
7308
  let root;
@@ -7337,7 +7365,7 @@ async function addServerActions(graph, services) {
7337
7365
  }
7338
7366
  for (const file of files) {
7339
7367
  if (isTestPath(file.path)) continue;
7340
- const parser = parserForExt4(import_node_path20.default.extname(file.path));
7368
+ const parser = parserForExt5(import_node_path20.default.extname(file.path));
7341
7369
  if (!parser) continue;
7342
7370
  const relPath = toPosix(import_node_path20.default.relative(service.dir, file.path));
7343
7371
  const fileDir = import_node_path20.default.dirname(file.path);
@@ -8301,6 +8329,7 @@ init_cjs_shims();
8301
8329
  var import_node_path31 = __toESM(require("path"), 1);
8302
8330
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
8303
8331
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
8332
+ var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
8304
8333
  var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
8305
8334
  var import_types20 = require("@neat.is/types");
8306
8335
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
@@ -8351,19 +8380,27 @@ function callsFromSource(source, parser, knownHosts) {
8351
8380
  }
8352
8381
  return out;
8353
8382
  }
8354
- function makeJsParser3() {
8355
- const p = new import_tree_sitter6.default();
8356
- p.setLanguage(import_tree_sitter_javascript4.default);
8357
- return p;
8358
- }
8359
- function makePyParser3() {
8360
- const p = new import_tree_sitter6.default();
8361
- p.setLanguage(import_tree_sitter_python3.default);
8362
- return p;
8383
+ var GRAMMAR_BY_EXT2 = {
8384
+ ".ts": import_tree_sitter_typescript2.default.typescript,
8385
+ ".tsx": import_tree_sitter_typescript2.default.tsx,
8386
+ ".js": import_tree_sitter_javascript4.default,
8387
+ ".jsx": import_tree_sitter_javascript4.default,
8388
+ ".mjs": import_tree_sitter_javascript4.default,
8389
+ ".cjs": import_tree_sitter_javascript4.default,
8390
+ ".py": import_tree_sitter_python3.default
8391
+ };
8392
+ function parserForExt(ext, cache) {
8393
+ const grammar = GRAMMAR_BY_EXT2[ext] ?? import_tree_sitter_javascript4.default;
8394
+ let parser = cache.get(grammar);
8395
+ if (!parser) {
8396
+ parser = new import_tree_sitter6.default();
8397
+ parser.setLanguage(grammar);
8398
+ cache.set(grammar, parser);
8399
+ }
8400
+ return parser;
8363
8401
  }
8364
8402
  async function addHttpCallEdges(graph, services) {
8365
- const jsParser = makeJsParser3();
8366
- const pyParser = makePyParser3();
8403
+ const parserCache = /* @__PURE__ */ new Map();
8367
8404
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
8368
8405
  let nodesAdded = 0;
8369
8406
  let edgesAdded = 0;
@@ -8372,7 +8409,7 @@ async function addHttpCallEdges(graph, services) {
8372
8409
  const seen = /* @__PURE__ */ new Set();
8373
8410
  for (const file of files) {
8374
8411
  if (isTestPath(file.path)) continue;
8375
- const parser = import_node_path31.default.extname(file.path) === ".py" ? pyParser : jsParser;
8412
+ const parser = parserForExt(import_node_path31.default.extname(file.path), parserCache);
8376
8413
  let sites;
8377
8414
  try {
8378
8415
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -8445,7 +8482,7 @@ function parseSource5(parser, source) {
8445
8482
  (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK5)
8446
8483
  );
8447
8484
  }
8448
- function makeJsParser4() {
8485
+ function makeJsParser3() {
8449
8486
  const p = new import_tree_sitter7.default();
8450
8487
  p.setLanguage(import_tree_sitter_javascript5.default);
8451
8488
  return p;
@@ -8623,7 +8660,7 @@ function findRoute(entries, method, normalizedPath) {
8623
8660
  );
8624
8661
  }
8625
8662
  async function addRouteCallEdges(graph, services) {
8626
- const jsParser = makeJsParser4();
8663
+ const jsParser = makeJsParser3();
8627
8664
  const { knownHosts, hostToNodeId } = buildServiceHostIndex(services);
8628
8665
  const routeIndex = buildRouteIndex(graph);
8629
8666
  if (routeIndex.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
@@ -9015,7 +9052,7 @@ var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"),
9015
9052
  var import_types27 = require("@neat.is/types");
9016
9053
  var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
9017
9054
  var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
9018
- function parserForExt(ext) {
9055
+ function parserForExt2(ext) {
9019
9056
  const p = new import_tree_sitter8.default();
9020
9057
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript6.default);
9021
9058
  return p;
@@ -9180,7 +9217,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9180
9217
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
9181
9218
  if (!hasClient && !hasAdmin) return [];
9182
9219
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
9183
- const tree = parseSource3(parserForExt(import_node_path38.default.extname(file.path)), file.content);
9220
+ const tree = parseSource3(parserForExt2(import_node_path38.default.extname(file.path)), file.content);
9184
9221
  const clientVars = firestoreClientVars(tree.rootNode);
9185
9222
  const collLine = /* @__PURE__ */ new Map();
9186
9223
  const writes = /* @__PURE__ */ new Map();
@@ -9597,7 +9634,7 @@ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
9597
9634
  var import_types29 = require("@neat.is/types");
9598
9635
  var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
9599
9636
  var PARSE_CHUNK6 = 16384;
9600
- function makePyParser4() {
9637
+ function makePyParser3() {
9601
9638
  const p = new import_tree_sitter9.default();
9602
9639
  p.setLanguage(import_tree_sitter_python4.default);
9603
9640
  return p;
@@ -9704,7 +9741,7 @@ function foreignKeyParentTable(call) {
9704
9741
  }
9705
9742
  function sqlalchemyForeignKeys(file, serviceDir) {
9706
9743
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
9707
- const tree = parseSource6(makePyParser4(), file.content);
9744
+ const tree = parseSource6(makePyParser3(), file.content);
9708
9745
  const out = [];
9709
9746
  const seen = /* @__PURE__ */ new Set();
9710
9747
  walk3(tree.rootNode, (node) => {
@@ -9741,7 +9778,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
9741
9778
  }
9742
9779
  function sqlalchemyEndpointsFromFile(file, serviceDir) {
9743
9780
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) return [];
9744
- const tree = parseSource6(makePyParser4(), file.content);
9781
+ const tree = parseSource6(makePyParser3(), file.content);
9745
9782
  const out = [];
9746
9783
  const seen = /* @__PURE__ */ new Set();
9747
9784
  const push = (name, line, columns) => {
@@ -9793,7 +9830,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
9793
9830
  function buildSqlalchemyModelRegistry(files) {
9794
9831
  const table = /* @__PURE__ */ new Map();
9795
9832
  const ambiguous = /* @__PURE__ */ new Set();
9796
- const parser = makePyParser4();
9833
+ const parser = makePyParser3();
9797
9834
  for (const file of files) {
9798
9835
  if (!SQLALCHEMY_IMPORT_RE.test(file.content)) continue;
9799
9836
  const tree = parseSource6(parser, file.content);
@@ -9842,7 +9879,7 @@ function importsModelName(content, name) {
9842
9879
  function pythonOrmCrossFileEndpoints(files, serviceDir) {
9843
9880
  const registry = buildSqlalchemyModelRegistry(files);
9844
9881
  if (registry.size === 0) return [];
9845
- const parser = makePyParser4();
9882
+ const parser = makePyParser3();
9846
9883
  const out = [];
9847
9884
  const seen = /* @__PURE__ */ new Set();
9848
9885
  for (const file of files) {
@@ -9880,7 +9917,7 @@ var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
9880
9917
  var import_types30 = require("@neat.is/types");
9881
9918
  var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
9882
9919
  var PARSE_CHUNK7 = 16384;
9883
- function makePyParser5() {
9920
+ function makePyParser4() {
9884
9921
  const p = new import_tree_sitter10.default();
9885
9922
  p.setLanguage(import_tree_sitter_python5.default);
9886
9923
  return p;
@@ -9941,7 +9978,7 @@ function readMeta(body) {
9941
9978
  }
9942
9979
  function djangoOrmEndpointsFromFile(file, serviceDir) {
9943
9980
  if (!DJANGO_IMPORT_RE.test(file.content)) return [];
9944
- const tree = parseSource7(makePyParser5(), file.content);
9981
+ const tree = parseSource7(makePyParser4(), file.content);
9945
9982
  const out = [];
9946
9983
  const seen = /* @__PURE__ */ new Set();
9947
9984
  const defaultAppLabel = import_node_path41.default.basename(import_node_path41.default.dirname(file.path));
@@ -9976,7 +10013,7 @@ var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"),
9976
10013
  var import_types31 = require("@neat.is/types");
9977
10014
  var DRIZZLE_IMPORT_RE = /drizzle-orm/;
9978
10015
  var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
9979
- function parserForExt2(ext) {
10016
+ function parserForExt3(ext) {
9980
10017
  const p = new import_tree_sitter11.default();
9981
10018
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
9982
10019
  return p;
@@ -10048,7 +10085,7 @@ function columnsFromObject(obj) {
10048
10085
  }
10049
10086
  function drizzleEndpointsFromFile(file, serviceDir) {
10050
10087
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
10051
- const tree = parseSource3(parserForExt2(import_node_path42.default.extname(file.path)), file.content);
10088
+ const tree = parseSource3(parserForExt3(import_node_path42.default.extname(file.path)), file.content);
10052
10089
  const out = [];
10053
10090
  const seen = /* @__PURE__ */ new Set();
10054
10091
  const walk9 = (node) => {
@@ -10137,7 +10174,7 @@ function referencesTargetVar(call) {
10137
10174
  }
10138
10175
  function drizzleForeignKeys(file, serviceDir) {
10139
10176
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
10140
- const tree = parseSource3(parserForExt2(import_node_path42.default.extname(file.path)), file.content);
10177
+ const tree = parseSource3(parserForExt3(import_node_path42.default.extname(file.path)), file.content);
10141
10178
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
10142
10179
  const out = [];
10143
10180
  const seen = /* @__PURE__ */ new Set();
@@ -12844,7 +12881,7 @@ var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"),
12844
12881
  var import_types47 = require("@neat.is/types");
12845
12882
  var ZOD_IMPORT_RE = /\bzod\b/;
12846
12883
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12847
- function parserForExt3(ext) {
12884
+ function parserForExt4(ext) {
12848
12885
  const p = new import_tree_sitter16.default();
12849
12886
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12850
12887
  return p;
@@ -12933,7 +12970,7 @@ function topLevelSchemas(root) {
12933
12970
  }
12934
12971
  function zodShapesFromFile(file, serviceDir) {
12935
12972
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12936
- const tree = parseSource3(parserForExt3(import_node_path58.default.extname(file.path)), file.content);
12973
+ const tree = parseSource3(parserForExt4(import_node_path58.default.extname(file.path)), file.content);
12937
12974
  const out = [];
12938
12975
  const seen = /* @__PURE__ */ new Set();
12939
12976
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -19275,12 +19312,46 @@ var SubstringIndex = class {
19275
19312
  this.graph = graph;
19276
19313
  }
19277
19314
  };
19315
+ var DEFAULT_SEARCH_INIT_TIMEOUT_MS = 3e4;
19316
+ function searchInitTimeoutMs() {
19317
+ const env = process.env.NEAT_SEARCH_INIT_TIMEOUT_MS;
19318
+ if (env !== void 0 && env.length > 0) {
19319
+ const n = Number.parseInt(env, 10);
19320
+ if (Number.isFinite(n) && n >= 0) return n;
19321
+ }
19322
+ return DEFAULT_SEARCH_INIT_TIMEOUT_MS;
19323
+ }
19324
+ async function resolveEmbedderBounded(factory, timeoutMs) {
19325
+ if (timeoutMs <= 0) return factory();
19326
+ let timer;
19327
+ const TIMED_OUT = /* @__PURE__ */ Symbol("embedder-init-timeout");
19328
+ const timeout = new Promise((resolve) => {
19329
+ timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
19330
+ timer.unref?.();
19331
+ });
19332
+ try {
19333
+ const result = await Promise.race([factory(), timeout]);
19334
+ if (result === TIMED_OUT) {
19335
+ console.warn(
19336
+ `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).`
19337
+ );
19338
+ return null;
19339
+ }
19340
+ return result;
19341
+ } finally {
19342
+ if (timer) clearTimeout(timer);
19343
+ }
19344
+ }
19278
19345
  async function buildSearchIndex(graph, options = {}) {
19279
19346
  let embedder = null;
19280
19347
  if (options.embedder) {
19281
19348
  embedder = options.embedder;
19282
19349
  } else if (options.forceProvider !== "substring") {
19283
- embedder = await pickEmbedder();
19350
+ const factory = options.embedderFactory ?? pickEmbedder;
19351
+ embedder = await resolveEmbedderBounded(
19352
+ factory,
19353
+ options.initTimeoutMs ?? searchInitTimeoutMs()
19354
+ );
19284
19355
  if (options.forceProvider === "ollama" && embedder?.provider !== "ollama") {
19285
19356
  embedder = null;
19286
19357
  }
@@ -19903,7 +19974,8 @@ var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
19903
19974
  try {
19904
19975
  const __rec = JSON.parse(__neatFs.readFileSync(__neatPath.join(__neatDir, 'neat-out', 'daemon.json'), 'utf8'))
19905
19976
  if (__rec && __rec.ports && typeof __rec.ports.otlp === 'number') {
19906
- process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:' + __rec.ports.otlp + '/v1/traces'
19977
+ const __neatProj = (typeof __rec.project === 'string' && __rec.project) || '__PROJECT__'
19978
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:' + __rec.ports.otlp + '/projects/' + __neatProj + '/v1/traces'
19907
19979
  break
19908
19980
  }
19909
19981
  } catch (_e) {}
@@ -19912,7 +19984,7 @@ var OTEL_ENDPOINT_RESOLVER_CJS = `;(function () {
19912
19984
  __neatDir = __parent
19913
19985
  }
19914
19986
  } catch (_e) {}
19915
- process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/v1/traces'
19987
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
19916
19988
  })()`;
19917
19989
  var OTEL_ESM_NODE_IMPORTS = "import { readFileSync as __neatReadFileSync } from 'node:fs'\nimport { join as __neatJoin, dirname as __neatDirname } from 'node:path'";
19918
19990
  var OTEL_ENDPOINT_RESOLVER_ESM = `;(function () {
@@ -19923,7 +19995,8 @@ var OTEL_ENDPOINT_RESOLVER_ESM = `;(function () {
19923
19995
  try {
19924
19996
  const __rec = JSON.parse(__neatReadFileSync(__neatJoin(__neatDir, 'neat-out', 'daemon.json'), 'utf8'))
19925
19997
  if (__rec && __rec.ports && typeof __rec.ports.otlp === 'number') {
19926
- process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:' + __rec.ports.otlp + '/v1/traces'
19998
+ const __neatProj = (typeof __rec.project === 'string' && __rec.project) || '__PROJECT__'
19999
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = 'http://localhost:' + __rec.ports.otlp + '/projects/' + __neatProj + '/v1/traces'
19927
20000
  break
19928
20001
  }
19929
20002
  } catch (_e) {}
@@ -19932,7 +20005,7 @@ var OTEL_ENDPOINT_RESOLVER_ESM = `;(function () {
19932
20005
  __neatDir = __parent
19933
20006
  }
19934
20007
  } catch (_e) {}
19935
- process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/v1/traces'
20008
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
19936
20009
  })()`;
19937
20010
  function neatCaptureSource(ts) {
19938
20011
  const spanT = ts ? ": any" : "";
@@ -20334,14 +20407,15 @@ ${registrations.join("\n")}
20334
20407
  `;
20335
20408
  return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
20336
20409
  }
20337
- function renderEnvNeat(serviceName, _projectName) {
20410
+ function renderEnvNeat(serviceName, projectName) {
20338
20411
  return [
20339
20412
  "# Generated by `neat init --apply` (ADR-069).",
20340
20413
  `OTEL_SERVICE_NAME=${serviceName}`,
20341
20414
  "# Advisory only \u2014 the generated otel-init resolves the live endpoint from",
20342
- "# <project>/neat-out/daemon.json (ports.otlp) at boot (ADR-096). This is the",
20343
- "# canonical default the daemon takes when its first-choice OTLP port is free.",
20344
- "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces",
20415
+ "# <project>/neat-out/daemon.json (ports.otlp + project) at boot (ADR-096).",
20416
+ "# This is the canonical default the daemon takes when its first-choice OTLP",
20417
+ "# port is free; the project scope routes the span without service.name guessing (#879).",
20418
+ `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/projects/${projectName}/v1/traces`,
20345
20419
  "OTEL_EXPORTER_OTLP_PROTOCOL=http/json",
20346
20420
  "# Set NEAT_OTEL_TOKEN to the daemon's OTLP secret to authenticate exported spans (#410).",
20347
20421
  "# NEAT_OTEL_TOKEN=",
@@ -20407,7 +20481,7 @@ var NEXT_INSTRUMENTATION_EDGE_TS = `${NEXT_INSTRUMENTATION_EDGE_HEADER}
20407
20481
  import { registerOTel } from '@vercel/otel'
20408
20482
 
20409
20483
  process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
20410
- process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/v1/traces'
20484
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
20411
20485
  ${OTEL_OTLP_PROTOCOL_JS}
20412
20486
  ${OTEL_OTLP_HEADERS_JS}
20413
20487