Invokej — for bun.sh

Invokej — for bun.sh

Thank-you for having us back to stroll across your attention span. I'm PeterB and this is Claude, my AI assistant. We're here to help you with your JavaScript automation needs. Let's get started!

We love the python Invoke package. Most every repo we've written has a tasks.py in the root — or we've written a cli tool with Invoke. It's just so simple:

from invoke.tasks import task

@task
def build(ctx):
    ctx.run("bun build")

And you can run it with inv build. You can't do that with javascript in Node or Bun. I've been using bun lately and it's been amazing. It's so fast and efficient. I'm really excited to see what I can do with it.

Wait — who is writing this article?

Anyone would think I work for Apple — worse, that I work for Bun. I don't, I'm just a simple developer.

We're going to need a class to hold our tasks, because javascript only supports decorators on class methods:

export class Tasks{
  /** use echo command line tool */
  async build(ctx){
    await ctx.run("echo build");
  }
}

No decorators — maybe one day we'll use them — and javascript fails to log comments with code, so we have to parse the tasks.js as text to strip out the docs. And in version 0.1.1 we had to learn that functions can be async and sync! But check out how we documented our function so that you can see what is does if you use -h — because who remembers what deploy-staging-with-rollback actually does three months later?

Don't moan — I can hear your inner voice from here — saying how you don't need another make, package script or bin. You're right, that's the last thing we need.

Just save this file in your project as tasks.js and we'll find it. Then install Invokej with bun install -g invokej (also available on npm). Now you can call your function with a simple invj build.

Bun has some great built-in packages

There's sql and s3:

import { S3Client, SQL } from "bun";

export class Tasks {
  /** list public table names */
  async tables(c) {
    const sql = new SQL();
    const response = await sql`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name`;
    console.log(response);
  }

  /** list s3 bucket */
  async bucket(c, bucket, prefix="") {
    console.log(`${bucket} filtered by ${prefix}`);
    const response = await S3Client.list({ prefix }, { bucket });
    console.log(response);
  }
}

Of course, you're right, there is a magic .env file that holds the DATABASE_URL and the AWS credentials. But that's ok — at least we're not going to check it in and get a call from Amazon. (Do they do that still?) Unlike the official AWS SDK, Bun's S3Client needs explicit credential configuration — but that keeps things simple and predictable.

The Real Story

But, seriously, you could write a make file — have fun with another syntax and its whitespace sensitivity — or add a script to the packages.json file — still have to coordinate that. npm scripts are great for simple one-liners, but they get unwieldy fast. Try passing dynamic arguments, or sharing code between scripts, or heaven forbid, writing something with conditional logic. You'll end up with a mess of shell scripting that only works on your machine. So this is simpler — and please, it's not exhaustive, but we try.

Invokej tasks are just JavaScript functions. You get proper parameter handling, async/await, error handling, shared utilities, and all the tooling you're already used to. Plus, the self-documenting aspect means your team (and future you) can actually figure out what each task does without diving into the implementation.

In 0.1.0 we bundled a complete bun — stopped doing that. Now we're just a bin script in a package.json file! So it's been an education. Needless to say Claude knew all the tricks, how to parse the file — hey I had to remind you to use bun — and how to publish a package on npm. That was funny as Bun has to install npm just so I could authenticate. Hey, I have a whole Node hidden somewhere…

Get Started

The moral of the story — just run bun install -g invokej and create yourself a tasks.js and take it for a spin. Have fun!