From 197137652781633eb0322151f6c691a309a9b840 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Fri, 29 May 2026 16:54:13 -0600 Subject: [PATCH 1/2] feat(web-api): expose public ts property on ChatStream --- packages/web-api/src/WebClient.test.ts | 28 ++++++++++++++++++++++++++ packages/web-api/src/chat-stream.ts | 10 +++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/web-api/src/WebClient.test.ts b/packages/web-api/src/WebClient.test.ts index 35925358d..4dcd4c851 100644 --- a/packages/web-api/src/WebClient.test.ts +++ b/packages/web-api/src/WebClient.test.ts @@ -1295,6 +1295,34 @@ describe('WebClient', () => { scope.done(); }); + it('ts is undefined before flush and set after', async () => { + const scope = nock('https://slack.com') + .post('/api/chat.startStream') + .reply(200, { + ok: true, + ts: '123.123', + }) + .post('/api/chat.stopStream') + .reply(200, { + ok: true, + }); + const streamer = client.chatStream({ + buffer_size: 5, + channel: 'C0123456789', + thread_ts: '123.000', + recipient_team_id: 'T0123456789', + recipient_user_id: 'U0123456789', + }); + assert.strictEqual(streamer.ts, undefined); + + await streamer.append({ markdown_text: 'hello!' }); + assert.strictEqual(streamer.ts, '123.123'); + + await streamer.stop(); + assert.strictEqual(streamer.ts, '123.123'); + scope.done(); + }); + it('streams a long message', async () => { const contextActionsBlock: ContextActionsBlock = { type: 'context_actions', diff --git a/packages/web-api/src/chat-stream.ts b/packages/web-api/src/chat-stream.ts index 314117f2f..a454b2ce1 100644 --- a/packages/web-api/src/chat-stream.ts +++ b/packages/web-api/src/chat-stream.ts @@ -54,6 +54,16 @@ export class ChatStreamer { this.streamArgs = args; } + /** + * @description The message timestamp of the stream. Returns `undefined` until the first flush + * (when `chat.startStream` is called). Can be used with `chat.update` as a fallback if the + * stream expires server-side. + * @see {@link https://docs.slack.dev/reference/methods/chat.update} + */ + get ts(): string | undefined { + return this.streamTs; + } + /** * Append to the stream. * From 3a3a0d6806320a752e51888e09bdc5c45bc91c1c Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Fri, 29 May 2026 17:04:18 -0600 Subject: [PATCH 2/2] add changeset --- .changeset/expose-ts-on-chat-streamer.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .changeset/expose-ts-on-chat-streamer.md diff --git a/.changeset/expose-ts-on-chat-streamer.md b/.changeset/expose-ts-on-chat-streamer.md new file mode 100644 index 000000000..0bacd91a1 --- /dev/null +++ b/.changeset/expose-ts-on-chat-streamer.md @@ -0,0 +1,24 @@ +--- +"@slack/web-api": minor +--- + +feat: expose public read-only `ts` getter on `ChatStreamer` for fallback to [`chat.update`](https://docs.slack.dev/reference/methods/chat.update) when a stream expires server-side + +```js +import { WebClient } from "@slack/web-api"; + +const client = new WebClient(process.env.SLACK_BOT_TOKEN); + +const streamer = client.chatStream({ + channel: "C0123456789", + thread_ts: "1700000001.123456", + recipient_team_id: "T0123456789", + recipient_user_id: "U0123456789", +}); + +await streamer.append({ markdown_text: "hello!" }); +// streamer.ts is now set after the first flush +console.log(streamer.ts); + +await streamer.stop(); +```