@neat.is/core 0.4.4 → 0.4.5

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.js CHANGED
@@ -41,15 +41,15 @@ import {
41
41
  setStatus,
42
42
  startPersistLoop,
43
43
  startStalenessLoop
44
- } from "./chunk-G3YGPWJL.js";
44
+ } from "./chunk-RBWL4HRB.js";
45
45
  import {
46
46
  startOtelGrpcReceiver
47
- } from "./chunk-YJOA7BBF.js";
47
+ } from "./chunk-3QCRUEQD.js";
48
48
  import {
49
49
  __dirname,
50
50
  __require,
51
51
  buildOtelReceiver
52
- } from "./chunk-ZVNP3ZDH.js";
52
+ } from "./chunk-HVF4S7J3.js";
53
53
 
54
54
  // src/cli.ts
55
55
  import path8 from "path";
@@ -697,22 +697,40 @@ var OTEL_INIT_CJS = `${OTEL_INIT_HEADER}
697
697
  process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
698
698
  process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
699
699
 
700
- require('@opentelemetry/auto-instrumentations-node/register')
700
+ const { NodeSDK } = require('@opentelemetry/sdk-node')
701
+ const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
702
+
703
+ const instrumentations = [getNodeAutoInstrumentations()]
704
+ __INSTRUMENTATION_BLOCK__
705
+ new NodeSDK({ instrumentations }).start()
701
706
  `;
702
707
  var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
703
708
  process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
704
709
  process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
705
710
 
706
- await import('@opentelemetry/auto-instrumentations-node/register')
711
+ import { NodeSDK } from '@opentelemetry/sdk-node'
712
+ import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
713
+
714
+ const instrumentations = [getNodeAutoInstrumentations()]
715
+ __INSTRUMENTATION_BLOCK__
716
+ new NodeSDK({ instrumentations }).start()
707
717
  `;
708
718
  var OTEL_INIT_TS = `${OTEL_INIT_HEADER}
709
719
  process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
710
720
  process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
711
721
 
712
- await import('@opentelemetry/auto-instrumentations-node/register')
722
+ import { NodeSDK } from '@opentelemetry/sdk-node'
723
+ import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
724
+
725
+ const instrumentations = [getNodeAutoInstrumentations()]
726
+ __INSTRUMENTATION_BLOCK__
727
+ new NodeSDK({ instrumentations }).start()
713
728
  `;
714
- function renderNodeOtelInit(template, serviceName, projectName) {
715
- return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName);
729
+ function renderNodeOtelInit(template, serviceName, projectName, registrations = []) {
730
+ const block = registrations.length === 0 ? "" : `
731
+ ${registrations.join("\n")}
732
+ `;
733
+ return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
716
734
  }
717
735
  function renderEnvNeat(serviceName, projectName) {
718
736
  return [
@@ -744,7 +762,9 @@ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projec
744
762
  import { NodeSDK } from '@opentelemetry/sdk-node'
745
763
  import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
746
764
 
747
- new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }).start()
765
+ const instrumentations = [getNodeAutoInstrumentations()]
766
+ __INSTRUMENTATION_BLOCK__
767
+ new NodeSDK({ instrumentations }).start()
748
768
  `;
749
769
  var NEXT_INSTRUMENTATION_NODE_JS = `${NEXT_INSTRUMENTATION_HEADER}
750
770
  process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
@@ -753,10 +773,15 @@ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projec
753
773
  const { NodeSDK } = require('@opentelemetry/sdk-node')
754
774
  const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
755
775
 
756
- new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }).start()
776
+ const instrumentations = [getNodeAutoInstrumentations()]
777
+ __INSTRUMENTATION_BLOCK__
778
+ new NodeSDK({ instrumentations }).start()
757
779
  `;
758
- function renderNextInstrumentationNode(template, serviceName, projectName) {
759
- return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName);
780
+ function renderNextInstrumentationNode(template, serviceName, projectName, registrations = []) {
781
+ const block = registrations.length === 0 ? "" : `
782
+ ${registrations.join("\n")}
783
+ `;
784
+ return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName).replace(/__INSTRUMENTATION_BLOCK__\n?/g, block);
760
785
  }
761
786
  var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074). OpenTelemetry SDK hook.";
762
787
  var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
@@ -852,6 +877,18 @@ var SDK_PACKAGES = [
852
877
  { name: "@opentelemetry/sdk-node", version: "^0.57.0" },
853
878
  { name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
854
879
  ];
880
+ function detectNonBundledInstrumentations(pkg) {
881
+ const deps = allDeps(pkg);
882
+ const out = [];
883
+ if ("@prisma/client" in deps) {
884
+ out.push({
885
+ pkg: "@prisma/instrumentation",
886
+ version: "^5.0.0",
887
+ registration: "instrumentations.push(new (require('@prisma/instrumentation').PrismaInstrumentation)())"
888
+ });
889
+ }
890
+ return out;
891
+ }
855
892
  var OTEL_ENV = {
856
893
  // ADR-069 §4 — endpoint moves into the per-package .env.neat (written
857
894
  // by the apply phase). The envEdits surface stays for the dry-run
@@ -1142,8 +1179,19 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
1142
1179
  version: sdk.version
1143
1180
  });
1144
1181
  }
1182
+ const nonBundled = detectNonBundledInstrumentations(pkg);
1183
+ for (const inst of nonBundled) {
1184
+ if (inst.pkg in existingDeps) continue;
1185
+ dependencyEdits.push({
1186
+ file: manifestPath,
1187
+ kind: "add",
1188
+ name: inst.pkg,
1189
+ version: inst.version
1190
+ });
1191
+ }
1145
1192
  const svcName = serviceNodeName(pkg, serviceDir);
1146
1193
  const projectName = projectToken(pkg, serviceDir, project);
1194
+ const registrations = nonBundled.map((i) => i.registration);
1147
1195
  const generatedFiles = [];
1148
1196
  if (!await exists(instrumentationFile)) {
1149
1197
  generatedFiles.push({
@@ -1158,7 +1206,8 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
1158
1206
  contents: renderNextInstrumentationNode(
1159
1207
  useTs ? NEXT_INSTRUMENTATION_NODE_TS : NEXT_INSTRUMENTATION_NODE_JS,
1160
1208
  svcName,
1161
- projectName
1209
+ projectName,
1210
+ registrations
1162
1211
  ),
1163
1212
  skipIfExists: true
1164
1213
  });
@@ -1508,55 +1557,73 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
1508
1557
  framework: "astro"
1509
1558
  };
1510
1559
  }
1511
- async function plan(serviceDir, opts) {
1512
- const pkg = await readPackageJson(serviceDir);
1513
- const manifestPath = path4.join(serviceDir, "package.json");
1514
- const project = opts?.project;
1515
- const empty = {
1516
- language: "javascript",
1517
- serviceDir,
1518
- dependencyEdits: [],
1519
- entrypointEdits: [],
1520
- envEdits: [],
1521
- generatedFiles: []
1522
- };
1523
- if (!pkg) return empty;
1560
+ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
1524
1561
  if (hasNextDependency(pkg)) {
1525
1562
  const nextConfig = await findNextConfig(serviceDir);
1526
1563
  if (nextConfig) {
1527
- return planNext(serviceDir, pkg, manifestPath, nextConfig, project);
1564
+ return () => planNext(serviceDir, pkg, manifestPath, nextConfig, project);
1528
1565
  }
1529
1566
  }
1530
1567
  if (hasRemixDependency(pkg)) {
1531
1568
  const remixEntry = await findRemixEntry(serviceDir);
1532
1569
  if (remixEntry) {
1533
- return planRemix(serviceDir, pkg, manifestPath, remixEntry, project);
1570
+ return () => planRemix(serviceDir, pkg, manifestPath, remixEntry, project);
1534
1571
  }
1535
1572
  }
1536
1573
  if (hasSvelteKitDependency(pkg)) {
1537
1574
  const hooks = await findSvelteKitHooks(serviceDir);
1538
1575
  const config = await findSvelteKitConfig(serviceDir);
1539
1576
  if (hooks || config) {
1540
- return planSvelteKit(serviceDir, pkg, manifestPath, hooks, project);
1577
+ return () => planSvelteKit(serviceDir, pkg, manifestPath, hooks, project);
1541
1578
  }
1542
1579
  }
1543
1580
  if (hasNuxtDependency(pkg)) {
1544
1581
  const nuxtConfig = await findNuxtConfig(serviceDir);
1545
1582
  if (nuxtConfig) {
1546
- return planNuxt(serviceDir, pkg, manifestPath, project);
1583
+ return () => planNuxt(serviceDir, pkg, manifestPath, project);
1547
1584
  }
1548
1585
  }
1549
1586
  if (hasAstroDependency(pkg)) {
1550
1587
  const astroConfig = await findAstroConfig(serviceDir);
1551
1588
  if (astroConfig) {
1552
- return planAstro(serviceDir, pkg, manifestPath, project);
1589
+ return () => planAstro(serviceDir, pkg, manifestPath, project);
1590
+ }
1591
+ }
1592
+ return null;
1593
+ }
1594
+ async function plan(serviceDir, opts) {
1595
+ const pkg = await readPackageJson(serviceDir);
1596
+ const manifestPath = path4.join(serviceDir, "package.json");
1597
+ const project = opts?.project;
1598
+ const empty = {
1599
+ language: "javascript",
1600
+ serviceDir,
1601
+ dependencyEdits: [],
1602
+ entrypointEdits: [],
1603
+ envEdits: [],
1604
+ generatedFiles: []
1605
+ };
1606
+ if (!pkg) return empty;
1607
+ const frameworkDispatch = await findFrameworkDispatch(
1608
+ serviceDir,
1609
+ pkg,
1610
+ manifestPath,
1611
+ project
1612
+ );
1613
+ let entryFile = null;
1614
+ if (!frameworkDispatch) {
1615
+ entryFile = await resolveEntry(serviceDir, pkg);
1616
+ if (!entryFile) {
1617
+ return { ...empty, libOnly: true };
1553
1618
  }
1554
1619
  }
1620
+ if (frameworkDispatch) {
1621
+ return frameworkDispatch();
1622
+ }
1555
1623
  const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
1556
1624
  if (runtimeKind !== "node") {
1557
1625
  return { ...empty, runtimeKind };
1558
1626
  }
1559
- const entryFile = await resolveEntry(serviceDir, pkg);
1560
1627
  if (!entryFile) {
1561
1628
  return { ...empty, libOnly: true };
1562
1629
  }
@@ -1574,6 +1641,16 @@ async function plan(serviceDir, opts) {
1574
1641
  version: sdk.version
1575
1642
  });
1576
1643
  }
1644
+ const nonBundled = detectNonBundledInstrumentations(pkg);
1645
+ for (const inst of nonBundled) {
1646
+ if (inst.pkg in existingDeps) continue;
1647
+ dependencyEdits.push({
1648
+ file: manifestPath,
1649
+ kind: "add",
1650
+ name: inst.pkg,
1651
+ version: inst.version
1652
+ });
1653
+ }
1577
1654
  const entrypointEdits = [];
1578
1655
  try {
1579
1656
  const raw = await fs4.readFile(entryFile, "utf8");
@@ -1592,11 +1669,17 @@ async function plan(serviceDir, opts) {
1592
1669
  }
1593
1670
  const svcName = serviceNodeName(pkg, serviceDir);
1594
1671
  const projectName = projectToken(pkg, serviceDir, project);
1672
+ const registrations = nonBundled.map((i) => i.registration);
1595
1673
  const generatedFiles = [];
1596
1674
  if (!await exists(otelInitFile)) {
1597
1675
  generatedFiles.push({
1598
1676
  file: otelInitFile,
1599
- contents: renderNodeOtelInit(otelInitContents(flavor), svcName, projectName),
1677
+ contents: renderNodeOtelInit(
1678
+ otelInitContents(flavor),
1679
+ svcName,
1680
+ projectName,
1681
+ registrations
1682
+ ),
1600
1683
  skipIfExists: true
1601
1684
  });
1602
1685
  }
@@ -2128,6 +2211,7 @@ function renderPatch(sections) {
2128
2211
  // src/orchestrator.ts
2129
2212
  import { promises as fs6 } from "fs";
2130
2213
  import http from "http";
2214
+ import net from "net";
2131
2215
  import path6 from "path";
2132
2216
  import { spawn as spawn2 } from "child_process";
2133
2217
  import readline from "readline";
@@ -2294,6 +2378,28 @@ async function waitForDaemonReady(restPort, timeoutMs) {
2294
2378
  stillBootstrapping: projects.filter((p) => p.status === "bootstrapping").map((p) => p.name)
2295
2379
  };
2296
2380
  }
2381
+ var NEAT_PORTS = [8080, 4318, 6328];
2382
+ async function isPortFree(port) {
2383
+ return new Promise((resolve) => {
2384
+ const server = net.createServer();
2385
+ server.once("error", () => resolve(false));
2386
+ server.once("listening", () => server.close(() => resolve(true)));
2387
+ server.listen(port, "127.0.0.1");
2388
+ });
2389
+ }
2390
+ async function probePortsFree() {
2391
+ for (const port of NEAT_PORTS) {
2392
+ if (!await isPortFree(port)) return { free: false, held: port };
2393
+ }
2394
+ return { free: true };
2395
+ }
2396
+ function formatPortCollisionMessage(port) {
2397
+ return [
2398
+ `neat: port ${port} is in use; the NEAT daemon needs it.`,
2399
+ ` run \`neatd stop\` to release the previous daemon, or`,
2400
+ ` \`lsof -i :${port}\` to find the holding process.`
2401
+ ];
2402
+ }
2297
2403
  function spawnDaemonDetached() {
2298
2404
  const here = path6.dirname(new URL(import.meta.url).pathname);
2299
2405
  const candidates = [
@@ -2430,6 +2536,14 @@ async function runOrchestrator(opts) {
2430
2536
  if (await checkDaemonHealth(restPort)) {
2431
2537
  result.steps.daemon = "already-running";
2432
2538
  } else {
2539
+ const probe = await probePortsFree();
2540
+ if (!probe.free) {
2541
+ for (const line of formatPortCollisionMessage(probe.held)) {
2542
+ console.error(line);
2543
+ }
2544
+ result.exitCode = 3;
2545
+ return result;
2546
+ }
2433
2547
  try {
2434
2548
  spawnDaemonDetached();
2435
2549
  } catch (err) {
@@ -3235,7 +3349,8 @@ function usage() {
3235
3349
  console.log(" 0 success");
3236
3350
  console.log(" 1 server error (4xx/5xx body printed to stderr)");
3237
3351
  console.log(" 2 misuse (missing args, bad flags) \u2014 handled before any network call");
3238
- console.log(" 3 daemon not reachable (connection refused / timeout)");
3352
+ console.log(" 3 environmental \u2014 daemon unreachable, or one of ports 8080 / 4318 / 6328");
3353
+ console.log(" is held by another process when `neat <path>` tries to spawn the daemon");
3239
3354
  console.log("");
3240
3355
  console.log("environment:");
3241
3356
  console.log(" NEAT_API_URL base URL for the core REST API (default http://localhost:8080)");