chore: initial commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import type {CompiledWorkflow} from '../../../types/bot/workflow/runtime.ts';
|
||||
import {validate_workflow} from './validate.ts';
|
||||
import {assert_json} from './value.ts';
|
||||
import {resolve_fields} from './template.ts';
|
||||
import {node_registry, start_registry} from '../registry.ts';
|
||||
import {node_fields, policy_fields} from '../common_template.ts';
|
||||
import type {Json} from '../../../types/bot/workflow/workflow.ts';
|
||||
|
||||
export function compile_workflow(value: unknown): CompiledWorkflow {
|
||||
assert_json(value);
|
||||
const normalized = structuredClone(value) as Record<string, any>;
|
||||
if (normalized && !Array.isArray(normalized) && typeof normalized === 'object') {
|
||||
const start = normalized.start;
|
||||
if (start && typeof start.type === 'string' && Object.hasOwn(start_registry, start.type)) {
|
||||
const module = start_registry[start.type as keyof typeof start_registry];
|
||||
normalized.start = resolve_fields(module.config_template.config, start);
|
||||
}
|
||||
if (normalized.policy !== null && (normalized.policy === undefined || typeof normalized.policy === 'object' && !Array.isArray(normalized.policy))) {
|
||||
normalized.policy = resolve_fields(policy_fields, normalized.policy ?? {});
|
||||
}
|
||||
if (Array.isArray(normalized.nodes)) normalized.nodes = normalized.nodes.map((node: Record<string, any>) => {
|
||||
if (!node || typeof node !== 'object' || Array.isArray(node) || typeof node.type !== 'string' || !Object.hasOwn(node_registry, node.type)) return node;
|
||||
const fields = node_registry[node.type as keyof typeof node_registry].config_template.config;
|
||||
const result = resolve_fields(node_fields, node as Record<string, Json>);
|
||||
if (node.config && typeof node.config === 'object' && !Array.isArray(node.config)) result.config = resolve_fields(fields, node.config);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
validate_workflow(normalized);
|
||||
const definition = normalized;
|
||||
const graph: CompiledWorkflow = {
|
||||
definition,
|
||||
nodes: new Map(definition.nodes.map(node => [node.id, node])),
|
||||
outgoing: new Map(), incoming: new Map(), join_owners: new Map(),
|
||||
};
|
||||
for (const edge of definition.edges) {
|
||||
graph.outgoing.set(edge.source, [...graph.outgoing.get(edge.source) ?? [], edge]);
|
||||
graph.incoming.set(edge.target, [...graph.incoming.get(edge.target) ?? [], edge]);
|
||||
}
|
||||
graph.join_owners = find_join_owners(graph);
|
||||
return graph;
|
||||
}
|
||||
|
||||
/** The nearest dominating fork owns each join, including joins downstream of other joins. */
|
||||
export function find_join_owners(graph: CompiledWorkflow): Map<string, string> {
|
||||
const ids = [...graph.nodes.keys()];
|
||||
const dominators = new Map(ids.map(id => [id, new Set(id === graph.definition.entry ? [id] : ids)]));
|
||||
let changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (const id of ids) {
|
||||
if (id === graph.definition.entry) continue;
|
||||
const predecessors = (graph.incoming.get(id) ?? []).map(edge => edge.source);
|
||||
const common = new Set(dominators.get(predecessors[0]) ?? []);
|
||||
for (const predecessor of predecessors.slice(1)) {
|
||||
for (const candidate of common) if (!dominators.get(predecessor)!.has(candidate)) common.delete(candidate);
|
||||
}
|
||||
common.add(id);
|
||||
const previous = dominators.get(id)!;
|
||||
if (common.size !== previous.size || [...common].some(item => !previous.has(item))) {
|
||||
dominators.set(id, common); changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
const owners = new Map<string, string>();
|
||||
for (const node of graph.nodes.values()) {
|
||||
if (node.type !== 'merge' || node.config.mode !== 'all') continue;
|
||||
const candidates = [...dominators.get(node.id)!].filter(id => id !== node.id && (graph.outgoing.get(id)?.length ?? 0) > 1);
|
||||
candidates.sort((a, b) => dominators.get(b)!.size - dominators.get(a)!.size);
|
||||
if (candidates[0]) owners.set(node.id, candidates[0]);
|
||||
}
|
||||
return owners;
|
||||
}
|
||||
Reference in New Issue
Block a user