@neat.is/core 0.4.20-dev.20260621 → 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/cli.cjs CHANGED
@@ -2931,6 +2931,9 @@ function workspaceGlobs(pkg) {
2931
2931
  if (Array.isArray(ws.packages)) return ws.packages.length > 0 ? ws.packages : null;
2932
2932
  return null;
2933
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
+ }
2934
2937
  async function loadGitignore(scanPath) {
2935
2938
  const gitignorePath = import_node_path9.default.join(scanPath, ".gitignore");
2936
2939
  if (!await exists(gitignorePath)) return null;
@@ -2997,6 +3000,13 @@ function detectJsFramework(pkg) {
2997
3000
  if (deps["astro"] !== void 0) return "astro";
2998
3001
  return void 0;
2999
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
+ }
3000
3010
  async function discoverNodeService(scanPath, dir) {
3001
3011
  const pkgPath = import_node_path9.default.join(dir, "package.json");
3002
3012
  if (!await exists(pkgPath)) return null;
@@ -3009,11 +3019,12 @@ async function discoverNodeService(scanPath, dir) {
3009
3019
  }
3010
3020
  if (!pkg.name) return null;
3011
3021
  const framework = detectJsFramework(pkg);
3022
+ const language = await detectJsServiceLanguage(dir, pkg);
3012
3023
  const node = {
3013
3024
  id: (0, import_types5.serviceId)(pkg.name),
3014
3025
  type: import_types5.NodeType.ServiceNode,
3015
3026
  name: pkg.name,
3016
- language: "javascript",
3027
+ language,
3017
3028
  version: pkg.version,
3018
3029
  dependencies: pkg.dependencies ?? {},
3019
3030
  repoPath: import_node_path9.default.relative(scanPath, dir),
@@ -3056,7 +3067,11 @@ async function discoverServices(scanPath) {
3056
3067
  if (wsGlobs) {
3057
3068
  candidateDirs.push(...await expandWorkspaceGlobs(scanPath, wsGlobs));
3058
3069
  } else {
3059
- if (rootPkg && rootPkg.name) candidateDirs.push(scanPath);
3070
+ if (rootPkg && rootPkg.name) {
3071
+ candidateDirs.push(scanPath);
3072
+ } else if (await hasPythonManifest(scanPath)) {
3073
+ candidateDirs.push(scanPath);
3074
+ }
3060
3075
  const ig = await loadGitignore(scanPath);
3061
3076
  await walkDirs(
3062
3077
  scanPath,
@@ -3065,7 +3080,7 @@ async function discoverServices(scanPath) {
3065
3080
  async (dir) => {
3066
3081
  if (await exists(import_node_path9.default.join(dir, "package.json"))) {
3067
3082
  candidateDirs.push(dir);
3068
- } else if (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"))) {
3083
+ } else if (await hasPythonManifest(dir)) {
3069
3084
  candidateDirs.push(dir);
3070
3085
  }
3071
3086
  }
@@ -8697,6 +8712,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
8697
8712
  `;
8698
8713
 
8699
8714
  // src/installers/javascript.ts
8715
+ var import_instrumentation_registry2 = require("@neat.is/instrumentation-registry");
8700
8716
  var SDK_PACKAGES = [
8701
8717
  { name: "@opentelemetry/api", version: "^1.9.0" },
8702
8718
  { name: "@opentelemetry/sdk-node", version: "^0.57.0" },
@@ -8821,6 +8837,34 @@ function hasNextDependency(pkg) {
8821
8837
  function allDeps(pkg) {
8822
8838
  return { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
8823
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
+ }
8824
8868
  var REMIX_ENTRY_CANDIDATES = [
8825
8869
  "app/entry.server.ts",
8826
8870
  "app/entry.server.tsx",
@@ -8900,6 +8944,9 @@ var SRC_INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `src/index${ext}`);
8900
8944
  var SRC_NAMED_CANDIDATES = ["server", "main", "app"].flatMap(
8901
8945
  (name) => INDEX_EXTENSIONS.map((ext) => `src/${name}${ext}`)
8902
8946
  );
8947
+ var ROOT_NAMED_CANDIDATES = ["server", "app", "main"].flatMap(
8948
+ (name) => INDEX_EXTENSIONS.map((ext) => `${name}${ext}`)
8949
+ );
8903
8950
  var SCRIPT_LAUNCHERS = /* @__PURE__ */ new Set([
8904
8951
  "node",
8905
8952
  "ts-node",
@@ -8974,6 +9021,10 @@ async function resolveEntry(serviceDir, pkg) {
8974
9021
  const candidate = import_node_path46.default.join(serviceDir, rel);
8975
9022
  if (await exists3(candidate)) return candidate;
8976
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
+ }
8977
9028
  for (const name of INDEX_CANDIDATES) {
8978
9029
  const candidate = import_node_path46.default.join(serviceDir, name);
8979
9030
  if (await exists3(candidate)) return candidate;
@@ -10183,8 +10234,17 @@ async function applyInstallersOver(services, project, options = {}) {
10183
10234
  if (!installPlans.has(key)) installPlans.set(key, cmd);
10184
10235
  }
10185
10236
  } else if (outcome.outcome === "already-instrumented") already++;
10186
- else if (outcome.outcome === "lib-only") libOnly++;
10187
- else if (outcome.outcome === "browser-bundle") {
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") {
10188
10248
  browserBundle++;
10189
10249
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
10190
10250
  } else if (outcome.outcome === "react-native") {
@@ -10243,6 +10303,19 @@ async function applyInstallersOver(services, project, options = {}) {
10243
10303
  See docs/installer-scope.md \u2192 "Manual setup for out-of-scope runtimes"`
10244
10304
  );
10245
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
+ }
10246
10319
  }
10247
10320
  const packageManagerInstalls = [];
10248
10321
  for (const cmd of installPlans.values()) {