mirror of https://github.com/buster-so/buster.git
7.5 KiB
7.5 KiB
@buster/slack - Claude Code Guidelines
This package provides a standalone Slack integration with OAuth, messaging, and channel management capabilities.
Architecture Overview
The package is designed to be completely standalone with no external dependencies beyond the Slack SDK. It uses an interface-based approach where consuming applications provide their own implementations for storage.
Core Services
-
SlackAuthService - OAuth 2.0 flow implementation
- Generates authorization URLs with CSRF protection
- Exchanges codes for access tokens
- Validates and revokes tokens
- Requires:
ISlackTokenStorage
,ISlackOAuthStateStorage
- NEW: Accepts optional WebClient for dependency injection
-
SlackChannelService - Channel management
- Lists available public channels
- Validates channel access
- Joins/leaves channels
- Accepts access token as parameter
- NEW: Accepts optional WebClient for dependency injection
-
SlackMessagingService - Message operations
- Sends messages with retry logic
- Supports threading and replies
- Updates and deletes messages
- Validates messaging capability
- Accepts access token as parameter
- NEW: Accepts optional WebClient for dependency injection
Interfaces
Applications must implement these interfaces:
// Token storage
interface ISlackTokenStorage {
storeToken(key: string, token: string): Promise<void>;
getToken(key: string): Promise<string | null>;
deleteToken(key: string): Promise<void>;
hasToken(key: string): Promise<boolean>;
}
// OAuth state storage (CSRF protection)
interface ISlackOAuthStateStorage {
storeState(state: string, data: SlackOAuthStateData): Promise<void>;
getState(state: string): Promise<SlackOAuthStateData | null>;
deleteState(state: string): Promise<void>;
}
// Message tracking (optional, for threading)
interface ISlackMessageTracking {
storeMessageTracking(trackingData: MessageTrackingData): Promise<void>;
getMessageTracking(internalMessageId: string): Promise<MessageTrackingData | null>;
deleteMessageTracking(internalMessageId: string): Promise<void>;
getChannelMessages(slackChannelId: string, options?: {...}): Promise<MessageTrackingData[]>;
hasMessageTracking(internalMessageId: string): Promise<boolean>;
}
Key Design Principles
-
No Database Dependencies
- All storage handled through interfaces
- Apps choose their own persistence layer
-
Token-Based Operations
- Every function accepts tokens as parameters
- No internal token storage or vault implementation
-
Type Safety
- NO
any
types allowed - Biome enforces this - NO
unknown
types - Use proper Zod schemas instead - All inputs validated with Zod schemas
- Comprehensive error types with discriminated unions
- Full TypeScript strict mode compliance
- NO
-
Dependency Injection
- All services accept optional WebClient in constructor
- Makes testing easy without type assertions
- No need to access private properties in tests
-
Error Handling
- Typed error codes for different scenarios
- Retry logic with exponential backoff (2^n seconds)
- User-friendly error messages
- Proper error discrimination in catch blocks
Usage Patterns
OAuth Flow
// 1. Generate auth URL
const { authUrl, state } = await authService.generateAuthUrl({ userId });
// 2. User authorizes, Slack redirects back
// 3. Handle callback
const result = await authService.handleCallback(code, state, tokenKey);
Sending Messages
// Simple message
await messagingService.sendMessage(token, channelId, { text: 'Hello!' });
// With retry
await messagingService.sendMessageWithRetry(token, channelId, message, 3);
// Threading
const { messageTs } = await messagingService.sendMessage(...);
await messagingService.replyToMessage(token, channelId, messageTs, reply);
Testing Guidelines
- Tests are located alongside source files -
*.test.ts
files - Mocks are in
src/mocks/
directory - Use dependency injection - Pass mock WebClient to constructors
- No type assertions needed - Clean testing with DI
- Test error scenarios and retry logic
- Ensure all Zod validations are covered
Example test setup:
import { createMockWebClient } from '../mocks';
import { SlackMessagingService } from './messaging';
import type { WebClient } from '@slack/web-api';
const mockClient = createMockWebClient();
const service = new SlackMessagingService(mockClient as unknown as WebClient);
Common Pitfalls to Avoid
- Don't store tokens internally - Always accept as parameters
- Don't assume channel access - Always validate first
- Don't ignore rate limits - Use retry logic
- Don't skip error handling - All errors should be typed
- Don't use
any
type - Biome will error on this - Don't use
unknown
type - Use proper Zod schemas - Don't access private properties in tests - Use dependency injection
- Don't use string concatenation - Use template literals
- Don't forget exponential backoff - Use 2^n for retry delays
Development Commands
# Type check (strict mode, no any/unknown)
pnpm run typecheck
# Run tests (vitest)
pnpm run test
# Build (TypeScript compilation)
pnpm run build
# Watch mode
pnpm run dev
# Lint and format with Biome
pnpm run check:fix src/
File Structure
src/
├── services/ # Core service classes
│ ├── auth.ts # OAuth authentication
│ ├── auth.test.ts # Auth tests
│ ├── channels.ts # Channel management
│ ├── channels.test.ts
│ ├── messaging.ts # Message operations
│ └── messaging.test.ts
├── interfaces/ # Storage interfaces
│ └── token-storage.ts
├── types/ # TypeScript types and Zod schemas
│ ├── index.ts # Main types
│ ├── blocks.ts # Slack Block Kit types
│ └── errors.ts # Error types
├── utils/ # Helper functions
│ ├── validation-helpers.ts
│ └── message-formatter.ts
├── mocks/ # Testing utilities
│ └── index.ts # Mock WebClient
└── index.ts # Package exports
Integration Checklist
When integrating this package:
- Implement
ISlackTokenStorage
for token persistence - Implement
ISlackOAuthStateStorage
for OAuth state - Implement
ISlackMessageTracking
if threading needed - Set up OAuth redirect handler
- Handle error cases appropriately
- Test with real Slack workspace
Security Considerations
- OAuth state expires after 15 minutes
- Tokens should be encrypted at rest
- Always validate channel access before sending
- No sensitive data in error messages
- HTTPS required for all OAuth flows
- Never log tokens or secrets
- Use CSRF protection via state parameter
Recent Updates
Type Safety Improvements
- Removed all
any
types throughout the codebase - Replaced
unknown
types with proper Zod schemas - Created comprehensive Slack Block Kit types
- Added discriminated unions for error handling
Testing Improvements
- Added dependency injection for WebClient
- Moved tests alongside source files (*.test.ts)
- Created mock utilities in
src/mocks/
- No more accessing private properties in tests
Code Quality
- Full Biome compliance (no linting errors)
- Template literals instead of string concatenation
- Proper exponential backoff with 2^n formula
- Consistent error handling patterns