nx 23.1.1 → 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 (128) hide show
  1. package/dist/release/changelog-renderer/index.js +4 -5
  2. package/dist/release/index.d.ts +1 -1
  3. package/dist/src/command-line/add/add.js +5 -0
  4. package/dist/src/command-line/affected/affected.js +1 -0
  5. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
  6. package/dist/src/command-line/import/import.js +1 -2
  7. package/dist/src/command-line/init/init-v2.js +4 -5
  8. package/dist/src/command-line/migrate/migrate.d.ts +4 -0
  9. package/dist/src/command-line/migrate/migrate.js +89 -26
  10. package/dist/src/command-line/release/changelog.js +5 -0
  11. package/dist/src/command-line/release/index.d.ts +1 -1
  12. package/dist/src/command-line/release/utils/git.js +1 -1
  13. package/dist/src/command-line/release/utils/release-graph.d.ts +17 -0
  14. package/dist/src/command-line/release/utils/release-graph.js +71 -19
  15. package/dist/src/command-line/release/utils/remote-release-clients/github.js +6 -0
  16. package/dist/src/command-line/release/version/release-group-processor.d.ts +24 -0
  17. package/dist/src/command-line/release/version/release-group-processor.js +80 -6
  18. package/dist/src/command-line/release/version/version-actions.d.ts +27 -2
  19. package/dist/src/command-line/release/version/version-actions.js +9 -1
  20. package/dist/src/command-line/release/version.js +9 -5
  21. package/dist/src/command-line/reset/reset.js +6 -1
  22. package/dist/src/command-line/run/run-one.js +1 -0
  23. package/dist/src/command-line/run-many/run-many.js +1 -0
  24. package/dist/src/command-line/watch/watch.js +13 -0
  25. package/dist/src/core/graph/main.js +1 -1
  26. package/dist/src/core/graph/styles.js +1 -1
  27. package/dist/src/daemon/client/client.d.ts +52 -1
  28. package/dist/src/daemon/client/client.js +160 -36
  29. package/dist/src/daemon/client/daemon-environment.d.ts +17 -0
  30. package/dist/src/daemon/client/daemon-environment.js +110 -2
  31. package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
  32. package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
  33. package/dist/src/daemon/message-types/daemon-message.js +25 -0
  34. package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +5 -0
  35. package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +16 -0
  36. package/dist/src/daemon/server/latest-nx.js +5 -2
  37. package/dist/src/daemon/server/server.js +24 -0
  38. package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
  39. package/dist/src/daemon/server/shutdown-utils.js +11 -4
  40. package/dist/src/daemon/socket-utils.d.ts +1 -0
  41. package/dist/src/daemon/socket-utils.js +24 -3
  42. package/dist/src/daemon/tmp-dir.d.ts +43 -3
  43. package/dist/src/daemon/tmp-dir.js +437 -15
  44. package/dist/src/devkit-internals.d.ts +93 -3
  45. package/dist/src/devkit-internals.js +192 -5
  46. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
  47. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
  48. package/dist/src/generators/tree.d.ts +4 -2
  49. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
  50. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
  51. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
  52. package/dist/src/native/index.d.ts +10 -0
  53. package/dist/src/native/index.js +71 -28
  54. package/dist/src/native/native-bindings.js +1 -0
  55. package/dist/src/native/native-file-cache-location.d.ts +17 -1
  56. package/dist/src/native/native-file-cache-location.js +86 -12
  57. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  58. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  59. package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
  60. package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
  61. package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
  62. package/dist/src/plugins/js/lock-file/pnpm-parser.js +60 -0
  63. package/dist/src/plugins/js/lock-file/project-graph-pruning.js +14 -8
  64. package/dist/src/plugins/js/project-graph/affected/npm-packages.js +51 -20
  65. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +2 -0
  66. package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
  67. package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
  68. package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
  69. package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
  70. package/dist/src/project-graph/error-types.d.ts +12 -1
  71. package/dist/src/project-graph/error-types.js +27 -2
  72. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
  73. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +18 -1
  74. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
  75. package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
  76. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
  77. package/dist/src/project-graph/project-graph.js +20 -0
  78. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +5 -1
  79. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +133 -3
  80. package/dist/src/tasks-runner/task-env.js +5 -2
  81. package/dist/src/tasks-runner/utils.js +2 -6
  82. package/dist/src/utils/assert-workspace-validity.js +7 -2
  83. package/dist/src/utils/fileutils.d.ts +5 -0
  84. package/dist/src/utils/fileutils.js +7 -2
  85. package/dist/src/utils/git-worktrees.d.ts +30 -0
  86. package/dist/src/utils/git-worktrees.js +212 -0
  87. package/dist/src/utils/ignore.d.ts +128 -1
  88. package/dist/src/utils/ignore.js +182 -12
  89. package/dist/src/utils/long-running-target.d.ts +10 -0
  90. package/dist/src/utils/long-running-target.js +19 -0
  91. package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
  92. package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
  93. package/dist/src/utils/min-release-age/packument.js +1 -1
  94. package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
  95. package/dist/src/utils/nx-tmp-dir.js +40 -0
  96. package/dist/src/utils/owned-private-dir.d.ts +132 -0
  97. package/dist/src/utils/owned-private-dir.js +323 -0
  98. package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
  99. package/dist/src/utils/package-manager-config/bunfig.js +49 -0
  100. package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
  101. package/dist/src/utils/package-manager-config/npmrc.js +124 -0
  102. package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
  103. package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
  104. package/dist/src/utils/package-manager.d.ts +21 -2
  105. package/dist/src/utils/package-manager.js +244 -47
  106. package/dist/src/utils/provenance.js +6 -14
  107. package/dist/src/utils/registry-config/bun.d.ts +2 -0
  108. package/dist/src/utils/registry-config/bun.js +257 -0
  109. package/dist/src/utils/registry-config/index.d.ts +13 -0
  110. package/dist/src/utils/registry-config/index.js +102 -0
  111. package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
  112. package/dist/src/utils/registry-config/pnpm.js +1551 -0
  113. package/dist/src/utils/registry-config/utils.d.ts +195 -0
  114. package/dist/src/utils/registry-config/utils.js +496 -0
  115. package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
  116. package/dist/src/utils/registry-config/yarn-berry.js +612 -0
  117. package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
  118. package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
  119. package/dist/src/utils/safe-spawn.d.ts +24 -0
  120. package/dist/src/utils/safe-spawn.js +104 -0
  121. package/dist/src/utils/shell-quoting.d.ts +15 -0
  122. package/dist/src/utils/shell-quoting.js +35 -0
  123. package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
  124. package/dist/src/utils/wait-for-socket-connection.js +14 -8
  125. package/migrations.json +6 -0
  126. package/package.json +12 -12
  127. package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
  128. package/dist/src/utils/min-release-age/npmrc.js +0 -45
@@ -1,26 +1,78 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDaemonSocketDir = exports.DAEMON_OUTPUT_LOG_FILE = exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE = void 0;
3
+ exports.getDaemonSocketDir = exports.DAEMON_OUTPUT_LOG_FILE = exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE = exports.InvalidSocketDirConfigured = void 0;
4
4
  exports.writeDaemonLogs = writeDaemonLogs;
5
5
  exports.markDaemonAsDisabled = markDaemonAsDisabled;
6
6
  exports.isDaemonDisabled = isDaemonDisabled;
7
7
  exports.getSocketDir = getSocketDir;
8
+ exports.getPluginSocketDir = getPluginSocketDir;
9
+ exports.resetSocketDirWarningsForTesting = resetSocketDirWarningsForTesting;
10
+ exports.getSocketDirFallbackCause = getSocketDirFallbackCause;
11
+ exports.getRefusedConfiguredSocketDir = getRefusedConfiguredSocketDir;
8
12
  exports.removeSocketDir = removeSocketDir;
9
13
  /**
10
- * Per workspace (to avoid subtle differences and issues), we want to have a deterministic
11
- * location within the OS's tmp directory where we write log files for background processes
12
- * and where we create the actual unix socket/named pipe for the daemon.
14
+ * Where the daemon, forked task processes and plugin workers put their sockets.
15
+ *
16
+ * Not `os.tmpdir()`, and not one location: the root is a literal `/tmp/.nx` so a
17
+ * sandbox can allowlist it once, and resolution walks an ordered chain —
18
+ * `$NX_SOCKET_DIR`, then `/tmp/.nx/<uid>/sockets`, then `~/.nx/sockets`, then the
19
+ * workspace data dir. The socket directory beneath the winning root is per-run,
20
+ * since its name hashes the pid; clients read the daemon's path back out of the
21
+ * process cache rather than deriving it.
22
+ *
23
+ * Daemon logs are not here — they live in the workspace data dir alongside the
24
+ * `disabled` marker, which is why that path survives a socket-root change.
13
25
  */
14
26
  const node_fs_1 = require("node:fs");
15
27
  const path_1 = require("path");
16
28
  const cache_directory_1 = require("../utils/cache-directory");
29
+ const owned_private_dir_1 = require("../utils/owned-private-dir");
17
30
  const crypto_1 = require("crypto");
31
+ // Refused as a socket *directory* (see InvalidSocketDirConfigured), and also
32
+ // the socket root itself on Windows, where named pipes are not filesystem
33
+ // objects and there is nothing to lock down beneath it.
18
34
  const tmp_1 = require("tmp");
35
+ const native_file_cache_location_1 = require("../native/native-file-cache-location");
36
+ const nx_tmp_dir_1 = require("../utils/nx-tmp-dir");
37
+ const is_sandbox_1 = require("../utils/is-sandbox");
19
38
  const workspace_root_1 = require("../utils/workspace-root");
39
+ /**
40
+ * Thrown when the socket dir resolves to a directory Nx will not accept.
41
+ * Invalid configuration, not a recoverable failure.
42
+ *
43
+ * Three reasons, and they are not interchangeable. A directory other users can
44
+ * reach is a security problem; the OS temp root is the user's own but holds
45
+ * everything else that uses it, and Nx deletes the socket directory
46
+ * recursively; an Nx container or cache root is refused because Nx manages what
47
+ * lives there. Telling someone their own `0700` directory lets a local attacker
48
+ * execute code would be false, which is why the reason is derived from the
49
+ * directory rather than the platform.
50
+ */
51
+ class InvalidSocketDirConfigured extends Error {
52
+ constructor(dir, reason) {
53
+ super(reason === 'shared-with-other-users'
54
+ ? `The configured Nx socket directory ${dir} is shared with the other users on this machine. Nx locks the socket directory to a single user, so pointing it at a shared one both shuts every other user out of it and — until it does — lets another local user connect to the daemon or plugin worker sockets and execute code in them. Set NX_SOCKET_DIR to a directory that only your user can access.`
55
+ : reason === 'os-temp-root'
56
+ ? // Two claims deliberately absent. "the temp files of everything else
57
+ // on the machine" is false here: this reason is chosen when the
58
+ // root is *not* peer-writable, so what it holds is this account's
59
+ // own. And "Nx already puts its sockets in a subdirectory of this
60
+ // root" is false on POSIX, where the default root is a literal
61
+ // /tmp/.nx and this one is os.tmpdir() — which is exactly when the
62
+ // two differ.
63
+ `The configured Nx socket directory ${dir} is the operating system temp directory. Nx deletes the socket directory and everything in it when the daemon stops, which here would take everything else using this temp directory with it. Point NX_SOCKET_DIR at a directory of your own instead — one nested beneath this root is fine.`
64
+ : `The configured Nx socket directory ${dir} is a directory Nx keeps its own runtime state in, and Nx creates and removes socket directories beneath it. Point NX_SOCKET_DIR at a directory of your own instead — one nested beneath this root is fine.`);
65
+ this.dir = dir;
66
+ this.reason = reason;
67
+ this.name = 'InvalidSocketDirConfigured';
68
+ }
69
+ }
70
+ exports.InvalidSocketDirConfigured = InvalidSocketDirConfigured;
20
71
  exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'd');
21
72
  exports.DAEMON_OUTPUT_LOG_FILE = (0, path_1.join)(exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE, 'daemon.log');
22
73
  const getDaemonSocketDir = () => (0, path_1.join)(getSocketDir(),
23
- // As per notes above on socket/named pipe length limitations, we keep this intentionally short
74
+ // Kept intentionally short to stay under the socket/named pipe path length
75
+ // limit enforced by `assertValidSocketPath` in socket-utils.ts.
24
76
  'd.sock');
25
77
  exports.getDaemonSocketDir = getDaemonSocketDir;
26
78
  function writeDaemonLogs(error) {
@@ -40,28 +92,398 @@ function isDaemonDisabled() {
40
92
  return false;
41
93
  }
42
94
  }
43
- function socketDirName() {
95
+ /**
96
+ * One short root beneath the current user's owner-only runtime directory. No
97
+ * extra segment on Windows: `%TMP%` is already per-user and already contains the
98
+ * username, so `\.nx\sockets` would spend 12 characters of the 95-char budget
99
+ * `assertValidSocketPath` enforces and newly overrun it for long account names.
100
+ */
101
+ function defaultSocketRoot() {
102
+ return process.platform === 'win32'
103
+ ? tmp_1.tmpdir
104
+ : (0, path_1.join)(nx_tmp_dir_1.NX_USER_TMP_DIR, 'sockets');
105
+ }
106
+ function homeSocketRoot() {
107
+ return nx_tmp_dir_1.NX_HOME_TMP_DIR ? (0, path_1.join)(nx_tmp_dir_1.NX_HOME_TMP_DIR, 'sockets') : undefined;
108
+ }
109
+ /**
110
+ * The spelling to compare a directory by. `resolve` does not dereference
111
+ * symlinks, and on macOS `/tmp` is a symlink to `/private/tmp`, so an
112
+ * exact-match list would wave through an alias of a root it means to refuse.
113
+ *
114
+ * Resolves the longest ancestor that exists and re-appends the rest: Nx's own
115
+ * roots are absent before its first run, and canonicalizing whole paths only
116
+ * would degrade this to a string match on exactly a fresh machine.
117
+ *
118
+ * Only `ENOENT` walks up — any other errno means the path exists and cannot be
119
+ * read through, which `ensureOwnedPrivateDir` cannot establish either.
120
+ */
121
+ function canonicalDir(dir) {
122
+ const resolved = (0, path_1.resolve)(dir);
123
+ const missing = [];
124
+ let candidate = resolved;
125
+ for (;;) {
126
+ try {
127
+ return (0, path_1.join)((0, node_fs_1.realpathSync)(candidate), ...missing);
128
+ }
129
+ catch (e) {
130
+ if (e?.code !== 'ENOENT') {
131
+ return resolved;
132
+ }
133
+ const parent = (0, path_1.dirname)(candidate);
134
+ if (parent === candidate) {
135
+ return resolved;
136
+ }
137
+ missing.unshift((0, path_1.basename)(candidate));
138
+ candidate = parent;
139
+ }
140
+ }
141
+ }
142
+ /**
143
+ * Whether `~/.nx` is somewhere other than the shared container. With
144
+ * `HOME=/tmp` they are the same path, and offering it as a second tier would
145
+ * point `ensureOwnedPrivateDir` at `/tmp/.nx` itself — which, when the container
146
+ * is already ours (or Nx runs as root), takes a `1777` container to `0700` and
147
+ * undoes the documented provisioning, with nothing to put it back.
148
+ */
149
+ function homeTierIsDistinct() {
150
+ if (!nx_tmp_dir_1.NX_HOME_TMP_DIR) {
151
+ return false;
152
+ }
153
+ // Hoisted: the callback runs once per root, and this operand does not change.
154
+ const home = canonicalDir(nx_tmp_dir_1.NX_HOME_TMP_DIR);
155
+ return ![
156
+ tmp_1.tmpdir,
157
+ nx_tmp_dir_1.NX_TMP_DIR,
158
+ nx_tmp_dir_1.NX_USER_TMP_DIR,
159
+ defaultSocketRoot(),
160
+ native_file_cache_location_1.NATIVE_CACHE_ROOT,
161
+ ].some((shared) => canonicalDir(shared) === home);
162
+ }
163
+ /**
164
+ * Socket roots to try, best first. Each entry establishes its own containment
165
+ * before it can be used; the first that succeeds wins, and the workspace data
166
+ * dir is the last resort when none does.
167
+ *
168
+ * `/tmp` first because it is the shortest path — the socket budget is 95
169
+ * characters. Home second because it needs no administrator. Windows has one
170
+ * tier: named pipes are not filesystem objects, so there is nothing to establish.
171
+ */
172
+ function socketRootTiers() {
173
+ if (process.platform === 'win32') {
174
+ return [{ root: tmp_1.tmpdir, establish: () => true }];
175
+ }
176
+ // Stops at the first refusal and records it: once the shared container is
177
+ // unusable, whether the directories beneath it would also have failed says
178
+ // nothing the user can act on.
179
+ const establishAll = (dirs, refusals, guard) => dirs.every((d) => {
180
+ const result = guard(d);
181
+ if (result.status === 'refused') {
182
+ refusals.push(result.refusal);
183
+ return false;
184
+ }
185
+ return true;
186
+ });
187
+ return [
188
+ {
189
+ root: defaultSocketRoot(),
190
+ establish: (refusals) => establishAll([nx_tmp_dir_1.NX_TMP_DIR], refusals, owned_private_dir_1.ensureSafeSharedRoot) &&
191
+ establishAll([nx_tmp_dir_1.NX_USER_TMP_DIR, defaultSocketRoot()], refusals, owned_private_dir_1.ensureOwnedPrivateDir),
192
+ },
193
+ // Omitted entirely when there is no home directory to use, or when it is
194
+ // the shared container under another name, rather than offered and then
195
+ // damaging what it lands on.
196
+ ...(homeTierIsDistinct() && homeSocketRoot()
197
+ ? [
198
+ {
199
+ root: homeSocketRoot(),
200
+ // No shared level to verify: the home directory is the user's own,
201
+ // so there is no container another user could have created first.
202
+ establish: (refusals) => establishAll([nx_tmp_dir_1.NX_HOME_TMP_DIR, homeSocketRoot()], refusals, owned_private_dir_1.ensureOwnedPrivateDir),
203
+ },
204
+ ]
205
+ : []),
206
+ ];
207
+ }
208
+ /**
209
+ * The first socket root whose containment could be established, or `undefined`
210
+ * when none could and the caller should fall back to the workspace.
211
+ */
212
+ function establishSocketRoot(refusals) {
213
+ const tiers = socketRootTiers();
214
+ for (const [index, tier] of tiers.entries()) {
215
+ if (tier.establish(refusals)) {
216
+ // `preferred` is set only on a demotion, and names the tier that was
217
+ // skipped — the caller records it so a later length failure can say the
218
+ // path was not the one Nx wanted.
219
+ return index === 0
220
+ ? { root: tier.root }
221
+ : { root: tier.root, preferred: tiers[0].root };
222
+ }
223
+ }
224
+ return undefined;
225
+ }
226
+ /** Directories that may not *be* the socket directory, and why. */
227
+ function dirsUnusableAsSocketDir() {
228
+ const onWindows = process.platform === 'win32';
229
+ return [
230
+ // Refused even when no peer can reach it: a configured directory becomes the
231
+ // socket directory itself, and `removeSocketDir` deletes that recursively —
232
+ // here, the user's whole temp directory.
233
+ {
234
+ dir: tmp_1.tmpdir,
235
+ reason: (0, owned_private_dir_1.isPeerWritable)(tmp_1.tmpdir)
236
+ ? 'shared-with-other-users'
237
+ : 'os-temp-root',
238
+ },
239
+ {
240
+ dir: nx_tmp_dir_1.NX_TMP_DIR,
241
+ reason: (0, owned_private_dir_1.isPeerWritable)(nx_tmp_dir_1.NX_TMP_DIR)
242
+ ? 'shared-with-other-users'
243
+ : 'nx-managed',
244
+ },
245
+ { dir: nx_tmp_dir_1.NX_USER_TMP_DIR, reason: 'nx-managed' },
246
+ { dir: defaultSocketRoot(), reason: 'nx-managed' },
247
+ // Skipped on Windows, where the home tier is never offered.
248
+ ...(!onWindows && nx_tmp_dir_1.NX_HOME_TMP_DIR
249
+ ? [
250
+ { dir: nx_tmp_dir_1.NX_HOME_TMP_DIR, reason: 'nx-managed' },
251
+ { dir: homeSocketRoot(), reason: 'nx-managed' },
252
+ ]
253
+ : []),
254
+ { dir: native_file_cache_location_1.NATIVE_CACHE_ROOT, reason: 'nx-managed' },
255
+ ];
256
+ }
257
+ /**
258
+ * The configured socket dir, normalized. `resolve` strips a trailing slash,
259
+ * which would otherwise defeat the `O_NOFOLLOW` guard downstream — this is the
260
+ * one socket path built from user input rather than by `join`.
261
+ *
262
+ * `||` rather than `??`: an empty value means unset. An empty string survives
263
+ * `??`, and `resolve('')` is the working directory — which `removeSocketDir`
264
+ * then deletes recursively. `NX_SOCKET_DIR=` with no value is ordinary in a
265
+ * .env file or a compose environment list.
266
+ */
267
+ function configuredSocketDir() {
268
+ const dir = process.env.NX_SOCKET_DIR || process.env.NX_DAEMON_SOCKET_DIR;
269
+ return dir ? (0, path_1.resolve)(dir) : undefined;
270
+ }
271
+ function socketDirName(root) {
44
272
  const hasher = (0, crypto_1.createHash)('sha256');
45
273
  hasher.update(workspace_root_1.workspaceRoot.toLowerCase());
46
274
  hasher.update(String(process.pid));
47
275
  const unique = hasher.digest('hex').substring(0, 20);
48
- return (0, path_1.join)(tmp_1.tmpdir, unique);
276
+ return (0, path_1.join)(root, unique);
277
+ }
278
+ function pluginSocketDirName(root) {
279
+ // Short so the socket file name still fits under assertValidSocketPath's limit.
280
+ const hash = (0, crypto_1.createHash)('sha256')
281
+ .update(workspace_root_1.workspaceRoot.toLowerCase())
282
+ .digest('hex')
283
+ .substring(0, 8);
284
+ return (0, path_1.join)(root, hash);
49
285
  }
50
286
  /**
51
- * We try to create a socket file in a tmp dir, but if it doesn't work because
52
- * for instance we don't have permissions, we create it in DAEMON_DIR_FOR_CURRENT_WORKSPACE
287
+ * A socket dir under the first usable root, falling back to the workspace data
288
+ * dir when no root can be established. Either way it is locked to the current
289
+ * user.
53
290
  */
54
- function getSocketDir(alreadyUnique = false) {
291
+ function socketDirUnderFirstUsableRoot(leafFor) {
292
+ const configuredDir = configuredSocketDir();
293
+ if (configuredDir !== undefined) {
294
+ return createOwnerOnlySocketDir(configuredDir, false);
295
+ }
296
+ // Cleared here too: the no-tier exit below returns without entering
297
+ // createOwnerOnlySocketDir, and a stale value would have assertValidSocketPath
298
+ // blame an NX_SOCKET_DIR the user no longer has set.
299
+ socketDirFallbackCause = undefined;
300
+ refusedConfiguredSocketDir = undefined;
301
+ const refusals = [];
302
+ const established = establishSocketRoot(refusals);
303
+ if (established === undefined) {
304
+ return fallBackToWorkspaceSocketDir(socketRootsUnavailable(refusals), undefined, refusals);
305
+ }
306
+ const dir = createOwnerOnlySocketDir(leafFor(established.root), true, refusals);
307
+ // Only when the directory we actually got is the demoted tier: if
308
+ // createOwnerOnlySocketDir fell back to the workspace, it recorded its own,
309
+ // more specific cause and that one should survive.
310
+ if (established.preferred !== undefined &&
311
+ socketDirFallbackCause === undefined) {
312
+ noteSocketRootDemotion(established.preferred, established.root, refusals);
313
+ }
314
+ return dir;
315
+ }
316
+ /**
317
+ * Record a successful demotion to a later tier. Verbose, not warn: nothing
318
+ * failed. The cause is still set because `assertValidSocketPath` keys its
319
+ * "run with --verbose" block off it, and without it a later length failure
320
+ * reads as though the user chose the path.
321
+ *
322
+ * `refusals` can name any of the three directories tier 0 establishes, so it is
323
+ * not always `preferred`.
324
+ */
325
+ function noteSocketRootDemotion(preferred, used, refusals) {
326
+ socketDirFallbackCause = new Error(`Nx could not establish its preferred socket root ${preferred}, so it used ${used}.`);
327
+ // Lazily required for the import-cycle reason spelled out in
328
+ // fallBackToWorkspaceSocketDir below.
329
+ const { logger } = require('../utils/logger');
330
+ logger.verbose(`Nx could not use the default socket directory ${preferred}. Using ${used} instead.` +
331
+ (refusals.length
332
+ ? ` ${refusals.map((r) => (0, owned_private_dir_1.describeRefusal)(r)).join('; ')}.`
333
+ : ''));
334
+ }
335
+ function getSocketDir() {
336
+ return socketDirUnderFirstUsableRoot(socketDirName);
337
+ }
338
+ /**
339
+ * Plugin worker sockets get their own workspace-scoped directory rather than
340
+ * sitting in the shared system temp dir, which cannot be locked down.
341
+ */
342
+ function getPluginSocketDir() {
343
+ return socketDirUnderFirstUsableRoot(pluginSocketDirName);
344
+ }
345
+ let socketDirFallbackCause;
346
+ let refusedConfiguredSocketDir;
347
+ let warnedAboutWorkspaceFallback = false;
348
+ let warnedAboutConfiguredSocketDir = false;
349
+ /**
350
+ * Exported for tests: both fallback warnings fire once per process, so a suite
351
+ * that stages either fallback more than once has to clear the latches between
352
+ * cases.
353
+ */
354
+ function resetSocketDirWarningsForTesting() {
355
+ warnedAboutWorkspaceFallback = false;
356
+ warnedAboutConfiguredSocketDir = false;
357
+ }
358
+ function getSocketDirFallbackCause() {
359
+ return socketDirFallbackCause;
360
+ }
361
+ /**
362
+ * The NX_SOCKET_DIR that was refused, if that is why we are in the fallback.
363
+ * Reflects the most recent resolution only — both accessors are cleared at the
364
+ * top of every `createOwnerOnlySocketDir` call, and the daemon and plugin socket
365
+ * paths each drive one. Read it immediately after the call that produced the
366
+ * path, which is what `assertValidSocketPath` does.
367
+ */
368
+ function getRefusedConfiguredSocketDir() {
369
+ return refusedConfiguredSocketDir;
370
+ }
371
+ /**
372
+ * @param dir the resolved socket directory to create and lock down.
373
+ * @param usingDefaultRoot whether `dir` sits under the default root, in which
374
+ * case Nx verifies the stable shared container and establishes the
375
+ * current user's owner-only roots first.
376
+ * @param priorRefusals why the tiers above this one were skipped. Ignored unless
377
+ * `usingDefaultRoot`.
378
+ */
379
+ function createOwnerOnlySocketDir(dir, usingDefaultRoot, priorRefusals = []) {
380
+ socketDirFallbackCause = undefined;
381
+ refusedConfiguredSocketDir = undefined;
382
+ // Outside the try so it is not swallowed by the fallback. Exact matches only,
383
+ // so the per-user directories under those roots never trip it.
384
+ const canonical = canonicalDir(dir);
385
+ const unusable = dirsUnusableAsSocketDir().find((d) => canonical === canonicalDir(d.dir));
386
+ if (unusable) {
387
+ throw new InvalidSocketDirConfigured(dir, unusable.reason);
388
+ }
55
389
  try {
56
- const dir = process.env.NX_SOCKET_DIR ??
57
- process.env.NX_DAEMON_SOCKET_DIR ??
58
- (alreadyUnique ? tmp_1.tmpdir : socketDirName());
59
- (0, node_fs_1.mkdirSync)(dir, { recursive: true });
390
+ // A default root has already had its containment established by the tier it
391
+ // came from; a configured one is the user's to create.
392
+ if (!usingDefaultRoot) {
393
+ (0, node_fs_1.mkdirSync)((0, path_1.dirname)(dir), { recursive: true });
394
+ }
395
+ // Separately from its parents: mkdirSync does not throw on a pre-planted
396
+ // symlink, so creating and locking down in one step would adopt it.
397
+ const created = (0, owned_private_dir_1.ensureOwnedPrivateDir)(dir);
398
+ if (created.status === 'refused') {
399
+ throw new owned_private_dir_1.DirectoryRefusedError(created.refusal);
400
+ }
60
401
  return dir;
61
402
  }
62
403
  catch (e) {
63
- return exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE;
404
+ const refusals = e instanceof owned_private_dir_1.DirectoryRefusedError
405
+ ? [...priorRefusals, e.refusal]
406
+ : priorRefusals;
407
+ // Recoverable: fall back to the owner-controlled workspace data dir.
408
+ if (usingDefaultRoot) {
409
+ return fallBackToWorkspaceSocketDir(refusals.length ? socketRootsUnavailable(refusals) : e, dir, refusals);
410
+ }
411
+ // Never swap out a configured directory silently — the substitute is longer
412
+ // and would resurface as a length complaint about a path the user never set.
413
+ // Latched: a task-per-PseudoTerminal command resolves this once per task.
414
+ if (!warnedAboutConfiguredSocketDir) {
415
+ warnedAboutConfiguredSocketDir = true;
416
+ console.warn(`Nx could not use the configured socket directory ${dir}: ${e instanceof Error ? e.message : e}\nFalling back to ${exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE}.`);
417
+ }
418
+ // Tracked separately from socketDirFallbackCause so the length error stops
419
+ // telling someone to shorten an NX_SOCKET_DIR that was refused for another
420
+ // reason.
421
+ refusedConfiguredSocketDir = dir;
422
+ return establishWorkspaceSocketDir(e);
423
+ }
424
+ }
425
+ /**
426
+ * The refusals as one error. `AggregateError` because there genuinely are
427
+ * several — one per root the chain tried — and its `errors` stay inspectable
428
+ * rather than being flattened into the message that `--verbose` prints.
429
+ */
430
+ function socketRootsUnavailable(refusals) {
431
+ return new AggregateError(refusals.map((r) => new owned_private_dir_1.DirectoryRefusedError(r)), `Nx could not establish any of its default socket directories: ${refusals
432
+ .map((r) => (0, owned_private_dir_1.describeRefusal)(r))
433
+ .join('; ')}.`);
434
+ }
435
+ /**
436
+ * The last resort once no default root could be used. Retains the cause so
437
+ * `assertValidSocketPath` can explain a length failure the user did not cause.
438
+ */
439
+ function fallBackToWorkspaceSocketDir(cause, attempted, refusals = []) {
440
+ socketDirFallbackCause = cause;
441
+ // Required lazily, and only on a path that is already failing. A static
442
+ // import closes a cycle — utils/logger reads `serverLogger` from
443
+ // daemon/logger while it is still evaluating, and daemon/logger imports this
444
+ // module — which throws whenever `isOnDaemon()` is true as this module loads.
445
+ // Production escapes it only because server.ts sets `global.NX_DAEMON` after
446
+ // its imports, so daemon boot rests on import order.
447
+ const { logger } = require('../utils/logger');
448
+ logger.verbose(`Nx could not use the default socket ${attempted ? `directory ${attempted}` : 'directories'}. Falling back to ${exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE}.`, cause);
449
+ // Warned rather than verbose: the workspace path grows with checkout depth, so
450
+ // this is where the 95-character budget is most likely to trip, and an
451
+ // allowlist scoped to Nx's usual roots no longer covers it.
452
+ //
453
+ // Latched once per process — neither socket-dir accessor is memoized and one
454
+ // CLI process resolves several, so without it a single command repeats this
455
+ // many times.
456
+ if (!warnedAboutWorkspaceFallback) {
457
+ warnedAboutWorkspaceFallback = true;
458
+ logger.warn([
459
+ `Nx could not use any of its usual socket directories and fell back to ${exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE}.`,
460
+ ...new Set(refusals.map(owned_private_dir_1.remedyFor).filter(Boolean)),
461
+ (0, is_sandbox_1.isSandbox)()
462
+ ? // NX_HOME_TMP_DIR is undefined when there is no home directory,
463
+ // which is itself one reason this fallback is reached.
464
+ `A sandbox allowlist covering only ${[nx_tmp_dir_1.NX_TMP_DIR, nx_tmp_dir_1.NX_HOME_TMP_DIR]
465
+ .filter(Boolean)
466
+ .join(' or ')} does not cover this path.`
467
+ : undefined,
468
+ 'Run with --verbose to see why the others were rejected.',
469
+ ]
470
+ .filter(Boolean)
471
+ .join(' '));
472
+ }
473
+ return establishWorkspaceSocketDir(cause);
474
+ }
475
+ function establishWorkspaceSocketDir(cause) {
476
+ (0, node_fs_1.mkdirSync)((0, path_1.dirname)(exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE), { recursive: true });
477
+ // The fallback is only safe if it passes the same checks the primary did.
478
+ const established = (0, owned_private_dir_1.ensureOwnedPrivateDir)(exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE);
479
+ if (established.status === 'refused') {
480
+ // With the remedy: this is the one refusal path with nowhere left to fall,
481
+ // so it is where the user most needs it and the only one that cannot reach
482
+ // the warning's copy.
483
+ const remedy = (0, owned_private_dir_1.remedyFor)(established.refusal);
484
+ throw new Error(`Nx could not establish a socket directory: ${(0, owned_private_dir_1.describeRefusal)(established.refusal)}.${remedy ? ` ${remedy}` : ''}`, { cause });
64
485
  }
486
+ return exports.DAEMON_DIR_FOR_CURRENT_WORKSPACE;
65
487
  }
66
488
  function removeSocketDir() {
67
489
  try {
@@ -1,7 +1,21 @@
1
1
  /**
2
- * Note to developers: STOP! These exports are available via requireNx in @nx/devkit.
2
+ * Note to developers: STOP! These are nx internals, not part of the public
3
+ * @nx/devkit API.
3
4
  *
4
- * These may not be available in certain version of Nx, so be sure to check them first.
5
+ * This barrel is NOT under @nx/devkit's version-tolerance contract. That contract
6
+ * (nx at the current major +/- 1, per devkit's peerDependencies) covers the public
7
+ * @nx/devkit API only. Two classes of consumer read this file, and the rules
8
+ * differ:
9
+ *
10
+ * 1. packages/devkit/src/ — devkit's own implementation. It ships to external
11
+ * plugins, so it *does* run under the +/- 1 tolerance (@nx/devkit@23 may be
12
+ * installed against nx@22) and must still guard: check at runtime, or only
13
+ * use symbols that have existed since the oldest supported nx major.
14
+ * 2. First-party plugins in this repo, via @nx/devkit/internal. They release in
15
+ * lockstep with nx, so they require an exactly matching nx version and need
16
+ * no guarding.
17
+ *
18
+ * See packages/devkit/CLAUDE.md.
5
19
  */
6
20
  export { createTempNpmDirectory } from './utils/package-manager';
7
21
  export { getExecutorInformation, parseExecutor, } from './command-line/run/executor-utils';
@@ -13,7 +27,7 @@ export { readProjectConfigurationsFromRootMap } from './project-graph/utils/proj
13
27
  export { findMatchingConfigFiles } from './project-graph/utils/project-configuration-utils';
14
28
  export { findMatchingProjects } from './utils/find-matching-projects';
15
29
  export { readTargetDefaultsForTarget } from './project-graph/utils/project-configuration/target-defaults';
16
- export { getIgnoreObjectForTree } from './utils/ignore';
30
+ export { createGitIgnoreChecker, createPrettierIgnoreChecker, type TreeIgnoreChecker, } from './utils/ignore';
17
31
  export { splitTarget } from './utils/split-target';
18
32
  export { combineOptionsForExecutor } from './utils/params';
19
33
  export { sortObjectByKeys } from './utils/object-sort';
@@ -41,3 +55,79 @@ export { resolvePrompt, PromptResolutionError, } from './command-line/migrate/pr
41
55
  export { checkFilesAreInputs, checkFilesAreOutputs, } from './hasher/check-task-files';
42
56
  export { getCatalogManager, getCatalogDependenciesFromPackageJson, } from './utils/catalog';
43
57
  export { acknowledgeBuildScripts } from './utils/acknowledge-build-scripts';
58
+ export { default as runCommandsExecutor } from './executors/run-commands/run-commands.impl';
59
+ export type { TaskResult } from './config/misc-interfaces';
60
+ export { toNewFormat, toOldFormat } from './adapter/angular-json';
61
+ export { setupAiAgentsGenerator } from './ai/set-up-ai-agents/set-up-ai-agents';
62
+ export type { Agent } from './ai/utils';
63
+ export type { GeneratorInformation } from './command-line/generate/generator-utils';
64
+ export { getGeneratorInformation } from './command-line/generate/generator-utils';
65
+ export type { FinalConfigForProject } from './command-line/release/utils/release-graph';
66
+ export { BatchFunctionRunner } from './command-line/watch/watch';
67
+ export type { BatchExecutorTaskResult } from './config/misc-interfaces';
68
+ export type { NxReleaseVersionConfiguration } from './config/nx-json';
69
+ export type { FileDataDependency } from './config/project-graph';
70
+ export { fileDataDepTarget, fileDataDepType, isProjectGraphExternalNode, isProjectGraphProjectNode, } from './config/project-graph';
71
+ export type { InputDefinition } from './config/workspace-json-project-json';
72
+ export type { ChangedFile, UnregisterCallback } from './daemon/client/client';
73
+ export { daemonClient } from './daemon/client/client';
74
+ export type { RunCommandsOptions } from './executors/run-commands/run-commands.impl';
75
+ export { LARGE_BUFFER } from './executors/run-commands/run-commands.impl';
76
+ export { FsTree } from './generators/tree';
77
+ export { getRelativeProjectJsonSchemaPath } from './generators/utils/project-configuration';
78
+ export { hashFile } from './hasher/file-hasher';
79
+ export { filterUsingGlobPatterns, getTargetInputs } from './hasher/task-hasher';
80
+ export type { JsonInput } from './native';
81
+ export { killProcessTreeGraceful } from './native';
82
+ export { connectToNxCloud } from './nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud';
83
+ export type { NxCloudOnBoardingStatus } from './nx-cloud/models/onboarding-status';
84
+ export { createNxCloudOnboardingURLForWelcomeApp, getNxCloudAppOnBoardingUrl, } from './nx-cloud/utilities/onboarding';
85
+ export { createNxCloudOnboardingURL } from './nx-cloud/utilities/url-shorten';
86
+ export { createLockFile, getLockFileName, } from './plugins/js/lock-file/lock-file';
87
+ export { createPackageJson, findProjectsNpmDependencies, } from './plugins/js/package-json/create-package-json';
88
+ export { TargetProjectLocator, isBuiltinModuleImport, } from './plugins/js/project-graph/build-dependencies/target-project-locator';
89
+ export { getWorkspacePackagesFromGraph } from './plugins/js/utils/get-workspace-packages-from-graph';
90
+ export { registerTsConfigPaths } from './plugins/js/utils/register';
91
+ export { getGlobPatternsFromPackageManagerWorkspaces } from './plugins/package-json';
92
+ export type { Change, FileChange } from './project-graph/file-utils';
93
+ export { DeletedFileChange, LockFileChange, TEN_MEGABYTES, WholeFileChange, calculateFileChanges, defaultFileRead, isDeletedFileChange, isLockFileChange, isWholeFileChange, readPackageJson, } from './project-graph/file-utils';
94
+ export type { FileMapCache } from './project-graph/nx-deps-cache';
95
+ export { readFileMapCache } from './project-graph/nx-deps-cache';
96
+ export { isNpmProject } from './project-graph/operators';
97
+ export { buildProjectGraphAndSourceMapsWithoutDaemon, createProjectGraphAndSourceMapsAsync, handleProjectGraphError, preventRecursionInGraphConstruction, readCachedProjectConfiguration, } from './project-graph/project-graph';
98
+ export type { ProjectRootMappings } from './project-graph/utils/find-project-for-path';
99
+ export type { BatchResults } from './tasks-runner/batch/batch-messages';
100
+ export type { CachedResult, TaskWithCachedResult } from './tasks-runner/cache';
101
+ export { Cache } from './tasks-runner/cache';
102
+ export { RemoteCacheV2 } from './tasks-runner/default-tasks-runner';
103
+ export type { LifeCycle, TaskMetadata } from './tasks-runner/life-cycle';
104
+ export { CompositeLifeCycle } from './tasks-runner/life-cycle';
105
+ export { createRunManyDynamicOutputRenderer } from './tasks-runner/life-cycles/dynamic-run-many-terminal-output-life-cycle';
106
+ export type { TaskStatus, TasksRunner } from './tasks-runner/tasks-runner';
107
+ export type { DependsOnEntryLocation, NormalizedTargetDependencyConfig, } from './tasks-runner/utils';
108
+ export { calculateReverseDeps, createTaskId, expandDependencyConfigSyntaxSugar, expandInitiatingTasksThroughNoop, expandWildcardTargetConfiguration, getCliPath, getCustomHasher, getDependencyConfigs, getExecutorForTask, getExecutorNameForTask, getOutputs, getPrintableCommandArgsForTask, getSerializedArgsForTask, getTargetConfigurationForTask, getUnparsedOverrideArgs, isCacheableTask, normalizeDependencyConfigDefinition, normalizeDependencyConfigProjects, normalizeTargetDependencyWithStringProjects, readProjectAndTargetFromTargetString, removeTasksFromTaskGraph, shouldStreamOutput, transformLegacyOutputs, unparse, validateOutputs, } from './tasks-runner/utils';
109
+ export { getLastValueFromAsyncIterableIterator } from './utils/async-iterator';
110
+ export { workspaceDataDirectory } from './utils/cache-directory';
111
+ export { runNxSync } from './utils/child-process';
112
+ export { codeFrameColumns } from './utils/code-frames';
113
+ export { deduceDefaultBase } from './utils/default-base';
114
+ export { createDirectory, directoryExists, fileExists, isRelativePath, readFileIfExisting, } from './utils/fileutils';
115
+ export { getLatestCommitSha } from './utils/git-utils';
116
+ export { combineGlobPatterns } from './utils/globs';
117
+ export { getNxRequirePaths } from './utils/installation-directory';
118
+ export { NX_PREFIX } from './utils/logger';
119
+ export type { CLIErrorMessageConfig, CLINoteMessageConfig, CLISuccessMessageConfig, CLIWarnMessageConfig, } from './utils/output';
120
+ export { orange } from './utils/output';
121
+ export { getNxCloudUrl, isNxCloudUsed } from './utils/nx-cloud-utils';
122
+ export type { PackageJson, PackageJsonDependencySection, } from './utils/package-json';
123
+ export { readNxMigrateConfig } from './utils/package-json';
124
+ export type { PackageManagerCommands } from './utils/package-manager';
125
+ export { deriveGroupNameFromTarget } from './utils/plugins/atomizer-utils';
126
+ export { findInstalledPlugins } from './utils/plugins/installed-plugins';
127
+ export { findAllProjectNodeDependencies, getSourceDirOfDependentProjects, } from './utils/project-graph-utils';
128
+ export type { SyncGeneratorResult } from './utils/sync-generators';
129
+ export { SyncError } from './utils/sync-generators';
130
+ export { nxVersion } from './utils/versions';
131
+ export { getFilesInDirectoryUsingContext, globWithWorkspaceContext, resetWorkspaceContext, setupWorkspaceContext, } from './utils/workspace-context';
132
+ export { setWorkspaceRoot, workspaceRootInner } from './utils/workspace-root';
133
+ export { safeExecFileSync, safeSpawn } from './utils/safe-spawn';