@neat.is/core 0.10.3 → 0.10.5-dev.20261001

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-DLQNMU3R.js";
5
+ } from "./chunk-TONL5O2E.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-LNIVRE4L.js";
9
+ } from "./chunk-HHAPD6JD.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
13
- } from "./chunk-M7CGATVC.js";
13
+ } from "./chunk-EFBWQIKG.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-5SJHA5ZH.js";
5
- import "./chunk-M7CGATVC.js";
4
+ } from "./chunk-LXQCL4MP.js";
5
+ import "./chunk-EFBWQIKG.js";
6
6
  export {
7
7
  reshapeGrpcRequest,
8
8
  startOtelGrpcReceiver
9
9
  };
10
- //# sourceMappingURL=otel-grpc-CU73Y5WT.js.map
10
+ //# sourceMappingURL=otel-grpc-B7ABHLV7.js.map
package/dist/server.cjs CHANGED
@@ -49,6 +49,10 @@ function assertBindAuthority(host, token) {
49
49
  if (isLoopbackHost2(host)) return;
50
50
  throw new BindAuthorityError(host);
51
51
  }
52
+ function isBearerDelegated(req) {
53
+ const config = req.routeOptions?.config;
54
+ return config?.bearerDelegated === true;
55
+ }
52
56
  function mountBearerAuth(app, opts) {
53
57
  if (!opts.token || opts.token.length === 0) return;
54
58
  if (opts.trustProxy) return;
@@ -64,6 +68,10 @@ function mountBearerAuth(app, opts) {
64
68
  done();
65
69
  return;
66
70
  }
71
+ if (isBearerDelegated(req)) {
72
+ done();
73
+ return;
74
+ }
67
75
  if (publicRead && PUBLIC_READ_METHODS.has(req.method)) {
68
76
  done();
69
77
  return;
@@ -97,7 +105,7 @@ function readAuthEnv(env = process.env) {
97
105
  publicRead: parseBoolEnv(env.NEAT_PUBLIC_READ)
98
106
  };
99
107
  }
100
- var import_node_crypto, LOOPBACK_HOSTS, BindAuthorityError, PUBLIC_READ_METHODS, DEFAULT_UNAUTH_PATHS, PROJECT_SCOPED_UNAUTH_PATTERN;
108
+ var import_node_crypto, LOOPBACK_HOSTS, BindAuthorityError, PUBLIC_READ_METHODS, DEFAULT_UNAUTH_PATHS, PROJECT_SCOPED_UNAUTH_PATTERN, BEARER_DELEGATED;
101
109
  var init_auth = __esm({
102
110
  "src/auth.ts"() {
103
111
  "use strict";
@@ -127,6 +135,7 @@ var init_auth = __esm({
127
135
  PROJECT_SCOPED_UNAUTH_PATTERN = new RegExp(
128
136
  `^/projects/[^/]+/(?:${DEFAULT_UNAUTH_PATHS.map((p) => p.slice(1)).join("|")})$`
129
137
  );
138
+ BEARER_DELEGATED = { bearerDelegated: true };
130
139
  }
131
140
  });
132
141
 
@@ -2531,6 +2540,10 @@ function languageForPath(relPath) {
2531
2540
  return void 0;
2532
2541
  }
2533
2542
  }
2543
+ var currentSource;
2544
+ function setExtractionSource(source) {
2545
+ currentSource = source;
2546
+ }
2534
2547
  function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
2535
2548
  let nodesAdded = 0;
2536
2549
  let edgesAdded = 0;
@@ -2543,10 +2556,16 @@ function ensureFileNode(graph, serviceName, serviceNodeId, relPath) {
2543
2556
  service: serviceName,
2544
2557
  path: relPath,
2545
2558
  ...language ? { language } : {},
2559
+ ...currentSource ? { source: currentSource } : {},
2546
2560
  discoveredVia: "static"
2547
2561
  };
2548
2562
  graph.addNode(fileNodeId, node);
2549
2563
  nodesAdded++;
2564
+ } else if (currentSource) {
2565
+ const existing = graph.getNodeAttributes(fileNodeId);
2566
+ if (existing.source !== currentSource) {
2567
+ graph.replaceNodeAttributes(fileNodeId, { ...existing, source: currentSource });
2568
+ }
2550
2569
  }
2551
2570
  const containsId = (0, import_types4.extractedEdgeId)(serviceNodeId, fileNodeId, import_types4.EdgeType.CONTAINS);
2552
2571
  if (!graph.hasEdge(containsId)) {
@@ -17379,10 +17398,12 @@ init_cjs_shims();
17379
17398
  var import_node_fs35 = require("fs");
17380
17399
  var import_node_path70 = __toESM(require("path"), 1);
17381
17400
  var import_types59 = require("@neat.is/types");
17382
- function dropOrphanedFileNodes(graph) {
17401
+ function dropOrphanedFileNodes(graph, source) {
17383
17402
  const orphans = [];
17384
17403
  graph.forEachNode((id, attrs) => {
17385
- if (attrs.type !== import_types59.NodeType.FileNode) return;
17404
+ const node = attrs;
17405
+ if (node.type !== import_types59.NodeType.FileNode) return;
17406
+ if (source && node.source !== source) return;
17386
17407
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
17387
17408
  orphans.push(id);
17388
17409
  }
@@ -17390,12 +17411,23 @@ function dropOrphanedFileNodes(graph) {
17390
17411
  for (const id of orphans) graph.dropNode(id);
17391
17412
  return orphans.length;
17392
17413
  }
17393
- function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
17414
+ function outOfScope(graph, edge, source) {
17415
+ if (!source) return false;
17416
+ for (const endpoint2 of [edge.source, edge.target]) {
17417
+ if (!graph.hasNode(endpoint2)) continue;
17418
+ const node = graph.getNodeAttributes(endpoint2);
17419
+ if (node.type !== import_types59.NodeType.FileNode) continue;
17420
+ return node.source !== source;
17421
+ }
17422
+ return false;
17423
+ }
17424
+ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = [], source) {
17394
17425
  const toDrop = [];
17395
17426
  const bases = [scanPath, ...serviceDirs];
17396
17427
  graph.forEachEdge((id, attrs) => {
17397
17428
  const edge = attrs;
17398
17429
  if (edge.provenance !== import_types59.Provenance.EXTRACTED) return;
17430
+ if (outOfScope(graph, edge, source)) return;
17399
17431
  const evidenceFile = edge.evidence?.file;
17400
17432
  if (!evidenceFile) return;
17401
17433
  if (import_node_path70.default.isAbsolute(evidenceFile)) {
@@ -17406,7 +17438,7 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
17406
17438
  if (!found) toDrop.push(id);
17407
17439
  });
17408
17440
  for (const id of toDrop) graph.dropEdge(id);
17409
- dropOrphanedFileNodes(graph);
17441
+ dropOrphanedFileNodes(graph, source);
17410
17442
  return toDrop.length;
17411
17443
  }
17412
17444
 
@@ -17415,6 +17447,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
17415
17447
  await ensureCompatLoaded();
17416
17448
  drainExtractionErrors();
17417
17449
  drainSkippedFiles();
17450
+ setExtractionSource(opts.source);
17418
17451
  const services = await discoverServices(scanPath);
17419
17452
  const phase1Nodes = addServiceNodes(graph, services);
17420
17453
  await addServiceAliases(graph, scanPath, services);
@@ -17435,7 +17468,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
17435
17468
  const ghostsRetired = retireExtractedEdgesByMissingFile(
17436
17469
  graph,
17437
17470
  scanPath,
17438
- services.map((s) => s.dir)
17471
+ services.map((s) => s.dir),
17472
+ opts.source
17439
17473
  );
17440
17474
  const frontiersPromoted = promoteFrontierNodes(graph);
17441
17475
  if (opts.onPolicyTrigger) await opts.onPolicyTrigger(graph);
@@ -17470,6 +17504,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
17470
17504
  );
17471
17505
  }
17472
17506
  }
17507
+ setExtractionSource(void 0);
17473
17508
  const result = {
17474
17509
  nodesAdded: phase1Nodes + fileEnum.nodesAdded + symbolEnum.nodesAdded + importGraph.nodesAdded + symbolEdges.nodesAdded + actionPhase.nodesAdded + phase2.nodesAdded + phase3.nodesAdded + routePhase.nodesAdded + grpcPhase.nodesAdded + phase4.nodesAdded + tableEdges.nodesAdded + phase5.nodesAdded + zodPhase.nodesAdded + firestoreRulesPhase.nodesAdded,
17475
17510
  edgesAdded: fileEnum.edgesAdded + symbolEnum.edgesAdded + importGraph.edgesAdded + symbolEdges.edgesAdded + actionPhase.edgesAdded + phase2.edgesAdded + phase3.edgesAdded + routePhase.edgesAdded + grpcPhase.edgesAdded + phase4.edgesAdded + tableEdges.edgesAdded + phase5.edgesAdded + zodPhase.edgesAdded + firestoreRulesPhase.edgesAdded,
@@ -23093,6 +23128,19 @@ function registerRoutes(scope, ctx) {
23093
23128
  });
23094
23129
  }
23095
23130
  });
23131
+ scope.post("/repo-sync", async (req, reply) => {
23132
+ const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
23133
+ if (!proj) return;
23134
+ const syncNow = ctx.repoSync?.(proj.name);
23135
+ if (!syncNow) {
23136
+ return reply.code(404).send({
23137
+ error: "repo-sync is not running for this project",
23138
+ project: proj.name,
23139
+ hint: "Only a hosted daemon syncs bound repos. On a local daemon, POST /graph/scan re-extracts the project path."
23140
+ });
23141
+ }
23142
+ return reply.code(202).send({ project: proj.name, ...syncNow() });
23143
+ });
23096
23144
  scope.post("/graph/scan", async (req, reply) => {
23097
23145
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
23098
23146
  if (!proj) return;
@@ -23340,6 +23388,39 @@ function registerRoutes(scope, ctx) {
23340
23388
  }
23341
23389
  });
23342
23390
  }
23391
+ var OTLP_BODY_LIMIT = 16 * 1024 * 1024;
23392
+ function otlpOnRest(getReceiver) {
23393
+ return async function plugin(scope) {
23394
+ scope.removeAllContentTypeParsers();
23395
+ scope.addContentTypeParser(
23396
+ "*",
23397
+ { parseAs: "buffer", bodyLimit: OTLP_BODY_LIMIT },
23398
+ (_req, body, done) => done(null, body)
23399
+ );
23400
+ const relay = async (req, reply) => {
23401
+ const receiver = getReceiver();
23402
+ if (!receiver) {
23403
+ return reply.code(503).header("retry-after", "1").send({ error: "OTLP receiver is starting" });
23404
+ }
23405
+ const headers = { ...req.headers };
23406
+ delete headers["content-length"];
23407
+ delete headers["transfer-encoding"];
23408
+ delete headers.connection;
23409
+ const res = await receiver.inject({
23410
+ method: "POST",
23411
+ url: req.url,
23412
+ headers,
23413
+ payload: req.body
23414
+ });
23415
+ const contentType = res.headers["content-type"];
23416
+ if (contentType !== void 0) void reply.header("content-type", contentType);
23417
+ return reply.code(res.statusCode).send(res.rawPayload);
23418
+ };
23419
+ const route = { config: BEARER_DELEGATED, bodyLimit: OTLP_BODY_LIMIT };
23420
+ scope.post("/v1/traces", route, relay);
23421
+ scope.post("/projects/:project/v1/traces", route, relay);
23422
+ };
23423
+ }
23343
23424
  async function buildApi(opts) {
23344
23425
  const app = (0, import_fastify2.default)({ logger: false, routerOptions: { maxParamLength: 1024 } });
23345
23426
  await app.register(import_cors.default, { origin: true });
@@ -23358,6 +23439,7 @@ async function buildApi(opts) {
23358
23439
  authProxy: trustProxy === true,
23359
23440
  requiresAuth
23360
23441
  }));
23442
+ if (opts.otlpReceiver) await app.register(otlpOnRest(opts.otlpReceiver));
23361
23443
  const startedAt = opts.startedAt ?? Date.now();
23362
23444
  const registry = buildLegacyRegistry(opts);
23363
23445
  const legacyErrorsExplicit = !opts.projects && opts.errorsPath !== void 0;
@@ -23388,6 +23470,7 @@ async function buildApi(opts) {
23388
23470
  bootstrap: opts.bootstrap,
23389
23471
  singleProject: opts.singleProject?.name,
23390
23472
  connectorsHome: opts.connectorsHome,
23473
+ repoSync: opts.repoSync,
23391
23474
  runPoll: opts.runPoll ?? runConnectorPoll
23392
23475
  };
23393
23476
  app.get("/health", async () => {
@@ -23885,11 +23968,13 @@ async function main() {
23885
23968
  const otelPort = Number(process.env.OTEL_PORT ?? 4318);
23886
23969
  const auth = readAuthEnv();
23887
23970
  assertBindAuthority(host, auth.authToken);
23971
+ let otelReceiver;
23888
23972
  const app = await buildApi({
23889
23973
  projects: registry,
23890
23974
  authToken: auth.authToken,
23891
23975
  trustProxy: auth.trustProxy,
23892
- publicRead: auth.publicRead
23976
+ publicRead: auth.publicRead,
23977
+ otlpReceiver: () => otelReceiver
23893
23978
  });
23894
23979
  await app.listen({ port, host });
23895
23980
  console.log(`neat-core listening on http://${host}:${port}`);
@@ -23906,6 +23991,7 @@ async function main() {
23906
23991
  authToken: auth.otelToken,
23907
23992
  trustProxy: auth.trustProxy
23908
23993
  });
23994
+ otelReceiver = otelApp;
23909
23995
  await otelApp.listen({ port: otelPort, host });
23910
23996
  console.log(`neat-core OTLP receiver on http://${host}:${otelPort}/v1/traces`);
23911
23997
  if (process.env.NEAT_OTLP_GRPC === "true") {