@lensmcp/cluster 1.16.29 → 1.16.30

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.
@@ -1,8 +1,9 @@
1
1
  import type { GatewayRuntime, ServiceCtl, SockPool } from './types';
2
2
  import type { Observability } from './observability';
3
3
  /** Bounded, async, non-blocking walk of the workspace source tree → a signature of the file SET
4
- * (`<count>:<xor-of-path-hashes>`). Skips node_modules/dist/build/dotdirs + `*-devserver` sock dirs, and
5
- * only counts source-extension files. Order-independent (readdir order irrelevant) and null-safe. */
4
+ * (`<count>:<xor-of-path-hashes>`). Skips node_modules/dist/build/dotdirs + `*-devserver` sock dirs + every
5
+ * GITIGNORED path (runtime output is not source — see `SRC_EXCLUDE`), and only counts source-extension
6
+ * files. Order-independent (readdir order irrelevant) and null-safe. */
6
7
  export declare function sourceSetSignature(root: string): Promise<string>;
7
8
  export interface PodRecycleConfig {
8
9
  maxAgeMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AA8C/D;;sGAEsG;AACtG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,oBAAoB,CAAC,EACpG,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,gBAAgB,GACpB,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAMT;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,2EAA2E;IAC3E,YAAY,IAAI,MAAM,CAAC,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,YAAY,CA4XvF"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AAyE/D;;;yEAGyE;AACzE,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,oBAAoB,CAAC,EACpG,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,gBAAgB,GACpB,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAMT;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,2EAA2E;IAC3E,YAAY,IAAI,MAAM,CAAC,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,YAAY,CA4XvF"}
@@ -14,6 +14,7 @@ const node_child_process_1 = require("node:child_process");
14
14
  const fs = tslib_1.__importStar(require("node:fs"));
15
15
  const http = tslib_1.__importStar(require("node:http"));
16
16
  const path = tslib_1.__importStar(require("node:path"));
17
+ const node_util_1 = require("node:util");
17
18
  const discovery_1 = require("./discovery");
18
19
  const pod_env_1 = require("./pod-env");
19
20
  const types_1 = require("./types");
@@ -48,6 +49,14 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
48
49
  // degraded watcher missed it. We detect a set change — NOT a content change — by a cheap order-independent
49
50
  // signature over source file PATHS (count + xor of per-path hashes): adding/removing a path flips the
50
51
  // signature; editing a file's CONTENTS does not, so ordinary saves never trigger a recycle.
52
+ // RUNTIME OUTPUT IS NOT SOURCE. The hardcoded list below can only ever name the conventional output dirs;
53
+ // a workspace whose app WRITES into some other gitignored dir under the root would otherwise read every
54
+ // write as "a dev added a source file" and recycle every pod. Worked failure (foodguard, 2026-07-28): the
55
+ // engine's local artifact store defaults to `./artifacts` at the workspace root, so each run's
56
+ // `publish_pack` added pack-manifest + source-snapshot `.json` → the set flipped → the sweeper SIGTERMed all
57
+ // four vite pods → a ~12-18s `gateway.unavailable` 502 window per run (201 dashboard recycles in one
58
+ // session, and it crossed workspaces: the sweeper's viteChildren span every REGISTERED workspace while the
59
+ // scan only walks the daemon host's root). So the set is filtered by git's OWN ignore verdict too.
51
60
  const SRC_EXCLUDE = new Set(['node_modules', 'dist', 'tmp', 'coverage', 'build', 'out', 'target']);
52
61
  const SRC_EXT = new Set([
53
62
  '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.css', '.scss', '.sass', '.less',
@@ -62,12 +71,32 @@ function fnv1a(s) {
62
71
  }
63
72
  return h >>> 0;
64
73
  }
74
+ const execFileP = (0, node_util_1.promisify)(node_child_process_1.execFile);
75
+ /** The repo-relative paths git IGNORES — directories carry a trailing `/` (git's `--directory` collapses a
76
+ * wholly-ignored dir), files do not. ONE `git ls-files` per scan (~80ms on a 2.6k-file workspace, off the
77
+ * event loop) buys exact gitignore semantics — nested `.gitignore`, `.git/info/exclude`, global excludes —
78
+ * with no new dependency and no hand-rolled glob matcher. A partially-ignored dir isn't collapsed, so its
79
+ * individual ignored files are listed and filtered per-file instead. Fails OPEN to an empty set (git absent,
80
+ * not a repo, timeout) → the hardcoded `SRC_EXCLUDE` list alone still applies, exactly as before. */
81
+ async function gitIgnoredPaths(root) {
82
+ try {
83
+ const { stdout } = await execFileP('git', ['ls-files', '--others', '--directory', '--ignored', '--exclude-standard', '-z'], { cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 10_000, windowsHide: true });
84
+ return new Set(stdout.split('\0').filter(Boolean)); // -z → NUL-separated, never git's quoted-path form
85
+ }
86
+ catch {
87
+ return new Set();
88
+ }
89
+ }
65
90
  /** Bounded, async, non-blocking walk of the workspace source tree → a signature of the file SET
66
- * (`<count>:<xor-of-path-hashes>`). Skips node_modules/dist/build/dotdirs + `*-devserver` sock dirs, and
67
- * only counts source-extension files. Order-independent (readdir order irrelevant) and null-safe. */
91
+ * (`<count>:<xor-of-path-hashes>`). Skips node_modules/dist/build/dotdirs + `*-devserver` sock dirs + every
92
+ * GITIGNORED path (runtime output is not source — see `SRC_EXCLUDE`), and only counts source-extension
93
+ * files. Order-independent (readdir order irrelevant) and null-safe. */
68
94
  async function sourceSetSignature(root) {
69
95
  let count = 0;
70
96
  let xor = 0;
97
+ const ignored = await gitIgnoredPaths(root);
98
+ // git speaks posix-relative; `path.relative` yields `\` on win32 — normalize before every lookup.
99
+ const rel = (abs) => path.relative(root, abs).split(path.sep).join('/');
71
100
  const walk = async (dir) => {
72
101
  if (count > SCAN_FILE_CAP)
73
102
  return;
@@ -82,17 +111,22 @@ async function sourceSetSignature(root) {
82
111
  if (count > SCAN_FILE_CAP)
83
112
  return;
84
113
  const name = e.name;
114
+ const abs = path.join(dir, name);
85
115
  if (e.isDirectory()) {
86
116
  if (name.startsWith('.') || SRC_EXCLUDE.has(name) || name.endsWith('-devserver'))
87
117
  continue;
88
- await walk(path.join(dir, name));
118
+ if (ignored.has(`${rel(abs)}/`))
119
+ continue; // a gitignored output dir (foodguard's `artifacts/`)
120
+ await walk(abs);
89
121
  }
90
122
  else if (e.isFile()) {
91
123
  const dot = name.lastIndexOf('.');
92
124
  if (dot <= 0 || !SRC_EXT.has(name.slice(dot)))
93
125
  continue;
126
+ if (ignored.has(rel(abs)))
127
+ continue; // a gitignored file in a tracked dir (generated reports)
94
128
  count += 1;
95
- xor ^= fnv1a(path.join(dir, name));
129
+ xor ^= fnv1a(abs);
96
130
  }
97
131
  }
98
132
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.16.29",
3
+ "version": "1.16.30",
4
4
  "description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -65,8 +65,8 @@
65
65
  }
66
66
  },
67
67
  "dependencies": {
68
- "@lensmcp/node-instrumentation": "1.16.29",
69
- "@lensmcp/nx-plugin": "1.16.29",
68
+ "@lensmcp/node-instrumentation": "1.16.30",
69
+ "@lensmcp/nx-plugin": "1.16.30",
70
70
  "fork-ts-checker-webpack-plugin": "^9.0.0",
71
71
  "glob": "^11.0.0",
72
72
  "http-proxy": "^1.18.0",