@max1874/feishu 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/docx.ts +299 -91
package/package.json
CHANGED
package/src/docx.ts
CHANGED
|
@@ -43,6 +43,60 @@ function extractImageUrls(markdown: string): string[] {
|
|
|
43
43
|
return urls;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/** Extract text content from a block for sorting */
|
|
47
|
+
function extractBlockText(block: any): string {
|
|
48
|
+
const elements =
|
|
49
|
+
block.text?.elements ??
|
|
50
|
+
block.heading1?.elements ??
|
|
51
|
+
block.heading2?.elements ??
|
|
52
|
+
block.heading3?.elements ??
|
|
53
|
+
block.bullet?.elements ??
|
|
54
|
+
block.ordered?.elements ??
|
|
55
|
+
block.quote?.elements ??
|
|
56
|
+
block.todo?.elements ??
|
|
57
|
+
[];
|
|
58
|
+
return elements
|
|
59
|
+
.filter((e: any) => e.text_run)
|
|
60
|
+
.map((e: any) => e.text_run.content)
|
|
61
|
+
.join("");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sort blocks by their position in firstLevelBlockIds.
|
|
66
|
+
* The Convert API returns blocks in arbitrary order, but provides
|
|
67
|
+
* first_level_block_ids in the correct document order.
|
|
68
|
+
*/
|
|
69
|
+
function sortBlocksByFirstLevelIds(blocks: any[], firstLevelBlockIds: string[]): any[] {
|
|
70
|
+
// Build a map of block_id -> block
|
|
71
|
+
const blockMap = new Map<string, any>();
|
|
72
|
+
for (const block of blocks) {
|
|
73
|
+
if (block.block_id) {
|
|
74
|
+
blockMap.set(block.block_id, block);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Reorder blocks according to firstLevelBlockIds
|
|
79
|
+
const sortedBlocks: any[] = [];
|
|
80
|
+
const usedIds = new Set<string>();
|
|
81
|
+
|
|
82
|
+
for (const id of firstLevelBlockIds) {
|
|
83
|
+
const block = blockMap.get(id);
|
|
84
|
+
if (block) {
|
|
85
|
+
sortedBlocks.push(block);
|
|
86
|
+
usedIds.add(id);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Append any remaining blocks (e.g., child blocks like TableCell)
|
|
91
|
+
for (const block of blocks) {
|
|
92
|
+
if (block.block_id && !usedIds.has(block.block_id)) {
|
|
93
|
+
sortedBlocks.push(block);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return sortedBlocks;
|
|
98
|
+
}
|
|
99
|
+
|
|
46
100
|
const BLOCK_TYPE_NAMES: Record<number, string> = {
|
|
47
101
|
1: "Page",
|
|
48
102
|
2: "Text",
|
|
@@ -75,12 +129,63 @@ interface TableData {
|
|
|
75
129
|
cells: string[][]; // 2D array of cell text content
|
|
76
130
|
}
|
|
77
131
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
/** A content item that can be either a regular block or a table */
|
|
133
|
+
type ContentItem =
|
|
134
|
+
| { type: "block"; block: any }
|
|
135
|
+
| { type: "table"; table: TableData };
|
|
136
|
+
|
|
137
|
+
/** Extract table data from a table block */
|
|
138
|
+
function extractTableData(tableBlock: any, blockMap: Map<string, any>): { tableData: TableData; relatedIds: Set<string> } {
|
|
139
|
+
const relatedIds = new Set<string>();
|
|
140
|
+
relatedIds.add(tableBlock.block_id);
|
|
141
|
+
|
|
142
|
+
const tableInfo = tableBlock.table;
|
|
143
|
+
const rowSize = tableInfo?.property?.row_size ?? 0;
|
|
144
|
+
const colSize = tableInfo?.property?.column_size ?? 0;
|
|
145
|
+
|
|
146
|
+
const cells: string[][] = Array.from({ length: rowSize }, () =>
|
|
147
|
+
Array.from({ length: colSize }, () => ""),
|
|
148
|
+
);
|
|
82
149
|
|
|
83
|
-
|
|
150
|
+
const cellIds: string[] = tableInfo?.cells ?? [];
|
|
151
|
+
let cellIndex = 0;
|
|
152
|
+
|
|
153
|
+
for (let row = 0; row < rowSize; row++) {
|
|
154
|
+
for (let col = 0; col < colSize; col++) {
|
|
155
|
+
if (cellIndex >= cellIds.length) break;
|
|
156
|
+
|
|
157
|
+
const cellId = cellIds[cellIndex];
|
|
158
|
+
relatedIds.add(cellId);
|
|
159
|
+
|
|
160
|
+
const cellBlock = blockMap.get(cellId);
|
|
161
|
+
if (cellBlock?.block_type === TABLE_CELL_BLOCK_TYPE) {
|
|
162
|
+
const childIds: string[] = cellBlock.children ?? [];
|
|
163
|
+
const textParts: string[] = [];
|
|
164
|
+
|
|
165
|
+
for (const childId of childIds) {
|
|
166
|
+
relatedIds.add(childId);
|
|
167
|
+
const textBlock = blockMap.get(childId);
|
|
168
|
+
if (textBlock?.block_type === 2 && textBlock.text?.elements) {
|
|
169
|
+
const text = textBlock.text.elements
|
|
170
|
+
.filter((e: any) => e.text_run)
|
|
171
|
+
.map((e: any) => e.text_run.content)
|
|
172
|
+
.join("");
|
|
173
|
+
textParts.push(text);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
cells[row][col] = textParts.join("\n");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
cellIndex++;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { tableData: { rowSize, colSize, cells }, relatedIds };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Convert blocks to content items, preserving order with tables in correct positions */
|
|
188
|
+
function convertBlocksToContentItems(blocks: any[]): ContentItem[] {
|
|
84
189
|
const blockMap = new Map<string, any>();
|
|
85
190
|
for (const block of blocks) {
|
|
86
191
|
if (block.block_id) {
|
|
@@ -88,69 +193,52 @@ function extractTableFromBlocks(blocks: any[]): { tables: TableData[]; otherBloc
|
|
|
88
193
|
}
|
|
89
194
|
}
|
|
90
195
|
|
|
91
|
-
// Track which blocks are part of tables
|
|
196
|
+
// Track which blocks are part of tables
|
|
92
197
|
const tableRelatedIds = new Set<string>();
|
|
198
|
+
const tableDataMap = new Map<string, TableData>();
|
|
93
199
|
|
|
200
|
+
// First pass: extract all table data and mark related IDs
|
|
94
201
|
for (const block of blocks) {
|
|
95
202
|
if (block.block_type === TABLE_BLOCK_TYPE) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const rowSize = tableInfo.property?.row_size ?? 0;
|
|
101
|
-
const colSize = tableInfo.property?.column_size ?? 0;
|
|
102
|
-
|
|
103
|
-
// Initialize cells array
|
|
104
|
-
const cells: string[][] = Array.from({ length: rowSize }, () =>
|
|
105
|
-
Array.from({ length: colSize }, () => ""),
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// table.cells is a flat array of cell block IDs in row-major order
|
|
109
|
-
const cellIds: string[] = tableInfo.cells ?? [];
|
|
110
|
-
|
|
111
|
-
let cellIndex = 0;
|
|
112
|
-
for (let row = 0; row < rowSize; row++) {
|
|
113
|
-
for (let col = 0; col < colSize; col++) {
|
|
114
|
-
if (cellIndex >= cellIds.length) break;
|
|
115
|
-
|
|
116
|
-
const cellId = cellIds[cellIndex];
|
|
117
|
-
tableRelatedIds.add(cellId);
|
|
118
|
-
|
|
119
|
-
// Find the TableCell block
|
|
120
|
-
const cellBlock = blockMap.get(cellId);
|
|
121
|
-
if (cellBlock?.block_type === TABLE_CELL_BLOCK_TYPE) {
|
|
122
|
-
// Get text content from cell's children (Text blocks)
|
|
123
|
-
const childIds: string[] = cellBlock.children ?? [];
|
|
124
|
-
const textParts: string[] = [];
|
|
125
|
-
|
|
126
|
-
for (const childId of childIds) {
|
|
127
|
-
tableRelatedIds.add(childId);
|
|
128
|
-
const textBlock = blockMap.get(childId);
|
|
129
|
-
if (textBlock?.block_type === 2 && textBlock.text?.elements) {
|
|
130
|
-
const text = textBlock.text.elements
|
|
131
|
-
.filter((e: any) => e.text_run)
|
|
132
|
-
.map((e: any) => e.text_run.content)
|
|
133
|
-
.join("");
|
|
134
|
-
textParts.push(text);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
cells[row][col] = textParts.join("\n");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
cellIndex++;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
tables.push({ rowSize, colSize, cells });
|
|
203
|
+
const { tableData, relatedIds } = extractTableData(block, blockMap);
|
|
204
|
+
tableDataMap.set(block.block_id, tableData);
|
|
205
|
+
for (const id of relatedIds) {
|
|
206
|
+
tableRelatedIds.add(id);
|
|
146
207
|
}
|
|
147
208
|
}
|
|
148
209
|
}
|
|
149
210
|
|
|
150
|
-
//
|
|
211
|
+
// Second pass: build content items in order
|
|
212
|
+
const items: ContentItem[] = [];
|
|
151
213
|
for (const block of blocks) {
|
|
152
|
-
if (
|
|
153
|
-
|
|
214
|
+
if (tableRelatedIds.has(block.block_id)) {
|
|
215
|
+
// If this is a table block itself, add the table item
|
|
216
|
+
if (block.block_type === TABLE_BLOCK_TYPE) {
|
|
217
|
+
const tableData = tableDataMap.get(block.block_id);
|
|
218
|
+
if (tableData) {
|
|
219
|
+
items.push({ type: "table", table: tableData });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Skip table-related blocks (cells, cell contents)
|
|
223
|
+
} else {
|
|
224
|
+
items.push({ type: "block", block });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return items;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Extract table data from converted blocks (flat list with ID references) - legacy function */
|
|
232
|
+
function extractTableFromBlocks(blocks: any[]): { tables: TableData[]; otherBlocks: any[] } {
|
|
233
|
+
const items = convertBlocksToContentItems(blocks);
|
|
234
|
+
const tables: TableData[] = [];
|
|
235
|
+
const otherBlocks: any[] = [];
|
|
236
|
+
|
|
237
|
+
for (const item of items) {
|
|
238
|
+
if (item.type === "table") {
|
|
239
|
+
tables.push(item.table);
|
|
240
|
+
} else {
|
|
241
|
+
otherBlocks.push(item.block);
|
|
154
242
|
}
|
|
155
243
|
}
|
|
156
244
|
|
|
@@ -183,10 +271,14 @@ async function convertMarkdown(client: Lark.Client, markdown: string) {
|
|
|
183
271
|
data: { content_type: "markdown", content: markdown },
|
|
184
272
|
});
|
|
185
273
|
if (res.code !== 0) throw new Error(res.msg);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
274
|
+
|
|
275
|
+
const rawBlocks = res.data?.blocks ?? [];
|
|
276
|
+
const firstLevelBlockIds = res.data?.first_level_block_ids ?? [];
|
|
277
|
+
|
|
278
|
+
// Sort blocks according to first_level_block_ids (Convert API returns blocks in arbitrary order)
|
|
279
|
+
const blocks = sortBlocksByFirstLevelIds(rawBlocks, firstLevelBlockIds);
|
|
280
|
+
|
|
281
|
+
return { blocks, firstLevelBlockIds };
|
|
190
282
|
}
|
|
191
283
|
|
|
192
284
|
/** Insert blocks as children of a parent block (with batching for >50 blocks) */
|
|
@@ -231,6 +323,40 @@ async function insertBlocks(
|
|
|
231
323
|
return { children: allChildren, skipped };
|
|
232
324
|
}
|
|
233
325
|
|
|
326
|
+
/** Insert blocks at a specific index (with batching for >50 blocks) */
|
|
327
|
+
async function insertBlocksAtIndex(
|
|
328
|
+
client: Lark.Client,
|
|
329
|
+
docToken: string,
|
|
330
|
+
blocks: any[],
|
|
331
|
+
startIndex: number,
|
|
332
|
+
parentBlockId?: string,
|
|
333
|
+
): Promise<{ children: any[]; skipped: string[] }> {
|
|
334
|
+
const { cleaned, skipped } = cleanBlocksForInsert(blocks);
|
|
335
|
+
const blockId = parentBlockId ?? docToken;
|
|
336
|
+
|
|
337
|
+
if (cleaned.length === 0) {
|
|
338
|
+
return { children: [], skipped };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const BATCH_SIZE = 50;
|
|
342
|
+
const allChildren: any[] = [];
|
|
343
|
+
let insertIndex = startIndex;
|
|
344
|
+
|
|
345
|
+
for (let i = 0; i < cleaned.length; i += BATCH_SIZE) {
|
|
346
|
+
const batch = cleaned.slice(i, i + BATCH_SIZE);
|
|
347
|
+
const res = await client.docx.documentBlockChildren.create({
|
|
348
|
+
path: { document_id: docToken, block_id: blockId },
|
|
349
|
+
data: { children: batch, index: insertIndex },
|
|
350
|
+
});
|
|
351
|
+
if (res.code !== 0) throw new Error(res.msg);
|
|
352
|
+
const inserted = res.data?.children ?? [];
|
|
353
|
+
allChildren.push(...inserted);
|
|
354
|
+
insertIndex += inserted.length;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return { children: allChildren, skipped };
|
|
358
|
+
}
|
|
359
|
+
|
|
234
360
|
/** Delete all child blocks from a parent */
|
|
235
361
|
async function clearDocumentContent(client: Lark.Client, docToken: string) {
|
|
236
362
|
const existing = await client.docx.documentBlock.list({
|
|
@@ -294,6 +420,7 @@ async function createEmptyTable(
|
|
|
294
420
|
parentBlockId: string,
|
|
295
421
|
rowSize: number,
|
|
296
422
|
colSize: number,
|
|
423
|
+
index?: number,
|
|
297
424
|
): Promise<string | null> {
|
|
298
425
|
const tableBlock = {
|
|
299
426
|
block_type: TABLE_BLOCK_TYPE,
|
|
@@ -309,7 +436,7 @@ async function createEmptyTable(
|
|
|
309
436
|
|
|
310
437
|
const res = await client.docx.documentBlockChildren.create({
|
|
311
438
|
path: { document_id: docToken, block_id: parentBlockId },
|
|
312
|
-
data: { children: [tableBlock] },
|
|
439
|
+
data: { children: [tableBlock], ...(index !== undefined && { index }) },
|
|
313
440
|
});
|
|
314
441
|
|
|
315
442
|
if (res.code !== 0) {
|
|
@@ -421,6 +548,34 @@ async function createAndFillTable(
|
|
|
421
548
|
return true;
|
|
422
549
|
}
|
|
423
550
|
|
|
551
|
+
/** Create and fill a table at a specific index */
|
|
552
|
+
async function createAndFillTableAtIndex(
|
|
553
|
+
client: Lark.Client,
|
|
554
|
+
docToken: string,
|
|
555
|
+
parentBlockId: string,
|
|
556
|
+
tableData: TableData,
|
|
557
|
+
index: number,
|
|
558
|
+
): Promise<boolean> {
|
|
559
|
+
// 1. Create empty table at specific index
|
|
560
|
+
const tableBlockId = await createEmptyTable(
|
|
561
|
+
client,
|
|
562
|
+
docToken,
|
|
563
|
+
parentBlockId,
|
|
564
|
+
tableData.rowSize,
|
|
565
|
+
tableData.colSize,
|
|
566
|
+
index,
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
if (!tableBlockId) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// 2. Fill table content
|
|
574
|
+
await fillTableContent(client, docToken, tableBlockId, tableData.cells);
|
|
575
|
+
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
|
|
424
579
|
/** Process images in markdown: download from URL, upload to Feishu, update blocks */
|
|
425
580
|
async function processImages(
|
|
426
581
|
client: Lark.Client,
|
|
@@ -661,30 +816,51 @@ async function writeDoc(client: Lark.Client, docToken: string, markdown: string)
|
|
|
661
816
|
return { success: true, blocks_deleted: deleted, blocks_added: 0, images_processed: 0, tables_created: 0 };
|
|
662
817
|
}
|
|
663
818
|
|
|
664
|
-
// 3.
|
|
665
|
-
const
|
|
819
|
+
// 3. Convert to content items (preserves table positions)
|
|
820
|
+
const contentItems = convertBlocksToContentItems(blocks);
|
|
666
821
|
|
|
667
|
-
// 4. Insert
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
822
|
+
// 4. Insert content items in order
|
|
823
|
+
let insertIndex = 0;
|
|
824
|
+
const allInserted: any[] = [];
|
|
825
|
+
const allSkipped: string[] = [];
|
|
671
826
|
let tablesCreated = 0;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
827
|
+
let pendingBlocks: any[] = [];
|
|
828
|
+
|
|
829
|
+
const flushPendingBlocks = async () => {
|
|
830
|
+
if (pendingBlocks.length === 0) return;
|
|
831
|
+
const { children, skipped } = await insertBlocksAtIndex(client, docToken, pendingBlocks, insertIndex);
|
|
832
|
+
allInserted.push(...children);
|
|
833
|
+
allSkipped.push(...skipped);
|
|
834
|
+
insertIndex += children.length;
|
|
835
|
+
pendingBlocks = [];
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
for (const item of contentItems) {
|
|
839
|
+
if (item.type === "block") {
|
|
840
|
+
pendingBlocks.push(item.block);
|
|
841
|
+
} else if (item.type === "table") {
|
|
842
|
+
await flushPendingBlocks();
|
|
843
|
+
const success = await createAndFillTableAtIndex(client, docToken, docToken, item.table, insertIndex);
|
|
844
|
+
if (success) {
|
|
845
|
+
tablesCreated++;
|
|
846
|
+
insertIndex++;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
675
849
|
}
|
|
676
850
|
|
|
677
|
-
|
|
678
|
-
|
|
851
|
+
await flushPendingBlocks();
|
|
852
|
+
|
|
853
|
+
// 5. Process images
|
|
854
|
+
const imagesProcessed = await processImages(client, docToken, markdown, allInserted);
|
|
679
855
|
|
|
680
856
|
return {
|
|
681
857
|
success: true,
|
|
682
858
|
blocks_deleted: deleted,
|
|
683
|
-
blocks_added:
|
|
859
|
+
blocks_added: allInserted.length,
|
|
684
860
|
tables_created: tablesCreated,
|
|
685
861
|
images_processed: imagesProcessed,
|
|
686
|
-
...(
|
|
687
|
-
warning: `Skipped unsupported block types: ${
|
|
862
|
+
...(allSkipped.length > 0 && {
|
|
863
|
+
warning: `Skipped unsupported block types: ${allSkipped.join(", ")}.`,
|
|
688
864
|
}),
|
|
689
865
|
};
|
|
690
866
|
}
|
|
@@ -696,30 +872,62 @@ async function appendDoc(client: Lark.Client, docToken: string, markdown: string
|
|
|
696
872
|
throw new Error("Content is empty");
|
|
697
873
|
}
|
|
698
874
|
|
|
699
|
-
// 2.
|
|
700
|
-
const
|
|
875
|
+
// 2. Convert to content items (preserves table positions)
|
|
876
|
+
const contentItems = convertBlocksToContentItems(blocks);
|
|
701
877
|
|
|
702
|
-
// 3.
|
|
703
|
-
|
|
878
|
+
// 3. Get current insert index
|
|
879
|
+
let insertIndex = 0;
|
|
880
|
+
const existing = await client.docx.documentBlockChildren.get({
|
|
881
|
+
path: { document_id: docToken, block_id: docToken },
|
|
882
|
+
});
|
|
883
|
+
if (existing.code === 0) {
|
|
884
|
+
insertIndex = existing.data?.items?.length ?? 0;
|
|
885
|
+
}
|
|
704
886
|
|
|
705
|
-
// 4.
|
|
887
|
+
// 4. Insert content items in order, batching consecutive blocks
|
|
888
|
+
const allInserted: any[] = [];
|
|
889
|
+
const allSkipped: string[] = [];
|
|
706
890
|
let tablesCreated = 0;
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
891
|
+
let pendingBlocks: any[] = [];
|
|
892
|
+
|
|
893
|
+
const flushPendingBlocks = async () => {
|
|
894
|
+
if (pendingBlocks.length === 0) return;
|
|
895
|
+
const { children, skipped } = await insertBlocksAtIndex(client, docToken, pendingBlocks, insertIndex);
|
|
896
|
+
allInserted.push(...children);
|
|
897
|
+
allSkipped.push(...skipped);
|
|
898
|
+
insertIndex += children.length;
|
|
899
|
+
pendingBlocks = [];
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
for (const item of contentItems) {
|
|
903
|
+
if (item.type === "block") {
|
|
904
|
+
pendingBlocks.push(item.block);
|
|
905
|
+
} else if (item.type === "table") {
|
|
906
|
+
// Flush pending blocks before creating table
|
|
907
|
+
await flushPendingBlocks();
|
|
908
|
+
// Create table at current position
|
|
909
|
+
const success = await createAndFillTableAtIndex(client, docToken, docToken, item.table, insertIndex);
|
|
910
|
+
if (success) {
|
|
911
|
+
tablesCreated++;
|
|
912
|
+
insertIndex++;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
710
915
|
}
|
|
711
916
|
|
|
917
|
+
// Flush any remaining blocks
|
|
918
|
+
await flushPendingBlocks();
|
|
919
|
+
|
|
712
920
|
// 5. Process images
|
|
713
|
-
const imagesProcessed = await processImages(client, docToken, markdown,
|
|
921
|
+
const imagesProcessed = await processImages(client, docToken, markdown, allInserted);
|
|
714
922
|
|
|
715
923
|
return {
|
|
716
924
|
success: true,
|
|
717
|
-
blocks_added:
|
|
925
|
+
blocks_added: allInserted.length,
|
|
718
926
|
tables_created: tablesCreated,
|
|
719
927
|
images_processed: imagesProcessed,
|
|
720
|
-
block_ids:
|
|
721
|
-
...(
|
|
722
|
-
warning: `Skipped unsupported block types: ${
|
|
928
|
+
block_ids: allInserted.map((b: any) => b.block_id),
|
|
929
|
+
...(allSkipped.length > 0 && {
|
|
930
|
+
warning: `Skipped unsupported block types: ${allSkipped.join(", ")}.`,
|
|
723
931
|
}),
|
|
724
932
|
};
|
|
725
933
|
}
|