/yoke

POC syntax for typescript bfx workflow thing

Primary LanguageTypeScript

Yoke

A proof of concept workflow ... thing ... in typescript

Run

npx ts-node main.ts

Deps

I don't know if the way I installed things is "good" or not, so here's a list

  • Node
  • Typescript

Idea

This is similar to Nextflow in that a user defines rules, then writes some code to connect each rule to the next one. This just uses javascript promises for "concurrency" which I haven't fully worked out yet. Bascially it will spawn background processes running docker to do each unit of work, localizing with symlinks, again similar to Nextflow.

The cool thing here is that TypeScripts type system is awesome. Union types and "object types" allow for flexible rules that are still checked by the compiler. Function and class decorators are nice ways of hooking into object instantiations to do "magic" that snakemake and nextflow need a DSL to do.

There is an added bonus here that TypeScript and Rust work very well together via WASM and Rust modules could easily be used and imported into a workflow.

Example Rule

// Object types and union types combine to make the input of TrimFastqInput
type PairedFastq = { r1: string, r2: string };
type TrimFastqParams = { five_prime?: number, three_prime?: number };
type TrimFastqInput = TrimFastqParams & PairedFastq;

class TrimFastq extends Rule<TrimFastqInput, PairedFastq> {
    // The bash command to run
    command(input: TrimFastqInput, output: PairedFastq): string {
        return `
    echo trim-fastq ${input.r1} ${input.five_prime ?? 5} ${input.three_prime ?? 3} --output ${output.r1};
    echo trim-fastq ${input.r2} ${input.five_prime ?? 5} ${input.three_prime ?? 3} --output ${output.r2};
    `}

    // Used to customize output paths / names
    createOutput(): PairedFastq {
        return { r1: this.input.r1, r2: this.input.r2 };
    }
}

await new TrimFastq({r1: "r1.fastq.gz", r2: "r2.fastq.gz"})

TrimFastq has a generic parameter I which we are setting to be TrimFastqInput here. What is super cool about the TypeScript type system is that you can pass any object that looks like TrimFastqInput to TrimFastq. So all you need is an object with the intersection of attributes of TrimFastqParams & PairedFastq. This is not the most exciting example, but the idea is powerful and makes re-useing rules simple AND the compiler can validate that your object types are correct!