@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.
- package/CHANGELOG.md +21 -0
- package/LICENSE.md +21 -0
- package/README.md +170 -36
- package/binding.js +204 -0
- package/dist/affected.d.ts +48 -0
- package/dist/cache.d.ts +103 -0
- package/dist/default-task-runner.d.ts +44 -0
- package/dist/file-access-tracker.d.ts +53 -0
- package/dist/fingerprint.d.ts +45 -0
- package/dist/framework-inference.d.ts +35 -0
- package/dist/graph-visualizer.d.ts +74 -0
- package/dist/incremental-hasher.d.ts +58 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +20 -0
- package/dist/life-cycle.d.ts +36 -0
- package/dist/lockfile-hasher.d.ts +73 -0
- package/dist/native-binding.d.ts +64 -0
- package/dist/packem_shared/Cache-IYpTYVUC.js +298 -0
- package/dist/packem_shared/CompositeLifeCycle-7AtYw1dv.js +112 -0
- package/dist/packem_shared/FileAccessTracker-CrtBAt5D.js +239 -0
- package/dist/packem_shared/FingerprintManager-D6Y0erg-.js +227 -0
- package/dist/packem_shared/IncrementalFileHasher-Ds3J6dgb.js +151 -0
- package/dist/packem_shared/RemoteCache-BDqrnDEi.js +179 -0
- package/dist/packem_shared/TaskOrchestrator-BvYs3ONw.js +342 -0
- package/dist/packem_shared/TaskScheduler-CJilHDta.js +111 -0
- package/dist/packem_shared/TrackedTaskExecutor-BGUKFE-7.js +164 -0
- package/dist/packem_shared/collectFiles-ClXHnHhg.js +22 -0
- package/dist/packem_shared/computeTaskHash-BoCnnvIJ.js +356 -0
- package/dist/packem_shared/createTaskGraph-CcsFaSrz.js +164 -0
- package/dist/packem_shared/defaultTaskRunner-CrW4v5Ye.js +79 -0
- package/dist/packem_shared/detectFrameworks-CeFzKE6J.js +101 -0
- package/dist/packem_shared/extractPackageName-CbVNW-dr.js +189 -0
- package/dist/packem_shared/filterAffectedTasks-I-18zPg6.js +135 -0
- package/dist/packem_shared/findCycle-DF4_BRdO.js +212 -0
- package/dist/packem_shared/generateRunSummary-qn-_jKwt.js +134 -0
- package/dist/packem_shared/isNativeAvailable-BWhnZ4ES.js +19 -0
- package/dist/packem_shared/projectGraphToDot-VdTjHcVp.js +202 -0
- package/dist/packem_shared/utils-zO0ZRgtf.js +390 -0
- package/dist/remote-cache.d.ts +55 -0
- package/dist/run-summary.d.ts +89 -0
- package/dist/task-graph-utils.d.ts +39 -0
- package/dist/task-graph.d.ts +22 -0
- package/dist/task-hasher.d.ts +67 -0
- package/dist/task-orchestrator.d.ts +38 -0
- package/dist/task-scheduler.d.ts +18 -0
- package/dist/tracked-executor.d.ts +46 -0
- package/dist/types.d.ts +385 -0
- package/dist/utils.d.ts +39 -0
- package/package.json +72 -7
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## @visulima/task-runner [1.0.0-alpha.2](https://github.com/visulima/visulima/compare/@visulima/task-runner@1.0.0-alpha.1...@visulima/task-runner@1.0.0-alpha.2) (2026-03-26)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **task-runner:** publish native binding packages with version lockstep via exec plugin ([12e342f](https://github.com/visulima/visulima/commit/12e342f1656ad0b595eaff627d3407c61c4ea7b6))
|
|
6
|
+
|
|
7
|
+
### Miscellaneous Chores
|
|
8
|
+
|
|
9
|
+
* **task-runner:** add project.json for all native binding packages ([eb48812](https://github.com/visulima/visulima/commit/eb48812cb87c0990b9822d9314ccc0081e41d11f))
|
|
10
|
+
|
|
11
|
+
## @visulima/task-runner 1.0.0-alpha.1 (2026-03-26)
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* Add @visulima/task-runner , vis and find-ai-runner ([#594](https://github.com/visulima/visulima/issues/594)) ([034b5db](https://github.com/visulima/visulima/commit/034b5db8aadcc02e23abe007208c5196859c7755))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Dependencies
|
|
19
|
+
|
|
20
|
+
* **@visulima/humanizer:** upgraded to 3.0.0-alpha.7
|
|
21
|
+
* **@visulima/path:** upgraded to 3.0.0-alpha.6
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 visulima
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,45 +1,179 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h3>Visulima task-runner</h3>
|
|
3
|
+
<p>
|
|
4
|
+
High-performance monorepo task runner with intelligent caching, dependency-aware scheduling, and auto-fingerprinting.
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
<br />
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
<div align="center">
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
[![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url]
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
</div>
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@visulima/task-runner`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
16
|
+
---
|
|
38
17
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
<div align="center">
|
|
19
|
+
<p>
|
|
20
|
+
<sup>
|
|
21
|
+
Daniel Bannert's open source work is supported by the community on <a href="https://github.com/sponsors/prisis">GitHub Sponsors</a>
|
|
22
|
+
</sup>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
42
25
|
|
|
43
26
|
---
|
|
44
27
|
|
|
45
|
-
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- **Two caching modes**: Nx-style explicit inputs or Vite Task-style auto-fingerprinting
|
|
31
|
+
- **Smart lockfile hashing**: Only hashes resolved versions relevant to each package (like Turborepo)
|
|
32
|
+
- **Framework env inference**: Auto-detects Next.js, Vite, CRA, Gatsby, Nuxt, and more
|
|
33
|
+
- **Remote caching**: Turborepo-compatible HTTP cache protocol
|
|
34
|
+
- **Native Rust addon**: Optional xxHash-based parallel file hashing via napi-rs
|
|
35
|
+
- **Dependency-aware scheduling**: Topological task ordering with priority-based batching
|
|
36
|
+
- **Incremental file hashing**: mtime-based change detection for near-instant cache checks
|
|
37
|
+
- **Affected detection**: Git diff-based filtering to only run tasks for changed packages
|
|
38
|
+
- **Graph visualization**: DOT, JSON, HTML, and ASCII output formats
|
|
39
|
+
- **Run summaries**: Detailed JSON reports for debugging cache behavior
|
|
40
|
+
- **Cache diagnostics**: Explains exactly why a cache miss occurred
|
|
41
|
+
- **Output archiving**: Caches and restores build outputs (dist/ directories)
|
|
42
|
+
- **LRU cache eviction**: Configurable max size and age limits
|
|
43
|
+
- **Lifecycle hooks**: 7 hook points for custom behavior
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @visulima/task-runner
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
yarn add @visulima/task-runner
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm add @visulima/task-runner
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { defaultTaskRunner } from "@visulima/task-runner";
|
|
63
|
+
|
|
64
|
+
const results = await defaultTaskRunner(tasks, {
|
|
65
|
+
// Nx-style: explicit inputs
|
|
66
|
+
namedInputs: {
|
|
67
|
+
production: ["{projectRoot}/src/**/*"],
|
|
68
|
+
},
|
|
69
|
+
globalInputs: ["pnpm-lock.yaml", "tsconfig.base.json"],
|
|
70
|
+
globalEnv: ["NODE_ENV"],
|
|
71
|
+
|
|
72
|
+
// Or: auto-fingerprinting (Vite Task-style)
|
|
73
|
+
// autoFingerprint: true,
|
|
74
|
+
|
|
75
|
+
// Smart lockfile hashing (only bust cache for affected packages)
|
|
76
|
+
smartLockfileHashing: true,
|
|
77
|
+
|
|
78
|
+
// Auto-detect framework env vars (NEXT_PUBLIC_*, VITE_*, etc.)
|
|
79
|
+
frameworkInference: true,
|
|
80
|
+
|
|
81
|
+
// Remote cache (Turborepo-compatible)
|
|
82
|
+
remoteCache: {
|
|
83
|
+
url: "https://cache.example.com",
|
|
84
|
+
token: process.env.CACHE_TOKEN,
|
|
85
|
+
teamId: "my-team",
|
|
86
|
+
},
|
|
87
|
+
}, context);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Caching Modes
|
|
91
|
+
|
|
92
|
+
### Nx-style (explicit inputs)
|
|
93
|
+
|
|
94
|
+
Declare which files, env vars, and runtime values should be included in the cache hash:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const results = await defaultTaskRunner(tasks, {
|
|
98
|
+
namedInputs: {
|
|
99
|
+
production: [
|
|
100
|
+
"{projectRoot}/src/**/*",
|
|
101
|
+
{ env: "NODE_ENV" },
|
|
102
|
+
{ runtime: "node --version" },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
targetDefaults: {
|
|
106
|
+
build: { inputs: ["production"] },
|
|
107
|
+
test: { inputs: ["production", "{projectRoot}/**/*.test.ts"] },
|
|
108
|
+
},
|
|
109
|
+
}, context);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Auto-fingerprint (Vite Task-style)
|
|
113
|
+
|
|
114
|
+
Automatically tracks which files a task accesses during execution:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const results = await defaultTaskRunner(tasks, {
|
|
118
|
+
autoFingerprint: true,
|
|
119
|
+
fingerprintEnvPatterns: ["VITE_*", "NODE_ENV"],
|
|
120
|
+
cacheDiagnostics: true, // Shows why cache misses occur
|
|
121
|
+
}, context);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## API
|
|
125
|
+
|
|
126
|
+
### `defaultTaskRunner(tasks, options, context)`
|
|
127
|
+
|
|
128
|
+
The main entry point. Runs tasks with caching, scheduling, and lifecycle support.
|
|
129
|
+
|
|
130
|
+
### Key Options
|
|
131
|
+
|
|
132
|
+
| Option | Type | Description |
|
|
133
|
+
|--------|------|-------------|
|
|
134
|
+
| `parallel` | `number \| boolean` | Max parallel tasks (default: 3) |
|
|
135
|
+
| `smartLockfileHashing` | `boolean` | Hash only relevant lockfile entries per package |
|
|
136
|
+
| `frameworkInference` | `boolean` | Auto-detect framework env var prefixes |
|
|
137
|
+
| `autoFingerprint` | `boolean` | Enable Vite Task-style auto-fingerprinting |
|
|
138
|
+
| `globalInputs` | `string[]` | Files that invalidate all caches when changed |
|
|
139
|
+
| `globalEnv` | `string[]` | Env vars that invalidate all caches |
|
|
140
|
+
| `remoteCache` | `object` | Remote cache server configuration |
|
|
141
|
+
| `dryRun` | `boolean` | Compute hashes without executing |
|
|
142
|
+
| `summarize` | `boolean` | Generate JSON run summary |
|
|
143
|
+
| `cacheDiagnostics` | `boolean` | Log cache miss reasons |
|
|
144
|
+
| `maxCacheSize` | `string` | Max cache size (e.g., "1GB") |
|
|
145
|
+
| `maxCacheAge` | `number` | Max cache entry age in ms |
|
|
146
|
+
|
|
147
|
+
### Exports
|
|
148
|
+
|
|
149
|
+
The package exports many building blocks for custom task runners:
|
|
150
|
+
|
|
151
|
+
- **Task Graph**: `createTaskGraph`, `findCycle`, `walkTaskGraph`, `makeAcyclic`
|
|
152
|
+
- **Hashing**: `InProcessTaskHasher`, `IncrementalFileHasher`, `computeTaskHash`
|
|
153
|
+
- **Caching**: `Cache`, `RemoteCache`, `FingerprintManager`
|
|
154
|
+
- **Scheduling**: `TaskScheduler`, `TaskOrchestrator`
|
|
155
|
+
- **Lockfile**: `LockfileHasher`, `parseNpmLockfile`, `parsePnpmLockfile`, `parseYarnLockfile`
|
|
156
|
+
- **Framework**: `detectFrameworks`, `inferFrameworkEnvPatterns`, `getFrameworkEnvVariables`
|
|
157
|
+
- **Affected**: `getAffectedProjects`, `getChangedFiles`, `filterAffectedTasks`
|
|
158
|
+
- **Visualization**: `toGraphvizDot`, `toGraphJson`, `toGraphHtml`, `toGraphAscii`
|
|
159
|
+
- **Summary**: `generateRunSummary`, `writeRunSummary`
|
|
160
|
+
- **Lifecycle**: `ConsoleLifeCycle`, `CompositeLifeCycle`
|
|
161
|
+
|
|
162
|
+
## Supported Node.js Versions
|
|
163
|
+
|
|
164
|
+
Libraries in this ecosystem make the best effort to track [Node.js' release schedule](https://github.com/nodejs/release#release-schedule).
|
|
165
|
+
|
|
166
|
+
## Contributing
|
|
167
|
+
|
|
168
|
+
If you would like to help take a look at the [list of issues](https://github.com/visulima/visulima/issues) and check our [Contributing](https://github.com/visulima/visulima/blob/main/.github/CONTRIBUTING.md) guidelines.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
The visulima task-runner is open-sourced software licensed under the [MIT][license-url]
|
|
173
|
+
|
|
174
|
+
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
175
|
+
[typescript-url]: https://www.typescriptlang.org/
|
|
176
|
+
[license-image]: https://img.shields.io/npm/l/@visulima/task-runner?color=blueviolet&style=for-the-badge
|
|
177
|
+
[license-url]: LICENSE.md
|
|
178
|
+
[npm-image]: https://img.shields.io/npm/v/@visulima/task-runner/latest.svg?style=for-the-badge&logo=npm
|
|
179
|
+
[npm-url]: https://www.npmjs.com/package/@visulima/task-runner/v/latest
|
package/binding.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/* auto-generated binding loader for @visulima/task-runner native addon */
|
|
3
|
+
|
|
4
|
+
const { existsSync, readFileSync } = require("node:fs");
|
|
5
|
+
const { join } = require("node:path");
|
|
6
|
+
|
|
7
|
+
const { platform, arch } = process;
|
|
8
|
+
|
|
9
|
+
let nativeBinding = null;
|
|
10
|
+
let localFileExisted = false;
|
|
11
|
+
let loadError = null;
|
|
12
|
+
|
|
13
|
+
function isMusl() {
|
|
14
|
+
// For Node 12+, check report.header for musl
|
|
15
|
+
if (
|
|
16
|
+
typeof process.report !== "undefined" &&
|
|
17
|
+
typeof process.report.getReport === "function"
|
|
18
|
+
) {
|
|
19
|
+
const { glibcVersionRuntime } = process.report.getReport().header;
|
|
20
|
+
if (glibcVersionRuntime) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const lddOutput = readFileSync("/usr/bin/ldd", "utf8");
|
|
28
|
+
return lddOutput.includes("musl");
|
|
29
|
+
} catch {
|
|
30
|
+
try {
|
|
31
|
+
return readFileSync("/proc/self/map_files/../maps", "utf8").includes(
|
|
32
|
+
"musl"
|
|
33
|
+
);
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
switch (platform) {
|
|
41
|
+
case "darwin":
|
|
42
|
+
switch (arch) {
|
|
43
|
+
case "x64":
|
|
44
|
+
localFileExisted = existsSync(
|
|
45
|
+
join(__dirname, "task-runner-native.darwin-x64.node")
|
|
46
|
+
);
|
|
47
|
+
try {
|
|
48
|
+
if (localFileExisted) {
|
|
49
|
+
nativeBinding = require("./task-runner-native.darwin-x64.node");
|
|
50
|
+
} else {
|
|
51
|
+
nativeBinding = require("@visulima/task-runner-binding-darwin-x64");
|
|
52
|
+
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
loadError = e;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case "arm64":
|
|
58
|
+
localFileExisted = existsSync(
|
|
59
|
+
join(__dirname, "task-runner-native.darwin-arm64.node")
|
|
60
|
+
);
|
|
61
|
+
try {
|
|
62
|
+
if (localFileExisted) {
|
|
63
|
+
nativeBinding = require("./task-runner-native.darwin-arm64.node");
|
|
64
|
+
} else {
|
|
65
|
+
nativeBinding = require("@visulima/task-runner-binding-darwin-arm64");
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
loadError = e;
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unsupported architecture on macOS: ${arch}`);
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case "linux":
|
|
76
|
+
switch (arch) {
|
|
77
|
+
case "x64":
|
|
78
|
+
if (isMusl()) {
|
|
79
|
+
localFileExisted = existsSync(
|
|
80
|
+
join(
|
|
81
|
+
__dirname,
|
|
82
|
+
"task-runner-native.linux-x64-musl.node"
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
try {
|
|
86
|
+
if (localFileExisted) {
|
|
87
|
+
nativeBinding = require("./task-runner-native.linux-x64-musl.node");
|
|
88
|
+
} else {
|
|
89
|
+
nativeBinding = require("@visulima/task-runner-binding-linux-x64-musl");
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
loadError = e;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
localFileExisted = existsSync(
|
|
96
|
+
join(
|
|
97
|
+
__dirname,
|
|
98
|
+
"task-runner-native.linux-x64-gnu.node"
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
try {
|
|
102
|
+
if (localFileExisted) {
|
|
103
|
+
nativeBinding = require("./task-runner-native.linux-x64-gnu.node");
|
|
104
|
+
} else {
|
|
105
|
+
nativeBinding = require("@visulima/task-runner-binding-linux-x64-gnu");
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
loadError = e;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case "arm64":
|
|
113
|
+
if (isMusl()) {
|
|
114
|
+
localFileExisted = existsSync(
|
|
115
|
+
join(
|
|
116
|
+
__dirname,
|
|
117
|
+
"task-runner-native.linux-arm64-musl.node"
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
try {
|
|
121
|
+
if (localFileExisted) {
|
|
122
|
+
nativeBinding = require("./task-runner-native.linux-arm64-musl.node");
|
|
123
|
+
} else {
|
|
124
|
+
nativeBinding = require("@visulima/task-runner-binding-linux-arm64-musl");
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
loadError = e;
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
localFileExisted = existsSync(
|
|
131
|
+
join(
|
|
132
|
+
__dirname,
|
|
133
|
+
"task-runner-native.linux-arm64-gnu.node"
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
try {
|
|
137
|
+
if (localFileExisted) {
|
|
138
|
+
nativeBinding = require("./task-runner-native.linux-arm64-gnu.node");
|
|
139
|
+
} else {
|
|
140
|
+
nativeBinding = require("@visulima/task-runner-binding-linux-arm64-gnu");
|
|
141
|
+
}
|
|
142
|
+
} catch (e) {
|
|
143
|
+
loadError = e;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unsupported architecture on Linux: ${arch}`);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
case "win32":
|
|
152
|
+
switch (arch) {
|
|
153
|
+
case "x64":
|
|
154
|
+
localFileExisted = existsSync(
|
|
155
|
+
join(
|
|
156
|
+
__dirname,
|
|
157
|
+
"task-runner-native.win32-x64-msvc.node"
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
try {
|
|
161
|
+
if (localFileExisted) {
|
|
162
|
+
nativeBinding = require("./task-runner-native.win32-x64-msvc.node");
|
|
163
|
+
} else {
|
|
164
|
+
nativeBinding = require("@visulima/task-runner-binding-win32-x64-msvc");
|
|
165
|
+
}
|
|
166
|
+
} catch (e) {
|
|
167
|
+
loadError = e;
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
case "arm64":
|
|
171
|
+
localFileExisted = existsSync(
|
|
172
|
+
join(
|
|
173
|
+
__dirname,
|
|
174
|
+
"task-runner-native.win32-arm64-msvc.node"
|
|
175
|
+
)
|
|
176
|
+
);
|
|
177
|
+
try {
|
|
178
|
+
if (localFileExisted) {
|
|
179
|
+
nativeBinding = require("./task-runner-native.win32-arm64-msvc.node");
|
|
180
|
+
} else {
|
|
181
|
+
nativeBinding = require("@visulima/task-runner-binding-win32-arm64-msvc");
|
|
182
|
+
}
|
|
183
|
+
} catch (e) {
|
|
184
|
+
loadError = e;
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
throw new Error(`Unsupported architecture on Windows: ${arch}`);
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
default:
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Unsupported OS: ${platform}, architecture: ${arch}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!nativeBinding) {
|
|
198
|
+
if (loadError) {
|
|
199
|
+
throw loadError;
|
|
200
|
+
}
|
|
201
|
+
throw new Error("Failed to load native binding");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = nativeBinding;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ProjectConfiguration, ProjectGraph } from "./types.d.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Options for determining affected projects.
|
|
4
|
+
*/
|
|
5
|
+
interface AffectedOptions {
|
|
6
|
+
/** The base ref to compare against (default: "main") */
|
|
7
|
+
base?: string;
|
|
8
|
+
/** The head ref to compare (default: "HEAD") */
|
|
9
|
+
head?: string;
|
|
10
|
+
/** Project graph for dependency resolution */
|
|
11
|
+
projectGraph: ProjectGraph;
|
|
12
|
+
/** All project configurations keyed by name */
|
|
13
|
+
projects: Record<string, ProjectConfiguration>;
|
|
14
|
+
/** The workspace root directory */
|
|
15
|
+
workspaceRoot: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Result of affected detection.
|
|
19
|
+
*/
|
|
20
|
+
interface AffectedResult {
|
|
21
|
+
/** Projects affected by changes (including transitive dependents) */
|
|
22
|
+
affectedProjects: string[];
|
|
23
|
+
/** Files that changed between base and head */
|
|
24
|
+
changedFiles: string[];
|
|
25
|
+
/** Projects that were directly changed */
|
|
26
|
+
changedProjects: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Gets the list of files changed between two git refs.
|
|
30
|
+
* Uses execFile with argument arrays to prevent command injection.
|
|
31
|
+
*/
|
|
32
|
+
declare const getChangedFiles: (workspaceRoot: string, base: string, head: string) => Promise<string[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Determines which projects are affected by changes between two git refs.
|
|
35
|
+
*
|
|
36
|
+
* Uses `git diff` to find changed files, maps them to projects based on
|
|
37
|
+
* project roots, then walks the project dependency graph to find all
|
|
38
|
+
* transitively affected projects.
|
|
39
|
+
*
|
|
40
|
+
* This is the same strategy used by `nx affected` and `turbo run --filter=[base...]`.
|
|
41
|
+
*/
|
|
42
|
+
declare const getAffectedProjects: (options: AffectedOptions) => Promise<AffectedResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Filters tasks to only include those that are affected by changes.
|
|
45
|
+
*/
|
|
46
|
+
declare const filterAffectedTasks: (taskIds: string[], affectedProjects: Set<string>) => string[];
|
|
47
|
+
export type { AffectedOptions, AffectedResult };
|
|
48
|
+
export { filterAffectedTasks, getAffectedProjects, getChangedFiles };
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { TaskFingerprint } from "./fingerprint.d.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Represents a cached task result.
|
|
4
|
+
*/
|
|
5
|
+
interface CachedResult {
|
|
6
|
+
/** The exit code of the original task execution */
|
|
7
|
+
code: number;
|
|
8
|
+
/** The auto-fingerprint data, if auto-fingerprinting was used */
|
|
9
|
+
fingerprint?: TaskFingerprint;
|
|
10
|
+
/** The hash that was used as the cache key */
|
|
11
|
+
hash: string;
|
|
12
|
+
/** The terminal output of the original task execution */
|
|
13
|
+
terminalOutput: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Options for creating a Cache instance.
|
|
17
|
+
*/
|
|
18
|
+
interface CacheOptions {
|
|
19
|
+
/** The cache directory (defaults to `{workspaceRoot}/.task-runner-cache`) */
|
|
20
|
+
cacheDirectory?: string;
|
|
21
|
+
/** Maximum age of cache entries in milliseconds (default: 7 days) */
|
|
22
|
+
maxCacheAge?: number;
|
|
23
|
+
/** Maximum cache size (e.g., "500MB", "1GB") */
|
|
24
|
+
maxCacheSize?: string;
|
|
25
|
+
/** The workspace root directory */
|
|
26
|
+
workspaceRoot: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parses a human-readable cache size string into bytes.
|
|
30
|
+
* Delegates to @visulima/humanizer's parseBytes with base-2 (1024) multipliers.
|
|
31
|
+
*/
|
|
32
|
+
declare const parseCacheSize: (sizeString: string) => number;
|
|
33
|
+
/**
|
|
34
|
+
* Formats a byte count into a human-readable string.
|
|
35
|
+
* Delegates to @visulima/humanizer's formatBytes with base-2 (1024) multipliers.
|
|
36
|
+
*/
|
|
37
|
+
declare const formatCacheSize: (bytes: number) => string;
|
|
38
|
+
/**
|
|
39
|
+
* Local file-based cache for task results.
|
|
40
|
+
*
|
|
41
|
+
* Cache structure:
|
|
42
|
+
* ```
|
|
43
|
+
* .task-runner-cache/
|
|
44
|
+
* <hash>/
|
|
45
|
+
* outputs/ (Archived build outputs)
|
|
46
|
+
* code (Exit code)
|
|
47
|
+
* terminalOutput (Captured terminal output)
|
|
48
|
+
* fingerprint.json (Auto-fingerprint data, optional)
|
|
49
|
+
* .commit (Marker indicating complete cache entry)
|
|
50
|
+
* .task-index.json (Task ID -> hash mapping for auto-fingerprint)
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Atomicity: Cache entries are written to a temporary directory first,
|
|
54
|
+
* then renamed into place. The `.commit` marker ensures readers only
|
|
55
|
+
* see complete entries.
|
|
56
|
+
*/
|
|
57
|
+
declare class Cache {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(options: CacheOptions);
|
|
60
|
+
/**
|
|
61
|
+
* Gets the cache directory path.
|
|
62
|
+
*/
|
|
63
|
+
get cacheDirectory(): string;
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves a cached result for the given task hash.
|
|
66
|
+
* Returns undefined if no valid cache entry exists.
|
|
67
|
+
*/
|
|
68
|
+
get(hash: string): Promise<CachedResult | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Stores a task result in the cache.
|
|
71
|
+
*
|
|
72
|
+
* Uses atomic write: builds the entry in a temporary directory,
|
|
73
|
+
* then renames into place to avoid partial reads by concurrent processes.
|
|
74
|
+
*/
|
|
75
|
+
put(hash: string, terminalOutput: string, outputs: string[], code: number, fingerprint?: TaskFingerprint): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Restores cached outputs to their original locations.
|
|
78
|
+
*/
|
|
79
|
+
restoreOutputs(hash: string, outputs: string[]): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* Retrieves the most recent cached result for a task by its ID.
|
|
82
|
+
* Used in auto-fingerprint mode where the hash is derived from
|
|
83
|
+
* tracked file accesses rather than computed upfront.
|
|
84
|
+
*/
|
|
85
|
+
getByTaskId(taskId: string): Promise<CachedResult | undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* Stores the mapping from task ID to cache hash.
|
|
88
|
+
* Uses a write queue to serialize concurrent writes and prevent lost updates.
|
|
89
|
+
* Each write is atomic (temp file + rename).
|
|
90
|
+
*/
|
|
91
|
+
setTaskIndex(taskId: string, hash: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Removes old cache entries that exceed the maximum age,
|
|
94
|
+
* and enforces the maximum cache size by evicting oldest entries.
|
|
95
|
+
*/
|
|
96
|
+
removeOldEntries(): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Clears the entire cache.
|
|
99
|
+
*/
|
|
100
|
+
clear(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
export type { CachedResult, CacheOptions };
|
|
103
|
+
export { Cache, formatCacheSize, parseCacheSize };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Task, TaskResults, TaskRunnerContext, TaskRunnerOptions } from "./types.d.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The default task runner implementation.
|
|
4
|
+
*
|
|
5
|
+
* Runs tasks with caching, scheduling, and lifecycle support.
|
|
6
|
+
* Supports two caching modes:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Nx-style** (default): Explicit input declarations with upfront hash computation.
|
|
9
|
+
* 2. **Auto-fingerprint** (Vite Task-style): Set `autoFingerprint: true` to automatically
|
|
10
|
+
* track file accesses and use them for cache invalidation.
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { defaultTaskRunner } from "@visulima/task-runner";
|
|
14
|
+
*
|
|
15
|
+
* // Nx-style (explicit inputs)
|
|
16
|
+
* const results = await defaultTaskRunner(tasks, options, context);
|
|
17
|
+
*
|
|
18
|
+
* // Vite Task-style (auto-fingerprinting)
|
|
19
|
+
* const results = await defaultTaskRunner(tasks, {
|
|
20
|
+
* ...options,
|
|
21
|
+
* autoFingerprint: true,
|
|
22
|
+
* fingerprintEnvPatterns: ["VITE_*", "NODE_ENV"],
|
|
23
|
+
* cacheDiagnostics: true,
|
|
24
|
+
* }, context);
|
|
25
|
+
*
|
|
26
|
+
* // With remote cache
|
|
27
|
+
* const results = await defaultTaskRunner(tasks, {
|
|
28
|
+
* ...options,
|
|
29
|
+
* remoteCache: {
|
|
30
|
+
* url: "https://cache.example.com",
|
|
31
|
+
* token: process.env.CACHE_TOKEN,
|
|
32
|
+
* teamId: "my-team",
|
|
33
|
+
* },
|
|
34
|
+
* }, context);
|
|
35
|
+
*
|
|
36
|
+
* // Dry-run (inspect hashes without executing)
|
|
37
|
+
* const results = await defaultTaskRunner(tasks, {
|
|
38
|
+
* ...options,
|
|
39
|
+
* dryRun: true,
|
|
40
|
+
* }, context);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
declare const defaultTaskRunner: (_tasks: Task[], options: TaskRunnerOptions, context: TaskRunnerContext) => Promise<TaskResults>;
|
|
44
|
+
export { defaultTaskRunner };
|