@neat.is/core 0.6.3-dev.20260725 → 0.6.3-dev.20260726

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/index.d.cts CHANGED
@@ -181,6 +181,7 @@ interface BuildOtelReceiverOptions {
181
181
  onErrorSpanSync?: (span: ParsedSpan) => Promise<void>;
182
182
  onProjectSpan?: ProjectSpanHandler;
183
183
  onProjectErrorSpanSync?: (project: string, span: ParsedSpan) => Promise<void>;
184
+ isProjectRegistered?: (project: string) => boolean;
184
185
  bodyLimit?: number;
185
186
  authToken?: string;
186
187
  trustProxy?: boolean;
package/dist/index.d.ts CHANGED
@@ -181,6 +181,7 @@ interface BuildOtelReceiverOptions {
181
181
  onErrorSpanSync?: (span: ParsedSpan) => Promise<void>;
182
182
  onProjectSpan?: ProjectSpanHandler;
183
183
  onProjectErrorSpanSync?: (project: string, span: ParsedSpan) => Promise<void>;
184
+ isProjectRegistered?: (project: string) => boolean;
184
185
  bodyLimit?: number;
185
186
  authToken?: string;
186
187
  trustProxy?: boolean;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-2BQTX7QX.js";
4
+ } from "./chunk-PWIT35Z4.js";
5
5
  import {
6
6
  ProjectNameCollisionError,
7
7
  addProject,
@@ -37,15 +37,15 @@ import {
37
37
  thresholdForEdgeType,
38
38
  touchLastSeen,
39
39
  writeAtomically
40
- } from "./chunk-C7TJMXO4.js";
40
+ } from "./chunk-L64CATKE.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
- } from "./chunk-TQPDQNUD.js";
43
+ } from "./chunk-7QXN726V.js";
44
44
  import {
45
45
  buildOtelReceiver,
46
46
  logSpanHandler,
47
47
  parseOtlpRequest
48
- } from "./chunk-H4EAELBI.js";
48
+ } from "./chunk-ZLAZ7PLC.js";
49
49
  export {
50
50
  ProjectNameCollisionError,
51
51
  addProject,
package/dist/neatd.cjs CHANGED
@@ -612,6 +612,9 @@ async function buildOtelReceiver(opts) {
612
612
  });
613
613
  app.post("/projects/:project/v1/traces", async (req2, reply) => {
614
614
  const project = req2.params.project;
615
+ if (opts.isProjectRegistered && !opts.isProjectRegistered(project)) {
616
+ return reply.code(404).send({ error: "project not found", project });
617
+ }
615
618
  const result = await readOtlpBody(req2);
616
619
  if (!result.ok) {
617
620
  return reply.code(result.code).send({ error: result.error });
@@ -3418,6 +3421,14 @@ function frontierIdFor(host) {
3418
3421
  function ensureServiceNode(graph, serviceName, env) {
3419
3422
  const id = (0, import_types6.serviceId)(serviceName, env);
3420
3423
  if (graph.hasNode(id)) return id;
3424
+ const wanted = serviceName.toLowerCase();
3425
+ const extractedId = graph.findNode((_nid, attrs) => {
3426
+ if (attrs.type !== import_types6.NodeType.ServiceNode) return false;
3427
+ const svc = attrs;
3428
+ if (svc.discoveredVia === "otel") return false;
3429
+ return typeof svc.name === "string" && svc.name.toLowerCase() === wanted;
3430
+ });
3431
+ if (extractedId) return extractedId;
3421
3432
  const node = {
3422
3433
  id,
3423
3434
  type: import_types6.NodeType.ServiceNode,
@@ -12585,7 +12596,7 @@ function routeSpanToProject(serviceName, projects) {
12585
12596
  if (!serviceName) return DEFAULT_PROJECT;
12586
12597
  for (const entry2 of projects) {
12587
12598
  if (entry2.status === "paused") continue;
12588
- if (entry2.name === serviceName) return entry2.name;
12599
+ if (entry2.name.toLowerCase() === serviceName.toLowerCase()) return entry2.name;
12589
12600
  }
12590
12601
  const candidates = [];
12591
12602
  for (const entry2 of projects) {
@@ -12603,18 +12614,22 @@ function routeSpanToProject(serviceName, projects) {
12603
12614
  return DEFAULT_PROJECT;
12604
12615
  }
12605
12616
  function isTokenPrefix(prefix, full) {
12606
- if (prefix.length >= full.length) return false;
12607
- if (!full.startsWith(prefix)) return false;
12608
- const sep = full.charAt(prefix.length);
12617
+ const p = prefix.toLowerCase();
12618
+ const f = full.toLowerCase();
12619
+ if (p.length >= f.length) return false;
12620
+ if (!f.startsWith(p)) return false;
12621
+ const sep = f.charAt(p.length);
12609
12622
  return sep === "-" || sep === "_";
12610
12623
  }
12611
12624
  function isTokenContained(needle, haystack) {
12612
- if (!haystack.includes(needle)) return false;
12613
- const tokens = haystack.split(/[-_]/);
12614
- return tokens.includes(needle);
12625
+ const n = needle.toLowerCase();
12626
+ const h = haystack.toLowerCase();
12627
+ if (!h.includes(n)) return false;
12628
+ const tokens = h.split(/[-_]/);
12629
+ return tokens.includes(n);
12615
12630
  }
12616
12631
  function serviceNameMatchesProject(serviceName, project) {
12617
- if (serviceName === project) return true;
12632
+ if (serviceName.toLowerCase() === project.toLowerCase()) return true;
12618
12633
  if (isTokenPrefix(project, serviceName)) return true;
12619
12634
  if (isTokenContained(project, serviceName)) return true;
12620
12635
  return false;
@@ -13071,7 +13086,12 @@ async function startDaemon(opts = {}) {
13071
13086
  const slot = await resolveSlotByName(project, span.service, span.traceId);
13072
13087
  if (!slot) return;
13073
13088
  await makeErrorSpanWriter(slot.paths.errorsPath, slot.graph, slot.entry.path)(span);
13074
- }
13089
+ },
13090
+ // #881 — 404 a project-scoped POST for a project this daemon doesn't
13091
+ // host, rather than accepting it and dropping the batch. `slots` covers
13092
+ // active/recovering projects, `bootstrapStatus` the ones still
13093
+ // extracting; a foreign or wrong-cased project name matches neither.
13094
+ isProjectRegistered: (project) => slots.has(project) || bootstrapStatus.has(project)
13075
13095
  });
13076
13096
  otlpAddress = await listenSteppingOtlp(otlpApp, otlpPort, host);
13077
13097
  console.log(`neatd: OTLP listening on ${otlpAddress}/v1/traces`);