@neat.is/core 0.4.20-dev.20260620 → 0.4.20-dev.20260622
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-ZV7GHZ2D.js → chunk-62ELQVIP.js} +2 -2
- package/dist/{chunk-Q5EDVWKZ.js → chunk-GNAX2CBF.js} +36 -9
- package/dist/chunk-GNAX2CBF.js.map +1 -0
- package/dist/cli.cjs +98 -11
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +67 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +34 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +34 -8
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +34 -8
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-Q5EDVWKZ.js.map +0 -1
- /package/dist/{chunk-ZV7GHZ2D.js.map → chunk-62ELQVIP.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -1958,13 +1958,14 @@ var parentSpanCache = /* @__PURE__ */ new Map();
|
|
|
1958
1958
|
function parentSpanKey(traceId, spanId) {
|
|
1959
1959
|
return `${traceId}:${spanId}`;
|
|
1960
1960
|
}
|
|
1961
|
-
function cacheSpanService(span, now) {
|
|
1961
|
+
function cacheSpanService(span, now, callSite) {
|
|
1962
1962
|
if (!span.traceId || !span.spanId) return;
|
|
1963
1963
|
const key = parentSpanKey(span.traceId, span.spanId);
|
|
1964
1964
|
parentSpanCache.delete(key);
|
|
1965
1965
|
parentSpanCache.set(key, {
|
|
1966
1966
|
service: span.service,
|
|
1967
1967
|
env: span.env ?? "unknown",
|
|
1968
|
+
...callSite ? { callSite } : {},
|
|
1968
1969
|
expiresAt: now + PARENT_SPAN_CACHE_TTL_MS
|
|
1969
1970
|
});
|
|
1970
1971
|
while (parentSpanCache.size > PARENT_SPAN_CACHE_SIZE) {
|
|
@@ -1980,7 +1981,11 @@ function lookupParentSpan(traceId, parentSpanId, now) {
|
|
|
1980
1981
|
parentSpanCache.delete(parentSpanKey(traceId, parentSpanId));
|
|
1981
1982
|
return null;
|
|
1982
1983
|
}
|
|
1983
|
-
return {
|
|
1984
|
+
return {
|
|
1985
|
+
service: entry2.service,
|
|
1986
|
+
env: entry2.env,
|
|
1987
|
+
...entry2.callSite ? { callSite: entry2.callSite } : {}
|
|
1988
|
+
};
|
|
1984
1989
|
}
|
|
1985
1990
|
function resolveServiceId(graph, host, env) {
|
|
1986
1991
|
const envTagged = (0, import_types3.serviceId)(host, env);
|
|
@@ -2253,9 +2258,9 @@ async function handleSpan(ctx, span) {
|
|
|
2253
2258
|
}
|
|
2254
2259
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
2255
2260
|
const isError = span.statusCode === 2;
|
|
2256
|
-
cacheSpanService(span, nowMs);
|
|
2257
2261
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
2258
2262
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
2263
|
+
cacheSpanService(span, nowMs, callSite);
|
|
2259
2264
|
const observedSource = () => callSite ? ensureObservedFileNode(ctx.graph, span.service, sourceId, callSite) : sourceId;
|
|
2260
2265
|
const callSiteEvidence = callSite ? { file: callSite.relPath, ...callSite.line !== void 0 ? { line: callSite.line } : {} } : void 0;
|
|
2261
2266
|
let affectedNode = sourceId;
|
|
@@ -2312,13 +2317,19 @@ async function handleSpan(ctx, span) {
|
|
|
2312
2317
|
const parent = lookupParentSpan(span.traceId, span.parentSpanId, nowMs);
|
|
2313
2318
|
if (parent && parent.service !== span.service) {
|
|
2314
2319
|
const parentId = ensureServiceNode(ctx.graph, parent.service, parent.env);
|
|
2320
|
+
const fallbackSource = parent.callSite ? ensureObservedFileNode(ctx.graph, parent.service, parentId, parent.callSite) : parentId;
|
|
2321
|
+
const fallbackEvidence = parent.callSite ? {
|
|
2322
|
+
file: parent.callSite.relPath,
|
|
2323
|
+
...parent.callSite.line !== void 0 ? { line: parent.callSite.line } : {}
|
|
2324
|
+
} : void 0;
|
|
2315
2325
|
upsertObservedEdge(
|
|
2316
2326
|
ctx.graph,
|
|
2317
2327
|
import_types3.EdgeType.CALLS,
|
|
2318
|
-
|
|
2328
|
+
fallbackSource,
|
|
2319
2329
|
sourceId,
|
|
2320
2330
|
ts,
|
|
2321
|
-
isError
|
|
2331
|
+
isError,
|
|
2332
|
+
fallbackEvidence
|
|
2322
2333
|
);
|
|
2323
2334
|
}
|
|
2324
2335
|
}
|
|
@@ -2920,6 +2931,9 @@ function workspaceGlobs(pkg) {
|
|
|
2920
2931
|
if (Array.isArray(ws.packages)) return ws.packages.length > 0 ? ws.packages : null;
|
|
2921
2932
|
return null;
|
|
2922
2933
|
}
|
|
2934
|
+
async function hasPythonManifest(dir) {
|
|
2935
|
+
return await exists(import_node_path9.default.join(dir, "pyproject.toml")) || await exists(import_node_path9.default.join(dir, "requirements.txt")) || await exists(import_node_path9.default.join(dir, "setup.py"));
|
|
2936
|
+
}
|
|
2923
2937
|
async function loadGitignore(scanPath) {
|
|
2924
2938
|
const gitignorePath = import_node_path9.default.join(scanPath, ".gitignore");
|
|
2925
2939
|
if (!await exists(gitignorePath)) return null;
|
|
@@ -2986,6 +3000,13 @@ function detectJsFramework(pkg) {
|
|
|
2986
3000
|
if (deps["astro"] !== void 0) return "astro";
|
|
2987
3001
|
return void 0;
|
|
2988
3002
|
}
|
|
3003
|
+
async function detectJsServiceLanguage(dir, pkg) {
|
|
3004
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
3005
|
+
if (deps["typescript"] !== void 0) return "typescript";
|
|
3006
|
+
const entries = await import_node_fs9.promises.readdir(dir).catch(() => []);
|
|
3007
|
+
if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
|
|
3008
|
+
return "javascript";
|
|
3009
|
+
}
|
|
2989
3010
|
async function discoverNodeService(scanPath, dir) {
|
|
2990
3011
|
const pkgPath = import_node_path9.default.join(dir, "package.json");
|
|
2991
3012
|
if (!await exists(pkgPath)) return null;
|
|
@@ -2998,11 +3019,12 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
2998
3019
|
}
|
|
2999
3020
|
if (!pkg.name) return null;
|
|
3000
3021
|
const framework = detectJsFramework(pkg);
|
|
3022
|
+
const language = await detectJsServiceLanguage(dir, pkg);
|
|
3001
3023
|
const node = {
|
|
3002
3024
|
id: (0, import_types5.serviceId)(pkg.name),
|
|
3003
3025
|
type: import_types5.NodeType.ServiceNode,
|
|
3004
3026
|
name: pkg.name,
|
|
3005
|
-
language
|
|
3027
|
+
language,
|
|
3006
3028
|
version: pkg.version,
|
|
3007
3029
|
dependencies: pkg.dependencies ?? {},
|
|
3008
3030
|
repoPath: import_node_path9.default.relative(scanPath, dir),
|
|
@@ -3045,7 +3067,11 @@ async function discoverServices(scanPath) {
|
|
|
3045
3067
|
if (wsGlobs) {
|
|
3046
3068
|
candidateDirs.push(...await expandWorkspaceGlobs(scanPath, wsGlobs));
|
|
3047
3069
|
} else {
|
|
3048
|
-
if (rootPkg && rootPkg.name)
|
|
3070
|
+
if (rootPkg && rootPkg.name) {
|
|
3071
|
+
candidateDirs.push(scanPath);
|
|
3072
|
+
} else if (await hasPythonManifest(scanPath)) {
|
|
3073
|
+
candidateDirs.push(scanPath);
|
|
3074
|
+
}
|
|
3049
3075
|
const ig = await loadGitignore(scanPath);
|
|
3050
3076
|
await walkDirs(
|
|
3051
3077
|
scanPath,
|
|
@@ -3054,7 +3080,7 @@ async function discoverServices(scanPath) {
|
|
|
3054
3080
|
async (dir) => {
|
|
3055
3081
|
if (await exists(import_node_path9.default.join(dir, "package.json"))) {
|
|
3056
3082
|
candidateDirs.push(dir);
|
|
3057
|
-
} else if (await
|
|
3083
|
+
} else if (await hasPythonManifest(dir)) {
|
|
3058
3084
|
candidateDirs.push(dir);
|
|
3059
3085
|
}
|
|
3060
3086
|
}
|
|
@@ -8686,6 +8712,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
8686
8712
|
`;
|
|
8687
8713
|
|
|
8688
8714
|
// src/installers/javascript.ts
|
|
8715
|
+
var import_instrumentation_registry2 = require("@neat.is/instrumentation-registry");
|
|
8689
8716
|
var SDK_PACKAGES = [
|
|
8690
8717
|
{ name: "@opentelemetry/api", version: "^1.9.0" },
|
|
8691
8718
|
{ name: "@opentelemetry/sdk-node", version: "^0.57.0" },
|
|
@@ -8810,6 +8837,34 @@ function hasNextDependency(pkg) {
|
|
|
8810
8837
|
function allDeps(pkg) {
|
|
8811
8838
|
return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8812
8839
|
}
|
|
8840
|
+
var WEB_FRAMEWORK_DEPS = [
|
|
8841
|
+
"express",
|
|
8842
|
+
"fastify",
|
|
8843
|
+
"koa",
|
|
8844
|
+
"@hapi/hapi",
|
|
8845
|
+
"hapi",
|
|
8846
|
+
"restify",
|
|
8847
|
+
"connect",
|
|
8848
|
+
"@nestjs/core",
|
|
8849
|
+
"next",
|
|
8850
|
+
"hono",
|
|
8851
|
+
"elysia",
|
|
8852
|
+
"polka",
|
|
8853
|
+
"micro"
|
|
8854
|
+
];
|
|
8855
|
+
function looksLikeWebApp(pkg) {
|
|
8856
|
+
const deps = allDeps(pkg);
|
|
8857
|
+
return WEB_FRAMEWORK_DEPS.some((name) => name in deps);
|
|
8858
|
+
}
|
|
8859
|
+
function uninstrumentedLibraries(pkg) {
|
|
8860
|
+
const deps = allDeps(pkg);
|
|
8861
|
+
const out = [];
|
|
8862
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
8863
|
+
const entry2 = (0, import_instrumentation_registry2.resolve)(name, version);
|
|
8864
|
+
if (entry2 && entry2.coverage === "gap") out.push(name);
|
|
8865
|
+
}
|
|
8866
|
+
return out;
|
|
8867
|
+
}
|
|
8813
8868
|
var REMIX_ENTRY_CANDIDATES = [
|
|
8814
8869
|
"app/entry.server.ts",
|
|
8815
8870
|
"app/entry.server.tsx",
|
|
@@ -8889,6 +8944,9 @@ var SRC_INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `src/index${ext}`);
|
|
|
8889
8944
|
var SRC_NAMED_CANDIDATES = ["server", "main", "app"].flatMap(
|
|
8890
8945
|
(name) => INDEX_EXTENSIONS.map((ext) => `src/${name}${ext}`)
|
|
8891
8946
|
);
|
|
8947
|
+
var ROOT_NAMED_CANDIDATES = ["server", "app", "main"].flatMap(
|
|
8948
|
+
(name) => INDEX_EXTENSIONS.map((ext) => `${name}${ext}`)
|
|
8949
|
+
);
|
|
8892
8950
|
var SCRIPT_LAUNCHERS = /* @__PURE__ */ new Set([
|
|
8893
8951
|
"node",
|
|
8894
8952
|
"ts-node",
|
|
@@ -8963,6 +9021,10 @@ async function resolveEntry(serviceDir, pkg) {
|
|
|
8963
9021
|
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
8964
9022
|
if (await exists3(candidate)) return candidate;
|
|
8965
9023
|
}
|
|
9024
|
+
for (const rel of ROOT_NAMED_CANDIDATES) {
|
|
9025
|
+
const candidate = import_node_path46.default.join(serviceDir, rel);
|
|
9026
|
+
if (await exists3(candidate)) return candidate;
|
|
9027
|
+
}
|
|
8966
9028
|
for (const name of INDEX_CANDIDATES) {
|
|
8967
9029
|
const candidate = import_node_path46.default.join(serviceDir, name);
|
|
8968
9030
|
if (await exists3(candidate)) return candidate;
|
|
@@ -10172,8 +10234,17 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
10172
10234
|
if (!installPlans.has(key)) installPlans.set(key, cmd);
|
|
10173
10235
|
}
|
|
10174
10236
|
} else if (outcome.outcome === "already-instrumented") already++;
|
|
10175
|
-
else if (outcome.outcome === "lib-only")
|
|
10176
|
-
|
|
10237
|
+
else if (outcome.outcome === "lib-only") {
|
|
10238
|
+
libOnly++;
|
|
10239
|
+
if (svc.pkg && looksLikeWebApp(svc.pkg)) {
|
|
10240
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
10241
|
+
console.warn(
|
|
10242
|
+
`neat: runtime layer won't engage for ${svcName}: no entry point found.
|
|
10243
|
+
${svc.dir} depends on a web framework but neat couldn't resolve an entry to instrument.
|
|
10244
|
+
Add a "start" script to package.json, or point neat at the entry file directly.`
|
|
10245
|
+
);
|
|
10246
|
+
}
|
|
10247
|
+
} else if (outcome.outcome === "browser-bundle") {
|
|
10177
10248
|
browserBundle++;
|
|
10178
10249
|
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
10179
10250
|
} else if (outcome.outcome === "react-native") {
|
|
@@ -10232,6 +10303,19 @@ async function applyInstallersOver(services, project, options = {}) {
|
|
|
10232
10303
|
See docs/installer-scope.md \u2192 "Manual setup for out-of-scope runtimes"`
|
|
10233
10304
|
);
|
|
10234
10305
|
}
|
|
10306
|
+
if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
|
|
10307
|
+
const gaps = uninstrumentedLibraries(svc.pkg);
|
|
10308
|
+
if (gaps.length > 0) {
|
|
10309
|
+
const svcName = import_node_path50.default.basename(svc.dir);
|
|
10310
|
+
const list = gaps.join(", ");
|
|
10311
|
+
const verb = gaps.length === 1 ? "this library" : "these libraries";
|
|
10312
|
+
console.warn(
|
|
10313
|
+
`neat: calls to ${list} won't be observed by default in ${svcName}.
|
|
10314
|
+
${verb} aren't in the default instrumentation set, so they produce no OBSERVED edges.
|
|
10315
|
+
Run \`neat list-uninstrumented\` to review them, then \`neat extend\` to capture them.`
|
|
10316
|
+
);
|
|
10317
|
+
}
|
|
10318
|
+
}
|
|
10235
10319
|
}
|
|
10236
10320
|
const packageManagerInstalls = [];
|
|
10237
10321
|
for (const cmd of installPlans.values()) {
|
|
@@ -11256,7 +11340,10 @@ async function checkDaemonHealth(baseUrl) {
|
|
|
11256
11340
|
}
|
|
11257
11341
|
function snapshotForGraph(persisted) {
|
|
11258
11342
|
return {
|
|
11259
|
-
|
|
11343
|
+
// Stamp the live schema version the daemon validates against on the
|
|
11344
|
+
// receiving `/snapshot` merge. Tracking the constant keeps the push
|
|
11345
|
+
// aligned with the current snapshot shape across schema migrations.
|
|
11346
|
+
schemaVersion: SCHEMA_VERSION,
|
|
11260
11347
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11261
11348
|
graph: persisted.graph.export()
|
|
11262
11349
|
};
|