Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 10 additions & 229 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,240 +85,21 @@ npm run build --workspace=@slack/socket-mode
- **Formatting**: configured in `biome.json`
- **Test files**: `*.test.{ts,js}` using `node:test` + `node:assert/strict`; coverage via `--experimental-test-coverage`
- **Type tests**: `*.test-d.ts` using tsd
- **Naming conventions for request/response types**:
- Request: `{Namespace}{Action}Arguments` (e.g., `ChatPostMessageArguments`)
- Response: `{Namespace}{Action}Response` (e.g., `ChatPostMessageResponse`)
- **Method names**: camelCase matching the Slack API method (e.g., `chat.postMessage` → `postMessage`)
- **CI matrix**: Node 18.x, 20.x, 22.x, 24.x on Ubuntu + Windows

## Adding a New Slack API Method
## Package-Level AGENTS.md

This is the most common contribution. Follow these steps in order.
Individual packages have their own `AGENTS.md` with package-specific guidance:

### Step 1: Look Up the API Method Documentation
- `packages/web-api/AGENTS.md` — adding API methods, code generation, client architecture

Reference: `https://docs.slack.dev/reference/methods/{method.name}`

For example, for `chat.appendStream`: https://docs.slack.dev/reference/methods/chat.appendStream

### Step 2: Generate Response Types

Run the generation script from the repo root:

```bash
bash scripts/generate-web-api-types.sh
```

This script:

1. Clones/updates `slackapi/java-slack-sdk` into `tmp/java-slack-sdk`
2. Reads JSON response samples from `tmp/java-slack-sdk/json-logs/samples/api/`
3. Runs `scripts/code_generator.rb` which uses quicktype to generate TypeScript types
4. Outputs `*Response.ts` files to `packages/web-api/src/types/response/`
5. Regenerates `packages/web-api/src/types/response/index.ts`
6. Runs `npm run lint:fix` on the generated files

**Prerequisites**: Ruby and npm must be installed.

Generated response types look like this (example: `ChatAppendStreamResponse.ts`):

```typescript
import type { WebAPICallResult } from '../../WebClient';
export type ChatAppendStreamResponse = WebAPICallResult & {
channel?: string;
error?: string;
needed?: string;
ok?: boolean;
provided?: string;
ts?: string;
};
```

All generated files extend `WebAPICallResult` and have all properties optional.

**Note**: If the JSON sample doesn't exist yet in `java-slack-sdk`, the API method has not been added to that project yet. The maintainers need to add it there first before it can be added here.

### Step 3: Add Request Argument Types

File: `packages/web-api/src/types/request/<namespace>.ts`

Create an interface/type for the method's arguments. Reuse mixins from `packages/web-api/src/types/request/common.ts`:

| Mixin | Purpose |
|-------|---------|
| `TokenOverridable` | Optional `token` override |
| `CursorPaginationEnabled` | `cursor` + `limit` pagination |
| `TimelinePaginationEnabled` | `oldest` + `latest` + `inclusive` pagination |
| `TraditionalPagingEnabled` | `count` + `page` pagination |
| `OptionalTeamAssignable` | Optional `team_id` |
| `LocaleAware` | Optional `include_locale` |

Also reuse namespace-specific mixins from the same file (e.g., `Channel`, `ChannelAndTS`, `AsUser` in `chat.ts`).

**Example** — `ChatAppendStreamArguments` from `packages/web-api/src/types/request/chat.ts`:

```typescript
export interface ChatAppendStreamArguments extends TokenOverridable, ChannelAndTS, Partial<MarkdownText> {
/**
* @description An array of chunk objects to append to the stream.
* Either `markdown_text` or `chunks` is required.
*/
chunks?: AnyChunk[];
}
```

### Step 4: Export Request Types

File: `packages/web-api/src/types/request/index.ts`

Add the new type to the appropriate export block:

```typescript
export type {
ChatAppendStreamArguments,
ChatStartStreamArguments,
ChatStopStreamArguments,
// ... existing exports
} from './chat';
```

### Step 5: Add Method Binding

File: `packages/web-api/src/methods.ts`

1. Import the argument and response types at the top of the file.
2. Add a method binding in the appropriate namespace object of the `Methods` class.

**Import pattern**:

```typescript
// Request types are imported from the request index barrel
import type {
ChatAppendStreamArguments,
// ...
} from './types/request';

// Response types are imported from individual files
import type { ChatAppendStreamResponse } from './types/response/ChatAppendStreamResponse';
```

**Note**: Request types are imported from the barrel `./types/request` (already grouped by namespace), but response types are imported from their individual files (e.g., `./types/response/ChatAppendStreamResponse`).

**Binding pattern** — within the appropriate namespace object in the `Methods` class:

```typescript
public readonly chat = {
/**
* @description Appends text to an existing streaming conversation.
* @see {@link https://docs.slack.dev/reference/methods/chat.appendStream `chat.appendStream` API reference}.
*/
appendStream: bindApiCall<ChatAppendStreamArguments, ChatAppendStreamResponse>(this, 'chat.appendStream'),
// ...
};
```

**`bindApiCall` vs `bindApiCallWithOptionalArgument`**:

- `bindApiCall` — for methods with **required** arguments (most methods)
- `bindApiCallWithOptionalArgument` — for methods where **all arguments are optional**

### Step 6: Add Type Tests

File: `packages/web-api/test/types/methods/<namespace>.test-d.ts`

Add both sad path (should error) and happy path (should compile) tests:

```typescript
import { expectAssignable, expectError } from 'tsd';
import { WebClient } from '../../../src/WebClient';

const web = new WebClient('TOKEN');

// chat.appendStream
// -- sad path
expectError(web.chat.appendStream()); // lacking argument
expectError(web.chat.appendStream({})); // empty argument
expectError(
web.chat.appendStream({
channel: 'C1234', // missing ts and markdown_text
}),
);

// -- happy path
expectAssignable<Parameters<typeof web.chat.appendStream>>([
{
channel: 'C1234',
ts: '1234.56',
markdown_text: 'hello',
},
]);
```

### Summary Checklist

- [ ] Looked up method docs at `https://docs.slack.dev/reference/methods/{method.name}`
- [ ] Generated response types via `scripts/generate-web-api-types.sh` (or created manually if JSON sample unavailable)
- [ ] Added request argument type in `packages/web-api/src/types/request/<namespace>.ts`
- [ ] Exported new request type from `packages/web-api/src/types/request/index.ts`
- [ ] Imported argument + response types in `packages/web-api/src/methods.ts`
- [ ] Added method binding with `bindApiCall` in appropriate namespace in `packages/web-api/src/methods.ts`
- [ ] Added sad/happy path type tests in `packages/web-api/test/types/methods/<namespace>.test-d.ts`
- [ ] Verified: `npm test --workspace=packages/web-api`

## Code Generation Details

### `scripts/generate-web-api-types.sh`

Entry point for response type generation. It:

1. Clones or pulls `slackapi/java-slack-sdk` into `tmp/`
2. Runs `npm install` in `scripts/` to install quicktype
3. Executes `scripts/code_generator.rb`
4. Runs `npm run lint:fix` on `packages/web-api`

## Testing

- **Test framework**: `node:test` (built-in) with `node:assert/strict` for assertions
- **Test runner**: `node --experimental-test-coverage --import tsx --test`
- **Unit tests**: `*.test.{ts,js}` files alongside source (e.g., `src/WebClient.test.ts`)
- **Type tests**: tsd (`*.test-d.ts` files in `packages/web-api/test/types/`)
- **Integration tests**: CommonJS, ESM, and TypeScript compatibility checks
- **CI matrix**: Node 18.x, 20.x, 22.x, 24.x on Ubuntu + Windows (8 jobs total)
- **Coverage output**: `lcov.info` at each package root (not in `coverage/` dir)
- **Test results**: `test-results.xml` at each package root (JUnit format)

Per-package test commands:

```bash
npm test --workspace=packages/web-api # all tests (types + integration + unit)
npm run test:unit --workspace=packages/web-api
npm run test:types --workspace=packages/web-api
```

## Web API Client Architecture

`packages/web-api/src/WebClient.ts`:
## Development Philosophy

- Extends the `Methods` class (which defines all API method bindings in `methods.ts`)
- Uses Axios for HTTP requests
- `p-queue` for request concurrency control
- `p-retry` for automatic retries with backoff
- Built-in cursor pagination via `paginate()`
- Streaming support via `chatStream()` using `ChatStreamer`
- Rate limiting with emitted events
- **Follow existing patterns exactly** — when adding or modifying code, match the style of adjacent code.
- **Reuse mixins and shared types** rather than duplicating field definitions across packages.
- **PascalCase for types**, camelCase for methods.
- **JSDoc on public APIs**: Always include `@description` and `@see` with a link to the API reference.

## Common Pitfalls

- **Don't edit generated response types** — use `scripts/generate-web-api-types.sh` instead. Look for the "DO NOT EDIT" banner.
- **Build in dependency order** — building `web-api` before `logger` and `types` will fail.
- **Use Biome, not ESLint/Prettier** — this repo uses Biome exclusively (`biome.json`).
- **Biome overrides exist for generated code** — `packages/web-api/src/types/response/**/*.ts` has relaxed rules (`noBannedTypes: off`, `noExplicitAny: off`).
- **Import organization is disabled** for `packages/web-api/src/index.ts` in Biome config.
- **Response type imports use individual files**, not the barrel — e.g., `import type { ChatPostMessageResponse } from './types/response/ChatPostMessageResponse'`.

## Development Philosophy

- **Follow existing patterns exactly** — when adding a new method, match the style of adjacent methods.
- **Reuse mixins** from `common.ts` and namespace-specific files rather than duplicating field definitions.
- **Every API method needs four things**: request type, response type, method binding, and type tests.
- **Naming conventions**: PascalCase for types (`ChatPostMessageArguments`), camelCase for methods (`postMessage`).
- **JSDoc on method bindings**: Always include `@description` and `@see` with a link to the API reference.
- **Build in dependency order** — see the dependency graph above.
Loading