@neat.is/core 0.4.0 → 0.4.3
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/{chunk-N6RPINEJ.js → chunk-CB2UK4EH.js} +138 -41
- package/dist/chunk-CB2UK4EH.js.map +1 -0
- package/dist/{chunk-IXIFJKMM.js → chunk-D5PIJFBE.js} +2 -2
- package/dist/{chunk-4V23KYOP.js → chunk-KYRIQIPG.js} +17 -9
- package/dist/chunk-KYRIQIPG.js.map +1 -0
- package/dist/{chunk-UPW4CMOH.js → chunk-NTQHMXWE.js} +122 -46
- package/dist/chunk-NTQHMXWE.js.map +1 -0
- package/dist/cli.cjs +351 -121
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +235 -81
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +267 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +276 -102
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-GGZHR7VM.js → otel-grpc-QTX2YQJZ.js} +3 -3
- package/dist/server.cjs +120 -44
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-4V23KYOP.js.map +0 -1
- package/dist/chunk-N6RPINEJ.js.map +0 -1
- package/dist/chunk-UPW4CMOH.js.map +0 -1
- /package/dist/{chunk-IXIFJKMM.js.map → chunk-D5PIJFBE.js.map} +0 -0
- /package/dist/{otel-grpc-GGZHR7VM.js.map → otel-grpc-QTX2YQJZ.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mountBearerAuth
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KYRIQIPG.js";
|
|
4
4
|
|
|
5
5
|
// src/graph.ts
|
|
6
6
|
import GraphDefault from "graphology";
|
|
@@ -1668,8 +1668,36 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
1668
1668
|
".turbo",
|
|
1669
1669
|
"dist",
|
|
1670
1670
|
"build",
|
|
1671
|
-
".next"
|
|
1671
|
+
".next",
|
|
1672
|
+
// Python virtualenv shapes (issue #344). Walking into a venv pulls in the
|
|
1673
|
+
// entire CPython stdlib + every installed package as if it were first-party
|
|
1674
|
+
// service code — 20k+ files, none of which the user wrote. The shape names
|
|
1675
|
+
// cover the three common venv tools (`venv` / `python -m venv`,
|
|
1676
|
+
// `virtualenv`) and the tox + PEP 582 conventions.
|
|
1677
|
+
".venv",
|
|
1678
|
+
"venv",
|
|
1679
|
+
"__pypackages__",
|
|
1680
|
+
".tox",
|
|
1681
|
+
// `site-packages` shows up nested inside venvs that don't carry one of the
|
|
1682
|
+
// outer names (system Python on macOS, in-place pyenv layouts). Listing it
|
|
1683
|
+
// here means we stop the walk at the boundary even when the wrapper dir
|
|
1684
|
+
// wasn't recognisable.
|
|
1685
|
+
"site-packages"
|
|
1672
1686
|
]);
|
|
1687
|
+
var PYVENV_MARKER_CACHE = /* @__PURE__ */ new Map();
|
|
1688
|
+
async function isPythonVenvDir(dir) {
|
|
1689
|
+
const cached = PYVENV_MARKER_CACHE.get(dir);
|
|
1690
|
+
if (cached !== void 0) return cached;
|
|
1691
|
+
try {
|
|
1692
|
+
const stat = await fs5.stat(path5.join(dir, "pyvenv.cfg"));
|
|
1693
|
+
const ok = stat.isFile();
|
|
1694
|
+
PYVENV_MARKER_CACHE.set(dir, ok);
|
|
1695
|
+
return ok;
|
|
1696
|
+
} catch {
|
|
1697
|
+
PYVENV_MARKER_CACHE.set(dir, false);
|
|
1698
|
+
return false;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1673
1701
|
function isConfigFile(name) {
|
|
1674
1702
|
const ext = path5.extname(name);
|
|
1675
1703
|
if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) };
|
|
@@ -1960,6 +1988,7 @@ async function walkDirs(start, scanPath, options, visit) {
|
|
|
1960
1988
|
const rel = path8.relative(scanPath, child).split(path8.sep).join("/");
|
|
1961
1989
|
if (rel && options.ig.ignores(rel + "/")) continue;
|
|
1962
1990
|
}
|
|
1991
|
+
if (await isPythonVenvDir(child)) continue;
|
|
1963
1992
|
await visit(child);
|
|
1964
1993
|
await recurse(child, depth + 1);
|
|
1965
1994
|
}
|
|
@@ -2237,7 +2266,9 @@ async function walkYamlFiles(start, depth = 0, max = 5) {
|
|
|
2237
2266
|
for (const entry of entries) {
|
|
2238
2267
|
if (entry.isDirectory()) {
|
|
2239
2268
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2240
|
-
|
|
2269
|
+
const child = path9.join(start, entry.name);
|
|
2270
|
+
if (await isPythonVenvDir(child)) continue;
|
|
2271
|
+
out.push(...await walkYamlFiles(child, depth + 1, max));
|
|
2241
2272
|
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path9.extname(entry.name))) {
|
|
2242
2273
|
out.push(path9.join(start, entry.name));
|
|
2243
2274
|
}
|
|
@@ -2919,7 +2950,9 @@ async function walkConfigFiles(dir) {
|
|
|
2919
2950
|
for (const entry of entries) {
|
|
2920
2951
|
const full = path18.join(current, entry.name);
|
|
2921
2952
|
if (entry.isDirectory()) {
|
|
2922
|
-
if (
|
|
2953
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2954
|
+
if (await isPythonVenvDir(full)) continue;
|
|
2955
|
+
await walk(full);
|
|
2923
2956
|
} else if (entry.isFile() && isConfigFile(entry.name).match) {
|
|
2924
2957
|
out.push(full);
|
|
2925
2958
|
}
|
|
@@ -2995,7 +3028,9 @@ async function walkSourceFiles(dir) {
|
|
|
2995
3028
|
for (const entry of entries) {
|
|
2996
3029
|
const full = path19.join(current, entry.name);
|
|
2997
3030
|
if (entry.isDirectory()) {
|
|
2998
|
-
if (
|
|
3031
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
3032
|
+
if (await isPythonVenvDir(full)) continue;
|
|
3033
|
+
await walk(full);
|
|
2999
3034
|
} else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(path19.extname(entry.name))) {
|
|
3000
3035
|
out.push(full);
|
|
3001
3036
|
}
|
|
@@ -3620,7 +3655,9 @@ async function walkTfFiles(start, depth = 0, max = 5) {
|
|
|
3620
3655
|
for (const entry of entries) {
|
|
3621
3656
|
if (entry.isDirectory()) {
|
|
3622
3657
|
if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
|
|
3623
|
-
|
|
3658
|
+
const child = path27.join(start, entry.name);
|
|
3659
|
+
if (await isPythonVenvDir(child)) continue;
|
|
3660
|
+
out.push(...await walkTfFiles(child, depth + 1, max));
|
|
3624
3661
|
} else if (entry.isFile() && entry.name.endsWith(".tf")) {
|
|
3625
3662
|
out.push(path27.join(start, entry.name));
|
|
3626
3663
|
}
|
|
@@ -3667,7 +3704,9 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
|
|
|
3667
3704
|
for (const entry of entries) {
|
|
3668
3705
|
if (entry.isDirectory()) {
|
|
3669
3706
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
3670
|
-
|
|
3707
|
+
const child = path28.join(start, entry.name);
|
|
3708
|
+
if (await isPythonVenvDir(child)) continue;
|
|
3709
|
+
out.push(...await walkYamlFiles2(child, depth + 1, max));
|
|
3671
3710
|
} else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path28.extname(entry.name))) {
|
|
3672
3711
|
out.push(path28.join(start, entry.name));
|
|
3673
3712
|
}
|
|
@@ -4541,10 +4580,19 @@ function projectFromReq(req) {
|
|
|
4541
4580
|
const params = req.params;
|
|
4542
4581
|
return params.project ?? DEFAULT_PROJECT;
|
|
4543
4582
|
}
|
|
4544
|
-
function resolveProject(registry, req, reply) {
|
|
4583
|
+
function resolveProject(registry, req, reply, bootstrap) {
|
|
4545
4584
|
const name = projectFromReq(req);
|
|
4546
4585
|
const ctx = registry.get(name);
|
|
4547
4586
|
if (!ctx) {
|
|
4587
|
+
const phase = bootstrap?.status(name);
|
|
4588
|
+
if (phase === "bootstrapping") {
|
|
4589
|
+
void reply.code(503).send({ ready: false, project: name, status: "bootstrapping" });
|
|
4590
|
+
return null;
|
|
4591
|
+
}
|
|
4592
|
+
if (phase === "broken") {
|
|
4593
|
+
void reply.code(503).send({ ready: false, project: name, status: "broken" });
|
|
4594
|
+
return null;
|
|
4595
|
+
}
|
|
4548
4596
|
void reply.code(404).send({ error: "project not found", project: name });
|
|
4549
4597
|
return null;
|
|
4550
4598
|
}
|
|
@@ -4574,36 +4622,38 @@ function buildLegacyRegistry(opts) {
|
|
|
4574
4622
|
function registerRoutes(scope, ctx) {
|
|
4575
4623
|
const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx;
|
|
4576
4624
|
scope.get("/events", (req, reply) => {
|
|
4577
|
-
const proj = resolveProject(registry, req, reply);
|
|
4625
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4578
4626
|
if (!proj) return;
|
|
4579
4627
|
handleSse(req, reply, { project: proj.name });
|
|
4580
4628
|
});
|
|
4581
|
-
scope
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4629
|
+
if (ctx.scope === "project") {
|
|
4630
|
+
scope.get("/health", async (req, reply) => {
|
|
4631
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4632
|
+
if (!proj) return;
|
|
4633
|
+
const uptimeMs = Date.now() - startedAt;
|
|
4634
|
+
return {
|
|
4635
|
+
ok: true,
|
|
4636
|
+
project: proj.name,
|
|
4637
|
+
uptimeMs,
|
|
4638
|
+
// Legacy fields kept additively. The web shell's StatusBar reads
|
|
4639
|
+
// nodeCount / edgeCount; ADR-061's HealthResponseSchema validates
|
|
4640
|
+
// the canonical triple and lets the extras pass through.
|
|
4641
|
+
uptime: Math.floor(uptimeMs / 1e3),
|
|
4642
|
+
nodeCount: proj.graph.order,
|
|
4643
|
+
edgeCount: proj.graph.size,
|
|
4644
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
4645
|
+
};
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4598
4648
|
scope.get("/graph", async (req, reply) => {
|
|
4599
|
-
const proj = resolveProject(registry, req, reply);
|
|
4649
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4600
4650
|
if (!proj) return;
|
|
4601
4651
|
return serializeGraph(proj.graph);
|
|
4602
4652
|
});
|
|
4603
4653
|
scope.get(
|
|
4604
4654
|
"/graph/node/:id",
|
|
4605
4655
|
async (req, reply) => {
|
|
4606
|
-
const proj = resolveProject(registry, req, reply);
|
|
4656
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4607
4657
|
if (!proj) return;
|
|
4608
4658
|
const { id } = req.params;
|
|
4609
4659
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -4615,7 +4665,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4615
4665
|
scope.get(
|
|
4616
4666
|
"/graph/edges/:id",
|
|
4617
4667
|
async (req, reply) => {
|
|
4618
|
-
const proj = resolveProject(registry, req, reply);
|
|
4668
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4619
4669
|
if (!proj) return;
|
|
4620
4670
|
const { id } = req.params;
|
|
4621
4671
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -4627,7 +4677,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4627
4677
|
}
|
|
4628
4678
|
);
|
|
4629
4679
|
scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
|
|
4630
|
-
const proj = resolveProject(registry, req, reply);
|
|
4680
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4631
4681
|
if (!proj) return;
|
|
4632
4682
|
const { nodeId } = req.params;
|
|
4633
4683
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -4642,7 +4692,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4642
4692
|
return getTransitiveDependencies(proj.graph, nodeId, depth);
|
|
4643
4693
|
});
|
|
4644
4694
|
scope.get("/graph/divergences", async (req, reply) => {
|
|
4645
|
-
const proj = resolveProject(registry, req, reply);
|
|
4695
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4646
4696
|
if (!proj) return;
|
|
4647
4697
|
let typeFilter;
|
|
4648
4698
|
if (req.query.type) {
|
|
@@ -4677,7 +4727,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4677
4727
|
});
|
|
4678
4728
|
});
|
|
4679
4729
|
scope.get("/incidents", async (req, reply) => {
|
|
4680
|
-
const proj = resolveProject(registry, req, reply);
|
|
4730
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4681
4731
|
if (!proj) return;
|
|
4682
4732
|
const epath = errorsPathFor(proj);
|
|
4683
4733
|
if (!epath) return { count: 0, total: 0, events: [] };
|
|
@@ -4689,7 +4739,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4689
4739
|
return { count: sliced.length, total, events: sliced };
|
|
4690
4740
|
});
|
|
4691
4741
|
scope.get("/stale-events", async (req, reply) => {
|
|
4692
|
-
const proj = resolveProject(registry, req, reply);
|
|
4742
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4693
4743
|
if (!proj) return;
|
|
4694
4744
|
const spath = staleEventsPathFor(proj);
|
|
4695
4745
|
if (!spath) return { count: 0, total: 0, events: [] };
|
|
@@ -4704,7 +4754,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4704
4754
|
scope.get(
|
|
4705
4755
|
"/incidents/:nodeId",
|
|
4706
4756
|
async (req, reply) => {
|
|
4707
|
-
const proj = resolveProject(registry, req, reply);
|
|
4757
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4708
4758
|
if (!proj) return;
|
|
4709
4759
|
const { nodeId } = req.params;
|
|
4710
4760
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -4720,7 +4770,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4720
4770
|
}
|
|
4721
4771
|
);
|
|
4722
4772
|
scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
|
|
4723
|
-
const proj = resolveProject(registry, req, reply);
|
|
4773
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4724
4774
|
if (!proj) return;
|
|
4725
4775
|
const { nodeId } = req.params;
|
|
4726
4776
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -4740,7 +4790,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4740
4790
|
return result;
|
|
4741
4791
|
});
|
|
4742
4792
|
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
4743
|
-
const proj = resolveProject(registry, req, reply);
|
|
4793
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4744
4794
|
if (!proj) return;
|
|
4745
4795
|
const { nodeId } = req.params;
|
|
4746
4796
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -4753,7 +4803,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4753
4803
|
return getBlastRadius(proj.graph, nodeId, depth);
|
|
4754
4804
|
});
|
|
4755
4805
|
scope.get("/search", async (req, reply) => {
|
|
4756
|
-
const proj = resolveProject(registry, req, reply);
|
|
4806
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4757
4807
|
if (!proj) return;
|
|
4758
4808
|
const raw = (req.query.q ?? "").trim();
|
|
4759
4809
|
if (!raw) return reply.code(400).send({ error: "query parameter `q` is required" });
|
|
@@ -4784,7 +4834,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4784
4834
|
scope.get(
|
|
4785
4835
|
"/graph/diff",
|
|
4786
4836
|
async (req, reply) => {
|
|
4787
|
-
const proj = resolveProject(registry, req, reply);
|
|
4837
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4788
4838
|
if (!proj) return;
|
|
4789
4839
|
const against = req.query.against;
|
|
4790
4840
|
if (!against) {
|
|
@@ -4799,7 +4849,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4799
4849
|
}
|
|
4800
4850
|
);
|
|
4801
4851
|
scope.post("/snapshot", async (req, reply) => {
|
|
4802
|
-
const proj = resolveProject(registry, req, reply);
|
|
4852
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4803
4853
|
if (!proj) return;
|
|
4804
4854
|
const body = req.body;
|
|
4805
4855
|
if (!body || typeof body !== "object" || !body.snapshot) {
|
|
@@ -4828,7 +4878,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4828
4878
|
}
|
|
4829
4879
|
});
|
|
4830
4880
|
scope.post("/graph/scan", async (req, reply) => {
|
|
4831
|
-
const proj = resolveProject(registry, req, reply);
|
|
4881
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4832
4882
|
if (!proj) return;
|
|
4833
4883
|
if (!proj.scanPath) {
|
|
4834
4884
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -4844,7 +4894,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4844
4894
|
};
|
|
4845
4895
|
});
|
|
4846
4896
|
scope.get("/policies", async (req, reply) => {
|
|
4847
|
-
const proj = resolveProject(registry, req, reply);
|
|
4897
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4848
4898
|
if (!proj) return;
|
|
4849
4899
|
const policyPath = ctx.policyFilePathFor(proj);
|
|
4850
4900
|
if (!policyPath) {
|
|
@@ -4861,7 +4911,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4861
4911
|
}
|
|
4862
4912
|
});
|
|
4863
4913
|
scope.get("/policies/violations", async (req, reply) => {
|
|
4864
|
-
const proj = resolveProject(registry, req, reply);
|
|
4914
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4865
4915
|
if (!proj) return;
|
|
4866
4916
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
4867
4917
|
let violations = await log.readAll();
|
|
@@ -4881,7 +4931,7 @@ function registerRoutes(scope, ctx) {
|
|
|
4881
4931
|
return { violations };
|
|
4882
4932
|
});
|
|
4883
4933
|
scope.post("/policies/check", async (req, reply) => {
|
|
4884
|
-
const proj = resolveProject(registry, req, reply);
|
|
4934
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
4885
4935
|
if (!proj) return;
|
|
4886
4936
|
const parsed = PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
4887
4937
|
if (!parsed.success) {
|
|
@@ -4952,10 +5002,36 @@ async function buildApi(opts) {
|
|
|
4952
5002
|
const routeCtx = {
|
|
4953
5003
|
registry,
|
|
4954
5004
|
startedAt,
|
|
5005
|
+
scope: "root",
|
|
4955
5006
|
errorsPathFor,
|
|
4956
5007
|
staleEventsPathFor,
|
|
4957
|
-
policyFilePathFor
|
|
5008
|
+
policyFilePathFor,
|
|
5009
|
+
bootstrap: opts.bootstrap
|
|
4958
5010
|
};
|
|
5011
|
+
app.get("/health", async () => {
|
|
5012
|
+
const uptimeMs = Date.now() - startedAt;
|
|
5013
|
+
const bootstrapList = opts.bootstrap?.list() ?? [];
|
|
5014
|
+
const byName = new Map(bootstrapList.map((p) => [p.name, p]));
|
|
5015
|
+
const names = /* @__PURE__ */ new Set([
|
|
5016
|
+
...registry.list(),
|
|
5017
|
+
...bootstrapList.map((p) => p.name)
|
|
5018
|
+
]);
|
|
5019
|
+
const projects = [...names].sort().map((name) => {
|
|
5020
|
+
const proj = registry.get(name);
|
|
5021
|
+
const tracked = byName.get(name);
|
|
5022
|
+
return {
|
|
5023
|
+
name,
|
|
5024
|
+
nodeCount: proj?.graph.order ?? 0,
|
|
5025
|
+
edgeCount: proj?.graph.size ?? 0,
|
|
5026
|
+
...tracked ? { status: tracked.status, elapsedMs: tracked.elapsedMs } : {}
|
|
5027
|
+
};
|
|
5028
|
+
});
|
|
5029
|
+
return {
|
|
5030
|
+
ok: true,
|
|
5031
|
+
uptimeMs,
|
|
5032
|
+
projects
|
|
5033
|
+
};
|
|
5034
|
+
});
|
|
4959
5035
|
app.get("/projects", async (_req, reply) => {
|
|
4960
5036
|
try {
|
|
4961
5037
|
return await listProjects();
|
|
@@ -4983,7 +5059,7 @@ async function buildApi(opts) {
|
|
|
4983
5059
|
registerRoutes(app, routeCtx);
|
|
4984
5060
|
await app.register(
|
|
4985
5061
|
async (scope) => {
|
|
4986
|
-
registerRoutes(scope, routeCtx);
|
|
5062
|
+
registerRoutes(scope, { ...routeCtx, scope: "project" });
|
|
4987
5063
|
},
|
|
4988
5064
|
{ prefix: "/projects/:project" }
|
|
4989
5065
|
);
|
|
@@ -5050,4 +5126,4 @@ export {
|
|
|
5050
5126
|
removeProject,
|
|
5051
5127
|
buildApi
|
|
5052
5128
|
};
|
|
5053
|
-
//# sourceMappingURL=chunk-
|
|
5129
|
+
//# sourceMappingURL=chunk-NTQHMXWE.js.map
|