nx 23.3.0 → 24.0.0-beta.1

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 (114) hide show
  1. package/README.md +3 -6
  2. package/dist/nx-cloud-internals.d.ts +1 -0
  3. package/dist/nx-cloud-internals.js +7 -1
  4. package/dist/src/adapter/ngcli-adapter.js +1 -0
  5. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +0 -12
  6. package/dist/src/command-line/graph/graph.js +3 -5
  7. package/dist/src/command-line/init/command-object.js +0 -12
  8. package/dist/src/command-line/init/init-v2.js +0 -2
  9. package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +1 -1
  10. package/dist/src/command-line/migrate/agentic/close-agent-session.js +4 -3
  11. package/dist/src/command-line/migrate/agentic/master/spawn-master.js +76 -15
  12. package/dist/src/command-line/migrate/execute-migration.d.ts +2 -2
  13. package/dist/src/command-line/migrate/execute-migration.js +18 -10
  14. package/dist/src/command-line/migrate/migrate-commits.d.ts +4 -1
  15. package/dist/src/command-line/migrate/migrate-commits.js +10 -3
  16. package/dist/src/command-line/migrate/migrate.js +43 -6
  17. package/dist/src/command-line/migrate/run/agent-output.d.ts +1 -0
  18. package/dist/src/command-line/migrate/run/agent-output.js +5 -0
  19. package/dist/src/command-line/migrate/run/broker.d.ts +14 -5
  20. package/dist/src/command-line/migrate/run/broker.js +68 -20
  21. package/dist/src/command-line/migrate/run/index.d.ts +4 -3
  22. package/dist/src/command-line/migrate/run/index.js +8 -1
  23. package/dist/src/command-line/migrate/run/orchestrator.js +72 -13
  24. package/dist/src/command-line/migrate/run/runbook.js +19 -0
  25. package/dist/src/command-line/migrate/run/state-machine.js +4 -1
  26. package/dist/src/command-line/migrate/run/util.d.ts +3 -0
  27. package/dist/src/command-line/migrate/run/util.js +7 -1
  28. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +17 -12
  29. package/dist/src/command-line/release/utils/remote-release-clients/github.js +5 -6
  30. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +5 -6
  31. package/dist/src/command-line/run/executor-utils.js +1 -0
  32. package/dist/src/command-line/run/run-one.d.ts +20 -1
  33. package/dist/src/command-line/run/run-one.js +153 -5
  34. package/dist/src/command-line/show/show-target/info.js +1 -1
  35. package/dist/src/command-line/show/show-target/utils.js +1 -0
  36. package/dist/src/command-line/watch/command-object.js +3 -22
  37. package/dist/src/command-line/watch/watch.d.ts +0 -7
  38. package/dist/src/command-line/watch/watch.js +1 -1
  39. package/dist/src/config/misc-interfaces.d.ts +12 -0
  40. package/dist/src/config/task-graph.d.ts +1 -1
  41. package/dist/src/config/workspace-json-project-json.d.ts +65 -0
  42. package/dist/src/core/graph/main.js +1 -1
  43. package/dist/src/daemon/client/client.d.ts +9 -0
  44. package/dist/src/daemon/client/client.js +89 -37
  45. package/dist/src/daemon/server/shutdown-utils.js +0 -3
  46. package/dist/src/daemon/socket-utils.d.ts +9 -4
  47. package/dist/src/daemon/socket-utils.js +12 -49
  48. package/dist/src/daemon/tmp-dir.d.ts +19 -14
  49. package/dist/src/daemon/tmp-dir.js +142 -354
  50. package/dist/src/devkit-exports.d.ts +1 -1
  51. package/dist/src/devkit-internals.d.ts +1 -1
  52. package/dist/src/devkit-internals.js +1 -0
  53. package/dist/src/hasher/check-task-files.js +4 -4
  54. package/dist/src/hasher/file-hasher.js +9 -7
  55. package/dist/src/hasher/hash-task.js +4 -0
  56. package/dist/src/hasher/native-task-hasher-impl.js +3 -1
  57. package/dist/src/migrations/update-24-0-0/find-custom-hashers.d.ts +3 -0
  58. package/dist/src/migrations/update-24-0-0/find-custom-hashers.js +100 -0
  59. package/dist/src/migrations/update-24-0-0/find-custom-hashers.md +63 -0
  60. package/dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md +145 -0
  61. package/dist/src/native/index.d.ts +180 -12
  62. package/dist/src/native/native-bindings.js +8 -0
  63. package/dist/src/native/nx.wasi-browser.js +1 -0
  64. package/dist/src/native/nx.wasi.cjs +1 -0
  65. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  66. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  67. package/dist/src/nx-cloud/update-manager.d.ts +7 -0
  68. package/dist/src/nx-cloud/update-manager.js +237 -50
  69. package/dist/src/project-graph/affected/affected-tasks.js +1 -0
  70. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +7 -0
  71. package/dist/src/tasks-runner/create-task-graph.d.ts +8 -7
  72. package/dist/src/tasks-runner/create-task-graph.js +47 -26
  73. package/dist/src/tasks-runner/init-tasks-runner.js +13 -0
  74. package/dist/src/tasks-runner/life-cycle.d.ts +4 -1
  75. package/dist/src/tasks-runner/life-cycle.js +7 -0
  76. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +5 -2
  77. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +13 -7
  78. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.d.ts +4 -3
  79. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +9 -2
  80. package/dist/src/tasks-runner/readiness/probes.d.ts +9 -0
  81. package/dist/src/tasks-runner/readiness/probes.js +62 -0
  82. package/dist/src/tasks-runner/readiness/ready-when.d.ts +38 -0
  83. package/dist/src/tasks-runner/readiness/ready-when.js +190 -0
  84. package/dist/src/tasks-runner/run-command.js +5 -4
  85. package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -4
  86. package/dist/src/tasks-runner/task-env.d.ts +10 -0
  87. package/dist/src/tasks-runner/task-env.js +26 -5
  88. package/dist/src/tasks-runner/task-graph-utils.d.ts +9 -6
  89. package/dist/src/tasks-runner/task-graph-utils.js +17 -12
  90. package/dist/src/tasks-runner/task-orchestrator.d.ts +37 -8
  91. package/dist/src/tasks-runner/task-orchestrator.js +460 -76
  92. package/dist/src/tasks-runner/tasks-schedule.d.ts +18 -1
  93. package/dist/src/tasks-runner/tasks-schedule.js +64 -6
  94. package/dist/src/tasks-runner/utils.d.ts +20 -5
  95. package/dist/src/tasks-runner/utils.js +70 -35
  96. package/dist/src/ultracache/overrides.js +4 -0
  97. package/dist/src/utils/child-process.js +1 -0
  98. package/dist/src/utils/git-utils.d.ts +6 -1
  99. package/dist/src/utils/git-utils.js +26 -5
  100. package/dist/src/utils/owned-private-dir.d.ts +1 -17
  101. package/dist/src/utils/owned-private-dir.js +10 -62
  102. package/dist/src/utils/perf-hooks.d.ts +1 -0
  103. package/dist/src/utils/plugins/core-plugins.js +1 -1
  104. package/dist/src/utils/spawn-without-terminal.d.ts +27 -0
  105. package/dist/src/utils/spawn-without-terminal.js +110 -0
  106. package/dist/src/utils/string-similarity.d.ts +38 -0
  107. package/dist/src/utils/string-similarity.js +108 -0
  108. package/migrations.json +7 -0
  109. package/package.json +11 -20
  110. package/readme-template.md +3 -3
  111. package/schemas/nx-schema.json +70 -0
  112. package/schemas/project-schema.json +67 -0
  113. package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +0 -18
  114. package/dist/src/tasks-runner/legacy-depends-on-warning.js +0 -109
@@ -9,6 +9,7 @@ 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
11
  const task_env_1 = require("../tasks-runner/task-env");
12
+ // TODO(v25): remove custom hasher support.
12
13
  const utils_1 = require("../tasks-runner/utils");
13
14
  const db_connection_1 = require("../utils/db-connection");
14
15
  const task_hasher_1 = require("./task-hasher");
@@ -32,6 +33,7 @@ async function hashTasksThatDoNotDependOnOutputsOfOtherTasks(hasher, projectGrap
32
33
  ? new Set((0, native_1.getUltracacheDeferredTaskIds)(ultracacheConfigurations, taskGraph))
33
34
  : null;
34
35
  const tasks = Object.values(taskGraph.tasks);
36
+ // TODO(v25): remove custom hasher support.
35
37
  const tasksWithHashers = await Promise.all(tasks.map(async (task) => {
36
38
  const customHasher = (0, utils_1.getCustomHasher)(task, projects);
37
39
  return { task, customHasher };
@@ -81,6 +83,7 @@ function readsDependencyOutputs(task, taskGraph, projectGraph, nxJson) {
81
83
  async function hashTask(hasher, projectGraph, taskGraph, task, env, taskDetails) {
82
84
  performance.mark('hashSingleTask:start');
83
85
  const projectsConfigurations = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
86
+ // TODO(v25): remove custom hasher support.
84
87
  const customHasher = (0, utils_1.getCustomHasher)(task, projectsConfigurations.projects);
85
88
  const { value, details, inputs } = await (customHasher
86
89
  ? customHasher(task, {
@@ -126,6 +129,7 @@ async function hashTasks(hasher, projectGraph, taskGraph, perTaskEnvs, taskDetai
126
129
  const projectsConfigurations = (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph);
127
130
  const nxJson = (0, nx_json_1.readNxJson)();
128
131
  const tasks = (tasksToHashOverride ?? Object.values(taskGraph.tasks)).filter((task) => !task.hash);
132
+ // TODO(v25): remove custom hasher support.
129
133
  // Separate tasks with custom hashers from those without
130
134
  const tasksWithCustomHashers = [];
131
135
  const tasksWithoutCustomHashers = [];
@@ -98,7 +98,9 @@ function taskGraphFingerprint(taskGraph, ultracacheConfigurations) {
98
98
  ];
99
99
  for (const id of Object.keys(taskGraph.tasks).sort()) {
100
100
  const task = taskGraph.tasks[id];
101
- parts.push(id, task.target.project, task.target.target, task.target.configuration ?? '', (task.outputs ?? []).join(','), (taskGraph.dependencies[id] ?? []).join(','), (taskGraph.continuousDependencies[id] ?? []).join(','));
101
+ parts.push(id, task.target.project, task.target.target, task.target.configuration ?? '', (task.outputs ?? []).join(','), (taskGraph.dependencies[id] ?? []).map((edge) => edge.id).join(','), (taskGraph.continuousDependencies[id] ?? [])
102
+ .map((edge) => JSON.stringify([edge.id, edge.inputs === false]))
103
+ .join(','));
102
104
  }
103
105
  return (0, native_1.hashArray)(parts);
104
106
  }
@@ -0,0 +1,3 @@
1
+ import type { MigrationReturnObject } from '../../config/misc-interfaces';
2
+ import type { Tree } from '../../generators/tree';
3
+ export default function findCustomHashers(tree: Tree): Promise<MigrationReturnObject>;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = findCustomHashers;
4
+ const glob_1 = require("../../generators/utils/glob");
5
+ const json_1 = require("../../utils/json");
6
+ const path_1 = require("../../utils/path");
7
+ const node_path_1 = require("node:path");
8
+ const DOCS_URL = 'https://nx.dev/docs/kb/local-executors#replace-a-custom-hasher-with-inputs';
9
+ async function findCustomHashers(tree) {
10
+ const hits = new Set();
11
+ const malformed = new Set();
12
+ const unparseable = [];
13
+ const visited = new Set();
14
+ for (const packageJsonPath of await (0, glob_1.globAsync)(tree, ['**/package.json'])) {
15
+ if (packageJsonPath.split('/').includes('node_modules')) {
16
+ continue;
17
+ }
18
+ const content = tree.read(packageJsonPath, 'utf-8');
19
+ if (!content.includes('"executors"') && !content.includes('"builders"')) {
20
+ continue;
21
+ }
22
+ const packageJson = tryParse(content);
23
+ if (!packageJson) {
24
+ unparseable.push(packageJsonPath);
25
+ continue;
26
+ }
27
+ const packageRoot = node_path_1.posix.dirname(packageJsonPath);
28
+ // A nameless local plugin is referenced by its relative path.
29
+ const pluginName = packageJson.name ?? (packageRoot === '.' ? '.' : `./${packageRoot}`);
30
+ for (const field of [packageJson.executors, packageJson.builders]) {
31
+ if (typeof field !== 'string') {
32
+ continue;
33
+ }
34
+ const executorsJsonPath = (0, path_1.joinPathFragments)(packageRoot, field);
35
+ if (visited.has(executorsJsonPath) || !tree.exists(executorsJsonPath)) {
36
+ continue;
37
+ }
38
+ visited.add(executorsJsonPath);
39
+ const executorsJson = tryParse(tree.read(executorsJsonPath, 'utf-8'));
40
+ if (!executorsJson) {
41
+ unparseable.push(executorsJsonPath);
42
+ continue;
43
+ }
44
+ const entries = [];
45
+ for (const map of [executorsJson.executors, executorsJson.builders]) {
46
+ if (map === undefined) {
47
+ continue;
48
+ }
49
+ if (!isPlainObject(map)) {
50
+ unparseable.push(executorsJsonPath);
51
+ break;
52
+ }
53
+ entries.push(...Object.entries(map));
54
+ }
55
+ for (const [name, entry] of entries) {
56
+ if (typeof entry === 'string') {
57
+ continue;
58
+ }
59
+ if (!isPlainObject(entry) || !isValidHasher(entry.hasher)) {
60
+ malformed.add(`Entry "${name}" in "${executorsJsonPath}" is not a valid executor entry. Check it by hand for a "hasher".`);
61
+ continue;
62
+ }
63
+ if (entry.hasher === undefined) {
64
+ continue;
65
+ }
66
+ const hasherPath = (0, path_1.joinPathFragments)(node_path_1.posix.dirname(executorsJsonPath), entry.hasher);
67
+ hits.add(`Executor "${pluginName}:${name}" declares a custom hasher at "${hasherPath}" in "${executorsJsonPath}".`);
68
+ }
69
+ }
70
+ }
71
+ if (hits.size === 0 && malformed.size === 0 && unparseable.length === 0) {
72
+ return { skipAgentic: true };
73
+ }
74
+ const manualNotes = [
75
+ ...malformed,
76
+ ...unparseable.map((path) => `Could not parse "${path}". Check it by hand for executors that declare a "hasher".`),
77
+ ];
78
+ return {
79
+ nextSteps: [
80
+ `Custom hashers are deprecated and will be removed in Nx 25. Replace each one with target inputs, then remove "hasher" from executors.json. See ${DOCS_URL}`,
81
+ ...hits,
82
+ ...manualNotes,
83
+ ],
84
+ agentContext: [...hits, ...manualNotes],
85
+ };
86
+ }
87
+ function isPlainObject(value) {
88
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
89
+ }
90
+ function isValidHasher(hasher) {
91
+ return hasher === undefined || typeof hasher === 'string';
92
+ }
93
+ function tryParse(content) {
94
+ try {
95
+ return (0, json_1.parseJson)(content);
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
@@ -0,0 +1,63 @@
1
+ #### Move custom executor hashers to target inputs
2
+
3
+ Custom hashers are deprecated in Nx 24 and will be removed in Nx 25.
4
+ A custom hasher is the `hasher` property of an executor in `executors.json`, pointing at a module that computes the task hash in code.
5
+ Target `inputs` replace them.
6
+
7
+ This migration finds every local plugin in the workspace whose `executors.json` (or `builders.json`) declares a `hasher`.
8
+ It doesn't edit any files.
9
+ When it finds one, it lists the executor, the hasher module, and the `executors.json` file in the migration's next steps.
10
+ It also lists files and executor entries it can't read, so you can check those by hand.
11
+ Under `nx migrate` with an AI agent, the agent reads each hasher and writes `inputs` that hash the same things.
12
+ A target that declares its own `inputs` doesn't inherit them from `targetDefaults`, so the agent checks every target that uses the executor and adds the inputs where they're missing.
13
+ It removes the `hasher` only once every one of those targets is covered.
14
+ If something the hasher hashes can't be expressed as an input, the agent leaves the hasher in place and tells you why.
15
+
16
+ Packages installed in `node_modules` aren't scanned.
17
+ If a third-party plugin you use declares a hasher, Nx warns about it when it hashes one of its tasks.
18
+
19
+ #### Sample code changes
20
+
21
+ Given a local plugin whose `echo` executor hashes the project, its dependencies, and the `API_URL` environment variable:
22
+
23
+ ##### Before
24
+
25
+ ```json title="tools/my-plugin/executors.json"
26
+ {
27
+ "executors": {
28
+ "echo": {
29
+ "implementation": "./src/executors/echo/executor",
30
+ "hasher": "./src/executors/echo/hasher",
31
+ "schema": "./src/executors/echo/schema.json"
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ##### After
38
+
39
+ ```json title="nx.json"
40
+ {
41
+ "targetDefaults": {
42
+ "@acme/my-plugin:echo": {
43
+ "inputs": ["default", "^default", { "env": "API_URL" }]
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ```json title="tools/my-plugin/executors.json"
50
+ {
51
+ "executors": {
52
+ "echo": {
53
+ "implementation": "./src/executors/echo/executor",
54
+ "schema": "./src/executors/echo/schema.json"
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ #### Reference
61
+
62
+ - [Replace a custom hasher with inputs](https://nx.dev/docs/kb/local-executors#replace-a-custom-hasher-with-inputs)
63
+ - [Inputs reference](https://nx.dev/docs/reference/inputs)
@@ -0,0 +1,145 @@
1
+ # Move custom executor hashers to target inputs
2
+
3
+ Custom hashers (the `hasher` property of an executor in `executors.json`) are
4
+ deprecated in Nx 24 and removed in Nx 25. Each one should become target `inputs`
5
+ that hash the same things, so the targets keep caching correctly once the hasher
6
+ is gone.
7
+
8
+ The generator half of this migration only scans. It edits no files, and it lists
9
+ every executor that declares a hasher in the advisory context, one line each,
10
+ with the executor name, the hasher path, and the `executors.json` path. Lines
11
+ starting with `Could not parse` name files the scan skipped, and lines starting
12
+ with `Entry` name executor entries it could not read.
13
+
14
+ Do not touch executors that declare no hasher, executors installed from
15
+ `node_modules`, or any other caching configuration.
16
+
17
+ ## First, check whether there is anything to do
18
+
19
+ Confirm this before changing anything. If it fails, make no changes and stop:
20
+
21
+ 1. The advisory context lists at least one executor that declares a custom
22
+ hasher, or at least one file or entry it could not read. Read each of those
23
+ yourself; if it declares no `hasher`, drop it from the list.
24
+
25
+ ## Step 1: Read what each hasher hashes
26
+
27
+ Open the hasher module named in the advisory context. Work out every value it
28
+ feeds into the hash it returns:
29
+
30
+ - `context.hasher.hashTask(task, ...)` alone is Nx default hashing.
31
+ - Files it reads, globs it walks, or other projects it hashes.
32
+ - Environment variables it reads.
33
+ - Commands it runs, or data it fetches (a remote API, a registry lookup).
34
+
35
+ ## Step 2: Write equivalent inputs
36
+
37
+ Map each value to an input:
38
+
39
+ | What the hasher hashes | Input |
40
+ | ----------------------------------------------------- | ------------------------------------------------------- |
41
+ | The project and its dependencies (Nx default hashing) | `"default"` and `"^default"` |
42
+ | Files of specific other projects | `{ "input": "default", "projects": ["shared-config"] }` |
43
+ | A subset of the files in the project | `{ "fileset": "{projectRoot}/src/**/*.graphql" }` |
44
+ | A file outside any project | `{ "fileset": "{workspaceRoot}/schema.json" }` |
45
+ | An environment variable | `{ "env": "API_URL" }` |
46
+ | The output of a command, such as a tool version | `{ "runtime": "node --version" }` |
47
+
48
+ If the workspace defines named inputs such as `production`, prefer them over
49
+ `default` where the hasher excluded test files.
50
+
51
+ ### Find every target that uses the executor
52
+
53
+ Write the resolved project graph and list every target whose `executor` is the
54
+ executor name (`"@acme/my-plugin:echo"`):
55
+
56
+ ```shell
57
+ npx nx graph --file=tmp/custom-hashers-graph.json
58
+ ```
59
+
60
+ Each target in `graph.nodes.<project>.data.targets` there is fully resolved: it
61
+ already reflects `targetDefaults` (including filtered array entries), inferred
62
+ targets, and project-level overrides. Record each consuming target's resolved
63
+ `inputs`. Every one of them must end up covered.
64
+
65
+ ### Place the inputs
66
+
67
+ A target that declares its own `inputs` replaces the `inputs` from
68
+ `targetDefaults` rather than merging with them. So a `targetDefaults` edit alone
69
+ does not reach a target that already declares `inputs`.
70
+
71
+ 1. Set the inputs in `nx.json` `targetDefaults` under the executor name. If that
72
+ key already exists, add `inputs` to it, and merge with any `inputs` it already
73
+ declares rather than replacing them. If its value is an array of filtered
74
+ entries, add the inputs to every entry that applies to a consuming target.
75
+ 2. For every consuming target that declares its own `inputs`, add the
76
+ replacement inputs to that target as well, keeping the inputs it already has.
77
+ It is declared in `project.json`, in the `nx.targets` block of
78
+ `package.json`, or by the plugin that infers the target. For an inferred
79
+ target, set `inputs` in `project.json` to the full resolved list plus the
80
+ replacement inputs.
81
+ 3. Write the graph again and confirm that every consuming target's resolved
82
+ `inputs` now contains every replacement input. Fix any that do not.
83
+
84
+ **Before:**
85
+
86
+ ```json
87
+ // tools/my-plugin/executors.json
88
+ {
89
+ "executors": {
90
+ "echo": {
91
+ "implementation": "./src/executors/echo/executor",
92
+ "hasher": "./src/executors/echo/hasher",
93
+ "schema": "./src/executors/echo/schema.json"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ **After:**
100
+
101
+ ```json
102
+ // nx.json
103
+ {
104
+ "targetDefaults": {
105
+ "@acme/my-plugin:echo": {
106
+ "inputs": ["default", "^default", { "env": "API_URL" }]
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ```json
113
+ // tools/my-plugin/executors.json
114
+ {
115
+ "executors": {
116
+ "echo": {
117
+ "implementation": "./src/executors/echo/executor",
118
+ "schema": "./src/executors/echo/schema.json"
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## Step 3: Remove the hasher only when the inputs cover it
125
+
126
+ Remove `hasher` from `executors.json` and delete the hasher module (and its
127
+ spec) only once the resolved inputs of every consuming target cover everything
128
+ the hasher hashed.
129
+
130
+ If a value has no file, environment variable, or project to point at, such as
131
+ data fetched from a remote service, use a `runtime` input whose command prints
132
+ that value. If no command can reproduce it, leave the hasher in place and tell
133
+ the user which value blocks the move and why.
134
+
135
+ ## Post-Migration Validation
136
+
137
+ 1. `npx nx show target <project>:<target>` for every consuming target found in
138
+ Step 2. Confirm each one lists the replacement inputs. After the hasher is
139
+ removed, `npx nx show target <project>:<target> inputs` also shows the files
140
+ and values those inputs resolve to.
141
+ 2. `npx nx run-many -t <target>` twice for the targets that used the executor.
142
+ The second run should be a cache hit. Change a file the hasher used to hash
143
+ and confirm the next run is a cache miss.
144
+ 3. `npx nx run-many -t build,test,lint -p <plugin project>` if you deleted hasher
145
+ files. Fix failures caused by this migration and re-run until green.
@@ -32,7 +32,7 @@ export declare class ExternalObject<T> {
32
32
  }
33
33
  }
34
34
  export declare class AppLifeCycle {
35
- constructor(tasks: Array<Task>, initiatingTasks: Array<string>, runMode: RunMode, pinnedTasks: Array<string>, tuiCliArgs: TuiCliArgs, tuiConfig: TuiConfig, titleText: string, workspaceRoot: string, taskGraph: TaskGraph, isCloudEnabled?: boolean | undefined | null)
35
+ constructor(tasks: Array<Task>, initiatingTasks: Array<string>, runMode: RunMode, pinnedTasks: Array<string>, tuiCliArgs: TuiCliArgs, tuiConfig: TuiConfig, titleText: string, workspaceRoot: string, taskGraph: TaskGraph, readyDependencies: Record<string, Array<string>>, isCloudEnabled?: boolean | undefined | null)
36
36
  startCommand(threadCount?: number | undefined | null): void
37
37
  scheduleTask(task: Task): void
38
38
  startTasks(tasks: Array<Task>, metadata: object): void
@@ -44,6 +44,7 @@ export declare class AppLifeCycle {
44
44
  registerRunningTaskWithEmptyParser(taskId: string): void
45
45
  appendTaskOutput(taskId: string, output: string, isPtyOutput: boolean): void
46
46
  setTaskStatus(taskId: string, status: TaskStatus): void
47
+ setTaskReadiness(taskId: string, readiness: TaskReadiness): void
47
48
  setTaskTiming(taskId: string, startTime: number, endTime: number): void
48
49
  registerForcedShutdownCallback(forcedShutdownCallback: (() => unknown)): void
49
50
  __setCloudMessage(message: string): Promise<void>
@@ -127,6 +128,15 @@ export declare class ImportResult {
127
128
  staticImportExpressions: Array<string>
128
129
  }
129
130
 
131
+ /**
132
+ * Done once every pattern has appeared in the fed output, in any order, even
133
+ * split across chunks or interleaved with terminal control sequences.
134
+ */
135
+ export declare class LogMatcher {
136
+ constructor(patterns: Array<string>)
137
+ feed(chunk: string): boolean
138
+ }
139
+
130
140
  export declare class NxCache {
131
141
  cacheDirectory: string
132
142
  constructor(workspaceRoot: string, cachePath: string, dbConnection: ExternalObject<NxDbConnection>, linkTaskDetails?: boolean | undefined | null, maxCacheSize?: number | undefined | null)
@@ -232,11 +242,33 @@ export declare class ProcessMetricsCollector {
232
242
  subscribe(callback: (err: Error | null, event: MetricsUpdate) => void): void
233
243
  }
234
244
 
245
+ /**
246
+ * Retries a probe until it passes, the timeout elapses or `cancel` is called.
247
+ * A failing attempt is never an error.
248
+ */
249
+ export declare class ReadinessProbe {
250
+ constructor(config: ReadinessProbeConfig, cwd: string)
251
+ /** False once the timeout elapses or `cancel` is called. */
252
+ wait(): Promise<boolean>
253
+ cancel(): void
254
+ }
255
+
235
256
  export declare class RunningTasksService {
236
257
  constructor(db: ExternalObject<NxDbConnection>)
237
258
  getRunningTasks(ids: Array<string>): Array<string>
238
259
  addRunningTask(taskId: string): void
260
+ /**
261
+ * Release this process's claim on a task and its readiness. A row another
262
+ * process has since taken over is left in place.
263
+ */
239
264
  removeRunningTask(taskId: string): void
265
+ /**
266
+ * No-op unless this process holds the task's claim, so a late or displaced
267
+ * probe result cannot overwrite the current owner's verdict.
268
+ */
269
+ setTaskReadiness(taskId: string, status: TaskReadiness): void
270
+ /** `None` when the task is not running. */
271
+ getTaskReadiness(taskId: string): TaskReadiness | null
240
272
  }
241
273
 
242
274
  export declare class RustPseudoTerminal {
@@ -282,14 +314,42 @@ export declare class TaskHasher {
282
314
  hashPlansFor(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>, taskIds: Array<string>, perTaskEnvs: Record<string, Record<string, string>>, cwd: string, collectTaskInputs?: boolean | undefined | null): Record<string, HashDetails>
283
315
  }
284
316
 
317
+ /**
318
+ * Tracks which tasks each Nx process in a nested process tree is running, so
319
+ * that a task which re-invokes itself can be reported instead of looping
320
+ * forever.
321
+ *
322
+ * Every process in the tree shares a `root_pid`, but sharing a root does not
323
+ * imply a parent/child relationship: sibling Nx processes are common and
324
+ * legitimate (N atomized e2e specs each spawning the same `serve-static` web
325
+ * server, or two parallel tasks that each shell out to `nx run lib:build`).
326
+ * Only an invocation belonging to an actual *ancestor* of this process is a
327
+ * loop, so the check is scoped to `ancestor_pids` rather than to the whole
328
+ * tree.
329
+ */
285
330
  export declare class TaskInvocationTracker {
286
- constructor(db: ExternalObject<NxDbConnection>, rootPid: number)
287
- /** Register a task as invoked. Throws if the task was already registered (loop detected). */
288
- registerTask(parentPid: number, taskId: string): void
289
- /** Remove a task invocation record after task completes. */
290
- unregisterTask(taskId: string): void
291
- /** Get all invocations for this root_pid, ordered by creation time. */
292
- getInvocationChain(): Array<InvocationRecord>
331
+ constructor(db: ExternalObject<NxDbConnection>, rootPid: number, ancestorPids: Array<number>)
332
+ /**
333
+ * Register a task as invoked by the process identified by `pid`.
334
+ *
335
+ * Returns the invocation chain when an ancestor process is already running
336
+ * this task, which is a genuine loop. The chain lists every task invoked
337
+ * along the ancestry path, outermost first. Returns `null` when the task
338
+ * was registered, including when a *sibling* process is already running it.
339
+ *
340
+ * An ancestor running this task with the *same* overrides is a loop
341
+ * immediately. An ancestor running it with different overrides is allowed
342
+ * up to `MAX_ANCESTOR_INVOCATIONS_PER_TASK`, which lets a terminating
343
+ * `app:build -> app:build --flag` chain through while still bounding a
344
+ * recursion whose forwarded arguments grow on every hop.
345
+ */
346
+ registerTask(pid: number, taskId: string, overridesHash: string): Array<InvocationRecord> | null
347
+ /**
348
+ * Remove a task invocation record after the task completes. Scoped to the
349
+ * registering process so that a sibling finishing the same task does not
350
+ * drop this process's record.
351
+ */
352
+ unregisterTask(pid: number, taskId: string): void
293
353
  /** Clean up stale invocations older than 1 day (handles PID recycling). */
294
354
  cleanupStale(): void
295
355
  }
@@ -886,7 +946,7 @@ export declare function installNxConsole(): Promise<boolean>
886
946
  export declare function installNxConsoleForEditor(editor: SupportedEditor): Promise<boolean>
887
947
 
888
948
  export interface InvocationRecord {
889
- parentPid: number
949
+ pid: number
890
950
  taskId: string
891
951
  }
892
952
 
@@ -1012,6 +1072,14 @@ export interface NxWorkspaceFilesExternals {
1012
1072
  ignoredIndex: ExternalObject<IgnoredIndexReader>
1013
1073
  }
1014
1074
 
1075
+ /**
1076
+ * Open `url` in the user's default browser. Returns `true` if an opener
1077
+ * process was spawned, `false` if none could be (e.g. no `xdg-open`) or `url`
1078
+ * isn't `http(s)`, so the caller can tell the user instead of failing
1079
+ * silently. Never throws.
1080
+ */
1081
+ export declare function openUrl(url: string): boolean
1082
+
1015
1083
  /** A workspace-relative file with the stamp it was left with. */
1016
1084
  export interface OutputFile {
1017
1085
  path: string
@@ -1076,8 +1144,44 @@ export interface ProjectGraph {
1076
1144
  externalNodes: Record<string, ExternalNode>
1077
1145
  }
1078
1146
 
1147
+ /** Exactly one of `url`, `port` or `command` is set. `interval` absent means backoff. */
1148
+ export interface ReadinessProbeConfig {
1149
+ url?: string
1150
+ port?: number
1151
+ host?: string
1152
+ command?: string
1153
+ timeout: number
1154
+ interval?: number
1155
+ }
1156
+
1079
1157
  export declare function remove(src: string): void
1080
1158
 
1159
+ /**
1160
+ * The daemon's own socket. Per run — the directory name hashes the pid, and
1161
+ * clients read the daemon's path back out of the process cache rather than
1162
+ * deriving it.
1163
+ */
1164
+ export declare function resolveDaemonSocketPath(workspaceRoot: string, env?: Record<string, string> | undefined | null): SocketDirDetails
1165
+
1166
+ /** A forked task process's socket, in the daemon's per-run directory. */
1167
+ export declare function resolveForkedProcessSocketPath(workspaceRoot: string, id: string, env?: Record<string, string> | undefined | null): SocketDirDetails
1168
+
1169
+ /**
1170
+ * Where the Nx Console socket lives, for whoever binds or connects to it.
1171
+ *
1172
+ * `env` is for callers that load a workspace `.env` into a copy rather than
1173
+ * into their own environment, and `workspaceRoot` for callers that do not run
1174
+ * inside the workspace — Nx Console is both, since it runs in the editor's
1175
+ * extension host.
1176
+ */
1177
+ export declare function resolveNxConsoleSocketPath(workspaceRoot: string, env?: Record<string, string> | undefined | null): SocketDirDetails
1178
+
1179
+ /**
1180
+ * Plugin worker sockets get their own workspace-scoped directory rather than
1181
+ * sitting in the shared system temp dir, which cannot be locked down.
1182
+ */
1183
+ export declare function resolvePluginSocketPath(workspaceRoot: string, id: string, env?: Record<string, string> | undefined | null): SocketDirDetails
1184
+
1081
1185
  export declare function restoreTerminal(): void
1082
1186
 
1083
1187
  export declare const enum RunMode {
@@ -1089,6 +1193,53 @@ export interface RuntimeInput {
1089
1193
  runtime: string
1090
1194
  }
1091
1195
 
1196
+ /**
1197
+ * One resolved socket location, and everything `daemon/tmp-dir.ts` needs to
1198
+ * explain it. Every user-facing sentence is rendered on the TypeScript side,
1199
+ * so this carries facts rather than prose.
1200
+ */
1201
+ export interface SocketDirDetails {
1202
+ /** The socket itself, which on Windows is a named pipe rather than a path. */
1203
+ path: string
1204
+ /** The directory holding it, for the caller that deletes it on shutdown. */
1205
+ dir: string
1206
+ /**
1207
+ * Whether `path` exceeds what the platform will bind. Reported rather than
1208
+ * thrown because which advice is correct depends on the other fields.
1209
+ */
1210
+ tooLong: boolean
1211
+ /**
1212
+ * Set when `path` is a directory Nx refuses to *be* the socket directory:
1213
+ * `shared-with-other-users`, `nx-managed`, or `os-temp-root`. The caller
1214
+ * throws on it rather than using `path`.
1215
+ */
1216
+ invalidReason?: string
1217
+ /** The preferred root that was skipped, when a later tier was used. */
1218
+ demotedFrom?: string
1219
+ /**
1220
+ * The default-root directory Nx tried before falling back, when there was
1221
+ * one.
1222
+ */
1223
+ attemptedDir?: string
1224
+ /**
1225
+ * The `NX_SOCKET_DIR` that was refused, when that is why this is a
1226
+ * fallback. Tracked apart from the other reasons so a later length error
1227
+ * stops telling someone to shorten a directory refused for another one.
1228
+ */
1229
+ refusedConfiguredDir?: string
1230
+ /** Why that configured directory was refused. */
1231
+ refusalError?: string
1232
+ /**
1233
+ * Whether this landed in the workspace data dir, which the caller warns
1234
+ * about once per process.
1235
+ */
1236
+ usedWorkspaceFallback: boolean
1237
+ /** Deduplicated advice for the refusals collected along the way. */
1238
+ remedies: Array<string>
1239
+ /** Every refusal as one sentence, for `--verbose`. */
1240
+ refusalDetails?: string
1241
+ }
1242
+
1092
1243
  export declare const enum SupportedEditor {
1093
1244
  VSCode = 0,
1094
1245
  VSCodeInsiders = 1,
@@ -1174,9 +1325,19 @@ export interface TaskGraph {
1174
1325
  roots: Array<string>
1175
1326
  /** Map of Task IDs to Tasks */
1176
1327
  tasks: Record<string, Task>
1177
- /** Map of Task IDs to IDs of tasks which the task depends on */
1178
- dependencies: Record<string, Array<string>>
1179
- continuousDependencies: Record<string, Array<string>>
1328
+ /** Map of Task IDs to the edges to tasks which the task depends on */
1329
+ dependencies: Record<string, Array<TaskGraphEdge>>
1330
+ continuousDependencies: Record<string, Array<TaskGraphEdge>>
1331
+ }
1332
+
1333
+ /** An edge from a task to one of the tasks it depends on */
1334
+ export interface TaskGraphEdge {
1335
+ /** ID of the task depended on */
1336
+ id: string
1337
+ /** What the dependent waits for on a continuous dependency. Defaults to `started`. */
1338
+ waitFor?: 'started' | 'ready'
1339
+ /** `false` on a continuous dependency whose inputs the dependent does not hash */
1340
+ inputs?: false
1180
1341
  }
1181
1342
 
1182
1343
  /** Details about the composition of a task's hash */
@@ -1221,6 +1382,12 @@ export interface TaskOutputs {
1221
1382
  files?: Array<OutputFile>
1222
1383
  }
1223
1384
 
1385
+ export declare const enum TaskReadiness {
1386
+ Pending = 0,
1387
+ Ready = 1,
1388
+ Failed = 2
1389
+ }
1390
+
1224
1391
  /**
1225
1392
  * The result of a completed Task.
1226
1393
  *
@@ -1346,6 +1513,7 @@ export interface UltracacheDiagnostic {
1346
1513
  export interface UltracacheEligibilityOptions {
1347
1514
  /** Tasks whose executor ships a custom hasher. */
1348
1515
  customHasherTaskIds?: Array<string>
1516
+ continuousTaskIds?: Array<string>
1349
1517
  }
1350
1518
 
1351
1519
  /**
@@ -584,11 +584,13 @@ module.exports.HashPlanner = nativeBinding.HashPlanner
584
584
  module.exports.HttpRemoteCache = nativeBinding.HttpRemoteCache
585
585
  module.exports.IgnoredIndexReader = nativeBinding.IgnoredIndexReader
586
586
  module.exports.ImportResult = nativeBinding.ImportResult
587
+ module.exports.LogMatcher = nativeBinding.LogMatcher
587
588
  module.exports.NxCache = nativeBinding.NxCache
588
589
  module.exports.NxConsolePreferences = nativeBinding.NxConsolePreferences
589
590
  module.exports.NxPluginCapabilities = nativeBinding.NxPluginCapabilities
590
591
  module.exports.NxTaskHistory = nativeBinding.NxTaskHistory
591
592
  module.exports.ProcessMetricsCollector = nativeBinding.ProcessMetricsCollector
593
+ module.exports.ReadinessProbe = nativeBinding.ReadinessProbe
592
594
  module.exports.RunningTasksService = nativeBinding.RunningTasksService
593
595
  module.exports.RustPseudoTerminal = nativeBinding.RustPseudoTerminal
594
596
  module.exports.TaskDetails = nativeBinding.TaskDetails
@@ -638,12 +640,18 @@ module.exports.locateTouchedProjects = nativeBinding.locateTouchedProjects
638
640
  module.exports.logDebug = nativeBinding.logDebug
639
641
  module.exports.matchGlobPaths = nativeBinding.matchGlobPaths
640
642
  module.exports.matchOutputPaths = nativeBinding.matchOutputPaths
643
+ module.exports.openUrl = nativeBinding.openUrl
641
644
  module.exports.parseTaskStatus = nativeBinding.parseTaskStatus
642
645
  module.exports.remove = nativeBinding.remove
646
+ module.exports.resolveDaemonSocketPath = nativeBinding.resolveDaemonSocketPath
647
+ module.exports.resolveForkedProcessSocketPath = nativeBinding.resolveForkedProcessSocketPath
648
+ module.exports.resolveNxConsoleSocketPath = nativeBinding.resolveNxConsoleSocketPath
649
+ module.exports.resolvePluginSocketPath = nativeBinding.resolvePluginSocketPath
643
650
  module.exports.restoreTerminal = nativeBinding.restoreTerminal
644
651
  module.exports.RunMode = nativeBinding.RunMode
645
652
  module.exports.SupportedEditor = nativeBinding.SupportedEditor
646
653
  module.exports.sweepBatchOutputs = nativeBinding.sweepBatchOutputs
654
+ module.exports.TaskReadiness = nativeBinding.TaskReadiness
647
655
  module.exports.TaskStatus = nativeBinding.TaskStatus
648
656
  module.exports.testOnlyTransferFileMap = nativeBinding.testOnlyTransferFileMap
649
657
  module.exports.trackEvent = nativeBinding.trackEvent