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

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 (49) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +170 -36
  4. package/binding.js +204 -0
  5. package/dist/affected.d.ts +48 -0
  6. package/dist/cache.d.ts +103 -0
  7. package/dist/default-task-runner.d.ts +44 -0
  8. package/dist/file-access-tracker.d.ts +53 -0
  9. package/dist/fingerprint.d.ts +45 -0
  10. package/dist/framework-inference.d.ts +35 -0
  11. package/dist/graph-visualizer.d.ts +74 -0
  12. package/dist/incremental-hasher.d.ts +58 -0
  13. package/dist/index.d.ts +34 -0
  14. package/dist/index.js +20 -0
  15. package/dist/life-cycle.d.ts +36 -0
  16. package/dist/lockfile-hasher.d.ts +73 -0
  17. package/dist/native-binding.d.ts +64 -0
  18. package/dist/packem_shared/Cache-IYpTYVUC.js +298 -0
  19. package/dist/packem_shared/CompositeLifeCycle-7AtYw1dv.js +112 -0
  20. package/dist/packem_shared/FileAccessTracker-CrtBAt5D.js +239 -0
  21. package/dist/packem_shared/FingerprintManager-D6Y0erg-.js +227 -0
  22. package/dist/packem_shared/IncrementalFileHasher-Ds3J6dgb.js +151 -0
  23. package/dist/packem_shared/RemoteCache-BDqrnDEi.js +179 -0
  24. package/dist/packem_shared/TaskOrchestrator-BvYs3ONw.js +342 -0
  25. package/dist/packem_shared/TaskScheduler-CJilHDta.js +111 -0
  26. package/dist/packem_shared/TrackedTaskExecutor-BGUKFE-7.js +164 -0
  27. package/dist/packem_shared/collectFiles-ClXHnHhg.js +22 -0
  28. package/dist/packem_shared/computeTaskHash-BoCnnvIJ.js +356 -0
  29. package/dist/packem_shared/createTaskGraph-CcsFaSrz.js +164 -0
  30. package/dist/packem_shared/defaultTaskRunner-CrW4v5Ye.js +79 -0
  31. package/dist/packem_shared/detectFrameworks-CeFzKE6J.js +101 -0
  32. package/dist/packem_shared/extractPackageName-CbVNW-dr.js +189 -0
  33. package/dist/packem_shared/filterAffectedTasks-I-18zPg6.js +135 -0
  34. package/dist/packem_shared/findCycle-DF4_BRdO.js +212 -0
  35. package/dist/packem_shared/generateRunSummary-qn-_jKwt.js +134 -0
  36. package/dist/packem_shared/isNativeAvailable-BWhnZ4ES.js +19 -0
  37. package/dist/packem_shared/projectGraphToDot-VdTjHcVp.js +202 -0
  38. package/dist/packem_shared/utils-zO0ZRgtf.js +390 -0
  39. package/dist/remote-cache.d.ts +55 -0
  40. package/dist/run-summary.d.ts +89 -0
  41. package/dist/task-graph-utils.d.ts +39 -0
  42. package/dist/task-graph.d.ts +22 -0
  43. package/dist/task-hasher.d.ts +67 -0
  44. package/dist/task-orchestrator.d.ts +38 -0
  45. package/dist/task-scheduler.d.ts +18 -0
  46. package/dist/tracked-executor.d.ts +46 -0
  47. package/dist/types.d.ts +385 -0
  48. package/dist/utils.d.ts +39 -0
  49. package/package.json +72 -7
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Represents a target that a task executes against.
3
+ */
4
+ export interface TaskTarget {
5
+ /** Optional configuration name */
6
+ configuration?: string;
7
+ /** The project name */
8
+ project: string;
9
+ /** The target name (e.g., "build", "test", "lint") */
10
+ target: string;
11
+ }
12
+ /**
13
+ * Represents a single task to be executed.
14
+ */
15
+ export interface Task {
16
+ /** Whether this task is eligible for caching */
17
+ cache?: boolean;
18
+ /** Hash of the task inputs for caching */
19
+ hash?: string;
20
+ /** Detailed hash information */
21
+ hashDetails?: TaskHashDetails;
22
+ /** Unique identifier for the task, typically "project:target:configuration" */
23
+ id: string;
24
+ /** Output file paths produced by this task */
25
+ outputs: string[];
26
+ /** Overrides/extra options passed to the task */
27
+ overrides: Record<string, unknown>;
28
+ /** Whether this task supports parallel execution */
29
+ parallelism?: boolean;
30
+ /** The project root directory */
31
+ projectRoot?: string;
32
+ /** The target this task executes */
33
+ target: TaskTarget;
34
+ }
35
+ /**
36
+ * Represents detailed hash information for a task.
37
+ */
38
+ export interface TaskHashDetails {
39
+ /** The command hash */
40
+ command: string;
41
+ /** Hash of implicit dependencies */
42
+ implicitDeps?: Record<string, string>;
43
+ /** Hashes of input files */
44
+ nodes: Record<string, string>;
45
+ /** Hash of runtime values */
46
+ runtime?: Record<string, string>;
47
+ }
48
+ /**
49
+ * Represents a directed acyclic graph of tasks.
50
+ */
51
+ export interface TaskGraph {
52
+ /** Dependencies for each task */
53
+ dependencies: Record<string, string[]>;
54
+ /** Top-level tasks that no other task depends on (the final outputs to run) */
55
+ roots: string[];
56
+ /** All tasks in the graph, keyed by task ID */
57
+ tasks: Record<string, Task>;
58
+ }
59
+ /**
60
+ * Possible states of a task after execution.
61
+ */
62
+ export type TaskStatus = "success" | "failure" | "skipped" | "local-cache" | "local-cache-kept-existing" | "remote-cache";
63
+ /**
64
+ * Result of executing a task.
65
+ */
66
+ export interface TaskResult {
67
+ /** The exit code, if applicable */
68
+ code?: number;
69
+ /** The end time of the task */
70
+ endTime?: number;
71
+ /** The start time of the task */
72
+ startTime?: number;
73
+ status: TaskStatus;
74
+ task: Task;
75
+ /** The terminal output produced during execution */
76
+ terminalOutput?: string;
77
+ }
78
+ /**
79
+ * Map of task results, keyed by task ID.
80
+ */
81
+ export type TaskResults = Map<string, TaskResult>;
82
+ /**
83
+ * Configuration for a project in the workspace.
84
+ */
85
+ export interface ProjectConfiguration {
86
+ /** Implicit dependencies on other projects */
87
+ implicitDependencies?: string[];
88
+ /** The project type */
89
+ projectType?: "library" | "application";
90
+ /** The root directory of the project, relative to workspace root */
91
+ root: string;
92
+ /** The source root directory */
93
+ sourceRoot?: string;
94
+ /** Tags for filtering */
95
+ tags?: string[];
96
+ /** Named targets and their configurations */
97
+ targets?: Record<string, TargetConfiguration>;
98
+ }
99
+ /**
100
+ * Configuration for a target within a project.
101
+ */
102
+ export interface TargetConfiguration {
103
+ /** Whether this target is cacheable */
104
+ cache?: boolean;
105
+ /** The command to run (alternative to executor) */
106
+ command?: string;
107
+ /** Named configurations (e.g., "production", "development") */
108
+ configurations?: Record<string, Record<string, unknown>>;
109
+ /** Other targets this target depends on */
110
+ dependsOn?: (string | TargetDependencyConfig)[];
111
+ /** The executor/command to run */
112
+ executor?: string;
113
+ /** Input patterns for cache invalidation */
114
+ inputs?: (string | InputDefinition)[];
115
+ /** Options passed to the executor */
116
+ options?: Record<string, unknown>;
117
+ /** Output patterns produced by this target */
118
+ outputs?: string[];
119
+ /** Whether this target supports parallel execution */
120
+ parallelism?: boolean;
121
+ }
122
+ /**
123
+ * Defines a dependency on another target.
124
+ */
125
+ export interface TargetDependencyConfig {
126
+ /** Whether this is a dependency on the same project */
127
+ dependencies?: boolean;
128
+ /** Params to pass through */
129
+ params?: "forward" | "ignore";
130
+ /** The project name (if different from the current project) */
131
+ projects?: string | string[];
132
+ /** The target name */
133
+ target: string;
134
+ }
135
+ /**
136
+ * Defines an input for cache invalidation.
137
+ */
138
+ export type InputDefinition = FileSetInput | RuntimeInput | EnvironmentInput | ExternalDependencyInput;
139
+ /**
140
+ * An input based on file patterns.
141
+ */
142
+ export interface FileSetInput {
143
+ fileset: string;
144
+ }
145
+ /**
146
+ * An input based on a runtime command.
147
+ */
148
+ export interface RuntimeInput {
149
+ runtime: string;
150
+ }
151
+ /**
152
+ * An input based on environment variables.
153
+ */
154
+ export interface EnvironmentInput {
155
+ env: string;
156
+ }
157
+ /**
158
+ * An input based on external dependency versions.
159
+ */
160
+ export interface ExternalDependencyInput {
161
+ externalDependencies: string[];
162
+ }
163
+ /**
164
+ * Workspace configuration containing all project configurations.
165
+ */
166
+ export interface WorkspaceConfiguration {
167
+ /** All projects in the workspace, keyed by project name */
168
+ projects: Record<string, ProjectConfiguration>;
169
+ }
170
+ /**
171
+ * A dependency relationship in the project graph.
172
+ */
173
+ export interface ProjectGraphDependency {
174
+ /** The source project */
175
+ source: string;
176
+ /** The target project */
177
+ target: string;
178
+ /** The type of dependency */
179
+ type: "static" | "dynamic" | "implicit";
180
+ }
181
+ /**
182
+ * A node in the project graph.
183
+ */
184
+ export interface ProjectGraphProjectNode {
185
+ /** The project configuration */
186
+ data: ProjectConfiguration;
187
+ /** The project name */
188
+ name: string;
189
+ /** The type of project */
190
+ type: "library" | "application";
191
+ }
192
+ /**
193
+ * The project graph represents the dependency relationships between projects.
194
+ */
195
+ export interface ProjectGraph {
196
+ /** All dependency relationships */
197
+ dependencies: Record<string, ProjectGraphDependency[]>;
198
+ /** All project nodes, keyed by project name */
199
+ nodes: Record<string, ProjectGraphProjectNode>;
200
+ }
201
+ /**
202
+ * Named input definitions that can be referenced by name.
203
+ */
204
+ export interface NamedInputs {
205
+ [name: string]: (string | InputDefinition)[];
206
+ }
207
+ /**
208
+ * Configuration for the task runner.
209
+ */
210
+ export interface TaskRunnerOptions {
211
+ /**
212
+ * Enable auto-fingerprinting mode (Vite Task-style).
213
+ * When enabled, the task runner automatically tracks which files
214
+ * a task accesses during execution and uses that for cache invalidation
215
+ * instead of requiring manual input configuration.
216
+ *
217
+ * Falls back to explicit inputs (Nx-style) when file tracking
218
+ * is not supported on the current platform.
219
+ * @default false
220
+ */
221
+ autoFingerprint?: boolean;
222
+ /**
223
+ * Whether to show cache miss diagnostics (why a cache miss occurred).
224
+ * @default false
225
+ */
226
+ cacheDiagnostics?: boolean;
227
+ /** Directory for storing cache */
228
+ cacheDirectory?: string;
229
+ /**
230
+ * Dry-run mode: compute hashes and check cache but don't execute tasks.
231
+ * Useful for debugging cache hits/misses.
232
+ * @default false
233
+ */
234
+ dryRun?: boolean;
235
+ /** Custom environment variables to include in hash */
236
+ envVars?: string[];
237
+ /**
238
+ * Environment variable patterns to include in auto-fingerprint.
239
+ * Supports wildcard patterns like "VITE_*".
240
+ * Only used when autoFingerprint is enabled.
241
+ */
242
+ fingerprintEnvPatterns?: string[];
243
+ /**
244
+ * Enable framework environment variable inference.
245
+ * When true, automatically detects common frontend frameworks and includes
246
+ * their public env var prefixes in the task hash:
247
+ * - Next.js: NEXT_PUBLIC_*
248
+ * - Vite: VITE_*
249
+ * - Create React App: REACT_APP_*
250
+ * - Gatsby: GATSBY_*
251
+ * - Nuxt: NUXT_PUBLIC_*
252
+ * - Expo: EXPO_PUBLIC_*
253
+ *
254
+ * Matches Turborepo's framework inference behavior.
255
+ * @default false
256
+ */
257
+ frameworkInference?: boolean;
258
+ /**
259
+ * Global environment variables that invalidate ALL task hashes.
260
+ * Matches Turborepo's `globalEnv`.
261
+ */
262
+ globalEnv?: string[];
263
+ /**
264
+ * Global input files that invalidate ALL task hashes when changed.
265
+ * These are workspace-root-relative paths.
266
+ *
267
+ * Default: ["package-lock.json", "pnpm-lock.yaml", "yarn.lock",
268
+ * "tsconfig.base.json", ".env"]
269
+ *
270
+ * When any global input changes, every task's hash changes,
271
+ * forcing a full rebuild. This matches Turborepo's `globalDependencies`.
272
+ */
273
+ globalInputs?: string[];
274
+ /** Maximum age of cache entries in milliseconds */
275
+ maxCacheAge?: number;
276
+ /** Maximum cache size (e.g., "1GB") */
277
+ maxCacheSize?: string;
278
+ /** Named inputs for cache invalidation */
279
+ namedInputs?: NamedInputs;
280
+ /** Maximum number of parallel tasks */
281
+ parallel?: number | boolean;
282
+ /**
283
+ * Remote cache configuration.
284
+ * When configured, the task runner will check the remote cache
285
+ * after a local cache miss, and upload results after execution.
286
+ */
287
+ remoteCache?: {
288
+ /**
289
+ * Called when a fire-and-forget upload fails.
290
+ * Since uploads are non-blocking, errors are silently swallowed by default.
291
+ * Provide this callback to log or report upload failures.
292
+ */
293
+ onUploadError?: (hash: string, error: unknown) => void;
294
+ /** Enable remote reads (default: true) */
295
+ read?: boolean;
296
+ /** Team/namespace for cache isolation */
297
+ teamId?: string;
298
+ /** Authentication token */
299
+ token?: string;
300
+ /** Remote cache server URL */
301
+ url: string;
302
+ /** Enable remote writes (default: true) */
303
+ write?: boolean;
304
+ };
305
+ /** Whether to skip cache reads */
306
+ skipNxCache?: boolean;
307
+ /**
308
+ * Enable smart lockfile hashing.
309
+ * Instead of hashing the entire lockfile (which busts ALL caches),
310
+ * only hash the resolved versions of each package's actual dependencies.
311
+ * This matches Turborepo's smart lockfile hashing behavior.
312
+ * @default false
313
+ */
314
+ smartLockfileHashing?: boolean;
315
+ /**
316
+ * Generate a detailed JSON run summary after execution.
317
+ * When true, writes a summary file to `.task-runner/runs/` containing
318
+ * all task inputs, outputs, hashes, timings, and cache status.
319
+ *
320
+ * Useful for debugging cache misses and comparing runs.
321
+ * Matches Turborepo's `--summarize` flag.
322
+ * @default false
323
+ */
324
+ summarize?: boolean;
325
+ /** Target-specific default configurations */
326
+ targetDefaults?: Record<string, Partial<TargetConfiguration>>;
327
+ /**
328
+ * Environment variables that should be passed to tasks but NOT
329
+ * included in the cache fingerprint. Useful for CI-specific vars.
330
+ * Only used when autoFingerprint is enabled.
331
+ */
332
+ untrackedEnvVars?: string[];
333
+ }
334
+ /**
335
+ * Options for executing a task.
336
+ */
337
+ export interface TaskExecutionOptions {
338
+ /** Whether to capture output */
339
+ captureOutput?: boolean;
340
+ /** The working directory */
341
+ cwd?: string;
342
+ /** Environment variables */
343
+ env?: Record<string, string>;
344
+ /** Stream output as it arrives */
345
+ streamOutput?: boolean;
346
+ }
347
+ /**
348
+ * A task executor function.
349
+ */
350
+ export type TaskExecutor = (task: Task, options: TaskExecutionOptions) => Promise<{
351
+ code: number;
352
+ terminalOutput: string;
353
+ }>;
354
+ /**
355
+ * The function type for a task runner.
356
+ */
357
+ export type TasksRunner = (tasks: Task[], options: TaskRunnerOptions, context: TaskRunnerContext) => Promise<TaskResults>;
358
+ /**
359
+ * Context provided to the task runner.
360
+ */
361
+ export interface TaskRunnerContext {
362
+ /** Lifecycle hooks */
363
+ lifeCycle: LifeCycleInterface;
364
+ /** The project graph */
365
+ projectGraph: ProjectGraph;
366
+ /** The task executor */
367
+ taskExecutor: TaskExecutor;
368
+ /** The task graph */
369
+ taskGraph: TaskGraph;
370
+ /** The workspace root directory */
371
+ workspaceRoot: string;
372
+ }
373
+ /**
374
+ * Interface for lifecycle event handlers.
375
+ */
376
+ export interface LifeCycleInterface {
377
+ endCommand?: () => void;
378
+ endTasks?: (taskResults: TaskResult[]) => void;
379
+ /** Called when a cache miss occurs with diagnostic information */
380
+ printCacheMiss?: (task: Task, reasons: string) => void;
381
+ printTaskTerminalOutput?: (task: Task, status: TaskStatus, terminalOutput: string) => void;
382
+ scheduleTask?: (task: Task) => void;
383
+ startCommand?: () => void;
384
+ startTasks?: (tasks: Task[]) => void;
385
+ }
@@ -0,0 +1,39 @@
1
+ import type { Task, TaskResult } from "./types.d.ts";
2
+ /**
3
+ * Hashes a file's content using xxh3-128.
4
+ * Returns undefined if the file cannot be read.
5
+ */
6
+ declare const hashFile: (filePath: string) => Promise<string | undefined>;
7
+ /**
8
+ * Hashes one or more string values using xxh3-128.
9
+ */
10
+ declare const hashStrings: (...values: string[]) => string;
11
+ /**
12
+ * Sorts an object's keys for deterministic serialization.
13
+ */
14
+ declare const sortObjectKeys: (object: Record<string, unknown>) => Record<string, unknown>;
15
+ /**
16
+ * Recursively collects all file paths in a directory,
17
+ * skipping directories in the ignored set.
18
+ *
19
+ * Tracks visited real paths to prevent infinite loops from symlink cycles.
20
+ */
21
+ declare const collectFiles: (directory: string, ignoredDirectories: Set<string>, visitedRealPaths?: Set<string>) => Promise<string[]>;
22
+ /**
23
+ * Resolves the working directory for a task.
24
+ */
25
+ declare const resolveTaskCwd: (workspaceRoot: string, task: Task) => string;
26
+ /**
27
+ * Creates a failure TaskResult from an error.
28
+ */
29
+ declare const createFailureResult: (task: Task, error: unknown, startTime: number) => TaskResult;
30
+ declare const readPackageDeps: (packageJsonPath: string, options?: {
31
+ optional?: boolean;
32
+ peer?: boolean;
33
+ }) => Promise<Set<string> | undefined>;
34
+ /**
35
+ * Generates a unique ID for temporary files/directories.
36
+ * Not cryptographically secure — for cache entry naming only.
37
+ */
38
+ declare const uniqueId: () => string;
39
+ export { collectFiles, createFailureResult, hashFile, hashStrings, readPackageDeps, resolveTaskCwd, sortObjectKeys, uniqueId };
package/package.json CHANGED
@@ -1,10 +1,75 @@
1
1
  {
2
2
  "name": "@visulima/task-runner",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @visulima/task-runner",
3
+ "version": "1.0.0-alpha.2",
4
+ "description": "A task runner with caching support for monorepo workspaces",
5
5
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
10
- }
6
+ "visulima",
7
+ "task-runner",
8
+ "cache",
9
+ "monorepo",
10
+ "task",
11
+ "runner",
12
+ "workspace"
13
+ ],
14
+ "homepage": "https://visulima.com/packages/task-runner",
15
+ "bugs": {
16
+ "url": "https://github.com/visulima/visulima/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/visulima/visulima.git",
21
+ "directory": "packages/tooling/task-runner"
22
+ },
23
+ "funding": [
24
+ {
25
+ "type": "github",
26
+ "url": "https://github.com/sponsors/prisis"
27
+ },
28
+ {
29
+ "type": "consulting",
30
+ "url": "https://anolilab.com/support"
31
+ }
32
+ ],
33
+ "license": "MIT",
34
+ "author": {
35
+ "name": "Daniel Bannert",
36
+ "email": "d.bannert@anolilab.de"
37
+ },
38
+ "sideEffects": false,
39
+ "type": "module",
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "files": [
48
+ "dist/**",
49
+ "binding.js",
50
+ "README.md",
51
+ "CHANGELOG.md",
52
+ "LICENSE.md"
53
+ ],
54
+ "dependencies": {
55
+ "@visulima/humanizer": "3.0.0-alpha.7",
56
+ "@visulima/path": "3.0.0-alpha.6"
57
+ },
58
+ "optionalDependencies": {
59
+ "@visulima/task-runner-binding-darwin-arm64": "1.0.0-alpha.2",
60
+ "@visulima/task-runner-binding-linux-arm64-musl": "1.0.0-alpha.2",
61
+ "@visulima/task-runner-binding-linux-x64-gnu": "1.0.0-alpha.2",
62
+ "@visulima/task-runner-binding-linux-arm64-gnu": "1.0.0-alpha.2",
63
+ "@visulima/task-runner-binding-darwin-x64": "1.0.0-alpha.2",
64
+ "@visulima/task-runner-binding-linux-x64-musl": "1.0.0-alpha.2",
65
+ "@visulima/task-runner-binding-win32-x64-msvc": "1.0.0-alpha.2",
66
+ "@visulima/task-runner-binding-win32-arm64-msvc": "1.0.0-alpha.2"
67
+ },
68
+ "engines": {
69
+ "node": ">=20.19 <=25.x"
70
+ },
71
+ "publishConfig": {
72
+ "access": "public",
73
+ "provenance": true
74
+ }
75
+ }