@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
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
var __freeze = Object.freeze;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) }));
|
|
4
|
+
const getNodeColor = (taskId, focused, statuses) => {
|
|
5
|
+
if (focused && !focused.has(taskId)) {
|
|
6
|
+
return "#eeeeee";
|
|
7
|
+
}
|
|
8
|
+
const status = statuses?.get(taskId);
|
|
9
|
+
switch (status) {
|
|
10
|
+
case "failure": {
|
|
11
|
+
return "#FFB6C1";
|
|
12
|
+
}
|
|
13
|
+
case "local-cache":
|
|
14
|
+
case "local-cache-kept-existing":
|
|
15
|
+
case "remote-cache": {
|
|
16
|
+
return "#87CEEB";
|
|
17
|
+
}
|
|
18
|
+
case "running": {
|
|
19
|
+
return "#FFD700";
|
|
20
|
+
}
|
|
21
|
+
case "skipped": {
|
|
22
|
+
return "#D3D3D3";
|
|
23
|
+
}
|
|
24
|
+
case "success": {
|
|
25
|
+
return "#90EE90";
|
|
26
|
+
}
|
|
27
|
+
default: {
|
|
28
|
+
return "#FFFFFF";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const getStatusIcon = (status) => {
|
|
33
|
+
switch (status) {
|
|
34
|
+
case "failure": {
|
|
35
|
+
return "[FAIL] ";
|
|
36
|
+
}
|
|
37
|
+
case "local-cache":
|
|
38
|
+
case "local-cache-kept-existing":
|
|
39
|
+
case "remote-cache": {
|
|
40
|
+
return "[cache] ";
|
|
41
|
+
}
|
|
42
|
+
case "running": {
|
|
43
|
+
return "[...] ";
|
|
44
|
+
}
|
|
45
|
+
case "skipped": {
|
|
46
|
+
return "[skip] ";
|
|
47
|
+
}
|
|
48
|
+
case "success": {
|
|
49
|
+
return "[ok] ";
|
|
50
|
+
}
|
|
51
|
+
default: {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const formatTaskLine = (taskId, statuses) => {
|
|
57
|
+
const icon = getStatusIcon(statuses?.get(taskId));
|
|
58
|
+
return `${icon}${taskId}`;
|
|
59
|
+
};
|
|
60
|
+
const printTree = (taskId, prefix, isLast, taskGraph, printed, lines, statuses) => {
|
|
61
|
+
let connector;
|
|
62
|
+
if (prefix === "") {
|
|
63
|
+
connector = "";
|
|
64
|
+
} else if (isLast) {
|
|
65
|
+
connector = "└── ";
|
|
66
|
+
} else {
|
|
67
|
+
connector = "├── ";
|
|
68
|
+
}
|
|
69
|
+
const isDuplicate = printed.has(taskId);
|
|
70
|
+
const suffix = isDuplicate ? " (*)" : "";
|
|
71
|
+
const statusIcon = getStatusIcon(statuses?.get(taskId));
|
|
72
|
+
lines.push(`${prefix}${connector}${statusIcon}${taskId}${suffix}`);
|
|
73
|
+
if (isDuplicate) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
printed.add(taskId);
|
|
77
|
+
const deps = taskGraph.dependencies[taskId] ?? [];
|
|
78
|
+
let childPrefix;
|
|
79
|
+
if (prefix === "") {
|
|
80
|
+
childPrefix = "";
|
|
81
|
+
} else if (isLast) {
|
|
82
|
+
childPrefix = `${prefix} `;
|
|
83
|
+
} else {
|
|
84
|
+
childPrefix = `${prefix}│ `;
|
|
85
|
+
}
|
|
86
|
+
for (let i = 0; i < deps.length; i += 1) {
|
|
87
|
+
const isChildLast = i === deps.length - 1;
|
|
88
|
+
const dependency = deps[i];
|
|
89
|
+
if (dependency) {
|
|
90
|
+
printTree(dependency, childPrefix, isChildLast, taskGraph, printed, lines, statuses);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const toGraphvizDot = (taskGraph, options = {}) => {
|
|
95
|
+
const { focusedTasks, groupByProject = true, taskStatuses } = options;
|
|
96
|
+
const focused = focusedTasks ? new Set(focusedTasks) : void 0;
|
|
97
|
+
const lines = ["digraph TaskGraph {", " rankdir=LR;", ' node [shape=box, style=filled, fontname="monospace"];'];
|
|
98
|
+
if (groupByProject) {
|
|
99
|
+
const projectTasks = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const task of Object.values(taskGraph.tasks)) {
|
|
101
|
+
const { project } = task.target;
|
|
102
|
+
const list = projectTasks.get(project) ?? [];
|
|
103
|
+
list.push(task);
|
|
104
|
+
projectTasks.set(project, list);
|
|
105
|
+
}
|
|
106
|
+
for (const [project, tasks] of projectTasks) {
|
|
107
|
+
lines.push(` subgraph "cluster_${project}" {`, ` label="${project}";`, " style=dashed;", ' color="#888888";');
|
|
108
|
+
for (const task of tasks) {
|
|
109
|
+
const color = getNodeColor(task.id, focused, taskStatuses);
|
|
110
|
+
lines.push(` "${task.id}" [label="${task.target.target}", fillcolor="${color}"];`);
|
|
111
|
+
}
|
|
112
|
+
lines.push(" }");
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
for (const task of Object.values(taskGraph.tasks)) {
|
|
116
|
+
const color = getNodeColor(task.id, focused, taskStatuses);
|
|
117
|
+
lines.push(` "${task.id}" [fillcolor="${color}"];`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const [taskId, deps] of Object.entries(taskGraph.dependencies)) {
|
|
121
|
+
for (const dep of deps) {
|
|
122
|
+
const edgeColor = focused && !focused.has(taskId) ? "#cccccc" : "#333333";
|
|
123
|
+
lines.push(` "${taskId}" -> "${dep}" [color="${edgeColor}"];`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
lines.push("}");
|
|
127
|
+
return lines.join("\n");
|
|
128
|
+
};
|
|
129
|
+
const toGraphJson = (taskGraph, taskStatuses) => {
|
|
130
|
+
const nodes = Object.values(taskGraph.tasks).map((task) => {
|
|
131
|
+
return {
|
|
132
|
+
configuration: task.target.configuration,
|
|
133
|
+
id: task.id,
|
|
134
|
+
project: task.target.project,
|
|
135
|
+
status: taskStatuses?.get(task.id),
|
|
136
|
+
target: task.target.target
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
const edges = [];
|
|
140
|
+
for (const [source, deps] of Object.entries(taskGraph.dependencies)) {
|
|
141
|
+
for (const target of deps) {
|
|
142
|
+
edges.push({ source, target });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { edges, nodes, roots: taskGraph.roots };
|
|
146
|
+
};
|
|
147
|
+
var _a;
|
|
148
|
+
const toGraphHtml = (taskGraph, options = {}) => {
|
|
149
|
+
const graphData = toGraphJson(taskGraph, options.taskStatuses);
|
|
150
|
+
if (options.focusedTasks && options.focusedTasks.length > 0) {
|
|
151
|
+
const focused = new Set(options.focusedTasks);
|
|
152
|
+
graphData.nodes = graphData.nodes.filter((n) => focused.has(n.id));
|
|
153
|
+
graphData.edges = graphData.edges.filter((e) => focused.has(e.source) && focused.has(e.target));
|
|
154
|
+
graphData.roots = graphData.roots.filter((r) => focused.has(r));
|
|
155
|
+
}
|
|
156
|
+
return String.raw(_a || (_a = __template(['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Task Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #1a1a2e; color: #eee; }\n svg { width: 100vw; height: 100vh; }\n .node rect { rx: 6; ry: 6; stroke: #555; stroke-width: 1.5; cursor: pointer; }\n .node text { font-size: 11px; fill: #1a1a2e; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke: #fff; stroke-width: 2; }\n .edge { stroke: #444; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }\n .label { font-size: 10px; fill: #888; }\n #info { position: fixed; top: 12px; right: 12px; background: #16213e; padding: 12px 16px; border-radius: 8px; font-size: 13px; }\n #info b { color: #e94560; }\n</style>\n</head>\n<body>\n<div id="info">\n <b>', "</b> tasks · <b>", "</b> dependencies · <b>", '</b> roots\n</div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#666"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst W = window.innerWidth, H = window.innerHeight;\nconst statusColors = {\n success: '#2ecc71', 'local-cache': '#3498db', 'remote-cache': '#9b59b6',\n failure: '#e74c3c', running: '#f39c12', skipped: '#95a5a6', pending: '#ecf0f1'\n};\nconst projectColors = {};\nconst palette = ['#e94560','#0f3460','#533483','#16c79a','#f39c12','#2ecc71','#3498db','#e67e22','#9b59b6','#1abc9c'];\nlet ci = 0;\ndata.nodes.forEach(n => {\n if (!projectColors[n.project]) projectColors[n.project] = palette[ci++ % palette.length];\n});\n\n// Simple force-directed layout\nconst nodes = data.nodes.map((n, i) => ({\n ...n, x: W/2 + (Math.random()-0.5)*400, y: H/2 + (Math.random()-0.5)*300, vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.id, n]));\nconst edges = data.edges.map(e => ({ source: nodeMap.get(e.source), target: nodeMap.get(e.target) }));\n\nfunction simulate() {\n for (let iter = 0; iter < 300; iter++) {\n // Repulsion\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = 8000 / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n // Attraction (edges)\n edges.forEach(e => {\n if (!e.source || !e.target) return;\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 150) * 0.01;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n // Gravity\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001;\n n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.8; n.vy *= 0.8;\n n.x = Math.max(60, Math.min(W-60, n.x));\n n.y = Math.max(30, Math.min(H-30, n.y));\n });\n }\n}\nsimulate();\n\n// Render\nedges.forEach(e => {\n if (!e.source || !e.target) return;\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1', e.source.x); line.setAttribute('y1', e.source.y);\n line.setAttribute('x2', e.target.x); line.setAttribute('y2', e.target.y);\n line.setAttribute('class','edge');\n svg.appendChild(line);\n});\nnodes.forEach(n => {\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x-50)+','+(n.y-14)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width','100'); rect.setAttribute('height','28');\n rect.setAttribute('fill', n.status ? (statusColors[n.status]||'#ecf0f1') : projectColors[n.project]);\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x','50'); text.setAttribute('y','18'); text.setAttribute('text-anchor','middle');\n text.textContent = n.id.length > 14 ? n.target : n.id;\n g.appendChild(text);\n g.addEventListener('click', () => {\n const deps = data.edges.filter(e => e.source === n.id).map(e => e.target);\n const rdeps = data.edges.filter(e => e.target === n.id).map(e => e.source);\n alert(n.id + '\n\nDepends on: ' + (deps.join(', ')||'none') + '\nRequired by: ' + (rdeps.join(', ')||'none'));\n });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"], ['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Task Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #1a1a2e; color: #eee; }\n svg { width: 100vw; height: 100vh; }\n .node rect { rx: 6; ry: 6; stroke: #555; stroke-width: 1.5; cursor: pointer; }\n .node text { font-size: 11px; fill: #1a1a2e; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke: #fff; stroke-width: 2; }\n .edge { stroke: #444; stroke-width: 1.5; fill: none; marker-end: url(#arrow); }\n .label { font-size: 10px; fill: #888; }\n #info { position: fixed; top: 12px; right: 12px; background: #16213e; padding: 12px 16px; border-radius: 8px; font-size: 13px; }\n #info b { color: #e94560; }\n</style>\n</head>\n<body>\n<div id="info">\n <b>', "</b> tasks · <b>", "</b> dependencies · <b>", '</b> roots\n</div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#666"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst W = window.innerWidth, H = window.innerHeight;\nconst statusColors = {\n success: '#2ecc71', 'local-cache': '#3498db', 'remote-cache': '#9b59b6',\n failure: '#e74c3c', running: '#f39c12', skipped: '#95a5a6', pending: '#ecf0f1'\n};\nconst projectColors = {};\nconst palette = ['#e94560','#0f3460','#533483','#16c79a','#f39c12','#2ecc71','#3498db','#e67e22','#9b59b6','#1abc9c'];\nlet ci = 0;\ndata.nodes.forEach(n => {\n if (!projectColors[n.project]) projectColors[n.project] = palette[ci++ % palette.length];\n});\n\n// Simple force-directed layout\nconst nodes = data.nodes.map((n, i) => ({\n ...n, x: W/2 + (Math.random()-0.5)*400, y: H/2 + (Math.random()-0.5)*300, vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.id, n]));\nconst edges = data.edges.map(e => ({ source: nodeMap.get(e.source), target: nodeMap.get(e.target) }));\n\nfunction simulate() {\n for (let iter = 0; iter < 300; iter++) {\n // Repulsion\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = 8000 / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n // Attraction (edges)\n edges.forEach(e => {\n if (!e.source || !e.target) return;\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 150) * 0.01;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n // Gravity\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001;\n n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.8; n.vy *= 0.8;\n n.x = Math.max(60, Math.min(W-60, n.x));\n n.y = Math.max(30, Math.min(H-30, n.y));\n });\n }\n}\nsimulate();\n\n// Render\nedges.forEach(e => {\n if (!e.source || !e.target) return;\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1', e.source.x); line.setAttribute('y1', e.source.y);\n line.setAttribute('x2', e.target.x); line.setAttribute('y2', e.target.y);\n line.setAttribute('class','edge');\n svg.appendChild(line);\n});\nnodes.forEach(n => {\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x-50)+','+(n.y-14)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width','100'); rect.setAttribute('height','28');\n rect.setAttribute('fill', n.status ? (statusColors[n.status]||'#ecf0f1') : projectColors[n.project]);\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x','50'); text.setAttribute('y','18'); text.setAttribute('text-anchor','middle');\n text.textContent = n.id.length > 14 ? n.target : n.id;\n g.appendChild(text);\n g.addEventListener('click', () => {\n const deps = data.edges.filter(e => e.source === n.id).map(e => e.target);\n const rdeps = data.edges.filter(e => e.target === n.id).map(e => e.source);\n alert(n.id + '\\n\\nDepends on: ' + (deps.join(', ')||'none') + '\\nRequired by: ' + (rdeps.join(', ')||'none'));\n });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"])), graphData.nodes.length, graphData.edges.length, graphData.roots.length, JSON.stringify(graphData).replaceAll("</", String.raw`<\/`));
|
|
157
|
+
};
|
|
158
|
+
const toGraphAscii = (taskGraph, options = {}) => {
|
|
159
|
+
const { taskStatuses } = options;
|
|
160
|
+
const lines = [];
|
|
161
|
+
const taskCount = Object.keys(taskGraph.tasks).length;
|
|
162
|
+
const edgeCount = Object.values(taskGraph.dependencies).reduce((s, d) => s + d.length, 0);
|
|
163
|
+
lines.push(`Task Graph (${taskCount} tasks, ${edgeCount} dependencies)`, "");
|
|
164
|
+
const allDeps = /* @__PURE__ */ new Set();
|
|
165
|
+
for (const deps of Object.values(taskGraph.dependencies)) {
|
|
166
|
+
for (const dep of deps) {
|
|
167
|
+
allDeps.add(dep);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const roots = Object.keys(taskGraph.tasks).filter((id) => !allDeps.has(id));
|
|
171
|
+
if (roots.length === 0) {
|
|
172
|
+
for (const taskId of Object.keys(taskGraph.tasks)) {
|
|
173
|
+
lines.push(formatTaskLine(taskId, taskStatuses));
|
|
174
|
+
}
|
|
175
|
+
return lines.join("\n");
|
|
176
|
+
}
|
|
177
|
+
const printed = /* @__PURE__ */ new Set();
|
|
178
|
+
for (const root of roots) {
|
|
179
|
+
printTree(root, "", true, taskGraph, printed, lines, taskStatuses);
|
|
180
|
+
}
|
|
181
|
+
if (printed.size < taskCount) {
|
|
182
|
+
lines.push("", "(*) = already shown above");
|
|
183
|
+
}
|
|
184
|
+
return lines.join("\n");
|
|
185
|
+
};
|
|
186
|
+
const projectGraphToDot = (projectGraph) => {
|
|
187
|
+
const lines = ["digraph ProjectGraph {", " rankdir=LR;", ' node [shape=box, style=filled, fillcolor="#87CEEB", fontname="monospace"];'];
|
|
188
|
+
for (const node of Object.values(projectGraph.nodes)) {
|
|
189
|
+
const color = node.type === "application" ? "#FFD700" : "#87CEEB";
|
|
190
|
+
lines.push(` "${node.name}" [fillcolor="${color}"];`);
|
|
191
|
+
}
|
|
192
|
+
for (const [project, deps] of Object.entries(projectGraph.dependencies)) {
|
|
193
|
+
for (const dep of deps) {
|
|
194
|
+
const style = dep.type === "implicit" ? "dashed" : "solid";
|
|
195
|
+
lines.push(` "${project}" -> "${dep.target}" [style=${style}];`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
lines.push("}");
|
|
199
|
+
return lines.join("\n");
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export { projectGraphToDot, toGraphAscii, toGraphHtml, toGraphJson, toGraphvizDot };
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
stat,
|
|
22
|
+
realpath,
|
|
23
|
+
readdir,
|
|
24
|
+
readFile
|
|
25
|
+
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
26
|
+
import { join } from '@visulima/path';
|
|
27
|
+
|
|
28
|
+
const MASK_128 = (1n << 128n) - 1n;
|
|
29
|
+
const MASK_64 = (1n << 64n) - 1n;
|
|
30
|
+
const MASK_32 = (1n << 32n) - 1n;
|
|
31
|
+
const PRIME32_1 = 0x9E3779B1n;
|
|
32
|
+
const PRIME32_2 = 0x85EBCA77n;
|
|
33
|
+
const PRIME32_3 = 0xC2B2AE3Dn;
|
|
34
|
+
const PRIME64_1 = 0x9E3779B185EBCA87n;
|
|
35
|
+
const PRIME64_2 = 0xC2B2AE3D27D4EB4Fn;
|
|
36
|
+
const PRIME64_3 = 0x165667B19E3779F9n;
|
|
37
|
+
const PRIME64_4 = 0x85EBCA77C2B2AE63n;
|
|
38
|
+
const PRIME64_5 = 0x27D4EB2F165667C5n;
|
|
39
|
+
const PRIME_MX1 = 0x165667919E3779F9n;
|
|
40
|
+
const PRIME_MX2 = 0x9FB21C651E98DF25n;
|
|
41
|
+
const STRIPE_LEN = 64;
|
|
42
|
+
const ACC_NB = STRIPE_LEN / 8;
|
|
43
|
+
const U64 = 8;
|
|
44
|
+
const KKEY = Buffer.from(
|
|
45
|
+
"b8fe6c3923a44bbe7c01812cf721ad1cded46de9839097db7240a4a4b7b3671fcb79e64eccc0e578825ad07dccff7221b8084674f743248ee03590e6813a264c3c2852bb91c300cb88d0658b1b532ea371644897a20df94e3819ef46a9deacd8a8fa763fe39c343ff9dcbbc7c70b4f1d8a51e04bcdb45931c89f7ec9d9787364eac5ac8334d3ebc3c581a0fffa1363eb170ddd51b7f0da49d316552629d4689e2b16be587d47a1fc8ff8b8d17ad031ce45cb3a8f95160428afd7fbcabb4b407e",
|
|
46
|
+
"hex"
|
|
47
|
+
);
|
|
48
|
+
const getView = (buf, offset = 0) => Buffer.from(buf.buffer, buf.byteOffset + offset, buf.length - offset);
|
|
49
|
+
const bswap64 = (a) => {
|
|
50
|
+
const scratch = Buffer.allocUnsafe(8);
|
|
51
|
+
scratch.writeBigUInt64LE(a);
|
|
52
|
+
return scratch.readBigUInt64BE();
|
|
53
|
+
};
|
|
54
|
+
const bswap32 = (a) => {
|
|
55
|
+
let v = a;
|
|
56
|
+
v = (v & 0x0000FFFFn) << 16n | (v & 0xFFFF0000n) >> 16n;
|
|
57
|
+
v = (v & 0x00FF00FFn) << 8n | (v & 0xFF00FF00n) >> 8n;
|
|
58
|
+
return v;
|
|
59
|
+
};
|
|
60
|
+
const multU32ToU64 = (a, b) => (a & MASK_32) * (b & MASK_32) & MASK_64;
|
|
61
|
+
const rotl32 = (a, b) => (a << b | a >> 32n - b) & MASK_32;
|
|
62
|
+
const xorshift64 = (b, shift) => b ^ b >> shift;
|
|
63
|
+
const inv64 = (x) => ~x + 1n & MASK_64;
|
|
64
|
+
const mul128Fold64 = (a, b) => {
|
|
65
|
+
const lll = a * b & MASK_128;
|
|
66
|
+
return lll & MASK_64 ^ lll >> 64n;
|
|
67
|
+
};
|
|
68
|
+
const avalanche = (h) => {
|
|
69
|
+
let v = h;
|
|
70
|
+
v ^= v >> 37n;
|
|
71
|
+
v = v * PRIME_MX1 & MASK_64;
|
|
72
|
+
v ^= v >> 32n;
|
|
73
|
+
return v;
|
|
74
|
+
};
|
|
75
|
+
const avalanche64 = (h) => {
|
|
76
|
+
let v = h;
|
|
77
|
+
v ^= v >> 33n;
|
|
78
|
+
v = v * PRIME64_2 & MASK_64;
|
|
79
|
+
v ^= v >> 29n;
|
|
80
|
+
v = v * PRIME64_3 & MASK_64;
|
|
81
|
+
v ^= v >> 32n;
|
|
82
|
+
return v;
|
|
83
|
+
};
|
|
84
|
+
const accumulate512 = (acc, data, key) => {
|
|
85
|
+
for (let i = 0; i < ACC_NB; i++) {
|
|
86
|
+
const dataVal = data.readBigUInt64LE(i * 8);
|
|
87
|
+
const dataKey = dataVal ^ key.readBigUInt64LE(i * 8);
|
|
88
|
+
acc[i ^ 1] += dataVal;
|
|
89
|
+
acc[i] += multU32ToU64(dataKey, dataKey >> 32n);
|
|
90
|
+
}
|
|
91
|
+
return acc;
|
|
92
|
+
};
|
|
93
|
+
const accumulate = (acc, data, key, nbStripes) => {
|
|
94
|
+
for (let i = 0; i < nbStripes; i++) {
|
|
95
|
+
accumulate512(acc, getView(data, i * STRIPE_LEN), getView(key, i * 8));
|
|
96
|
+
}
|
|
97
|
+
return acc;
|
|
98
|
+
};
|
|
99
|
+
const scrambleAcc = (acc, key) => {
|
|
100
|
+
for (let i = 0; i < ACC_NB; i++) {
|
|
101
|
+
const key64 = key.readBigUInt64LE(i * 8);
|
|
102
|
+
let acc64 = acc[i];
|
|
103
|
+
acc64 = xorshift64(acc64, 47n);
|
|
104
|
+
acc64 ^= key64;
|
|
105
|
+
acc64 *= PRIME32_1;
|
|
106
|
+
acc[i] = acc64 & MASK_64;
|
|
107
|
+
}
|
|
108
|
+
return acc;
|
|
109
|
+
};
|
|
110
|
+
const mix2Accs = (acc, key) => (
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
112
|
+
mul128Fold64(acc[0] ^ key.readBigUInt64LE(0), acc[1] ^ key.readBigUInt64LE(U64))
|
|
113
|
+
);
|
|
114
|
+
const mergeAccs = (acc, key, start) => {
|
|
115
|
+
let result64 = start;
|
|
116
|
+
result64 += mix2Accs(acc.slice(0), getView(key, 0));
|
|
117
|
+
result64 += mix2Accs(acc.slice(2), getView(key, 16));
|
|
118
|
+
result64 += mix2Accs(acc.slice(4), getView(key, 32));
|
|
119
|
+
result64 += mix2Accs(acc.slice(6), getView(key, 48));
|
|
120
|
+
return avalanche(result64 & MASK_64);
|
|
121
|
+
};
|
|
122
|
+
const hashLong = (acc, data, secret) => {
|
|
123
|
+
const nbStripesPerBlock = Math.floor((secret.byteLength - STRIPE_LEN) / 8);
|
|
124
|
+
const blockLen = STRIPE_LEN * nbStripesPerBlock;
|
|
125
|
+
const nbBlocks = Math.floor((data.byteLength - 1) / blockLen);
|
|
126
|
+
for (let i = 0; i < nbBlocks; i++) {
|
|
127
|
+
accumulate(acc, getView(data, i * blockLen), secret, nbStripesPerBlock);
|
|
128
|
+
scrambleAcc(acc, getView(secret, secret.byteLength - STRIPE_LEN));
|
|
129
|
+
}
|
|
130
|
+
const nbStripes = Math.floor((data.byteLength - 1 - blockLen * nbBlocks) / STRIPE_LEN);
|
|
131
|
+
accumulate(acc, getView(data, nbBlocks * blockLen), secret, nbStripes);
|
|
132
|
+
accumulate512(acc, getView(data, data.byteLength - STRIPE_LEN), getView(secret, secret.byteLength - STRIPE_LEN - 7));
|
|
133
|
+
return acc;
|
|
134
|
+
};
|
|
135
|
+
const hashLong128b = (data, secret) => {
|
|
136
|
+
const acc = new BigUint64Array([PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3, PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1]);
|
|
137
|
+
hashLong(acc, data, secret);
|
|
138
|
+
const low64 = mergeAccs(acc, getView(secret, 11), BigInt(data.byteLength) * PRIME64_1 & MASK_64);
|
|
139
|
+
const high64 = mergeAccs(
|
|
140
|
+
acc,
|
|
141
|
+
getView(secret, secret.byteLength - STRIPE_LEN - 11),
|
|
142
|
+
~(BigInt(data.byteLength) * PRIME64_2) & MASK_64
|
|
143
|
+
);
|
|
144
|
+
return high64 << 64n | low64;
|
|
145
|
+
};
|
|
146
|
+
const mix16B = (data, key, seed) => mul128Fold64(
|
|
147
|
+
(data.readBigUInt64LE(0) ^ key.readBigUInt64LE(0) + seed) & MASK_64,
|
|
148
|
+
(data.readBigUInt64LE(8) ^ key.readBigUInt64LE(8) - seed) & MASK_64
|
|
149
|
+
);
|
|
150
|
+
const mix32B = (acc, data1, data2, key, seed) => {
|
|
151
|
+
let accl = acc & MASK_64;
|
|
152
|
+
let acch = acc >> 64n & MASK_64;
|
|
153
|
+
accl += mix16B(data1, key, seed);
|
|
154
|
+
accl ^= data2.readBigUInt64LE(0) + data2.readBigUInt64LE(8);
|
|
155
|
+
accl &= MASK_64;
|
|
156
|
+
acch += mix16B(data2, getView(key, 16), seed);
|
|
157
|
+
acch ^= data1.readBigUInt64LE(0) + data1.readBigUInt64LE(8);
|
|
158
|
+
acch &= MASK_64;
|
|
159
|
+
return acch << 64n | accl;
|
|
160
|
+
};
|
|
161
|
+
const len1to3_128b = (data, key32, seed) => {
|
|
162
|
+
const len = data.byteLength;
|
|
163
|
+
const combined = BigInt(data.readUInt8(len - 1)) | BigInt(len << 8) | BigInt(data.readUInt8(0) << 16) | BigInt(data.readUInt8(len >> 1) << 24);
|
|
164
|
+
const blow = (BigInt(key32.readUInt32LE(0)) ^ BigInt(key32.readUInt32LE(4))) + seed;
|
|
165
|
+
const low = (combined ^ blow) & MASK_64;
|
|
166
|
+
const bhigh = (BigInt(key32.readUInt32LE(8)) ^ BigInt(key32.readUInt32LE(12))) - seed;
|
|
167
|
+
const high = (rotl32(bswap32(combined), 13n) ^ bhigh) & MASK_64;
|
|
168
|
+
return (avalanche64(high) & MASK_64) << 64n | avalanche64(low);
|
|
169
|
+
};
|
|
170
|
+
const len4to8_128b = (data, key32, seed) => {
|
|
171
|
+
const len = data.byteLength;
|
|
172
|
+
const l1 = data.readUInt32LE(0);
|
|
173
|
+
const l2 = data.readUInt32LE(len - 4);
|
|
174
|
+
const l64 = BigInt(l1) | BigInt(l2) << 32n;
|
|
175
|
+
const bitflip = (key32.readBigUInt64LE(16) ^ key32.readBigUInt64LE(24)) + seed & MASK_64;
|
|
176
|
+
const keyed = l64 ^ bitflip;
|
|
177
|
+
let m128 = keyed * (PRIME64_1 + (BigInt(len) << 2n)) & MASK_128;
|
|
178
|
+
m128 += (m128 & MASK_64) << 65n;
|
|
179
|
+
m128 &= MASK_128;
|
|
180
|
+
m128 ^= m128 >> 67n;
|
|
181
|
+
return xorshift64(xorshift64(m128 & MASK_64, 35n) * PRIME_MX2 & MASK_64, 28n) | avalanche(m128 >> 64n) << 64n;
|
|
182
|
+
};
|
|
183
|
+
const len9to16_128b = (data, key64, seed) => {
|
|
184
|
+
const len = data.byteLength;
|
|
185
|
+
const bitflipl = (key64.readBigUInt64LE(32) ^ key64.readBigUInt64LE(40)) + seed & MASK_64;
|
|
186
|
+
const bitfliph = (key64.readBigUInt64LE(48) ^ key64.readBigUInt64LE(56)) - seed & MASK_64;
|
|
187
|
+
const ll1 = data.readBigUInt64LE();
|
|
188
|
+
let ll2 = data.readBigUInt64LE(len - 8);
|
|
189
|
+
let m128 = (ll1 ^ ll2 ^ bitflipl) * PRIME64_1;
|
|
190
|
+
const m128l = (m128 & MASK_64) + (BigInt(len - 1) << 54n);
|
|
191
|
+
m128 = m128 & (MASK_128 ^ MASK_64) | m128l;
|
|
192
|
+
ll2 ^= bitfliph;
|
|
193
|
+
m128 += ll2 + (ll2 & MASK_32) * (PRIME32_2 - 1n) << 64n;
|
|
194
|
+
m128 &= MASK_128;
|
|
195
|
+
m128 ^= bswap64(m128 >> 64n);
|
|
196
|
+
let h128 = (m128 & MASK_64) * PRIME64_2;
|
|
197
|
+
h128 += (m128 >> 64n) * PRIME64_2 << 64n;
|
|
198
|
+
h128 &= MASK_128;
|
|
199
|
+
return avalanche(h128 & MASK_64) | avalanche(h128 >> 64n) << 64n;
|
|
200
|
+
};
|
|
201
|
+
const len0to16_128b = (data, seed) => {
|
|
202
|
+
const len = data.byteLength;
|
|
203
|
+
if (len > 8) {
|
|
204
|
+
return len9to16_128b(data, KKEY, seed);
|
|
205
|
+
}
|
|
206
|
+
if (len >= 4) {
|
|
207
|
+
return len4to8_128b(data, KKEY, seed);
|
|
208
|
+
}
|
|
209
|
+
if (len > 0) {
|
|
210
|
+
return len1to3_128b(data, KKEY, seed);
|
|
211
|
+
}
|
|
212
|
+
return avalanche64(seed ^ KKEY.readBigUInt64LE(64) ^ KKEY.readBigUInt64LE(72)) | avalanche64(seed ^ KKEY.readBigUInt64LE(80) ^ KKEY.readBigUInt64LE(88)) << 64n;
|
|
213
|
+
};
|
|
214
|
+
const len17to128_128b = (data, secret, seed) => {
|
|
215
|
+
let acc = BigInt(data.byteLength) * PRIME64_1 & MASK_64;
|
|
216
|
+
let i = BigInt(data.byteLength - 1) / 32n;
|
|
217
|
+
while (i >= 0n) {
|
|
218
|
+
const ni = Number(i);
|
|
219
|
+
acc = mix32B(acc, getView(data, 16 * ni), getView(data, data.byteLength - 16 * (ni + 1)), getView(secret, 32 * ni), seed);
|
|
220
|
+
i--;
|
|
221
|
+
}
|
|
222
|
+
let h128l = acc + (acc >> 64n) & MASK_64;
|
|
223
|
+
h128l = avalanche(h128l);
|
|
224
|
+
let h128h = (acc & MASK_64) * PRIME64_1 + (acc >> 64n) * PRIME64_4 + (BigInt(data.byteLength) - seed & MASK_64) * PRIME64_2;
|
|
225
|
+
h128h &= MASK_64;
|
|
226
|
+
h128h = inv64(avalanche(h128h));
|
|
227
|
+
return h128l | h128h << 64n;
|
|
228
|
+
};
|
|
229
|
+
const len129to240_128b = (data, secret, seed) => {
|
|
230
|
+
let acc = BigInt(data.byteLength) * PRIME64_1 & MASK_64;
|
|
231
|
+
for (let i = 32; i < 160; i += 32) {
|
|
232
|
+
acc = mix32B(acc, getView(data, i - 32), getView(data, i - 16), getView(secret, i - 32), seed);
|
|
233
|
+
}
|
|
234
|
+
acc = avalanche(acc & MASK_64) | avalanche(acc >> 64n) << 64n;
|
|
235
|
+
for (let i = 160; i <= data.byteLength; i += 32) {
|
|
236
|
+
acc = mix32B(acc, getView(data, i - 32), getView(data, i - 16), getView(secret, 3 + i - 160), seed);
|
|
237
|
+
}
|
|
238
|
+
acc = mix32B(acc, getView(data, data.byteLength - 16), getView(data, data.byteLength - 32), getView(secret, 136 - 17 - 16), inv64(seed));
|
|
239
|
+
let h128l = acc + (acc >> 64n) & MASK_64;
|
|
240
|
+
h128l = avalanche(h128l);
|
|
241
|
+
let h128h = (acc & MASK_64) * PRIME64_1 + (acc >> 64n) * PRIME64_4 + (BigInt(data.byteLength) - seed & MASK_64) * PRIME64_2;
|
|
242
|
+
h128h &= MASK_64;
|
|
243
|
+
h128h = inv64(avalanche(h128h));
|
|
244
|
+
return h128l | h128h << 64n;
|
|
245
|
+
};
|
|
246
|
+
const xxh3_128 = (data, seed = 0n) => {
|
|
247
|
+
const len = data.byteLength;
|
|
248
|
+
if (len <= 16) {
|
|
249
|
+
return len0to16_128b(data, seed);
|
|
250
|
+
}
|
|
251
|
+
if (len <= 128) {
|
|
252
|
+
return len17to128_128b(data, KKEY, seed);
|
|
253
|
+
}
|
|
254
|
+
if (len <= 240) {
|
|
255
|
+
return len129to240_128b(data, KKEY, seed);
|
|
256
|
+
}
|
|
257
|
+
return hashLong128b(data, KKEY);
|
|
258
|
+
};
|
|
259
|
+
const bigintToHex = (value) => {
|
|
260
|
+
const hi = value >> 64n & MASK_64;
|
|
261
|
+
const lo = value & MASK_64;
|
|
262
|
+
return hi.toString(16).padStart(16, "0") + lo.toString(16).padStart(16, "0");
|
|
263
|
+
};
|
|
264
|
+
const xxh3Hash = (data) => bigintToHex(xxh3_128(data));
|
|
265
|
+
class Xxh3Hasher {
|
|
266
|
+
#chunks = [];
|
|
267
|
+
update(data) {
|
|
268
|
+
if (typeof data === "string") {
|
|
269
|
+
this.#chunks.push(Buffer.from(data));
|
|
270
|
+
} else {
|
|
271
|
+
this.#chunks.push(data);
|
|
272
|
+
}
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
digest() {
|
|
276
|
+
return xxh3Hash(Buffer.concat(this.#chunks));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const createXxh3Hasher = () => new Xxh3Hasher();
|
|
280
|
+
|
|
281
|
+
const hashFile = async (filePath) => {
|
|
282
|
+
try {
|
|
283
|
+
const content = await readFile(filePath);
|
|
284
|
+
return xxh3Hash(content);
|
|
285
|
+
} catch {
|
|
286
|
+
return void 0;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const hashStrings = (...values) => {
|
|
290
|
+
const hash = createXxh3Hasher();
|
|
291
|
+
for (const v of values) {
|
|
292
|
+
hash.update(v);
|
|
293
|
+
hash.update("\0");
|
|
294
|
+
}
|
|
295
|
+
return hash.digest();
|
|
296
|
+
};
|
|
297
|
+
const sortObjectKeys = (object) => {
|
|
298
|
+
const sorted = {};
|
|
299
|
+
for (const key of Object.keys(object).toSorted()) {
|
|
300
|
+
const value = object[key];
|
|
301
|
+
sorted[key] = value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? sortObjectKeys(value) : value;
|
|
302
|
+
}
|
|
303
|
+
return sorted;
|
|
304
|
+
};
|
|
305
|
+
const collectFiles = async (directory, ignoredDirectories, visitedRealPaths) => {
|
|
306
|
+
const visited = visitedRealPaths ?? /* @__PURE__ */ new Set();
|
|
307
|
+
const results = [];
|
|
308
|
+
try {
|
|
309
|
+
const directoryStat = await stat(directory);
|
|
310
|
+
if (directoryStat.isFile()) {
|
|
311
|
+
return [directory];
|
|
312
|
+
}
|
|
313
|
+
const resolvedPath = await realpath(directory);
|
|
314
|
+
if (visited.has(resolvedPath)) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
visited.add(resolvedPath);
|
|
318
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
319
|
+
const promises = entries.map(async (entry) => {
|
|
320
|
+
if (ignoredDirectories.has(entry.name)) {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
const fullPath = join(directory, entry.name);
|
|
324
|
+
if (entry.isDirectory()) {
|
|
325
|
+
return collectFiles(fullPath, ignoredDirectories, visited);
|
|
326
|
+
}
|
|
327
|
+
if (entry.isFile()) {
|
|
328
|
+
return [fullPath];
|
|
329
|
+
}
|
|
330
|
+
if (entry.isSymbolicLink()) {
|
|
331
|
+
try {
|
|
332
|
+
const linkStat = await stat(fullPath);
|
|
333
|
+
if (linkStat.isFile()) {
|
|
334
|
+
return [fullPath];
|
|
335
|
+
}
|
|
336
|
+
if (linkStat.isDirectory()) {
|
|
337
|
+
return collectFiles(fullPath, ignoredDirectories, visited);
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return [];
|
|
343
|
+
});
|
|
344
|
+
const nested = await Promise.all(promises);
|
|
345
|
+
for (const files of nested) {
|
|
346
|
+
results.push(...files);
|
|
347
|
+
}
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
return results;
|
|
351
|
+
};
|
|
352
|
+
const resolveTaskCwd = (workspaceRoot, task) => task.projectRoot ? join(workspaceRoot, task.projectRoot) : workspaceRoot;
|
|
353
|
+
const createFailureResult = (task, error, startTime) => {
|
|
354
|
+
return {
|
|
355
|
+
code: 1,
|
|
356
|
+
endTime: Date.now(),
|
|
357
|
+
startTime,
|
|
358
|
+
status: "failure",
|
|
359
|
+
task,
|
|
360
|
+
terminalOutput: error instanceof Error ? error.message : String(error)
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
const READ_PACKAGE_DEPS_DEFAULTS = { optional: true, peer: true };
|
|
364
|
+
const readPackageDeps = async (packageJsonPath, options = READ_PACKAGE_DEPS_DEFAULTS) => {
|
|
365
|
+
try {
|
|
366
|
+
const content = await readFile(packageJsonPath, "utf8");
|
|
367
|
+
const pkg = JSON.parse(content);
|
|
368
|
+
const deps = /* @__PURE__ */ new Set();
|
|
369
|
+
const maps = [pkg.dependencies, pkg.devDependencies];
|
|
370
|
+
if (options.peer !== false) {
|
|
371
|
+
maps.push(pkg.peerDependencies);
|
|
372
|
+
}
|
|
373
|
+
if (options.optional !== false) {
|
|
374
|
+
maps.push(pkg.optionalDependencies);
|
|
375
|
+
}
|
|
376
|
+
for (const depMap of maps) {
|
|
377
|
+
if (depMap) {
|
|
378
|
+
for (const name of Object.keys(depMap)) {
|
|
379
|
+
deps.add(name);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return deps;
|
|
384
|
+
} catch {
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
const uniqueId = () => `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
389
|
+
|
|
390
|
+
export { createFailureResult as a, hashStrings as b, collectFiles as c, resolveTaskCwd as d, createXxh3Hasher as e, hashFile as h, readPackageDeps as r, sortObjectKeys as s, uniqueId as u, xxh3Hash as x };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the remote cache.
|
|
3
|
+
*/
|
|
4
|
+
interface RemoteCacheOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Called when a fire-and-forget upload fails.
|
|
7
|
+
* Since uploads are non-blocking, errors are silently swallowed by default.
|
|
8
|
+
* Provide this callback to log or report upload failures.
|
|
9
|
+
*/
|
|
10
|
+
onUploadError?: (hash: string, error: unknown) => void;
|
|
11
|
+
/** Whether to enable remote cache reads */
|
|
12
|
+
read?: boolean;
|
|
13
|
+
/** Team ID or namespace for cache isolation */
|
|
14
|
+
teamId?: string;
|
|
15
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
16
|
+
timeout?: number;
|
|
17
|
+
/** Authentication token for the remote cache */
|
|
18
|
+
token?: string;
|
|
19
|
+
/** Remote cache server URL (e.g., "https://cache.example.com") */
|
|
20
|
+
url: string;
|
|
21
|
+
/** Whether to enable remote cache writes */
|
|
22
|
+
write?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* HTTP-based remote cache compatible with the Turborepo remote cache protocol.
|
|
26
|
+
*
|
|
27
|
+
* Protocol:
|
|
28
|
+
* - GET /v8/artifacts/{hash}?teamId={team} -> retrieve cached artifact (gzipped tar)
|
|
29
|
+
* - PUT /v8/artifacts/{hash}?teamId={team} -> store artifact
|
|
30
|
+
* - POST /v8/artifacts/events -> analytics (optional)
|
|
31
|
+
*
|
|
32
|
+
* Authentication via `Authorization: Bearer {token}` header.
|
|
33
|
+
*
|
|
34
|
+
* The cache entry is a gzipped tarball containing the cache directory contents
|
|
35
|
+
* (code, terminalOutput, fingerprint.json, outputs/, .commit).
|
|
36
|
+
*/
|
|
37
|
+
declare class RemoteCache {
|
|
38
|
+
#private;
|
|
39
|
+
constructor(options: RemoteCacheOptions);
|
|
40
|
+
/**
|
|
41
|
+
* Retrieves a cached artifact from the remote cache.
|
|
42
|
+
* Returns the local path to the extracted cache entry, or undefined if not found.
|
|
43
|
+
*/
|
|
44
|
+
retrieve(hash: string, localCacheDirectory: string): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Stores a cache entry in the remote cache.
|
|
47
|
+
*/
|
|
48
|
+
store(hash: string, localCacheDirectory: string): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Checks if an artifact exists in the remote cache without downloading it.
|
|
51
|
+
*/
|
|
52
|
+
exists(hash: string): Promise<boolean>;
|
|
53
|
+
}
|
|
54
|
+
export type { RemoteCacheOptions };
|
|
55
|
+
export { RemoteCache };
|