@visulima/task-runner 1.0.0-alpha.2 → 1.0.0-alpha.4

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 (53) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +193 -51
  3. package/dist/affected.d.ts +37 -3
  4. package/dist/command-parser/expand-arguments.d.ts +11 -0
  5. package/dist/command-parser/expand-shortcut.d.ts +15 -0
  6. package/dist/command-parser/expand-wildcard.d.ts +13 -0
  7. package/dist/command-parser/index.d.ts +18 -0
  8. package/dist/command-parser/strip-quotes.d.ts +6 -0
  9. package/dist/concurrent-fallback.d.ts +16 -0
  10. package/dist/concurrent.d.ts +23 -0
  11. package/dist/detect-shell.d.ts +19 -0
  12. package/dist/flow-controllers/index.d.ts +7 -0
  13. package/dist/flow-controllers/input-handler.d.ts +44 -0
  14. package/dist/flow-controllers/log-timings.d.ts +18 -0
  15. package/dist/flow-controllers/restart-process.d.ts +21 -0
  16. package/dist/flow-controllers/teardown.d.ts +22 -0
  17. package/dist/index.d.ts +13 -3
  18. package/dist/index.js +26 -12
  19. package/dist/native-binding.d.ts +44 -2
  20. package/dist/packem_shared/{Cache-IYpTYVUC.js → Cache-C23LywYn.js} +2 -3
  21. package/dist/packem_shared/{FingerprintManager-D6Y0erg-.js → FingerprintManager-Cu-ta9ee.js} +0 -1
  22. package/dist/packem_shared/{IncrementalFileHasher-Ds3J6dgb.js → IncrementalFileHasher-Cm_kJY5V.js} +1 -1
  23. package/dist/packem_shared/{TaskOrchestrator-BvYs3ONw.js → TaskOrchestrator-lLn-PH1m.js} +2 -5
  24. package/dist/packem_shared/TerminalBuffer-D6zP2zLh.js +250 -0
  25. package/dist/packem_shared/{filterAffectedTasks-I-18zPg6.js → buildForwardDependencyMap-Cu08NWB1.js} +58 -20
  26. package/dist/packem_shared/{computeTaskHash-BoCnnvIJ.js → computeTaskHash-B2SVZqgp.js} +1 -2
  27. package/dist/packem_shared/createInputHandler-DTfePcTG.js +37 -0
  28. package/dist/packem_shared/{defaultTaskRunner-CrW4v5Ye.js → defaultTaskRunner-X1MIynHu.js} +6 -7
  29. package/dist/packem_shared/detectScriptShell-CR-xXKA4.js +53 -0
  30. package/dist/packem_shared/enforceProjectConstraints-_Ej0zHch.js +90 -0
  31. package/dist/packem_shared/expandArguments-0AwD2BIA.js +26 -0
  32. package/dist/packem_shared/expandShortcut-BVG05ee4.js +23 -0
  33. package/dist/packem_shared/expandWildcard-B0xN_knq.js +107 -0
  34. package/dist/packem_shared/{findCycle-DF4_BRdO.js → findCycle-DefgNYhg.js} +1 -1
  35. package/dist/packem_shared/formatTimingTable-3qtCM552.js +46 -0
  36. package/dist/packem_shared/isNativeAvailable-BpD28A6Z.js +44 -0
  37. package/dist/packem_shared/parseCommands-D-IgF8Zh.js +26 -0
  38. package/dist/packem_shared/{TaskScheduler-CJilHDta.js → parsePartition-C4-P5RjK.js} +44 -1
  39. package/dist/packem_shared/{projectGraphToDot-VdTjHcVp.js → projectGraphToDot-C8uYeaPo.js} +20 -3
  40. package/dist/packem_shared/runConcurrentFallback-3q46z4AS.js +357 -0
  41. package/dist/packem_shared/runConcurrently-ATDwJNR6.js +67 -0
  42. package/dist/packem_shared/runTeardown-BAezH79J.js +49 -0
  43. package/dist/packem_shared/stripQuotes-Cey-zwFf.js +9 -0
  44. package/dist/packem_shared/withRestart-BREjRJa4.js +49 -0
  45. package/dist/project-constraints.d.ts +16 -0
  46. package/dist/task-scheduler.d.ts +23 -0
  47. package/dist/terminal-buffer.d.ts +29 -0
  48. package/dist/types.d.ts +220 -1
  49. package/index.js +599 -0
  50. package/package.json +14 -13
  51. package/binding.js +0 -204
  52. package/dist/packem_shared/isNativeAvailable-BWhnZ4ES.js +0 -19
  53. package/dist/packem_shared/{RemoteCache-BDqrnDEi.js → RemoteCache-BFceSe4a.js} +1 -1
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Restart flow controller.
3
+ *
4
+ * Re-runs failed commands with configurable retry count and delay.
5
+ * Supports fixed delay or exponential backoff.
6
+ */
7
+ import type { ConcurrentCommandConfig, ConcurrentRunnerOptions, ConcurrentRunResult } from "../types.d.ts";
8
+ export interface RestartOptions {
9
+ /** Delay between restarts in milliseconds. "exponential" for 2^attempt * 1000ms. */
10
+ delay: number | "exponential";
11
+ /** Maximum number of restart attempts per command. 0 = no restarts. -1 = infinite. */
12
+ tries: number;
13
+ }
14
+ /**
15
+ * Wraps a runner function to add restart-on-failure behavior.
16
+ * @param runFn The underlying runner function (runConcurrently or runConcurrentFallback)
17
+ * @param commands The original command configs
18
+ * @param options Runner options
19
+ * @param restartOptions Restart-specific options
20
+ */
21
+ export declare const withRestart: (runFunction: (commands: ConcurrentCommandConfig[], options: ConcurrentRunnerOptions) => Promise<ConcurrentRunResult>, commands: ConcurrentCommandConfig[], options: ConcurrentRunnerOptions, restartOptions: RestartOptions) => Promise<ConcurrentRunResult>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Teardown flow controller.
3
+ *
4
+ * Runs cleanup commands sequentially after all concurrent processes complete.
5
+ * Each teardown command inherits stdio (output goes directly to terminal).
6
+ *
7
+ * Commands are sourced from configuration (trusted, not user input).
8
+ * Shell execution is intentional for pipe/redirect support.
9
+ */
10
+ export interface TeardownOptions {
11
+ /** Commands to run in sequence after all processes complete. */
12
+ commands: string[];
13
+ /** Working directory for teardown commands. */
14
+ cwd?: string;
15
+ }
16
+ /**
17
+ * Run teardown commands sequentially.
18
+ * Each command runs in the shell with inherited stdio.
19
+ * If a command fails, subsequent commands are still attempted.
20
+ * @returns Array of exit codes for each teardown command
21
+ */
22
+ export declare const runTeardown: (options: TeardownOptions) => Promise<number[]>;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  export type { AffectedOptions, AffectedResult } from "./affected.d.ts";
2
- export { filterAffectedTasks, getAffectedProjects, getChangedFiles } from "./affected.d.ts";
2
+ export { buildForwardDependencyMap, buildReverseDependencyMap, expandAffected, filterAffectedTasks, getAffectedProjects, getChangedFiles } from "./affected.d.ts";
3
3
  export type { CachedResult, CacheOptions } from "./cache.d.ts";
4
4
  export { Cache, formatCacheSize, parseCacheSize } from "./cache.d.ts";
5
+ export type { ParseCommandsOptions } from "./command-parser/index.d.ts";
6
+ export { expandArguments, expandShortcut, expandWildcard, parseCommands, stripQuotes } from "./command-parser/index.d.ts";
7
+ export { runConcurrently } from "./concurrent.d.ts";
8
+ export { runConcurrentFallback } from "./concurrent-fallback.d.ts";
5
9
  export { defaultTaskRunner } from "./default-task-runner.d.ts";
10
+ export { detectScriptShell } from "./detect-shell.d.ts";
6
11
  export type { FileAccess, TrackingResult } from "./file-access-tracker.d.ts";
7
12
  export { FileAccessTracker, generatePreloadScript } from "./file-access-tracker.d.ts";
8
13
  export type { CacheMissReason, TaskFingerprint } from "./fingerprint.d.ts";
9
14
  export { FingerprintManager } from "./fingerprint.d.ts";
15
+ export type { InputHandlerOptions, RestartOptions, TeardownOptions } from "./flow-controllers/index.d.ts";
16
+ export { createInputHandler, formatTimingTable, logTimings, runTeardown, withRestart } from "./flow-controllers/index.d.ts";
10
17
  export type { DetectedFramework } from "./framework-inference.d.ts";
11
18
  export { detectFrameworks, getFrameworkEnvVariables, inferFrameworkEnvPatterns } from "./framework-inference.d.ts";
12
19
  export type { GraphFormat, GraphJson, GraphVisualizerOptions } from "./graph-visualizer.d.ts";
@@ -17,6 +24,7 @@ export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle } from "./life-cyc
17
24
  export type { PackageLockfileHash, ResolvedDependency } from "./lockfile-hasher.d.ts";
18
25
  export { extractPackageName, LockfileHasher, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile } from "./lockfile-hasher.d.ts";
19
26
  export { isNativeAvailable, loadNativeBindings } from "./native-binding.d.ts";
27
+ export { enforceProjectConstraints } from "./project-constraints.d.ts";
20
28
  export type { RemoteCacheOptions } from "./remote-cache.d.ts";
21
29
  export { RemoteCache } from "./remote-cache.d.ts";
22
30
  export type { RunSummary, TaskSummary } from "./run-summary.d.ts";
@@ -27,8 +35,10 @@ export type { TaskHasher, TaskHasherOptions } from "./task-hasher.d.ts";
27
35
  export { computeTaskHash, InProcessTaskHasher } from "./task-hasher.d.ts";
28
36
  export type { TaskOrchestratorOptions } from "./task-orchestrator.d.ts";
29
37
  export { TaskOrchestrator } from "./task-orchestrator.d.ts";
30
- export { TaskScheduler } from "./task-scheduler.d.ts";
38
+ export type { PartitionOptions } from "./task-scheduler.d.ts";
39
+ export { parsePartition, TaskScheduler } from "./task-scheduler.d.ts";
40
+ export { TerminalBuffer } from "./terminal-buffer.d.ts";
31
41
  export type { TrackedExecutionResult } from "./tracked-executor.d.ts";
32
42
  export { TrackedTaskExecutor } from "./tracked-executor.d.ts";
33
- export type { EnvironmentInput, ExternalDependencyInput, FileSetInput, InputDefinition, LifeCycleInterface, NamedInputs, ProjectConfiguration, ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode, RuntimeInput, TargetConfiguration, TargetDependencyConfig, Task, TaskExecutionOptions, TaskExecutor, TaskGraph, TaskHashDetails, TaskResult, TaskResults, TaskRunnerContext, TaskRunnerOptions, TasksRunner, TaskStatus, TaskTarget, WorkspaceConfiguration, } from "./types.d.ts";
43
+ export type { AffectedScope, ConcurrentCloseEvent, ConcurrentCommandConfig, ConcurrentCommandInput, ConcurrentRunnerOptions, ConcurrentRunResult, ConstraintsConfig, ConstraintViolation, DependencyKindRules, DependencyType, EnvironmentInput, ExternalDependencyInput, FileSetInput, InputDefinition, LifeCycleInterface, NamedInputs, ProcessEvent, ProjectConfiguration, ProjectGraph, ProjectGraphDependency, ProjectGraphProjectNode, RuntimeInput, TagRelationships, TargetConfiguration, TargetDependencyConfig, Task, TaskExecutionOptions, TaskExecutor, TaskGraph, TaskHashDetails, TaskResult, TaskResults, TaskRunnerContext, TaskRunnerOptions, TasksRunner, TaskStatus, TaskTarget, TypeBoundaries, WorkspaceConfiguration, } from "./types.d.ts";
34
44
  export { collectFiles, createFailureResult, hashFile, hashStrings, readPackageDeps, resolveTaskCwd, sortObjectKeys, uniqueId } from "./utils.d.ts";
package/dist/index.js CHANGED
@@ -1,20 +1,34 @@
1
- export { filterAffectedTasks, getAffectedProjects, getChangedFiles } from './packem_shared/filterAffectedTasks-I-18zPg6.js';
2
- export { Cache, formatCacheSize, parseCacheSize } from './packem_shared/Cache-IYpTYVUC.js';
3
- export { defaultTaskRunner } from './packem_shared/defaultTaskRunner-CrW4v5Ye.js';
1
+ export { buildForwardDependencyMap, buildReverseDependencyMap, expandAffected, filterAffectedTasks, getAffectedProjects, getChangedFiles } from './packem_shared/buildForwardDependencyMap-Cu08NWB1.js';
2
+ export { Cache, formatCacheSize, parseCacheSize } from './packem_shared/Cache-C23LywYn.js';
3
+ export { parseCommands } from './packem_shared/parseCommands-D-IgF8Zh.js';
4
+ export { runConcurrently } from './packem_shared/runConcurrently-ATDwJNR6.js';
5
+ export { runConcurrentFallback } from './packem_shared/runConcurrentFallback-3q46z4AS.js';
6
+ export { defaultTaskRunner } from './packem_shared/defaultTaskRunner-X1MIynHu.js';
7
+ export { detectScriptShell } from './packem_shared/detectScriptShell-CR-xXKA4.js';
4
8
  export { FileAccessTracker, generatePreloadScript } from './packem_shared/FileAccessTracker-CrtBAt5D.js';
5
- export { FingerprintManager } from './packem_shared/FingerprintManager-D6Y0erg-.js';
9
+ export { FingerprintManager } from './packem_shared/FingerprintManager-Cu-ta9ee.js';
6
10
  export { detectFrameworks, getFrameworkEnvVariables, inferFrameworkEnvPatterns } from './packem_shared/detectFrameworks-CeFzKE6J.js';
7
- export { projectGraphToDot, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot } from './packem_shared/projectGraphToDot-VdTjHcVp.js';
8
- export { IncrementalFileHasher } from './packem_shared/IncrementalFileHasher-Ds3J6dgb.js';
11
+ export { projectGraphToDot, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot } from './packem_shared/projectGraphToDot-C8uYeaPo.js';
12
+ export { IncrementalFileHasher } from './packem_shared/IncrementalFileHasher-Cm_kJY5V.js';
9
13
  export { CompositeLifeCycle, ConsoleLifeCycle, EmptyLifeCycle } from './packem_shared/CompositeLifeCycle-7AtYw1dv.js';
10
14
  export { LockfileHasher, extractPackageName, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile } from './packem_shared/extractPackageName-CbVNW-dr.js';
11
- export { isNativeAvailable, loadNativeBindings } from './packem_shared/isNativeAvailable-BWhnZ4ES.js';
12
- export { RemoteCache } from './packem_shared/RemoteCache-BDqrnDEi.js';
15
+ export { isNativeAvailable, loadNativeBindings } from './packem_shared/isNativeAvailable-BpD28A6Z.js';
16
+ export { enforceProjectConstraints } from './packem_shared/enforceProjectConstraints-_Ej0zHch.js';
17
+ export { RemoteCache } from './packem_shared/RemoteCache-BFceSe4a.js';
13
18
  export { generateRunSummary, writeRunSummary } from './packem_shared/generateRunSummary-qn-_jKwt.js';
14
19
  export { createTaskGraph, getTaskId, parseTaskId } from './packem_shared/createTaskGraph-CcsFaSrz.js';
15
- export { findCycle, findCycles, getDependentTasks, getLeafTasks, getTransitiveDependencies, makeAcyclic, reverseTaskGraph, walkTaskGraph } from './packem_shared/findCycle-DF4_BRdO.js';
16
- export { InProcessTaskHasher, computeTaskHash } from './packem_shared/computeTaskHash-BoCnnvIJ.js';
17
- export { TaskOrchestrator } from './packem_shared/TaskOrchestrator-BvYs3ONw.js';
18
- export { TaskScheduler } from './packem_shared/TaskScheduler-CJilHDta.js';
20
+ export { findCycle, findCycles, getDependentTasks, getLeafTasks, getTransitiveDependencies, makeAcyclic, reverseTaskGraph, walkTaskGraph } from './packem_shared/findCycle-DefgNYhg.js';
21
+ export { InProcessTaskHasher, computeTaskHash } from './packem_shared/computeTaskHash-B2SVZqgp.js';
22
+ export { TaskOrchestrator } from './packem_shared/TaskOrchestrator-lLn-PH1m.js';
23
+ export { TaskScheduler, parsePartition } from './packem_shared/parsePartition-C4-P5RjK.js';
24
+ export { TerminalBuffer } from './packem_shared/TerminalBuffer-D6zP2zLh.js';
19
25
  export { TrackedTaskExecutor } from './packem_shared/TrackedTaskExecutor-BGUKFE-7.js';
20
26
  export { c as collectFiles, a as createFailureResult, h as hashFile, b as hashStrings, r as readPackageDeps, d as resolveTaskCwd, s as sortObjectKeys, u as uniqueId } from './packem_shared/utils-zO0ZRgtf.js';
27
+ export { createInputHandler } from './packem_shared/createInputHandler-DTfePcTG.js';
28
+ export { expandArguments } from './packem_shared/expandArguments-0AwD2BIA.js';
29
+ export { expandShortcut } from './packem_shared/expandShortcut-BVG05ee4.js';
30
+ export { expandWildcard } from './packem_shared/expandWildcard-B0xN_knq.js';
31
+ export { formatTimingTable, logTimings } from './packem_shared/formatTimingTable-3qtCM552.js';
32
+ export { runTeardown } from './packem_shared/runTeardown-BAezH79J.js';
33
+ export { stripQuotes } from './packem_shared/stripQuotes-Cey-zwFf.js';
34
+ export { withRestart } from './packem_shared/withRestart-BREjRJa4.js';
@@ -30,6 +30,44 @@ interface NativeCycleResult {
30
30
  cycle: string[];
31
31
  has_cycle: boolean;
32
32
  }
33
+ interface NativeConcurrentCommandConfig {
34
+ command: string;
35
+ cwd?: string;
36
+ env?: Record<string, string>;
37
+ name?: string;
38
+ shell?: boolean;
39
+ stdin?: string;
40
+ }
41
+ interface NativeConcurrentRunnerOptions {
42
+ killOthers?: string[];
43
+ killSignal?: string;
44
+ killTimeout?: number;
45
+ maxProcesses?: number;
46
+ shellPath?: string;
47
+ successCondition?: string;
48
+ }
49
+ interface NativeProcessEvent {
50
+ commandName?: string;
51
+ durationMs?: number;
52
+ exitCode?: number;
53
+ index: number;
54
+ killed?: boolean;
55
+ kind: string;
56
+ message?: string;
57
+ text?: string;
58
+ }
59
+ interface NativeConcurrentCloseEvent {
60
+ command: string;
61
+ durationMs: number;
62
+ exitCode: number;
63
+ index: number;
64
+ killed: boolean;
65
+ name?: string;
66
+ }
67
+ interface NativeConcurrentRunResult {
68
+ closeEvents: NativeConcurrentCloseEvent[];
69
+ success: boolean;
70
+ }
33
71
  interface NativeBindings {
34
72
  collectFiles: (directory: string) => string[];
35
73
  computeTaskHash: (details: NativeTaskHashDetails) => string;
@@ -45,6 +83,8 @@ interface NativeBindings {
45
83
  hashFilesInDirectory: (directory: string, workspaceRoot: string) => NativeFileHash[];
46
84
  hashString: (input: string) => string;
47
85
  hashStrings: (inputs: string[]) => string;
86
+ runConcurrent: (commands: NativeConcurrentCommandConfig[], options: NativeConcurrentRunnerOptions, onEvent: (event: NativeProcessEvent) => void) => Promise<NativeConcurrentRunResult>;
87
+ runConcurrentBatch: (commands: NativeConcurrentCommandConfig[], options: NativeConcurrentRunnerOptions) => Promise<NativeConcurrentRunResult>;
48
88
  topologicalSort: (graph: NativeTaskGraph) => string[];
49
89
  }
50
90
  /**
@@ -52,13 +92,15 @@ interface NativeBindings {
52
92
  * The result is cached after the first attempt.
53
93
  *
54
94
  * napi v3 outputs the .node file to the package root as
55
- * `task-runner-native.&lt;platform>.node`. The napi-generated binding.js
95
+ * `task-runner-native.&lt;platform>.node`. The napi-generated index.js
56
96
  * handles platform detection automatically.
97
+ *
98
+ * Uses createRequire because the napi-generated index.js is CJS.
57
99
  */
58
100
  declare const loadNativeBindings: () => NativeBindings | undefined;
59
101
  /**
60
102
  * Returns true if the native addon is loaded and available.
61
103
  */
62
104
  declare const isNativeAvailable: () => boolean;
63
- export type { NativeBindings, NativeCycleResult, NativeFileHash, NativeTaskGraph, NativeTaskHashDetails };
105
+ export type { NativeBindings, NativeConcurrentCloseEvent, NativeConcurrentCommandConfig, NativeConcurrentRunnerOptions, NativeConcurrentRunResult, NativeCycleResult, NativeFileHash, NativeProcessEvent, NativeTaskGraph, NativeTaskHashDetails, };
64
106
  export { isNativeAvailable, loadNativeBindings };
@@ -27,8 +27,8 @@ const {
27
27
  cp,
28
28
  readdir
29
29
  } = __cjs_getBuiltinModule("node:fs/promises");
30
- import { join, resolve, dirname } from '@visulima/path';
31
30
  import { formatBytes, parseBytes } from '@visulima/humanizer';
31
+ import { join, resolve, dirname } from '@visulima/path';
32
32
  import { u as uniqueId } from './utils-zO0ZRgtf.js';
33
33
 
34
34
  const DEFAULT_MAX_CACHE_AGE = 7 * 24 * 60 * 60 * 1e3;
@@ -58,7 +58,7 @@ const getDirectorySize = async (directoryPath) => {
58
58
  const parseCacheSize = (sizeString) => {
59
59
  const result = parseBytes(sizeString.trim());
60
60
  if (Number.isNaN(result)) {
61
- throw new Error(`Invalid cache size format: "${sizeString}". Expected format like "500MB" or "1GB".`);
61
+ throw new TypeError(`Invalid cache size format: "${sizeString}". Expected format like "500MB" or "1GB".`);
62
62
  }
63
63
  return result;
64
64
  };
@@ -222,7 +222,6 @@ class Cache {
222
222
  * Removes old cache entries that exceed the maximum age,
223
223
  * and enforces the maximum cache size by evicting oldest entries.
224
224
  */
225
- // eslint-disable-next-line sonarjs/cognitive-complexity
226
225
  async removeOldEntries() {
227
226
  try {
228
227
  const entries = await readdir(this.#cacheDirectory);
@@ -30,7 +30,6 @@ class FingerprintManager {
30
30
  constructor(workspaceRoot) {
31
31
  this.#workspaceRoot = resolve(workspaceRoot);
32
32
  }
33
- // eslint-disable-next-line sonarjs/cognitive-complexity
34
33
  async createFingerprint(accesses, command, args, envVariables, envPatterns = [], untrackedEnvVariables = []) {
35
34
  const fileHashes = {};
36
35
  const missingPaths = /* @__PURE__ */ new Set();
@@ -118,7 +118,7 @@ class IncrementalFileHasher {
118
118
  return void 0;
119
119
  }
120
120
  const cached = this.#snapshot.get(filePath);
121
- if (cached && cached.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) {
121
+ if (cached?.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) {
122
122
  return cached.hash;
123
123
  }
124
124
  const content = await readFile(filePath);
@@ -1,8 +1,8 @@
1
1
  import { d as resolveTaskCwd, a as createFailureResult, e as createXxh3Hasher } from './utils-zO0ZRgtf.js';
2
2
  import { join } from '@visulima/path';
3
- import { FingerprintManager } from './FingerprintManager-D6Y0erg-.js';
3
+ import { FingerprintManager } from './FingerprintManager-Cu-ta9ee.js';
4
4
  import { generateRunSummary, writeRunSummary } from './generateRunSummary-qn-_jKwt.js';
5
- import { computeTaskHash } from './computeTaskHash-BoCnnvIJ.js';
5
+ import { computeTaskHash } from './computeTaskHash-B2SVZqgp.js';
6
6
  import { TrackedTaskExecutor } from './TrackedTaskExecutor-BGUKFE-7.js';
7
7
 
8
8
  const hashFingerprint = (fingerprint) => {
@@ -107,7 +107,6 @@ class TaskOrchestrator {
107
107
  }
108
108
  return this.#results;
109
109
  }
110
- // eslint-disable-next-line sonarjs/cognitive-complexity
111
110
  async #executionLoop() {
112
111
  while (!this.#scheduler.isComplete() && !this.#aborted) {
113
112
  const batch = this.#scheduler.getNextBatch();
@@ -153,7 +152,6 @@ class TaskOrchestrator {
153
152
  await Promise.all(this.#inFlightTasks.values());
154
153
  }
155
154
  }
156
- // eslint-disable-next-line sonarjs/cognitive-complexity
157
155
  async #processTask(task) {
158
156
  const startTime = Date.now();
159
157
  const hashDetails = await this.#taskHasher.hashTask(task);
@@ -186,7 +184,6 @@ class TaskOrchestrator {
186
184
  }
187
185
  return result;
188
186
  }
189
- // eslint-disable-next-line sonarjs/cognitive-complexity
190
187
  async #processTaskWithFingerprint(task) {
191
188
  const startTime = Date.now();
192
189
  if (!this.#skipCache && task.cache !== false) {
@@ -0,0 +1,250 @@
1
+ class TerminalBuffer {
2
+ #lines = [""];
3
+ #row = 0;
4
+ #col = 0;
5
+ #maxBytes;
6
+ constructor(maxBytes = 256 * 1024) {
7
+ this.#maxBytes = maxBytes;
8
+ }
9
+ /**
10
+ * Process raw PTY output data.
11
+ */
12
+ write(data) {
13
+ let i = 0;
14
+ while (i < data.length) {
15
+ const ch = data[i];
16
+ if (ch === "\x1B") {
17
+ if (i + 1 >= data.length) {
18
+ i++;
19
+ continue;
20
+ }
21
+ if (data[i + 1] === "[") {
22
+ const consumed = this.#processCsi(data, i + 2);
23
+ i = consumed;
24
+ continue;
25
+ }
26
+ if (data[i + 1] === "(" || data[i + 1] === ")") {
27
+ i += 3;
28
+ } else {
29
+ i += 2;
30
+ }
31
+ continue;
32
+ }
33
+ if (ch === "\r") {
34
+ this.#col = 0;
35
+ i++;
36
+ continue;
37
+ }
38
+ if (ch === "\n") {
39
+ this.#row++;
40
+ this.#col = 0;
41
+ this.#ensureRow(this.#row);
42
+ i++;
43
+ continue;
44
+ }
45
+ this.#ensureRow(this.#row);
46
+ this.#putChar(ch);
47
+ this.#col++;
48
+ i++;
49
+ }
50
+ this.#trimToMaxBytes();
51
+ }
52
+ /** Get the current buffer content as a string. */
53
+ toString() {
54
+ return this.#lines.join("\n");
55
+ }
56
+ /**
57
+ * Process a CSI sequence starting after "ESC [".
58
+ * Returns the index to continue parsing from.
59
+ */
60
+ #processCsi(data, start) {
61
+ let j = start;
62
+ let params = "";
63
+ while (j < data.length && (data[j] >= "0" && data[j] <= "9" || data[j] === ";")) {
64
+ params += data[j];
65
+ j++;
66
+ }
67
+ if (j >= data.length) {
68
+ return j;
69
+ }
70
+ const cmd = data[j];
71
+ const n = Number.parseInt(params, 10) || 1;
72
+ switch (cmd) {
73
+ case "A":
74
+ this.#row = Math.max(0, this.#row - n);
75
+ break;
76
+ case "B":
77
+ this.#row = Math.min(this.#lines.length - 1, this.#row + n);
78
+ break;
79
+ case "C":
80
+ this.#col += n;
81
+ break;
82
+ case "D":
83
+ this.#col = Math.max(0, this.#col - n);
84
+ break;
85
+ case "G":
86
+ this.#col = Math.max(0, n - 1);
87
+ break;
88
+ case "H":
89
+ // cursor position (row;col, 1-based)
90
+ case "f": {
91
+ const parts = params.split(";");
92
+ this.#row = Math.max(0, (Number.parseInt(parts[0] ?? "1", 10) || 1) - 1);
93
+ this.#col = Math.max(0, (Number.parseInt(parts[1] ?? "1", 10) || 1) - 1);
94
+ this.#ensureRow(this.#row);
95
+ break;
96
+ }
97
+ case "J":
98
+ this.#eraseDisplay(Number.parseInt(params, 10) || 0);
99
+ break;
100
+ case "K":
101
+ this.#eraseLine(Number.parseInt(params, 10) || 0);
102
+ break;
103
+ case "m": {
104
+ const seq = `\x1B[${params}m`;
105
+ this.#ensureRow(this.#row);
106
+ this.#appendAtCursor(seq);
107
+ break;
108
+ }
109
+ }
110
+ return j + 1;
111
+ }
112
+ /** Write a visible character at the cursor position (overwrites). */
113
+ #putChar(ch) {
114
+ const line = this.#lines[this.#row] ?? "";
115
+ const visCol = this.#col;
116
+ let strIndex = 0;
117
+ let vis = 0;
118
+ while (strIndex < line.length && vis < visCol) {
119
+ if (line[strIndex] === "\x1B" && line[strIndex + 1] === "[") {
120
+ strIndex += 2;
121
+ while (strIndex < line.length && !(line[strIndex] >= "A" && line[strIndex] <= "Z" || line[strIndex] >= "a" && line[strIndex] <= "z")) {
122
+ strIndex++;
123
+ }
124
+ if (strIndex < line.length) {
125
+ strIndex++;
126
+ }
127
+ } else {
128
+ strIndex++;
129
+ vis++;
130
+ }
131
+ }
132
+ if (vis < visCol) {
133
+ this.#lines[this.#row] = line + " ".repeat(visCol - vis) + ch;
134
+ } else {
135
+ let endIndex = strIndex;
136
+ if (endIndex < line.length && line[endIndex] !== "\x1B") {
137
+ endIndex++;
138
+ }
139
+ this.#lines[this.#row] = line.slice(0, strIndex) + ch + line.slice(endIndex);
140
+ }
141
+ }
142
+ /** Append a zero-width sequence (SGR) at the current cursor position. */
143
+ #appendAtCursor(seq) {
144
+ const line = this.#lines[this.#row] ?? "";
145
+ let strIndex = 0;
146
+ let vis = 0;
147
+ while (strIndex < line.length && vis < this.#col) {
148
+ if (line[strIndex] === "\x1B" && line[strIndex + 1] === "[") {
149
+ strIndex += 2;
150
+ while (strIndex < line.length && !(line[strIndex] >= "A" && line[strIndex] <= "Z" || line[strIndex] >= "a" && line[strIndex] <= "z")) {
151
+ strIndex++;
152
+ }
153
+ if (strIndex < line.length) {
154
+ strIndex++;
155
+ }
156
+ } else {
157
+ strIndex++;
158
+ vis++;
159
+ }
160
+ }
161
+ this.#lines[this.#row] = line.slice(0, strIndex) + seq + line.slice(strIndex);
162
+ }
163
+ #eraseDisplay(mode) {
164
+ if (mode === 2) {
165
+ this.#lines = [""];
166
+ this.#row = 0;
167
+ this.#col = 0;
168
+ } else if (mode === 0) {
169
+ this.#ensureRow(this.#row);
170
+ this.#truncateLineAtCol(this.#row, this.#col);
171
+ this.#lines.length = this.#row + 1;
172
+ } else if (mode === 1) {
173
+ for (let r = 0; r < this.#row; r++) {
174
+ this.#lines[r] = "";
175
+ }
176
+ this.#ensureRow(this.#row);
177
+ this.#fillLineToCol(this.#row, this.#col);
178
+ }
179
+ }
180
+ #eraseLine(mode) {
181
+ this.#ensureRow(this.#row);
182
+ if (mode === 2) {
183
+ this.#lines[this.#row] = "";
184
+ } else if (mode === 0) {
185
+ this.#truncateLineAtCol(this.#row, this.#col);
186
+ } else if (mode === 1) {
187
+ this.#fillLineToCol(this.#row, this.#col);
188
+ }
189
+ }
190
+ /** Truncate a line at the given visual column. */
191
+ #truncateLineAtCol(row, col) {
192
+ const line = this.#lines[row] ?? "";
193
+ let strIndex = 0;
194
+ let vis = 0;
195
+ while (strIndex < line.length && vis < col) {
196
+ if (line[strIndex] === "\x1B" && line[strIndex + 1] === "[") {
197
+ strIndex += 2;
198
+ while (strIndex < line.length && !(line[strIndex] >= "A" && line[strIndex] <= "Z" || line[strIndex] >= "a" && line[strIndex] <= "z")) {
199
+ strIndex++;
200
+ }
201
+ if (strIndex < line.length) {
202
+ strIndex++;
203
+ }
204
+ } else {
205
+ strIndex++;
206
+ vis++;
207
+ }
208
+ }
209
+ this.#lines[row] = line.slice(0, strIndex);
210
+ }
211
+ /** Fill a line with spaces from start to the given visual column. */
212
+ #fillLineToCol(row, col) {
213
+ const line = this.#lines[row] ?? "";
214
+ let strIndex = 0;
215
+ let vis = 0;
216
+ while (strIndex < line.length && vis < col) {
217
+ if (line[strIndex] === "\x1B" && line[strIndex + 1] === "[") {
218
+ strIndex += 2;
219
+ while (strIndex < line.length && !(line[strIndex] >= "A" && line[strIndex] <= "Z" || line[strIndex] >= "a" && line[strIndex] <= "z")) {
220
+ strIndex++;
221
+ }
222
+ if (strIndex < line.length) {
223
+ strIndex++;
224
+ }
225
+ } else {
226
+ strIndex++;
227
+ vis++;
228
+ }
229
+ }
230
+ this.#lines[row] = " ".repeat(col) + line.slice(strIndex);
231
+ }
232
+ #ensureRow(row) {
233
+ while (this.#lines.length <= row) {
234
+ this.#lines.push("");
235
+ }
236
+ }
237
+ #trimToMaxBytes() {
238
+ let totalSize = 0;
239
+ for (const line of this.#lines) {
240
+ totalSize += line.length + 1;
241
+ }
242
+ while (totalSize > this.#maxBytes && this.#lines.length > 1) {
243
+ const removed = this.#lines.shift();
244
+ totalSize -= removed.length + 1;
245
+ this.#row = Math.max(0, this.#row - 1);
246
+ }
247
+ }
248
+ }
249
+
250
+ export { TerminalBuffer };
@@ -22,7 +22,7 @@ const {
22
22
  } = __cjs_getBuiltinModule("node:child_process");
23
23
 
24
24
  const validateGitRef = (ref) => {
25
- if (!/^[a-zA-Z0-9._\-/~^@{}]+$/.test(ref)) {
25
+ if (!/^[\w.\-/~^@{}]+$/.test(ref)) {
26
26
  throw new Error(`Invalid git ref: "${ref}". Only alphanumeric characters, dots, dashes, underscores, slashes, tildes, carets, and @ are allowed.`);
27
27
  }
28
28
  };
@@ -38,34 +38,69 @@ const findProjectForFile = (filePath, projects) => {
38
38
  }
39
39
  return bestMatch;
40
40
  };
41
- const expandAffected = (affectedProjects, projectGraph) => {
42
- const reverseDependencies = /* @__PURE__ */ new Map();
41
+ const buildReverseDependencyMap = (projectGraph) => {
42
+ const map = /* @__PURE__ */ new Map();
43
43
  for (const [project, dependencies] of Object.entries(projectGraph.dependencies)) {
44
44
  for (const dependency of dependencies) {
45
- let set = reverseDependencies.get(dependency.target);
45
+ let set = map.get(dependency.target);
46
46
  if (!set) {
47
47
  set = /* @__PURE__ */ new Set();
48
- reverseDependencies.set(dependency.target, set);
48
+ map.set(dependency.target, set);
49
49
  }
50
50
  set.add(project);
51
51
  }
52
52
  }
53
- const queue = [...affectedProjects];
53
+ return map;
54
+ };
55
+ const buildForwardDependencyMap = (projectGraph) => {
56
+ const map = /* @__PURE__ */ new Map();
57
+ for (const [project, dependencies] of Object.entries(projectGraph.dependencies)) {
58
+ const set = /* @__PURE__ */ new Set();
59
+ for (const dependency of dependencies) {
60
+ set.add(dependency.target);
61
+ }
62
+ if (set.size > 0) {
63
+ map.set(project, set);
64
+ }
65
+ }
66
+ return map;
67
+ };
68
+ const expandInDirection = (affected, seeds, adjacency, scope) => {
69
+ const added = /* @__PURE__ */ new Set();
70
+ const visited = new Set(seeds);
71
+ const queue = [...seeds];
54
72
  while (queue.length > 0) {
55
73
  const project = queue.shift();
56
- if (project === void 0) {
74
+ const neighbors = adjacency.get(project);
75
+ if (!neighbors) {
57
76
  continue;
58
77
  }
59
- const dependents = reverseDependencies.get(project);
60
- if (dependents) {
61
- for (const dependent of dependents) {
62
- if (!affectedProjects.has(dependent)) {
63
- affectedProjects.add(dependent);
64
- queue.push(dependent);
78
+ for (const neighbor of neighbors) {
79
+ if (!visited.has(neighbor)) {
80
+ visited.add(neighbor);
81
+ affected.add(neighbor);
82
+ added.add(neighbor);
83
+ if (scope === "deep") {
84
+ queue.push(neighbor);
65
85
  }
66
86
  }
67
87
  }
68
88
  }
89
+ return added;
90
+ };
91
+ const expandAffected = (changedProjects, projectGraph, options) => {
92
+ const affected = new Set(changedProjects);
93
+ let downstream = /* @__PURE__ */ new Set();
94
+ let upstream = /* @__PURE__ */ new Set();
95
+ if (options.downstream !== "none") {
96
+ const reverseDeps = buildReverseDependencyMap(projectGraph);
97
+ downstream = expandInDirection(affected, changedProjects, reverseDeps, options.downstream);
98
+ }
99
+ if (options.upstream !== "none") {
100
+ const forwardDeps = buildForwardDependencyMap(projectGraph);
101
+ upstream = expandInDirection(affected, changedProjects, forwardDeps, options.upstream);
102
+ }
103
+ return { affected, downstream, upstream };
69
104
  };
70
105
  const getMergeBase = (workspaceRoot, base, head) => new Promise((resolve, reject) => {
71
106
  execFile("git", ["merge-base", base, head], { cwd: workspaceRoot }, (error, stdout) => {
@@ -103,7 +138,7 @@ const getChangedFiles = async (workspaceRoot, base, head) => {
103
138
  }
104
139
  };
105
140
  const getAffectedProjects = async (options) => {
106
- const { base = "main", head = "HEAD", projectGraph, projects, workspaceRoot } = options;
141
+ const { base = "main", downstream = "deep", head = "HEAD", projectGraph, projects, upstream = "none", workspaceRoot } = options;
107
142
  const changedFiles = await getChangedFiles(workspaceRoot, base, head);
108
143
  const changedProjects = /* @__PURE__ */ new Set();
109
144
  for (const file of changedFiles) {
@@ -114,16 +149,19 @@ const getAffectedProjects = async (options) => {
114
149
  return {
115
150
  affectedProjects: Object.keys(projects),
116
151
  changedFiles,
117
- changedProjects: Object.keys(projects)
152
+ changedProjects: [...changedProjects],
153
+ downstreamProjects: [],
154
+ upstreamProjects: []
118
155
  };
119
156
  }
120
157
  }
121
- const affectedProjects = new Set(changedProjects);
122
- expandAffected(affectedProjects, projectGraph);
158
+ const result = expandAffected(changedProjects, projectGraph, { downstream, upstream });
123
159
  return {
124
- affectedProjects: [...affectedProjects],
160
+ affectedProjects: [...result.affected],
125
161
  changedFiles,
126
- changedProjects: [...changedProjects]
162
+ changedProjects: [...changedProjects],
163
+ downstreamProjects: [...result.downstream],
164
+ upstreamProjects: [...result.upstream]
127
165
  };
128
166
  };
129
167
  const filterAffectedTasks = (taskIds, affectedProjects) => taskIds.filter((taskId) => {
@@ -132,4 +170,4 @@ const filterAffectedTasks = (taskIds, affectedProjects) => taskIds.filter((taskI
132
170
  return affectedProjects.has(project);
133
171
  });
134
172
 
135
- export { filterAffectedTasks, getAffectedProjects, getChangedFiles };
173
+ export { buildForwardDependencyMap, buildReverseDependencyMap, expandAffected, filterAffectedTasks, getAffectedProjects, getChangedFiles };