@neat.is/core 0.2.10 → 0.3.0

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.cjs CHANGED
@@ -4516,9 +4516,15 @@ function registerRoutes(scope, ctx) {
4516
4516
  scope.get("/health", async (req, reply) => {
4517
4517
  const proj = resolveProject(registry, req, reply);
4518
4518
  if (!proj) return;
4519
+ const uptimeMs = Date.now() - startedAt;
4519
4520
  return {
4520
- uptime: Math.floor((Date.now() - startedAt) / 1e3),
4521
+ ok: true,
4521
4522
  project: proj.name,
4523
+ uptimeMs,
4524
+ // Legacy fields kept additively. The web shell's StatusBar reads
4525
+ // nodeCount / edgeCount; ADR-061's HealthResponseSchema validates
4526
+ // the canonical triple and lets the extras pass through.
4527
+ uptime: Math.floor(uptimeMs / 1e3),
4522
4528
  nodeCount: proj.graph.order,
4523
4529
  edgeCount: proj.graph.size,
4524
4530
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
@@ -4538,7 +4544,7 @@ function registerRoutes(scope, ctx) {
4538
4544
  if (!proj.graph.hasNode(id)) {
4539
4545
  return reply.code(404).send({ error: "node not found", id });
4540
4546
  }
4541
- return proj.graph.getNodeAttributes(id);
4547
+ return { node: proj.graph.getNodeAttributes(id) };
4542
4548
  }
4543
4549
  );
4544
4550
  scope.get(
@@ -4555,12 +4561,12 @@ function registerRoutes(scope, ctx) {
4555
4561
  return { inbound, outbound };
4556
4562
  }
4557
4563
  );
4558
- scope.get("/graph/node/:id/dependencies", async (req, reply) => {
4564
+ scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
4559
4565
  const proj = resolveProject(registry, req, reply);
4560
4566
  if (!proj) return;
4561
- const { id } = req.params;
4562
- if (!proj.graph.hasNode(id)) {
4563
- return reply.code(404).send({ error: "node not found", id });
4567
+ const { nodeId } = req.params;
4568
+ if (!proj.graph.hasNode(nodeId)) {
4569
+ return reply.code(404).send({ error: "node not found", id: nodeId });
4564
4570
  }
4565
4571
  const depth = req.query.depth ? Number(req.query.depth) : TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH;
4566
4572
  if (!Number.isFinite(depth) || depth < 1 || depth > TRANSITIVE_DEPENDENCIES_MAX_DEPTH) {
@@ -4568,7 +4574,7 @@ function registerRoutes(scope, ctx) {
4568
4574
  error: `depth must be an integer in [1, ${TRANSITIVE_DEPENDENCIES_MAX_DEPTH}]`
4569
4575
  });
4570
4576
  }
4571
- return getTransitiveDependencies(proj.graph, id, depth);
4577
+ return getTransitiveDependencies(proj.graph, nodeId, depth);
4572
4578
  });
4573
4579
  scope.get("/graph/divergences", async (req, reply) => {
4574
4580
  const proj = resolveProject(registry, req, reply);
@@ -4609,19 +4615,26 @@ function registerRoutes(scope, ctx) {
4609
4615
  const proj = resolveProject(registry, req, reply);
4610
4616
  if (!proj) return;
4611
4617
  const epath = errorsPathFor(proj);
4612
- if (!epath) return [];
4613
- return readErrorEvents(epath);
4618
+ if (!epath) return { count: 0, total: 0, events: [] };
4619
+ const events = await readErrorEvents(epath);
4620
+ const total = events.length;
4621
+ const limit = req.query.limit ? Number(req.query.limit) : 50;
4622
+ const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.min(limit, 200) : 50;
4623
+ const sliced = events.slice(0, safeLimit);
4624
+ return { count: sliced.length, total, events: sliced };
4614
4625
  });
4615
- scope.get("/incidents/stale", async (req, reply) => {
4626
+ scope.get("/stale-events", async (req, reply) => {
4616
4627
  const proj = resolveProject(registry, req, reply);
4617
4628
  if (!proj) return;
4618
4629
  const spath = staleEventsPathFor(proj);
4619
- if (!spath) return [];
4630
+ if (!spath) return { count: 0, total: 0, events: [] };
4620
4631
  const events = await readStaleEvents(spath);
4621
4632
  const filtered = req.query.edgeType ? events.filter((e) => e.edgeType === req.query.edgeType) : events;
4622
4633
  const ordered = [...filtered].reverse();
4634
+ const total = ordered.length;
4623
4635
  const limit = req.query.limit ? Number(req.query.limit) : 50;
4624
- return ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50);
4636
+ const sliced = ordered.slice(0, Number.isFinite(limit) && limit > 0 ? limit : 50);
4637
+ return { count: sliced.length, total, events: sliced };
4625
4638
  });
4626
4639
  scope.get(
4627
4640
  "/incidents/:nodeId",
@@ -4633,14 +4646,15 @@ function registerRoutes(scope, ctx) {
4633
4646
  return reply.code(404).send({ error: "node not found", id: nodeId });
4634
4647
  }
4635
4648
  const epath = errorsPathFor(proj);
4636
- if (!epath) return [];
4649
+ if (!epath) return { count: 0, total: 0, events: [] };
4637
4650
  const events = await readErrorEvents(epath);
4638
- return events.filter(
4651
+ const filtered = events.filter(
4639
4652
  (e) => e.affectedNode === nodeId || e.service === nodeId.replace(/^service:/, "")
4640
4653
  );
4654
+ return { count: filtered.length, total: filtered.length, events: filtered };
4641
4655
  }
4642
4656
  );
4643
- scope.get("/traverse/root-cause/:nodeId", async (req, reply) => {
4657
+ scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
4644
4658
  const proj = resolveProject(registry, req, reply);
4645
4659
  if (!proj) return;
4646
4660
  const { nodeId } = req.params;
@@ -4660,7 +4674,7 @@ function registerRoutes(scope, ctx) {
4660
4674
  if (!result) return reply.code(404).send({ error: "no root cause found", id: nodeId });
4661
4675
  return result;
4662
4676
  });
4663
- scope.get("/traverse/blast-radius/:nodeId", async (req, reply) => {
4677
+ scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
4664
4678
  const proj = resolveProject(registry, req, reply);
4665
4679
  if (!proj) return;
4666
4680
  const { nodeId } = req.params;
@@ -4770,7 +4784,7 @@ function registerRoutes(scope, ctx) {
4770
4784
  if (req.query.policyId) {
4771
4785
  violations = violations.filter((v) => v.policyId === req.query.policyId);
4772
4786
  }
4773
- return violations;
4787
+ return { violations };
4774
4788
  });
4775
4789
  scope.post("/policies/check", async (req, reply) => {
4776
4790
  const proj = resolveProject(registry, req, reply);
@@ -4849,6 +4863,20 @@ async function buildApi(opts) {
4849
4863
  });
4850
4864
  }
4851
4865
  });
4866
+ app.get("/projects/:project", async (req, reply) => {
4867
+ try {
4868
+ const entry = await getProject(req.params.project);
4869
+ if (!entry) {
4870
+ return reply.code(404).send({ error: "project not found", project: req.params.project });
4871
+ }
4872
+ return { project: entry };
4873
+ } catch (err) {
4874
+ return reply.code(500).send({
4875
+ error: "failed to read project registry",
4876
+ details: err.message
4877
+ });
4878
+ }
4879
+ });
4852
4880
  registerRoutes(app, routeCtx);
4853
4881
  await app.register(
4854
4882
  async (scope) => {