diff --git a/src/lib/sanitize-blocks.ts b/src/lib/sanitize-blocks.ts index 6f9e377..6a37a2f 100644 --- a/src/lib/sanitize-blocks.ts +++ b/src/lib/sanitize-blocks.ts @@ -28,7 +28,10 @@ const IMAGE_KEYS = new Set(['image_url']); */ const RETRIEVAL_ONLY_KEYS = new Map>([ // Covers both the image block and the image element — both `type: 'image'`. - ['image', new Set(['image_width', 'image_height', 'image_bytes', 'fallback', 'is_animated'])] + ['image', new Set(['image_width', 'image_height', 'image_bytes', 'fallback', 'is_animated'])], + // Slack renders the chart server-side and attaches the rendered previews on + // retrieval; sending them back is rejected as `unknown property 'preview_images'`. + ['data_visualization', new Set(['preview_images'])] ]); /** diff --git a/test/sanitize-blocks.test.ts b/test/sanitize-blocks.test.ts index 152c258..9bbe990 100644 --- a/test/sanitize-blocks.test.ts +++ b/test/sanitize-blocks.test.ts @@ -136,6 +136,30 @@ describe('retrieval-only image metadata', () => { expect(Object.hasOwn(out.elements[0], 'image_bytes')).toBe(false); }); + it('drops preview_images Slack adds to retrieved data_visualization blocks', () => { + const block = { + type: 'data_visualization', + title: 'Weekly active users', + chart: { type: 'line', series: [] }, + preview_images: [{ url: 'https://slack.example/chart.png' }] + } as unknown as SupportedBlock; + const out = sanitizeBlock(block) as Record; + expect(Object.hasOwn(out, 'preview_images')).toBe(false); + expect(out.title).toBe('Weekly active users'); + expect(out.chart).toBeDefined(); + }); + + it('leaves preview_images untouched on non-data_visualization objects', () => { + const block = { + type: 'section', + text: { type: 'mrkdwn', text: 'hi' }, + preview_images: ['keep me'] + } as unknown as SupportedBlock; + const out = sanitizeBlock(block) as Record; + expect(out.preview_images).toEqual(['keep me']); + expect(out).toBe(block); + }); + it('leaves the same field names untouched on non-image objects', () => { const block = { type: 'section',