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.
- package/README.md +3 -6
- package/dist/nx-cloud-internals.d.ts +1 -0
- package/dist/nx-cloud-internals.js +7 -1
- package/dist/src/adapter/ngcli-adapter.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +0 -12
- package/dist/src/command-line/graph/graph.js +3 -5
- package/dist/src/command-line/init/command-object.js +0 -12
- package/dist/src/command-line/init/init-v2.js +0 -2
- package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +1 -1
- package/dist/src/command-line/migrate/agentic/close-agent-session.js +4 -3
- package/dist/src/command-line/migrate/agentic/master/spawn-master.js +76 -15
- package/dist/src/command-line/migrate/execute-migration.d.ts +2 -2
- package/dist/src/command-line/migrate/execute-migration.js +18 -10
- package/dist/src/command-line/migrate/migrate-commits.d.ts +4 -1
- package/dist/src/command-line/migrate/migrate-commits.js +10 -3
- package/dist/src/command-line/migrate/migrate.js +43 -6
- package/dist/src/command-line/migrate/run/agent-output.d.ts +1 -0
- package/dist/src/command-line/migrate/run/agent-output.js +5 -0
- package/dist/src/command-line/migrate/run/broker.d.ts +14 -5
- package/dist/src/command-line/migrate/run/broker.js +68 -20
- package/dist/src/command-line/migrate/run/index.d.ts +4 -3
- package/dist/src/command-line/migrate/run/index.js +8 -1
- package/dist/src/command-line/migrate/run/orchestrator.js +72 -13
- package/dist/src/command-line/migrate/run/runbook.js +19 -0
- package/dist/src/command-line/migrate/run/state-machine.js +4 -1
- package/dist/src/command-line/migrate/run/util.d.ts +3 -0
- package/dist/src/command-line/migrate/run/util.js +7 -1
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +17 -12
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +5 -6
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +5 -6
- package/dist/src/command-line/run/executor-utils.js +1 -0
- package/dist/src/command-line/run/run-one.d.ts +20 -1
- package/dist/src/command-line/run/run-one.js +153 -5
- package/dist/src/command-line/show/show-target/info.js +1 -1
- package/dist/src/command-line/show/show-target/utils.js +1 -0
- package/dist/src/command-line/watch/command-object.js +3 -22
- package/dist/src/command-line/watch/watch.d.ts +0 -7
- package/dist/src/command-line/watch/watch.js +1 -1
- package/dist/src/config/misc-interfaces.d.ts +12 -0
- package/dist/src/config/task-graph.d.ts +1 -1
- package/dist/src/config/workspace-json-project-json.d.ts +65 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/client/client.d.ts +9 -0
- package/dist/src/daemon/client/client.js +89 -37
- package/dist/src/daemon/server/shutdown-utils.js +0 -3
- package/dist/src/daemon/socket-utils.d.ts +9 -4
- package/dist/src/daemon/socket-utils.js +12 -49
- package/dist/src/daemon/tmp-dir.d.ts +19 -14
- package/dist/src/daemon/tmp-dir.js +142 -354
- package/dist/src/devkit-exports.d.ts +1 -1
- package/dist/src/devkit-internals.d.ts +1 -1
- package/dist/src/devkit-internals.js +1 -0
- package/dist/src/hasher/check-task-files.js +4 -4
- package/dist/src/hasher/file-hasher.js +9 -7
- package/dist/src/hasher/hash-task.js +4 -0
- package/dist/src/hasher/native-task-hasher-impl.js +3 -1
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.d.ts +3 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.js +100 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.md +63 -0
- package/dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md +145 -0
- package/dist/src/native/index.d.ts +180 -12
- package/dist/src/native/native-bindings.js +8 -0
- package/dist/src/native/nx.wasi-browser.js +1 -0
- package/dist/src/native/nx.wasi.cjs +1 -0
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/update-manager.d.ts +7 -0
- package/dist/src/nx-cloud/update-manager.js +237 -50
- package/dist/src/project-graph/affected/affected-tasks.js +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +7 -0
- package/dist/src/tasks-runner/create-task-graph.d.ts +8 -7
- package/dist/src/tasks-runner/create-task-graph.js +47 -26
- package/dist/src/tasks-runner/init-tasks-runner.js +13 -0
- package/dist/src/tasks-runner/life-cycle.d.ts +4 -1
- package/dist/src/tasks-runner/life-cycle.js +7 -0
- package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +5 -2
- package/dist/src/tasks-runner/life-cycles/performance-analysis.js +13 -7
- package/dist/src/tasks-runner/life-cycles/performance-life-cycle.d.ts +4 -3
- package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +9 -2
- package/dist/src/tasks-runner/readiness/probes.d.ts +9 -0
- package/dist/src/tasks-runner/readiness/probes.js +62 -0
- package/dist/src/tasks-runner/readiness/ready-when.d.ts +38 -0
- package/dist/src/tasks-runner/readiness/ready-when.js +190 -0
- package/dist/src/tasks-runner/run-command.js +5 -4
- package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -4
- package/dist/src/tasks-runner/task-env.d.ts +10 -0
- package/dist/src/tasks-runner/task-env.js +26 -5
- package/dist/src/tasks-runner/task-graph-utils.d.ts +9 -6
- package/dist/src/tasks-runner/task-graph-utils.js +17 -12
- package/dist/src/tasks-runner/task-orchestrator.d.ts +37 -8
- package/dist/src/tasks-runner/task-orchestrator.js +460 -76
- package/dist/src/tasks-runner/tasks-schedule.d.ts +18 -1
- package/dist/src/tasks-runner/tasks-schedule.js +64 -6
- package/dist/src/tasks-runner/utils.d.ts +20 -5
- package/dist/src/tasks-runner/utils.js +70 -35
- package/dist/src/ultracache/overrides.js +4 -0
- package/dist/src/utils/child-process.js +1 -0
- package/dist/src/utils/git-utils.d.ts +6 -1
- package/dist/src/utils/git-utils.js +26 -5
- package/dist/src/utils/owned-private-dir.d.ts +1 -17
- package/dist/src/utils/owned-private-dir.js +10 -62
- package/dist/src/utils/perf-hooks.d.ts +1 -0
- package/dist/src/utils/plugins/core-plugins.js +1 -1
- package/dist/src/utils/spawn-without-terminal.d.ts +27 -0
- package/dist/src/utils/spawn-without-terminal.js +110 -0
- package/dist/src/utils/string-similarity.d.ts +38 -0
- package/dist/src/utils/string-similarity.js +108 -0
- package/migrations.json +7 -0
- package/package.json +11 -20
- package/readme-template.md +3 -3
- package/schemas/nx-schema.json +70 -0
- package/schemas/project-schema.json +67 -0
- package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +0 -18
- package/dist/src/tasks-runner/legacy-depends-on-warning.js +0 -109
|
@@ -2,14 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DirectoryRefusedError = void 0;
|
|
4
4
|
exports.describeRefusal = describeRefusal;
|
|
5
|
-
exports.remedyFor = remedyFor;
|
|
6
5
|
exports.canonicalDir = canonicalDir;
|
|
7
6
|
exports.isSafeSharedRoot = isSafeSharedRoot;
|
|
8
|
-
exports.isPeerWritable = isPeerWritable;
|
|
9
7
|
exports.ensureSafeSharedRoot = ensureSafeSharedRoot;
|
|
10
8
|
exports.isOwnedRealDirectory = isOwnedRealDirectory;
|
|
11
9
|
exports.getUserSegment = getUserSegment;
|
|
12
10
|
exports.ensureOwnedPrivateDir = ensureOwnedPrivateDir;
|
|
11
|
+
/**
|
|
12
|
+
* Directory guards for Nx's runtime state: create a directory only when it is
|
|
13
|
+
* ours and unreachable by other users on this machine.
|
|
14
|
+
*
|
|
15
|
+
* `native/utils/owned_dir.rs` implements the same rules and must change with
|
|
16
|
+
* this file. This copy exists because the native binding loader has to place
|
|
17
|
+
* and lock down the `.node` before Rust can be called, and `cache-directory.ts`,
|
|
18
|
+
* `git-utils.ts` and `nx-tmp-dir.ts` share it. Node builtins only, for the
|
|
19
|
+
* loader's sake.
|
|
20
|
+
*/
|
|
13
21
|
const node_fs_1 = require("node:fs");
|
|
14
22
|
const node_os_1 = require("node:os");
|
|
15
23
|
const node_path_1 = require("node:path");
|
|
@@ -56,42 +64,6 @@ function describeRefusal(r) {
|
|
|
56
64
|
}
|
|
57
65
|
}
|
|
58
66
|
}
|
|
59
|
-
/** Single-quoted for a shell, so a path with a space or quote survives a paste. */
|
|
60
|
-
const shellQuote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
61
|
-
/** What the user can do about a refusal, or `undefined` when there is nothing. */
|
|
62
|
-
function remedyFor(r) {
|
|
63
|
-
switch (r.kind) {
|
|
64
|
-
case 'not-a-directory':
|
|
65
|
-
return r.symlink
|
|
66
|
-
? `${r.dir} is a symlink where Nx expects a directory. If you did not create it, treat it as hostile: remove the link itself (not what it points at) and run the command again.`
|
|
67
|
-
: undefined;
|
|
68
|
-
case 'foreign-owner':
|
|
69
|
-
return `${r.dir} belongs to another user on this machine, so Nx cannot keep its own directory there. Set NX_SOCKET_DIR to a short directory your user owns, or move it aside — which you can do yourself if you own the directory it sits in, and otherwise needs an administrator.`;
|
|
70
|
-
case 'not-tightenable':
|
|
71
|
-
// Both producers reach here having already established that the directory
|
|
72
|
-
// is ours, so `chmod` is the user's to run. Names no cause: one producer
|
|
73
|
-
// is a mount that discards the mode, the other is any `chmod` failure.
|
|
74
|
-
return `${r.dir} is reachable by other users (mode ${asMode(r.mode)}) and Nx could not restrict it. Run \`chmod 0700 ${shellQuote(r.dir)}\` and try again; if the mode does not stick, set NX_SOCKET_DIR to a short directory on a filesystem that keeps POSIX permissions.`;
|
|
75
|
-
case 'foreign-shared-container':
|
|
76
|
-
// Unreachable today: `isSafeSharedRoot` denies with this kind only when
|
|
77
|
-
// `stats.uid !== 0`, so a root-owned container never reaches here.
|
|
78
|
-
if (r.uid === 0) {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
const q = shellQuote(r.dir);
|
|
82
|
-
return `${r.dir} belongs to another user on this machine, so Nx cannot keep a private directory beneath it. Ask an administrator to hand it to root with \`sudo chown root ${q} && sudo chmod 1777 ${q}\`; every user can then keep their own directory under it.`;
|
|
83
|
-
case 'not-created':
|
|
84
|
-
case 'not-inspectable':
|
|
85
|
-
case 'peer-writable-not-sticky':
|
|
86
|
-
return undefined;
|
|
87
|
-
default: {
|
|
88
|
-
// Matches `describeRefusal`'s arm, so a new kind cannot ship with a
|
|
89
|
-
// sentence and no advice — which is how `not-tightenable` went unadvised.
|
|
90
|
-
const unhandled = r;
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
67
|
/** One refusal as an `Error`, so several can travel in an `AggregateError`. */
|
|
96
68
|
class DirectoryRefusedError extends Error {
|
|
97
69
|
constructor(refusal) {
|
|
@@ -207,30 +179,6 @@ function isSafeSharedRoot(dir) {
|
|
|
207
179
|
return deny({ kind: 'not-inspectable', dir, code: e?.code });
|
|
208
180
|
}
|
|
209
181
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Whether other users on this machine can write into `dir`. Gates whether a
|
|
212
|
-
* refusal message may cite other users, so it must not over-report: `false` on
|
|
213
|
-
* Windows, where libuv synthesizes `st_mode` from the READONLY attribute and
|
|
214
|
-
* reports an ordinary per-account directory as `0666`, and `false` on a path
|
|
215
|
-
* that cannot be inspected.
|
|
216
|
-
*
|
|
217
|
-
* `statSync`, not `lstatSync`: the question is about the directory that will be
|
|
218
|
-
* used, not the link pointing at it. Link modes mislead in both directions —
|
|
219
|
-
* Linux creates symlinks `0777`, macOS applies the umask. Nothing here decides
|
|
220
|
-
* whether a path is accepted, so following the link costs nothing the guards
|
|
221
|
-
* do not already re-check.
|
|
222
|
-
*/
|
|
223
|
-
function isPeerWritable(dir) {
|
|
224
|
-
if (process.platform === 'win32') {
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
try {
|
|
228
|
-
return !!((0, node_fs_1.statSync)(dir).mode & 0o022);
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
182
|
/**
|
|
235
183
|
* Create a shared container as sticky + world-writable if it does not exist,
|
|
236
184
|
* and report whether the resulting path is safe for the current user.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ChildProcess, type SpawnOptions } from 'child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Runs `command` in a shell that cannot reach the terminal nx shares with
|
|
4
|
+
* another program, so a prompt fails instead of waiting for keys that program
|
|
5
|
+
* reads. On POSIX the shell leads a new session with no controlling terminal,
|
|
6
|
+
* and loses every environment variable that names nx's terminal. Windows
|
|
7
|
+
* spawns it as usual, and with no stdio inherited it gets no console, so the
|
|
8
|
+
* terminal's Ctrl+C misses it there too. Whoever shares the terminal forwards
|
|
9
|
+
* signals with `signalCommandsWithoutTerminal`, and an nx that exits first
|
|
10
|
+
* kills each command whose output is still open.
|
|
11
|
+
*/
|
|
12
|
+
export declare function spawnWithoutTerminal(command: string, options: Omit<SpawnOptions, 'shell' | 'detached'>): ChildProcess;
|
|
13
|
+
/**
|
|
14
|
+
* Sends `signal` to every command started by `spawnWithoutTerminal` whose
|
|
15
|
+
* output is still open: to its process group on POSIX, and on a SIGKILL also to
|
|
16
|
+
* the processes still descending from its shell, since a parent killed outright
|
|
17
|
+
* cannot stop the ones it started in groups of their own. Windows has no
|
|
18
|
+
* process groups, so there those descendants are terminated, whatever the
|
|
19
|
+
* signal.
|
|
20
|
+
*/
|
|
21
|
+
export declare function signalCommandsWithoutTerminal(signal: NodeJS.Signals): void;
|
|
22
|
+
/**
|
|
23
|
+
* `env` without the variables whose value is the path of one of `terminals`
|
|
24
|
+
* (device ids). A process outside the new session, such as gpg-agent reading
|
|
25
|
+
* `GPG_TTY`, would otherwise open that terminal itself.
|
|
26
|
+
*/
|
|
27
|
+
export declare function withoutTerminalPaths(env: NodeJS.ProcessEnv, terminals: ReadonlySet<number>): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.spawnWithoutTerminal = spawnWithoutTerminal;
|
|
4
|
+
exports.signalCommandsWithoutTerminal = signalCommandsWithoutTerminal;
|
|
5
|
+
exports.withoutTerminalPaths = withoutTerminalPaths;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const tty_1 = require("tty");
|
|
9
|
+
// The commands spawned below whose output is still open, by pid. On POSIX each
|
|
10
|
+
// one leads its own process group.
|
|
11
|
+
const commands = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Runs `command` in a shell that cannot reach the terminal nx shares with
|
|
14
|
+
* another program, so a prompt fails instead of waiting for keys that program
|
|
15
|
+
* reads. On POSIX the shell leads a new session with no controlling terminal,
|
|
16
|
+
* and loses every environment variable that names nx's terminal. Windows
|
|
17
|
+
* spawns it as usual, and with no stdio inherited it gets no console, so the
|
|
18
|
+
* terminal's Ctrl+C misses it there too. Whoever shares the terminal forwards
|
|
19
|
+
* signals with `signalCommandsWithoutTerminal`, and an nx that exits first
|
|
20
|
+
* kills each command whose output is still open.
|
|
21
|
+
*/
|
|
22
|
+
function spawnWithoutTerminal(command, options) {
|
|
23
|
+
const child = (0, child_process_1.spawn)(command, {
|
|
24
|
+
...options,
|
|
25
|
+
shell: true,
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
detached: process.platform !== 'win32',
|
|
28
|
+
...(process.platform !== 'win32' && {
|
|
29
|
+
env: withoutTerminalPaths(options.env ?? process.env, stdioTerminals()),
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
const pid = child.pid;
|
|
33
|
+
if (pid !== undefined) {
|
|
34
|
+
if (commands.size === 0)
|
|
35
|
+
process.on('exit', killCommands);
|
|
36
|
+
commands.set(pid, child);
|
|
37
|
+
child.once('close', () => {
|
|
38
|
+
commands.delete(pid);
|
|
39
|
+
if (commands.size === 0)
|
|
40
|
+
process.removeListener('exit', killCommands);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return child;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sends `signal` to every command started by `spawnWithoutTerminal` whose
|
|
47
|
+
* output is still open: to its process group on POSIX, and on a SIGKILL also to
|
|
48
|
+
* the processes still descending from its shell, since a parent killed outright
|
|
49
|
+
* cannot stop the ones it started in groups of their own. Windows has no
|
|
50
|
+
* process groups, so there those descendants are terminated, whatever the
|
|
51
|
+
* signal.
|
|
52
|
+
*/
|
|
53
|
+
function signalCommandsWithoutTerminal(signal) {
|
|
54
|
+
for (const [pid, child] of commands) {
|
|
55
|
+
// Tree first: killing the group reparents its members' children.
|
|
56
|
+
if (process.platform === 'win32' || signal === 'SIGKILL') {
|
|
57
|
+
killTree(child, pid, signal);
|
|
58
|
+
}
|
|
59
|
+
if (process.platform !== 'win32') {
|
|
60
|
+
try {
|
|
61
|
+
process.kill(-pid, signal);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// The command already ended; its `close` is on the way.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Only while the shell lives: once it has exited, its pid can be reused.
|
|
70
|
+
function killTree(child, pid, signal) {
|
|
71
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
72
|
+
return;
|
|
73
|
+
try {
|
|
74
|
+
const { killProcessTree } = require('../native');
|
|
75
|
+
killProcessTree(pid, signal);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// No native killer in this build; on POSIX the group signal still goes out.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function killCommands() {
|
|
82
|
+
signalCommandsWithoutTerminal('SIGKILL');
|
|
83
|
+
}
|
|
84
|
+
function stdioTerminals() {
|
|
85
|
+
const devices = new Set();
|
|
86
|
+
for (const fd of [0, 1, 2]) {
|
|
87
|
+
if ((0, tty_1.isatty)(fd))
|
|
88
|
+
devices.add((0, fs_1.fstatSync)(fd).rdev);
|
|
89
|
+
}
|
|
90
|
+
return devices;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `env` without the variables whose value is the path of one of `terminals`
|
|
94
|
+
* (device ids). A process outside the new session, such as gpg-agent reading
|
|
95
|
+
* `GPG_TTY`, would otherwise open that terminal itself.
|
|
96
|
+
*/
|
|
97
|
+
function withoutTerminalPaths(env, terminals) {
|
|
98
|
+
return Object.fromEntries(Object.entries(env).filter(([, value]) => !namesTerminal(value, terminals)));
|
|
99
|
+
}
|
|
100
|
+
function namesTerminal(value, terminals) {
|
|
101
|
+
if (!value?.startsWith('/dev/'))
|
|
102
|
+
return false;
|
|
103
|
+
try {
|
|
104
|
+
const stat = (0, fs_1.statSync)(value);
|
|
105
|
+
return stat.isCharacterDevice() && terminals.has(stat.rdev);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the Levenshtein edit distance between two strings, i.e. the minimum
|
|
3
|
+
* number of single-character insertions, deletions, or substitutions required
|
|
4
|
+
* to turn `a` into `b`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function levenshteinDistance(a: string, b: string): number;
|
|
7
|
+
/**
|
|
8
|
+
* Ranks every candidate by how closely it resembles `input`, from closest to
|
|
9
|
+
* furthest, tie-broken alphabetically so the order is deterministic. Each entry
|
|
10
|
+
* keeps its computed distance so callers can reuse it (e.g. to both pick the
|
|
11
|
+
* closest match and order a list) without recomputing.
|
|
12
|
+
*/
|
|
13
|
+
export declare function rankByDistance(input: string, candidates: readonly string[]): {
|
|
14
|
+
candidate: string;
|
|
15
|
+
distance: number;
|
|
16
|
+
}[];
|
|
17
|
+
/**
|
|
18
|
+
* Whether a candidate at edit `distance` from `input` is close enough to be a
|
|
19
|
+
* useful suggestion. Exposed so callers that already have a distance (e.g. from
|
|
20
|
+
* `rankByDistance`) can gate on it without recomputing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isWithinSuggestionThreshold(input: string, candidate: string, distance: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Ranks the candidates that are similar enough to `input` to be worth
|
|
25
|
+
* suggesting, closest first (ties broken alphabetically). Each entry keeps its
|
|
26
|
+
* distance so callers can compare suggestions found for different forms of the
|
|
27
|
+
* same input without recomputing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rankSuggestions(input: string, candidates: readonly string[]): {
|
|
30
|
+
candidate: string;
|
|
31
|
+
distance: number;
|
|
32
|
+
}[];
|
|
33
|
+
/**
|
|
34
|
+
* Returns up to `limit` candidates that most closely resemble `input`, sorted
|
|
35
|
+
* from closest to furthest. Candidates that are not similar enough to be a
|
|
36
|
+
* useful suggestion are excluded.
|
|
37
|
+
*/
|
|
38
|
+
export declare function findClosestMatches(input: string, candidates: readonly string[], limit?: number): string[];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.levenshteinDistance = levenshteinDistance;
|
|
4
|
+
exports.rankByDistance = rankByDistance;
|
|
5
|
+
exports.isWithinSuggestionThreshold = isWithinSuggestionThreshold;
|
|
6
|
+
exports.rankSuggestions = rankSuggestions;
|
|
7
|
+
exports.findClosestMatches = findClosestMatches;
|
|
8
|
+
/**
|
|
9
|
+
* Computes the Levenshtein edit distance between two strings, i.e. the minimum
|
|
10
|
+
* number of single-character insertions, deletions, or substitutions required
|
|
11
|
+
* to turn `a` into `b`.
|
|
12
|
+
*/
|
|
13
|
+
function levenshteinDistance(a, b) {
|
|
14
|
+
if (a === b) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
if (a.length === 0) {
|
|
18
|
+
return b.length;
|
|
19
|
+
}
|
|
20
|
+
if (b.length === 0) {
|
|
21
|
+
return a.length;
|
|
22
|
+
}
|
|
23
|
+
let previousRow = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
24
|
+
let currentRow = new Array(b.length + 1);
|
|
25
|
+
for (let i = 0; i < a.length; i++) {
|
|
26
|
+
currentRow[0] = i + 1;
|
|
27
|
+
for (let j = 0; j < b.length; j++) {
|
|
28
|
+
const cost = a[i] === b[j] ? 0 : 1;
|
|
29
|
+
currentRow[j + 1] = Math.min(currentRow[j] + 1, // insertion
|
|
30
|
+
previousRow[j + 1] + 1, // deletion
|
|
31
|
+
previousRow[j] + cost // substitution
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
[previousRow, currentRow] = [currentRow, previousRow];
|
|
35
|
+
}
|
|
36
|
+
return previousRow[b.length];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Ranks every candidate by how closely it resembles `input`, from closest to
|
|
40
|
+
* furthest, tie-broken alphabetically so the order is deterministic. Each entry
|
|
41
|
+
* keeps its computed distance so callers can reuse it (e.g. to both pick the
|
|
42
|
+
* closest match and order a list) without recomputing.
|
|
43
|
+
*/
|
|
44
|
+
function rankByDistance(input, candidates) {
|
|
45
|
+
return candidates
|
|
46
|
+
.map((candidate) => ({
|
|
47
|
+
candidate,
|
|
48
|
+
distance: levenshteinDistance(input, candidate),
|
|
49
|
+
}))
|
|
50
|
+
.sort((a, b) => a.distance - b.distance || a.candidate.localeCompare(b.candidate));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The number of edits tolerated before `candidate` stops being a useful
|
|
54
|
+
* suggestion for `input`.
|
|
55
|
+
*
|
|
56
|
+
* The tolerance scales with length so longer words allow for more typos, but it
|
|
57
|
+
* scales with the length of the part that actually *differs* — the shared
|
|
58
|
+
* prefix and suffix are stripped first. Scaling on the raw length lets text the
|
|
59
|
+
* user typed correctly buy a bigger budget for the part they got wrong, which
|
|
60
|
+
* matters because these inputs are joined `project:target[:configuration]`
|
|
61
|
+
* specifiers: `zzz:build` and `api:build` are 3 edits apart, but the shared
|
|
62
|
+
* `:build` inflates the raw-length budget to 4 and a project name with no
|
|
63
|
+
* character in common gets suggested.
|
|
64
|
+
*
|
|
65
|
+
* Stripping a common prefix and suffix does not change the Levenshtein
|
|
66
|
+
* distance, so a caller's precomputed `distance` remains directly comparable.
|
|
67
|
+
*/
|
|
68
|
+
function suggestionThreshold(input, candidate) {
|
|
69
|
+
const shortest = Math.min(input.length, candidate.length);
|
|
70
|
+
let prefix = 0;
|
|
71
|
+
while (prefix < shortest && input[prefix] === candidate[prefix]) {
|
|
72
|
+
prefix++;
|
|
73
|
+
}
|
|
74
|
+
let suffix = 0;
|
|
75
|
+
while (suffix < shortest - prefix &&
|
|
76
|
+
input[input.length - 1 - suffix] ===
|
|
77
|
+
candidate[candidate.length - 1 - suffix]) {
|
|
78
|
+
suffix++;
|
|
79
|
+
}
|
|
80
|
+
return Math.max(2, Math.ceil((input.length - prefix - suffix) * 0.4));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether a candidate at edit `distance` from `input` is close enough to be a
|
|
84
|
+
* useful suggestion. Exposed so callers that already have a distance (e.g. from
|
|
85
|
+
* `rankByDistance`) can gate on it without recomputing.
|
|
86
|
+
*/
|
|
87
|
+
function isWithinSuggestionThreshold(input, candidate, distance) {
|
|
88
|
+
return distance <= suggestionThreshold(input, candidate);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Ranks the candidates that are similar enough to `input` to be worth
|
|
92
|
+
* suggesting, closest first (ties broken alphabetically). Each entry keeps its
|
|
93
|
+
* distance so callers can compare suggestions found for different forms of the
|
|
94
|
+
* same input without recomputing.
|
|
95
|
+
*/
|
|
96
|
+
function rankSuggestions(input, candidates) {
|
|
97
|
+
return rankByDistance(input, candidates).filter(({ candidate, distance }) => isWithinSuggestionThreshold(input, candidate, distance));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Returns up to `limit` candidates that most closely resemble `input`, sorted
|
|
101
|
+
* from closest to furthest. Candidates that are not similar enough to be a
|
|
102
|
+
* useful suggestion are excluded.
|
|
103
|
+
*/
|
|
104
|
+
function findClosestMatches(input, candidates, limit = 3) {
|
|
105
|
+
return rankSuggestions(input, candidates)
|
|
106
|
+
.slice(0, limit)
|
|
107
|
+
.map(({ candidate }) => candidate);
|
|
108
|
+
}
|
package/migrations.json
CHANGED
|
@@ -190,6 +190,13 @@
|
|
|
190
190
|
"description": "Sets \"cache\" on some executor-based target defaults whose caching was previously enabled by a target name default, maintaining compatibility with how cacheability resolved before Nx 23. Keys the migration cannot decide are left alone and keep working through a deprecated runtime fallback.",
|
|
191
191
|
"implementation": "./dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults",
|
|
192
192
|
"documentation": "./dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md"
|
|
193
|
+
},
|
|
194
|
+
"24-0-0-move-custom-hashers-to-inputs": {
|
|
195
|
+
"version": "24.0.0-beta.0",
|
|
196
|
+
"description": "Finds local executors that declare a custom hasher, which is deprecated and will be removed in Nx 25. Rewriting a hasher as target inputs means reading what its code hashes, so the rewrite is driven by an AI prompt; the generator only reports each hasher.",
|
|
197
|
+
"implementation": "./dist/src/migrations/update-24-0-0/find-custom-hashers",
|
|
198
|
+
"prompt": "./dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md",
|
|
199
|
+
"documentation": "./dist/src/migrations/update-24-0-0/find-custom-hashers.md"
|
|
193
200
|
}
|
|
194
201
|
}
|
|
195
202
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nx",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "24.0.0-beta.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
|
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
"bl": "4.1.0",
|
|
67
67
|
"brace-expansion": "5.0.12",
|
|
68
68
|
"buffer": "5.7.1",
|
|
69
|
-
"bundle-name": "4.1.0",
|
|
70
69
|
"chalk": "4.1.2",
|
|
71
70
|
"cli-cursor": "3.1.0",
|
|
72
71
|
"cli-spinners": "2.6.1",
|
|
@@ -74,10 +73,7 @@
|
|
|
74
73
|
"clone": "1.0.4",
|
|
75
74
|
"color-convert": "2.0.1",
|
|
76
75
|
"color-name": "1.1.4",
|
|
77
|
-
"default-browser": "5.2.1",
|
|
78
|
-
"default-browser-id": "5.0.0",
|
|
79
76
|
"defaults": "1.0.4",
|
|
80
|
-
"define-lazy-prop": "3.0.0",
|
|
81
77
|
"dotenv": "16.4.7",
|
|
82
78
|
"dotenv-expand": "12.0.3",
|
|
83
79
|
"ejs": "5.0.1",
|
|
@@ -95,12 +91,9 @@
|
|
|
95
91
|
"ieee754": "1.2.1",
|
|
96
92
|
"ignore": "7.0.6",
|
|
97
93
|
"inherits": "2.0.4",
|
|
98
|
-
"is-docker": "3.0.0",
|
|
99
94
|
"is-fullwidth-code-point": "3.0.0",
|
|
100
|
-
"is-inside-container": "1.0.0",
|
|
101
95
|
"is-interactive": "1.0.0",
|
|
102
96
|
"is-unicode-supported": "0.1.0",
|
|
103
|
-
"is-wsl": "3.1.0",
|
|
104
97
|
"isexe": "2.0.0",
|
|
105
98
|
"json5": "2.2.3",
|
|
106
99
|
"jsonc-parser": "3.3.1",
|
|
@@ -112,7 +105,6 @@
|
|
|
112
105
|
"npm-run-path": "4.0.1",
|
|
113
106
|
"once": "1.4.0",
|
|
114
107
|
"onetime": "5.1.2",
|
|
115
|
-
"open": "10.1.0",
|
|
116
108
|
"ora": "5.4.1",
|
|
117
109
|
"path-key": "3.1.1",
|
|
118
110
|
"picocolors": "1.1.1",
|
|
@@ -120,7 +112,6 @@
|
|
|
120
112
|
"require-directory": "2.1.1",
|
|
121
113
|
"resolve.exports": "2.0.3",
|
|
122
114
|
"restore-cursor": "3.1.0",
|
|
123
|
-
"run-applescript": "7.0.0",
|
|
124
115
|
"safe-buffer": "5.2.1",
|
|
125
116
|
"semver": "7.8.5",
|
|
126
117
|
"signal-exit": "3.0.7",
|
|
@@ -159,16 +150,16 @@
|
|
|
159
150
|
}
|
|
160
151
|
},
|
|
161
152
|
"optionalDependencies": {
|
|
162
|
-
"@nx/nx-darwin-arm64": "
|
|
163
|
-
"@nx/nx-darwin-x64": "
|
|
164
|
-
"@nx/nx-freebsd-x64": "
|
|
165
|
-
"@nx/nx-linux-arm-gnueabihf": "
|
|
166
|
-
"@nx/nx-linux-arm64-gnu": "
|
|
167
|
-
"@nx/nx-linux-arm64-musl": "
|
|
168
|
-
"@nx/nx-linux-x64-gnu": "
|
|
169
|
-
"@nx/nx-linux-x64-musl": "
|
|
170
|
-
"@nx/nx-win32-arm64-msvc": "
|
|
171
|
-
"@nx/nx-win32-x64-msvc": "
|
|
153
|
+
"@nx/nx-darwin-arm64": "24.0.0-beta.1",
|
|
154
|
+
"@nx/nx-darwin-x64": "24.0.0-beta.1",
|
|
155
|
+
"@nx/nx-freebsd-x64": "24.0.0-beta.1",
|
|
156
|
+
"@nx/nx-linux-arm-gnueabihf": "24.0.0-beta.1",
|
|
157
|
+
"@nx/nx-linux-arm64-gnu": "24.0.0-beta.1",
|
|
158
|
+
"@nx/nx-linux-arm64-musl": "24.0.0-beta.1",
|
|
159
|
+
"@nx/nx-linux-x64-gnu": "24.0.0-beta.1",
|
|
160
|
+
"@nx/nx-linux-x64-musl": "24.0.0-beta.1",
|
|
161
|
+
"@nx/nx-win32-arm64-msvc": "24.0.0-beta.1",
|
|
162
|
+
"@nx/nx-win32-x64-msvc": "24.0.0-beta.1"
|
|
172
163
|
},
|
|
173
164
|
"nx-migrations": {
|
|
174
165
|
"migrations": "./migrations.json",
|
package/readme-template.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<p
|
|
1
|
+
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-
|
|
4
|
-
<img alt="Nx
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo-light.svg">
|
|
4
|
+
<img alt="Nx Logo" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.svg" width="140">
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
package/schemas/nx-schema.json
CHANGED
|
@@ -615,6 +615,59 @@
|
|
|
615
615
|
}
|
|
616
616
|
},
|
|
617
617
|
"definitions": {
|
|
618
|
+
"readyWhen": {
|
|
619
|
+
"description": "How Nx detects that this continuous target is ready. Dependents opt in with \"waitFor\": \"ready\" on their dependsOn entry. Strings may use the {workspaceRoot}, {projectRoot}, {projectName} and {options.*} placeholders, resolved against the options the task runs with.",
|
|
620
|
+
"type": "object",
|
|
621
|
+
"properties": {
|
|
622
|
+
"url": {
|
|
623
|
+
"type": "string",
|
|
624
|
+
"description": "Ready when an HTTP or HTTPS GET gets a status from 200 to 403. A redirect counts as ready and is not followed"
|
|
625
|
+
},
|
|
626
|
+
"port": {
|
|
627
|
+
"type": ["integer", "string"],
|
|
628
|
+
"minimum": 1,
|
|
629
|
+
"maximum": 65535,
|
|
630
|
+
"description": "Ready when a TCP connection to this port succeeds. A string must resolve to a port number through a placeholder such as {options.port}"
|
|
631
|
+
},
|
|
632
|
+
"host": {
|
|
633
|
+
"type": "string",
|
|
634
|
+
"description": "Host to connect to with port. By default 127.0.0.1 and ::1 are both tried"
|
|
635
|
+
},
|
|
636
|
+
"command": {
|
|
637
|
+
"type": "string",
|
|
638
|
+
"description": "Ready when this command, run through the shell at the workspace root, exits with code 0"
|
|
639
|
+
},
|
|
640
|
+
"logMatches": {
|
|
641
|
+
"description": "Ready when every one of these substrings has appeared in the task's output",
|
|
642
|
+
"oneOf": [
|
|
643
|
+
{ "type": "string" },
|
|
644
|
+
{
|
|
645
|
+
"type": "array",
|
|
646
|
+
"items": { "type": "string" },
|
|
647
|
+
"minItems": 1
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
},
|
|
651
|
+
"timeout": {
|
|
652
|
+
"type": "integer",
|
|
653
|
+
"minimum": 1,
|
|
654
|
+
"default": 60000,
|
|
655
|
+
"description": "Milliseconds to wait for readiness before failing dependents that wait for it"
|
|
656
|
+
},
|
|
657
|
+
"interval": {
|
|
658
|
+
"type": "integer",
|
|
659
|
+
"minimum": 1,
|
|
660
|
+
"description": "Milliseconds between probe attempts. By default attempts back off from 100 to 1000 milliseconds. Unused by logMatches"
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
"oneOf": [
|
|
664
|
+
{ "required": ["url"] },
|
|
665
|
+
{ "required": ["port"] },
|
|
666
|
+
{ "required": ["command"] },
|
|
667
|
+
{ "required": ["logMatches"] }
|
|
668
|
+
],
|
|
669
|
+
"additionalProperties": false
|
|
670
|
+
},
|
|
618
671
|
"ownersPattern": {
|
|
619
672
|
"type": "object",
|
|
620
673
|
"properties": {
|
|
@@ -912,6 +965,9 @@
|
|
|
912
965
|
"continuous": {
|
|
913
966
|
"$ref": "#/definitions/targetDefaultsConfig/properties/continuous"
|
|
914
967
|
},
|
|
968
|
+
"readyWhen": {
|
|
969
|
+
"$ref": "#/definitions/targetDefaultsConfig/properties/readyWhen"
|
|
970
|
+
},
|
|
915
971
|
"parallelism": {
|
|
916
972
|
"$ref": "#/definitions/targetDefaultsConfig/properties/parallelism"
|
|
917
973
|
},
|
|
@@ -975,6 +1031,9 @@
|
|
|
975
1031
|
"default": false,
|
|
976
1032
|
"description": "Whether this target runs continuously until stopped"
|
|
977
1033
|
},
|
|
1034
|
+
"readyWhen": {
|
|
1035
|
+
"$ref": "#/definitions/readyWhen"
|
|
1036
|
+
},
|
|
978
1037
|
"parallelism": {
|
|
979
1038
|
"type": "boolean",
|
|
980
1039
|
"default": true,
|
|
@@ -1021,6 +1080,17 @@
|
|
|
1021
1080
|
"description": "Configuration for params handling.",
|
|
1022
1081
|
"enum": ["ignore", "forward"],
|
|
1023
1082
|
"default": "ignore"
|
|
1083
|
+
},
|
|
1084
|
+
"inputs": {
|
|
1085
|
+
"type": "boolean",
|
|
1086
|
+
"description": "Set to false to keep a continuous dependency's inputs out of this target's hash and affected calculation. Has no effect on dependencies that are not continuous.",
|
|
1087
|
+
"default": true
|
|
1088
|
+
},
|
|
1089
|
+
"waitFor": {
|
|
1090
|
+
"type": "string",
|
|
1091
|
+
"description": "For a continuous dependency, whether to wait until it has started or until it is ready per its readyWhen. Ignored when the dependency is not continuous or has no readyWhen.",
|
|
1092
|
+
"enum": ["started", "ready"],
|
|
1093
|
+
"default": "started"
|
|
1024
1094
|
}
|
|
1025
1095
|
},
|
|
1026
1096
|
"oneOf": [
|