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

@desplega.ai/slack-mock

v0.4.0

Published

Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots

Readme

slack-mock

A mock Slack server for end-to-end testing Slack bots. It speaks the Slack Web API and Socket Mode, so a real @slack/bolt app connects to it unchanged (only clientOptions.slackApiUrl differs). Messages, threads, reactions, files, modals and assistant threads are stored, journaled as JSONL, and rendered as Slack-looking HTML for screenshots.

Built for the agent-swarm Slack integration. Bun + TypeScript, no runtime dependencies.

Install

bun add -d @desplega.ai/slack-mock          # from npm (once published)
bun add -d github:desplega-ai/slack-mock    # or straight from the repo (Bun runs the TypeScript sources)

Quick start (from a checkout)

bun install
bun run start                 # slack-mock serve --port 4040
bun run demo                  # same, seeded with threads, blocks, files, an ephemeral and a DM

Point your bot at it:

SLACK_BOT_TOKEN=xoxb-mock-bot-token \
SLACK_APP_TOKEN=xapp-mock-app-token \
SLACK_API_URL=http://127.0.0.1:4040/api/ \
bun src/http.ts               # agent-swarm reads SLACK_API_URL into clientOptions.slackApiUrl

Talk to the bot as a human and watch the thread:

curl -X POST http://127.0.0.1:4040/mock/messages -H 'content-type: application/json' \
  -d '{"channel":"general","user":"alice","text":"<@U0BOT00000> hello"}'
open http://127.0.0.1:4040/c/general            # live view: new messages arrive over SSE and flash (?live=0 turns it off, ?refresh=2 polls instead)
bun src/cli.ts screenshot http://127.0.0.1:4040/c/general --out general.png

UI

Use the avatar and name chip to choose who posts from every composer. The browser remembers your choice across channels and threads. Open /c/support?as=taras to select Taras. The page removes as from the URL after selection. Public workspaces show the chip after presenter sign-in. Optional user status text appears beside names in the users table and the chip's user list. Each message offers reaction and thread icons, including messages without replies. The thread icon opens the thread without focusing the composer. Icons stay visible on phones and touch screens. Desktop shows them on hover or keyboard focus. Choose an emoji from the picker beside the message, or select an existing reaction to add or remove your own. Reactions use the identity in the chip and require presenter sign-in in public workspaces. Below 700px, the sidebar becomes a top bar with a channel switcher. Threads fill the phone width. The composer follows the visible viewport and includes padding for the iOS home bar. Capture the phone layout with bun test test/phone-ui.test.ts (requires Chrome). The PNG is saved to test/artifacts/phone-support-thread.png at 390 x 844.

In tests

import { App } from "@slack/bolt";
import { SlackMock } from "@desplega.ai/slack-mock";

const slack = await SlackMock.start({ port: 0, manifest: "slack-manifest.json" });
const app = new App({
  token: slack.env.SLACK_BOT_TOKEN,
  appToken: slack.env.SLACK_APP_TOKEN,
  socketMode: true,
  clientOptions: { slackApiUrl: slack.env.SLACK_API_URL },
});
await app.start();
await slack.waitForConnection();

const ask = await slack.postMessage({ channel: "general", user: "alice", text: `<@${slack.bot.userId}> hi` });
const reply = await slack.waitForMessage({ channel: "general", thread_ts: ask.ts, from: "bot" });

What tests can do:

| humans do | postMessage (text, thread, files), editMessage, deleteMessage, addReaction, removeReaction, slashCommand, clickButton, submitView, startAssistantThread, changeAssistantContext, addUser, addChannel, openDm, invite | |---|---| | observe | messages, thread, ephemeralMessages, findMessages, waitForMessage, apiCalls, waitForApiCall, deliveries (every envelope and its ack), assistantThread | | break things | injectFault({ method, error, httpStatus, retryAfterSec, extra }), disconnectSockets("refresh_requested"), options ackTimeoutMs, maxRetries, triggerIdTtlMs, echoBotMessages, subscribedEvents |

The same operations exist over HTTP under /mock/* for use from another process (see docs/design.md).

Frames from a journal

A run recorded with dataFile can be replayed into one PNG per event that touched a thread (or, without a thread, a channel): the view after each message.add, message.update, message.delete, reaction.add and reaction.remove line, in journal order, plus final-thread.png (a copy of the last frame) and final-desktop.png (the full UI with the thread panel, 1280x900). Nothing is served: the journal is replayed in memory and each page is screenshotted from a temp file by the same headless Chrome helper as screenshot. A file.add line carries no message, so a shared file first appears in the frame of the message that shares it.

slack-mock frames --journal run.jsonl --channel C0GENERAL0 --thread 1788518410.672000 --out ./frames
#  5	message.add	/abs/frames/01-message.add.png
#  6	reaction.add	/abs/frames/02-reaction.add.png
#  ...
#  final-thread=/abs/frames/final-thread.png
#  final-desktop=/abs/frames/final-desktop.png

Leave out --thread for the channel view. --manifest app.json (bot display name), --width, --height and --no-desktop are optional. The same from code:

import { frames } from "@desplega.ai/slack-mock";

const result = await frames({ journal: "run.jsonl", channel: "general", thread: ask.ts, out: "./frames" });
// result.frames[1] = { index: 6, kind: "reaction.add", path: "/abs/frames/02-reaction.add.png" }
// result.finalThread, result.finalDesktop

Stitch the PNGs into a GIF with ffmpeg if you want motion; the mock only produces frames.

Slack behaviour that is modelled

  • Socket Mode: apps.connections.open, hello, envelopes for events_api, slash_commands and interactive, acks with response payloads, redelivery with retry_attempt when an envelope is not acked, server pings, disconnect messages, reconnects.
  • Events: message (channels, groups, im), app_mention, message_changed, message_deleted, file_share, reaction_added/removed, member_joined_channel, assistant_thread_started, assistant_thread_context_changed. The bot's own messages are echoed back like Slack does (Bolt's ignoreSelf drops them).
  • Web API: auth, chat (post, update, delete, ephemeral, permalink, streams), conversations (list, info, create, join, invite, archive, history, replies, members, open), users (info, list, lookupByEmail), reactions, files (info, upload v2 flow, download with bearer token), views (open, update, push), assistant.threads (status, title, prompts), pins, bots, team. Errors come back as { ok: false, error } with the real codes (not_in_channel, already_reacted, message_not_found, name_taken, expired_trigger_id, message_not_in_streaming_state, ...).
  • Slash command and interactive response_urls are served by the mock (ephemeral / in_channel, replace_original, delete_original).

Commands

bun test                      # unit and integration tests (real Bolt client)
bun run test:e2e              # boots ../agent-swarm against the mock (AGENT_SWARM_REPO to override; needs agent-swarm PR #1310)
bun run typecheck
bun run lint
bun run build                 # dist/ (JS for Bun + .d.ts). CI publishes to npm when the version in package.json changes on main

Hosted demo

https://demo.slack-mock.dev runs scripts/demo-server.ts: the mock plus a small demo bot (scripts/demo-bot.ts) in one container, deployed to Fly.io from main by .github/workflows/deploy.yml (fly.toml, Dockerfile). State lives in a JSONL journal on a volume and resets daily. Run it yourself:

docker build -t slack-mock-demo . && docker run -p 8080:8080 -e ADMIN_AUTH=demo:secret slack-mock-demo

PUBLIC_URL, ADMIN_AUTH (basic auth for the UI and /mock/*), DATA_FILE, RESET_EVERY_HOURS and MANIFEST configure it. The same knobs exist on the CLI (--public-url, --auth) and as SlackMockOptions (publicUrl, adminAuth).

The same image also serves a workspace for an external bot, the Agent Swarm demo at https://swarm-demo.slack-mock.dev (fly.swarm.toml, app slack-mock-swarm). Knobs that make that possible, all read by scripts/demo-server.ts:

| Env | Effect | |---|---| | DEMO_BOT=off | Skip the built-in demo bot; some other Bolt app connects over Socket Mode. | | SLACK_MOCK_BOT_TOKEN, SLACK_MOCK_APP_TOKEN | The tokens the app must present (botToken / appToken options). The defaults are public, so set these on any shared host. | | APP_NAME | Bot display name (appName). | | SEED_FILE | JSON with users, channels, messages and driver.prompts (scripts/seed.ts); replaces the built-in #general and is applied whenever the journal has no channels, so the workspace comes back after every reset. seeds/agent-swarm-demo.json is the shipped example. | | UI_PUBLIC=true | Keep / and /c/... readable without credentials while ADMIN_AUTH still gates /mock/* (publicUi option). The composer then shows a sign-in instead of the browser's basic-auth prompt. | | PRESENTER_AUTH | user:password that permits POST /mock/messages, POST /mock/reactions, and GET /mock/presenter (presenterAuth option). A presenter can post messages and manage reactions without the admin credential. Opening /c/<channel>#presenter=user:password signs that browser in. | | DRIVER_INTERVAL_MINUTES | Every N minutes post the next driver.prompts entry as that user, mentioning the bot, while an app is connected (scripts/driver.ts). 0 disables it. |

Docs

  • skills/slack-mock/SKILL.md: how to use the package from another repo (agents and humans).
  • docs/design.md: architecture, decisions, status.
  • docs/research/: the research behind it (agent-swarm's inbound/outbound Slack surface, Bolt and socket-mode internals, payload shapes, protocol docs and prior art, e2e conventions, Block Kit surface).