Skip to content
Open
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
23 changes: 12 additions & 11 deletions .claude/skills/docs-sync/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ Before the sync check, make sure every server-side snippet on the tested pages h

- Pages: every page of the `[chat][node].json` and `[platform][default].json` sidebars (`$DOCS_DIR/_sidebars/`) under `chat/` or `platform/`, the same list as `findSidebarPages` in `sync/parse.ts`.
- Fences: every JS fence (`js`, `javascript`, `ts`, `typescript`) labelled `JavaScript` or unlabelled, together with its tab group: the other fences in the same `<Tabs>` block, or the fence alone when it isn't in one.
- Out of scope: `React` and `React Native` fences, `Node.js` fences inside a `<disclosure label="... (no SDK)">` block, and comment-only fences.
- Out of scope: `React` and `React Native` fences, the stream-chat-js@9 fences (`JavaScript (stream-chat-js@9)`, `Node.js (stream-chat-js@9)`), `Node.js` fences inside a `<disclosure label="... (no SDK)">` block, and comment-only fences.

List them with a throwaway script in the scratchpad (page, heading, line, the labels in the group, the code). Then classify each group by its tabs first and the prose second:

| Group | Action |
| -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| A `JavaScript` fence with server-only code (secret, `updateAppSettings`, `createToken`, ...) | Relabel it `Node.js` and rewrite it to the node SDK call (`client.updateApp`, ...). |
| Only client SDKs (`JavaScript` plus Kotlin, Swift, Dart, Unity, Unreal, React, ...) | Client-side: no `Node.js` tab, even when a server equivalent exists. |
| `JavaScript` only (or with non-SDK tabs such as `JSON`) | Add a `Node.js` tab when the node SDK can do the same thing, unless the heading or prose hints "client-side only". See below. |
| Other server SDK tabs (Python, Ruby, PHP, Go, C#, Java) but no `Node.js` | Add a `Node.js` tab. If the others are comment-only notes ("client-side only operation"), add the same note for the Node SDK. |
| A client group, and the page has its own server-side section for it ("Server-Side Usage", "Server Side") | No `Node.js` tab: the server-side section covers it. |
| Group | Action |
| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A `JavaScript` fence with server-only code (secret, `updateAppSettings`, `createToken`, ...) | Relabel it `Node.js` and rewrite it to the node SDK call (`client.updateApp`, ...). Relabel its `JavaScript (stream-chat-js@9)` partner, if any, `Node.js (stream-chat-js@9)`, code unchanged. |
| Only client SDKs (`JavaScript` plus Kotlin, Swift, Dart, Unity, Unreal, React, ...) | Client-side: no `Node.js` tab, even when a server equivalent exists. |
| `JavaScript` only (or with non-SDK tabs such as `JSON`) | Add a `Node.js` tab when the node SDK can do the same thing, unless the heading or prose hints "client-side only". See below. |
| Other server SDK tabs (Python, Ruby, PHP, Go, C#, Java) but no `Node.js` | Add a `Node.js` tab. If the others are comment-only notes ("client-side only operation"), add the same note for the Node SDK. |
| A client group, and the page has its own server-side section for it ("Server-Side Usage", "Server Side") | No `Node.js` tab: the server-side section covers it. |

JavaScript-only groups are common: JavaScript is usually the first SDK documented, and the other SDKs follow later. Being alone is no hint that the code is client-side. Client APIs used in passing aren't either: translate them to the server side (`client.user?.language` → `user.language` of a user you have; the message from `client.chat.getMessage`). Skip the group only when:

Expand All @@ -74,6 +74,7 @@ To check what the node SDK can do, read `src/StreamClient.ts`, `src/StreamChatCl

- Mirror the `JavaScript` fence: same comments, structure and placeholder literals (`"user-1"`, `"messaging:general"`).
- Use the node SDK's shapes and types: `client.chat.*` / `client.feeds.*`, request objects (`filter_conditions`, `preferences: [...]`), `Date` where the types say `Date` (not `.toISOString()`), and `user_id` where the client code acts as the connected user (server-side `queryChannels` with `hidden` / `muted`, drafts, devices need it).
- Don't write a `Node.js (stream-chat-js@9)` fence for it: list the group in the report so the v9 version can be added by hand.
- Put it before the group's `</Tabs>`. A fence that isn't in a `<Tabs>` block: wrap it and the new fence in `<Tabs>`.
- From `$DOCS_DIR`, run `npx prettier --write <page path>` and `node scripts/tab-lint.mjs <page path>` (it puts the tabs in the canonical order: client SDKs first, then server SDKs).

Expand All @@ -90,7 +91,7 @@ It runs without network and reports four kinds of failure. Collect them all, gro
| Failure | Meaning |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `snippets without a test region` | A `Node.js` fence on a tested sidebar page (chat Node, platform) has no region: a new snippet, a new page, or a fence whose key changed (see the next row). |
| mislabelled JS fences | A JS fence is unlabelled or has a label other than `JavaScript`, `React`, `React Native` or `Node.js`. |
| mislabelled JS fences | A JS fence is unlabelled or has a label other than `JavaScript`, `React`, `React Native`, `Node.js` or their stream-chat-js@9 versions. |
| `no such fence` | A region's key (`docs`, `heading`, `tab`, `index`) matches no fence: the fence was removed, moved, or its heading was renamed. The failure lists the page's fence keys. |
| `<region> differs from <docs file>:<line>` | The region's code and the fence's code differ. |

Expand All @@ -104,7 +105,7 @@ Work one docs page at a time.

### Mislabelled fence

Label it `Node.js` for `@stream-io/node-sdk` (server-side) code, `JavaScript` for client code. Code that works on both sides (e.g. a bare filter object) gets `JavaScript`: an unlabelled `js` fence already renders like `label="JavaScript"` (the JavaScript route's own sample, a titled fallback on the Node route), so the page doesn't change. Whether it also needs a `Node.js` tab is decided by the sweep (step 3). Otherwise, `JavaScript`, `React` and `React Native` fences, and `Node.js` fences inside a `<disclosure label="... (no SDK)">` block (reference implementations without the SDK), are out of scope: never test or change them.
Label it `Node.js` for `@stream-io/node-sdk` (server-side) code, `JavaScript` for client code. Code that works on both sides (e.g. a bare filter object) gets `JavaScript`: an unlabelled `js` fence already renders like `label="JavaScript"` (the JavaScript route's own sample, a titled fallback on the Node route), so the page doesn't change. Whether it also needs a `Node.js` tab is decided by the sweep (step 3). Otherwise, `JavaScript`, `React` and `React Native` fences, the stream-chat-js@9 fences (`JavaScript (stream-chat-js@9)`, `Node.js (stream-chat-js@9)`), and `Node.js` fences inside a `<disclosure label="... (no SDK)">` block (reference implementations without the SDK), are out of scope: never test or change them.

### Region differs from its fence (docs changed)

Expand Down Expand Up @@ -134,7 +135,7 @@ Write a test for it in the page's test file (create the file if the page is new)
- Comment-only fences need no region.
- A fence that isn't code (e.g. a bare data shape) is a docs bug: rewrite it as code that runs (e.g. reading and logging the fields), and report it. Never remove a `Node.js` fence just because it is a fragment or can't be tested: wrap it, or put it in `it.skip`.
- A snippet the test app can't run goes in `it.skip('BLOCKED: <reason>', ...)` so it is still typechecked; report the exact API error.
- **Client-side code in a `Node.js` fence** (needs a connected user or a WebSocket, or a method the node SDK doesn't have: `connectUser`, `channel.watch()`, `client.on(...)`): never test different code from what the fence shows. If the node SDK can do the same thing, make the smallest docs fix that turns the fence into that call (`channel.watch()` → `channel.getOrCreate()`, `user_id` instead of `connectUser`). If it can't (events, WebSocket, local client state), the fence is mislabelled: label it `JavaScript`. Use `it.skip('BLOCKED: client-side code: ...')` only while that decision is open. Report it either way. To check what a server-side endpoint accepts, read the SDK source (`src/StreamClient.ts`, `src/StreamChatClient.ts`, `src/StreamChannel.ts`, `src/gen/`) or the OpenAPI spec (`chat-serverside-api.json` in https://github.com/GetStream/protocol/tree/main/openapi/v2).
- **Client-side code in a `Node.js` fence** (needs a connected user or a WebSocket, or a method the node SDK doesn't have: `connectUser`, `channel.watch()`, `client.on(...)`): never test different code from what the fence shows. If the node SDK can do the same thing, make the smallest docs fix that turns the fence into that call (`channel.watch()` → `channel.getOrCreate()`, `user_id` instead of `connectUser`). If it can't (events, WebSocket, local client state), the fence is mislabelled: label it `JavaScript`. Its `Node.js (stream-chat-js@9)` partner, if any, becomes `JavaScript (stream-chat-js@9)`, code unchanged. Use `it.skip('BLOCKED: client-side code: ...')` only while that decision is open. Report it either way. To check what a server-side endpoint accepts, read the SDK source (`src/StreamClient.ts`, `src/StreamChatClient.ts`, `src/StreamChannel.ts`, `src/gen/`) or the OpenAPI spec (`chat-serverside-api.json` in https://github.com/GetStream/protocol/tree/main/openapi/v2).
- **Push and webhooks**: don't test that notifications or webhook events arrive; assert the response or read the setting back (`getApp()`, `listPushProviders()`, ...). Never touch or use the test app's own push provider (see "Test app" in `AGENTS.md`): only use providers the test creates with `uniqueId` names. Snippets that need real credentials run in `describe.skipIf(!hasFirebaseCredentials)` (`helpers/push.ts`); the README's "Push credentials" section has the details.
- **App-level side effects** (app settings, built-in channel types, grants, webhook URLs, push config, ...): read the original value first and restore it with `cleanup.add(...)`, or the file fails with `DRIFT`.

Expand Down
7 changes: 4 additions & 3 deletions __tests__/docs-snippets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Typed tests that run the server-side Node.js snippets of the chat docs (the page
`getstream.io/content/docs/_sidebars/[chat][node].json`) and of the platform docs (the pages of
`getstream.io/content/docs/_sidebars/[platform][default].json`) against a real Stream app.
Only fences labelled `Node.js` are in scope (docs fences must always have a label): they are fixed when they are
wrong. `JavaScript`, `React` and `React Native` fences (client-side code) and `Node.js` fences inside a `<disclosure label="... (no SDK)">` block (reference
wrong. `JavaScript`, `React` and `React Native` fences (client-side code), the stream-chat-js@9 fences (`JavaScript (stream-chat-js@9)`,
`Node.js (stream-chat-js@9)`) and `Node.js` fences inside a `<disclosure label="... (no SDK)">` block (reference
implementations without the SDK) are out of scope and are neither tested nor changed.

```sh
Expand Down Expand Up @@ -80,8 +81,8 @@ await channel.getOrCreate({ data: { created_by_id: creatorId } });
- `index` is 1-based among fences with the same heading text and tab (default 1).
- **COPY**: `name="literal"` pairs, applied to the region before comparing (identifier `name` becomes the docs literal). Don't use COPY variables as object keys or shorthand properties.
- **Comparison**: both sides are formatted with the same prettier options, so quote style and line wrapping don't matter, but any code or comment change does.
- **Coverage** (checked first): every `Node.js` fence (`js`/`javascript`/`ts`/`typescript`) on the pages of the chat Node sidebar and the platform sidebar (`content/docs/_sidebars/[chat][node].json`, `[platform][default].json`), whether or not the page has tests yet, must have a region (inside `it.skip` if it can't run). `JavaScript` / `React` / `React Native` fences and comment-only fences are ignored.
- **Labels**: every JS fence on those pages must be labelled `JavaScript`, `React`, `React Native` or `Node.js`; unlabelled or otherwise labelled JS fences fail.
- **Coverage** (checked first): every `Node.js` fence (`js`/`javascript`/`ts`/`typescript`) on the pages of the chat Node sidebar and the platform sidebar (`content/docs/_sidebars/[chat][node].json`, `[platform][default].json`), whether or not the page has tests yet, must have a region (inside `it.skip` if it can't run). `JavaScript` / `React` / `React Native` / stream-chat-js@9 fences and comment-only fences are ignored.
- **Labels**: every JS fence on those pages must be labelled `JavaScript`, `React`, `React Native`, `Node.js`, `JavaScript (stream-chat-js@9)` or `Node.js (stream-chat-js@9)`; unlabelled or otherwise labelled JS fences fail.
- **Excluded pages**: `EXCLUDED_PAGES` in `sync/parse.ts` lists sidebar pages that are deliberately not validated, with a reason. Both checks skip them.
- **`DOCS_SYNC_WRITE=<page path> yarn test:docs:sync`** writes the regions of that page into the docs fences (docs prettier style). Use it to apply a fix to the docs. `DOCS_SYNC_WRITE=1` writes all pages.
- **`DOCS_DIR`** points at another docs checkout (default: `../getstream.io/content/docs` next to this repo). The check is skipped if the directory doesn't exist.
5 changes: 3 additions & 2 deletions __tests__/docs-snippets/sync/docs-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import type { DocsFence, TestRegion } from './parse';
// `yarn test:docs:sync`, in this order:
// 1. Every `Node.js` fence (except comment-only fences) on the pages of the chat Node
// sidebar and the platform sidebar has a test region. `JavaScript`, `React` and
// `React Native` (client-side) fences, and fences inside a
// `React Native` (client-side) fences, the stream-chat-js@9 fences
// (`JavaScript (stream-chat-js@9)`, `Node.js (stream-chat-js@9)`), and fences inside a
// `<disclosure label="... (no SDK)">` block, are out of scope.
// 2. Every JS fence on those pages is labelled with one of `ALLOWED_JS_LABELS`.
// 3. Every marked test region points at an existing fence and matches it (after
Expand Down Expand Up @@ -97,7 +98,7 @@ describe.skipIf(!hasDocs)(`docs sync (${DOCS_DIR})`, () => {
);
expect(
mislabelled,
`JS fences need label="${ALLOWED_JS_LABELS.join('" or label="')}" (client-side code or @stream-io/node-sdk code)`,
`JS fences need label="${ALLOWED_JS_LABELS.join('" or label="')}" (client-side code, @stream-io/node-sdk code, or their stream-chat-js@9 versions)`,
).toEqual([]);
});

Expand Down
12 changes: 10 additions & 2 deletions __tests__/docs-snippets/sync/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,16 @@ const JS_LANGS = new Set(['js', 'javascript', 'ts', 'typescript']);
export const JS_TAB = 'JavaScript';
/** The tested tab (`@stream-io/node-sdk` code). */
export const NODE_TAB = 'Node.js';
/** Out of scope too: client-side React / React Native SDK code. */
const OUT_OF_SCOPE_TABS = ['React', 'React Native'];
/**
* Out of scope too: client-side React / React Native SDK code, and the stream-chat-js@9
* versions shown next to `JavaScript` (client-side) and `Node.js` (server-side).
*/
const OUT_OF_SCOPE_TABS = [
'React',
'React Native',
'JavaScript (stream-chat-js@9)',
'Node.js (stream-chat-js@9)',
];
/**
* Opens a block of reference implementations that don't use the SDK (platform pages).
* Its `Node.js` fences are out of scope.
Expand Down
Loading