@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/cli.js CHANGED
@@ -2,9 +2,6 @@
2
2
  import {
3
3
  buildSearchIndex
4
4
  } from "./chunk-XOOCA5T7.js";
5
- import {
6
- buildApi
7
- } from "./chunk-NVCEZXL7.js";
8
5
  import {
9
6
  DEFAULT_PROJECT,
10
7
  PolicyViolationsLog,
@@ -18,6 +15,7 @@ import {
18
15
  addServiceAliases,
19
16
  addServiceNodes,
20
17
  attachGraphToEventBus,
18
+ buildApi,
21
19
  discoverServices,
22
20
  emitNeatEvent,
23
21
  ensureCompatLoaded,
@@ -37,18 +35,20 @@ import {
37
35
  setStatus,
38
36
  startPersistLoop,
39
37
  startStalenessLoop
40
- } from "./chunk-B7UUGIXB.js";
38
+ } from "./chunk-FIXKIYNF.js";
41
39
  import {
42
- buildOtelReceiver,
43
40
  startOtelGrpcReceiver
44
- } from "./chunk-QYUB3FQL.js";
45
- import "./chunk-DGUM43GV.js";
41
+ } from "./chunk-G3PDTGOW.js";
42
+ import {
43
+ buildOtelReceiver
44
+ } from "./chunk-4ASCXBZF.js";
46
45
 
47
46
  // src/cli.ts
48
47
  import path4 from "path";
49
- import { promises as fs3 } from "fs";
48
+ import { promises as fs4 } from "fs";
50
49
 
51
50
  // src/watch.ts
51
+ import fs from "fs";
52
52
  import path from "path";
53
53
  import chokidar from "chokidar";
54
54
 
@@ -144,6 +144,16 @@ async function runExtractPhases(graph, scanPath, phases, project = DEFAULT_PROJE
144
144
  durationMs: Date.now() - started
145
145
  };
146
146
  }
147
+ var IGNORED_WATCH_GLOBS = [
148
+ "**/node_modules/**",
149
+ "**/.git/**",
150
+ "**/dist/**",
151
+ "**/build/**",
152
+ "**/.turbo/**",
153
+ "**/.next/**",
154
+ "**/neat-out/**",
155
+ "**/.DS_Store"
156
+ ];
147
157
  var IGNORED_WATCH_PATHS = [
148
158
  /(?:^|[\\/])node_modules[\\/]/,
149
159
  /(?:^|[\\/])\.git[\\/]/,
@@ -157,6 +167,35 @@ var IGNORED_WATCH_PATHS = [
157
167
  function shouldIgnore(absPath) {
158
168
  return IGNORED_WATCH_PATHS.some((re) => re.test(absPath));
159
169
  }
170
+ var DARWIN_POLLING_DIR_THRESHOLD = 400;
171
+ function countWatchableDirs(scanPath, limit) {
172
+ let count = 0;
173
+ const visit = (dir, depth) => {
174
+ if (count >= limit) return;
175
+ let entries;
176
+ try {
177
+ entries = fs.readdirSync(dir, { withFileTypes: true });
178
+ } catch {
179
+ return;
180
+ }
181
+ for (const e of entries) {
182
+ if (count >= limit) return;
183
+ if (!e.isDirectory()) continue;
184
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(path.join(dir, e.name) + path.sep))) continue;
185
+ count++;
186
+ if (depth < 2) visit(path.join(dir, e.name), depth + 1);
187
+ }
188
+ };
189
+ visit(scanPath, 0);
190
+ return count;
191
+ }
192
+ function shouldUsePolling(scanPath) {
193
+ const env = process.env.NEAT_WATCH_POLLING;
194
+ if (env === "1" || env === "true") return true;
195
+ if (env === "0" || env === "false") return false;
196
+ if (process.platform !== "darwin") return false;
197
+ return countWatchableDirs(scanPath, DARWIN_POLLING_DIR_THRESHOLD) >= DARWIN_POLLING_DIR_THRESHOLD;
198
+ }
160
199
  async function startWatch(graph, opts) {
161
200
  const debounceMs = opts.debounceMs ?? 1e3;
162
201
  const projectName = opts.project ?? DEFAULT_PROJECT;
@@ -326,10 +365,19 @@ async function startWatch(graph, opts) {
326
365
  }
327
366
  schedule();
328
367
  };
368
+ const usePolling = shouldUsePolling(opts.scanPath);
369
+ if (usePolling) {
370
+ const reason = process.env.NEAT_WATCH_POLLING === "1" || process.env.NEAT_WATCH_POLLING === "true" ? "NEAT_WATCH_POLLING env override" : "darwin heuristic \u2014 large scan root, kqueue cap risk";
371
+ console.log(`[${projectName}] watch: usePolling=true (${reason})`);
372
+ }
329
373
  const watcher = chokidar.watch(opts.scanPath, {
330
374
  ignoreInitial: true,
331
- ignored: (p) => shouldIgnore(p),
375
+ // Glob array prunes at descent time (#233) so chokidar never opens a
376
+ // kqueue handle for `node_modules` and friends. The function backstop
377
+ // catches any path that slipped through and matches the regex set.
378
+ ignored: [...IGNORED_WATCH_GLOBS, (p) => shouldIgnore(p)],
332
379
  persistent: true,
380
+ usePolling,
333
381
  awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
334
382
  });
335
383
  watcher.on("add", onPath);
@@ -361,7 +409,7 @@ async function startWatch(graph, opts) {
361
409
  }
362
410
 
363
411
  // src/installers/javascript.ts
364
- import { promises as fs } from "fs";
412
+ import { promises as fs2 } from "fs";
365
413
  import path2 from "path";
366
414
  var SDK_PACKAGES = [
367
415
  { name: "@opentelemetry/api", version: "^1.9.0" },
@@ -378,7 +426,7 @@ var OTEL_ENV = {
378
426
  };
379
427
  async function readPackageJson(serviceDir) {
380
428
  try {
381
- const raw = await fs.readFile(path2.join(serviceDir, "package.json"), "utf8");
429
+ const raw = await fs2.readFile(path2.join(serviceDir, "package.json"), "utf8");
382
430
  return JSON.parse(raw);
383
431
  } catch {
384
432
  return null;
@@ -444,7 +492,7 @@ async function apply(installPlan) {
444
492
  const originals = /* @__PURE__ */ new Map();
445
493
  for (const file of touched) {
446
494
  try {
447
- originals.set(file, await fs.readFile(file, "utf8"));
495
+ originals.set(file, await fs2.readFile(file, "utf8"));
448
496
  } catch {
449
497
  }
450
498
  }
@@ -470,8 +518,8 @@ async function apply(installPlan) {
470
518
  }
471
519
  const newRaw = JSON.stringify(pkg, null, 2) + "\n";
472
520
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
473
- await fs.writeFile(tmp, newRaw, "utf8");
474
- await fs.rename(tmp, file);
521
+ await fs2.writeFile(tmp, newRaw, "utf8");
522
+ await fs2.rename(tmp, file);
475
523
  }
476
524
  } catch (err) {
477
525
  await rollback(installPlan, originals);
@@ -482,7 +530,7 @@ async function rollback(installPlan, originals) {
482
530
  const restored = [];
483
531
  for (const [file, raw] of originals.entries()) {
484
532
  try {
485
- await fs.writeFile(file, raw, "utf8");
533
+ await fs2.writeFile(file, raw, "utf8");
486
534
  restored.push(file);
487
535
  } catch {
488
536
  }
@@ -497,7 +545,7 @@ async function rollback(installPlan, originals) {
497
545
  ""
498
546
  ];
499
547
  const rollbackPath = path2.join(installPlan.serviceDir, "neat-rollback.patch");
500
- await fs.writeFile(rollbackPath, lines.join("\n"), "utf8");
548
+ await fs2.writeFile(rollbackPath, lines.join("\n"), "utf8");
501
549
  }
502
550
  var javascriptInstaller = {
503
551
  name: "javascript",
@@ -507,7 +555,7 @@ var javascriptInstaller = {
507
555
  };
508
556
 
509
557
  // src/installers/python.ts
510
- import { promises as fs2 } from "fs";
558
+ import { promises as fs3 } from "fs";
511
559
  import path3 from "path";
512
560
  var SDK_PACKAGES2 = [
513
561
  { name: "opentelemetry-distro", version: ">=0.49b0" },
@@ -520,7 +568,7 @@ var OTEL_ENV2 = {
520
568
  };
521
569
  async function exists(p) {
522
570
  try {
523
- await fs2.stat(p);
571
+ await fs3.stat(p);
524
572
  return true;
525
573
  } catch {
526
574
  return false;
@@ -541,7 +589,7 @@ function reqPackageName(line) {
541
589
  async function planRequirementsTxtEdits(serviceDir) {
542
590
  const file = path3.join(serviceDir, "requirements.txt");
543
591
  if (!await exists(file)) return null;
544
- const raw = await fs2.readFile(file, "utf8");
592
+ const raw = await fs3.readFile(file, "utf8");
545
593
  const presentNames = new Set(
546
594
  raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
547
595
  );
@@ -551,7 +599,7 @@ async function planRequirementsTxtEdits(serviceDir) {
551
599
  async function planProcfileEdits(serviceDir) {
552
600
  const procfile = path3.join(serviceDir, "Procfile");
553
601
  if (!await exists(procfile)) return [];
554
- const raw = await fs2.readFile(procfile, "utf8");
602
+ const raw = await fs3.readFile(procfile, "utf8");
555
603
  const edits = [];
556
604
  for (const line of raw.split(/\r?\n/)) {
557
605
  if (line.length === 0) continue;
@@ -603,8 +651,8 @@ async function applyRequirementsTxt(manifest, edits, original) {
603
651
  const next = `${original}${trailing}${newlines.join("\n")}
604
652
  `;
605
653
  const tmp = `${manifest}.${process.pid}.${Date.now()}.tmp`;
606
- await fs2.writeFile(tmp, next, "utf8");
607
- await fs2.rename(tmp, manifest);
654
+ await fs3.writeFile(tmp, next, "utf8");
655
+ await fs3.rename(tmp, manifest);
608
656
  }
609
657
  async function applyProcfile(procfile, edits, original) {
610
658
  let next = original;
@@ -613,8 +661,8 @@ async function applyProcfile(procfile, edits, original) {
613
661
  next = next.replace(e.before, e.after);
614
662
  }
615
663
  const tmp = `${procfile}.${process.pid}.${Date.now()}.tmp`;
616
- await fs2.writeFile(tmp, next, "utf8");
617
- await fs2.rename(tmp, procfile);
664
+ await fs3.writeFile(tmp, next, "utf8");
665
+ await fs3.rename(tmp, procfile);
618
666
  }
619
667
  async function apply2(installPlan) {
620
668
  const touched = /* @__PURE__ */ new Set();
@@ -624,7 +672,7 @@ async function apply2(installPlan) {
624
672
  const originals = /* @__PURE__ */ new Map();
625
673
  for (const file of touched) {
626
674
  try {
627
- originals.set(file, await fs2.readFile(file, "utf8"));
675
+ originals.set(file, await fs3.readFile(file, "utf8"));
628
676
  } catch {
629
677
  }
630
678
  }
@@ -652,7 +700,7 @@ async function rollback2(installPlan, originals) {
652
700
  const restored = [];
653
701
  for (const [file, raw] of originals.entries()) {
654
702
  try {
655
- await fs2.writeFile(file, raw, "utf8");
703
+ await fs3.writeFile(file, raw, "utf8");
656
704
  restored.push(file);
657
705
  } catch {
658
706
  }
@@ -667,7 +715,7 @@ async function rollback2(installPlan, originals) {
667
715
  ""
668
716
  ];
669
717
  const rollbackPath = path3.join(installPlan.serviceDir, "neat-rollback.patch");
670
- await fs2.writeFile(rollbackPath, lines.join("\n"), "utf8");
718
+ await fs3.writeFile(rollbackPath, lines.join("\n"), "utf8");
671
719
  }
672
720
  var pythonInstaller = {
673
721
  name: "python",
@@ -1501,7 +1549,7 @@ async function buildPatchSections(services) {
1501
1549
  }
1502
1550
  async function runInit(opts) {
1503
1551
  const written = [];
1504
- const stat = await fs3.stat(opts.scanPath).catch(() => null);
1552
+ const stat = await fs4.stat(opts.scanPath).catch(() => null);
1505
1553
  if (!stat || !stat.isDirectory()) {
1506
1554
  console.error(`neat init: ${opts.scanPath} is not a directory`);
1507
1555
  return { exitCode: 2, writtenFiles: written };
@@ -1512,7 +1560,7 @@ async function runInit(opts) {
1512
1560
  const patch = renderPatch(sections);
1513
1561
  const patchPath = path4.join(opts.scanPath, "neat.patch");
1514
1562
  if (opts.dryRun) {
1515
- await fs3.writeFile(patchPath, patch, "utf8");
1563
+ await fs4.writeFile(patchPath, patch, "utf8");
1516
1564
  written.push(patchPath);
1517
1565
  console.log(`dry-run: patch written to ${patchPath}`);
1518
1566
  console.log("rerun without --dry-run to register and snapshot.");
@@ -1552,7 +1600,7 @@ async function runInit(opts) {
1552
1600
  console.log("patch applied. Run `npm install` (or your language equivalent) to refresh lockfiles.");
1553
1601
  }
1554
1602
  } else {
1555
- await fs3.writeFile(patchPath, patch, "utf8");
1603
+ await fs4.writeFile(patchPath, patch, "utf8");
1556
1604
  written.push(patchPath);
1557
1605
  }
1558
1606
  }
@@ -1606,7 +1654,7 @@ async function runSkill(opts) {
1606
1654
  const target = claudeConfigPath();
1607
1655
  let existing = {};
1608
1656
  try {
1609
- existing = JSON.parse(await fs3.readFile(target, "utf8"));
1657
+ existing = JSON.parse(await fs4.readFile(target, "utf8"));
1610
1658
  } catch (err) {
1611
1659
  if (err.code !== "ENOENT") {
1612
1660
  console.error(`neat skill: failed to read ${target} \u2014 ${err.message}`);
@@ -1618,8 +1666,8 @@ async function runSkill(opts) {
1618
1666
  ...existing,
1619
1667
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
1620
1668
  };
1621
- await fs3.mkdir(path4.dirname(target), { recursive: true });
1622
- await fs3.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
1669
+ await fs4.mkdir(path4.dirname(target), { recursive: true });
1670
+ await fs4.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
1623
1671
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
1624
1672
  console.log("restart Claude Code to pick up the new MCP server.");
1625
1673
  return { exitCode: 0 };
@@ -1679,7 +1727,7 @@ async function main() {
1679
1727
  process.exit(2);
1680
1728
  }
1681
1729
  const scanPath = path4.resolve(target);
1682
- const stat = await fs3.stat(scanPath).catch(() => null);
1730
+ const stat = await fs4.stat(scanPath).catch(() => null);
1683
1731
  if (!stat || !stat.isDirectory()) {
1684
1732
  console.error(`neat watch: ${scanPath} is not a directory`);
1685
1733
  process.exit(2);