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

@gomani/jobs

v0.14.0

Published

A durable job queue, workers, and cron for splitting workloads by failure/resource profile — a crashing worker backs jobs up while the web process keeps serving.

Readme

@gomani/jobs

A durable job queue, workers, and cron. Split your workloads by their failure and resource profile: the latency-sensitive web process only enqueues (cheap); a separate worker process drains. A durable queue sits between them — so a slow or crashing worker just backs jobs up in the store while requests keep being served, and the worker picks them up on restart. That is the real "one thing dies, the rest survive" win — from workload isolation, not one-service-per-domain.

import { createJobRunner, memoryStore, fileStore } from '@gomani/jobs';

const runner = createJobRunner({
  store: fileStore('.goma/queue.json'), // local-default; survives a restart
  jobs: [
    {
      name: 'transcode',
      concurrency: 2, // at most 2 in flight
      maxAttempts: 5, // then dead-letter
      backoff: { baseMs: 1000 }, // exponential + jitter
      handler: async (payload) => {
        await doHeavyWork(payload);
      },
    },
  ],
});

// From a request handler / loader (latency-safe — just a write):
await runner.enqueue('transcode', { fileId }, { idempotencyKey: `transcode:${fileId}` });

// In the worker process (`gomani worker` runs this):
await runner.work({ signal }); // drains forever, honoring per-job concurrency

What it guarantees

  • At-least-once + idempotency. A job runs until it succeeds; enqueue with an idempotencyKey dedupes while a job with that key is pending. A worker that crashes mid-run leaves the job in the store; on restart, jobs left active are reset to queued and run again.
  • Retry with backoff → dead-letter. A failing handler is retried with exponential backoff (reusing @gomani/resilience) up to maxAttempts, then moved to dead for inspection — a poison job never spins forever.
  • Per-job concurrency. The worker loop runs at most concurrency of each job at once, so one heavy job can't starve the others.

Cron

Scheduled work flows through the same durable pipeline (so a missed cron run is retried, and nothing runs "outside" the system):

import { defineCron, cronsToJobs, createJobRunner, createCronScheduler } from '@gomani/jobs';

const nightly = defineCron('royalties', '0 2 * * *', async () => {
  await runRoyalties();
});

const runner = createJobRunner({ store, jobs: cronsToJobs([nightly]) });
const scheduler = createCronScheduler({ crons: [nightly], runner });
await scheduler.run({ signal }); // ticks each minute; enqueues a job when a schedule is due

cronMatches(schedule, date) implements the standard 5-field syntax (minute hour day-of-month month day-of-week) with *, */n, a-b, a-b/n, and comma lists (and the Vixie day-of-month/day-of-week OR).

Stores (local-default, pluggable)

  • memoryStore() — dev + tests.
  • fileStore(path) — single-box; persists to JSON, survives a restart. Single-writer.
  • Your own — a Postgres/Redis QueueStore is a drop-in for the multi-process / split-service topologies where several workers claim concurrently. The interface is the seam — the same local-default-then-real-backend pattern as the payment providers.

Every clock/id source is injectable (now, newId), so the whole engine is deterministically testable.

Topology — the same code, single / multi / split

An app declares its worker in app/worker.ts (default export: a WorkerApp = { store, jobs, crons }). The web side imports it to enqueue; the worker side drains it. Where the worker runs is a config choice in gomani.topology.json, not a code change:

{ "mode": "single" } // single | multi | split
  • single (default) — web + workers + cron in one process. gomani dev runs the worker in-process; a small deploy needs nothing more.
  • multi — a web process + a separate gomani worker process on one box, coordinated by the durable queue (a fileStore works — both re-read the same file).
  • split — web and workers as separate deployables against a shared queue/DB (a real QueueStore adapter) — "if you outgrow the box".
gomani dev       # single: serves + drains + cron, one process
gomani worker    # a standalone worker process (multi/split)

runWorker(app, { signal }) is the worker entry (drain loop + cron scheduler); webRunsWorker(topology) and resolveTopology(config) are the deploy-time helpers. Constraint-first: single is the default; distribution is opt-in, escalated deliberately — the same shape as @gomani/native's PWA-first.