slack-notification-library
v0.2.0
Published
Slack webhook notifications and structured file logging for Bun with Proxy + MessageChannel support
Maintainers
Readme
Slack Notification Library
Bun server library for Slack webhook notifications and structured file logging with:
- Named global Slack + file logger instances
- User-defined templates via class inheritance + hardcoded class fields
- Main thread or worker thread mode
- Proxy + MessageChannel — main thread forwards calls/args to worker layers
- Cron scheduler with graceful hard-stop shutdown (Slack)
- Hourly log files:
YYYY-MM-DD_hh.log - TypeScript types + compiled JavaScript output
Install
bun add slack-notification-library
# or
npm install slack-notification-libraryQuick start (main thread)
import {
SlackMessageTemplate,
bootstrap,
createTemplate,
} from 'slack-notification-library';
class AlertTemplate extends SlackMessageTemplate {
icon = ':fire:';
prefix = '[ALERT]';
build(data: Record<string, unknown>) {
return {
text: `${this.prefix} ${this.icon} ${data.message}`,
};
}
}
const slack = await bootstrap({
mode: 'main',
instances: [{ name: 'alerts', webhookUrl: process.env.SLACK_WEBHOOK_URL! }],
variables: { env: 'prod', service: 'my-service' },
schedules: [
{
instance: 'alerts',
template: createTemplate(AlertTemplate),
cron: '0 */5 * * * *',
data: () => ({ message: 'Heartbeat OK' }),
},
],
});
const alert = createTemplate(AlertTemplate);
await slack.alerts.send(alert, { message: 'Server started' });
process.on('SIGTERM', async () => {
await slack.shutdown();
process.exit(0);
});Worker mode (Proxy + MessageChannel)
In worker mode, webhook HTTP runs in a worker thread. The main thread uses a Proxy that forwards method calls and arguments over a MessageChannel.
Export your templates from a module:
// slack-templates.ts
import { SlackMessageTemplate } from 'slack-notification-library';
export class AlertTemplate extends SlackMessageTemplate {
icon = ':fire:';
build(data: Record<string, unknown>) {
return { text: `${this.icon} ${data.message}` };
}
}Bootstrap:
import { bootstrap, createTemplate } from 'slack-notification-library';
import { AlertTemplate } from './slack-templates.js';
const slack = await bootstrap({
mode: 'worker',
templateModule: new URL('./slack-templates.js', import.meta.url).pathname,
instances: [{ name: 'alerts', webhookUrl: process.env.SLACK_WEBHOOK_URL! }],
variables: { env: 'prod' },
});
const alert = createTemplate(AlertTemplate);
// Proxy forwards { instance: 'alerts', method: 'send', templateId, args } to worker
await slack.alerts.send(alert, { message: 'Non-blocking send' });Scheduler lifecycle
- Startup: declare
schedulesinbootstrap()— jobs are registered on every server start - Shutdown: call
await slack.shutdown()or rely onSIGTERM/SIGINThandlers (hard-stop, no drain)
Error handling
Webhook and file write failures are log-only — they do not throw.
File logger
Same pattern as Slack: bootstrap once, named global instances, user-defined templates.
Log line format:
2026-05-27 01:32:09,38935187,SENT|base|pancakeswapV3|cbBTC/WETH|...,gap_us=222| Segment | Meaning |
|---------|---------|
| 2026-05-27 01:32:09 | Timestamp (auto) |
| 38935187 | Optional meta (e.g. block number) |
| SENT\|base\|... | label + pipe-delimited fields |
| ,gap_us=222 | Optional suffix |
Files: {dir}/YYYY-MM-DD_hh.log (one file per hour)
import {
LogMessageTemplate,
bootstrapLogger,
createLogTemplate,
bootstrap,
} from 'slack-notification-library';
class SentTradeLog extends LogMessageTemplate {
label = 'SENT';
build(data: Record<string, unknown>) {
return {
label: this.label,
meta: data.blockNumber,
fields: [
data.chain,
data.dex,
data.pair,
data.pool,
// ...more pipe fields
],
suffix: data.suffix, // e.g. ',gap_us=222'
};
}
}
const fileLog = await bootstrapLogger({
mode: 'main',
instances: [{ name: 'trades', dir: './logs' }],
variables: { service: 'mev-bot' },
});
const sentLog = createLogTemplate(SentTradeLog);
await fileLog.trades.write(sentLog, {
blockNumber: 38935187,
chain: 'base',
dex: 'pancakeswapV3',
pair: 'cbBTC/WETH',
pool: '0xc211...',
suffix: ',gap_us=222',
});Global scope (Slack + file logger)
// app/loggers.ts
export let slack: Awaited<ReturnType<typeof bootstrap>>;
export let fileLog: Awaited<ReturnType<typeof bootstrapLogger>>;
export async function initLoggers() {
slack = await bootstrap({ /* ... */ signalHandlers: false });
fileLog = await bootstrapLogger({ /* ... */ signalHandlers: true });
}Call initLoggers() once at startup, then import slack / fileLog anywhere.
API
| Export | Description |
|--------|-------------|
| bootstrap(config) | Create global Slack client (main or worker) |
| bootstrapLogger(config) | Create global file logger client |
| createTemplate(TemplateClass) | Slack template factory |
| createLogTemplate(TemplateClass) | File log template factory |
| SlackMessageTemplate | Slack base class |
| LogMessageTemplate | File log base class |
| formatLogLine(payload) | Preview formatted log line |
| slack.<name>.send(template, data?) | Send Slack message |
| fileLog.<name>.write(template, data?) | Append log line to file |
| slack.schedule(...) | Register cron job |
| *.shutdown() | Hard-stop (shared SIGTERM/SIGINT handler) |
TypeScript + JavaScript
Write in TypeScript or import the compiled JS from dist/index.js. Type definitions ship in dist/index.d.ts.
License
MIT
