npm.io
1.1.1 • Published 11h ago

@drej/workflow

Licence
Version
1.1.1
Deps
1
Size
20 kB
Vulns
0
Weekly
0

@drej/workflow

Lazy pipeline builder for drej — retry, conditional branching, fan-out, and parallel sandboxes, all flushed in one await.

bun add @drej/workflow

Full documentation →


How it works

workflow(client).sandbox(opts, fn) returns a builder. The fn callback receives a SandboxBuilder — all methods on it queue operations synchronously. Nothing runs until you await .pipe() or .result().

import { workflow } from "@drej/workflow";

await workflow(client)
  .sandbox({ image: "node:20-slim", resources: { cpu: "1", memory: "512Mi" } }, (sb) => {
    sb.exec("npm ci");
    sb.exec("npm run build");
    sb.exec("npm test");
  })
  .pipe(process.stdout);

Retry

sb.retry(
  3,
  (r) => {
    r.exec("flaky-network-call");
  },
  { delayMs: 500, backoff: "exponential" },
);

Conditional branching

The predicate receives { stdout, exitCode, vars } from the previous step.

sb.exec("test -f /app/build.sh", { strict: false });
sb.when(
  (ctx) => ctx.exitCode === 0,
  (s) => s.exec("bash /app/build.sh"),
  (s) => s.exec("echo 'no build script'"),
);

Fan-out

sb.forEach(
  ["a.ts", "b.ts", "c.ts"],
  (s, file) => {
    s.exec(`tsc --noEmit ${file}`);
  },
  { concurrency: 4 },
);

Capture a value

sb.readFile(path, as) stores file contents in vars[as] on the result object.

const { vars } = await workflow(client)
  .sandbox({ image: "node:20-slim", resources: { cpu: "500m", memory: "256Mi" } }, (sb) => {
    sb.exec(`node -e "require('fs').writeFileSync('/out.txt', process.version)"`);
    sb.readFile("/out.txt", "nodeVersion");
  })
  .result();

console.log(vars.nodeVersion); // "v20.x.x"

Parallel sandboxes

Run the same builder across multiple configs simultaneously:

await workflow(client)
  .parallel(
    [
      { image: "node:18-slim", resources: { cpu: "500m", memory: "256Mi" } },
      { image: "node:20-slim", resources: { cpu: "500m", memory: "256Mi" } },
      { image: "node:22-slim", resources: { cpu: "500m", memory: "256Mi" } },
    ],
    (sb) => {
      sb.exec("node --version");
      sb.exec("npm ci && npm test");
    },
  )
  .pipe(process.stdout);

Sequential pipeline

Pass work across sandboxes in order, each starting fresh:

await workflow(client)
  .sequence([
    {
      image: "node:20-slim",
      resources: { cpu: "1", memory: "512Mi" },
      run: (sb) => {
        sb.exec("npm ci");
        sb.exec("npm run build");
      },
    },
    {
      image: "alpine:3",
      resources: { cpu: "500m", memory: "256Mi" },
      run: (sb) => {
        sb.exec("ls /app/dist");
      },
    },
  ])
  .pipe(process.stdout);

License

Apache 2.0