Files
suaveplan/openspec/changes/enterprise-slack-client/proposal.md
2026-05-27 23:29:53 +01:00

5.5 KiB

OpenSpec Change: enterprise-slack-client

  • Change ID: enterprise-slack-client
  • Package: @suaveplan/slack-client
  • Category: universal
  • Target path: universal/social/slack-client
  • Status: IN PROGRESS
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/slack-client.md

Summary

Deliver @suaveplan/slack-client on the genesis branch from scratch at enterprise quality. The package provides a generic Slack API client: Web API method wrappers for messaging (chat.postMessage, chat.update, chat.delete), channel management (conversations.*), user info (users.*), file uploads, Block Kit message builder, Incoming Webhooks, Event subscription parsing with signature verification, slash-command payload parsing, interactive component payload handling, and a SlackClient facade. Injectable fetch and crypto; no Slack SDK dependency. Genesis adds structured errors, Zod schemas, OTel instrumentation, retry with circuit breaker, and AbortSignal cancellation.


Current-state audit (genesis)

The package does not exist on genesis yet — this is a fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) SlackError; SLACK.* error codes
@suaveplan/types (workspace:^) LoggerLike, Clock
@suaveplan/retry (workspace:^) Retry + circuit breaker for API calls
@suaveplan/crypto (workspace:^) HMAC-SHA256 request signature verification
zod (catalog:forms) Schema validation
@opentelemetry/api (catalog:observability) Spans + counters

Dev deps: vitest, @vitest/coverage-istanbul, fast-check (all catalog:testing).


Public API surface

SlackClient

class SlackClient {
  constructor(options: SlackClientOptions)

  // Messaging
  postMessage(options: PostMessageOptions): Promise<SlackMessageResponse>
  updateMessage(options: UpdateMessageOptions): Promise<SlackMessageResponse>
  deleteMessage(channel: string, ts: string): Promise<void>

  // Channels
  listConversations(options?: ListConversationsOptions): AsyncGenerator<SlackChannel>
  getConversationInfo(channel: string): Promise<SlackChannel>

  // Users
  getUserInfo(userId: string): Promise<SlackUser>
  getUserByEmail(email: string): Promise<SlackUser>

  // Files
  uploadFile(options: FileUploadOptions): Promise<SlackFile>

  // Webhooks
  postToWebhook(url: string, payload: WebhookPayload): Promise<void>
}

interface SlackClientOptions {
  token: string
  signingSecret?: string   // for event verification
  retryOptions?: RetryOptions
  fetch?: typeof globalThis.fetch
  clock?: Clock
  logger?: LoggerLike
}

Block Kit builder

function createMessage(text: string): MessageBuilder
interface MessageBuilder {
  section(text: string): MessageBuilder
  divider(): MessageBuilder
  image(url: string, altText: string): MessageBuilder
  actions(...elements: ActionElement[]): MessageBuilder
  context(...elements: ContextElement[]): MessageBuilder
  build(): SlackBlocks
}

Event handling

function verifySlackSignature(
  body: string,
  timestamp: string,
  signature: string,
  signingSecret: string,
  options?: { maxAgeSeconds?: number; clock?: Clock }
): boolean

function parseSlackEvent(body: unknown): SlackEvent
function parseSlashCommand(body: unknown): SlashCommand
function parseInteractivePayload(body: unknown): InteractivePayload

Error codes

type SlackErrorCode =
  | 'SLACK_AUTH_FAILED'
  | 'SLACK_API_ERROR'
  | 'SLACK_RATE_LIMITED'
  | 'SLACK_SIGNATURE_INVALID'
  | 'SLACK_CHANNEL_NOT_FOUND'
  | 'SLACK_USER_NOT_FOUND'
  | 'SLACK_ABORTED'

Genesis additions

  1. SlackError extends SuaveplanError with seven stable codes.
  2. Zod schemas for PostMessageOptions, SlackEvent, SlashCommand, InteractivePayload, SlackChannel, SlackUser, SlackFile.
  3. Rate-limit handling — respects Retry-After header from Slack 429 responses; integrates with @suaveplan/retry.
  4. Circuit breaker — wraps all API calls via @suaveplan/retry circuit breaker; opens after 5 consecutive failures.
  5. OTel instrumentation — span slack.api_call with attribute slack.method; counter slack.api_total; histogram slack.api_duration_ms.
  6. Property-based tests — Block Kit builder produces valid JSON structures for arbitrary sequences of builder calls.

File layout

universal/social/slack-client/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/
      index.ts
    types/
      common.ts
      common.md
    core/
      slack-client/
        slack-client.ts
        slack-client.md
        slack-client.types.ts
        slack-client.test.ts
        index.ts
      api-caller/
        api-caller.ts
        api-caller.md
        api-caller.test.ts
        index.ts
    blocks/
      message-builder/
        message-builder.ts
        message-builder.md
        message-builder.test.ts
        index.ts
    events/
      event-parser/
        event-parser.ts
        event-parser.md
        event-parser.test.ts
        index.ts
      signature-verifier/
        signature-verifier.ts
        signature-verifier.md
        signature-verifier.test.ts
        index.ts
    __benchmarks__/
      slack-client.bench.ts
  tests/
    integration.test.ts
    property.test.ts

Impact

Downstream consumers: @suaveplan/message-bridge, @suaveplan/notification-channel.

Workspace dep prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/retry, @suaveplan/crypto.