@depup/nx 23.1.2-depup.1 → 23.2.0-depup.0
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 +7 -6
- package/changes.json +12 -8
- package/dist/bin/init-local.js +3 -5
- package/dist/bin/nx.js +1 -0
- package/dist/src/adapter/ngcli-adapter.js +60 -1
- package/dist/src/ai/constants.d.ts +15 -0
- package/dist/src/ai/constants.js +17 -1
- package/dist/src/ai/set-up-ai-agents/set-up-ai-agents.js +57 -9
- package/dist/src/ai/utils.d.ts +13 -0
- package/dist/src/ai/utils.js +29 -0
- package/dist/src/analytics/analytics.js +2 -4
- package/dist/src/command-line/completion/command-object.js +7 -12
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +42 -31
- package/dist/src/command-line/format/format.js +84 -101
- package/dist/src/command-line/generate/generate.js +35 -35
- package/dist/src/command-line/import/import.js +29 -38
- package/dist/src/command-line/init/ai-agent-prompts.js +5 -11
- package/dist/src/command-line/init/command-object.js +4 -2
- package/dist/src/command-line/init/configure-plugins.js +4 -1
- package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +12 -32
- package/dist/src/command-line/init/implementation/add-nx-to-nest.js +17 -21
- package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +8 -21
- package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +3 -17
- package/dist/src/command-line/init/implementation/angular/index.js +10 -15
- package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +2 -0
- package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +8 -2
- package/dist/src/command-line/init/implementation/check-compatible-with-plugins.js +2 -0
- package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +5 -0
- package/dist/src/command-line/init/implementation/format.d.ts +18 -0
- package/dist/src/command-line/init/implementation/format.js +114 -0
- package/dist/src/command-line/init/implementation/utils.js +8 -8
- package/dist/src/command-line/init/init-v1.js +15 -18
- package/dist/src/command-line/init/init-v2.js +77 -66
- package/dist/src/command-line/migrate/agentic/definitions.js +36 -9
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +37 -22
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +30 -39
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +7 -7
- package/dist/src/command-line/migrate/agentic/handoff.js +13 -10
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +7 -12
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +7 -0
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +10 -0
- package/dist/src/command-line/migrate/agentic/run-step.js +1 -0
- package/dist/src/command-line/migrate/agentic/runner.js +24 -24
- package/dist/src/command-line/migrate/agentic/select.d.ts +10 -2
- package/dist/src/command-line/migrate/agentic/select.js +30 -32
- package/dist/src/command-line/migrate/agentic/types.d.ts +33 -3
- package/dist/src/command-line/migrate/agentic/types.js +35 -0
- package/dist/src/command-line/migrate/command-object.d.ts +6 -0
- package/dist/src/command-line/migrate/command-object.js +19 -1
- package/dist/src/command-line/migrate/execute-migration.d.ts +28 -9
- package/dist/src/command-line/migrate/execute-migration.js +112 -27
- package/dist/src/command-line/migrate/migrate-analytics.d.ts +35 -0
- package/dist/src/command-line/migrate/migrate-analytics.js +64 -0
- package/dist/src/command-line/migrate/migrate-commits.d.ts +38 -8
- package/dist/src/command-line/migrate/migrate-commits.js +130 -15
- package/dist/src/command-line/migrate/migrate-config.d.ts +13 -12
- package/dist/src/command-line/migrate/migrate-config.js +22 -14
- package/dist/src/command-line/migrate/migrate-output.d.ts +45 -7
- package/dist/src/command-line/migrate/migrate-output.js +52 -8
- package/dist/src/command-line/migrate/migrate-ui-api.d.ts +2 -1
- package/dist/src/command-line/migrate/migrate-ui-api.js +16 -7
- package/dist/src/command-line/migrate/migrate.d.ts +24 -55
- package/dist/src/command-line/migrate/migrate.js +553 -226
- package/dist/src/command-line/migrate/migration-shape.d.ts +8 -0
- package/dist/src/command-line/migrate/multi-major.js +7 -10
- package/dist/src/command-line/migrate/resolve-package-version.js +4 -8
- package/dist/src/command-line/migrate/run/agent-output.d.ts +20 -0
- package/dist/src/command-line/migrate/run/agent-output.js +65 -0
- package/dist/src/command-line/migrate/run/index.d.ts +9 -0
- package/dist/src/command-line/migrate/run/index.js +23 -0
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +20 -0
- package/dist/src/command-line/migrate/run/orchestrator.js +1200 -0
- package/dist/src/command-line/migrate/run/run-id.d.ts +16 -0
- package/dist/src/command-line/migrate/run/run-id.js +56 -0
- package/dist/src/command-line/migrate/run/run-state.d.ts +157 -0
- package/dist/src/command-line/migrate/run/run-state.js +438 -0
- package/dist/src/command-line/migrate/run/state-lock.d.ts +25 -0
- package/dist/src/command-line/migrate/run/state-lock.js +76 -0
- package/dist/src/command-line/migrate/run/state-machine.d.ts +84 -0
- package/dist/src/command-line/migrate/run/state-machine.js +315 -0
- package/dist/src/command-line/migrate/run/util.d.ts +59 -0
- package/dist/src/command-line/migrate/run/util.js +148 -0
- package/dist/src/command-line/migrate/run/worker.d.ts +16 -0
- package/dist/src/command-line/migrate/run/worker.js +682 -0
- package/dist/src/command-line/migrate/run-migration-process.js +6 -5
- package/dist/src/command-line/migrate/safe-prompt.d.ts +14 -26
- package/dist/src/command-line/migrate/safe-prompt.js +32 -43
- package/dist/src/command-line/migrate/sort-migrations.d.ts +16 -0
- package/dist/src/command-line/migrate/sort-migrations.js +32 -0
- package/dist/src/command-line/migrate/step-actions.d.ts +5 -0
- package/dist/src/command-line/migrate/step-actions.js +12 -0
- package/dist/src/command-line/migrate/text.d.ts +18 -0
- package/dist/src/command-line/migrate/text.js +26 -0
- package/dist/src/command-line/migrate/version-skew-guard.d.ts +60 -0
- package/dist/src/command-line/migrate/version-skew-guard.js +175 -0
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +16 -20
- package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.d.ts +2 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.js +16 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.d.ts +4 -0
- package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.js +13 -0
- package/dist/src/command-line/nx-commands.js +26 -24
- package/dist/src/command-line/release/changelog.js +6 -17
- package/dist/src/command-line/release/plan.js +15 -28
- package/dist/src/command-line/release/publish.js +5 -0
- package/dist/src/command-line/release/release.js +6 -17
- package/dist/src/command-line/release/utils/release-graph.d.ts +1 -0
- package/dist/src/command-line/release/utils/release-graph.js +10 -2
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +10 -22
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +13 -22
- package/dist/src/command-line/release/utils/resolve-semver-specifier.js +19 -41
- package/dist/src/command-line/release/version/resolve-current-version.js +7 -10
- package/dist/src/command-line/release/version.js +2 -2
- package/dist/src/command-line/reset/reset.js +17 -15
- package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
- package/dist/src/command-line/yargs-utils/shared-options.js +3 -1
- package/dist/src/config/misc-interfaces.d.ts +19 -7
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +2 -7
- package/dist/src/daemon/client/client.js +113 -28
- package/dist/src/daemon/client/daemon-environment.d.ts +35 -0
- package/dist/src/daemon/client/daemon-environment.js +106 -6
- package/dist/src/daemon/client/daemon-socket-messenger.d.ts +1 -1
- package/dist/src/daemon/client/daemon-socket-messenger.js +8 -3
- package/dist/src/daemon/is-v8-serializer-enabled.d.ts +2 -2
- package/dist/src/daemon/is-v8-serializer-enabled.js +2 -2
- package/dist/src/daemon/message-types/daemon-message.d.ts +2 -1
- package/dist/src/daemon/message-types/daemon-message.js +25 -2
- package/dist/src/daemon/sandbox-socket-hint.d.ts +27 -0
- package/dist/src/daemon/sandbox-socket-hint.js +136 -0
- package/dist/src/daemon/server/client-socket-context.d.ts +1 -1
- package/dist/src/daemon/server/client-socket-context.js +2 -3
- package/dist/src/daemon/server/dotenv-graph-changes.d.ts +97 -0
- package/dist/src/daemon/server/dotenv-graph-changes.js +284 -0
- package/dist/src/daemon/server/handle-client-env.d.ts +8 -0
- package/dist/src/daemon/server/handle-client-env.js +92 -0
- package/dist/src/daemon/server/handle-outputs-changes.d.ts +10 -0
- package/dist/src/daemon/server/handle-outputs-changes.js +86 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +21 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +159 -6
- package/dist/src/daemon/server/server.js +36 -72
- package/dist/src/daemon/server/shutdown-utils.d.ts +1 -1
- package/dist/src/daemon/server/shutdown-utils.js +2 -2
- package/dist/src/daemon/socket-utils.d.ts +26 -7
- package/dist/src/daemon/socket-utils.js +45 -31
- package/dist/src/daemon/tmp-dir.d.ts +10 -0
- package/dist/src/daemon/tmp-dir.js +21 -37
- package/dist/src/devkit-internals.d.ts +20 -9
- package/dist/src/devkit-internals.js +49 -6
- package/dist/src/executors/run-commands/running-tasks.js +46 -29
- package/dist/src/generators/internal-utils/{format-changed-files-with-prettier-if-available.d.ts → format-changed-files.d.ts} +6 -6
- package/dist/src/generators/internal-utils/format-changed-files.js +163 -0
- package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.d.ts +15 -0
- package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.js +21 -3
- package/dist/src/migrations/update-15-0-0/prefix-outputs.js +2 -2
- package/dist/src/migrations/update-16-0-0/remove-nrwl-cli.js +2 -2
- package/dist/src/migrations/update-16-0-0/update-depends-on-to-tokens.js +2 -2
- package/dist/src/migrations/update-16-0-0/update-nx-cloud-runner.js +2 -2
- package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +2 -2
- package/dist/src/migrations/update-17-0-0/rm-default-collection-npm-scope.js +2 -2
- package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +2 -2
- package/dist/src/migrations/update-17-2-0/move-default-base.js +2 -2
- package/dist/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.js +2 -2
- package/dist/src/migrations/update-19-2-4/set-project-name.js +2 -2
- package/dist/src/migrations/update-20-0-0/move-use-daemon-process.js +2 -2
- package/dist/src/migrations/update-20-0-1/use-legacy-cache.js +2 -2
- package/dist/src/migrations/update-21-0-0/release-changelog-config-changes.js +2 -2
- package/dist/src/migrations/update-21-0-0/release-version-config-changes.js +2 -2
- package/dist/src/migrations/update-21-0-0/remove-custom-tasks-runner.js +2 -2
- package/dist/src/migrations/update-21-0-0/remove-legacy-cache.js +2 -2
- package/dist/src/migrations/update-22-0-0/consolidate-release-tag-config.js +2 -2
- package/dist/src/migrations/update-22-0-0/release-version-config-changes.js +2 -2
- package/dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore.js +2 -2
- package/dist/src/migrations/update-22-6-0/add-claude-settings-local-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-22-6-0/add-claude-worktrees-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-22-7-0/add-polygraph-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.js +2 -2
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +2 -2
- package/dist/src/native/index.d.ts +7 -3
- 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/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +3 -3
- package/dist/src/nx-cloud/utilities/url-shorten.d.ts +1 -4
- package/dist/src/nx-cloud/utilities/url-shorten.js +9 -8
- package/dist/src/plugins/js/lock-file/lock-file.d.ts +46 -4
- package/dist/src/plugins/js/lock-file/lock-file.js +192 -15
- package/dist/src/plugins/js/lock-file/npm-parser.js +10 -3
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +356 -62
- package/dist/src/plugins/js/lock-file/project-graph-pruning.d.ts +25 -1
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +78 -4
- package/dist/src/plugins/js/lock-file/pruned-output.d.ts +330 -0
- package/dist/src/plugins/js/lock-file/pruned-output.js +1441 -0
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +1 -0
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +6 -5
- package/dist/src/plugins/js/utils/register.d.ts +11 -0
- package/dist/src/plugins/js/utils/register.js +40 -6
- package/dist/src/project-graph/plugins/get-plugins.d.ts +27 -1
- package/dist/src/project-graph/plugins/get-plugins.js +110 -5
- package/dist/src/project-graph/plugins/isolation/enabled.js +1 -4
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +16 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +69 -11
- package/dist/src/project-graph/plugins/isolation/messaging.js +1 -3
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +23 -2
- package/dist/src/project-graph/project-graph.js +27 -2
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +7 -0
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +14 -6
- package/dist/src/project-graph/utils/project-configuration/target-defaults.js +47 -21
- package/dist/src/tasks-runner/is-tui-enabled.js +3 -1
- package/dist/src/tasks-runner/life-cycle.d.ts +2 -2
- package/dist/src/tasks-runner/life-cycle.js +2 -2
- package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +12 -0
- package/dist/src/tasks-runner/life-cycles/performance-report.js +62 -3
- package/dist/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.d.ts +26 -1
- package/dist/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.js +99 -5
- package/dist/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.d.ts +26 -0
- package/dist/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.js +113 -17
- package/dist/src/tasks-runner/pseudo-ipc.js +14 -13
- package/dist/src/tasks-runner/pseudo-terminal.d.ts +17 -3
- package/dist/src/tasks-runner/pseudo-terminal.js +22 -10
- package/dist/src/tasks-runner/run-command.js +32 -39
- package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +55 -0
- package/dist/src/tasks-runner/running-tasks/batch-process.js +161 -8
- package/dist/src/tasks-runner/task-env.d.ts +25 -0
- package/dist/src/tasks-runner/task-env.js +39 -0
- package/dist/src/tasks-runner/task-orchestrator.d.ts +68 -0
- package/dist/src/tasks-runner/task-orchestrator.js +159 -8
- package/dist/src/utils/analytics-enabled.d.ts +7 -0
- package/dist/src/utils/analytics-enabled.js +11 -0
- package/dist/src/utils/analytics-prompt.js +4 -7
- package/dist/src/utils/cache-directory.d.ts +62 -3
- package/dist/src/utils/cache-directory.js +276 -25
- package/dist/src/utils/catalog/bun-manager-utils.d.ts +8 -0
- package/dist/src/utils/catalog/bun-manager-utils.js +174 -0
- package/dist/src/utils/catalog/bun-manager.d.ts +28 -0
- package/dist/src/utils/catalog/bun-manager.js +130 -0
- package/dist/src/utils/catalog/index.d.ts +1 -1
- package/dist/src/utils/catalog/index.js +1 -1
- package/dist/src/utils/catalog/manager-factory.js +3 -0
- package/dist/src/utils/catalog/manager-utils.d.ts +8 -1
- package/dist/src/utils/catalog/manager-utils.js +9 -4
- package/dist/src/utils/catalog/manager.d.ts +13 -1
- package/dist/src/utils/catalog/manager.js +30 -0
- package/dist/src/utils/catalog/pnpm-manager.d.ts +2 -1
- package/dist/src/utils/catalog/pnpm-manager.js +3 -0
- package/dist/src/utils/catalog/types.d.ts +4 -0
- package/dist/src/utils/catalog/yarn-manager.d.ts +6 -2
- package/dist/src/utils/catalog/yarn-manager.js +23 -32
- package/dist/src/utils/child-process.d.ts +31 -1
- package/dist/src/utils/child-process.js +103 -0
- package/dist/src/utils/chunkify.d.ts +7 -1
- package/dist/src/utils/chunkify.js +10 -3
- package/dist/src/utils/consume-messages-from-socket.d.ts +42 -7
- package/dist/src/utils/consume-messages-from-socket.js +273 -38
- package/dist/src/utils/db-connection.js +8 -16
- package/dist/src/utils/exit-codes.d.ts +9 -0
- package/dist/src/utils/exit-codes.js +19 -0
- package/dist/src/utils/formatters/index.d.ts +13 -0
- package/dist/src/utils/formatters/index.js +74 -0
- package/dist/src/utils/formatters/oxfmt.d.ts +32 -0
- package/dist/src/utils/formatters/oxfmt.js +764 -0
- package/dist/src/utils/formatters/prettier.d.ts +26 -0
- package/dist/src/utils/formatters/prettier.js +201 -0
- package/dist/src/utils/formatters/shared.d.ts +5 -0
- package/dist/src/utils/formatters/shared.js +8 -0
- package/dist/src/utils/git-utils.d.ts +38 -2
- package/dist/src/utils/git-utils.js +422 -19
- package/dist/src/utils/has-nx-js-plugin.d.ts +1 -1
- package/dist/src/utils/has-nx-js-plugin.js +1 -1
- package/dist/src/utils/ignore.d.ts +47 -5
- package/dist/src/utils/ignore.js +93 -11
- package/dist/src/utils/kill-child-on-host-exit.d.ts +7 -0
- package/dist/src/utils/kill-child-on-host-exit.js +42 -0
- package/dist/src/utils/nx-console-prompt.js +3 -6
- package/dist/src/utils/nx-tmp-dir.d.ts +5 -0
- package/dist/src/utils/nx-tmp-dir.js +7 -2
- package/dist/src/utils/output.d.ts +153 -0
- package/dist/src/utils/output.js +289 -5
- package/dist/src/utils/owned-private-dir.d.ts +13 -0
- package/dist/src/utils/owned-private-dir.js +39 -0
- package/dist/src/utils/package-json.d.ts +2 -0
- package/dist/src/utils/package-manager.js +17 -7
- package/dist/src/utils/params.d.ts +7 -1
- package/dist/src/utils/params.js +64 -6
- package/dist/src/utils/permission-errors.d.ts +11 -0
- package/dist/src/utils/permission-errors.js +17 -0
- package/dist/src/utils/plugins/core-plugins.js +4 -0
- package/dist/src/utils/prompt-helpers.d.ts +53 -0
- package/dist/src/utils/prompt-helpers.js +111 -0
- package/dist/src/utils/socket-refused-exit-code.d.ts +15 -0
- package/dist/src/utils/socket-refused-exit-code.js +18 -0
- package/dist/src/utils/write-formatted-json-file.d.ts +3 -2
- package/dist/src/utils/write-formatted-json-file.js +5 -4
- package/package.json +33 -24
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +0 -88
- package/dist/src/utils/is-using-prettier.d.ts +0 -3
- package/dist/src/utils/is-using-prettier.js +0 -62
|
@@ -23,7 +23,8 @@ type WorkspaceScopedMessage = {
|
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
25
|
* A message from a different workspace — two sharing an NX_SOCKET_DIR — must not
|
|
26
|
-
* be processed.
|
|
26
|
+
* be processed. Roots are normalized first so that a workspace does not look
|
|
27
|
+
* foreign to itself; see {@link normalizeWorkspaceRoot}.
|
|
27
28
|
*/
|
|
28
29
|
export declare function isForeignWorkspaceMessage(msg: WorkspaceScopedMessage, receiverWorkspaceRoot: string): boolean;
|
|
29
30
|
/**
|
|
@@ -3,18 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.isDaemonMessage = isDaemonMessage;
|
|
4
4
|
exports.isForeignWorkspaceMessage = isForeignWorkspaceMessage;
|
|
5
5
|
exports.assertNotForeignWorkspaceMessage = assertNotForeignWorkspaceMessage;
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
6
7
|
function isDaemonMessage(msg) {
|
|
7
8
|
return typeof msg === 'object' && msg && 'type' in msg;
|
|
8
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Puts a workspace root into a form two independently-resolved spellings of the
|
|
12
|
+
* same directory agree on.
|
|
13
|
+
*
|
|
14
|
+
* The sender's root and the receiver's root do not always come out of the same
|
|
15
|
+
* branch of `workspaceRootInner`: one side can be handed `NX_WORKSPACE_ROOT_PATH`
|
|
16
|
+
* verbatim while the other walks up from `process.cwd()`. On Windows those two
|
|
17
|
+
* sources routinely disagree on the case of the drive letter — an editor or
|
|
18
|
+
* agent may export `d:\repo` while `process.cwd()` reports `D:\repo` — and on
|
|
19
|
+
* separators. Both address the same directory: Windows paths are
|
|
20
|
+
* case-insensitive, and a drive letter is case-insensitive without exception.
|
|
21
|
+
*
|
|
22
|
+
* POSIX roots keep their case, where `/repo` and `/REPO` really can be two
|
|
23
|
+
* directories.
|
|
24
|
+
*/
|
|
25
|
+
function normalizeWorkspaceRoot(workspaceRoot) {
|
|
26
|
+
return process.platform === 'win32'
|
|
27
|
+
? node_path_1.win32.normalize(workspaceRoot).toLowerCase()
|
|
28
|
+
: node_path_1.posix.normalize(workspaceRoot);
|
|
29
|
+
}
|
|
9
30
|
/**
|
|
10
31
|
* A message from a different workspace — two sharing an NX_SOCKET_DIR — must not
|
|
11
|
-
* be processed.
|
|
32
|
+
* be processed. Roots are normalized first so that a workspace does not look
|
|
33
|
+
* foreign to itself; see {@link normalizeWorkspaceRoot}.
|
|
12
34
|
*/
|
|
13
35
|
function isForeignWorkspaceMessage(msg, receiverWorkspaceRoot) {
|
|
14
36
|
if (msg.workspaceRoot === undefined) {
|
|
15
37
|
return false;
|
|
16
38
|
}
|
|
17
|
-
return msg.workspaceRoot !==
|
|
39
|
+
return (normalizeWorkspaceRoot(msg.workspaceRoot) !==
|
|
40
|
+
normalizeWorkspaceRoot(receiverWorkspaceRoot));
|
|
18
41
|
}
|
|
19
42
|
/**
|
|
20
43
|
* Throws on a message this receiver must not act on. The daemon catches it and
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface SandboxSocketHintOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Pass `true` only when an errno proves the operating system refused the
|
|
4
|
+
* socket (`EPERM`/`EACCES` on a bind or connect). Most callers reach this
|
|
5
|
+
* hint holding a failure that denied permission only explains — a worker
|
|
6
|
+
* that died before it reported anything, an internal daemon error — and for
|
|
7
|
+
* those the lead line offers the cause rather than asserting it.
|
|
8
|
+
*/
|
|
9
|
+
certain?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Guidance for a unix socket Nx was not allowed to use. The lead line names
|
|
13
|
+
* denied permission, which is the part Nx can observe; a sandbox is the most
|
|
14
|
+
* common source but not the only one, so which remedy applies is left to the
|
|
15
|
+
* list rather than decided in the first sentence.
|
|
16
|
+
*
|
|
17
|
+
* Every sandbox-shaped line — the `configure-ai-agents` remediation, the
|
|
18
|
+
* per-agent setting, the knowledge base link — is dropped unless a sandbox is
|
|
19
|
+
* actually running. A root-owned socket dir on a plain workstation reaches this
|
|
20
|
+
* hint too, and there the permission line is the whole truth, so it is stated
|
|
21
|
+
* outright rather than offered as the one item of a list.
|
|
22
|
+
*
|
|
23
|
+
* Lives in its own module (rather than the daemon client) so that plugin
|
|
24
|
+
* workers and the plugin host can use it without pulling in the daemon
|
|
25
|
+
* client's module-level `DaemonClient` singleton.
|
|
26
|
+
*/
|
|
27
|
+
export declare function sandboxSocketHint({ certain, }?: SandboxSocketHintOptions): string[];
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sandboxSocketHint = sandboxSocketHint;
|
|
4
|
+
const native_1 = require("../native");
|
|
5
|
+
const is_sandbox_1 = require("../utils/is-sandbox");
|
|
6
|
+
const nx_tmp_dir_1 = require("../utils/nx-tmp-dir");
|
|
7
|
+
/**
|
|
8
|
+
* The containers a sandbox has to allow, best first — not the directory finally
|
|
9
|
+
* used. Socket resolution walks a chain and the winning tier depends on what it
|
|
10
|
+
* could establish, so naming one root would be wrong wherever the other was
|
|
11
|
+
* picked. These are also the paths an allowlist entry covers: everything Nx
|
|
12
|
+
* binds lives beneath them.
|
|
13
|
+
*
|
|
14
|
+
* The home root is shown as the literal `~/.nx` rather than expanded, because a
|
|
15
|
+
* committed allowlist entry has to expand per user to be worth sharing.
|
|
16
|
+
*
|
|
17
|
+
* A configured NX_SOCKET_DIR overrides the chain entirely, so it is named on its
|
|
18
|
+
* own — pointing someone at the default roots when their sockets are somewhere
|
|
19
|
+
* else is worse than saying nothing. `||` rather than `??` matches how
|
|
20
|
+
* `tmp-dir` reads it: an empty value means unset.
|
|
21
|
+
*
|
|
22
|
+
* Read from `utils/nx-tmp-dir` rather than from `daemon/tmp-dir`, whose module
|
|
23
|
+
* scope establishes directories and resolves a socket tier on import. Plugin
|
|
24
|
+
* workers print this hint, and must not pay that to do it.
|
|
25
|
+
*/
|
|
26
|
+
function socketRoots() {
|
|
27
|
+
const configured = process.env.NX_SOCKET_DIR || process.env.NX_DAEMON_SOCKET_DIR;
|
|
28
|
+
if (configured) {
|
|
29
|
+
return configured;
|
|
30
|
+
}
|
|
31
|
+
return [nx_tmp_dir_1.NX_TMP_DIR, nx_tmp_dir_1.NX_HOME_TMP_DIR ? '~/.nx' : undefined]
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.join(' or ');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Guidance for a unix socket Nx was not allowed to use. The lead line names
|
|
37
|
+
* denied permission, which is the part Nx can observe; a sandbox is the most
|
|
38
|
+
* common source but not the only one, so which remedy applies is left to the
|
|
39
|
+
* list rather than decided in the first sentence.
|
|
40
|
+
*
|
|
41
|
+
* Every sandbox-shaped line — the `configure-ai-agents` remediation, the
|
|
42
|
+
* per-agent setting, the knowledge base link — is dropped unless a sandbox is
|
|
43
|
+
* actually running. A root-owned socket dir on a plain workstation reaches this
|
|
44
|
+
* hint too, and there the permission line is the whole truth, so it is stated
|
|
45
|
+
* outright rather than offered as the one item of a list.
|
|
46
|
+
*
|
|
47
|
+
* Lives in its own module (rather than the daemon client) so that plugin
|
|
48
|
+
* workers and the plugin host can use it without pulling in the daemon
|
|
49
|
+
* client's module-level `DaemonClient` singleton.
|
|
50
|
+
*/
|
|
51
|
+
function sandboxSocketHint({ certain = false, } = {}) {
|
|
52
|
+
const roots = socketRoots();
|
|
53
|
+
const sandboxRemedy = sandboxSpecificRemedy(roots, certain);
|
|
54
|
+
const lead = certain
|
|
55
|
+
? `Nx was denied permission to use its unix socket under ${roots}.`
|
|
56
|
+
: `Nx could not use its unix socket under ${roots}. Denied permission on that directory is a common cause.`;
|
|
57
|
+
const permissionRemedy = `Get permission to create sockets under ${roots}, or set NX_SOCKET_DIR to a directory you do have permission for.`;
|
|
58
|
+
if (sandboxRemedy.length === 0) {
|
|
59
|
+
return [lead, permissionRemedy];
|
|
60
|
+
}
|
|
61
|
+
return [
|
|
62
|
+
lead,
|
|
63
|
+
'To fix this, do one of the following:',
|
|
64
|
+
// Only Claude Code: the generator's `sandbox` block sits inside
|
|
65
|
+
// `hasAgent('claude')`, so offering this to a Codex or Copilot CLI user
|
|
66
|
+
// sends them to a command that writes nothing they need.
|
|
67
|
+
...((0, native_1.detectAiAgent)() === 'claude'
|
|
68
|
+
? [
|
|
69
|
+
' - Run `nx configure-ai-agents` to write the sandbox allowances Nx needs. Agent sandboxes usually block writes to their own settings file, so this may have to be run from a regular terminal rather than through the agent.',
|
|
70
|
+
]
|
|
71
|
+
: []),
|
|
72
|
+
...sandboxRemedy.map((remedy) => ` - ${remedy}`),
|
|
73
|
+
` - ${permissionRemedy}`,
|
|
74
|
+
'See https://nx.dev/docs/kb/nx-sandbox-unix-sockets for details.',
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The per-agent form of "allow sockets under these roots", or nothing at all
|
|
79
|
+
* when nothing indicates a sandbox.
|
|
80
|
+
*
|
|
81
|
+
* A detected agent is not itself that indication: every Claude Code session sets
|
|
82
|
+
* `CLAUDECODE`, but only a sandboxed one sets `SANDBOX_RUNTIME` (measured on
|
|
83
|
+
* 2.1.248 with `sandbox.enabled` both ways). What counts is `isSandbox()`, or —
|
|
84
|
+
* for a *known* agent — an errno proving the socket was refused. Copilot CLI
|
|
85
|
+
* needs that second arm: its sandbox sets no variable `isSandbox()` reads
|
|
86
|
+
* (measured on 1.0.80, where a bind under both roots is refused while `$TMPDIR`
|
|
87
|
+
* succeeds), so the errno is the only evidence Nx ever gets. An unknown
|
|
88
|
+
* environment gets no such benefit, because there a refusal is just a refusal —
|
|
89
|
+
* a root-owned socket dir left by `sudo nx` reads identically.
|
|
90
|
+
*
|
|
91
|
+
* The agent then decides *which* setting to name, because each sandbox gates
|
|
92
|
+
* socket creation on something different and a single generic sentence sends at
|
|
93
|
+
* least one of them to a setting that does not exist:
|
|
94
|
+
*
|
|
95
|
+
* - Claude Code gates on the path allowlist. A scoped `allowUnixSockets` entry
|
|
96
|
+
* covers binding as well as connecting, so it is sufficient and preferable to
|
|
97
|
+
* `allowAllUnixSockets`, which opens every socket on the machine.
|
|
98
|
+
* - Codex gates on `network_access` instead, and has no path-scoped socket
|
|
99
|
+
* setting. `writable_roots` alone does not unblock a bind. That grant is a
|
|
100
|
+
* broad one, so the line says what it costs rather than presenting it as
|
|
101
|
+
* equivalent to the scoped entries.
|
|
102
|
+
* - Copilot CLI gates on the path like Claude, but reads its sandbox policy
|
|
103
|
+
* only from the user-level settings file, so this is the one agent
|
|
104
|
+
* `configure-ai-agents` cannot configure by writing into the workspace.
|
|
105
|
+
*
|
|
106
|
+
* Any other agent, or none, leaves the setting unknown, so the fallback can only
|
|
107
|
+
* describe the grant.
|
|
108
|
+
*/
|
|
109
|
+
function sandboxSpecificRemedy(roots, certain) {
|
|
110
|
+
const sandboxed = (0, is_sandbox_1.isSandbox)();
|
|
111
|
+
const agent = (0, native_1.detectAiAgent)();
|
|
112
|
+
const knownAgent = agent === 'claude' || agent === 'codex' || agent === 'copilot-cli';
|
|
113
|
+
if (knownAgent && !sandboxed && !certain) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
switch (agent) {
|
|
117
|
+
case 'claude':
|
|
118
|
+
return [
|
|
119
|
+
`Add ${roots} to \`sandbox.network.allowUnixSockets\` and to \`sandbox.filesystem.allowRead\`/\`allowWrite\` in \`.claude/settings.json\`. The scoped entry covers creating sockets, not only connecting to them.`,
|
|
120
|
+
];
|
|
121
|
+
case 'copilot-cli':
|
|
122
|
+
return [
|
|
123
|
+
`Add ${roots} to \`sandbox.userPolicy.filesystem.readwritePaths\` in \`~/.copilot/settings.json\`. Copilot CLI reads sandbox policy only from that user-level file, so a copy committed to the workspace has no effect.`,
|
|
124
|
+
];
|
|
125
|
+
case 'codex':
|
|
126
|
+
return [
|
|
127
|
+
`In \`~/.codex/config.toml\`, set \`sandbox_workspace_write.network_access\` to true and add ${roots} to \`sandbox_workspace_write.writable_roots\`. Codex has no setting that scopes sockets to a path, and \`network_access\` also grants general network access, so this is a broader grant than the other agents need.`,
|
|
128
|
+
];
|
|
129
|
+
default:
|
|
130
|
+
return sandboxed
|
|
131
|
+
? [
|
|
132
|
+
`Allow your sandbox to create unix sockets under ${roots}, not only connect to them, and grant read and write on those directories.`,
|
|
133
|
+
]
|
|
134
|
+
: [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -7,7 +7,7 @@ export declare function getTopicSubscribers(topic: ProgressTopic): Set<Socket>;
|
|
|
7
7
|
export declare function assertOnDaemon(helperName: string): void;
|
|
8
8
|
/**
|
|
9
9
|
* Writes a streaming message over the given socket using the daemon's
|
|
10
|
-
* configured serialization format
|
|
10
|
+
* configured serialization format, length-prefixed like any other message.
|
|
11
11
|
* Errors are logged to the daemon's stdout (redirected to the daemon
|
|
12
12
|
* log) rather than propagated — a disconnected client shouldn't tear
|
|
13
13
|
* down the current request handler or other subscribers.
|
|
@@ -7,7 +7,6 @@ exports.assertOnDaemon = assertOnDaemon;
|
|
|
7
7
|
exports.writeStreamingMessage = writeStreamingMessage;
|
|
8
8
|
exports.sendProgressMessageToTopic = sendProgressMessageToTopic;
|
|
9
9
|
exports.sendEmitLogMessageToTopic = sendEmitLogMessageToTopic;
|
|
10
|
-
const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
|
|
11
10
|
const is_on_daemon_1 = require("../is-on-daemon");
|
|
12
11
|
const logger_1 = require("../logger");
|
|
13
12
|
const streaming_messages_1 = require("../message-types/streaming-messages");
|
|
@@ -43,7 +42,7 @@ function assertOnDaemon(helperName) {
|
|
|
43
42
|
}
|
|
44
43
|
/**
|
|
45
44
|
* Writes a streaming message over the given socket using the daemon's
|
|
46
|
-
* configured serialization format
|
|
45
|
+
* configured serialization format, length-prefixed like any other message.
|
|
47
46
|
* Errors are logged to the daemon's stdout (redirected to the daemon
|
|
48
47
|
* log) rather than propagated — a disconnected client shouldn't tear
|
|
49
48
|
* down the current request handler or other subscribers.
|
|
@@ -51,7 +50,7 @@ function assertOnDaemon(helperName) {
|
|
|
51
50
|
function writeStreamingMessage(socket, payload, description) {
|
|
52
51
|
try {
|
|
53
52
|
logger_1.serverLogger.log('Streaming message to client:', description);
|
|
54
|
-
|
|
53
|
+
(0, socket_utils_1.sendMessage)(socket, payload, undefined, (err) => {
|
|
55
54
|
if (err) {
|
|
56
55
|
console.log(`Streaming message write error (client likely disconnected): ${err.message}`);
|
|
57
56
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ProjectGraph } from '../../config/project-graph';
|
|
2
|
+
import { type WatchEvent } from '../../native';
|
|
3
|
+
export interface DotEnvChangeClassification {
|
|
4
|
+
invalidating: string[];
|
|
5
|
+
unclassified: WatchEvent[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Splits the change events into `invalidating`: the paths with the dotenv name
|
|
9
|
+
* shape getEnvPathsForTask loads (`.env[.<id>]` / `.<id>.env` variants), under
|
|
10
|
+
* the workspace root or a project root, whose content actually changed; and
|
|
11
|
+
* `unclassified`: the dotenv-shaped events under no known root. The
|
|
12
|
+
* invalidating names are a superset of what any task loads: target and
|
|
13
|
+
* configuration names are unknown here, so `.env.staging` is reported whether
|
|
14
|
+
* or not a task loads it. The daemon uses this to refresh its graph cache so
|
|
15
|
+
* createNodes re-resolves config that reads process.env.
|
|
16
|
+
*
|
|
17
|
+
* Only the workspace root and project roots invalidate: getEnvPathsForTask
|
|
18
|
+
* loads dotenv files from those, never from an arbitrary subdirectory (e.g. one
|
|
19
|
+
* under node_modules), and the outputs watcher spans the whole workspace root.
|
|
20
|
+
* An unclassified event is not necessarily irrelevant, though: the graph it was
|
|
21
|
+
* classified against can predate the file's project root (none is committed
|
|
22
|
+
* during the initial computation, and a replaced graph lacks a project that
|
|
23
|
+
* computation is adding), so the caller queues it for replay against the next
|
|
24
|
+
* graph a computation is about to serve rather than dropping it.
|
|
25
|
+
*
|
|
26
|
+
* Known limitation: a `.nxignore`d dotenv file never reaches this watcher (the
|
|
27
|
+
* native watcher applies `.nxignore` even with `use_ignore: false`), so a warm
|
|
28
|
+
* edit of one does not invalidate the graph. The cold path still resolves it:
|
|
29
|
+
* getGraphTimeDotEnvForTask reads dotenv from disk directly.
|
|
30
|
+
*/
|
|
31
|
+
export declare function classifyDotEnvChanges(changeEvents: WatchEvent[], projectGraph: ProjectGraph | undefined): DotEnvChangeClassification;
|
|
32
|
+
/**
|
|
33
|
+
* `generation` is the recomputation generation current at queue time; the
|
|
34
|
+
* drain compares it against the serving computation's generation to prove
|
|
35
|
+
* whether that computation started before the event arrived.
|
|
36
|
+
*/
|
|
37
|
+
export declare function queuePendingDotEnvEvents(paths: string[], generation: number): void;
|
|
38
|
+
/**
|
|
39
|
+
* Takes and clears the queued unclassified events, returning the paths that
|
|
40
|
+
* are dotenv files under a root of `projectGraph` and were queued at or after
|
|
41
|
+
* `sinceGeneration` (the serving computation's generation). A path queued
|
|
42
|
+
* earlier is dropped safely: the computation claimed its generation after the
|
|
43
|
+
* event was queued, so it read the file after the edit landed. Content hashes
|
|
44
|
+
* are neither consulted nor recorded here, and any hash recorded for a
|
|
45
|
+
* drained path is dropped: a hash taken mid-computation is not proof any
|
|
46
|
+
* served graph observed those bytes (the computation may read intermediate
|
|
47
|
+
* content), so suppressing a later event on it could leave the graph stale.
|
|
48
|
+
* `overflowed` means events were lost at or after `sinceGeneration`, so the
|
|
49
|
+
* caller cannot prove its graph fresh and must invalidate; an overflow
|
|
50
|
+
* recorded earlier is dropped by the same rule as a queued entry. A relevant
|
|
51
|
+
* overflow also drops every recorded hash: with events lost, a retained hash
|
|
52
|
+
* (even for a path that invalidated directly and never entered the queue)
|
|
53
|
+
* could suppress a later event over intermediate bytes read by the successor
|
|
54
|
+
* this drain forces. That successor is already being forced, so clearing
|
|
55
|
+
* adds no recomputation.
|
|
56
|
+
*
|
|
57
|
+
* A stamp records callback time, not edit time, so an event whose edit a
|
|
58
|
+
* workspace-watcher-triggered computation already observed can still
|
|
59
|
+
* invalidate it: one redundant recompute, accepted because the callback
|
|
60
|
+
* cannot prove which side of that computation's file read the edit landed on.
|
|
61
|
+
*/
|
|
62
|
+
export declare function drainPendingDotEnvEvents(projectGraph: ProjectGraph | undefined, sinceGeneration: number): {
|
|
63
|
+
invalidating: string[];
|
|
64
|
+
overflowed: boolean;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Whether the queue holds evidence that a computation at `sinceGeneration`
|
|
68
|
+
* may have read a dotenv file before a reported edit landed: an entry or an
|
|
69
|
+
* overflow stamped at or after that generation. Consumes nothing and
|
|
70
|
+
* classifies against no roots: the error paths use this to decide on a retry,
|
|
71
|
+
* where there may be no graph to classify against, and a spurious retry costs
|
|
72
|
+
* one recompute on an already failing path. A persistent error retries once,
|
|
73
|
+
* because the retry's successor claims a generation above every stamp
|
|
74
|
+
* recorded so far.
|
|
75
|
+
*/
|
|
76
|
+
export declare function hasPendingDotEnvEvidence(sinceGeneration: number): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Like hasPendingDotEnvEvidence, but classifies each entry against the roots
|
|
79
|
+
* of `projectGraph`: evidence is an overflow stamped at or after
|
|
80
|
+
* `sinceGeneration`, or an entry so stamped whose path is a dotenv file under
|
|
81
|
+
* one of the graph's roots. The warm-reuse check uses this, where the graph
|
|
82
|
+
* the cache serves exists and is exactly what a recompute would refresh;
|
|
83
|
+
* skipping paths under none of its roots avoids recomputing for events only
|
|
84
|
+
* a future graph could classify, and consuming nothing leaves those entries
|
|
85
|
+
* queued for that computation's drain.
|
|
86
|
+
*/
|
|
87
|
+
export declare function hasRelevantPendingDotEnvEvidence(projectGraph: ProjectGraph | undefined, sinceGeneration: number): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Drops every recorded content hash. Each computation clears on claiming its
|
|
90
|
+
* generation, bounding every hash to the window since the last claim: an
|
|
91
|
+
* older hash is not proof the graph a successor serves observed those bytes,
|
|
92
|
+
* and kept, it could suppress a callback that lands while the successor
|
|
93
|
+
* reads. The error-path retry and the warm-reuse check also clear when they
|
|
94
|
+
* force a successor while preserving the queue for its drain.
|
|
95
|
+
*/
|
|
96
|
+
export declare function clearDotEnvFileHashes(): void;
|
|
97
|
+
export declare function _resetPendingDotEnvEvents(): void;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifyDotEnvChanges = classifyDotEnvChanges;
|
|
4
|
+
exports.queuePendingDotEnvEvents = queuePendingDotEnvEvents;
|
|
5
|
+
exports.drainPendingDotEnvEvents = drainPendingDotEnvEvents;
|
|
6
|
+
exports.hasPendingDotEnvEvidence = hasPendingDotEnvEvidence;
|
|
7
|
+
exports.hasRelevantPendingDotEnvEvidence = hasRelevantPendingDotEnvEvidence;
|
|
8
|
+
exports.clearDotEnvFileHashes = clearDotEnvFileHashes;
|
|
9
|
+
exports._resetPendingDotEnvEvents = _resetPendingDotEnvEvents;
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const native_1 = require("../../native");
|
|
12
|
+
const workspace_root_1 = require("../../utils/workspace-root");
|
|
13
|
+
// Last-seen content hash per dotenv file, so a byte-identical rewrite (an editor
|
|
14
|
+
// save that changes nothing) does not invalidate the graph cache.
|
|
15
|
+
const dotEnvFileHashes = new Map();
|
|
16
|
+
// getEnvPathsForTask loads `.env`, `.env.local`, `.local.env` and the
|
|
17
|
+
// target-scoped `.env.<id>[.local]` / `.<id>[.local].env` variants. The `<id>`
|
|
18
|
+
// (a target or configuration name) may itself contain `/`, so the name is
|
|
19
|
+
// matched relative to the owning root rather than by basename.
|
|
20
|
+
const DOTENV_PREFIXED = /^\.env(\..+)?$/;
|
|
21
|
+
const DOTENV_SUFFIXED = /^\..+\.env$/;
|
|
22
|
+
function isDotEnvName(name) {
|
|
23
|
+
return DOTENV_PREFIXED.test(name) || DOTENV_SUFFIXED.test(name);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Splits the change events into `invalidating`: the paths with the dotenv name
|
|
27
|
+
* shape getEnvPathsForTask loads (`.env[.<id>]` / `.<id>.env` variants), under
|
|
28
|
+
* the workspace root or a project root, whose content actually changed; and
|
|
29
|
+
* `unclassified`: the dotenv-shaped events under no known root. The
|
|
30
|
+
* invalidating names are a superset of what any task loads: target and
|
|
31
|
+
* configuration names are unknown here, so `.env.staging` is reported whether
|
|
32
|
+
* or not a task loads it. The daemon uses this to refresh its graph cache so
|
|
33
|
+
* createNodes re-resolves config that reads process.env.
|
|
34
|
+
*
|
|
35
|
+
* Only the workspace root and project roots invalidate: getEnvPathsForTask
|
|
36
|
+
* loads dotenv files from those, never from an arbitrary subdirectory (e.g. one
|
|
37
|
+
* under node_modules), and the outputs watcher spans the whole workspace root.
|
|
38
|
+
* An unclassified event is not necessarily irrelevant, though: the graph it was
|
|
39
|
+
* classified against can predate the file's project root (none is committed
|
|
40
|
+
* during the initial computation, and a replaced graph lacks a project that
|
|
41
|
+
* computation is adding), so the caller queues it for replay against the next
|
|
42
|
+
* graph a computation is about to serve rather than dropping it.
|
|
43
|
+
*
|
|
44
|
+
* Known limitation: a `.nxignore`d dotenv file never reaches this watcher (the
|
|
45
|
+
* native watcher applies `.nxignore` even with `use_ignore: false`), so a warm
|
|
46
|
+
* edit of one does not invalidate the graph. The cold path still resolves it:
|
|
47
|
+
* getGraphTimeDotEnvForTask reads dotenv from disk directly.
|
|
48
|
+
*/
|
|
49
|
+
function classifyDotEnvChanges(changeEvents, projectGraph) {
|
|
50
|
+
// Outputs batches rarely touch dotenv files, so the O(projects) roots set is
|
|
51
|
+
// only built once a path clears this superset-of-dotenv-names check.
|
|
52
|
+
const candidates = changeEvents.filter((event) => mayBeDotEnvPath(event.path));
|
|
53
|
+
if (candidates.length === 0) {
|
|
54
|
+
return { invalidating: [], unclassified: [] };
|
|
55
|
+
}
|
|
56
|
+
const roots = graphTimeDotEnvRoots(projectGraph);
|
|
57
|
+
const invalidating = [];
|
|
58
|
+
const unclassified = [];
|
|
59
|
+
for (const event of candidates) {
|
|
60
|
+
const { path, type } = event;
|
|
61
|
+
if (!isDotEnvUnderRoot(path, roots)) {
|
|
62
|
+
// A recorded hash stops being proof the graph observed the current
|
|
63
|
+
// content once an event for the path cannot be classified: the pending
|
|
64
|
+
// replay can drop it, and a later classified event with the same bytes
|
|
65
|
+
// would then be suppressed as an unchanged rewrite over a graph that
|
|
66
|
+
// observed a different state (e.g. the file's absence).
|
|
67
|
+
dotEnvFileHashes.delete(path);
|
|
68
|
+
unclassified.push(event);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (type === "delete" /* EventType.delete */) {
|
|
72
|
+
// A removed dotenv file drops the vars it set, so the config resolves
|
|
73
|
+
// differently; there is no content to hash.
|
|
74
|
+
dotEnvFileHashes.delete(path);
|
|
75
|
+
invalidating.push(path);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const hash = (0, native_1.hashFile)((0, node_path_1.join)(workspace_root_1.workspaceRoot, path));
|
|
79
|
+
if (hash !== null && dotEnvFileHashes.get(path) === hash) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
// hashFile returns null on a vanished/unreadable file; when we cannot prove
|
|
83
|
+
// the content is unchanged, report the path rather than risk a stale graph.
|
|
84
|
+
if (hash !== null) {
|
|
85
|
+
dotEnvFileHashes.set(path, hash);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
dotEnvFileHashes.delete(path);
|
|
89
|
+
}
|
|
90
|
+
invalidating.push(path);
|
|
91
|
+
}
|
|
92
|
+
return { invalidating, unclassified };
|
|
93
|
+
}
|
|
94
|
+
// Superset of the names both regexes accept: a prefixed name's first segment
|
|
95
|
+
// starts with `.env` and a suffixed name's last segment ends with `.env`, even
|
|
96
|
+
// when the target/configuration identifier contains `/`.
|
|
97
|
+
function mayBeDotEnvPath(path) {
|
|
98
|
+
// Nearly every outputs path lacks the substring, so check it before the
|
|
99
|
+
// per-segment split: this runs for every path in every outputs batch.
|
|
100
|
+
if (!path.includes('.env')) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return path
|
|
104
|
+
.split('/')
|
|
105
|
+
.some((segment) => segment.startsWith('.env') || segment.endsWith('.env'));
|
|
106
|
+
}
|
|
107
|
+
function graphTimeDotEnvRoots(projectGraph) {
|
|
108
|
+
const roots = new Set(['.']);
|
|
109
|
+
if (projectGraph) {
|
|
110
|
+
for (const node of Object.values(projectGraph.nodes)) {
|
|
111
|
+
roots.add(node.data.root);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return roots;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Whether `path` is a dotenv file that getEnvPathsForTask would load from the
|
|
118
|
+
* workspace root or a project root. For a project root, the name relative to
|
|
119
|
+
* the root may contain `/` because a target/configuration identifier can, and
|
|
120
|
+
* every root ancestor is tried, so a nested project root does not shadow a
|
|
121
|
+
* parent's slash-identifier dotenv. At the workspace root, only
|
|
122
|
+
* single-segment names match.
|
|
123
|
+
*/
|
|
124
|
+
function isDotEnvUnderRoot(path, roots) {
|
|
125
|
+
for (let slash = path.lastIndexOf('/'); slash > 0; slash = path.lastIndexOf('/', slash - 1)) {
|
|
126
|
+
const dir = path.slice(0, slash);
|
|
127
|
+
// Keep walking past a closer root that yields no dotenv name: the same path
|
|
128
|
+
// can still be a slash-identifier dotenv for a shallower (parent) root.
|
|
129
|
+
if (roots.has(dir) && isDotEnvName(path.slice(slash + 1))) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// No project-root ancestor: a workspace-root dotenv, single-segment names
|
|
134
|
+
// only. A deeper path (e.g. `.github/workflows/ci.env`) has the dotenv name
|
|
135
|
+
// shape only for a target identifier containing `/`; accepting those would
|
|
136
|
+
// invalidate on every write under such dot-directories.
|
|
137
|
+
return roots.has('.') && !path.includes('/') && isDotEnvName(path);
|
|
138
|
+
}
|
|
139
|
+
// Paths of dotenv-shaped events whose graph refresh is not provably scheduled:
|
|
140
|
+
// paths under no known root on arrival (the next graph may know the root) and
|
|
141
|
+
// edits to tracked files (the workspace watcher schedules the refresh, but a
|
|
142
|
+
// computation already in flight may have read the file before the edit). Each
|
|
143
|
+
// maps to the recomputation generation current at queue time, so the pre-serve
|
|
144
|
+
// replay can prove whether a computation started before the event arrived.
|
|
145
|
+
const pendingDotEnvEvents = new Map();
|
|
146
|
+
// Bounds daemon-lived growth when nothing drains for a long time. The
|
|
147
|
+
// generation current when the last event was lost stands in for the lost
|
|
148
|
+
// stamps, so overflow follows the same freshness rule as a queued entry.
|
|
149
|
+
const MAX_PENDING_DOTENV_EVENTS = 1024;
|
|
150
|
+
let pendingDotEnvEventsOverflowedAtGeneration;
|
|
151
|
+
/**
|
|
152
|
+
* `generation` is the recomputation generation current at queue time; the
|
|
153
|
+
* drain compares it against the serving computation's generation to prove
|
|
154
|
+
* whether that computation started before the event arrived.
|
|
155
|
+
*/
|
|
156
|
+
function queuePendingDotEnvEvents(paths, generation) {
|
|
157
|
+
for (const path of paths) {
|
|
158
|
+
if (pendingDotEnvEvents.size >= MAX_PENDING_DOTENV_EVENTS &&
|
|
159
|
+
!pendingDotEnvEvents.has(path)) {
|
|
160
|
+
pendingDotEnvEventsOverflowedAtGeneration = generation;
|
|
161
|
+
// A lost path never reaches a drain, so its recorded hash would outlive
|
|
162
|
+
// the queue's deletion discipline and could suppress a later event over
|
|
163
|
+
// a graph that observed different bytes. Drop it while the identity is
|
|
164
|
+
// still known.
|
|
165
|
+
dotEnvFileHashes.delete(path);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
pendingDotEnvEvents.set(path, generation);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Takes and clears the queued unclassified events, returning the paths that
|
|
174
|
+
* are dotenv files under a root of `projectGraph` and were queued at or after
|
|
175
|
+
* `sinceGeneration` (the serving computation's generation). A path queued
|
|
176
|
+
* earlier is dropped safely: the computation claimed its generation after the
|
|
177
|
+
* event was queued, so it read the file after the edit landed. Content hashes
|
|
178
|
+
* are neither consulted nor recorded here, and any hash recorded for a
|
|
179
|
+
* drained path is dropped: a hash taken mid-computation is not proof any
|
|
180
|
+
* served graph observed those bytes (the computation may read intermediate
|
|
181
|
+
* content), so suppressing a later event on it could leave the graph stale.
|
|
182
|
+
* `overflowed` means events were lost at or after `sinceGeneration`, so the
|
|
183
|
+
* caller cannot prove its graph fresh and must invalidate; an overflow
|
|
184
|
+
* recorded earlier is dropped by the same rule as a queued entry. A relevant
|
|
185
|
+
* overflow also drops every recorded hash: with events lost, a retained hash
|
|
186
|
+
* (even for a path that invalidated directly and never entered the queue)
|
|
187
|
+
* could suppress a later event over intermediate bytes read by the successor
|
|
188
|
+
* this drain forces. That successor is already being forced, so clearing
|
|
189
|
+
* adds no recomputation.
|
|
190
|
+
*
|
|
191
|
+
* A stamp records callback time, not edit time, so an event whose edit a
|
|
192
|
+
* workspace-watcher-triggered computation already observed can still
|
|
193
|
+
* invalidate it: one redundant recompute, accepted because the callback
|
|
194
|
+
* cannot prove which side of that computation's file read the edit landed on.
|
|
195
|
+
*/
|
|
196
|
+
function drainPendingDotEnvEvents(projectGraph, sinceGeneration) {
|
|
197
|
+
const entries = Array.from(pendingDotEnvEvents.entries());
|
|
198
|
+
const overflowed = pendingDotEnvEventsOverflowedAtGeneration !== undefined &&
|
|
199
|
+
pendingDotEnvEventsOverflowedAtGeneration >= sinceGeneration;
|
|
200
|
+
pendingDotEnvEvents.clear();
|
|
201
|
+
pendingDotEnvEventsOverflowedAtGeneration = undefined;
|
|
202
|
+
if (overflowed) {
|
|
203
|
+
dotEnvFileHashes.clear();
|
|
204
|
+
}
|
|
205
|
+
if (entries.length === 0) {
|
|
206
|
+
return { invalidating: [], overflowed };
|
|
207
|
+
}
|
|
208
|
+
const roots = graphTimeDotEnvRoots(projectGraph);
|
|
209
|
+
const invalidating = [];
|
|
210
|
+
for (const [path, generation] of entries) {
|
|
211
|
+
dotEnvFileHashes.delete(path);
|
|
212
|
+
if (generation >= sinceGeneration && isDotEnvUnderRoot(path, roots)) {
|
|
213
|
+
invalidating.push(path);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { invalidating, overflowed };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Whether the queue holds evidence that a computation at `sinceGeneration`
|
|
220
|
+
* may have read a dotenv file before a reported edit landed: an entry or an
|
|
221
|
+
* overflow stamped at or after that generation. Consumes nothing and
|
|
222
|
+
* classifies against no roots: the error paths use this to decide on a retry,
|
|
223
|
+
* where there may be no graph to classify against, and a spurious retry costs
|
|
224
|
+
* one recompute on an already failing path. A persistent error retries once,
|
|
225
|
+
* because the retry's successor claims a generation above every stamp
|
|
226
|
+
* recorded so far.
|
|
227
|
+
*/
|
|
228
|
+
function hasPendingDotEnvEvidence(sinceGeneration) {
|
|
229
|
+
if (pendingDotEnvEventsOverflowedAtGeneration !== undefined &&
|
|
230
|
+
pendingDotEnvEventsOverflowedAtGeneration >= sinceGeneration) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
for (const generation of pendingDotEnvEvents.values()) {
|
|
234
|
+
if (generation >= sinceGeneration) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Like hasPendingDotEnvEvidence, but classifies each entry against the roots
|
|
242
|
+
* of `projectGraph`: evidence is an overflow stamped at or after
|
|
243
|
+
* `sinceGeneration`, or an entry so stamped whose path is a dotenv file under
|
|
244
|
+
* one of the graph's roots. The warm-reuse check uses this, where the graph
|
|
245
|
+
* the cache serves exists and is exactly what a recompute would refresh;
|
|
246
|
+
* skipping paths under none of its roots avoids recomputing for events only
|
|
247
|
+
* a future graph could classify, and consuming nothing leaves those entries
|
|
248
|
+
* queued for that computation's drain.
|
|
249
|
+
*/
|
|
250
|
+
function hasRelevantPendingDotEnvEvidence(projectGraph, sinceGeneration) {
|
|
251
|
+
if (pendingDotEnvEventsOverflowedAtGeneration !== undefined &&
|
|
252
|
+
pendingDotEnvEventsOverflowedAtGeneration >= sinceGeneration) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
let roots;
|
|
256
|
+
for (const [path, generation] of pendingDotEnvEvents) {
|
|
257
|
+
if (generation < sinceGeneration) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (!roots) {
|
|
261
|
+
roots = graphTimeDotEnvRoots(projectGraph);
|
|
262
|
+
}
|
|
263
|
+
if (isDotEnvUnderRoot(path, roots)) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Drops every recorded content hash. Each computation clears on claiming its
|
|
271
|
+
* generation, bounding every hash to the window since the last claim: an
|
|
272
|
+
* older hash is not proof the graph a successor serves observed those bytes,
|
|
273
|
+
* and kept, it could suppress a callback that lands while the successor
|
|
274
|
+
* reads. The error-path retry and the warm-reuse check also clear when they
|
|
275
|
+
* force a successor while preserving the queue for its drain.
|
|
276
|
+
*/
|
|
277
|
+
function clearDotEnvFileHashes() {
|
|
278
|
+
dotEnvFileHashes.clear();
|
|
279
|
+
}
|
|
280
|
+
// Test helper: the queue is daemon-lived module state.
|
|
281
|
+
function _resetPendingDotEnvEvents() {
|
|
282
|
+
pendingDotEnvEvents.clear();
|
|
283
|
+
pendingDotEnvEventsOverflowedAtGeneration = undefined;
|
|
284
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function _setEnvForwardTimeoutMs(ms: number): void;
|
|
2
|
+
/**
|
|
3
|
+
* Applies an env-carrying client message to the daemon. Must be awaited
|
|
4
|
+
* before dispatching the message's handler: plugin workers key their disk
|
|
5
|
+
* caches on their own process env, so a graph request must not reach a worker
|
|
6
|
+
* whose env still reflects the previous client.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleClientEnv(env: Record<string, string>): Promise<void>;
|