@neat.is/core 0.3.0 → 0.3.2
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-QYUB3FQL.js → chunk-4ASCXBZF.js} +12 -124
- package/dist/chunk-4ASCXBZF.js.map +1 -0
- package/dist/{chunk-I5KODOJV.js → chunk-66Z5IEFY.js} +126 -7
- package/dist/chunk-66Z5IEFY.js.map +1 -0
- package/dist/{chunk-B7UUGIXB.js → chunk-FIXKIYNF.js} +818 -18
- package/dist/chunk-FIXKIYNF.js.map +1 -0
- package/dist/chunk-G3PDTGOW.js +125 -0
- package/dist/chunk-G3PDTGOW.js.map +1 -0
- package/dist/cli.cjs +76 -27
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +82 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +141 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -11
- package/dist/neatd.cjs +1900 -36
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +19 -5
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-USFL3OI3.js → otel-grpc-J4O2SIBZ.js} +3 -3
- package/dist/server.js +6 -7
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-B7UUGIXB.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-I5KODOJV.js.map +0 -1
- package/dist/chunk-NVCEZXL7.js +0 -836
- package/dist/chunk-NVCEZXL7.js.map +0 -1
- package/dist/chunk-QYUB3FQL.js.map +0 -1
- package/dist/otel-grpc-USFL3OI3.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → otel-grpc-J4O2SIBZ.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1653,6 +1653,29 @@ async function appendErrorEvent(ctx, ev) {
|
|
|
1653
1653
|
await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(ctx.errorsPath), { recursive: true });
|
|
1654
1654
|
await import_node_fs3.promises.appendFile(ctx.errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
1655
1655
|
}
|
|
1656
|
+
function buildErrorEventForReceiver(span) {
|
|
1657
|
+
if (span.statusCode !== 2) return null;
|
|
1658
|
+
const ts = span.startTimeIso ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1659
|
+
return {
|
|
1660
|
+
id: `${span.traceId}:${span.spanId}`,
|
|
1661
|
+
timestamp: ts,
|
|
1662
|
+
service: span.service,
|
|
1663
|
+
traceId: span.traceId,
|
|
1664
|
+
spanId: span.spanId,
|
|
1665
|
+
errorMessage: span.exception?.message ?? span.errorMessage ?? span.name ?? "unknown error",
|
|
1666
|
+
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1667
|
+
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1668
|
+
affectedNode: (0, import_types3.serviceId)(span.service)
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
function makeErrorSpanWriter(errorsPath) {
|
|
1672
|
+
return async (span) => {
|
|
1673
|
+
const ev = buildErrorEventForReceiver(span);
|
|
1674
|
+
if (!ev) return;
|
|
1675
|
+
await import_node_fs3.promises.mkdir(import_node_path3.default.dirname(errorsPath), { recursive: true });
|
|
1676
|
+
await import_node_fs3.promises.appendFile(errorsPath, JSON.stringify(ev) + "\n", "utf8");
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1656
1679
|
async function handleSpan(ctx, span) {
|
|
1657
1680
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
1658
1681
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
@@ -4895,6 +4918,7 @@ init_otel_grpc();
|
|
|
4895
4918
|
init_cjs_shims();
|
|
4896
4919
|
var import_node_fs19 = require("fs");
|
|
4897
4920
|
var import_node_path33 = __toESM(require("path"), 1);
|
|
4921
|
+
init_otel();
|
|
4898
4922
|
function neatHomeFor(opts) {
|
|
4899
4923
|
if (opts.neatHome && opts.neatHome.length > 0) return import_node_path33.default.resolve(opts.neatHome);
|
|
4900
4924
|
const env = process.env.NEAT_HOME;
|
|
@@ -4913,6 +4937,7 @@ function routeSpanToProject(serviceName, projects) {
|
|
|
4913
4937
|
return DEFAULT_PROJECT;
|
|
4914
4938
|
}
|
|
4915
4939
|
async function bootstrapProject(entry) {
|
|
4940
|
+
const paths = pathsForProject(entry.name, import_node_path33.default.join(entry.path, "neat-out"));
|
|
4916
4941
|
try {
|
|
4917
4942
|
const stat = await import_node_fs19.promises.stat(entry.path);
|
|
4918
4943
|
if (!stat.isDirectory()) {
|
|
@@ -4927,6 +4952,7 @@ async function bootstrapProject(entry) {
|
|
|
4927
4952
|
// output; nothing routes to it because it's not 'active'.
|
|
4928
4953
|
graph: getGraph(`__broken__:${entry.name}`),
|
|
4929
4954
|
outPath: "",
|
|
4955
|
+
paths,
|
|
4930
4956
|
stopPersist: () => {
|
|
4931
4957
|
},
|
|
4932
4958
|
status: "broken",
|
|
@@ -4935,10 +4961,7 @@ async function bootstrapProject(entry) {
|
|
|
4935
4961
|
}
|
|
4936
4962
|
resetGraph(entry.name);
|
|
4937
4963
|
const graph = getGraph(entry.name);
|
|
4938
|
-
const outPath =
|
|
4939
|
-
entry.name,
|
|
4940
|
-
import_node_path33.default.join(entry.path, "neat-out")
|
|
4941
|
-
).snapshotPath;
|
|
4964
|
+
const outPath = paths.snapshotPath;
|
|
4942
4965
|
await loadGraphFromDisk(graph, outPath);
|
|
4943
4966
|
await extractFromDirectory(graph, entry.path);
|
|
4944
4967
|
const stopPersist = startPersistLoop(graph, outPath);
|
|
@@ -4948,10 +4971,35 @@ async function bootstrapProject(entry) {
|
|
|
4948
4971
|
entry,
|
|
4949
4972
|
graph,
|
|
4950
4973
|
outPath,
|
|
4974
|
+
paths,
|
|
4951
4975
|
stopPersist,
|
|
4952
4976
|
status: "active"
|
|
4953
4977
|
};
|
|
4954
4978
|
}
|
|
4979
|
+
function resolveRestPort(opts) {
|
|
4980
|
+
if (typeof opts.restPort === "number") return opts.restPort;
|
|
4981
|
+
const env = process.env.PORT;
|
|
4982
|
+
if (env && env.length > 0) {
|
|
4983
|
+
const n = Number.parseInt(env, 10);
|
|
4984
|
+
if (Number.isFinite(n)) return n;
|
|
4985
|
+
}
|
|
4986
|
+
return 8080;
|
|
4987
|
+
}
|
|
4988
|
+
function resolveOtlpPort(opts) {
|
|
4989
|
+
if (typeof opts.otlpPort === "number") return opts.otlpPort;
|
|
4990
|
+
const env = process.env.OTEL_PORT;
|
|
4991
|
+
if (env && env.length > 0) {
|
|
4992
|
+
const n = Number.parseInt(env, 10);
|
|
4993
|
+
if (Number.isFinite(n)) return n;
|
|
4994
|
+
}
|
|
4995
|
+
return 4318;
|
|
4996
|
+
}
|
|
4997
|
+
function resolveHost(opts) {
|
|
4998
|
+
if (opts.host && opts.host.length > 0) return opts.host;
|
|
4999
|
+
const env = process.env.HOST;
|
|
5000
|
+
if (env && env.length > 0) return env;
|
|
5001
|
+
return "0.0.0.0";
|
|
5002
|
+
}
|
|
4955
5003
|
async function startDaemon(opts = {}) {
|
|
4956
5004
|
const home = neatHomeFor(opts);
|
|
4957
5005
|
const regPath = registryPath();
|
|
@@ -4966,6 +5014,15 @@ async function startDaemon(opts = {}) {
|
|
|
4966
5014
|
await writeAtomically(pidPath, `${process.pid}
|
|
4967
5015
|
`);
|
|
4968
5016
|
const slots = /* @__PURE__ */ new Map();
|
|
5017
|
+
const registry = new Projects();
|
|
5018
|
+
function upsertRegistryFromSlot(slot) {
|
|
5019
|
+
if (slot.status !== "active") return;
|
|
5020
|
+
registry.set(slot.entry.name, {
|
|
5021
|
+
scanPath: slot.entry.path,
|
|
5022
|
+
paths: slot.paths,
|
|
5023
|
+
graph: slot.graph
|
|
5024
|
+
});
|
|
5025
|
+
}
|
|
4969
5026
|
async function loadAll() {
|
|
4970
5027
|
const projects = await listProjects();
|
|
4971
5028
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4975,6 +5032,7 @@ async function startDaemon(opts = {}) {
|
|
|
4975
5032
|
try {
|
|
4976
5033
|
const slot = await bootstrapProject(entry);
|
|
4977
5034
|
slots.set(entry.name, slot);
|
|
5035
|
+
upsertRegistryFromSlot(slot);
|
|
4978
5036
|
if (slot.status === "broken") {
|
|
4979
5037
|
console.warn(`neatd: project "${entry.name}" broken \u2014 ${slot.errorReason}`);
|
|
4980
5038
|
} else {
|
|
@@ -4999,6 +5057,80 @@ async function startDaemon(opts = {}) {
|
|
|
4999
5057
|
}
|
|
5000
5058
|
}
|
|
5001
5059
|
await loadAll();
|
|
5060
|
+
const bind = opts.bindListeners !== false;
|
|
5061
|
+
let restApp = null;
|
|
5062
|
+
let otlpApp = null;
|
|
5063
|
+
let restAddress = "";
|
|
5064
|
+
let otlpAddress = "";
|
|
5065
|
+
if (bind) {
|
|
5066
|
+
const host = resolveHost(opts);
|
|
5067
|
+
const restPort = resolveRestPort(opts);
|
|
5068
|
+
const otlpPort = resolveOtlpPort(opts);
|
|
5069
|
+
try {
|
|
5070
|
+
restApp = await buildApi({ projects: registry });
|
|
5071
|
+
restAddress = await restApp.listen({ port: restPort, host });
|
|
5072
|
+
console.log(`neatd: REST listening on ${restAddress}`);
|
|
5073
|
+
} catch (err) {
|
|
5074
|
+
for (const slot of slots.values()) {
|
|
5075
|
+
try {
|
|
5076
|
+
slot.stopPersist();
|
|
5077
|
+
} catch {
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
if (restApp) await restApp.close().catch(() => {
|
|
5081
|
+
});
|
|
5082
|
+
await import_node_fs19.promises.unlink(pidPath).catch(() => {
|
|
5083
|
+
});
|
|
5084
|
+
throw new Error(
|
|
5085
|
+
`neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
|
|
5086
|
+
);
|
|
5087
|
+
}
|
|
5088
|
+
try {
|
|
5089
|
+
otlpApp = await buildOtelReceiver({
|
|
5090
|
+
onSpan: async (span) => {
|
|
5091
|
+
const liveEntries = await listProjects().catch(() => []);
|
|
5092
|
+
const target = routeSpanToProject(span.service, liveEntries);
|
|
5093
|
+
const slot = slots.get(target) ?? slots.get(DEFAULT_PROJECT);
|
|
5094
|
+
if (!slot || slot.status !== "active") return;
|
|
5095
|
+
await handleSpan(
|
|
5096
|
+
{
|
|
5097
|
+
graph: slot.graph,
|
|
5098
|
+
errorsPath: slot.paths.errorsPath,
|
|
5099
|
+
project: slot.entry.name,
|
|
5100
|
+
// Receiver already wrote the error event synchronously below.
|
|
5101
|
+
writeErrorEventInline: false
|
|
5102
|
+
},
|
|
5103
|
+
span
|
|
5104
|
+
);
|
|
5105
|
+
},
|
|
5106
|
+
onErrorSpanSync: async (span) => {
|
|
5107
|
+
const liveEntries = await listProjects().catch(() => []);
|
|
5108
|
+
const target = routeSpanToProject(span.service, liveEntries);
|
|
5109
|
+
const slot = slots.get(target) ?? slots.get(DEFAULT_PROJECT);
|
|
5110
|
+
if (!slot || slot.status !== "active") return;
|
|
5111
|
+
await makeErrorSpanWriter(slot.paths.errorsPath)(span);
|
|
5112
|
+
}
|
|
5113
|
+
});
|
|
5114
|
+
otlpAddress = await otlpApp.listen({ port: otlpPort, host });
|
|
5115
|
+
console.log(`neatd: OTLP listening on ${otlpAddress}/v1/traces`);
|
|
5116
|
+
} catch (err) {
|
|
5117
|
+
for (const slot of slots.values()) {
|
|
5118
|
+
try {
|
|
5119
|
+
slot.stopPersist();
|
|
5120
|
+
} catch {
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
if (restApp) await restApp.close().catch(() => {
|
|
5124
|
+
});
|
|
5125
|
+
if (otlpApp) await otlpApp.close().catch(() => {
|
|
5126
|
+
});
|
|
5127
|
+
await import_node_fs19.promises.unlink(pidPath).catch(() => {
|
|
5128
|
+
});
|
|
5129
|
+
throw new Error(
|
|
5130
|
+
`neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
|
|
5131
|
+
);
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5002
5134
|
let reloading = null;
|
|
5003
5135
|
const reload = async () => {
|
|
5004
5136
|
if (reloading) return reloading;
|
|
@@ -5022,6 +5154,10 @@ async function startDaemon(opts = {}) {
|
|
|
5022
5154
|
if (stopped) return;
|
|
5023
5155
|
stopped = true;
|
|
5024
5156
|
process.off("SIGHUP", sighupHandler);
|
|
5157
|
+
if (otlpApp) await otlpApp.close().catch(() => {
|
|
5158
|
+
});
|
|
5159
|
+
if (restApp) await restApp.close().catch(() => {
|
|
5160
|
+
});
|
|
5025
5161
|
for (const slot of slots.values()) {
|
|
5026
5162
|
try {
|
|
5027
5163
|
slot.stopPersist();
|
|
@@ -5031,7 +5167,7 @@ async function startDaemon(opts = {}) {
|
|
|
5031
5167
|
await import_node_fs19.promises.unlink(pidPath).catch(() => {
|
|
5032
5168
|
});
|
|
5033
5169
|
};
|
|
5034
|
-
return { slots, reload, stop, pidPath };
|
|
5170
|
+
return { slots, reload, stop, pidPath, restAddress, otlpAddress };
|
|
5035
5171
|
}
|
|
5036
5172
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5037
5173
|
0 && (module.exports = {
|