Files
2026-05-27 23:29:53 +01:00

4.5 KiB

Tasks: enterprise-discord-client

Change ID: enterprise-discord-client Package: @suaveplan/discord-client Target path: universal/social/discord-client Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/retry, @suaveplan/crypto


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create universal/social/discord-client/ with src/, tests/, src/__benchmarks__/ and all feature subdirectories per the layout in proposal.md.
  • Write package.json at version 0.1.0 with "type": "module", "sideEffects": false, correct exports map, dependencies, and devDependencies.
  • Write tsconfig.json extending @suaveplan/typescript-config/library.json.
  • Write vitest.config.ts using createTestConfig() from @suaveplan/testing/config.
  • Add lint, typecheck, test, test:coverage, build, docs:check scripts.

2. Types + errors

  • src/types/common.ts — DiscordClientOptions, MessagePayload, WebhookPayload, ExecuteWebhookOptions, ThreadOptions, DiscordMessage, DiscordChannel, DiscordGuild, DiscordMember, DiscordEmbed, DiscordInteraction.
  • src/types/common.md (≥200 words, both examples, API reference).
  • src/error-codes.ts — DiscordErrorCode (7 codes), metadata, register, eager call.
  • src/types/schemas.ts + schemas.md — Zod schemas.

3. Core modules

  • src/core/rate-limiter/ — RateLimiter: parses X-RateLimit-Bucket, X-RateLimit-Remaining, X-RateLimit-Reset headers; tracks per-bucket state; waitIfNeeded(bucket) returns a Promise that resolves after the reset time when remaining === 0. Tests: no-op when remaining > 0, waits when remaining = 0, multiple buckets tracked independently.
  • src/core/api-caller/ — ApiCaller: wraps fetch to https://discord.com/api/v10/; sets Authorization: Bot header; invokes RateLimiter.waitIfNeeded; parses JSON; throws DISCORD_API_ERROR on non-2xx; handles 429 (uses retry_after). Tests: success, 401, 404, 429 rate-limit.
  • src/core/discord-client/ — DiscordClient: implements all public methods via ApiCaller. Tests: sendMessage, editMessage, deleteMessage, addReaction, createThread, getGuild, getMember, sendWebhook.
  • src/embeds/embed-builder/ — createEmbed(): fluent builder; build() returns DiscordEmbed. Tests: title+description, with fields, with footer, with image, timestamp auto-set.
  • src/interactions/interaction-verifier/ — verifyDiscordInteraction(body, signature, timestamp, publicKey): Ed25519 verify via @suaveplan/crypto. Tests: valid, tampered body, wrong key.
  • src/interactions/interaction-parser/ — parseInteraction(body): Zod validation. Tests: slash command, button component, select component, invalid throws.

4. OTel instrumentation

  • Span discord.api_call with attribute discord.endpoint; counter discord.api_total; histogram discord.api_duration_ms.

5. Property-based tests (tests/property.test.ts)

  • Embed builder: for arbitrary sequences of field additions, build() produces fields array with correct structure and length.
  • Rate-limiter bucket: parsing arbitrary valid header combinations never throws and always produces a non-negative remaining count.

6. Benchmarks (src/__benchmarks__/discord-client.bench.ts)

  • createEmbed builder chain (10 fields) 100 000 times.

7. Docs + verify

  • One .md per implementation file (≥200 words, both examples, API reference). Root README.md.
  • bunx turbo lint typecheck test --filter=@suaveplan/discord-client — green.
  • bunx turbo test:coverage --filter=@suaveplan/discord-client — 100%.
  • bun scripts/publish.ts --package @suaveplan/discord-client.
  • Archive: openspec/archive/2026-04-24-enterprise-discord-client/.