npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@toist/aja

v0.10.5

Published

Durable Toist host surface: SQLite runtime, HTTP runner, UI mount, filesystem helpers, and the typed runner client.

Readme

@toist/aja

Durable Toist host surface: SQLite runtime, HTTP runner, UI mount, filesystem helpers, and the typed runner client.

import { startRunner } from "@toist/aja"

await startRunner({
  rootDir: import.meta.dir,
  port: 3000,
})

Main exports

  • startRunner(options)
  • createSqliteRuntime(options)
  • createFilesystemPipelineStore({ rootDir, watch? })
  • loadFilesystemResources(rootDir)
  • createRunnerClient({ baseUrl })
  • convenience re-exports from @toist/core

Client quickstart

import { createRunnerClient } from "@toist/aja"

const client = createRunnerClient({ baseUrl: "http://localhost:3000" })
const started = await client.pipelines.runSpec(spec, { name: "world" })
for await (const event of client.runs.events(started.id)) {
  console.log(event.type)
}

Runtime layout

With startRunner({ rootDir, port }), the runner:

  • loads pipelines from <rootDir>/pipelines
  • stores runtime state under <rootDir>/data
  • reads resources from <rootDir>/toist.yml
  • serves UI + API from one Bun process

/manifest

GET /api/manifest returns stable JSON for tooling:

{
  "version": 1,
  "schema": "https://toist.in/schemas/manifest-v1.json",
  "kinds": []
}

Resources

loadFilesystemResources(rootDir) reads toist.yml and resolves ${env:VAR} and ${env:VAR:-fallback} substitutions.

Scheduling

A pipeline's schedule: field (a 5-field POSIX cron string) is a declaration, not a binding to any particular firing mechanism. Three paths can deliver scheduled runs; pick the one that matches your deployment.

Recommended: cron + CLI + memory runner

For idempotent pipelines whose effects land in external systems (filesystem, HTTP APIs, Nalich, your DB of choice), the simplest path is your OS scheduler plus a one-shot CLI invocation:

*/5 * * * * cd /path/to/instance && bunx @toist/in run pipelines/digest.yaml --ephemeral

Each tick spawns a fresh bun process, loads the YAML, runs the pipeline in a memory-backed runtime, and exits. No long-running runner. No SQLite lock. No daemon to babysit. The same schedule: value lives in the pipeline YAML — the cron entry is generated from it (see toist schedule export).

Pros: familiar tooling, robust to runner crashes (cron just retries on its next tick), zero coordination state to corrupt. Limits: no durable run ledger, no admin-UI visibility, HITL pipelines that suspend mid-run can't resume across ticks.

Embedded scheduler

Useful when you specifically need:

  • HITL pipelines that suspend on human.input and must resume durably from a persisted ledger
  • Admin UI visibility of every scheduled run alongside manual launches
  • HTTP API surface (other systems calling /api/pipelines/:id/run) co-located with scheduling

When the runner boots with the durable SQLite runtime, the embedded scheduler scans pipelines for schedule: fields, ticks every 60 s, and fires runs with trigger="scheduled". Best-effort: it fires when the runner is alive and skips occurrences when it is not. There is no missed-run replay, no catch-up window, and no spec snapshotting. When a previous run is pending, running, or suspended, the next cron occurrence is silently skipped, unless the pipeline opts in with concurrency: parallel:

apiVersion: toist.in/v1
id: weekly-digest
schedule: "0 9 * * 1"
concurrency: parallel   # optional; default is "skip"
nodes:
  - { id: start, kind: start }
  - { id: review, kind: human.input, ... }
  - { id: out, kind: sink, ... }

Pipelines carry references to data (paths, URIs, IDs) — not the data itself — and are reloaded from source at fire time; persisting a snapshot is the data owner's responsibility, not the runner's.

Durable execution (future): Temporal-backed runtime

For exactly-once guarantees, missed-run replay, workflow versioning, complex retry/overlap policies, or multi-host scheduling, run Toist on top of a durable engine such as Temporal. The ToistRuntime interface (runs, tasks, logs, outputs) is designed to be backed by alternative implementations; a Temporal adapter would map runs to workflows, HITL tasks to signals, and the pipeline's schedule field to a native Temporal Schedule. No adapter ships in this package yet — when concrete demand appears, this is the seam.

HITL with cron

HITL pipelines can use the same cron-driven deployment style, but they must use a durable local ledger so the suspend → resume cycle survives process exit. Do not pass --ephemeral for these pipelines.

Minimal scheduled pipeline:

apiVersion: "toist.in/v1"
id: approval-digest
schedule: "*/5 * * * *"
nodes:
  - id: start
    kind: start
  - id: review
    kind: human.input
    dependsOn: [start]
    notify: ["console"]
    params:
      prompt: Approve this digest?
      schema:
        type: object
        properties:
          approved:
            type: boolean
  - id: out
    kind: sink
    dependsOn: [review]
    input:
      value: { expr: "ctx.results.review" }

Install a cron entry without --ephemeral:

*/5 * * * * cd /path/to/instance && bunx @toist/in run pipelines/approval-digest.yaml

When cron fires, the CLI starts the pipeline, creates a durable task, suspends the run, sends the configured notification, and exits. The console notifier prints a resume hint like:

toist tasks answer 42 --response '<json>'

List open tasks from the same environment:

toist tasks list

Respond with JSON when the human decision is ready:

toist tasks answer 42 --response '{"approved": true}'

The embedded scheduler can run the same pipeline; only the firing mechanism differs. In both cases the run must use durable state. HITL pipelines fail loudly when run with --ephemeral; the CLI error names the suspending nodes and points back to this section.

Run status

The runs ledger uses workflow-engine standard status values:

  • pending — run row created, not yet executing
  • running — currently executing
  • suspended — paused at a HITL task or error review
  • succeeded — completed successfully
  • failed — terminated with an error

When to use @toist/aja

Use it when you want a durable single-tenant runner with HTTP APIs. For in-process execution without the server, import @toist/core directly.