From cd2bb184eac94a84fea41371e6ec5cb56a07b034 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 13 May 2026 00:40:34 -0700 Subject: [PATCH 1/9] feat(web-api): add highlight_type to files.completeUploadExternal and filesUploadV2 Co-Authored-By: Claude --- packages/web-api/src/file-upload.ts | 4 +++- packages/web-api/src/types/request/files.ts | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/web-api/src/file-upload.ts b/packages/web-api/src/file-upload.ts index b8b44e424..81a03e7ea 100644 --- a/packages/web-api/src/file-upload.ts +++ b/packages/web-api/src/file-upload.ts @@ -30,6 +30,7 @@ export async function getFileUploadJob( blocks: options.blocks, channel_id: options.channels ?? options.channel_id, filename: options.filename ?? fileName, + highlight_type: options.highlight_type, initial_comment: options.initial_comment, snippet_type: options.snippet_type, title: options.title ?? options.filename ?? fileName, // default title to filename unless otherwise specified @@ -234,7 +235,7 @@ export function getAllFileUploadsToComplete( ): Record { const toComplete: Record = {}; for (const upload of fileUploads) { - const { blocks, channel_id, thread_ts, initial_comment, file_id, title } = upload; + const { blocks, channel_id, thread_ts, highlight_type, initial_comment, file_id, title } = upload; if (file_id) { const compareString = `:::${channel_id}:::${thread_ts}:::${initial_comment}:::${JSON.stringify(blocks)}`; // biome-ignore lint/suspicious/noPrototypeBuiltins: TODO use hasOwn instead of hasOwnProperty @@ -243,6 +244,7 @@ export function getAllFileUploadsToComplete( files: [{ id: file_id, title }], channel_id, blocks, + highlight_type, initial_comment, }; if (thread_ts && channel_id) { diff --git a/packages/web-api/src/types/request/files.ts b/packages/web-api/src/types/request/files.ts index e891bff6d..52e266849 100644 --- a/packages/web-api/src/types/request/files.ts +++ b/packages/web-api/src/types/request/files.ts @@ -70,6 +70,11 @@ export type FilesCompleteUploadExternalArguments = FileDestinationArgument & * @example [{"id":"F044GKUHN9Z", "title":"slack-test"}] **/ files: [FileUploadComplete, ...FileUploadComplete[]]; + /** + * @description Syntax type of the snippet being uploaded. E.g. `python`. + * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal#arg_highlight_type} + */ + highlight_type?: string; /** @description The message text introducing the file in the specified channel. */ initial_comment?: string; /** @@ -168,6 +173,11 @@ export type FileUploadV2 = FileUpload & { channel_id?: string; /** @deprecated use channel_id instead */ channels?: string; + /** + * @description Syntax type of the snippet being uploaded. E.g. `python`. + * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal#arg_highlight_type} + */ + highlight_type?: string; /** @description Syntax type of the snippet being uploaded. E.g. `python`. */ snippet_type?: string; }; From 69a4357a00c483cb73d3e645f362396851a9787a Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 13 May 2026 01:08:11 -0700 Subject: [PATCH 2/9] test: confirm arguments of filesUploadV2 reach the complete method --- packages/web-api/src/file-upload.test.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/web-api/src/file-upload.test.ts b/packages/web-api/src/file-upload.test.ts index bc14885d5..a0c1d8059 100644 --- a/packages/web-api/src/file-upload.test.ts +++ b/packages/web-api/src/file-upload.test.ts @@ -316,6 +316,28 @@ describe('file-upload', () => { }); }); describe('getAllFileUploadsToComplete', () => { + it('should pass all expected properties through to the completion job', () => { + const fileUploadJob1 = { + file: Buffer.from('test'), + filename: 'test.py', + file_id: 'id1', + title: 'test1', + channel_id: 'C123', + thread_ts: '1.0', + initial_comment: 'Here is a snippet', + highlight_type: 'python', + blocks: [{ type: 'section', text: { type: 'plain_text', text: 'hello' } }], + }; + const toComplete = getAllFileUploadsToComplete([fileUploadJob1]); + const job = Object.values(toComplete)[0]; + assert.strictEqual(job.files[0].id, 'id1'); + assert.strictEqual(job.files[0].title, 'test1'); + assert.strictEqual(job.channel_id, 'C123'); + assert.strictEqual(job.thread_ts, '1.0'); + assert.strictEqual(job.initial_comment, 'Here is a snippet'); + assert.strictEqual(job.highlight_type, 'python'); + assert.deepStrictEqual(job.blocks, fileUploadJob1.blocks); + }); describe('when channel_id is the same', () => { it('should group uploads with matching thread_ts and initial_comment together', () => { const fileUploadJob1 = { From f756f41c48a6acd10adb4030eb893a3ab5412fe3 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 13 May 2026 01:08:31 -0700 Subject: [PATCH 3/9] docs: update link to argument reference without anchor --- packages/web-api/src/types/request/files.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web-api/src/types/request/files.ts b/packages/web-api/src/types/request/files.ts index 52e266849..705d14b64 100644 --- a/packages/web-api/src/types/request/files.ts +++ b/packages/web-api/src/types/request/files.ts @@ -72,7 +72,7 @@ export type FilesCompleteUploadExternalArguments = FileDestinationArgument & files: [FileUploadComplete, ...FileUploadComplete[]]; /** * @description Syntax type of the snippet being uploaded. E.g. `python`. - * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal#arg_highlight_type} + * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal} */ highlight_type?: string; /** @description The message text introducing the file in the specified channel. */ @@ -175,7 +175,7 @@ export type FileUploadV2 = FileUpload & { channels?: string; /** * @description Syntax type of the snippet being uploaded. E.g. `python`. - * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal#arg_highlight_type} + * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal} */ highlight_type?: string; /** @description Syntax type of the snippet being uploaded. E.g. `python`. */ From 9dfb9719bacf513f0cbe3f96a312567f97f333ca Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 13 May 2026 01:10:38 -0700 Subject: [PATCH 4/9] chore: changeset --- .changeset/deep-knives-tie.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .changeset/deep-knives-tie.md diff --git a/.changeset/deep-knives-tie.md b/.changeset/deep-knives-tie.md new file mode 100644 index 000000000..f6adca519 --- /dev/null +++ b/.changeset/deep-knives-tie.md @@ -0,0 +1,19 @@ +--- +"@slack/web-api": patch +--- + +feat: add `highlight_type` to [`files.completeUploadExternal`](https://docs.slack.dev/reference/methods/files.completeUploadExternal) and [`filesUploadV2`](https://docs.slack.dev/tools/node-slack-sdk/web-api#upload-a-file) for optimistic rendering + +```js +import { WebClient } from "@slack/web-api"; + +const client = new WebClient(process.env.SLACK_BOT_TOKEN); + +await client.filesUploadV2({ + channel_id: "C0123456789", + file: "app.js", + filename: "app.js", + title: "Example Snippet", + highlight_type: "javascript", +}); +``` From 37f8dfa3a10b1bd14e8d7504ee909ad33bbfeabe Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Wed, 13 May 2026 18:16:55 -0700 Subject: [PATCH 5/9] chore: update semantic version bump to minor --- .changeset/deep-knives-tie.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/deep-knives-tie.md b/.changeset/deep-knives-tie.md index f6adca519..7c197d64a 100644 --- a/.changeset/deep-knives-tie.md +++ b/.changeset/deep-knives-tie.md @@ -1,5 +1,5 @@ --- -"@slack/web-api": patch +"@slack/web-api": minor --- feat: add `highlight_type` to [`files.completeUploadExternal`](https://docs.slack.dev/reference/methods/files.completeUploadExternal) and [`filesUploadV2`](https://docs.slack.dev/tools/node-slack-sdk/web-api#upload-a-file) for optimistic rendering From 03ca84d29157c7f167a45d8ad254f1079421e5a4 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 14 May 2026 14:55:32 -0700 Subject: [PATCH 6/9] fix: use highlight_type in files array --- packages/web-api/src/file-upload.test.ts | 2 +- packages/web-api/src/file-upload.ts | 4 ++-- packages/web-api/src/types/request/files.ts | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/web-api/src/file-upload.test.ts b/packages/web-api/src/file-upload.test.ts index a0c1d8059..3b0d2f26c 100644 --- a/packages/web-api/src/file-upload.test.ts +++ b/packages/web-api/src/file-upload.test.ts @@ -335,7 +335,7 @@ describe('file-upload', () => { assert.strictEqual(job.channel_id, 'C123'); assert.strictEqual(job.thread_ts, '1.0'); assert.strictEqual(job.initial_comment, 'Here is a snippet'); - assert.strictEqual(job.highlight_type, 'python'); + assert.strictEqual(job.files[0].highlight_type, 'python'); assert.deepStrictEqual(job.blocks, fileUploadJob1.blocks); }); describe('when channel_id is the same', () => { diff --git a/packages/web-api/src/file-upload.ts b/packages/web-api/src/file-upload.ts index 81a03e7ea..b6beade2d 100644 --- a/packages/web-api/src/file-upload.ts +++ b/packages/web-api/src/file-upload.ts @@ -241,10 +241,9 @@ export function getAllFileUploadsToComplete( // biome-ignore lint/suspicious/noPrototypeBuiltins: TODO use hasOwn instead of hasOwnProperty if (!Object.prototype.hasOwnProperty.call(toComplete, compareString)) { toComplete[compareString] = { - files: [{ id: file_id, title }], + files: [{ id: file_id, title, highlight_type }], channel_id, blocks, - highlight_type, initial_comment, }; if (thread_ts && channel_id) { @@ -264,6 +263,7 @@ export function getAllFileUploadsToComplete( toComplete[compareString].files.push({ id: file_id, title, + highlight_type, }); } } else { diff --git a/packages/web-api/src/types/request/files.ts b/packages/web-api/src/types/request/files.ts index 705d14b64..519c6ba3b 100644 --- a/packages/web-api/src/types/request/files.ts +++ b/packages/web-api/src/types/request/files.ts @@ -34,6 +34,11 @@ export interface FileType { export interface FileUploadComplete { /** @description Encoded file ID. */ id: string; + /** + * @description Optional highlight type hint for the file. The upload processing job may overwrite this value. + * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal} + */ + highlight_type?: string; /** @description File title. */ title?: string; } @@ -70,11 +75,6 @@ export type FilesCompleteUploadExternalArguments = FileDestinationArgument & * @example [{"id":"F044GKUHN9Z", "title":"slack-test"}] **/ files: [FileUploadComplete, ...FileUploadComplete[]]; - /** - * @description Syntax type of the snippet being uploaded. E.g. `python`. - * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal} - */ - highlight_type?: string; /** @description The message text introducing the file in the specified channel. */ initial_comment?: string; /** From f608388252e508ab3a45c9abb53740a23cdba92d Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 14 May 2026 15:11:35 -0700 Subject: [PATCH 7/9] docs: update changeset example to use image upload Co-Authored-By: Claude --- .changeset/deep-knives-tie.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/deep-knives-tie.md b/.changeset/deep-knives-tie.md index 7c197d64a..5adf33e8f 100644 --- a/.changeset/deep-knives-tie.md +++ b/.changeset/deep-knives-tie.md @@ -11,9 +11,9 @@ const client = new WebClient(process.env.SLACK_BOT_TOKEN); await client.filesUploadV2({ channel_id: "C0123456789", - file: "app.js", - filename: "app.js", - title: "Example Snippet", - highlight_type: "javascript", + file: "./image.png", + filename: "image.png", + title: "Image Upload", + highlight_type: "png", }); ``` From e7bad5d3880ad24d96ac0ee22a8d557048edcf75 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 14 May 2026 15:12:53 -0700 Subject: [PATCH 8/9] docs: align highlight_type description on FileUploadV2 Co-Authored-By: Claude --- packages/web-api/src/types/request/files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-api/src/types/request/files.ts b/packages/web-api/src/types/request/files.ts index 519c6ba3b..3d05ddd7f 100644 --- a/packages/web-api/src/types/request/files.ts +++ b/packages/web-api/src/types/request/files.ts @@ -174,7 +174,7 @@ export type FileUploadV2 = FileUpload & { /** @deprecated use channel_id instead */ channels?: string; /** - * @description Syntax type of the snippet being uploaded. E.g. `python`. + * @description Optional highlight type hint for the file. The upload processing job may overwrite this value. * @see {@link https://docs.slack.dev/reference/methods/files.completeUploadExternal} */ highlight_type?: string; From 50af6d939d70a6b1914a7bd2376268667d31e9d5 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 14 May 2026 15:20:25 -0700 Subject: [PATCH 9/9] test: use image upload in highlight_type test Co-Authored-By: Claude --- packages/web-api/src/file-upload.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/web-api/src/file-upload.test.ts b/packages/web-api/src/file-upload.test.ts index 3b0d2f26c..e9902e333 100644 --- a/packages/web-api/src/file-upload.test.ts +++ b/packages/web-api/src/file-upload.test.ts @@ -319,13 +319,13 @@ describe('file-upload', () => { it('should pass all expected properties through to the completion job', () => { const fileUploadJob1 = { file: Buffer.from('test'), - filename: 'test.py', + filename: 'image.png', file_id: 'id1', title: 'test1', channel_id: 'C123', thread_ts: '1.0', - initial_comment: 'Here is a snippet', - highlight_type: 'python', + initial_comment: 'Here is an image', + highlight_type: 'png', blocks: [{ type: 'section', text: { type: 'plain_text', text: 'hello' } }], }; const toComplete = getAllFileUploadsToComplete([fileUploadJob1]); @@ -334,8 +334,8 @@ describe('file-upload', () => { assert.strictEqual(job.files[0].title, 'test1'); assert.strictEqual(job.channel_id, 'C123'); assert.strictEqual(job.thread_ts, '1.0'); - assert.strictEqual(job.initial_comment, 'Here is a snippet'); - assert.strictEqual(job.files[0].highlight_type, 'python'); + assert.strictEqual(job.initial_comment, 'Here is an image'); + assert.strictEqual(job.files[0].highlight_type, 'png'); assert.deepStrictEqual(job.blocks, fileUploadJob1.blocks); }); describe('when channel_id is the same', () => {