From 6760c1c13d9fdcd605b0f9ac096a26bf91b951b8 Mon Sep 17 00:00:00 2001 From: Elarwei Date: Sat, 28 Feb 2026 12:30:26 +0800 Subject: [PATCH] fix(feishu): address second-round codex bot review feedback P1 - Reject single oversized subtrees in batch insert (docx-batch-insert.ts): A first-level block whose descendant count exceeds BATCH_SIZE (1000) cannot be split atomically (e.g. a very large table). Previously such a block was silently added to the current batch and sent as an oversized request, violating the API limit. Now throws a descriptive error so callers know to reduce the content size. P2 - Preserve unmatched brackets in color markup parser (docx-color-text.ts): Text like 'Revenue [Q1] up' contains a bracket pair with no matching '[/...]' closer. The original regex dropped the '[' character in this case, silently corrupting the text. Fixed by appending '|\[' to the plain-text alternative so any '[' that does not open a complete tag is captured as literal text. --- extensions/feishu/src/docx-batch-insert.ts | 10 ++++++++++ extensions/feishu/src/docx-color-text.ts | 12 +++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/extensions/feishu/src/docx-batch-insert.ts b/extensions/feishu/src/docx-batch-insert.ts index d629cd34fad..e38552a4857 100644 --- a/extensions/feishu/src/docx-batch-insert.ts +++ b/extensions/feishu/src/docx-batch-insert.ts @@ -128,6 +128,16 @@ export async function insertBlocksInBatches( const descendants = collectDescendants(blocks, [firstLevelId]); const newBlocks = descendants.filter((b) => !usedBlockIds.has(b.block_id)); + // A single block whose subtree exceeds the API limit cannot be split + // (a table or other compound block must be inserted atomically). + if (newBlocks.length > BATCH_SIZE) { + throw new Error( + `Block "${firstLevelId}" has ${newBlocks.length} descendants, which exceeds the ` + + `Feishu API limit of ${BATCH_SIZE} blocks per request. ` + + `Please split the content into smaller sections.`, + ); + } + // If adding this first-level block would exceed limit, start new batch if ( currentBatch.blocks.length + newBlocks.length > BATCH_SIZE && diff --git a/extensions/feishu/src/docx-color-text.ts b/extensions/feishu/src/docx-color-text.ts index b25a3633553..0ea6f2c6a5e 100644 --- a/extensions/feishu/src/docx-color-text.ts +++ b/extensions/feishu/src/docx-color-text.ts @@ -55,11 +55,13 @@ interface Segment { */ export function parseColorMarkup(content: string): Segment[] { const segments: Segment[] = []; - // Match [tag]...[/tag] or plain text between tags. - // The closing tag name is intentionally not validated against the opening tag: - // mismatched tags like [red]text[/green] are treated as [red]text[/red] — - // the opening tag's style is applied and the closing tag is consumed. - const tagPattern = /\[([^\]]+)\](.*?)\[\/(?:[^\]]+)\]|([^[]+)/gs; + // Match [tag]...[/tag], plain text, or a bare '[' that is not part of a + // complete tag pair. Without the trailing `|\[` fallback, a '[' that has no + // matching '[/...]' closer (e.g. "[Q1]" with no "[/...]") would be silently + // dropped, corrupting the surrounding text. The closing tag name is not + // validated against the opening tag: [red]text[/green] is treated as + // [red]text[/red] — opening tag style applies, closing tag is consumed. + const tagPattern = /\[([^\]]+)\](.*?)\[\/(?:[^\]]+)\]|([^[]+|\[)/gs; let match; while ((match = tagPattern.exec(content)) !== null) {