Task-oriented programming DSL with lazy evaluation and execution strategies.
- π³ Task Tree Structure: Build complex workflows as composable AST nodes
- π Lazy Evaluation: Separate program structure from execution
- π― Pluggable Strategies: Customizable execution strategies (retry, timeout, etc.)
- β‘ Parallel Execution: Run tasks concurrently with
parallel() - π Sequential Execution: Chain tasks with
sequence() - π¦ Universal Support: Works in Node.js and browsers (ESM & CJS)
npm install @on-the-ground/task-tree-jsimport {
leaf,
sequence,
parallel,
leafChainOf,
} from "@on-the-ground/task-tree-js";
// Define tasks
const workflow = sequence("main", [
leaf("task1", async (input: string) => input + " -> processed"),
parallel("parallel-tasks", [
leaf("task2a", async (input: string) => input + " -> A"),
leaf("task2b", async (input: string) => input + " -> B"),
]),
leaf("task3", async (input: object) => JSON.stringify(input)),
]);
// Compile to executable function
const program = leafChainOf(workflow);
// Execute
const result = await program("start");
console.log(result);import { retry } from "@on-the-ground/task-tree-js/sample-strategy";
const taskWithRetry = sequence(
"api-call",
[
leaf("fetch-data", async (url: string) => {
const response = await fetch(url);
if (!response.ok) throw new Error("Failed");
return response.json();
}),
],
retry(3) // Retry up to 3 times
);
const program = leafChainOf(taskWithRetry);
const data = await program("https://api.example.com");import { timeout } from "@on-the-ground/task-tree-js/sample-strategy";
const taskWithTimeout = sequence(
"slow-operation",
[
leaf("process", async (data: string) => {
// Long running operation
return processData(data);
}),
],
timeout(5000) // 5 seconds
);
const program = leafChainOf(taskWithTimeout);
const result = await program("input-data");import { retry } from "@on-the-ground/task-tree-js/sample-strategy";
const complexWorkflow = sequence("main", [
leaf("step1", async (i: string) => i + "-step1"),
sequence(
"retry-section",
[
leaf("risky-operation", async (i: string) => {
// May fail, will retry
return riskyCall(i);
}),
],
retry(3) // Only this section will retry
),
leaf("step3", async (i: string) => i + "-step3"),
]);
const program = leafChainOf(complexWorkflow);
const result = await program("input");Create a leaf task node.
name: Task identifiertask: Async function(input: I) => Promise<O>
Execute tasks sequentially, passing output to next input.
name: Sequence identifierchildren: Array of TaskNodestrategy: Optional execution strategy
Execute tasks concurrently with same input.
name: Parallel identifierchildren: Array of TaskNodestrategy: Optional execution strategy
Compile task tree into an executable function.
Returns (input: I) => Promise<O> function.
Strategies are higher-order functions that wrap task execution with additional behavior.
Import from @on-the-ground/task-tree-js/sample-strategy:
Automatically retry failed tasks.
import { retry } from "@on-the-ground/task-tree-js/sample-strategy";
sequence("task", [...tasks], retry(3));Cancel tasks exceeding duration (milliseconds).
import { timeout } from "@on-the-ground/task-tree-js/sample-strategy";
sequence("task", [...tasks], timeout(5000));The Strategy type is a higher-order function interface:
type Strategy = <I, O>(
prevPromise: Promise<I>,
leaf: LeafNode<I, O>
) => Promise<O>;prevPromise: The promise from the previous task in the chainleaf: The current leaf node containing the task to execute- Returns: A promise of the output type
Create your own strategies by implementing this interface:
import { Strategy, LeafNode } from "@on-the-ground/task-tree-js";
const myStrategy: Strategy = async <I, O>(
prevPromise: Promise<I>,
leaf: LeafNode<I, O>
): Promise<O> => {
return prevPromise.then(async (input) => {
// Add custom logic: logging, caching, error handling, etc.
console.log(`Executing: ${leaf.name}`);
const result = await leaf.task(input);
console.log(`Completed: ${leaf.name}`);
return result;
});
};This library implements:
- Code as Data: Tasks are AST nodes that can be transformed
- BFS Flattening: Converts tree structure to linear execution chain
- Strategy Pattern: Execution behaviors as metadata
- Lazy Evaluation: Build once, execute multiple times
MIT