import type * as Lark from "@larksuiteoapi/node-sdk"; import { readStringValue } from "openclaw/plugin-sdk/text-runtime"; import { cleanBlocksForDescendant } from "./docx-table-ops.js"; import type { FeishuDocxBlock, FeishuDocxBlockChild } from "./docx-types.js";
export const BATCH_SIZE = 1000; // Feishu API limit per request
type Logger = { info?: (msg: string) => void };
type DocxDescendantCreatePayload = NonNullable<
Parameters<Lark.Client["docx"]["documentBlockDescendant"]["create"]>[0]
>;
type DocxDescendantCreateBlock = NonNullable<
NonNullable<DocxDescendantCreatePayload["data"]>["descendants"]
>[number];
function collect(blockId: string) { if (visited.has(blockId)) { return;
}
visited.add(blockId);
const block = blockMap.get(blockId); if (!block) { return;
}
result.push(block);
// Recursively collect children const children = block.children; if (Array.isArray(children)) { for (const childId of children) {
collect(childId);
}
} elseif (typeof children === "string") {
collect(children);
}
}
// Build batches ensuring each batch has ≤1000 total descendants const batches: Array<{ firstLevelIds: string[]; blocks: FeishuDocxBlock[] }> = [];
let currentBatch: { firstLevelIds: string[]; blocks: FeishuDocxBlock[] } = {
firstLevelIds: [],
blocks: [],
}; const usedBlockIds = new Set<string>(); const blockMap = new Map<string, FeishuDocxBlock>(); for (const block of blocks) { if (block.block_id) {
blockMap.set(block.block_id, block);
}
}
for (const firstLevelId of firstLevelBlockIds) { const descendants = collectDescendants(blockMap, firstLevelId); const newBlocks = descendants.filter((b) => b.block_id && !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) { thrownew 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 &&
currentBatch.blocks.length > 0
) {
batches.push(currentBatch);
currentBatch = { firstLevelIds: [], blocks: [] };
}
// Add to current batch
currentBatch.firstLevelIds.push(firstLevelId); for (const block of newBlocks) {
currentBatch.blocks.push(block); if (block.block_id) {
usedBlockIds.add(block.block_id);
}
}
}
// Don't forget the last batch if (currentBatch.blocks.length > 0) {
batches.push(currentBatch);
}
// Insert each batch, advancing index for position-aware inserts. // When startIndex == -1 (append to end), each batch appends after the previous. // When startIndex >= 0, each batch starts at startIndex + count of first-level IDs already inserted.
let currentIndex = startIndex; for (let i = 0; i < batches.length; i++) { const batch = batches[i];
logger?.info?.(
`feishu_doc: Inserting batch ${i + 1}/${batches.length} (${batch.blocks.length} blocks)...`,
);
// Advance index only for explicit positions; -1 always means "after last inserted" if (currentIndex !== -1) {
currentIndex += batch.firstLevelIds.length;
}
}
return { children: allChildren, skipped: [] };
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.