@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.
Files changed (296) hide show
  1. package/README.md +7 -6
  2. package/changes.json +12 -8
  3. package/dist/bin/init-local.js +3 -5
  4. package/dist/bin/nx.js +1 -0
  5. package/dist/src/adapter/ngcli-adapter.js +60 -1
  6. package/dist/src/ai/constants.d.ts +15 -0
  7. package/dist/src/ai/constants.js +17 -1
  8. package/dist/src/ai/set-up-ai-agents/set-up-ai-agents.js +57 -9
  9. package/dist/src/ai/utils.d.ts +13 -0
  10. package/dist/src/ai/utils.js +29 -0
  11. package/dist/src/analytics/analytics.js +2 -4
  12. package/dist/src/command-line/completion/command-object.js +7 -12
  13. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +42 -31
  14. package/dist/src/command-line/format/format.js +84 -101
  15. package/dist/src/command-line/generate/generate.js +35 -35
  16. package/dist/src/command-line/import/import.js +29 -38
  17. package/dist/src/command-line/init/ai-agent-prompts.js +5 -11
  18. package/dist/src/command-line/init/command-object.js +4 -2
  19. package/dist/src/command-line/init/configure-plugins.js +4 -1
  20. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +12 -32
  21. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +17 -21
  22. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +8 -21
  23. package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +3 -17
  24. package/dist/src/command-line/init/implementation/angular/index.js +10 -15
  25. package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +2 -0
  26. package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +8 -2
  27. package/dist/src/command-line/init/implementation/check-compatible-with-plugins.js +2 -0
  28. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +5 -0
  29. package/dist/src/command-line/init/implementation/format.d.ts +18 -0
  30. package/dist/src/command-line/init/implementation/format.js +114 -0
  31. package/dist/src/command-line/init/implementation/utils.js +8 -8
  32. package/dist/src/command-line/init/init-v1.js +15 -18
  33. package/dist/src/command-line/init/init-v2.js +77 -66
  34. package/dist/src/command-line/migrate/agentic/definitions.js +36 -9
  35. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +37 -22
  36. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +30 -39
  37. package/dist/src/command-line/migrate/agentic/handoff.d.ts +7 -7
  38. package/dist/src/command-line/migrate/agentic/handoff.js +13 -10
  39. package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +7 -12
  40. package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +7 -0
  41. package/dist/src/command-line/migrate/agentic/run-step.d.ts +10 -0
  42. package/dist/src/command-line/migrate/agentic/run-step.js +1 -0
  43. package/dist/src/command-line/migrate/agentic/runner.js +24 -24
  44. package/dist/src/command-line/migrate/agentic/select.d.ts +10 -2
  45. package/dist/src/command-line/migrate/agentic/select.js +30 -32
  46. package/dist/src/command-line/migrate/agentic/types.d.ts +33 -3
  47. package/dist/src/command-line/migrate/agentic/types.js +35 -0
  48. package/dist/src/command-line/migrate/command-object.d.ts +6 -0
  49. package/dist/src/command-line/migrate/command-object.js +19 -1
  50. package/dist/src/command-line/migrate/execute-migration.d.ts +28 -9
  51. package/dist/src/command-line/migrate/execute-migration.js +112 -27
  52. package/dist/src/command-line/migrate/migrate-analytics.d.ts +35 -0
  53. package/dist/src/command-line/migrate/migrate-analytics.js +64 -0
  54. package/dist/src/command-line/migrate/migrate-commits.d.ts +38 -8
  55. package/dist/src/command-line/migrate/migrate-commits.js +130 -15
  56. package/dist/src/command-line/migrate/migrate-config.d.ts +13 -12
  57. package/dist/src/command-line/migrate/migrate-config.js +22 -14
  58. package/dist/src/command-line/migrate/migrate-output.d.ts +45 -7
  59. package/dist/src/command-line/migrate/migrate-output.js +52 -8
  60. package/dist/src/command-line/migrate/migrate-ui-api.d.ts +2 -1
  61. package/dist/src/command-line/migrate/migrate-ui-api.js +16 -7
  62. package/dist/src/command-line/migrate/migrate.d.ts +24 -55
  63. package/dist/src/command-line/migrate/migrate.js +553 -226
  64. package/dist/src/command-line/migrate/migration-shape.d.ts +8 -0
  65. package/dist/src/command-line/migrate/multi-major.js +7 -10
  66. package/dist/src/command-line/migrate/resolve-package-version.js +4 -8
  67. package/dist/src/command-line/migrate/run/agent-output.d.ts +20 -0
  68. package/dist/src/command-line/migrate/run/agent-output.js +65 -0
  69. package/dist/src/command-line/migrate/run/index.d.ts +9 -0
  70. package/dist/src/command-line/migrate/run/index.js +23 -0
  71. package/dist/src/command-line/migrate/run/orchestrator.d.ts +20 -0
  72. package/dist/src/command-line/migrate/run/orchestrator.js +1200 -0
  73. package/dist/src/command-line/migrate/run/run-id.d.ts +16 -0
  74. package/dist/src/command-line/migrate/run/run-id.js +56 -0
  75. package/dist/src/command-line/migrate/run/run-state.d.ts +157 -0
  76. package/dist/src/command-line/migrate/run/run-state.js +438 -0
  77. package/dist/src/command-line/migrate/run/state-lock.d.ts +25 -0
  78. package/dist/src/command-line/migrate/run/state-lock.js +76 -0
  79. package/dist/src/command-line/migrate/run/state-machine.d.ts +84 -0
  80. package/dist/src/command-line/migrate/run/state-machine.js +315 -0
  81. package/dist/src/command-line/migrate/run/util.d.ts +59 -0
  82. package/dist/src/command-line/migrate/run/util.js +148 -0
  83. package/dist/src/command-line/migrate/run/worker.d.ts +16 -0
  84. package/dist/src/command-line/migrate/run/worker.js +682 -0
  85. package/dist/src/command-line/migrate/run-migration-process.js +6 -5
  86. package/dist/src/command-line/migrate/safe-prompt.d.ts +14 -26
  87. package/dist/src/command-line/migrate/safe-prompt.js +32 -43
  88. package/dist/src/command-line/migrate/sort-migrations.d.ts +16 -0
  89. package/dist/src/command-line/migrate/sort-migrations.js +32 -0
  90. package/dist/src/command-line/migrate/step-actions.d.ts +5 -0
  91. package/dist/src/command-line/migrate/step-actions.js +12 -0
  92. package/dist/src/command-line/migrate/text.d.ts +18 -0
  93. package/dist/src/command-line/migrate/text.js +26 -0
  94. package/dist/src/command-line/migrate/version-skew-guard.d.ts +60 -0
  95. package/dist/src/command-line/migrate/version-skew-guard.js +175 -0
  96. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +16 -20
  97. package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.d.ts +2 -0
  98. package/dist/src/command-line/nx-cloud/start-nx-agents/command-object.js +16 -0
  99. package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.d.ts +4 -0
  100. package/dist/src/command-line/nx-cloud/start-nx-agents/start-nx-agents.js +13 -0
  101. package/dist/src/command-line/nx-commands.js +26 -24
  102. package/dist/src/command-line/release/changelog.js +6 -17
  103. package/dist/src/command-line/release/plan.js +15 -28
  104. package/dist/src/command-line/release/publish.js +5 -0
  105. package/dist/src/command-line/release/release.js +6 -17
  106. package/dist/src/command-line/release/utils/release-graph.d.ts +1 -0
  107. package/dist/src/command-line/release/utils/release-graph.js +10 -2
  108. package/dist/src/command-line/release/utils/remote-release-clients/github.js +10 -22
  109. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +13 -22
  110. package/dist/src/command-line/release/utils/resolve-semver-specifier.js +19 -41
  111. package/dist/src/command-line/release/version/resolve-current-version.js +7 -10
  112. package/dist/src/command-line/release/version.js +2 -2
  113. package/dist/src/command-line/reset/reset.js +17 -15
  114. package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  115. package/dist/src/command-line/yargs-utils/shared-options.js +3 -1
  116. package/dist/src/config/misc-interfaces.d.ts +19 -7
  117. package/dist/src/core/graph/main.js +1 -1
  118. package/dist/src/core/graph/styles.js +1 -1
  119. package/dist/src/daemon/client/client.d.ts +2 -7
  120. package/dist/src/daemon/client/client.js +113 -28
  121. package/dist/src/daemon/client/daemon-environment.d.ts +35 -0
  122. package/dist/src/daemon/client/daemon-environment.js +106 -6
  123. package/dist/src/daemon/client/daemon-socket-messenger.d.ts +1 -1
  124. package/dist/src/daemon/client/daemon-socket-messenger.js +8 -3
  125. package/dist/src/daemon/is-v8-serializer-enabled.d.ts +2 -2
  126. package/dist/src/daemon/is-v8-serializer-enabled.js +2 -2
  127. package/dist/src/daemon/message-types/daemon-message.d.ts +2 -1
  128. package/dist/src/daemon/message-types/daemon-message.js +25 -2
  129. package/dist/src/daemon/sandbox-socket-hint.d.ts +27 -0
  130. package/dist/src/daemon/sandbox-socket-hint.js +136 -0
  131. package/dist/src/daemon/server/client-socket-context.d.ts +1 -1
  132. package/dist/src/daemon/server/client-socket-context.js +2 -3
  133. package/dist/src/daemon/server/dotenv-graph-changes.d.ts +97 -0
  134. package/dist/src/daemon/server/dotenv-graph-changes.js +284 -0
  135. package/dist/src/daemon/server/handle-client-env.d.ts +8 -0
  136. package/dist/src/daemon/server/handle-client-env.js +92 -0
  137. package/dist/src/daemon/server/handle-outputs-changes.d.ts +10 -0
  138. package/dist/src/daemon/server/handle-outputs-changes.js +86 -0
  139. package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +21 -0
  140. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +159 -6
  141. package/dist/src/daemon/server/server.js +36 -72
  142. package/dist/src/daemon/server/shutdown-utils.d.ts +1 -1
  143. package/dist/src/daemon/server/shutdown-utils.js +2 -2
  144. package/dist/src/daemon/socket-utils.d.ts +26 -7
  145. package/dist/src/daemon/socket-utils.js +45 -31
  146. package/dist/src/daemon/tmp-dir.d.ts +10 -0
  147. package/dist/src/daemon/tmp-dir.js +21 -37
  148. package/dist/src/devkit-internals.d.ts +20 -9
  149. package/dist/src/devkit-internals.js +49 -6
  150. package/dist/src/executors/run-commands/running-tasks.js +46 -29
  151. package/dist/src/generators/internal-utils/{format-changed-files-with-prettier-if-available.d.ts → format-changed-files.d.ts} +6 -6
  152. package/dist/src/generators/internal-utils/format-changed-files.js +163 -0
  153. package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.d.ts +15 -0
  154. package/dist/src/generators/testing-utils/create-tree-with-empty-workspace.js +21 -3
  155. package/dist/src/migrations/update-15-0-0/prefix-outputs.js +2 -2
  156. package/dist/src/migrations/update-16-0-0/remove-nrwl-cli.js +2 -2
  157. package/dist/src/migrations/update-16-0-0/update-depends-on-to-tokens.js +2 -2
  158. package/dist/src/migrations/update-16-0-0/update-nx-cloud-runner.js +2 -2
  159. package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +2 -2
  160. package/dist/src/migrations/update-17-0-0/rm-default-collection-npm-scope.js +2 -2
  161. package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +2 -2
  162. package/dist/src/migrations/update-17-2-0/move-default-base.js +2 -2
  163. package/dist/src/migrations/update-18-0-0/disable-crystal-for-existing-workspaces.js +2 -2
  164. package/dist/src/migrations/update-19-2-4/set-project-name.js +2 -2
  165. package/dist/src/migrations/update-20-0-0/move-use-daemon-process.js +2 -2
  166. package/dist/src/migrations/update-20-0-1/use-legacy-cache.js +2 -2
  167. package/dist/src/migrations/update-21-0-0/release-changelog-config-changes.js +2 -2
  168. package/dist/src/migrations/update-21-0-0/release-version-config-changes.js +2 -2
  169. package/dist/src/migrations/update-21-0-0/remove-custom-tasks-runner.js +2 -2
  170. package/dist/src/migrations/update-21-0-0/remove-legacy-cache.js +2 -2
  171. package/dist/src/migrations/update-22-0-0/consolidate-release-tag-config.js +2 -2
  172. package/dist/src/migrations/update-22-0-0/release-version-config-changes.js +2 -2
  173. package/dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore.js +2 -2
  174. package/dist/src/migrations/update-22-6-0/add-claude-settings-local-to-git-ignore.js +2 -2
  175. package/dist/src/migrations/update-22-6-0/add-claude-worktrees-to-git-ignore.js +2 -2
  176. package/dist/src/migrations/update-22-7-0/add-polygraph-to-git-ignore.js +2 -2
  177. package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.js +2 -2
  178. package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +2 -2
  179. package/dist/src/native/index.d.ts +7 -3
  180. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  181. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  182. package/dist/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud.js +3 -3
  183. package/dist/src/nx-cloud/utilities/url-shorten.d.ts +1 -4
  184. package/dist/src/nx-cloud/utilities/url-shorten.js +9 -8
  185. package/dist/src/plugins/js/lock-file/lock-file.d.ts +46 -4
  186. package/dist/src/plugins/js/lock-file/lock-file.js +192 -15
  187. package/dist/src/plugins/js/lock-file/npm-parser.js +10 -3
  188. package/dist/src/plugins/js/lock-file/pnpm-parser.js +356 -62
  189. package/dist/src/plugins/js/lock-file/project-graph-pruning.d.ts +25 -1
  190. package/dist/src/plugins/js/lock-file/project-graph-pruning.js +78 -4
  191. package/dist/src/plugins/js/lock-file/pruned-output.d.ts +330 -0
  192. package/dist/src/plugins/js/lock-file/pruned-output.js +1441 -0
  193. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +1 -0
  194. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +6 -5
  195. package/dist/src/plugins/js/utils/register.d.ts +11 -0
  196. package/dist/src/plugins/js/utils/register.js +40 -6
  197. package/dist/src/project-graph/plugins/get-plugins.d.ts +27 -1
  198. package/dist/src/project-graph/plugins/get-plugins.js +110 -5
  199. package/dist/src/project-graph/plugins/isolation/enabled.js +1 -4
  200. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +16 -0
  201. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +69 -11
  202. package/dist/src/project-graph/plugins/isolation/messaging.js +1 -3
  203. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +23 -2
  204. package/dist/src/project-graph/project-graph.js +27 -2
  205. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +7 -0
  206. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +14 -6
  207. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +47 -21
  208. package/dist/src/tasks-runner/is-tui-enabled.js +3 -1
  209. package/dist/src/tasks-runner/life-cycle.d.ts +2 -2
  210. package/dist/src/tasks-runner/life-cycle.js +2 -2
  211. package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +12 -0
  212. package/dist/src/tasks-runner/life-cycles/performance-report.js +62 -3
  213. package/dist/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.d.ts +26 -1
  214. package/dist/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.js +99 -5
  215. package/dist/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.d.ts +26 -0
  216. package/dist/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.js +113 -17
  217. package/dist/src/tasks-runner/pseudo-ipc.js +14 -13
  218. package/dist/src/tasks-runner/pseudo-terminal.d.ts +17 -3
  219. package/dist/src/tasks-runner/pseudo-terminal.js +22 -10
  220. package/dist/src/tasks-runner/run-command.js +32 -39
  221. package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +55 -0
  222. package/dist/src/tasks-runner/running-tasks/batch-process.js +161 -8
  223. package/dist/src/tasks-runner/task-env.d.ts +25 -0
  224. package/dist/src/tasks-runner/task-env.js +39 -0
  225. package/dist/src/tasks-runner/task-orchestrator.d.ts +68 -0
  226. package/dist/src/tasks-runner/task-orchestrator.js +159 -8
  227. package/dist/src/utils/analytics-enabled.d.ts +7 -0
  228. package/dist/src/utils/analytics-enabled.js +11 -0
  229. package/dist/src/utils/analytics-prompt.js +4 -7
  230. package/dist/src/utils/cache-directory.d.ts +62 -3
  231. package/dist/src/utils/cache-directory.js +276 -25
  232. package/dist/src/utils/catalog/bun-manager-utils.d.ts +8 -0
  233. package/dist/src/utils/catalog/bun-manager-utils.js +174 -0
  234. package/dist/src/utils/catalog/bun-manager.d.ts +28 -0
  235. package/dist/src/utils/catalog/bun-manager.js +130 -0
  236. package/dist/src/utils/catalog/index.d.ts +1 -1
  237. package/dist/src/utils/catalog/index.js +1 -1
  238. package/dist/src/utils/catalog/manager-factory.js +3 -0
  239. package/dist/src/utils/catalog/manager-utils.d.ts +8 -1
  240. package/dist/src/utils/catalog/manager-utils.js +9 -4
  241. package/dist/src/utils/catalog/manager.d.ts +13 -1
  242. package/dist/src/utils/catalog/manager.js +30 -0
  243. package/dist/src/utils/catalog/pnpm-manager.d.ts +2 -1
  244. package/dist/src/utils/catalog/pnpm-manager.js +3 -0
  245. package/dist/src/utils/catalog/types.d.ts +4 -0
  246. package/dist/src/utils/catalog/yarn-manager.d.ts +6 -2
  247. package/dist/src/utils/catalog/yarn-manager.js +23 -32
  248. package/dist/src/utils/child-process.d.ts +31 -1
  249. package/dist/src/utils/child-process.js +103 -0
  250. package/dist/src/utils/chunkify.d.ts +7 -1
  251. package/dist/src/utils/chunkify.js +10 -3
  252. package/dist/src/utils/consume-messages-from-socket.d.ts +42 -7
  253. package/dist/src/utils/consume-messages-from-socket.js +273 -38
  254. package/dist/src/utils/db-connection.js +8 -16
  255. package/dist/src/utils/exit-codes.d.ts +9 -0
  256. package/dist/src/utils/exit-codes.js +19 -0
  257. package/dist/src/utils/formatters/index.d.ts +13 -0
  258. package/dist/src/utils/formatters/index.js +74 -0
  259. package/dist/src/utils/formatters/oxfmt.d.ts +32 -0
  260. package/dist/src/utils/formatters/oxfmt.js +764 -0
  261. package/dist/src/utils/formatters/prettier.d.ts +26 -0
  262. package/dist/src/utils/formatters/prettier.js +201 -0
  263. package/dist/src/utils/formatters/shared.d.ts +5 -0
  264. package/dist/src/utils/formatters/shared.js +8 -0
  265. package/dist/src/utils/git-utils.d.ts +38 -2
  266. package/dist/src/utils/git-utils.js +422 -19
  267. package/dist/src/utils/has-nx-js-plugin.d.ts +1 -1
  268. package/dist/src/utils/has-nx-js-plugin.js +1 -1
  269. package/dist/src/utils/ignore.d.ts +47 -5
  270. package/dist/src/utils/ignore.js +93 -11
  271. package/dist/src/utils/kill-child-on-host-exit.d.ts +7 -0
  272. package/dist/src/utils/kill-child-on-host-exit.js +42 -0
  273. package/dist/src/utils/nx-console-prompt.js +3 -6
  274. package/dist/src/utils/nx-tmp-dir.d.ts +5 -0
  275. package/dist/src/utils/nx-tmp-dir.js +7 -2
  276. package/dist/src/utils/output.d.ts +153 -0
  277. package/dist/src/utils/output.js +289 -5
  278. package/dist/src/utils/owned-private-dir.d.ts +13 -0
  279. package/dist/src/utils/owned-private-dir.js +39 -0
  280. package/dist/src/utils/package-json.d.ts +2 -0
  281. package/dist/src/utils/package-manager.js +17 -7
  282. package/dist/src/utils/params.d.ts +7 -1
  283. package/dist/src/utils/params.js +64 -6
  284. package/dist/src/utils/permission-errors.d.ts +11 -0
  285. package/dist/src/utils/permission-errors.js +17 -0
  286. package/dist/src/utils/plugins/core-plugins.js +4 -0
  287. package/dist/src/utils/prompt-helpers.d.ts +53 -0
  288. package/dist/src/utils/prompt-helpers.js +111 -0
  289. package/dist/src/utils/socket-refused-exit-code.d.ts +15 -0
  290. package/dist/src/utils/socket-refused-exit-code.js +18 -0
  291. package/dist/src/utils/write-formatted-json-file.d.ts +3 -2
  292. package/dist/src/utils/write-formatted-json-file.js +5 -4
  293. package/package.json +33 -24
  294. package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +0 -88
  295. package/dist/src/utils/is-using-prettier.d.ts +0 -3
  296. 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. Compared directly; both come from the same resolution.
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. Compared directly; both come from the same resolution.
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 !== receiverWorkspaceRoot;
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 and terminated with MESSAGE_END_SEQ.
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 and terminated with MESSAGE_END_SEQ.
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
- socket.write((0, socket_utils_1.serialize)(payload) + consume_messages_from_socket_1.MESSAGE_END_SEQ, (err) => {
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>;