<p align="center">
  <a href="https://getworkbench.dev">
    <img src="https://raw.githubusercontent.com/pontusab/workbench/main/hero.png" alt="Workbench — the missing dashboard for BullMQ" />
  </a>
</p>

# @getworkbench/core

Core of [Workbench](https://getworkbench.dev) — `QueueManager`, API router, and bundled React UI.

This package is framework-agnostic. You typically don't depend on it directly — use a framework adapter instead:

| Adapter | Package |
| ------- | ------- |
| Hono | [`@getworkbench/hono`](https://npm.im/@getworkbench/hono) |
| Elysia | [`@getworkbench/elysia`](https://npm.im/@getworkbench/elysia) |
| Express | [`@getworkbench/express`](https://npm.im/@getworkbench/express) |
| Fastify | [`@getworkbench/fastify`](https://npm.im/@getworkbench/fastify) |
| NestJS | [`@getworkbench/nestjs`](https://npm.im/@getworkbench/nestjs) |
| AdonisJS | [`@getworkbench/adonis`](https://npm.im/@getworkbench/adonis) |
| Next.js | [`@getworkbench/next`](https://npm.im/@getworkbench/next) |
| TanStack Start | [`@getworkbench/tanstack-start`](https://npm.im/@getworkbench/tanstack-start) |
| Koa | [`@getworkbench/koa`](https://npm.im/@getworkbench/koa) |
| Astro | [`@getworkbench/astro`](https://npm.im/@getworkbench/astro) |
| Nuxt | [`@getworkbench/nuxt`](https://npm.im/@getworkbench/nuxt) |
| h3 | [`@getworkbench/h3`](https://npm.im/@getworkbench/h3) |
| Bun.serve | [`@getworkbench/bun`](https://npm.im/@getworkbench/bun) |

Or scaffold with the CLI:

```bash
npx @getworkbench/cli init
```

## What's inside

- `WorkbenchCore` — orchestrates queues, flows, schedulers, metrics, and search.
- `QueueManager` — wraps your BullMQ `Queue` instances and powers list / detail / mutation endpoints.
- **Overview dashboard** — fleet KPI cards, 24h throughput chart, attention alerts, and per-queue health cards (default home at `/`).
- **Redis auto-discovery** — pass a `redis` connection instead of explicit `Queue[]` instances; scans BullMQ keys via `discoverQueues()` and `WorkbenchCore.fromOptions()`.
- HTTP-agnostic API router — handles `/api/*` requests for the dashboard.
- Bundled UI — pre-built React app served as a single HTML entrypoint.

## Migrating from bull-board?

Workbench is the modern alternative to `@bull-board/api` + `@bull-board/ui`. Same Redis, same queues — swap the mount and keep your workers unchanged. See the [full comparison](https://getworkbench.dev/blog/workbench-vs-bull-board) or run `npx @getworkbench/cli init`.

## Install

```bash
npm i @getworkbench/core bullmq ioredis
```

## Direct usage (advanced)

Most users should reach for an adapter. If you need to wire Workbench into an unsupported framework, use one of two integration paths:

**Fetch-native runtimes** (Next.js, Elysia, Bun.serve, Adonis, …) — `createFetchHandler` returns a web-standard `(req: Request) => Response` handler:

```ts
import { Queue } from "bullmq";
import { createFetchHandler } from "@getworkbench/core";

const emailQueue = new Queue("email", {
  connection: { url: process.env.REDIS_URL! },
});

const { fetch } = createFetchHandler({
  queues: [emailQueue],
  basePath: "/jobs",
  auth: {
    username: process.env.WORKBENCH_USER!,
    password: process.env.WORKBENCH_PASS!,
  },
});

// Wire `fetch` into your framework's request handler.
```

**Node HTTP frameworks** (Express, Fastify, …) — use `buildRouteTable` + `renderIndexHtml` / `serveStaticAsset` directly. See `@getworkbench/express` or `@getworkbench/fastify` for the pattern.

**Auto-discovery** — omit `queues` and pass `redis` instead:

```ts
import { WorkbenchCore } from "@getworkbench/core";

const core = await WorkbenchCore.fromOptions({
  redis: process.env.REDIS_URL!,
  prefix: "bull", // optional, default "bull"
  maxQueues: 100, // optional cap for large deployments
});
```

## Entry points

| Entry | Purpose |
| ----- | ------- |
| `@getworkbench/core` | Framework-agnostic surface: `WorkbenchCore`, `discoverQueues`, `buildRouteTable`, `createFetchHandler`, types, basic auth + static helpers. |
| `@getworkbench/core/hono` | `buildWorkbenchApp`, `buildWorkbenchApiApp`, `createApiRoutes` — anything that returns a `Hono` instance. |
| `@getworkbench/core/ui` | React `Dashboard` component for embedding the dashboard inside a host Vite/React app. Also exports `createAppRouter`, `setApiBase`, and `getConfigUrl`. |
| `@getworkbench/core/ui/styles.css` | Tailwind-generated stylesheet that ships with the dashboard. |

The Hono-typed helpers live on a dedicated subpath so non-Hono adapters (Express, Fastify, NestJS, Next.js, Elysia) don't drag `hono`'s `.d.ts` into the consumer's TypeScript program. That `.d.ts` uses TypeScript 5.0 syntax (`const` type parameters) and isolating it lets TS 4.x users keep building.

## Options

Shared by every adapter and by `createFetchHandler` / `WorkbenchCore`:

| Option | Type | Description |
| ------ | ---- | ----------- |
| `queues` | `Queue[]` | BullMQ `Queue` instances to display. Required unless `redis` is set. |
| `redis` | `string \| RedisOptions` | Redis connection for auto-discovery when `queues` is omitted. |
| `prefix` | `string` | BullMQ key prefix used during auto-discovery. Default: `"bull"`. |
| `maxQueues` | `number` | Cap on discovered queues for large deployments. Default: `100`. |
| `auth` | `{ username, password }` | Basic auth credentials. Strongly recommended in prod. |
| `title` | `string` | Dashboard title. Default: `"Workbench"`. |
| `logo` | `string` | Logo URL to display in the nav. |
| `basePath` | `string` | Override base path detection. Required for some adapters (Elysia, Koa, Next.js, TanStack Start, Astro, Nuxt, h3, Adonis). |
| `readonly` | `boolean` | Disable actions (retry, remove, promote). |
| `tags` | `string[]` | Fields from `job.data` to extract as filterable tags. |
| `alerts` | `AlertsOptions` | Self-hosted alerting via BullMQ `QueueEvents`, Slack/Discord/webhook contact points, and dashboard-managed rules. |

## Alerts

Alerts are enabled by default. Configure contact points and rules from the dashboard **Alerts** page — nothing is sent until you add destinations and matching rules.

Optional seed data and settings in code:

```ts
workbench({
  queues: [emailQueue],
  basePath: "/jobs",
  alerts: {
    dashboardUrl: "https://jobs.example.com/jobs",
    contactPoints: [
      {
        id: "slack-prod",
        name: "Slack #ops",
        preset: "slack",
        url: process.env.SLACK_WEBHOOK_URL!,
        enabled: true,
        createdAt: Date.now(),
        updatedAt: Date.now(),
      },
    ],
    rules: [
      {
        id: "failed-jobs",
        name: "Job failed",
        enabled: true,
        trigger: "job_failed",
        severity: "warning",
        contactPointIds: ["slack-prod"],
        createdAt: Date.now(),
        updatedAt: Date.now(),
      },
    ],
  },
});
```

Supported triggers: `job_failed`, `job_stalled`, `retries_exhausted`, `failed_backlog`, `no_workers_with_backlog`.

Dashboard-managed config is **persisted in Redis** at `{prefix}:workbench:alerts:*` using the same connection as your queues. Code-defined `contactPoints` / `rules` seed Redis once when the store is empty.

Set `alerts.persistence: "memory"` to keep config in-process only (resets on restart). Set `alerts.enabled: false` to disable alerting entirely.

### How this compares

| Tool | Alert setup |
| --- | --- |
| **Bull Board** | No built-in alerting — you wire Slack/webhooks in worker code |
| **Sidekiq** | Code hooks (`death_handlers`) in your initializer |
| **Grafana** | UI contact points + alert rules + optional routing tree |
| **Workbench** | Grafana-style **contact points** and **rules** in the dashboard; event-driven via BullMQ `QueueEvents` |

### Slack setup

1. Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps)
2. Enable **Incoming Webhooks** → **Add New Webhook to Workspace** → pick a channel
3. Copy the webhook URL (`https://hooks.slack.com/services/...`)
4. In Workbench **Alerts** → **Add contact point** → preset **Slack** → paste URL → **Test**

Slack preset sends Block Kit messages with queue, job, and severity details. Set `alerts.dashboardUrl` so notifications include an **Open in Workbench** link (HTTPS links render as buttons; HTTP dev URLs use a text link). Generic webhook preset sends a stable JSON payload for automation.

## Requirements

- Node 18+ (or Bun 1.1+ for the Elysia and Bun.serve adapters)
- TypeScript 4.x or 5.x for the main entry. **TypeScript 5.0+ required if you import from `@getworkbench/core/hono`** — Hono 4's bundled `.d.ts` uses TS 5.0 syntax (`const` type parameters).

## Documentation

**https://getworkbench.dev/docs/guides/configuration** — full API reference.

[getworkbench.dev](https://getworkbench.dev/docs) · [GitHub](https://github.com/pontusab/workbench)

## License

MIT
