Skip to content

feat(slack): add the Slack Web API SDK - #453

Closed
Mkassabov wants to merge 4 commits into
mainfrom
claude/slack-distilled-sdk-7471ae
Closed

Mkassabov wants to merge 4 commits into
mainfrom
claude/slack-distilled-sdk-7471ae

Conversation

@Mkassabov

Copy link
Copy Markdown
Collaborator

Summary

Adds @distilled.cloud/slack — a full Slack Web API SDK (317 operations across 34 service modules), generated from the JSON twins of the docs.slack.dev method reference.

Slack has no OpenAPI document (slack-api-specs froze in 2020), but every docs.slack.dev reference page serves a structured JSON twin by appending .json to the URL. The pipeline mirrors cloudflare's committed-docs flow:

  • scripts/download-docs.ts — crawls the method index + 317 method JSON twins (plus scopes/types/objects lists) into specs/ (committed). Example blocks are stripped at download time: they embed realistic-looking third-party credentials that trip push protection, and the converter never reads them.
  • scripts/convert.ts — method pages → per-family OpenAPI slices → 34 Smithy models via core's shared convertOpenApiToSmithy. Named refs ({"schema": "channel"}) have no JSON twins upstream, so ID-like names (mined from the corpus — channel is always the ID string) inline as string; object refs inline as loose Documents, enrichable later via patches.
  • scripts/generate.ts — the provider spec for the shared smithy→SDK compiler. Wire-verbatim snake_case args; per-method error slugs and required scopes in every operation's doc comment.

Provider shape

  • ok/error envelope at HTTP 200: SlackProtocol dispatches the error slug — auth slugs → Unauthorized, rate_limited → SlackRateLimited (honoring Retry-After), server hiccups → retryable 5xx classes, everything else → SlackError carrying the slug.
  • Form-urlencoded is the default input encoding — JSON only where the docs declare json_input_supported: true. Verified live that Slack ignores JSON bodies without an Authorization header, which is exactly what the tokenless OAuth exchange methods send. The protocol serializes Slack-style: ID arrays comma-joined, rich values JSON-encoded (core's bracket notation is not Slack's dialect).
  • Tokenless OAuth exchange (oauth.v2.access, openid.connect.token, …) works via an empty-token credential — the Authorization header is omitted.
  • admin.analytics.getFile's gzipped-file answer returns as raw bytes.

Verification

Live against slack.com/api: api.test round-trip; form, query, and JSON encodings all parse server-side; error mapping (not_authed/invalid_auth → Unauthorized, invalid_code → SlackError; tokenless oauth.v2.access proves the form path transmits args). Both tsconfigs typecheck clean; package is wired into the root build and generate-all.

Known gaps (follow-ups)

  • Output object types are loose (unknown) where Slack's docs don't structure them (message, conversation, user, …) — patch territory.
  • No cursor auto-pagination profile yet (response_metadata.next_cursor stays a plain response member).

Slack has no OpenAPI document (slack-api-specs froze in 2020), but every
docs.slack.dev reference page serves a structured JSON twin by appending
.json to the URL. The pipeline mirrors cloudflare's committed-docs flow:

  scripts/download-docs.ts  crawl the method index + 317 method JSON twins
                            (plus scopes/types/objects lists) into specs/
  scripts/convert.ts        method pages -> per-family OpenAPI slices ->
                            34 Smithy models via core's shared converter
  scripts/generate.ts       Smithy models -> src/services (317 operations)

Provider shape:
  - ok/error envelope at HTTP 200: SlackProtocol dispatches the error slug
    (auth slugs -> Unauthorized, rate_limited -> SlackRateLimited with
    Retry-After, server hiccups -> retryable 5xx classes, everything else
    -> SlackError carrying the slug; per-method slugs are listed in each
    operation's doc comment)
  - form-urlencoded is the default input encoding (JSON only where the
    docs declare json_input_supported - verified live that JSON bodies
    are ignored without an Authorization header); the protocol serializes
    Slack-style: ID arrays comma-joined, rich values JSON-encoded
  - tokenless OAuth exchange methods work via an empty-token credential
    (the Authorization header is omitted)
  - admin.analytics.getFile's gzipped file answer returns as raw bytes

Verified live against slack.com/api: api.test round-trip, form + query +
JSON encodings, and the error mapping (not_authed/invalid_auth ->
Unauthorized, invalid_code -> SlackError).
@alchemy-version-bot

alchemy-version-bot Bot commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

Distilled

@distilled.cloud/core

bun add https://pkg.ing/@distilled.cloud/core/b2714ad

@distilled.cloud/aws

bun add https://pkg.ing/@distilled.cloud/aws/b2714ad

@distilled.cloud/axiom

bun add https://pkg.ing/@distilled.cloud/axiom/b2714ad

@distilled.cloud/azure

bun add https://pkg.ing/@distilled.cloud/azure/b2714ad

@distilled.cloud/cloudflare

bun add https://pkg.ing/@distilled.cloud/cloudflare/b2714ad

@distilled.cloud/coinbase

bun add https://pkg.ing/@distilled.cloud/coinbase/b2714ad

@distilled.cloud/discord

bun add https://pkg.ing/@distilled.cloud/discord/b2714ad

@distilled.cloud/expo-eas

bun add https://pkg.ing/@distilled.cloud/expo-eas/b2714ad

@distilled.cloud/fly-io

bun add https://pkg.ing/@distilled.cloud/fly-io/b2714ad

@distilled.cloud/gcp

bun add https://pkg.ing/@distilled.cloud/gcp/b2714ad

@distilled.cloud/github

bun add https://pkg.ing/@distilled.cloud/github/b2714ad

@distilled.cloud/kubernetes

bun add https://pkg.ing/@distilled.cloud/kubernetes/b2714ad

@distilled.cloud/mongodb-atlas

bun add https://pkg.ing/@distilled.cloud/mongodb-atlas/b2714ad

@distilled.cloud/neon

bun add https://pkg.ing/@distilled.cloud/neon/b2714ad

@distilled.cloud/planetscale

bun add https://pkg.ing/@distilled.cloud/planetscale/b2714ad

@distilled.cloud/posthog

bun add https://pkg.ing/@distilled.cloud/posthog/b2714ad

@distilled.cloud/prisma-postgres

bun add https://pkg.ing/@distilled.cloud/prisma-postgres/b2714ad

@distilled.cloud/railway

bun add https://pkg.ing/@distilled.cloud/railway/b2714ad

@distilled.cloud/stripe

bun add https://pkg.ing/@distilled.cloud/stripe/b2714ad

@distilled.cloud/supabase

bun add https://pkg.ing/@distilled.cloud/supabase/b2714ad

@distilled.cloud/turso

bun add https://pkg.ing/@distilled.cloud/turso/b2714ad

@distilled.cloud/typesense

bun add https://pkg.ing/@distilled.cloud/typesense/b2714ad

@distilled.cloud/vercel

bun add https://pkg.ing/@distilled.cloud/vercel/b2714ad

@distilled.cloud/workos

bun add https://pkg.ing/@distilled.cloud/workos/b2714ad

…eric code

Core's ErrorMatcher codes are numeric; Slack's are string slugs. The slug
already rides in the matcher's message argument, so patch-declared per-op
classes match on status/message. Fixes the strict (--noCheck false) CI
typecheck.
Slack's cursor convention (pass cursor, follow
response_metadata.next_cursor, empty string = done) maps directly onto
core's paginateCursor traversal:

  - convert.ts stamps smithy.api#paginated on every method that takes a
    cursor arg and has an identifiable items list (the sole top-level
    array member of its documented output, plus explicit overrides for
    conversations.history/replies whose outputs carry several arrays) —
    42 operations across 11 families
  - the typed response_metadata.next_cursor struct is modeled uniformly
    on every cursor op's output (the docs model it on only ~2/3, some as
    an opaque ref), so the traversal has a cursor to follow and callers
    can read it
  - src/pagination.ts supplies the slackPaginate strategy; the plain
    SlackProtocol already keeps response_metadata (a modeled member), so
    no separate paginated protocol is needed

Cursor methods whose docs model no items list (admin.emoji.list's map,
admin.conversations.getTeams' missing member) stay plain operations
rather than guessing — patch territory.
@Mkassabov

Copy link
Copy Markdown
Collaborator Author

Superseded by #551 (rebased, catalog deps fixed, verbNoun naming applied), merging as part of the SDK stack.

@Mkassabov Mkassabov closed this Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant