nx 22.7.0-beta.12 → 22.7.0-beta.13

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 (66) hide show
  1. package/dist/schemas/nx-schema.json +25 -0
  2. package/dist/schemas/project-schema.json +25 -0
  3. package/dist/src/adapter/ngcli-adapter.js +11 -12
  4. package/dist/src/command-line/daemon/daemon.js +8 -4
  5. package/dist/src/command-line/graph/graph.js +8 -1
  6. package/dist/src/command-line/show/show-target/utils.js +4 -3
  7. package/dist/src/command-line/yargs-utils/shared-options.js +5 -1
  8. package/dist/src/config/nx-json.d.ts +3 -1
  9. package/dist/src/config/workspace-json-project-json.d.ts +2 -1
  10. package/dist/src/core/graph/main.js +1 -1
  11. package/dist/src/daemon/client/client.d.ts +10 -3
  12. package/dist/src/daemon/client/client.js +21 -31
  13. package/dist/src/daemon/client/daemon-environment.d.ts +4 -0
  14. package/dist/src/daemon/client/daemon-environment.js +119 -0
  15. package/dist/src/daemon/client/daemon-socket-messenger.d.ts +2 -5
  16. package/dist/src/daemon/client/daemon-socket-messenger.js +1 -1
  17. package/dist/src/daemon/message-types/daemon-message.d.ts +6 -0
  18. package/dist/src/daemon/message-types/daemon-message.js +6 -0
  19. package/dist/src/daemon/server/handle-hash-tasks.d.ts +1 -1
  20. package/dist/src/daemon/server/handle-hash-tasks.js +1 -1
  21. package/dist/src/daemon/server/handle-outputs-tracking.d.ts +4 -4
  22. package/dist/src/daemon/server/handle-outputs-tracking.js +11 -11
  23. package/dist/src/daemon/server/outputs-tracking.d.ts +18 -3
  24. package/dist/src/daemon/server/outputs-tracking.js +49 -22
  25. package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +2 -1
  26. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +20 -4
  27. package/dist/src/daemon/server/server.js +71 -40
  28. package/dist/src/executors/run-commands/running-tasks.js +2 -3
  29. package/dist/src/executors/run-script/run-script.impl.js +16 -8
  30. package/dist/src/hasher/hash-task.d.ts +9 -1
  31. package/dist/src/hasher/hash-task.js +41 -14
  32. package/dist/src/hasher/native-task-hasher-impl.d.ts +1 -1
  33. package/dist/src/hasher/native-task-hasher-impl.js +4 -6
  34. package/dist/src/hasher/task-hasher.d.ts +20 -9
  35. package/dist/src/hasher/task-hasher.js +34 -6
  36. package/dist/src/native/index.d.ts +34 -7
  37. package/dist/src/native/native-bindings.js +1 -1
  38. package/dist/src/native/nx.wasi-browser.js +0 -1
  39. package/dist/src/native/nx.wasi.cjs +0 -1
  40. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  41. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  42. package/dist/src/plugins/js/utils/register.js +83 -4
  43. package/dist/src/project-graph/error-types.js +31 -11
  44. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
  45. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +9 -0
  46. package/dist/src/project-graph/plugins/isolation/message-types.d.ts +1 -1
  47. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +9 -0
  48. package/dist/src/project-graph/plugins/isolation/messaging.js +2 -0
  49. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +36 -68
  50. package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +6 -0
  51. package/dist/src/tasks-runner/cache.d.ts +6 -0
  52. package/dist/src/tasks-runner/cache.js +58 -0
  53. package/dist/src/tasks-runner/init-tasks-runner.js +13 -7
  54. package/dist/src/tasks-runner/run-command.js +13 -5
  55. package/dist/src/tasks-runner/task-env.js +17 -1
  56. package/dist/src/tasks-runner/task-orchestrator.d.ts +79 -7
  57. package/dist/src/tasks-runner/task-orchestrator.js +327 -120
  58. package/dist/src/tasks-runner/tasks-schedule.d.ts +5 -2
  59. package/dist/src/tasks-runner/tasks-schedule.js +31 -17
  60. package/dist/src/tasks-runner/utils.d.ts +3 -3
  61. package/dist/src/tasks-runner/utils.js +5 -6
  62. package/package.json +12 -12
  63. package/dist/src/daemon/client/exec-is-server-available.d.ts +0 -1
  64. package/dist/src/daemon/client/exec-is-server-available.js +0 -11
  65. package/dist/src/daemon/client/generate-help-output.d.ts +0 -1
  66. package/dist/src/daemon/client/generate-help-output.js +0 -26
@@ -6,64 +6,67 @@ exports.startServer = startServer;
6
6
  const fs_1 = require("fs");
7
7
  const net_1 = require("net");
8
8
  const path_1 = require("path");
9
- require("../../utils/perf-logging");
9
+ const v8_1 = require("v8");
10
+ const analytics_1 = require("../../analytics");
10
11
  const file_hasher_1 = require("../../hasher/file-hasher");
11
12
  const native_1 = require("../../native");
12
13
  const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
14
+ require("../../utils/perf-logging");
13
15
  const versions_1 = require("../../utils/versions");
14
16
  const workspace_context_1 = require("../../utils/workspace-context");
15
17
  const workspace_root_1 = require("../../utils/workspace-root");
18
+ const get_plugins_1 = require("../../project-graph/plugins/get-plugins");
16
19
  const cache_1 = require("../cache");
17
- const analytics_1 = require("../../analytics");
18
20
  const is_nx_version_mismatch_1 = require("../is-nx-version-mismatch");
21
+ const logger_1 = require("../logger");
22
+ const configure_ai_agents_1 = require("../message-types/configure-ai-agents");
23
+ const daemon_message_1 = require("../message-types/daemon-message");
24
+ const flush_sync_generator_changes_to_disk_1 = require("../message-types/flush-sync-generator-changes-to-disk");
25
+ const force_shutdown_1 = require("../message-types/force-shutdown");
26
+ const get_context_file_data_1 = require("../message-types/get-context-file-data");
27
+ const get_files_in_directory_1 = require("../message-types/get-files-in-directory");
28
+ const get_nx_workspace_files_1 = require("../message-types/get-nx-workspace-files");
29
+ const get_registered_sync_generators_1 = require("../message-types/get-registered-sync-generators");
30
+ const get_sync_generator_changes_1 = require("../message-types/get-sync-generator-changes");
31
+ const glob_1 = require("../message-types/glob");
32
+ const hash_glob_1 = require("../message-types/hash-glob");
33
+ const nx_console_1 = require("../message-types/nx-console");
34
+ const register_project_graph_listener_1 = require("../message-types/register-project-graph-listener");
35
+ const run_tasks_execution_hooks_1 = require("../message-types/run-tasks-execution-hooks");
36
+ const task_history_1 = require("../message-types/task-history");
37
+ const update_workspace_context_1 = require("../message-types/update-workspace-context");
19
38
  const socket_utils_1 = require("../socket-utils");
39
+ const file_change_events_1 = require("./file-watching/file-change-events");
20
40
  const file_watcher_sockets_1 = require("./file-watching/file-watcher-sockets");
21
- const project_graph_listener_sockets_1 = require("./project-graph-listener-sockets");
41
+ const handle_configure_ai_agents_1 = require("./handle-configure-ai-agents");
42
+ const handle_context_file_data_1 = require("./handle-context-file-data");
43
+ const handle_flush_sync_generator_changes_to_disk_1 = require("./handle-flush-sync-generator-changes-to-disk");
44
+ const handle_force_shutdown_1 = require("./handle-force-shutdown");
45
+ const handle_get_files_in_directory_1 = require("./handle-get-files-in-directory");
46
+ const handle_get_registered_sync_generators_1 = require("./handle-get-registered-sync-generators");
47
+ const handle_get_sync_generator_changes_1 = require("./handle-get-sync-generator-changes");
48
+ const handle_glob_1 = require("./handle-glob");
49
+ const handle_hash_glob_1 = require("./handle-hash-glob");
22
50
  const handle_hash_tasks_1 = require("./handle-hash-tasks");
51
+ const handle_nx_console_1 = require("./handle-nx-console");
52
+ const handle_nx_workspace_files_1 = require("./handle-nx-workspace-files");
23
53
  const handle_outputs_tracking_1 = require("./handle-outputs-tracking");
24
54
  const handle_process_in_background_1 = require("./handle-process-in-background");
25
55
  const handle_request_project_graph_1 = require("./handle-request-project-graph");
26
56
  const handle_request_shutdown_1 = require("./handle-request-shutdown");
27
- const logger_1 = require("../logger");
57
+ const handle_task_history_1 = require("./handle-task-history");
58
+ const handle_tasks_execution_hooks_1 = require("./handle-tasks-execution-hooks");
59
+ const handle_update_workspace_context_1 = require("./handle-update-workspace-context");
28
60
  const outputs_tracking_1 = require("./outputs-tracking");
29
61
  const project_graph_incremental_recomputation_1 = require("./project-graph-incremental-recomputation");
62
+ const project_graph_listener_sockets_1 = require("./project-graph-listener-sockets");
30
63
  const shutdown_utils_1 = require("./shutdown-utils");
31
- const watcher_1 = require("./watcher");
32
- const handle_glob_1 = require("./handle-glob");
33
- const glob_1 = require("../message-types/glob");
34
- const get_nx_workspace_files_1 = require("../message-types/get-nx-workspace-files");
35
- const handle_nx_workspace_files_1 = require("./handle-nx-workspace-files");
36
- const get_context_file_data_1 = require("../message-types/get-context-file-data");
37
- const handle_context_file_data_1 = require("./handle-context-file-data");
38
- const get_files_in_directory_1 = require("../message-types/get-files-in-directory");
39
- const handle_get_files_in_directory_1 = require("./handle-get-files-in-directory");
40
- const hash_glob_1 = require("../message-types/hash-glob");
41
- const handle_hash_glob_1 = require("./handle-hash-glob");
42
- const task_history_1 = require("../message-types/task-history");
43
- const handle_task_history_1 = require("./handle-task-history");
44
- const force_shutdown_1 = require("../message-types/force-shutdown");
45
- const handle_force_shutdown_1 = require("./handle-force-shutdown");
46
- const get_sync_generator_changes_1 = require("../message-types/get-sync-generator-changes");
47
- const handle_get_sync_generator_changes_1 = require("./handle-get-sync-generator-changes");
48
64
  const sync_generators_1 = require("./sync-generators");
49
- const file_change_events_1 = require("./file-watching/file-change-events");
50
- const get_registered_sync_generators_1 = require("../message-types/get-registered-sync-generators");
51
- const handle_get_registered_sync_generators_1 = require("./handle-get-registered-sync-generators");
52
- const update_workspace_context_1 = require("../message-types/update-workspace-context");
53
- const handle_update_workspace_context_1 = require("./handle-update-workspace-context");
54
- const flush_sync_generator_changes_to_disk_1 = require("../message-types/flush-sync-generator-changes-to-disk");
55
- const handle_flush_sync_generator_changes_to_disk_1 = require("./handle-flush-sync-generator-changes-to-disk");
56
- const run_tasks_execution_hooks_1 = require("../message-types/run-tasks-execution-hooks");
57
- const handle_tasks_execution_hooks_1 = require("./handle-tasks-execution-hooks");
58
- const register_project_graph_listener_1 = require("../message-types/register-project-graph-listener");
59
- const nx_console_1 = require("../message-types/nx-console");
60
- const handle_nx_console_1 = require("./handle-nx-console");
61
- const configure_ai_agents_1 = require("../message-types/configure-ai-agents");
62
- const handle_configure_ai_agents_1 = require("./handle-configure-ai-agents");
63
- const v8_1 = require("v8");
65
+ const watcher_1 = require("./watcher");
64
66
  let workspaceWatcherError;
65
67
  let outputsWatcherError;
66
68
  global.NX_DAEMON = true;
69
+ process.env.NX_DAEMON_PROCESS = 'true';
67
70
  let numberOfOpenConnections = 0;
68
71
  exports.openSockets = new Set();
69
72
  const server = (0, net_1.createServer)(async (socket) => {
@@ -112,6 +115,21 @@ async function handleMessage(socket, data) {
112
115
  await (0, shutdown_utils_1.respondWithErrorAndExit)(socket, `Invalid payload from the client`, new Error(`Unsupported payload sent to daemon server: ${unparsedPayload}`));
113
116
  }
114
117
  logger_1.serverLogger.log(`Received ${mode} message of type ${payload.type}`);
118
+ if ((0, daemon_message_1.isDaemonMessage)(payload) && payload.env) {
119
+ let shouldRecomputeGraph = false;
120
+ for (const key in payload.env) {
121
+ if (process.env[key] !== payload.env[key]) {
122
+ logger_1.serverLogger.log(`Refreshing env var ${key} from client connection.`);
123
+ process.env[key] = payload.env[key];
124
+ shouldRecomputeGraph = true;
125
+ }
126
+ }
127
+ if (shouldRecomputeGraph) {
128
+ logger_1.serverLogger.log('Graph recompute necessary due to env variable refresh');
129
+ forwardEnvToPluginWorkers(payload.env);
130
+ (0, project_graph_incremental_recomputation_1.invalidateGraphCache)();
131
+ }
132
+ }
115
133
  if (payload.type === 'PING') {
116
134
  await handleResult(socket, 'PING', () => Promise.resolve({ response: true, description: 'ping' }), mode);
117
135
  }
@@ -124,11 +142,11 @@ async function handleMessage(socket, data) {
124
142
  else if (payload.type === 'PROCESS_IN_BACKGROUND') {
125
143
  await handleResult(socket, 'PROCESS_IN_BACKGROUND', () => (0, handle_process_in_background_1.handleProcessInBackground)(payload), mode);
126
144
  }
127
- else if (payload.type === 'RECORD_OUTPUTS_HASH') {
128
- await handleResult(socket, 'RECORD_OUTPUTS_HASH', () => (0, handle_outputs_tracking_1.handleRecordOutputsHash)(payload), mode);
145
+ else if (payload.type === 'RECORD_OUTPUTS_HASH_BATCH') {
146
+ await handleResult(socket, 'RECORD_OUTPUTS_HASH_BATCH', () => (0, handle_outputs_tracking_1.handleRecordOutputsHashBatch)(payload), mode);
129
147
  }
130
- else if (payload.type === 'OUTPUTS_HASHES_MATCH') {
131
- await handleResult(socket, 'OUTPUTS_HASHES_MATCH', () => (0, handle_outputs_tracking_1.handleOutputsHashesMatch)(payload), mode);
148
+ else if (payload.type === 'OUTPUTS_HASHES_MATCH_BATCH') {
149
+ await handleResult(socket, 'OUTPUTS_HASHES_MATCH_BATCH', () => (0, handle_outputs_tracking_1.handleOutputsHashesMatchBatch)(payload), mode);
132
150
  }
133
151
  else if (payload.type === 'REQUEST_SHUTDOWN') {
134
152
  await handleResult(socket, 'REQUEST_SHUTDOWN', () => (0, handle_request_shutdown_1.handleRequestShutdown)(server, numberOfOpenConnections), mode);
@@ -477,6 +495,19 @@ async function startServer() {
477
495
  }
478
496
  });
479
497
  }
498
+ function forwardEnvToPluginWorkers(env) {
499
+ (0, get_plugins_1.getPlugins)()
500
+ .then((plugins) => {
501
+ for (const plugin of plugins) {
502
+ plugin.setWorkerEnv?.(env)?.catch((e) => {
503
+ logger_1.serverLogger.log(`Failed to forward env to plugin worker "${plugin.name}": ${e.message}`);
504
+ });
505
+ }
506
+ })
507
+ .catch(() => {
508
+ // Plugins may not be loaded yet — env will be picked up on next load
509
+ });
510
+ }
480
511
  function serializeUnserializedResult(response, mode) {
481
512
  if (mode === 'json') {
482
513
  return JSON.stringify(response);
@@ -12,7 +12,6 @@ const pseudo_terminal_1 = require("../../tasks-runner/pseudo-terminal");
12
12
  const task_env_1 = require("../../tasks-runner/task-env");
13
13
  const task_io_service_1 = require("../../tasks-runner/task-io-service");
14
14
  const exit_codes_1 = require("../../utils/exit-codes");
15
- const run_commands_impl_1 = require("./run-commands.impl");
16
15
  class ParallelRunningTasks {
17
16
  constructor(options, context, taskId) {
18
17
  this.exitCallbacks = [];
@@ -244,8 +243,8 @@ class RunningNodeProcess {
244
243
  if (streamOutput) {
245
244
  process.stdout.write(header);
246
245
  }
247
- this.childProcess = (0, child_process_1.exec)(commandConfig.command, {
248
- maxBuffer: run_commands_impl_1.LARGE_BUFFER,
246
+ this.childProcess = (0, child_process_1.spawn)(commandConfig.command, [], {
247
+ shell: true,
249
248
  env,
250
249
  cwd,
251
250
  windowsHide: true,
@@ -7,7 +7,6 @@ const path = tslib_1.__importStar(require("path"));
7
7
  const tree_kill_1 = tslib_1.__importDefault(require("tree-kill"));
8
8
  const pseudo_terminal_1 = require("../../tasks-runner/pseudo-terminal");
9
9
  const package_manager_1 = require("../../utils/package-manager");
10
- const LARGE_BUFFER = 1024 * 1000000;
11
10
  async function default_1(options, context) {
12
11
  const pm = (0, package_manager_1.getPackageManagerCommand)();
13
12
  try {
@@ -35,17 +34,26 @@ async function default_1(options, context) {
35
34
  }
36
35
  function nodeProcess(command, cwd, env) {
37
36
  return new Promise((res, rej) => {
38
- let cp = (0, child_process_1.exec)(command, { cwd, env, maxBuffer: LARGE_BUFFER, windowsHide: true }, (error) => {
39
- if (error) {
40
- rej(error);
41
- }
42
- else {
43
- res();
44
- }
37
+ let cp = (0, child_process_1.spawn)(command, [], {
38
+ shell: true,
39
+ cwd,
40
+ env,
41
+ windowsHide: true,
45
42
  });
46
43
  // Forward stdout/stderr to parent process
47
44
  cp.stdout.pipe(process.stdout);
48
45
  cp.stderr.pipe(process.stderr);
46
+ cp.on('error', (error) => {
47
+ rej(error);
48
+ });
49
+ cp.on('exit', (code) => {
50
+ if (code === 0) {
51
+ res();
52
+ }
53
+ else {
54
+ rej(new Error(`Command "${command}" exited with non-zero status code`));
55
+ }
56
+ });
49
57
  const exitHandler = (signal) => {
50
58
  if (cp && cp.pid && !cp.killed) {
51
59
  (0, tree_kill_1.default)(cp.pid, signal, (error) => {
@@ -6,4 +6,12 @@ import { TaskHasher } from './task-hasher';
6
6
  export declare function getTaskDetails(): TaskDetails | null;
7
7
  export declare function hashTasksThatDoNotDependOnOutputsOfOtherTasks(hasher: TaskHasher, projectGraph: ProjectGraph, taskGraph: TaskGraph, nxJson: NxJsonConfiguration, tasksDetails: TaskDetails | null): Promise<void>;
8
8
  export declare function hashTask(hasher: TaskHasher, projectGraph: ProjectGraph, taskGraph: TaskGraph, task: Task, env: NodeJS.ProcessEnv, taskDetails: TaskDetails | null): Promise<void>;
9
- export declare function hashTasks(hasher: TaskHasher, projectGraph: ProjectGraph, taskGraph: TaskGraph, env: NodeJS.ProcessEnv, taskDetails: TaskDetails | null, tasksToHashOverride?: Task[]): Promise<void>;
9
+ /**
10
+ * Batch-hash `tasks`. `perTaskEnvs` must contain an entry keyed by
11
+ * `task.id` for every task — the per-task env is what each task's
12
+ * custom hasher sees and what the built-in hasher reads
13
+ * `HashInstruction::Environment` inputs against. Callers that
14
+ * genuinely want to hash against a single shared env should build
15
+ * `{ [task.id]: env }` for every task.
16
+ */
17
+ export declare function hashTasks(hasher: TaskHasher, projectGraph: ProjectGraph, taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, taskDetails: TaskDetails | null, tasksToHashOverride?: Task[]): Promise<void>;
@@ -8,6 +8,7 @@ const nx_json_1 = require("../config/nx-json");
8
8
  const native_1 = require("../native");
9
9
  const project_graph_1 = require("../project-graph/project-graph");
10
10
  const task_io_service_1 = require("../tasks-runner/task-io-service");
11
+ const task_env_1 = require("../tasks-runner/task-env");
11
12
  const utils_1 = require("../tasks-runner/utils");
12
13
  const db_connection_1 = require("../utils/db-connection");
13
14
  const task_hasher_1 = require("./task-hasher");
@@ -24,9 +25,10 @@ function getTaskDetails() {
24
25
  }
25
26
  async function hashTasksThatDoNotDependOnOutputsOfOtherTasks(hasher, projectGraph, taskGraph, nxJson, tasksDetails) {
26
27
  performance.mark('hashMultipleTasks:start');
28
+ const projects = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph).projects;
27
29
  const tasks = Object.values(taskGraph.tasks);
28
30
  const tasksWithHashers = await Promise.all(tasks.map(async (task) => {
29
- const customHasher = (0, utils_1.getCustomHasher)(task, projectGraph);
31
+ const customHasher = (0, utils_1.getCustomHasher)(task, projects);
30
32
  return { task, customHasher };
31
33
  }));
32
34
  const tasksToHash = tasksWithHashers
@@ -39,7 +41,11 @@ async function hashTasksThatDoNotDependOnOutputsOfOtherTasks(hasher, projectGrap
39
41
  (0, task_hasher_1.getInputs)(task, projectGraph, nxJson).depsOutputs.length > 0);
40
42
  })
41
43
  .map((t) => t.task);
42
- const hashes = await hasher.hashTasks(tasksToHash, taskGraph, process.env);
44
+ const perTaskEnvs = {};
45
+ for (const task of tasksToHash) {
46
+ perTaskEnvs[task.id] = (0, task_env_1.getTaskSpecificEnv)(task, projectGraph);
47
+ }
48
+ const hashes = await hasher.hashTasks(tasksToHash, taskGraph, perTaskEnvs);
43
49
  const ioService = (0, task_io_service_1.getTaskIOService)();
44
50
  const hasInputSubscribers = ioService.hasTaskInputSubscribers();
45
51
  for (let i = 0; i < tasksToHash.length; i++) {
@@ -63,8 +69,8 @@ async function hashTasksThatDoNotDependOnOutputsOfOtherTasks(hasher, projectGrap
63
69
  }
64
70
  async function hashTask(hasher, projectGraph, taskGraph, task, env, taskDetails) {
65
71
  performance.mark('hashSingleTask:start');
66
- const customHasher = (0, utils_1.getCustomHasher)(task, projectGraph);
67
72
  const projectsConfigurations = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
73
+ const customHasher = (0, utils_1.getCustomHasher)(task, projectsConfigurations.projects);
68
74
  const { value, details, inputs } = await (customHasher
69
75
  ? customHasher(task, {
70
76
  hasher,
@@ -96,7 +102,15 @@ async function hashTask(hasher, projectGraph, taskGraph, task, env, taskDetails)
96
102
  performance.mark('hashSingleTask:end');
97
103
  performance.measure('hashSingleTask', 'hashSingleTask:start', 'hashSingleTask:end');
98
104
  }
99
- async function hashTasks(hasher, projectGraph, taskGraph, env, taskDetails, tasksToHashOverride) {
105
+ /**
106
+ * Batch-hash `tasks`. `perTaskEnvs` must contain an entry keyed by
107
+ * `task.id` for every task — the per-task env is what each task's
108
+ * custom hasher sees and what the built-in hasher reads
109
+ * `HashInstruction::Environment` inputs against. Callers that
110
+ * genuinely want to hash against a single shared env should build
111
+ * `{ [task.id]: env }` for every task.
112
+ */
113
+ async function hashTasks(hasher, projectGraph, taskGraph, perTaskEnvs, taskDetails, tasksToHashOverride) {
100
114
  performance.mark('hashMultipleTasks:start');
101
115
  const projectsConfigurations = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
102
116
  const nxJson = (0, nx_json_1.readNxJson)();
@@ -105,7 +119,7 @@ async function hashTasks(hasher, projectGraph, taskGraph, env, taskDetails, task
105
119
  const tasksWithCustomHashers = [];
106
120
  const tasksWithoutCustomHashers = [];
107
121
  for (const task of tasks) {
108
- const customHasher = (0, utils_1.getCustomHasher)(task, projectGraph);
122
+ const customHasher = (0, utils_1.getCustomHasher)(task, projectsConfigurations.projects);
109
123
  if (customHasher) {
110
124
  tasksWithCustomHashers.push(task);
111
125
  }
@@ -117,7 +131,7 @@ async function hashTasks(hasher, projectGraph, taskGraph, env, taskDetails, task
117
131
  const ioService = (0, task_io_service_1.getTaskIOService)();
118
132
  const hasInputSubscribers = ioService.hasTaskInputSubscribers();
119
133
  const customHasherPromises = tasksWithCustomHashers.map(async (task) => {
120
- const customHasher = (0, utils_1.getCustomHasher)(task, projectGraph);
134
+ const customHasher = (0, utils_1.getCustomHasher)(task, projectsConfigurations.projects);
121
135
  const { value, details, inputs } = await customHasher(task, {
122
136
  hasher,
123
137
  projectGraph,
@@ -125,7 +139,7 @@ async function hashTasks(hasher, projectGraph, taskGraph, env, taskDetails, task
125
139
  workspaceConfig: projectsConfigurations,
126
140
  projectsConfigurations,
127
141
  nxJsonConfiguration: nxJson,
128
- env,
142
+ env: perTaskEnvs[task.id],
129
143
  });
130
144
  task.hash = value;
131
145
  task.hashDetails = details;
@@ -138,7 +152,7 @@ async function hashTasks(hasher, projectGraph, taskGraph, env, taskDetails, task
138
152
  let batchHashPromise = Promise.resolve();
139
153
  if (tasksWithoutCustomHashers.length > 0) {
140
154
  batchHashPromise = hasher
141
- .hashTasks(tasksWithoutCustomHashers, taskGraph, env)
155
+ .hashTasks(tasksWithoutCustomHashers, taskGraph, perTaskEnvs)
142
156
  .then((hashes) => {
143
157
  for (let i = 0; i < tasksWithoutCustomHashers.length; i++) {
144
158
  tasksWithoutCustomHashers[i].hash = hashes[i].value;
@@ -152,12 +166,25 @@ async function hashTasks(hasher, projectGraph, taskGraph, env, taskDetails, task
152
166
  }
153
167
  await Promise.all([...customHasherPromises, batchHashPromise]);
154
168
  if (taskDetails?.recordTaskDetails) {
155
- taskDetails.recordTaskDetails(tasks.map((task) => ({
156
- hash: task.hash,
157
- project: task.target.project,
158
- target: task.target.target,
159
- configuration: task.target.configuration,
160
- })));
169
+ // Guard against a custom hasher resolving with a falsy value —
170
+ // the built-in batch hasher always produces a hash, but user-written
171
+ // custom hashers are untrusted and an empty/undefined hash would
172
+ // violate the task_details schema downstream.
173
+ const hashedTasks = [];
174
+ for (const t of tasks) {
175
+ if (!t.hash) {
176
+ continue;
177
+ }
178
+ hashedTasks.push({
179
+ hash: t.hash,
180
+ project: t.target.project,
181
+ target: t.target.target,
182
+ configuration: t.target.configuration,
183
+ });
184
+ }
185
+ if (hashedTasks.length > 0) {
186
+ taskDetails.recordTaskDetails(hashedTasks);
187
+ }
161
188
  }
162
189
  performance.mark('hashMultipleTasks:end');
163
190
  performance.measure('hashMultipleTasks', 'hashMultipleTasks:start', 'hashMultipleTasks:end');
@@ -14,5 +14,5 @@ export declare class NativeTaskHasherImpl implements TaskHasherImpl {
14
14
  selectivelyHashTsConfig: boolean;
15
15
  });
16
16
  hashTask(task: Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string, collectInputs?: boolean): Promise<PartialHash>;
17
- hashTasks(tasks: Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string, collectInputs?: boolean): Promise<PartialHash[]>;
17
+ hashTasks(tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd?: string, collectInputs?: boolean): Promise<PartialHash[]>;
18
18
  }
@@ -25,15 +25,13 @@ class NativeTaskHasherImpl {
25
25
  this.hasher = new native_1.TaskHasher(workspaceRoot, this.projectGraphRef, this.projectFileMapRef, this.allWorkspaceFilesRef, Buffer.from(JSON.stringify(tsconfig)), paths, rootTsConfigPath, options);
26
26
  }
27
27
  async hashTask(task, taskGraph, env, cwd, collectInputs) {
28
- const plans = this.planner.getPlansReference([task.id], taskGraph);
29
- const shouldCollectInputs = collectInputs ?? (0, task_io_service_1.getTaskIOService)().hasTaskInputSubscribers();
30
- const hashes = this.hasher.hashPlans(plans, env, cwd ?? process.cwd(), shouldCollectInputs);
31
- return hashes[task.id];
28
+ const hashes = await this.hashTasks([task], taskGraph, { [task.id]: env }, cwd, collectInputs);
29
+ return hashes[0];
32
30
  }
33
- async hashTasks(tasks, taskGraph, env, cwd, collectInputs) {
31
+ async hashTasks(tasks, taskGraph, perTaskEnvs, cwd, collectInputs) {
34
32
  const plans = this.planner.getPlansReference(tasks.map((t) => t.id), taskGraph);
35
33
  const shouldCollectInputs = collectInputs ?? (0, task_io_service_1.getTaskIOService)().hasTaskInputSubscribers();
36
- const hashes = this.hasher.hashPlans(plans, env, cwd ?? process.cwd(), shouldCollectInputs);
34
+ const hashes = this.hasher.hashPlans(plans, perTaskEnvs, cwd ?? process.cwd(), shouldCollectInputs);
37
35
  return tasks.map((t) => hashes[t.id]);
38
36
  }
39
37
  }
@@ -46,18 +46,29 @@ export interface TaskHasher {
46
46
  hashTask(task: Task, taskGraph: TaskGraph): Promise<Hash>;
47
47
  hashTask(task: Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string): Promise<Hash>;
48
48
  /**
49
- * @deprecated use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
50
- * @param tasks
49
+ * @deprecated pass `perTaskEnvs` keyed by `task.id` instead hashing
50
+ * every task against one shared env produces the wrong cache key when
51
+ * tasks have per-project/target `.env` files or custom hashers that
52
+ * read env. Will be removed in v22.
51
53
  */
52
- hashTasks(tasks: Task[]): Promise<Hash[]>;
54
+ hashTasks(tasks: Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string): Promise<Hash[]>;
53
55
  /**
54
- * @deprecated use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
56
+ * Hash `tasks`. `perTaskEnvs` must contain an entry keyed by `task.id`
57
+ * for every task in `tasks` — task-specific env (per-project/target
58
+ * `.env` files, custom-hasher env reads) participates in the hash, so
59
+ * a shared env across tasks would compute the wrong cache key when
60
+ * tasks actually differ.
55
61
  */
56
- hashTasks(tasks: Task[], taskGraph: TaskGraph): Promise<Hash[]>;
57
- hashTasks(tasks: Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string): Promise<Hash[]>;
62
+ hashTasks(tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd?: string): Promise<Hash[]>;
58
63
  }
59
64
  export interface TaskHasherImpl {
60
- hashTasks(tasks: Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string, collectInputs?: boolean): Promise<PartialHash[]>;
65
+ /**
66
+ * Hash `tasks` where each task is keyed in `perTaskEnvs` by `task.id`.
67
+ * Every task must have an entry — callers who want to hash against a
68
+ * single shared env should construct `{ [task.id]: env }` for every
69
+ * task.
70
+ */
71
+ hashTasks(tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd?: string, collectInputs?: boolean): Promise<PartialHash[]>;
61
72
  hashTask(task: Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd?: string, collectInputs?: boolean): Promise<PartialHash>;
62
73
  }
63
74
  export type Hasher = TaskHasher;
@@ -65,7 +76,7 @@ export declare class DaemonBasedTaskHasher implements TaskHasher {
65
76
  private readonly daemonClient;
66
77
  private readonly runnerOptions;
67
78
  constructor(daemonClient: DaemonClient, runnerOptions: any);
68
- hashTasks(tasks: Task[], taskGraph?: TaskGraph, env?: NodeJS.ProcessEnv): Promise<Hash[]>;
79
+ hashTasks(tasks: Task[], taskGraph: TaskGraph, envOrPerTaskEnvs: NodeJS.ProcessEnv | Record<string, NodeJS.ProcessEnv>): Promise<Hash[]>;
69
80
  hashTask(task: Task, taskGraph?: TaskGraph, env?: NodeJS.ProcessEnv): Promise<Hash>;
70
81
  }
71
82
  export declare class InProcessTaskHasher implements TaskHasher {
@@ -75,7 +86,7 @@ export declare class InProcessTaskHasher implements TaskHasher {
75
86
  private readonly options;
76
87
  private taskHasher;
77
88
  constructor(projectGraph: ProjectGraph, nxJson: NxJsonConfiguration, externalRustReferences: NxWorkspaceFilesExternals | null, options: any);
78
- hashTasks(tasks: Task[], taskGraph?: TaskGraph, env?: NodeJS.ProcessEnv, cwd?: string, collectInputs?: boolean): Promise<Hash[]>;
89
+ hashTasks(tasks: Task[], taskGraph: TaskGraph, envOrPerTaskEnvs: NodeJS.ProcessEnv | Record<string, NodeJS.ProcessEnv>, cwd?: string, collectInputs?: boolean): Promise<Hash[]>;
79
90
  hashTask(task: Task, taskGraph?: TaskGraph, env?: NodeJS.ProcessEnv, cwd?: string, collectInputs?: boolean): Promise<Hash>;
80
91
  private createHashDetails;
81
92
  private hashCommand;
@@ -16,18 +16,46 @@ const minimatch_1 = require("minimatch");
16
16
  const native_task_hasher_impl_1 = require("./native-task-hasher-impl");
17
17
  const workspace_root_1 = require("../utils/workspace-root");
18
18
  const task_io_service_1 = require("../tasks-runner/task-io-service");
19
+ /**
20
+ * Normalize the legacy single-env `hashTasks(tasks, taskGraph, env)`
21
+ * signature to the per-task-env shape. External plugins still call the
22
+ * legacy shape, so `InProcessTaskHasher` and `DaemonBasedTaskHasher`
23
+ * detect at runtime and broadcast the shared env across every task.
24
+ *
25
+ * Detection is by value shape: `NodeJS.ProcessEnv` values are strings
26
+ * (or undefined), `perTaskEnvs` values are objects.
27
+ */
28
+ function normalizePerTaskEnvs(tasks, arg) {
29
+ for (const value of Object.values(arg)) {
30
+ if (value === undefined)
31
+ continue;
32
+ if (typeof value === 'object') {
33
+ return arg;
34
+ }
35
+ // First defined value is a string — legacy env; broadcast it.
36
+ const env = arg;
37
+ const perTaskEnvs = {};
38
+ for (const task of tasks)
39
+ perTaskEnvs[task.id] = env;
40
+ return perTaskEnvs;
41
+ }
42
+ // Empty or all-undefined: treat as perTaskEnvs — safe because the
43
+ // Rust-side check will surface a clear error if entries are missing.
44
+ return arg;
45
+ }
19
46
  class DaemonBasedTaskHasher {
20
47
  constructor(daemonClient, runnerOptions) {
21
48
  this.daemonClient = daemonClient;
22
49
  this.runnerOptions = runnerOptions;
23
50
  }
24
- async hashTasks(tasks, taskGraph, env) {
51
+ async hashTasks(tasks, taskGraph, envOrPerTaskEnvs) {
25
52
  const collectInputs = (0, task_io_service_1.getTaskIOService)().hasTaskInputSubscribers();
26
- return this.daemonClient.hashTasks(this.runnerOptions, tasks, taskGraph, env ?? process.env, process.cwd(), collectInputs);
53
+ return this.daemonClient.hashTasks(this.runnerOptions, tasks, taskGraph, normalizePerTaskEnvs(tasks, envOrPerTaskEnvs), process.cwd(), collectInputs);
27
54
  }
28
55
  async hashTask(task, taskGraph, env) {
29
- const collectInputs = (0, task_io_service_1.getTaskIOService)().hasTaskInputSubscribers();
30
- return (await this.daemonClient.hashTasks(this.runnerOptions, [task], taskGraph, env ?? process.env, process.cwd(), collectInputs))[0];
56
+ return (await this.hashTasks([task], taskGraph, {
57
+ [task.id]: env ?? process.env,
58
+ }))[0];
31
59
  }
32
60
  }
33
61
  exports.DaemonBasedTaskHasher = DaemonBasedTaskHasher;
@@ -41,8 +69,8 @@ class InProcessTaskHasher {
41
69
  selectivelyHashTsConfig: this.options?.selectivelyHashTsConfig ?? false,
42
70
  });
43
71
  }
44
- async hashTasks(tasks, taskGraph, env, cwd, collectInputs) {
45
- const hashes = await this.taskHasher.hashTasks(tasks, taskGraph, env ?? process.env, cwd ?? process.cwd(), collectInputs);
72
+ async hashTasks(tasks, taskGraph, envOrPerTaskEnvs, cwd, collectInputs) {
73
+ const hashes = await this.taskHasher.hashTasks(tasks, taskGraph, normalizePerTaskEnvs(tasks, envOrPerTaskEnvs), cwd ?? process.cwd(), collectInputs);
46
74
  return tasks.map((task, index) => this.createHashDetails(task, hashes[index]));
47
75
  }
48
76
  async hashTask(task, taskGraph, env, cwd, collectInputs) {
@@ -55,7 +55,9 @@ export declare class HashPlanInspector {
55
55
  * Like `inspect()` but returns structured `HashInputs` objects instead of flat strings.
56
56
  * Each `HashInstruction` is categorized into the appropriate bucket (files, runtime,
57
57
  * environment, depOutputs, external). TsConfiguration is resolved to the root tsconfig
58
- * file path. ProjectConfiguration is skipped for now. Cwd is skipped as it's ambient.
58
+ * file path. JsonFileSet is resolved to the matched JSON file paths (field/excludeField
59
+ * filters only affect hashing, not which files are reported as inputs).
60
+ * ProjectConfiguration is skipped for now. Cwd is skipped as it's ambient.
59
61
  */
60
62
  inspectInputs(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>): Record<string, HashInputs>
61
63
  }
@@ -83,6 +85,11 @@ export declare class NxCache {
83
85
  cacheDirectory: string
84
86
  constructor(workspaceRoot: string, cachePath: string, dbConnection: ExternalObject<NxDbConnection>, linkTaskDetails?: boolean | undefined | null, maxCacheSize?: number | undefined | null)
85
87
  get(hash: string): CachedResult | null
88
+ /**
89
+ * Batch version of get() that fetches multiple cache entries in a single
90
+ * SQL query and reads terminal output files in parallel via Rayon.
91
+ */
92
+ getBatch(hashes: Array<string>): Array<CachedResult | undefined | null>
86
93
  put(hash: string, terminalOutput: string, outputs: Array<string>, code: number): Array<string>
87
94
  applyRemoteCacheResults(hash: string, result: CachedResult, outputs?: Array<string> | undefined | null): void
88
95
  getTaskOutputsPath(hash: string): string
@@ -168,7 +175,15 @@ export declare class TaskDetails {
168
175
 
169
176
  export declare class TaskHasher {
170
177
  constructor(workspaceRoot: string, projectGraph: ExternalObject<ProjectGraph>, projectFileMap: ExternalObject<ProjectFiles>, allWorkspaceFiles: ExternalObject<Array<FileData>>, tsConfig: Buffer, tsConfigPaths: Record<string, Array<string>>, rootTsconfigPath?: string | undefined | null, options?: HasherOptions | undefined | null)
171
- hashPlans(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>, jsEnv: Record<string, string>, cwd: string, collectTaskInputs?: boolean | undefined | null): NapiDashMap<string, HashDetails>
178
+ /**
179
+ * Hash each task's instructions using the env map keyed by `task.id`.
180
+ * Every task in `hash_plans` must have an entry in `per_task_envs` —
181
+ * a missing id surfaces as an error rather than silently hashing
182
+ * against an empty env. Callers that want to hash all tasks against
183
+ * the same env should build `per_task_envs` by keying that env under
184
+ * every task id.
185
+ */
186
+ hashPlans(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>, perTaskEnvs: Record<string, Record<string, string>>, cwd: string, collectTaskInputs?: boolean | undefined | null): NapiDashMap<string, HashDetails>
172
187
  }
173
188
 
174
189
  export declare class Watcher {
@@ -238,7 +253,7 @@ export interface DepsOutputsInput {
238
253
 
239
254
  /**
240
255
  * Detects which AI agent is running and returns its name.
241
- * Returns None if no agent is detected.
256
+ * Returns None if no agent is detected or when running inside the Nx daemon.
242
257
  * Filtering against supported agents should be done on the TypeScript side.
243
258
  */
244
259
  export declare function detectAiAgent(): string | null
@@ -311,10 +326,11 @@ export declare function getDefaultMaxCacheSize(cachePath: string): number
311
326
  export declare function getEventDimensions(): EventDimensions
312
327
 
313
328
  /**
314
- * Expands the given outputs into a list of existing files.
315
- * This is used when hashing outputs
329
+ * Batch version of get_files_for_outputs that processes multiple output
330
+ * entries in parallel using Rayon. Each entry is a list of output paths
331
+ * for a single task.
316
332
  */
317
- export declare function getFilesForOutputs(directory: string, entries: Array<string>): Array<string>
333
+ export declare function getFilesForOutputsBatch(directory: string, entriesBatch: Array<Array<string>>): Array<Array<string>>
318
334
 
319
335
  /**
320
336
  * If `workspace_root` is inside a git worktree, returns the main repo root.
@@ -413,11 +429,22 @@ export declare function installNxConsoleForEditor(editor: SupportedEditor): Prom
413
429
 
414
430
  export const IS_WASM: boolean
415
431
 
416
- /** Detects if the current process is being run by an AI agent */
432
+ /**
433
+ * Detects if the current process is being run by an AI agent.
434
+ * Always returns false when running inside the Nx daemon, since the daemon
435
+ * is a long-lived process that should not inherit AI agent behavior from
436
+ * the client that connected to it.
437
+ */
417
438
  export declare function isAiAgent(): boolean
418
439
 
419
440
  export declare function isEditorInstalled(editor: SupportedEditor): Promise<boolean>
420
441
 
442
+ export interface JsonInput {
443
+ json: string
444
+ fields?: Array<string>
445
+ excludeFields?: Array<string>
446
+ }
447
+
421
448
  export declare function logDebug(message: string): void
422
449
 
423
450
  /** Combined metadata for groups and processes */
@@ -607,7 +607,7 @@ module.exports.flushTelemetry = nativeBinding.flushTelemetry
607
607
  module.exports.getBinaryTarget = nativeBinding.getBinaryTarget
608
608
  module.exports.getDefaultMaxCacheSize = nativeBinding.getDefaultMaxCacheSize
609
609
  module.exports.getEventDimensions = nativeBinding.getEventDimensions
610
- module.exports.getFilesForOutputs = nativeBinding.getFilesForOutputs
610
+ module.exports.getFilesForOutputsBatch = nativeBinding.getFilesForOutputsBatch
611
611
  module.exports.getMainWorktreeRoot = nativeBinding.getMainWorktreeRoot
612
612
  module.exports.getTransformableOutputs = nativeBinding.getTransformableOutputs
613
613
  module.exports.GroupType = nativeBinding.GroupType
@@ -117,7 +117,6 @@ export const copy = __napiModule.exports.copy
117
117
  export const expandOutputs = __napiModule.exports.expandOutputs
118
118
  export const findImports = __napiModule.exports.findImports
119
119
  export const getBinaryTarget = __napiModule.exports.getBinaryTarget
120
- export const getFilesForOutputs = __napiModule.exports.getFilesForOutputs
121
120
  export const getTransformableOutputs = __napiModule.exports.getTransformableOutputs
122
121
  export const hashArray = __napiModule.exports.hashArray
123
122
  export const hashFile = __napiModule.exports.hashFile
@@ -148,7 +148,6 @@ module.exports.copy = __napiModule.exports.copy
148
148
  module.exports.expandOutputs = __napiModule.exports.expandOutputs
149
149
  module.exports.findImports = __napiModule.exports.findImports
150
150
  module.exports.getBinaryTarget = __napiModule.exports.getBinaryTarget
151
- module.exports.getFilesForOutputs = __napiModule.exports.getFilesForOutputs
152
151
  module.exports.getTransformableOutputs = __napiModule.exports.getTransformableOutputs
153
152
  module.exports.hashArray = __napiModule.exports.hashArray
154
153
  module.exports.hashFile = __napiModule.exports.hashFile
Binary file