@galaxy-tool-util/schema 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/test-format/tests.schema.generated.d.ts +47 -0
- package/dist/test-format/tests.schema.generated.d.ts.map +1 -1
- package/dist/test-format/tests.schema.generated.js +62 -0
- package/dist/test-format/tests.schema.generated.js.map +1 -1
- package/dist/workflow/cytoscape.d.ts.map +1 -1
- package/dist/workflow/cytoscape.js +5 -3
- package/dist/workflow/cytoscape.js.map +1 -1
- package/dist/workflow/draft-checks.d.ts +2 -1
- package/dist/workflow/draft-checks.d.ts.map +1 -1
- package/dist/workflow/draft-checks.js +134 -64
- package/dist/workflow/draft-checks.js.map +1 -1
- package/dist/workflow/index.d.ts +4 -2
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +4 -2
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/layout-properties.d.ts +10 -0
- package/dist/workflow/layout-properties.d.ts.map +1 -0
- package/dist/workflow/layout-properties.js +112 -0
- package/dist/workflow/layout-properties.js.map +1 -0
- package/dist/workflow/layout.d.ts +84 -0
- package/dist/workflow/layout.d.ts.map +1 -0
- package/dist/workflow/layout.js +356 -0
- package/dist/workflow/layout.js.map +1 -0
- package/dist/workflow/lint-rules.d.ts +6 -0
- package/dist/workflow/lint-rules.d.ts.map +1 -1
- package/dist/workflow/lint-rules.js +6 -0
- package/dist/workflow/lint-rules.js.map +1 -1
- package/dist/workflow/lint.d.ts +1 -1
- package/dist/workflow/lint.d.ts.map +1 -1
- package/dist/workflow/lint.js +83 -15
- package/dist/workflow/lint.js.map +1 -1
- package/dist/workflow/lint_profiles.yml +1 -0
- package/dist/workflow/mermaid.d.ts +1 -1
- package/dist/workflow/mermaid.d.ts.map +1 -1
- package/dist/workflow/mermaid.js +5 -3
- package/dist/workflow/mermaid.js.map +1 -1
- package/dist/workflow/normalized/expanded.d.ts +12 -0
- package/dist/workflow/normalized/expanded.d.ts.map +1 -1
- package/dist/workflow/normalized/expanded.js +50 -3
- package/dist/workflow/normalized/expanded.js.map +1 -1
- package/dist/workflow/normalized/format2.d.ts +22 -1
- package/dist/workflow/normalized/format2.d.ts.map +1 -1
- package/dist/workflow/normalized/format2.js +26 -5
- package/dist/workflow/normalized/format2.js.map +1 -1
- package/dist/workflow/normalized/index.d.ts +3 -3
- package/dist/workflow/normalized/index.d.ts.map +1 -1
- package/dist/workflow/normalized/index.js +3 -3
- package/dist/workflow/normalized/index.js.map +1 -1
- package/dist/workflow/normalized/labels.d.ts +7 -0
- package/dist/workflow/normalized/labels.d.ts.map +1 -1
- package/dist/workflow/normalized/labels.js +11 -0
- package/dist/workflow/normalized/labels.js.map +1 -1
- package/dist/workflow/normalized/toFormat2.d.ts +1 -0
- package/dist/workflow/normalized/toFormat2.d.ts.map +1 -1
- package/dist/workflow/normalized/toFormat2.js +38 -23
- package/dist/workflow/normalized/toFormat2.js.map +1 -1
- package/dist/workflow/normalized/toNative.d.ts +16 -1
- package/dist/workflow/normalized/toNative.d.ts.map +1 -1
- package/dist/workflow/normalized/toNative.js +39 -32
- package/dist/workflow/normalized/toNative.js.map +1 -1
- package/dist/workflow/semantic-validators.d.ts +9 -0
- package/dist/workflow/semantic-validators.d.ts.map +1 -1
- package/dist/workflow/semantic-validators.js +27 -14
- package/dist/workflow/semantic-validators.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute and apply node positions for Galaxy workflows.
|
|
3
|
+
*
|
|
4
|
+
* Port of gxformat2/layout. Two strategies: `topological` (the cross-language
|
|
5
|
+
* layering from `cytoscape-layout.ts`) and `layered` (barycenter Sugiyama
|
|
6
|
+
* crossing reduction). Layered coordinates are not a cross-language contract;
|
|
7
|
+
* both strategies are validated by the structural checkers in
|
|
8
|
+
* `layout-properties.ts`. Cyclic workflows raise {@link LayoutCycleError}.
|
|
9
|
+
*/
|
|
10
|
+
import { COL_STRIDE, ROW_STRIDE, topologicalPositions } from "./cytoscape-layout.js";
|
|
11
|
+
import { cytoscapeElements } from "./cytoscape.js";
|
|
12
|
+
import { rawStepRenderIdentity, unlabeledNodeId } from "./normalized/labels.js";
|
|
13
|
+
import { INPUT_STEP_TYPES } from "./normalized/toFormat2.js";
|
|
14
|
+
/** Sentinel value for `position:` that requests automatic layout. */
|
|
15
|
+
export const AUTO = "auto";
|
|
16
|
+
/**
|
|
17
|
+
* Raised when a workflow graph contains a cycle. The bake path refuses to lay
|
|
18
|
+
* out a cyclic graph; the cytoscape viz path keeps its diagonal fallback.
|
|
19
|
+
*/
|
|
20
|
+
export class LayoutCycleError extends Error {
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "LayoutCycleError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function isDict(value) {
|
|
27
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
function containerItems(container) {
|
|
30
|
+
if (Array.isArray(container))
|
|
31
|
+
return container;
|
|
32
|
+
if (isDict(container))
|
|
33
|
+
return Object.values(container);
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
// --- Graph + layering -------------------------------------------------------
|
|
37
|
+
/** Pull `[nodeIds, edges]` out of cytoscape elements, dropping dangling edges. */
|
|
38
|
+
export function extractGraph(elements) {
|
|
39
|
+
const nodeIds = elements.nodes.map((n) => n.data.id);
|
|
40
|
+
const idSet = new Set(nodeIds);
|
|
41
|
+
const edges = [];
|
|
42
|
+
for (const edge of elements.edges) {
|
|
43
|
+
const { source, target } = edge.data;
|
|
44
|
+
if (idSet.has(source) && idSet.has(target))
|
|
45
|
+
edges.push([source, target]);
|
|
46
|
+
}
|
|
47
|
+
return [nodeIds, edges];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Longest-path layering via Kahn topological sort with declaration-index tie
|
|
51
|
+
* break (same as `topologicalPositions`). Throws {@link LayoutCycleError} when
|
|
52
|
+
* the graph is not a DAG.
|
|
53
|
+
*/
|
|
54
|
+
export function layerAssignment(nodeIds, edges) {
|
|
55
|
+
const indexById = new Map(nodeIds.map((id, i) => [id, i]));
|
|
56
|
+
const incoming = new Map(nodeIds.map((id) => [id, []]));
|
|
57
|
+
const dependents = new Map(nodeIds.map((id) => [id, []]));
|
|
58
|
+
for (const [source, target] of edges) {
|
|
59
|
+
incoming.get(target).push(source);
|
|
60
|
+
dependents.get(source).push(target);
|
|
61
|
+
}
|
|
62
|
+
const inDegree = new Map(nodeIds.map((id) => [id, incoming.get(id).length]));
|
|
63
|
+
const column = new Map();
|
|
64
|
+
const queue = nodeIds.filter((id) => inDegree.get(id) === 0);
|
|
65
|
+
while (queue.length > 0) {
|
|
66
|
+
let best = 0;
|
|
67
|
+
for (let i = 1; i < queue.length; i++) {
|
|
68
|
+
if (indexById.get(queue[i]) < indexById.get(queue[best]))
|
|
69
|
+
best = i;
|
|
70
|
+
}
|
|
71
|
+
const id = queue.splice(best, 1)[0];
|
|
72
|
+
const sources = incoming.get(id);
|
|
73
|
+
column.set(id, sources.length === 0 ? 0 : Math.max(...sources.map((s) => column.get(s) + 1)));
|
|
74
|
+
for (const dep of dependents.get(id)) {
|
|
75
|
+
const remaining = inDegree.get(dep) - 1;
|
|
76
|
+
inDegree.set(dep, remaining);
|
|
77
|
+
if (remaining === 0)
|
|
78
|
+
queue.push(dep);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (column.size !== nodeIds.length) {
|
|
82
|
+
const unplaced = nodeIds.filter((id) => !column.has(id)).sort();
|
|
83
|
+
throw new LayoutCycleError("Workflow graph contains a cycle; cannot compute a layout. " +
|
|
84
|
+
`Nodes on or downstream of the cycle: ${JSON.stringify(unplaced)}`);
|
|
85
|
+
}
|
|
86
|
+
return column;
|
|
87
|
+
}
|
|
88
|
+
/** Throw {@link LayoutCycleError} if `elements` describes a cyclic graph. */
|
|
89
|
+
export function checkAcyclic(elements) {
|
|
90
|
+
const [nodeIds, edges] = extractGraph(elements);
|
|
91
|
+
layerAssignment(nodeIds, edges);
|
|
92
|
+
}
|
|
93
|
+
/** Barycenter sweeps to attempt; the best ordering seen is kept. */
|
|
94
|
+
const MAX_SWEEPS = 8;
|
|
95
|
+
function countCrossingsBetween(upper, lower, successors) {
|
|
96
|
+
const posLower = new Map(lower.map((id, i) => [id, i]));
|
|
97
|
+
const edges = [];
|
|
98
|
+
upper.forEach((u, uIdx) => {
|
|
99
|
+
for (const v of successors.get(u)) {
|
|
100
|
+
const vIdx = posLower.get(v);
|
|
101
|
+
if (vIdx !== undefined)
|
|
102
|
+
edges.push([uIdx, vIdx]);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
let crossings = 0;
|
|
106
|
+
for (let i = 0; i < edges.length; i++) {
|
|
107
|
+
const [ui, vi] = edges[i];
|
|
108
|
+
for (let j = i + 1; j < edges.length; j++) {
|
|
109
|
+
const [uj, vj] = edges[j];
|
|
110
|
+
if ((ui < uj && vi > vj) || (ui > uj && vi < vj))
|
|
111
|
+
crossings += 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return crossings;
|
|
115
|
+
}
|
|
116
|
+
function countAllCrossings(layers, successors) {
|
|
117
|
+
let total = 0;
|
|
118
|
+
for (let col = 0; col < layers.length - 1; col++) {
|
|
119
|
+
total += countCrossingsBetween(layers[col], layers[col + 1], successors);
|
|
120
|
+
}
|
|
121
|
+
return total;
|
|
122
|
+
}
|
|
123
|
+
/** Stable reorder of `layer` by mean position of each node's fixed neighbors. */
|
|
124
|
+
function orderByBarycenter(layer, neighbors, posInFixed) {
|
|
125
|
+
const keyed = layer.map((id, idx) => {
|
|
126
|
+
const fixed = neighbors
|
|
127
|
+
.get(id)
|
|
128
|
+
.map((m) => posInFixed.get(m))
|
|
129
|
+
.filter((p) => p !== undefined);
|
|
130
|
+
const key = fixed.length > 0 ? fixed.reduce((a, b) => a + b, 0) / fixed.length : idx;
|
|
131
|
+
return { id, key };
|
|
132
|
+
});
|
|
133
|
+
// Array.prototype.sort is stable, so ties preserve input order.
|
|
134
|
+
return keyed.sort((a, b) => a.key - b.key).map((k) => k.id);
|
|
135
|
+
}
|
|
136
|
+
function sweep(layers, neighbors, down) {
|
|
137
|
+
const columns = [];
|
|
138
|
+
if (down)
|
|
139
|
+
for (let col = 1; col < layers.length; col++)
|
|
140
|
+
columns.push(col);
|
|
141
|
+
else
|
|
142
|
+
for (let col = layers.length - 2; col >= 0; col--)
|
|
143
|
+
columns.push(col);
|
|
144
|
+
for (const col of columns) {
|
|
145
|
+
const fixed = layers[down ? col - 1 : col + 1];
|
|
146
|
+
const posInFixed = new Map(fixed.map((id, i) => [id, i]));
|
|
147
|
+
layers[col] = orderByBarycenter(layers[col], neighbors, posInFixed);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Sugiyama-style layered layout: longest-path layering, barycenter crossing
|
|
152
|
+
* reduction, then order-index rows per layer. Skip-level edges are not split
|
|
153
|
+
* into dummy nodes, so results can be suboptimal but every edge still points
|
|
154
|
+
* rightward.
|
|
155
|
+
*/
|
|
156
|
+
export function layeredPositions(elements) {
|
|
157
|
+
const [nodeIds, edges] = extractGraph(elements);
|
|
158
|
+
const column = layerAssignment(nodeIds, edges);
|
|
159
|
+
const maxCol = Math.max(-1, ...column.values());
|
|
160
|
+
const layers = Array.from({ length: maxCol + 1 }, () => []);
|
|
161
|
+
for (const id of nodeIds)
|
|
162
|
+
layers[column.get(id)].push(id);
|
|
163
|
+
const predecessors = new Map(nodeIds.map((id) => [id, []]));
|
|
164
|
+
const successors = new Map(nodeIds.map((id) => [id, []]));
|
|
165
|
+
for (const [source, target] of edges) {
|
|
166
|
+
successors.get(source).push(target);
|
|
167
|
+
predecessors.get(target).push(source);
|
|
168
|
+
}
|
|
169
|
+
let bestLayers = layers.map((l) => [...l]);
|
|
170
|
+
let bestCrossings = countAllCrossings(bestLayers, successors);
|
|
171
|
+
for (let i = 0; i < MAX_SWEEPS && bestCrossings > 0; i++) {
|
|
172
|
+
const goingDown = i % 2 === 0;
|
|
173
|
+
sweep(layers, goingDown ? predecessors : successors, goingDown);
|
|
174
|
+
const crossings = countAllCrossings(layers, successors);
|
|
175
|
+
if (crossings < bestCrossings) {
|
|
176
|
+
bestCrossings = crossings;
|
|
177
|
+
bestLayers = layers.map((l) => [...l]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const positions = new Map();
|
|
181
|
+
bestLayers.forEach((layer, col) => {
|
|
182
|
+
layer.forEach((id, row) => positions.set(id, { x: col * COL_STRIDE, y: row * ROW_STRIDE }));
|
|
183
|
+
});
|
|
184
|
+
return positions;
|
|
185
|
+
}
|
|
186
|
+
// --- Document helpers (shared with layout-properties.ts) ---------------------
|
|
187
|
+
export function isNativeDocument(workflow) {
|
|
188
|
+
return workflow.a_galaxy_workflow === "true";
|
|
189
|
+
}
|
|
190
|
+
/** A `$graph` multi-workflow document (many workflows, one file). */
|
|
191
|
+
export function isGraphDocument(workflow) {
|
|
192
|
+
return isDict(workflow) && "$graph" in workflow && !("class" in workflow);
|
|
193
|
+
}
|
|
194
|
+
function isEmbeddedWorkflow(value) {
|
|
195
|
+
return isDict(value) && (value.class === "GalaxyWorkflow" || value.a_galaxy_workflow === "true");
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Yield in-file subworkflows embedded in `workflow`'s steps (native inline
|
|
199
|
+
* `subworkflow`, Format2 embedded `run`). String references are not followed.
|
|
200
|
+
*/
|
|
201
|
+
export function* iterSubworkflows(workflow) {
|
|
202
|
+
const native = isNativeDocument(workflow);
|
|
203
|
+
for (const step of containerItems(workflow.steps)) {
|
|
204
|
+
if (!isDict(step))
|
|
205
|
+
continue;
|
|
206
|
+
const candidate = native ? step.subworkflow : step.run;
|
|
207
|
+
if (isEmbeddedWorkflow(candidate))
|
|
208
|
+
yield candidate;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Id cytoscape assigns to a Format2 node: inputs by id, steps by render
|
|
213
|
+
* identity. `key` is the dict-container key, or null for list form.
|
|
214
|
+
*/
|
|
215
|
+
export function format2NodeId(item, key, isInput) {
|
|
216
|
+
if (isInput) {
|
|
217
|
+
if (key !== null)
|
|
218
|
+
return key;
|
|
219
|
+
return isDict(item) && typeof item.id === "string" ? item.id : null;
|
|
220
|
+
}
|
|
221
|
+
if (key !== null)
|
|
222
|
+
return rawStepRenderIdentity(item, key);
|
|
223
|
+
if (!isDict(item))
|
|
224
|
+
return null;
|
|
225
|
+
const id = item.label || item.id;
|
|
226
|
+
return id ?? null;
|
|
227
|
+
}
|
|
228
|
+
/** Id normalization assigns to a native step (label, else unlabeled sentinel). */
|
|
229
|
+
export function nativeNodeId(key, step) {
|
|
230
|
+
return unlabeledNodeId(step.label, step.id ?? key, INPUT_STEP_TYPES.has(step.type));
|
|
231
|
+
}
|
|
232
|
+
function stripPositions(workflow) {
|
|
233
|
+
const cleaned = JSON.parse(JSON.stringify(workflow));
|
|
234
|
+
const containerKeys = isNativeDocument(cleaned) ? ["steps"] : ["inputs", "steps"];
|
|
235
|
+
for (const containerKey of containerKeys) {
|
|
236
|
+
for (const item of containerItems(cleaned[containerKey])) {
|
|
237
|
+
if (isDict(item))
|
|
238
|
+
delete item.position;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return cleaned;
|
|
242
|
+
}
|
|
243
|
+
function shouldWrite(existing, overwrite) {
|
|
244
|
+
if (existing == null || existing === AUTO)
|
|
245
|
+
return true;
|
|
246
|
+
return overwrite;
|
|
247
|
+
}
|
|
248
|
+
// --- Public API ---------------------------------------------------------------
|
|
249
|
+
/**
|
|
250
|
+
* Compute node positions keyed by node id (Format2 input ids / step render
|
|
251
|
+
* identities — the ids the cytoscape builder emits).
|
|
252
|
+
*/
|
|
253
|
+
/**
|
|
254
|
+
* Preset cytoscape elements for one layout unit. `graphEntries` are the
|
|
255
|
+
* sibling entries when `workflow` is part of a `$graph` document.
|
|
256
|
+
*/
|
|
257
|
+
export function layoutElements(workflow, graphEntries = []) {
|
|
258
|
+
// Strip positions first: strategies ignore them and `auto` isn't a valid position.
|
|
259
|
+
let source = isDict(workflow) ? stripPositions(workflow) : workflow;
|
|
260
|
+
if (graphEntries.length > 0 && isDict(source)) {
|
|
261
|
+
// Normalize the entry as `main` alongside its siblings so `#id` run
|
|
262
|
+
// references resolve.
|
|
263
|
+
const siblings = graphEntries.filter((e) => e !== workflow && e.id !== "main");
|
|
264
|
+
source = { $graph: [{ ...source, id: "main" }, ...siblings] };
|
|
265
|
+
}
|
|
266
|
+
return cytoscapeElements(source, { layout: "preset", draftOverlay: null });
|
|
267
|
+
}
|
|
268
|
+
export function layoutPositions(workflow, strategy = "topological", graphEntries = []) {
|
|
269
|
+
if (strategy !== "topological" && strategy !== "layered") {
|
|
270
|
+
throw new Error(`Unknown layout strategy "${strategy}". Valid values: topological, layered.`);
|
|
271
|
+
}
|
|
272
|
+
const elements = layoutElements(workflow, graphEntries);
|
|
273
|
+
let positions;
|
|
274
|
+
if (strategy === "topological") {
|
|
275
|
+
// topologicalPositions silently falls back on cycles; refuse up front.
|
|
276
|
+
checkAcyclic(elements);
|
|
277
|
+
positions = topologicalPositions(elements);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
positions = layeredPositions(elements);
|
|
281
|
+
}
|
|
282
|
+
const result = new Map();
|
|
283
|
+
for (const [id, p] of positions)
|
|
284
|
+
result.set(id, { left: p.x, top: p.y });
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
function applyToContainer(container, positions, overwrite, isInput) {
|
|
288
|
+
if (isDict(container)) {
|
|
289
|
+
for (const key of Object.keys(container)) {
|
|
290
|
+
const pos = positions.get(format2NodeId(container[key], key, isInput) ?? "");
|
|
291
|
+
if (pos === undefined)
|
|
292
|
+
continue;
|
|
293
|
+
// Promote Format2 shorthand (e.g. `x: data`) to mapping form.
|
|
294
|
+
const value = isDict(container[key]) ? container[key] : { type: container[key] };
|
|
295
|
+
if (shouldWrite(value.position, overwrite))
|
|
296
|
+
value.position = { ...pos };
|
|
297
|
+
container[key] = value;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else if (Array.isArray(container)) {
|
|
301
|
+
for (const item of container) {
|
|
302
|
+
if (!isDict(item))
|
|
303
|
+
continue;
|
|
304
|
+
const pos = positions.get(format2NodeId(item, null, isInput) ?? "");
|
|
305
|
+
if (pos !== undefined && shouldWrite(item.position, overwrite))
|
|
306
|
+
item.position = { ...pos };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return container;
|
|
310
|
+
}
|
|
311
|
+
function applyNative(workflow, positions, overwrite) {
|
|
312
|
+
if (!isDict(workflow.steps))
|
|
313
|
+
return;
|
|
314
|
+
for (const [key, step] of Object.entries(workflow.steps)) {
|
|
315
|
+
if (!isDict(step))
|
|
316
|
+
continue;
|
|
317
|
+
const pos = positions.get(nativeNodeId(key, step));
|
|
318
|
+
if (pos !== undefined && shouldWrite(step.position, overwrite))
|
|
319
|
+
step.position = { ...pos };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Merge computed positions into a Format2 or native workflow dict, mutating it
|
|
324
|
+
* in place and returning it. Explicit positions are kept unless `overwrite`;
|
|
325
|
+
* `position: auto` is always replaced. With `recursive` (default), embedded
|
|
326
|
+
* subworkflows and every `$graph` entry get their own coordinate space.
|
|
327
|
+
*/
|
|
328
|
+
export function applyLayout(workflow, opts = {}) {
|
|
329
|
+
if (isGraphDocument(workflow)) {
|
|
330
|
+
const entries = containerItems(workflow.$graph).filter(isDict);
|
|
331
|
+
for (const entry of entries)
|
|
332
|
+
applyLayoutUnit(entry, opts, entries);
|
|
333
|
+
return workflow;
|
|
334
|
+
}
|
|
335
|
+
return applyLayoutUnit(workflow, opts, []);
|
|
336
|
+
}
|
|
337
|
+
function applyLayoutUnit(workflow, opts, graphEntries) {
|
|
338
|
+
const { strategy = "topological", overwrite = false, recursive = true } = opts;
|
|
339
|
+
const positions = layoutPositions(workflow, strategy, graphEntries);
|
|
340
|
+
if (isNativeDocument(workflow)) {
|
|
341
|
+
applyNative(workflow, positions, overwrite);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
for (const containerKey of ["inputs", "steps"]) {
|
|
345
|
+
if (workflow[containerKey] == null)
|
|
346
|
+
continue;
|
|
347
|
+
workflow[containerKey] = applyToContainer(workflow[containerKey], positions, overwrite, containerKey === "inputs");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (recursive) {
|
|
351
|
+
for (const sub of iterSubworkflows(workflow))
|
|
352
|
+
applyLayout(sub, opts);
|
|
353
|
+
}
|
|
354
|
+
return workflow;
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/workflow/layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,4 +12,10 @@ export declare class NativeStepKeyNotInteger extends Linter {
|
|
|
12
12
|
static applies_to: readonly ["native"];
|
|
13
13
|
static profile: string;
|
|
14
14
|
}
|
|
15
|
+
/** Step labels (including inputs) must be unique within a workflow; Galaxy refuses duplicates at import. */
|
|
16
|
+
export declare class StepLabelNotUnique extends Linter {
|
|
17
|
+
static severity: "error";
|
|
18
|
+
static applies_to: readonly ["format2", "native"];
|
|
19
|
+
static profile: string;
|
|
20
|
+
}
|
|
15
21
|
//# sourceMappingURL=lint-rules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint-rules.d.ts","sourceRoot":"","sources":["../../src/workflow/lint-rules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,4EAA4E;AAC5E,qBAAa,uBAAwB,SAAQ,MAAM;IACjD,MAAM,CAAC,QAAQ,EAAG,OAAO,CAAU;IACnC,MAAM,CAAC,UAAU,sBAAuB;IACxC,MAAM,CAAC,OAAO,SAAgB;CAC/B"}
|
|
1
|
+
{"version":3,"file":"lint-rules.d.ts","sourceRoot":"","sources":["../../src/workflow/lint-rules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,4EAA4E;AAC5E,qBAAa,uBAAwB,SAAQ,MAAM;IACjD,MAAM,CAAC,QAAQ,EAAG,OAAO,CAAU;IACnC,MAAM,CAAC,UAAU,sBAAuB;IACxC,MAAM,CAAC,OAAO,SAAgB;CAC/B;AAED,4GAA4G;AAC5G,qBAAa,kBAAmB,SAAQ,MAAM;IAC5C,MAAM,CAAC,QAAQ,EAAG,OAAO,CAAU;IACnC,MAAM,CAAC,UAAU,iCAAkC;IACnD,MAAM,CAAC,OAAO,SAAgB;CAC/B"}
|
|
@@ -12,4 +12,10 @@ export class NativeStepKeyNotInteger extends Linter {
|
|
|
12
12
|
static applies_to = ["native"];
|
|
13
13
|
static profile = "structural";
|
|
14
14
|
}
|
|
15
|
+
/** Step labels (including inputs) must be unique within a workflow; Galaxy refuses duplicates at import. */
|
|
16
|
+
export class StepLabelNotUnique extends Linter {
|
|
17
|
+
static severity = "error";
|
|
18
|
+
static applies_to = ["format2", "native"];
|
|
19
|
+
static profile = "structural";
|
|
20
|
+
}
|
|
15
21
|
//# sourceMappingURL=lint-rules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint-rules.js","sourceRoot":"","sources":["../../src/workflow/lint-rules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,4EAA4E;AAC5E,MAAM,OAAO,uBAAwB,SAAQ,MAAM;IACjD,MAAM,CAAC,QAAQ,GAAG,OAAgB,CAAC;IACnC,MAAM,CAAC,UAAU,GAAG,CAAC,QAAQ,CAAU,CAAC;IACxC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"lint-rules.js","sourceRoot":"","sources":["../../src/workflow/lint-rules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,4EAA4E;AAC5E,MAAM,OAAO,uBAAwB,SAAQ,MAAM;IACjD,MAAM,CAAC,QAAQ,GAAG,OAAgB,CAAC;IACnC,MAAM,CAAC,UAAU,GAAG,CAAC,QAAQ,CAAU,CAAC;IACxC,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC;;AAGhC,4GAA4G;AAC5G,MAAM,OAAO,kBAAmB,SAAQ,MAAM;IAC5C,MAAM,CAAC,QAAQ,GAAG,OAAgB,CAAC;IACnC,MAAM,CAAC,UAAU,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAU,CAAC;IACnD,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC"}
|
package/dist/workflow/lint.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare function lintWorkflow(workflowDict: Record<string, unknown>): Lin
|
|
|
29
29
|
export declare function lintBestPracticesFormat2(workflowDict: Record<string, unknown>): LintResult;
|
|
30
30
|
/**
|
|
31
31
|
* Lint best practices for a native Galaxy workflow.
|
|
32
|
-
* Runs
|
|
32
|
+
* Runs top-level checks on the format2 view and step checks on native steps.
|
|
33
33
|
*/
|
|
34
34
|
export declare function lintBestPracticesNative(workflowDict: Record<string, unknown>): LintResult;
|
|
35
35
|
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/workflow/lint.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/workflow/lint.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAI5D,OAAO,EACL,WAAW,EACX,WAAW,EACX,MAAM,EACN,WAAW,EACX,UAAU,EACV,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AA4QtB;;;GAGG;AACH,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAU5E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAU7E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAG9E;AA+ID;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAe1F;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAuBzF"}
|
package/dist/workflow/lint.js
CHANGED
|
@@ -9,7 +9,8 @@ import { normalizedNative } from "./normalized/native.js";
|
|
|
9
9
|
import { ensureFormat2 } from "./normalized/ensure.js";
|
|
10
10
|
import { detectFormat } from "./detect-format.js";
|
|
11
11
|
import { LintContext } from "./linting.js";
|
|
12
|
-
import {
|
|
12
|
+
import { nativeInputLabel, nativeStepLabel } from "./normalized/toNative.js";
|
|
13
|
+
import { NativeStepKeyNotInteger, StepLabelNotUnique } from "./lint-rules.js";
|
|
13
14
|
export { LintContext, LintMessage, Linter, LEVEL_ERROR, LEVEL_WARN, } from "./linting.js";
|
|
14
15
|
function toLintResult(ctx) {
|
|
15
16
|
return {
|
|
@@ -41,6 +42,34 @@ function validateReport(ctx, report) {
|
|
|
41
42
|
}
|
|
42
43
|
// Note: Galaxy markdown validation (```galaxy directives) not yet ported
|
|
43
44
|
}
|
|
45
|
+
function nativeStepContext(ctx, stepKey) {
|
|
46
|
+
return ctx.child("steps").child(stepKey);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Error on step labels shared by more than one step, as Galaxy rejects these at import.
|
|
50
|
+
*
|
|
51
|
+
* `labeledSteps` holds `[label, description, labelContext]` for each step of one
|
|
52
|
+
* workflow level (subworkflows have their own label namespace); `labelContext`
|
|
53
|
+
* locates the label, or `undefined` to report at `ctx`. Empty labels are
|
|
54
|
+
* ignored, matching Galaxy.
|
|
55
|
+
*/
|
|
56
|
+
function lintDuplicateStepLabels(ctx, labeledSteps) {
|
|
57
|
+
const stepsByLabel = new Map();
|
|
58
|
+
for (const [label, description, labelContext] of labeledSteps) {
|
|
59
|
+
if (!label)
|
|
60
|
+
continue;
|
|
61
|
+
const steps = stepsByLabel.get(label);
|
|
62
|
+
if (steps)
|
|
63
|
+
steps.push([description, labelContext]);
|
|
64
|
+
else
|
|
65
|
+
stepsByLabel.set(label, [[description, labelContext]]);
|
|
66
|
+
}
|
|
67
|
+
for (const [label, steps] of stepsByLabel) {
|
|
68
|
+
if (steps.length < 2)
|
|
69
|
+
continue;
|
|
70
|
+
(steps[1][1] ?? ctx).error(`Duplicated step label '${label}' (${steps.map(([d]) => d).join(", ")})`, { linter: StepLabelNotUnique });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
44
73
|
// ---------------------------------------------------------------------------
|
|
45
74
|
// Native workflow linting
|
|
46
75
|
// ---------------------------------------------------------------------------
|
|
@@ -66,11 +95,16 @@ function lintNativeWorkflow(ctx, nnw, rawDict) {
|
|
|
66
95
|
}
|
|
67
96
|
let foundOutputs = false;
|
|
68
97
|
let foundOutputWithoutLabel = false;
|
|
98
|
+
lintDuplicateStepLabels(ctx, Object.entries(nnw.steps).map(([key, step]) => [
|
|
99
|
+
step.label,
|
|
100
|
+
`step ${key}`,
|
|
101
|
+
nativeStepContext(ctx, key).child("label"),
|
|
102
|
+
]));
|
|
69
103
|
for (const [orderIndex, step] of Object.entries(nnw.steps)) {
|
|
104
|
+
const stepContext = nativeStepContext(ctx, orderIndex);
|
|
70
105
|
if (!/^\d+$/.test(orderIndex)) {
|
|
71
|
-
|
|
106
|
+
stepContext.error(`expected step_key to be integer not [${orderIndex}]`, {
|
|
72
107
|
linter: NativeStepKeyNotInteger,
|
|
73
|
-
json_pointer: `/steps/${orderIndex}`,
|
|
74
108
|
});
|
|
75
109
|
}
|
|
76
110
|
for (const wo of step.workflow_outputs) {
|
|
@@ -85,7 +119,7 @@ function lintNativeWorkflow(ctx, nnw, rawDict) {
|
|
|
85
119
|
ctx.error("subworkflow is missing steps or steps are empty");
|
|
86
120
|
}
|
|
87
121
|
else {
|
|
88
|
-
lintNativeWorkflow(
|
|
122
|
+
lintNativeWorkflow(stepContext.child("subworkflow"), step.subworkflow);
|
|
89
123
|
}
|
|
90
124
|
}
|
|
91
125
|
lintStepErrors(ctx, step.errors);
|
|
@@ -166,6 +200,19 @@ function lintFormat2Workflow(ctx, nf2, rawDict) {
|
|
|
166
200
|
ctx.error("expected to find key [class] but absent");
|
|
167
201
|
}
|
|
168
202
|
}
|
|
203
|
+
// Check the labels conversion will assign to the native steps.
|
|
204
|
+
lintDuplicateStepLabels(ctx, [
|
|
205
|
+
...nf2.inputs.map((inp, i) => [
|
|
206
|
+
nativeInputLabel(inp, i),
|
|
207
|
+
`input ${inp.id || i}`,
|
|
208
|
+
undefined,
|
|
209
|
+
]),
|
|
210
|
+
...nf2.steps.map((step) => [
|
|
211
|
+
nativeStepLabel(step),
|
|
212
|
+
`step ${step.id}`,
|
|
213
|
+
undefined,
|
|
214
|
+
]),
|
|
215
|
+
]);
|
|
169
216
|
for (const step of nf2.steps) {
|
|
170
217
|
lintStepErrors(ctx, step.errors);
|
|
171
218
|
lintToolIfPresent(ctx, step.tool_id);
|
|
@@ -276,6 +323,10 @@ function lintStepBestPractices(ctx, step) {
|
|
|
276
323
|
ctx.warn(`Workflow step ${stepId} specifies an untyped parameter in the post-job actions.`);
|
|
277
324
|
}
|
|
278
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Native step best-practice checks on native fields, so messages reference
|
|
328
|
+
* native ids / labels rather than format2 sentinels like `_unlabeled_step_1`.
|
|
329
|
+
*/
|
|
279
330
|
function lintNativeStepBestPractices(ctx, step) {
|
|
280
331
|
const stepId = step.label || step.annotation || String(step.id);
|
|
281
332
|
// disconnected inputs
|
|
@@ -286,14 +337,34 @@ function lintNativeStepBestPractices(ctx, step) {
|
|
|
286
337
|
}
|
|
287
338
|
}
|
|
288
339
|
}
|
|
340
|
+
// missing metadata
|
|
341
|
+
if (!step.annotation) {
|
|
342
|
+
ctx.warn(`Workflow step ${stepId} has no annotation.`);
|
|
343
|
+
}
|
|
344
|
+
if (!step.label) {
|
|
345
|
+
ctx.warn(`Workflow step ${stepId} has no label.`);
|
|
346
|
+
}
|
|
347
|
+
// untyped params in tool_state
|
|
348
|
+
let toolState = step.tool_state;
|
|
349
|
+
if (typeof toolState === "string") {
|
|
350
|
+
try {
|
|
351
|
+
toolState = JSON.parse(toolState);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
toolState = {};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (toolState && typeof toolState === "object" && checkJsonForUntypedParams(toolState)) {
|
|
358
|
+
ctx.warn(`Workflow step ${stepId} specifies an untyped parameter as an input.`);
|
|
359
|
+
}
|
|
289
360
|
// untyped params in post_job_actions
|
|
290
361
|
if (step.post_job_actions && Object.keys(step.post_job_actions).length > 0) {
|
|
291
362
|
if (checkJsonForUntypedParams(step.post_job_actions)) {
|
|
292
|
-
ctx.warn(`Workflow step
|
|
363
|
+
ctx.warn(`Workflow step ${stepId} specifies an untyped parameter in the post-job actions.`);
|
|
293
364
|
}
|
|
294
365
|
}
|
|
295
366
|
}
|
|
296
|
-
function
|
|
367
|
+
function lintWorkflowTopLevel(ctx, nf2, rawDict) {
|
|
297
368
|
// annotation/doc
|
|
298
369
|
if (!nf2.doc || !nf2.doc.trim()) {
|
|
299
370
|
ctx.warn("Workflow is not annotated.");
|
|
@@ -322,10 +393,6 @@ function lintBestPractices(ctx, nf2, rawDict) {
|
|
|
322
393
|
if (!nf2.license) {
|
|
323
394
|
ctx.warn("Workflow does not specify a license.");
|
|
324
395
|
}
|
|
325
|
-
// step-level
|
|
326
|
-
for (const step of nf2.steps) {
|
|
327
|
-
lintStepBestPractices(ctx, step);
|
|
328
|
-
}
|
|
329
396
|
}
|
|
330
397
|
/**
|
|
331
398
|
* Lint best practices for a Format2 Galaxy workflow.
|
|
@@ -342,17 +409,19 @@ export function lintBestPracticesFormat2(workflowDict) {
|
|
|
342
409
|
nf2 = null;
|
|
343
410
|
}
|
|
344
411
|
if (nf2) {
|
|
345
|
-
|
|
412
|
+
lintWorkflowTopLevel(ctx, nf2, workflowDict);
|
|
413
|
+
for (const step of nf2.steps) {
|
|
414
|
+
lintStepBestPractices(ctx, step);
|
|
415
|
+
}
|
|
346
416
|
}
|
|
347
417
|
return toLintResult(ctx);
|
|
348
418
|
}
|
|
349
419
|
/**
|
|
350
420
|
* Lint best practices for a native Galaxy workflow.
|
|
351
|
-
* Runs
|
|
421
|
+
* Runs top-level checks on the format2 view and step checks on native steps.
|
|
352
422
|
*/
|
|
353
423
|
export function lintBestPracticesNative(workflowDict) {
|
|
354
424
|
const ctx = new LintContext();
|
|
355
|
-
// Shared best practices on format2 view
|
|
356
425
|
let nf2;
|
|
357
426
|
try {
|
|
358
427
|
nf2 = ensureFormat2(workflowDict);
|
|
@@ -361,9 +430,8 @@ export function lintBestPracticesNative(workflowDict) {
|
|
|
361
430
|
nf2 = null;
|
|
362
431
|
}
|
|
363
432
|
if (nf2) {
|
|
364
|
-
|
|
433
|
+
lintWorkflowTopLevel(ctx, nf2, workflowDict);
|
|
365
434
|
}
|
|
366
|
-
// Native-specific checks
|
|
367
435
|
let nnw;
|
|
368
436
|
try {
|
|
369
437
|
nnw = normalizedNative(workflowDict);
|