nx 23.1.0 → 23.1.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.
Files changed (223) hide show
  1. package/dist/bin/nx.js +2 -1
  2. package/dist/release/changelog-renderer/index.js +4 -5
  3. package/dist/release/index.d.ts +1 -1
  4. package/dist/src/ai/configure-ai-agents-disclaimer.d.ts +5 -0
  5. package/dist/src/ai/configure-ai-agents-disclaimer.js +31 -0
  6. package/dist/src/analytics/analytics.d.ts +6 -0
  7. package/dist/src/analytics/analytics.js +16 -8
  8. package/dist/src/analytics/index.d.ts +1 -1
  9. package/dist/src/analytics/index.js +2 -1
  10. package/dist/src/command-line/add/add.js +13 -1
  11. package/dist/src/command-line/affected/affected.js +1 -0
  12. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
  13. package/dist/src/command-line/graph/graph.d.ts +1 -0
  14. package/dist/src/command-line/graph/graph.js +15 -6
  15. package/dist/src/command-line/import/import.js +2 -3
  16. package/dist/src/command-line/init/ai-agent-prompts.d.ts +1 -1
  17. package/dist/src/command-line/init/ai-agent-prompts.js +7 -6
  18. package/dist/src/command-line/init/command-object.js +10 -2
  19. package/dist/src/command-line/init/configure-plugins.js +3 -2
  20. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +4 -2
  21. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +4 -3
  22. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +4 -3
  23. package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +4 -3
  24. package/dist/src/command-line/init/implementation/angular/index.js +4 -2
  25. package/dist/src/command-line/init/implementation/angular/integrated-workspace.js +2 -5
  26. package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +3 -0
  27. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +4 -1
  28. package/dist/src/command-line/init/implementation/utils.d.ts +3 -3
  29. package/dist/src/command-line/init/implementation/utils.js +22 -3
  30. package/dist/src/command-line/init/init-v2.js +4 -5
  31. package/dist/src/command-line/migrate/execute-migration.d.ts +72 -0
  32. package/dist/src/command-line/migrate/execute-migration.js +389 -0
  33. package/dist/src/command-line/migrate/migrate-ui-api.js +17 -12
  34. package/dist/src/command-line/migrate/migrate.d.ts +5 -45
  35. package/dist/src/command-line/migrate/migrate.js +122 -402
  36. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +1 -1
  37. package/dist/src/command-line/nx-cloud/connect/view-logs.js +2 -0
  38. package/dist/src/command-line/release/changelog.js +5 -0
  39. package/dist/src/command-line/release/index.d.ts +1 -1
  40. package/dist/src/command-line/release/utils/git.js +7 -3
  41. package/dist/src/command-line/release/utils/release-graph.d.ts +19 -2
  42. package/dist/src/command-line/release/utils/release-graph.js +71 -19
  43. package/dist/src/command-line/release/utils/remote-release-clients/github.js +7 -1
  44. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +1 -1
  45. package/dist/src/command-line/release/utils/shared.d.ts +2 -2
  46. package/dist/src/command-line/release/version/release-group-processor.d.ts +24 -0
  47. package/dist/src/command-line/release/version/release-group-processor.js +80 -6
  48. package/dist/src/command-line/release/version/version-actions.d.ts +27 -2
  49. package/dist/src/command-line/release/version/version-actions.js +9 -1
  50. package/dist/src/command-line/release/version.js +9 -5
  51. package/dist/src/command-line/reset/reset.js +6 -1
  52. package/dist/src/command-line/run/run-one.js +1 -0
  53. package/dist/src/command-line/run-many/run-many.js +1 -0
  54. package/dist/src/command-line/show/show-target/info.js +11 -2
  55. package/dist/src/command-line/show/show-target/inputs.js +50 -153
  56. package/dist/src/command-line/show/show-target/outputs.js +48 -173
  57. package/dist/src/command-line/show/show-target/utils.d.ts +28 -0
  58. package/dist/src/command-line/show/show-target/utils.js +83 -3
  59. package/dist/src/command-line/watch/watch.js +13 -0
  60. package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  61. package/dist/src/commands-runner/create-command-graph.js +5 -0
  62. package/dist/src/config/nx-json.d.ts +5 -3
  63. package/dist/src/core/graph/main.js +1 -1
  64. package/dist/src/core/graph/styles.js +1 -1
  65. package/dist/src/daemon/client/client.d.ts +52 -3
  66. package/dist/src/daemon/client/client.js +177 -55
  67. package/dist/src/daemon/client/daemon-environment.d.ts +17 -0
  68. package/dist/src/daemon/client/daemon-environment.js +110 -2
  69. package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
  70. package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
  71. package/dist/src/daemon/message-types/daemon-message.js +25 -0
  72. package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +5 -0
  73. package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +16 -0
  74. package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +1 -1
  75. package/dist/src/daemon/server/latest-nx.js +5 -2
  76. package/dist/src/daemon/server/server.js +31 -4
  77. package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
  78. package/dist/src/daemon/server/shutdown-utils.js +11 -4
  79. package/dist/src/daemon/socket-utils.d.ts +1 -0
  80. package/dist/src/daemon/socket-utils.js +24 -3
  81. package/dist/src/daemon/tmp-dir.d.ts +43 -3
  82. package/dist/src/daemon/tmp-dir.js +437 -15
  83. package/dist/src/devkit-internals.d.ts +96 -3
  84. package/dist/src/devkit-internals.js +200 -4
  85. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
  86. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
  87. package/dist/src/generators/tree.d.ts +4 -2
  88. package/dist/src/generators/utils/nx-json.js +13 -3
  89. package/dist/src/hasher/check-task-files.d.ts +86 -0
  90. package/dist/src/hasher/check-task-files.js +378 -0
  91. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
  92. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
  93. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
  94. package/dist/src/native/index.d.ts +31 -2
  95. package/dist/src/native/index.js +71 -28
  96. package/dist/src/native/native-bindings.js +3 -0
  97. package/dist/src/native/native-file-cache-location.d.ts +17 -1
  98. package/dist/src/native/native-file-cache-location.js +86 -12
  99. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  100. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  101. package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
  102. package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
  103. package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
  104. package/dist/src/plugins/js/lock-file/npm-parser.js +71 -27
  105. package/dist/src/plugins/js/lock-file/pnpm-parser.js +110 -17
  106. package/dist/src/plugins/js/lock-file/project-graph-pruning.js +15 -9
  107. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +3 -0
  108. package/dist/src/plugins/js/lock-file/yarn-parser.js +32 -5
  109. package/dist/src/plugins/js/project-graph/affected/npm-packages.js +51 -20
  110. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +14 -4
  111. package/dist/src/plugins/js/utils/register.d.ts +18 -0
  112. package/dist/src/plugins/js/utils/register.js +81 -0
  113. package/dist/src/plugins/js/utils/typescript.d.ts +9 -0
  114. package/dist/src/plugins/js/utils/typescript.js +15 -0
  115. package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
  116. package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
  117. package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
  118. package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
  119. package/dist/src/project-graph/build-project-graph.js +7 -1
  120. package/dist/src/project-graph/error-types.d.ts +12 -1
  121. package/dist/src/project-graph/error-types.js +68 -3
  122. package/dist/src/project-graph/file-utils.js +6 -1
  123. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
  124. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +22 -1
  125. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
  126. package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
  127. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
  128. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +1 -0
  129. package/dist/src/project-graph/plugins/transpiler.js +4 -0
  130. package/dist/src/project-graph/project-graph.js +23 -0
  131. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.d.ts +10 -12
  132. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.js +93 -63
  133. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +5 -8
  134. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +16 -15
  135. package/dist/src/project-graph/utils/project-configuration/source-maps.d.ts +12 -5
  136. package/dist/src/project-graph/utils/project-configuration/source-maps.js +21 -7
  137. package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +1 -1
  138. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +26 -27
  139. package/dist/src/project-graph/utils/project-configuration/target-merging.js +50 -8
  140. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +149 -6
  141. package/dist/src/project-graph/utils/project-configuration/utils.d.ts +15 -0
  142. package/dist/src/project-graph/utils/project-configuration/utils.js +42 -6
  143. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +15 -12
  144. package/dist/src/project-graph/utils/project-configuration-utils.js +79 -101
  145. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +11 -4
  146. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +15 -6
  147. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +5 -4
  148. package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +18 -8
  149. package/dist/src/tasks-runner/life-cycles/performance-report.js +66 -29
  150. package/dist/src/tasks-runner/run-command.js +4 -2
  151. package/dist/src/tasks-runner/task-env.d.ts +9 -0
  152. package/dist/src/tasks-runner/task-env.js +31 -4
  153. package/dist/src/tasks-runner/task-orchestrator.d.ts +4 -1
  154. package/dist/src/tasks-runner/task-orchestrator.js +23 -10
  155. package/dist/src/tasks-runner/utils.js +2 -6
  156. package/dist/src/utils/acknowledge-build-scripts.d.ts +13 -0
  157. package/dist/src/utils/acknowledge-build-scripts.js +102 -0
  158. package/dist/src/utils/analytics-prompt.d.ts +0 -6
  159. package/dist/src/utils/analytics-prompt.js +0 -51
  160. package/dist/src/utils/assert-workspace-validity.js +7 -2
  161. package/dist/src/utils/catalog/manager-utils.d.ts +1 -2
  162. package/dist/src/utils/catalog/manager-utils.js +22 -5
  163. package/dist/src/utils/catalog/pnpm-manager.d.ts +1 -0
  164. package/dist/src/utils/catalog/pnpm-manager.js +3 -15
  165. package/dist/src/utils/catalog/yarn-manager.d.ts +1 -0
  166. package/dist/src/utils/catalog/yarn-manager.js +3 -15
  167. package/dist/src/utils/command-line-utils.js +30 -20
  168. package/dist/src/utils/fileutils.d.ts +5 -0
  169. package/dist/src/utils/fileutils.js +7 -2
  170. package/dist/src/utils/git-revision.d.ts +12 -0
  171. package/dist/src/utils/git-revision.js +25 -0
  172. package/dist/src/utils/git-utils.d.ts +16 -3
  173. package/dist/src/utils/git-utils.js +146 -52
  174. package/dist/src/utils/git-worktrees.d.ts +30 -0
  175. package/dist/src/utils/git-worktrees.js +212 -0
  176. package/dist/src/utils/ignore.d.ts +128 -1
  177. package/dist/src/utils/ignore.js +182 -12
  178. package/dist/src/utils/long-running-target.d.ts +10 -0
  179. package/dist/src/utils/long-running-target.js +19 -0
  180. package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
  181. package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
  182. package/dist/src/utils/min-release-age/behavior/pnpm.js +9 -7
  183. package/dist/src/utils/min-release-age/packument.js +1 -1
  184. package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
  185. package/dist/src/utils/nx-tmp-dir.js +40 -0
  186. package/dist/src/utils/owned-private-dir.d.ts +132 -0
  187. package/dist/src/utils/owned-private-dir.js +323 -0
  188. package/dist/src/utils/package-json.js +11 -7
  189. package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
  190. package/dist/src/utils/package-manager-config/bunfig.js +49 -0
  191. package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
  192. package/dist/src/utils/package-manager-config/npmrc.js +124 -0
  193. package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
  194. package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
  195. package/dist/src/utils/package-manager.d.ts +23 -2
  196. package/dist/src/utils/package-manager.js +264 -43
  197. package/dist/src/utils/provenance.js +18 -16
  198. package/dist/src/utils/registry-config/bun.d.ts +2 -0
  199. package/dist/src/utils/registry-config/bun.js +257 -0
  200. package/dist/src/utils/registry-config/index.d.ts +13 -0
  201. package/dist/src/utils/registry-config/index.js +102 -0
  202. package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
  203. package/dist/src/utils/registry-config/pnpm.js +1551 -0
  204. package/dist/src/utils/registry-config/utils.d.ts +195 -0
  205. package/dist/src/utils/registry-config/utils.js +496 -0
  206. package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
  207. package/dist/src/utils/registry-config/yarn-berry.js +612 -0
  208. package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
  209. package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
  210. package/dist/src/utils/safe-spawn.d.ts +24 -0
  211. package/dist/src/utils/safe-spawn.js +104 -0
  212. package/dist/src/utils/shell-quoting.d.ts +15 -0
  213. package/dist/src/utils/shell-quoting.js +35 -0
  214. package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
  215. package/dist/src/utils/wait-for-socket-connection.js +14 -8
  216. package/dist/src/utils/workspace-id.d.ts +20 -0
  217. package/dist/src/utils/workspace-id.js +54 -0
  218. package/migrations.json +13 -1
  219. package/package.json +22 -17
  220. package/dist/src/migrations/update-17-3-0/nx-release-path.d.ts +0 -3
  221. package/dist/src/migrations/update-17-3-0/nx-release-path.js +0 -47
  222. package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
  223. package/dist/src/utils/min-release-age/npmrc.js +0 -45
@@ -1 +1 @@
1
- "use strict";(self.webpackChunk_nx_graph_client=self.webpackChunk_nx_graph_client||[]).push([[869],{8027(){}},e=>{var n;n=8027,e(e.s=n)}]);
1
+ "use strict";(self.webpackChunk_nx_graph_client=self.webpackChunk_nx_graph_client||[]).push([[869],{946(){}},e=>{var n;n=946,e(e.s=n)}]);
@@ -8,11 +8,23 @@ import { ConfigurationSourceMaps } from '../../project-graph/utils/project-confi
8
8
  import type { FlushSyncGeneratorChangesResult, SyncGeneratorRunResult } from '../../utils/sync-generators';
9
9
  import { type ConfigureAiAgentsStatusResponse } from '../message-types/configure-ai-agents';
10
10
  import { type NxConsoleStatusResponse, type SetNxConsolePreferenceAndInstallResponse } from '../message-types/nx-console';
11
+ /** A refused connect: the errno, and the path it was made against. */
12
+ type ConnectRefusal = {
13
+ error: NodeJS.ErrnoException;
14
+ socketPath: string;
15
+ };
11
16
  export type UnregisterCallback = () => void;
12
17
  export type ChangedFile = {
13
18
  path: string;
14
19
  type: 'create' | 'update' | 'delete';
15
20
  };
21
+ /**
22
+ * The daemon's workspace watcher died. Nothing it serves will see file changes
23
+ * again, so a watching client has to restart rather than keep waiting.
24
+ */
25
+ export declare class WatcherFailedError extends Error {
26
+ constructor(message: string);
27
+ }
16
28
  export declare class DaemonClient {
17
29
  private readonly nxJson;
18
30
  constructor();
@@ -26,8 +38,6 @@ export declare class DaemonClient {
26
38
  private _daemonStatus;
27
39
  private _waitForDaemonReady;
28
40
  private _daemonReady;
29
- private _out;
30
- private _err;
31
41
  private fileWatcherMessenger;
32
42
  private fileWatcherReconnecting;
33
43
  private fileWatcherCallbacks;
@@ -95,6 +105,12 @@ export declare class DaemonClient {
95
105
  resetConfigureAiAgentsStatus(): Promise<{
96
106
  success: boolean;
97
107
  }>;
108
+ /**
109
+ * The pre-start probe, returning why it failed rather than leaving it on the
110
+ * instance: `_daemonStatus` does not serialize `isServerAvailable`'s five
111
+ * public callers against `startDaemonIfNecessary`.
112
+ */
113
+ private probeServer;
98
114
  isServerAvailable(): Promise<boolean>;
99
115
  private startDaemonIfNecessary;
100
116
  private sendToDaemonViaQueue;
@@ -110,8 +126,41 @@ export declare class DaemonClient {
110
126
  private sendMessageToDaemon;
111
127
  private registerDaemonProcessWithMetricsService;
112
128
  private handleMessage;
113
- startInBackground(): Promise<ChildProcess['pid']>;
129
+ /**
130
+ * @param probeRefusal what the caller's pre-start probe saw. The only evidence
131
+ * when a daemon refuses us and then exits — the poll cannot reproduce it
132
+ * once the process json is gone. `nx daemon --start` passes nothing.
133
+ */
134
+ startInBackground(probeRefusal?: ConnectRefusal): Promise<ChildProcess['pid']>;
114
135
  stop(): Promise<void>;
115
136
  }
116
137
  export declare const daemonClient: DaemonClient;
117
138
  export declare function isDaemonEnabled(): boolean;
139
+ /**
140
+ * EACCES and EPERM are the two errnos that mean the OS refused us rather than
141
+ * that nothing was listening. They need opposite remedies — a socket owned by
142
+ * someone else versus a sandbox refusing the connect syscall — but they share
143
+ * the property that retrying cannot change the answer.
144
+ */
145
+ export declare function isPermissionErrno(error: NodeJS.ErrnoException): boolean;
146
+ /**
147
+ * The operating system refused the connection. Most often the socket belongs to
148
+ * another user, which is the guarantee the owner-only socket directory buys —
149
+ * but a sandbox that denies unix-socket connects produces the same errno, so the
150
+ * message does not assert which. Either way it is an environment condition
151
+ * rather than a defect in Nx, and it deliberately does not
152
+ * carry `internalDaemonError`: that tag tells the user to file an issue and
153
+ * disables the daemon until `nx reset`, which would outlast the stale socket
154
+ * that caused it.
155
+ *
156
+ * It also skips the daemon log that `daemonProcessException` appends. The log
157
+ * belongs to *our* daemon; the process holding this socket is someone else's, so
158
+ * quoting it would describe an unrelated run.
159
+ */
160
+ export declare function daemonPermissionException(socketPath: string, cause: string): Error;
161
+ /**
162
+ * Exported for testing: the `internalDaemonError` tag decides whether a daemon
163
+ * failure degrades to a daemonless graph build or aborts the command.
164
+ */
165
+ export declare function daemonProcessException(message: string): Error;
166
+ export {};
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.daemonClient = exports.DaemonClient = void 0;
3
+ exports.daemonClient = exports.DaemonClient = exports.WatcherFailedError = void 0;
4
4
  exports.isDaemonEnabled = isDaemonEnabled;
5
+ exports.isPermissionErrno = isPermissionErrno;
6
+ exports.daemonPermissionException = daemonPermissionException;
7
+ exports.daemonProcessException = daemonProcessException;
5
8
  const child_process_1 = require("child_process");
6
- const promises_1 = require("fs/promises");
7
9
  const net_1 = require("net");
8
10
  const node_fs_1 = require("node:fs");
9
11
  const path_1 = require("path");
@@ -12,6 +14,7 @@ const configuration_1 = require("../../config/configuration");
12
14
  const nx_json_1 = require("../../config/nx-json");
13
15
  const native_1 = require("../../native");
14
16
  const error_types_1 = require("../../project-graph/error-types");
17
+ const typescript_1 = require("../../plugins/js/utils/typescript");
15
18
  const project_graph_1 = require("../../project-graph/project-graph");
16
19
  const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
17
20
  const delayed_spinner_1 = require("../../utils/delayed-spinner");
@@ -52,6 +55,18 @@ const WAIT_FOR_SERVER_CONFIG = {
52
55
  delayMs: 10,
53
56
  maxAttempts: 6000, // 6000 * 10ms = 60 seconds
54
57
  };
58
+ /**
59
+ * The daemon's workspace watcher died. Nothing it serves will see file changes
60
+ * again, so a watching client has to restart rather than keep waiting.
61
+ */
62
+ class WatcherFailedError extends Error {
63
+ constructor(message) {
64
+ super(message);
65
+ this.name = 'WatcherFailedError';
66
+ Object.setPrototypeOf(this, WatcherFailedError.prototype);
67
+ }
68
+ }
69
+ exports.WatcherFailedError = WatcherFailedError;
55
70
  class DaemonClient {
56
71
  constructor() {
57
72
  // Tracks the spinner owned by the in-flight request so streamed
@@ -62,8 +77,6 @@ class DaemonClient {
62
77
  this._daemonStatus = DaemonStatus.DISCONNECTED;
63
78
  this._waitForDaemonReady = null;
64
79
  this._daemonReady = null;
65
- this._out = null;
66
- this._err = null;
67
80
  this.fileWatcherReconnecting = false;
68
81
  this.fileWatcherCallbacks = new Map();
69
82
  this.fileWatcherConfigs = new Map();
@@ -127,10 +140,6 @@ class DaemonClient {
127
140
  this.currentResolve = null;
128
141
  this.currentReject = null;
129
142
  this._enabled = undefined;
130
- this._out?.close();
131
- this._err?.close();
132
- this._out = null;
133
- this._err = null;
134
143
  // Clean up file watcher and project graph listener connections
135
144
  this.fileWatcherMessenger?.close();
136
145
  this.fileWatcherMessenger = undefined;
@@ -183,12 +192,21 @@ class DaemonClient {
183
192
  return await this.sendToDaemonViaQueue({ type: 'REQUEST_FILE_DATA' });
184
193
  }
185
194
  hashTasks(runnerOptions, tasks, taskGraph, perTaskEnvs, cwd, collectInputs) {
195
+ // Task results get written back onto these task objects as the run
196
+ // progresses — hash/hashDetails/timestamps by hashing and the
197
+ // orchestrator, terminalOutput by the Nx Cloud life cycle (untyped) —
198
+ // so a later message would otherwise re-ship every earlier result.
199
+ const trimmedTasks = {};
200
+ for (const [id, t] of Object.entries(taskGraph.tasks)) {
201
+ const { hash, hashDetails, startTime, endTime, terminalOutput, ...strippedTask } = t;
202
+ trimmedTasks[id] = strippedTask;
203
+ }
186
204
  return this.sendToDaemonViaQueue({
187
205
  type: 'HASH_TASKS',
188
206
  runnerOptions,
189
207
  perTaskEnvs,
190
- tasks,
191
- taskGraph,
208
+ tasks: tasks.map((t) => trimmedTasks[t.id]),
209
+ taskGraph: { ...taskGraph, tasks: trimmedTasks },
192
210
  cwd,
193
211
  collectInputs,
194
212
  });
@@ -224,6 +242,13 @@ class DaemonClient {
224
242
  this.fileWatcherMessenger = new daemon_socket_messenger_1.DaemonSocketMessenger((0, net_1.connect)(socketPath)).listen((message) => {
225
243
  try {
226
244
  const parsedMessage = (0, consume_messages_from_socket_1.parseMessage)(message);
245
+ if (parsedMessage?.watcherError) {
246
+ const error = new WatcherFailedError(parsedMessage.watcherError);
247
+ for (const cb of this.fileWatcherCallbacks.values()) {
248
+ cb(error, null);
249
+ }
250
+ return;
251
+ }
227
252
  // Notify all callbacks
228
253
  for (const cb of this.fileWatcherCallbacks.values()) {
229
254
  cb(null, parsedMessage);
@@ -282,9 +307,9 @@ class DaemonClient {
282
307
  // Wait for daemon server to be available before trying to reconnect
283
308
  let serverAvailable;
284
309
  try {
285
- serverAvailable = await this.waitForServerToBeAvailable({
310
+ ({ available: serverAvailable } = await this.waitForServerToBeAvailable({
286
311
  ignoreVersionMismatch: false,
287
- });
312
+ }));
288
313
  }
289
314
  catch (err) {
290
315
  // Version mismatch - pass error to callbacks so they can handle it
@@ -434,9 +459,9 @@ class DaemonClient {
434
459
  // Wait for daemon server to be available before trying to reconnect
435
460
  let serverAvailable;
436
461
  try {
437
- serverAvailable = await this.waitForServerToBeAvailable({
462
+ ({ available: serverAvailable } = await this.waitForServerToBeAvailable({
438
463
  ignoreVersionMismatch: false,
439
- });
464
+ }));
440
465
  }
441
466
  catch (err) {
442
467
  // Version mismatch - pass error to callbacks so they can handle it
@@ -671,30 +696,40 @@ class DaemonClient {
671
696
  };
672
697
  return this.sendToDaemonViaQueue(message);
673
698
  }
674
- async isServerAvailable() {
699
+ /**
700
+ * The pre-start probe, returning why it failed rather than leaving it on the
701
+ * instance: `_daemonStatus` does not serialize `isServerAvailable`'s five
702
+ * public callers against `startDaemonIfNecessary`.
703
+ */
704
+ async probeServer() {
675
705
  return new Promise((resolve, reject) => {
676
706
  try {
677
707
  const socketPath = this.getSocketPath();
678
708
  if (!socketPath) {
679
- resolve(false);
709
+ resolve({ available: false });
680
710
  return;
681
711
  }
682
712
  const socket = (0, net_1.connect)(socketPath, () => {
683
713
  socket.destroy();
684
- resolve(true);
714
+ resolve({ available: true });
685
715
  });
686
- socket.once('error', () => {
687
- resolve(false);
716
+ socket.once('error', (err) => {
717
+ // The only place the errno for "the socket is there but refuses us"
718
+ // is produced, and the only thing separating it from "no daemon yet".
719
+ resolve({ available: false, refusal: { error: err, socketPath } });
688
720
  });
689
721
  }
690
722
  catch (err) {
691
723
  if (err instanceof daemon_socket_messenger_1.VersionMismatchError) {
692
724
  reject(err); // Let version mismatch bubble up
693
725
  }
694
- resolve(false);
726
+ resolve({ available: false });
695
727
  }
696
728
  });
697
729
  }
730
+ async isServerAvailable() {
731
+ return (await this.probeServer()).available;
732
+ }
698
733
  async startDaemonIfNecessary() {
699
734
  if (this._daemonStatus == DaemonStatus.CONNECTED) {
700
735
  return;
@@ -703,21 +738,22 @@ class DaemonClient {
703
738
  if (this._daemonStatus == DaemonStatus.DISCONNECTED) {
704
739
  this._daemonStatus = DaemonStatus.CONNECTING;
705
740
  let daemonPid = null;
706
- let serverAvailable;
741
+ let probe;
707
742
  try {
708
- serverAvailable = await this.isServerAvailable();
743
+ probe = await this.probeServer();
709
744
  }
710
745
  catch (err) {
711
746
  // Version mismatch - treat as server not available, start new one
712
747
  if (err instanceof daemon_socket_messenger_1.VersionMismatchError) {
713
- serverAvailable = false;
748
+ probe = { available: false };
714
749
  }
715
750
  else {
716
751
  throw err;
717
752
  }
718
753
  }
719
- if (!serverAvailable) {
720
- daemonPid = await this.startInBackground();
754
+ if (!probe.available) {
755
+ // Carried as a value so no other caller's probe can substitute for it.
756
+ daemonPid = await this.startInBackground(probe.refusal);
721
757
  }
722
758
  this.setUpConnection();
723
759
  this._daemonStatus = DaemonStatus.CONNECTED;
@@ -768,6 +804,11 @@ class DaemonClient {
768
804
  if (err.message.startsWith('connect ENOENT')) {
769
805
  error = daemonProcessException('The Daemon Server is not running');
770
806
  }
807
+ else if (isPermissionErrno(err)) {
808
+ // The 0700 dir and 0600 socket mean the OS refuses this rather than the
809
+ // connect silently succeeding.
810
+ error = daemonPermissionException(socketPath, err.message);
811
+ }
771
812
  else if (err.message.startsWith('connect ECONNREFUSED')) {
772
813
  error = daemonProcessException(`A server instance had not been fully shut down. Please try running the command again.`);
773
814
  }
@@ -788,9 +829,9 @@ class DaemonClient {
788
829
  this._daemonStatus = DaemonStatus.CONNECTING;
789
830
  let serverAvailable;
790
831
  try {
791
- serverAvailable = await this.waitForServerToBeAvailable({
832
+ ({ available: serverAvailable } = await this.waitForServerToBeAvailable({
792
833
  ignoreVersionMismatch: false,
793
- });
834
+ }));
794
835
  }
795
836
  catch (err) {
796
837
  if (err instanceof daemon_socket_messenger_1.VersionMismatchError) {
@@ -836,6 +877,11 @@ class DaemonClient {
836
877
  */
837
878
  async waitForServerToBeAvailable(options) {
838
879
  logger_1.clientLogger.log(`[Client] Waiting for server (max: ${WAIT_FOR_SERVER_CONFIG.maxAttempts} attempts, ${WAIT_FOR_SERVER_CONFIG.delayMs}ms interval)`);
880
+ // Poll-scoped, not instance state: reconnect paths have their own flags, so
881
+ // several attempts can be in flight and a shared slot would let one report
882
+ // another's socket.
883
+ let stoppedOnRefusal = false;
884
+ let refusal;
839
885
  const socket = await (0, wait_for_socket_connection_1.waitForSocketConnection)(() => {
840
886
  try {
841
887
  return this.getSocketPath();
@@ -852,14 +898,29 @@ class DaemonClient {
852
898
  }, {
853
899
  maxAttempts: WAIT_FOR_SERVER_CONFIG.maxAttempts,
854
900
  delayMs: WAIT_FOR_SERVER_CONFIG.delayMs,
901
+ onConnectError: (error, socketPath) => {
902
+ refusal = { error, socketPath };
903
+ // A refusal is not expected to become an acceptance, so polling the
904
+ // full 60s budget only delays the same answer. Not absolute:
905
+ // `server.ts` binds before it chmods to 0600, so under a umask that
906
+ // strips owner write a same-user connect can lose a microsecond race
907
+ // and see EACCES. That costs a specific error rather than a retry — a
908
+ // better trade than a 60s hang.
909
+ return (stoppedOnRefusal = isPermissionErrno(error));
910
+ },
855
911
  });
856
912
  if (socket) {
857
913
  socket.destroy();
858
914
  logger_1.clientLogger.log(`[Client] Server available`);
859
- return true;
915
+ return { available: true };
860
916
  }
861
- logger_1.clientLogger.log(`[Client] Server not available after ${WAIT_FOR_SERVER_CONFIG.maxAttempts} attempts`);
862
- return false;
917
+ // Keyed on the early exit taken, not on whether an errno was recorded:
918
+ // every failed connect records one, including an ordinary cold start's
919
+ // ENOENT.
920
+ logger_1.clientLogger.log(stoppedOnRefusal
921
+ ? `[Client] Server refused the connection (${refusal?.error.code}), stopped polling`
922
+ : `[Client] Server not available after ${WAIT_FOR_SERVER_CONFIG.maxAttempts} attempts`);
923
+ return { available: false, refusal };
863
924
  }
864
925
  async sendMessageToDaemon(message, force) {
865
926
  // the first message sent to the daemon includes an env prop
@@ -941,7 +1002,12 @@ class DaemonClient {
941
1002
  ].join('\n')));
942
1003
  }
943
1004
  }
944
- async startInBackground() {
1005
+ /**
1006
+ * @param probeRefusal what the caller's pre-start probe saw. The only evidence
1007
+ * when a daemon refuses us and then exits — the poll cannot reproduce it
1008
+ * once the process json is gone. `nx daemon --start` passes nothing.
1009
+ */
1010
+ async startInBackground(probeRefusal) {
945
1011
  if (global.NX_PLUGIN_WORKER) {
946
1012
  throw new Error('Fatal Error: Something unexpected has occurred. Plugin Workers should not start a new daemon process. Please report this issue.');
947
1013
  }
@@ -949,25 +1015,29 @@ class DaemonClient {
949
1015
  if (!(0, node_fs_1.existsSync)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE)) {
950
1016
  (0, node_fs_1.writeFileSync)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE, '');
951
1017
  }
952
- // Open the log handles into locals first. If the previous daemon's
953
- // socket close handler fires reset() while we're awaiting these opens,
954
- // it would null out this._out/this._err and the spawn below would hit
955
- // `Cannot read properties of null (reading 'fd')`.
956
- const [out, err] = await Promise.all([
957
- (0, promises_1.open)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE, 'a'),
958
- (0, promises_1.open)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE, 'a'),
959
- ]);
960
- this._out = out;
961
- this._err = err;
1018
+ // Redirect the detached daemon's stdout/stderr into the log file. The
1019
+ // child dup's these descriptors at spawn, so we close ours right after
1020
+ // instead of holding them for the life of this process (Node >=26 turns a
1021
+ // file descriptor closed during garbage collection into a fatal error).
1022
+ const outFd = (0, node_fs_1.openSync)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE, 'a');
1023
+ const errFd = (0, node_fs_1.openSync)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE, 'a');
962
1024
  logger_1.clientLogger.log(`[Client] Starting new daemon server in background`);
963
- const backgroundProcess = (0, child_process_1.spawn)(process.execPath, [(0, path_1.join)(__dirname, `../server/start.js`)], {
1025
+ const backgroundProcess = (0, child_process_1.spawn)(process.execPath, [
1026
+ // Spawn with the same resolve conditions Nx uses for plugin entries so a
1027
+ // source-loaded plugin's transitive workspace imports resolve to source.
1028
+ ...(0, typescript_1.getPluginResolveConditionNodeArgs)(),
1029
+ (0, path_1.join)(__dirname, `../server/start.js`),
1030
+ ], {
964
1031
  cwd: workspace_root_1.workspaceRoot,
965
- stdio: ['ignore', out.fd, err.fd],
1032
+ stdio: ['ignore', outFd, errFd],
966
1033
  detached: true,
967
1034
  windowsHide: true,
968
1035
  shell: false,
969
- env: (0, daemon_environment_1.getDaemonEnv)(),
1036
+ env: (0, daemon_environment_1.getDaemonSpawnEnv)(),
970
1037
  });
1038
+ // The child now owns dup'd copies of the descriptors, so release ours.
1039
+ (0, node_fs_1.closeSync)(outFd);
1040
+ (0, node_fs_1.closeSync)(errFd);
971
1041
  // if this process is the process that spawned the daemon,
972
1042
  // the daemon env is already up to date
973
1043
  this.envReflectionSent = true;
@@ -975,14 +1045,25 @@ class DaemonClient {
975
1045
  /**
976
1046
  * Ensure the server is actually available to connect to via IPC before resolving
977
1047
  */
978
- const serverAvailable = await this.waitForServerToBeAvailable({
979
- ignoreVersionMismatch: true,
980
- });
981
- if (serverAvailable) {
1048
+ const { available, refusal: polled } = await this.waitForServerToBeAvailable({ ignoreVersionMismatch: true });
1049
+ if (available) {
982
1050
  logger_1.clientLogger.log(`[Client] Daemon server started, pid=${backgroundProcess.pid}`);
983
1051
  return backgroundProcess.pid;
984
1052
  }
985
1053
  else {
1054
+ // A permission refusal from either source wins, then the poll's errno,
1055
+ // then the probe's. Recency alone would report a daemon's ENOENT over the
1056
+ // EACCES the probe saw a moment earlier, losing the diagnosis.
1057
+ const refusal = [polled, probeRefusal].find((r) => r && isPermissionErrno(r.error)) ??
1058
+ polled ??
1059
+ probeRefusal;
1060
+ if (refusal && isPermissionErrno(refusal.error)) {
1061
+ // Reported here rather than as a generic startup failure, so it degrades
1062
+ // without disabling the daemon until `nx reset`. Both operands come from
1063
+ // the refusal: resolving a path here instead would throw once a daemon
1064
+ // that failed to bind has unlinked its process json.
1065
+ throw daemonPermissionException(refusal.socketPath, refusal.error.message);
1066
+ }
986
1067
  throw daemonProcessException('Failed to start or connect to the Nx Daemon process.');
987
1068
  }
988
1069
  }
@@ -1025,24 +1106,65 @@ function isDocker() {
1025
1106
  function nxJsonIsNotPresent() {
1026
1107
  return !(0, nx_json_1.hasNxJson)(workspace_root_1.workspaceRoot);
1027
1108
  }
1109
+ /**
1110
+ * EACCES and EPERM are the two errnos that mean the OS refused us rather than
1111
+ * that nothing was listening. They need opposite remedies — a socket owned by
1112
+ * someone else versus a sandbox refusing the connect syscall — but they share
1113
+ * the property that retrying cannot change the answer.
1114
+ */
1115
+ function isPermissionErrno(error) {
1116
+ return error?.code === 'EACCES' || error?.code === 'EPERM';
1117
+ }
1118
+ /**
1119
+ * The operating system refused the connection. Most often the socket belongs to
1120
+ * another user, which is the guarantee the owner-only socket directory buys —
1121
+ * but a sandbox that denies unix-socket connects produces the same errno, so the
1122
+ * message does not assert which. Either way it is an environment condition
1123
+ * rather than a defect in Nx, and it deliberately does not
1124
+ * carry `internalDaemonError`: that tag tells the user to file an issue and
1125
+ * disables the daemon until `nx reset`, which would outlast the stale socket
1126
+ * that caused it.
1127
+ *
1128
+ * It also skips the daemon log that `daemonProcessException` appends. The log
1129
+ * belongs to *our* daemon; the process holding this socket is someone else's, so
1130
+ * quoting it would describe an unrelated run.
1131
+ */
1132
+ function daemonPermissionException(socketPath, cause) {
1133
+ const error = new Error([
1134
+ `The operating system refused the connection to the Nx Daemon socket (${cause}).`,
1135
+ '',
1136
+ `Socket: ${socketPath}`,
1137
+ '',
1138
+ 'Most often the socket belongs to a different user: a daemon left behind by running Nx under `sudo`, a different uid inside a container, or a working copy shared between accounts. If the socket is your own, a sandbox is refusing the connection instead.',
1139
+ 'If it belongs to another user, delete the socket above or set NX_SOCKET_DIR to a directory only your user can reach. If you are in a sandbox, allow unix sockets under the Nx socket root — in Claude Code a scoped `allowUnixSockets` only permits connecting, so starting a daemon there needs `allowAllUnixSockets: true`. See https://nx.dev/docs/kb/nx-sandbox-unix-sockets',
1140
+ ].join('\n'));
1141
+ error.daemonPermissionError = true;
1142
+ return error;
1143
+ }
1144
+ /**
1145
+ * Exported for testing: the `internalDaemonError` tag decides whether a daemon
1146
+ * failure degrades to a daemonless graph build or aborts the command.
1147
+ */
1028
1148
  function daemonProcessException(message) {
1149
+ // The log is an enrichment, not the classifier: it is absent on a first run,
1150
+ // which is exactly when the daemon is most likely to fail to start.
1151
+ let body = message;
1029
1152
  try {
1030
1153
  let log = (0, node_fs_1.readFileSync)(tmp_dir_1.DAEMON_OUTPUT_LOG_FILE).toString().split('\n');
1031
1154
  if (log.length > 20) {
1032
1155
  log = log.slice(log.length - 20);
1033
1156
  }
1034
- const error = new Error([
1157
+ body = [
1035
1158
  message,
1036
1159
  '',
1037
1160
  'Messages from the log:',
1038
1161
  ...log,
1039
1162
  '\n',
1040
1163
  `More information: ${tmp_dir_1.DAEMON_OUTPUT_LOG_FILE}`,
1041
- ].join('\n'));
1042
- error.internalDaemonError = true;
1043
- return error;
1044
- }
1045
- catch (e) {
1046
- return new Error(message);
1164
+ ].join('\n');
1047
1165
  }
1166
+ catch { }
1167
+ const error = new Error(body);
1168
+ error.internalDaemonError = true;
1169
+ return error;
1048
1170
  }
@@ -2,6 +2,23 @@ export declare function getDaemonEnv(): NodeJS.ProcessEnv & {
2
2
  NX_PROJECT_GLOB_CACHE: string;
3
3
  NX_CACHE_PROJECTS_CONFIG: string;
4
4
  };
5
+ /**
6
+ * Env for spawning the daemon process. On top of the reflected env, it must
7
+ * keep excluded vars the daemon needs to start correctly:
8
+ * - ELECTRON_RUN_AS_NODE (matched by the ELECTRON_ prefix exclusion): when
9
+ * the spawning client runs inside an Electron host, process.execPath is
10
+ * the Electron binary and only this var makes it run the daemon's Node
11
+ * entry point.
12
+ * - NX_WORKSPACE_ROOT_PATH: the daemon resolves its workspace root at
13
+ * startup by walking up from cwd looking for workspace markers; without
14
+ * the pin, a root without markers under an ancestor that has them
15
+ * resolves to the ancestor and the daemon publishes its socket under the
16
+ * wrong workspace.
17
+ */
18
+ export declare function getDaemonSpawnEnv(): NodeJS.ProcessEnv & {
19
+ NX_PROJECT_GLOB_CACHE: string;
20
+ NX_CACHE_PROJECTS_CONFIG: string;
21
+ };
5
22
  /**
6
23
  * Without the deletion step, a var set by one client (e.g.
7
24
  * `NX_PREFER_NODE_STRIP_TYPES=true` or `JAVA_TOOL_OPTIONS=...` for a single