@neat.is/core 0.4.18 → 0.4.19
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-T3X6XJPU.js → chunk-Q5EDVWKZ.js} +68 -38
- package/dist/{chunk-T3X6XJPU.js.map → chunk-Q5EDVWKZ.js.map} +1 -1
- package/dist/{chunk-GXWBMJDJ.js → chunk-ZV7GHZ2D.js} +17 -18
- package/dist/chunk-ZV7GHZ2D.js.map +1 -0
- package/dist/cli.cjs +67 -37
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +81 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +89 -54
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +10 -3
- package/dist/neatd.js.map +1 -1
- package/dist/server.cjs +67 -37
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-GXWBMJDJ.js.map +0 -1
|
@@ -4980,7 +4980,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
4980
4980
|
graph.clear();
|
|
4981
4981
|
graph.import(payload.graph);
|
|
4982
4982
|
}
|
|
4983
|
-
function startPersistLoop(graph, outPath,
|
|
4983
|
+
function startPersistLoop(graph, outPath, opts = {}) {
|
|
4984
|
+
const intervalMs = opts.intervalMs ?? 6e4;
|
|
4985
|
+
const exitOnSignal = opts.exitOnSignal ?? true;
|
|
4984
4986
|
let stopped = false;
|
|
4985
4987
|
const tick = async () => {
|
|
4986
4988
|
if (stopped) return;
|
|
@@ -4993,7 +4995,7 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
4993
4995
|
const interval = setInterval(() => {
|
|
4994
4996
|
void tick();
|
|
4995
4997
|
}, intervalMs);
|
|
4996
|
-
const onSignal = (signal) => {
|
|
4998
|
+
const onSignal = exitOnSignal ? (signal) => {
|
|
4997
4999
|
void (async () => {
|
|
4998
5000
|
try {
|
|
4999
5001
|
await saveGraphToDisk(graph, outPath);
|
|
@@ -5003,14 +5005,18 @@ function startPersistLoop(graph, outPath, intervalMs = 6e4) {
|
|
|
5003
5005
|
process.exit(0);
|
|
5004
5006
|
}
|
|
5005
5007
|
})();
|
|
5006
|
-
};
|
|
5007
|
-
|
|
5008
|
-
|
|
5008
|
+
} : null;
|
|
5009
|
+
if (onSignal) {
|
|
5010
|
+
process.on("SIGTERM", onSignal);
|
|
5011
|
+
process.on("SIGINT", onSignal);
|
|
5012
|
+
}
|
|
5009
5013
|
return () => {
|
|
5010
5014
|
stopped = true;
|
|
5011
5015
|
clearInterval(interval);
|
|
5012
|
-
|
|
5013
|
-
|
|
5016
|
+
if (onSignal) {
|
|
5017
|
+
process.off("SIGTERM", onSignal);
|
|
5018
|
+
process.off("SIGINT", onSignal);
|
|
5019
|
+
}
|
|
5014
5020
|
};
|
|
5015
5021
|
}
|
|
5016
5022
|
|
|
@@ -5926,12 +5932,16 @@ function serializeGraph(graph) {
|
|
|
5926
5932
|
});
|
|
5927
5933
|
return { nodes, edges };
|
|
5928
5934
|
}
|
|
5929
|
-
function projectFromReq(req) {
|
|
5935
|
+
function projectFromReq(req, singleProject) {
|
|
5930
5936
|
const params = req.params;
|
|
5931
|
-
|
|
5937
|
+
const named = params.project;
|
|
5938
|
+
if (singleProject) {
|
|
5939
|
+
return named === void 0 || named === DEFAULT_PROJECT ? singleProject : named;
|
|
5940
|
+
}
|
|
5941
|
+
return named ?? DEFAULT_PROJECT;
|
|
5932
5942
|
}
|
|
5933
|
-
function resolveProject(registry, req, reply, bootstrap) {
|
|
5934
|
-
const name = projectFromReq(req);
|
|
5943
|
+
function resolveProject(registry, req, reply, bootstrap, singleProject) {
|
|
5944
|
+
const name = projectFromReq(req, singleProject);
|
|
5935
5945
|
const ctx = registry.get(name);
|
|
5936
5946
|
if (!ctx) {
|
|
5937
5947
|
const phase = bootstrap?.status(name);
|
|
@@ -5972,13 +5982,13 @@ function buildLegacyRegistry(opts) {
|
|
|
5972
5982
|
function registerRoutes(scope, ctx) {
|
|
5973
5983
|
const { registry, startedAt, errorsPathFor, staleEventsPathFor } = ctx;
|
|
5974
5984
|
scope.get("/events", (req, reply) => {
|
|
5975
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
5985
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5976
5986
|
if (!proj) return;
|
|
5977
5987
|
handleSse(req, reply, { project: proj.name });
|
|
5978
5988
|
});
|
|
5979
5989
|
if (ctx.scope === "project") {
|
|
5980
5990
|
scope.get("/health", async (req, reply) => {
|
|
5981
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
5991
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
5982
5992
|
if (!proj) return;
|
|
5983
5993
|
const uptimeMs = Date.now() - startedAt;
|
|
5984
5994
|
return {
|
|
@@ -5996,14 +6006,14 @@ function registerRoutes(scope, ctx) {
|
|
|
5996
6006
|
});
|
|
5997
6007
|
}
|
|
5998
6008
|
scope.get("/graph", async (req, reply) => {
|
|
5999
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6009
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6000
6010
|
if (!proj) return;
|
|
6001
6011
|
return serializeGraph(proj.graph);
|
|
6002
6012
|
});
|
|
6003
6013
|
scope.get(
|
|
6004
6014
|
"/graph/node/:id",
|
|
6005
6015
|
async (req, reply) => {
|
|
6006
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6016
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6007
6017
|
if (!proj) return;
|
|
6008
6018
|
const { id } = req.params;
|
|
6009
6019
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -6015,7 +6025,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6015
6025
|
scope.get(
|
|
6016
6026
|
"/graph/edges/:id",
|
|
6017
6027
|
async (req, reply) => {
|
|
6018
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6028
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6019
6029
|
if (!proj) return;
|
|
6020
6030
|
const { id } = req.params;
|
|
6021
6031
|
if (!proj.graph.hasNode(id)) {
|
|
@@ -6027,7 +6037,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6027
6037
|
}
|
|
6028
6038
|
);
|
|
6029
6039
|
scope.get("/graph/dependencies/:nodeId", async (req, reply) => {
|
|
6030
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6040
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6031
6041
|
if (!proj) return;
|
|
6032
6042
|
const { nodeId } = req.params;
|
|
6033
6043
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6042,7 +6052,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6042
6052
|
return getTransitiveDependencies(proj.graph, nodeId, depth);
|
|
6043
6053
|
});
|
|
6044
6054
|
scope.get("/graph/divergences", async (req, reply) => {
|
|
6045
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6055
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6046
6056
|
if (!proj) return;
|
|
6047
6057
|
let typeFilter;
|
|
6048
6058
|
if (req.query.type) {
|
|
@@ -6077,7 +6087,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6077
6087
|
});
|
|
6078
6088
|
});
|
|
6079
6089
|
scope.get("/incidents", async (req, reply) => {
|
|
6080
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6090
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6081
6091
|
if (!proj) return;
|
|
6082
6092
|
const epath = errorsPathFor(proj);
|
|
6083
6093
|
if (!epath) return { count: 0, total: 0, events: [] };
|
|
@@ -6089,7 +6099,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6089
6099
|
return { count: sliced.length, total, events: sliced };
|
|
6090
6100
|
});
|
|
6091
6101
|
scope.get("/stale-events", async (req, reply) => {
|
|
6092
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6102
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6093
6103
|
if (!proj) return;
|
|
6094
6104
|
const spath = staleEventsPathFor(proj);
|
|
6095
6105
|
if (!spath) return { count: 0, total: 0, events: [] };
|
|
@@ -6104,7 +6114,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6104
6114
|
scope.get(
|
|
6105
6115
|
"/incidents/:nodeId",
|
|
6106
6116
|
async (req, reply) => {
|
|
6107
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6117
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6108
6118
|
if (!proj) return;
|
|
6109
6119
|
const { nodeId } = req.params;
|
|
6110
6120
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6120,7 +6130,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6120
6130
|
}
|
|
6121
6131
|
);
|
|
6122
6132
|
scope.get("/graph/root-cause/:nodeId", async (req, reply) => {
|
|
6123
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6133
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6124
6134
|
if (!proj) return;
|
|
6125
6135
|
const { nodeId } = req.params;
|
|
6126
6136
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6140,7 +6150,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6140
6150
|
return result;
|
|
6141
6151
|
});
|
|
6142
6152
|
scope.get("/graph/blast-radius/:nodeId", async (req, reply) => {
|
|
6143
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6153
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6144
6154
|
if (!proj) return;
|
|
6145
6155
|
const { nodeId } = req.params;
|
|
6146
6156
|
if (!proj.graph.hasNode(nodeId)) {
|
|
@@ -6153,7 +6163,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6153
6163
|
return getBlastRadius(proj.graph, nodeId, depth);
|
|
6154
6164
|
});
|
|
6155
6165
|
scope.get("/search", async (req, reply) => {
|
|
6156
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6166
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6157
6167
|
if (!proj) return;
|
|
6158
6168
|
const raw = (req.query.q ?? "").trim();
|
|
6159
6169
|
if (!raw) return reply.code(400).send({ error: "query parameter `q` is required" });
|
|
@@ -6184,7 +6194,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6184
6194
|
scope.get(
|
|
6185
6195
|
"/graph/diff",
|
|
6186
6196
|
async (req, reply) => {
|
|
6187
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6197
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6188
6198
|
if (!proj) return;
|
|
6189
6199
|
const against = req.query.against;
|
|
6190
6200
|
if (!against) {
|
|
@@ -6199,7 +6209,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6199
6209
|
}
|
|
6200
6210
|
);
|
|
6201
6211
|
scope.post("/snapshot", async (req, reply) => {
|
|
6202
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6212
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6203
6213
|
if (!proj) return;
|
|
6204
6214
|
const body = req.body;
|
|
6205
6215
|
if (!body || typeof body !== "object" || !body.snapshot) {
|
|
@@ -6228,7 +6238,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6228
6238
|
}
|
|
6229
6239
|
});
|
|
6230
6240
|
scope.post("/graph/scan", async (req, reply) => {
|
|
6231
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6241
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6232
6242
|
if (!proj) return;
|
|
6233
6243
|
if (!proj.scanPath) {
|
|
6234
6244
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6244,7 +6254,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6244
6254
|
};
|
|
6245
6255
|
});
|
|
6246
6256
|
scope.get("/policies", async (req, reply) => {
|
|
6247
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6257
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6248
6258
|
if (!proj) return;
|
|
6249
6259
|
const policyPath = ctx.policyFilePathFor(proj);
|
|
6250
6260
|
if (!policyPath) {
|
|
@@ -6261,7 +6271,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6261
6271
|
}
|
|
6262
6272
|
});
|
|
6263
6273
|
scope.get("/policies/violations", async (req, reply) => {
|
|
6264
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6274
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6265
6275
|
if (!proj) return;
|
|
6266
6276
|
const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
|
|
6267
6277
|
let violations = await log.readAll();
|
|
@@ -6281,7 +6291,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6281
6291
|
return { violations };
|
|
6282
6292
|
});
|
|
6283
6293
|
scope.post("/policies/check", async (req, reply) => {
|
|
6284
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6294
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6285
6295
|
if (!proj) return;
|
|
6286
6296
|
const parsed = PoliciesCheckBodySchema.safeParse(req.body ?? {});
|
|
6287
6297
|
if (!parsed.success) {
|
|
@@ -6317,7 +6327,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6317
6327
|
};
|
|
6318
6328
|
});
|
|
6319
6329
|
scope.get("/extend/list-uninstrumented", async (req, reply) => {
|
|
6320
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6330
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6321
6331
|
if (!proj) return;
|
|
6322
6332
|
if (!proj.scanPath) {
|
|
6323
6333
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6330,7 +6340,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6330
6340
|
}
|
|
6331
6341
|
});
|
|
6332
6342
|
scope.get("/extend/lookup", async (req, reply) => {
|
|
6333
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6343
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6334
6344
|
if (!proj) return;
|
|
6335
6345
|
const { library, version } = req.query;
|
|
6336
6346
|
if (!library) {
|
|
@@ -6343,7 +6353,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6343
6353
|
return result;
|
|
6344
6354
|
});
|
|
6345
6355
|
scope.get("/extend/describe", async (req, reply) => {
|
|
6346
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6356
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6347
6357
|
if (!proj) return;
|
|
6348
6358
|
if (!proj.scanPath) {
|
|
6349
6359
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6356,7 +6366,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6356
6366
|
}
|
|
6357
6367
|
});
|
|
6358
6368
|
scope.post("/extend/apply", async (req, reply) => {
|
|
6359
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6369
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6360
6370
|
if (!proj) return;
|
|
6361
6371
|
if (!proj.scanPath) {
|
|
6362
6372
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6376,7 +6386,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6376
6386
|
}
|
|
6377
6387
|
});
|
|
6378
6388
|
scope.post("/extend/dry-run", async (req, reply) => {
|
|
6379
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6389
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6380
6390
|
if (!proj) return;
|
|
6381
6391
|
if (!proj.scanPath) {
|
|
6382
6392
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6396,7 +6406,7 @@ function registerRoutes(scope, ctx) {
|
|
|
6396
6406
|
}
|
|
6397
6407
|
});
|
|
6398
6408
|
scope.post("/extend/rollback", async (req, reply) => {
|
|
6399
|
-
const proj = resolveProject(registry, req, reply, ctx.bootstrap);
|
|
6409
|
+
const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
|
|
6400
6410
|
if (!proj) return;
|
|
6401
6411
|
if (!proj.scanPath) {
|
|
6402
6412
|
return reply.code(409).send({ error: "scan path not configured for this project", project: proj.name });
|
|
@@ -6459,7 +6469,8 @@ async function buildApi(opts) {
|
|
|
6459
6469
|
errorsPathFor,
|
|
6460
6470
|
staleEventsPathFor,
|
|
6461
6471
|
policyFilePathFor,
|
|
6462
|
-
bootstrap: opts.bootstrap
|
|
6472
|
+
bootstrap: opts.bootstrap,
|
|
6473
|
+
singleProject: opts.singleProject?.name
|
|
6463
6474
|
};
|
|
6464
6475
|
app.get("/health", async () => {
|
|
6465
6476
|
const uptimeMs = Date.now() - startedAt;
|
|
@@ -6482,10 +6493,29 @@ async function buildApi(opts) {
|
|
|
6482
6493
|
return {
|
|
6483
6494
|
ok: true,
|
|
6484
6495
|
uptimeMs,
|
|
6496
|
+
// ADR-096 §7 — a per-project daemon carries its project at the top level
|
|
6497
|
+
// so a spawn can confirm a daemon found on a reused port is serving THIS
|
|
6498
|
+
// project before reusing it. The legacy multi-project daemon omits it and
|
|
6499
|
+
// is matched against the `projects` array instead.
|
|
6500
|
+
...opts.singleProject ? { project: opts.singleProject.name } : {},
|
|
6485
6501
|
projects
|
|
6486
6502
|
};
|
|
6487
6503
|
});
|
|
6488
6504
|
app.get("/projects", async (_req, reply) => {
|
|
6505
|
+
if (opts.singleProject) {
|
|
6506
|
+
const phase = opts.bootstrap?.status(opts.singleProject.name);
|
|
6507
|
+
const entry = {
|
|
6508
|
+
name: opts.singleProject.name,
|
|
6509
|
+
path: opts.singleProject.path,
|
|
6510
|
+
registeredAt: new Date(startedAt).toISOString(),
|
|
6511
|
+
languages: [],
|
|
6512
|
+
// The daemon is serving this project, so it's active unless its
|
|
6513
|
+
// bootstrap broke. ('bootstrapping' still reads as active — the project
|
|
6514
|
+
// is the one to land on; its graph route answers 503 until ready.)
|
|
6515
|
+
status: phase === "broken" ? "broken" : "active"
|
|
6516
|
+
};
|
|
6517
|
+
return [entry];
|
|
6518
|
+
}
|
|
6489
6519
|
try {
|
|
6490
6520
|
return await listProjects();
|
|
6491
6521
|
} catch (err) {
|
|
@@ -6587,4 +6617,4 @@ export {
|
|
|
6587
6617
|
pruneRegistry,
|
|
6588
6618
|
buildApi
|
|
6589
6619
|
};
|
|
6590
|
-
//# sourceMappingURL=chunk-
|
|
6620
|
+
//# sourceMappingURL=chunk-Q5EDVWKZ.js.map
|