@excitedjs/feishu-transport 0.0.1
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/README.md +39 -0
- package/dist/contract/access-store.d.ts +23 -0
- package/dist/contract/access-store.d.ts.map +1 -0
- package/dist/contract/access-store.js +16 -0
- package/dist/contract/access-store.js.map +1 -0
- package/dist/contract/outbound.d.ts +39 -0
- package/dist/contract/outbound.d.ts.map +1 -0
- package/dist/contract/outbound.js +16 -0
- package/dist/contract/outbound.js.map +1 -0
- package/dist/contract/types.d.ts +86 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +10 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +10 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +14 -0
- package/dist/json.js.map +1 -0
- package/dist/parse/comment.d.ts +41 -0
- package/dist/parse/comment.d.ts.map +1 -0
- package/dist/parse/comment.js +51 -0
- package/dist/parse/comment.js.map +1 -0
- package/dist/parse/content.d.ts +42 -0
- package/dist/parse/content.d.ts.map +1 -0
- package/dist/parse/content.js +208 -0
- package/dist/parse/content.js.map +1 -0
- package/dist/policy/gate.d.ts +105 -0
- package/dist/policy/gate.d.ts.map +1 -0
- package/dist/policy/gate.js +276 -0
- package/dist/policy/gate.js.map +1 -0
- package/dist/policy/pairing.d.ts +12 -0
- package/dist/policy/pairing.d.ts.map +1 -0
- package/dist/policy/pairing.js +15 -0
- package/dist/policy/pairing.js.map +1 -0
- package/dist/render/render.d.ts +186 -0
- package/dist/render/render.d.ts.map +1 -0
- package/dist/render/render.js +630 -0
- package/dist/render/render.js.map +1 -0
- package/dist/transport/connection.d.ts +25 -0
- package/dist/transport/connection.d.ts.map +1 -0
- package/dist/transport/connection.js +42 -0
- package/dist/transport/connection.js.map +1 -0
- package/dist/transport/feishu.d.ts +222 -0
- package/dist/transport/feishu.d.ts.map +1 -0
- package/dist/transport/feishu.js +431 -0
- package/dist/transport/feishu.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a Markdown source into one or more Feishu v2 interactive cards.
|
|
3
|
+
*
|
|
4
|
+
* Feishu's `tag: markdown` body element accepts only the "lark_md" subset —
|
|
5
|
+
* bold, italic, strikethrough, inline code, fenced code, links, and (on
|
|
6
|
+
* Feishu 7.6+) lists. It does NOT support headings or GFM tables: a `#` is
|
|
7
|
+
* shown as a literal `#`, and `| a | b |` is shown as the source pipes.
|
|
8
|
+
* Routing every block through `tag: markdown` therefore leaks raw markup into
|
|
9
|
+
* the rendered card whenever the body has a heading or a table.
|
|
10
|
+
*
|
|
11
|
+
* This module parses the source into block tokens (via `marked.lexer`) and
|
|
12
|
+
* routes each block to the v2 card component that actually renders it:
|
|
13
|
+
*
|
|
14
|
+
* - The first `# h1` becomes the card's `header.title` plain-text slot.
|
|
15
|
+
* A later `# h1`, and every `##`/`###`/... heading, becomes a body
|
|
16
|
+
* element wrapping the flattened heading text in `**...**` so lark_md
|
|
17
|
+
* renders it as visible bold.
|
|
18
|
+
* - A GFM `| ... |` table becomes a dedicated `tag: table` element with
|
|
19
|
+
* the header alignment markers mapped to each column's
|
|
20
|
+
* `horizontal_align`. Cells that contain inline markup flip the
|
|
21
|
+
* column's `data_type` from `text` to `lark_md`. A cell over
|
|
22
|
+
* `CELL_MAX_BYTES` is a render-time error — splitting one row's cell
|
|
23
|
+
* across multiple rows would break alignment, and silently truncating
|
|
24
|
+
* hides the data the caller asked to deliver.
|
|
25
|
+
* - A horizontal rule (`---`) becomes a `tag: hr` element.
|
|
26
|
+
* - Every other block — paragraphs, lists, blockquotes, fenced code —
|
|
27
|
+
* is emitted as a `tag: markdown` element with the block's raw source,
|
|
28
|
+
* since lark_md renders those natively.
|
|
29
|
+
*
|
|
30
|
+
* Elements are then packed greedily into cards by a dual budget: Feishu's
|
|
31
|
+
* ~30 KB request body cap AND the v2 card per-card element-count cap.
|
|
32
|
+
* Only the first card carries the header — splitting the body produces
|
|
33
|
+
* follow-up cards with no header, the way a long thread reads. A markdown
|
|
34
|
+
* element body too large for one card is split with `splitMarkdownByBytes`,
|
|
35
|
+
* which counts UTF-8 bytes (so a CJK-heavy body is not budgeted as if it
|
|
36
|
+
* were ASCII) and respects grapheme cluster boundaries (so a ZWJ emoji
|
|
37
|
+
* does not get cut in half).
|
|
38
|
+
*
|
|
39
|
+
* `renderMarkdownToCards` validates every produced card against both
|
|
40
|
+
* budgets before returning; if any card would still exceed them despite
|
|
41
|
+
* splitting, it throws. Callers therefore know that the array they get
|
|
42
|
+
* back is wire-ready — there is no path where the renderer hands the
|
|
43
|
+
* sender a card Feishu would reject. This is the structural half of the
|
|
44
|
+
* "atomic" semantic the channel offers; the network half (every send
|
|
45
|
+
* succeeds or all sends fail) cannot be guaranteed on Feishu's IM API,
|
|
46
|
+
* which has no message-batch transaction.
|
|
47
|
+
*
|
|
48
|
+
* The result is `RenderedCard[]`. `cardToContent(card)` turns one card into
|
|
49
|
+
* the JSON string the `im.message.create` `content` field expects.
|
|
50
|
+
*
|
|
51
|
+
* The 9-row × 9-column limit in `markdown-ref.md` belongs to a different
|
|
52
|
+
* surface (the docs-create pipeline that turns markdown into Feishu document
|
|
53
|
+
* blocks); v2 card tables accept up to 50 columns, paginate rows in-card via
|
|
54
|
+
* `page_size`, and have no per-table row cap of their own. This renderer
|
|
55
|
+
* therefore column-splits at 50 (with the first column preserved on each
|
|
56
|
+
* split half as the identifier) and never row-splits when row-splitting
|
|
57
|
+
* would not actually reduce a card's size.
|
|
58
|
+
*/
|
|
59
|
+
import { marked } from 'marked';
|
|
60
|
+
/**
|
|
61
|
+
* Feishu's documented hard limit for a card request body. Past this the API
|
|
62
|
+
* rejects the call outright; the packer keeps each card's serialised content
|
|
63
|
+
* below `CARD_CONTENT_SAFE_BYTES` so HTTP headers and the request envelope
|
|
64
|
+
* still fit underneath the hard cap.
|
|
65
|
+
*/
|
|
66
|
+
export const FEISHU_CARD_REQUEST_LIMIT_BYTES = 30 * 1024;
|
|
67
|
+
const CARD_CONTENT_SAFE_BYTES = 28 * 1024;
|
|
68
|
+
/**
|
|
69
|
+
* v2 card per-card element-count cap. Observed in PR #73 review: a card with
|
|
70
|
+
* 250 short markdown elements is rejected by Feishu, though the JSON itself
|
|
71
|
+
* is well under the byte cap. The exact upper bound is not in the open
|
|
72
|
+
* docs; the reviewer-cited 200 figure is treated as the hard limit and a
|
|
73
|
+
* lower number is used as the safe budget so a card on the boundary does
|
|
74
|
+
* not silently fail under server-side counting differences.
|
|
75
|
+
*/
|
|
76
|
+
export const FEISHU_CARD_ELEMENT_HARD_CAP = 200;
|
|
77
|
+
const CARD_ELEMENT_SAFE_CAP = 180;
|
|
78
|
+
/**
|
|
79
|
+
* v2 table column cap from the Feishu card API. A wider table is split into
|
|
80
|
+
* several adjacent table elements; each split half repeats the original
|
|
81
|
+
* first column as an identifier the reader can still align rows against.
|
|
82
|
+
*/
|
|
83
|
+
const TABLE_COLUMN_HARD_CAP = 50;
|
|
84
|
+
/** In-card paginator page size for tables. The API caps this at 10. */
|
|
85
|
+
const TABLE_DEFAULT_PAGE_SIZE = 10;
|
|
86
|
+
/**
|
|
87
|
+
* Per-cell byte cap. A cell larger than this is rejected at render time:
|
|
88
|
+
* splitting one row's cell across multiple rows breaks alignment with the
|
|
89
|
+
* other columns, and silently truncating drops the data the caller asked
|
|
90
|
+
* to deliver. The author is expected to move the oversized content into a
|
|
91
|
+
* paragraph or fenced code block, where the byte-aware splitter handles
|
|
92
|
+
* arbitrary sizes.
|
|
93
|
+
*
|
|
94
|
+
* 4 KB is comfortably above any reasonable cell value (a long sentence is
|
|
95
|
+
* ~200 bytes) while small enough that a 50-column table of full-budget
|
|
96
|
+
* cells still leaves room for the card envelope inside the byte cap.
|
|
97
|
+
*/
|
|
98
|
+
export const CELL_MAX_BYTES = 4 * 1024;
|
|
99
|
+
/**
|
|
100
|
+
* Convert `<@ou_...>` shorthand into the lark_md `<at id="ou_..."></at>`
|
|
101
|
+
* mention tag, leaving inline code spans untouched. Fenced code block tokens
|
|
102
|
+
* are excluded upstream by the `token.type === 'code'` guard in
|
|
103
|
+
* `tokensToElements`; this function only needs to skip backtick-delimited
|
|
104
|
+
* inline code spans (single or multi-backtick) within the raw text of other
|
|
105
|
+
* block types.
|
|
106
|
+
*/
|
|
107
|
+
function replaceAtMentions(text) {
|
|
108
|
+
const parts = [];
|
|
109
|
+
let lastIndex = 0;
|
|
110
|
+
// Match any inline code span — one or more backticks as the delimiter.
|
|
111
|
+
const codeSpan = /`+[\s\S]*?`+/g;
|
|
112
|
+
let match;
|
|
113
|
+
while ((match = codeSpan.exec(text)) !== null) {
|
|
114
|
+
// Non-code segment before this span — apply the substitution.
|
|
115
|
+
parts.push(text.slice(lastIndex, match.index).replace(/<@(ou_[A-Za-z0-9_-]+)>/g, '<at id="$1"></at>'));
|
|
116
|
+
// Inline code span — pass through unchanged.
|
|
117
|
+
parts.push(match[0]);
|
|
118
|
+
lastIndex = match.index + match[0].length;
|
|
119
|
+
}
|
|
120
|
+
// Trailing non-code segment.
|
|
121
|
+
parts.push(text.slice(lastIndex).replace(/<@(ou_[A-Za-z0-9_-]+)>/g, '<at id="$1"></at>'));
|
|
122
|
+
return parts.join('');
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Render a Markdown source into one or more v2 cards.
|
|
126
|
+
*
|
|
127
|
+
* The output is always non-empty: an empty source produces one card with a
|
|
128
|
+
* single empty `tag: markdown` element, so the caller always has something
|
|
129
|
+
* to send. Splitting happens automatically when the serialised card would
|
|
130
|
+
* exceed Feishu's 30 KB request cap, when the body would exceed the
|
|
131
|
+
* 200-element cap, or when a table has more than 50 columns.
|
|
132
|
+
*
|
|
133
|
+
* Throws when a single block cannot be made to fit even after splitting —
|
|
134
|
+
* for example a table whose row, after every cell has been validated under
|
|
135
|
+
* `CELL_MAX_BYTES`, still serialises above the per-card byte cap. The
|
|
136
|
+
* caller therefore sees a render-time error before any send is attempted,
|
|
137
|
+
* instead of the channel posting some cards and then failing on a later
|
|
138
|
+
* one (partial visible state).
|
|
139
|
+
*/
|
|
140
|
+
export function renderMarkdownToCards(text) {
|
|
141
|
+
const tokens = marked.lexer(text);
|
|
142
|
+
const { header, elements } = tokensToElements(tokens);
|
|
143
|
+
const cards = packIntoCards(elements, header);
|
|
144
|
+
// Final structural check. By construction every card already fits both
|
|
145
|
+
// budgets, but a defensive assertion makes a future packing-loop bug
|
|
146
|
+
// surface here as a clear error rather than a Feishu reject downstream.
|
|
147
|
+
for (let i = 0; i < cards.length; i++) {
|
|
148
|
+
const card = cards[i];
|
|
149
|
+
const bytes = cardContentBytes(card);
|
|
150
|
+
const count = card.body.elements.length;
|
|
151
|
+
if (bytes > CARD_CONTENT_SAFE_BYTES) {
|
|
152
|
+
throw new Error(`rendered card ${i + 1} of ${cards.length} is ${bytes} bytes; ` +
|
|
153
|
+
`Feishu rejects a card body over ${FEISHU_CARD_REQUEST_LIMIT_BYTES} bytes. ` +
|
|
154
|
+
'Reduce the content (shorter paragraphs, fewer rows in any one table).');
|
|
155
|
+
}
|
|
156
|
+
if (count > FEISHU_CARD_ELEMENT_HARD_CAP) {
|
|
157
|
+
throw new Error(`rendered card ${i + 1} of ${cards.length} has ${count} elements; ` +
|
|
158
|
+
`Feishu rejects a card with more than ${FEISHU_CARD_ELEMENT_HARD_CAP} elements. ` +
|
|
159
|
+
'Combine adjacent paragraphs or send fewer items per reply.');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return cards;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Serialise one card into the JSON string Feishu's `im.message.create`
|
|
166
|
+
* `content` field expects. Pure: no side effects, no I/O.
|
|
167
|
+
*/
|
|
168
|
+
export function cardToContent(card) {
|
|
169
|
+
return JSON.stringify(card);
|
|
170
|
+
}
|
|
171
|
+
/** Byte length of `card`'s serialised content, in UTF-8. */
|
|
172
|
+
export function cardContentBytes(card) {
|
|
173
|
+
return Buffer.byteLength(cardToContent(card), 'utf8');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Walk every block token, route it to the right v2 component, and emit
|
|
177
|
+
* `{ header, elements }`. The header slot is filled by the first heading
|
|
178
|
+
* with depth 1; any later h1 falls through to the bold-body fallback so its
|
|
179
|
+
* text is not silently dropped.
|
|
180
|
+
*/
|
|
181
|
+
function tokensToElements(tokens) {
|
|
182
|
+
let header;
|
|
183
|
+
const elements = [];
|
|
184
|
+
for (const token of tokens) {
|
|
185
|
+
if (token.type === 'space')
|
|
186
|
+
continue;
|
|
187
|
+
if (token.type === 'heading') {
|
|
188
|
+
const heading = token;
|
|
189
|
+
const flat = flattenInline(heading.tokens);
|
|
190
|
+
if (heading.depth === 1 && header === undefined) {
|
|
191
|
+
header = { title: { tag: 'plain_text', content: flat } };
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
// h2+, or a later h1 — lark_md has no heading syntax, so the rendered
|
|
195
|
+
// text would otherwise show the literal `##`. Wrap the flattened text
|
|
196
|
+
// in `**...**` so it renders as visible bold.
|
|
197
|
+
elements.push({ tag: 'markdown', content: `**${flat}**` });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (token.type === 'hr') {
|
|
201
|
+
elements.push({ tag: 'hr' });
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (token.type === 'table') {
|
|
205
|
+
for (const t of tableTokenToElements(token)) {
|
|
206
|
+
elements.push(t);
|
|
207
|
+
}
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// Everything else — paragraph, list, blockquote, code, html block — has
|
|
211
|
+
// a `raw` field that preserves the original markdown source. lark_md
|
|
212
|
+
// renders the supported subset; unsupported markup (e.g. blockquote `>`)
|
|
213
|
+
// passes through and may show its leading characters literally.
|
|
214
|
+
const raw = token.raw ?? '';
|
|
215
|
+
const trimmed = raw.replace(/\n+$/, '');
|
|
216
|
+
if (trimmed.length === 0)
|
|
217
|
+
continue;
|
|
218
|
+
// Fenced and indented code blocks must not have <@> patterns substituted —
|
|
219
|
+
// a code example like `<@ou_abc>` must not ping anyone. All other blocks
|
|
220
|
+
// (paragraphs, lists, blockquotes, …) run through replaceAtMentions, which
|
|
221
|
+
// skips inline code spans within them.
|
|
222
|
+
const content = token.type === 'code' ? trimmed : replaceAtMentions(trimmed);
|
|
223
|
+
elements.push({ tag: 'markdown', content });
|
|
224
|
+
}
|
|
225
|
+
if (elements.length === 0 && header === undefined) {
|
|
226
|
+
// Always emit something so the send loop has at least one card to post.
|
|
227
|
+
elements.push({ tag: 'markdown', content: '' });
|
|
228
|
+
}
|
|
229
|
+
return { header, elements };
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Walk a `marked` inline-token tree and produce the plain-text equivalent —
|
|
233
|
+
* `**bold**` becomes `bold`, `[link](url)` becomes `link`, etc. Used for the
|
|
234
|
+
* card's plain-text header slot, which renders markup characters literally.
|
|
235
|
+
*/
|
|
236
|
+
function flattenInline(tokens) {
|
|
237
|
+
if (!tokens)
|
|
238
|
+
return '';
|
|
239
|
+
let out = '';
|
|
240
|
+
for (const t of tokens) {
|
|
241
|
+
if ('tokens' in t && Array.isArray(t.tokens)) {
|
|
242
|
+
out += flattenInline(t.tokens);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
out += t.text ?? '';
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Convert one GFM table token into one or more `tag: table` body elements.
|
|
251
|
+
* A table wider than the API's 50-column cap is split into adjacent halves;
|
|
252
|
+
* each half repeats the original first column so the reader can still align
|
|
253
|
+
* a row across the split. Returns at least one element.
|
|
254
|
+
*
|
|
255
|
+
* Throws when any cell exceeds `CELL_MAX_BYTES` — a giant cell does not
|
|
256
|
+
* render usefully and would either force silent truncation or cell-level
|
|
257
|
+
* row splitting that breaks alignment with the other columns. The author
|
|
258
|
+
* should move the oversized content into a paragraph or fenced code block.
|
|
259
|
+
*/
|
|
260
|
+
function tableTokenToElements(table) {
|
|
261
|
+
validateCellSizes(table);
|
|
262
|
+
const headerCells = table.header;
|
|
263
|
+
const rows = table.rows;
|
|
264
|
+
const totalCols = headerCells.length;
|
|
265
|
+
if (totalCols <= TABLE_COLUMN_HARD_CAP) {
|
|
266
|
+
return [buildTableElement(headerCells, rows, 0, totalCols, false)];
|
|
267
|
+
}
|
|
268
|
+
const out = [];
|
|
269
|
+
// First slice: columns [0, 50). Later slices repeat column 0 plus the next
|
|
270
|
+
// 49 columns, so every emitted table stays within the 50-column cap.
|
|
271
|
+
out.push(buildTableElement(headerCells, rows, 0, TABLE_COLUMN_HARD_CAP, false));
|
|
272
|
+
const chunkSize = TABLE_COLUMN_HARD_CAP - 1;
|
|
273
|
+
for (let start = TABLE_COLUMN_HARD_CAP; start < totalCols; start += chunkSize) {
|
|
274
|
+
const end = Math.min(totalCols, start + chunkSize);
|
|
275
|
+
out.push(buildTableElement(headerCells, rows, start, end, true));
|
|
276
|
+
}
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Walk every cell of a parsed table and throw on the first one larger than
|
|
281
|
+
* `CELL_MAX_BYTES`. Run at render time so the error names the row and
|
|
282
|
+
* column position the author can locate in the source.
|
|
283
|
+
*/
|
|
284
|
+
function validateCellSizes(table) {
|
|
285
|
+
for (let rowIdx = 0; rowIdx < table.rows.length; rowIdx++) {
|
|
286
|
+
const row = table.rows[rowIdx];
|
|
287
|
+
if (!row)
|
|
288
|
+
continue;
|
|
289
|
+
for (let colIdx = 0; colIdx < row.length; colIdx++) {
|
|
290
|
+
const cell = row[colIdx];
|
|
291
|
+
const text = cell?.text ?? '';
|
|
292
|
+
const bytes = Buffer.byteLength(text, 'utf8');
|
|
293
|
+
if (bytes > CELL_MAX_BYTES) {
|
|
294
|
+
const header = table.header[colIdx]?.text ?? `column ${colIdx + 1}`;
|
|
295
|
+
throw new Error(`table cell at row ${rowIdx + 1}, column "${header}" is ${bytes} bytes; ` +
|
|
296
|
+
`cells over ${CELL_MAX_BYTES} bytes do not render usefully in a card table. ` +
|
|
297
|
+
'Move the content out of the table — a paragraph or fenced code block has no such cap.');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Build one `tag: table` element from the rows of an already-parsed GFM
|
|
304
|
+
* table. `start..end` selects which columns this element covers; when
|
|
305
|
+
* `includeIdentifier` is true, the original first column is prepended to
|
|
306
|
+
* the selection so a split table keeps an identifier column on every half.
|
|
307
|
+
*
|
|
308
|
+
* Column `data_type` is `text` unless any cell in that column carries
|
|
309
|
+
* inline markup, in which case it flips to `lark_md` so the markup renders.
|
|
310
|
+
*/
|
|
311
|
+
function buildTableElement(headerCells, rows, start, end, includeIdentifier) {
|
|
312
|
+
// Indices into the source table that this element covers. The identifier
|
|
313
|
+
// column is duplicated by index so each cell still resolves correctly.
|
|
314
|
+
const indices = [];
|
|
315
|
+
if (includeIdentifier && start > 0)
|
|
316
|
+
indices.push(0);
|
|
317
|
+
for (let i = start; i < end; i++)
|
|
318
|
+
indices.push(i);
|
|
319
|
+
// Stable, in-row-unique column names so a row object can be keyed by name
|
|
320
|
+
// without collisions even when display names repeat. `col_0`, `col_1`, ...
|
|
321
|
+
// are internal keys; the `display_name` is the visible label.
|
|
322
|
+
const columns = indices.map((srcIdx, outIdx) => {
|
|
323
|
+
const cell = headerCells[srcIdx];
|
|
324
|
+
const hasInline = rows.some((row) => cellHasInlineMarkup(row[srcIdx]));
|
|
325
|
+
return {
|
|
326
|
+
name: `col_${outIdx}`,
|
|
327
|
+
display_name: cell?.text ?? '',
|
|
328
|
+
data_type: hasInline ? 'lark_md' : 'text',
|
|
329
|
+
...alignToHorizontal(cell?.align),
|
|
330
|
+
};
|
|
331
|
+
});
|
|
332
|
+
const builtRows = rows.map((row) => {
|
|
333
|
+
const record = {};
|
|
334
|
+
indices.forEach((srcIdx, outIdx) => {
|
|
335
|
+
const cell = row[srcIdx];
|
|
336
|
+
const colName = `col_${outIdx}`;
|
|
337
|
+
record[colName] = cell?.text ?? '';
|
|
338
|
+
});
|
|
339
|
+
return record;
|
|
340
|
+
});
|
|
341
|
+
return {
|
|
342
|
+
tag: 'table',
|
|
343
|
+
page_size: TABLE_DEFAULT_PAGE_SIZE,
|
|
344
|
+
row_height: 'low',
|
|
345
|
+
header_style: { bold: true, background_style: 'grey' },
|
|
346
|
+
columns,
|
|
347
|
+
rows: builtRows,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Translate a GFM alignment marker into the column field. `marked` reports
|
|
352
|
+
* `align` as `'left' | 'center' | 'right' | null`; an unaligned column gets
|
|
353
|
+
* no `horizontal_align`, so the API picks its default rather than this
|
|
354
|
+
* renderer forcing one.
|
|
355
|
+
*/
|
|
356
|
+
function alignToHorizontal(align) {
|
|
357
|
+
if (align === 'left' || align === 'center' || align === 'right') {
|
|
358
|
+
return { horizontal_align: align };
|
|
359
|
+
}
|
|
360
|
+
return {};
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* True when a table cell's parsed tokens include any non-text token —
|
|
364
|
+
* bold, italic, link, inline code, etc. The column whose cells trigger this
|
|
365
|
+
* needs `data_type: lark_md` so the markup renders instead of leaking
|
|
366
|
+
* through as literal characters.
|
|
367
|
+
*/
|
|
368
|
+
function cellHasInlineMarkup(cell) {
|
|
369
|
+
if (!cell || !cell.tokens)
|
|
370
|
+
return false;
|
|
371
|
+
for (const t of cell.tokens) {
|
|
372
|
+
if (t.type !== 'text')
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Pack elements into cards by a dual budget: Feishu's 30 KB request cap AND
|
|
379
|
+
* the per-card element count cap. Both must be satisfied for an element to
|
|
380
|
+
* join the current card; either tripping flushes the card. The first card
|
|
381
|
+
* carries the header; later cards do not, so a long reply reads as a
|
|
382
|
+
* threaded continuation rather than repeated banners.
|
|
383
|
+
*
|
|
384
|
+
* A single element that itself would exceed either budget is split inline:
|
|
385
|
+
* a `tag: markdown` element by `splitMarkdownByBytes`, a `tag: table`
|
|
386
|
+
* element by reducing the rows it carries until it fits.
|
|
387
|
+
*/
|
|
388
|
+
function packIntoCards(elements, header) {
|
|
389
|
+
const cards = [];
|
|
390
|
+
let current = newCard(header);
|
|
391
|
+
let isFirstCard = true;
|
|
392
|
+
const flush = () => {
|
|
393
|
+
if (current.body.elements.length === 0 && current.header === undefined)
|
|
394
|
+
return;
|
|
395
|
+
cards.push(current);
|
|
396
|
+
isFirstCard = false;
|
|
397
|
+
current = newCard(undefined);
|
|
398
|
+
};
|
|
399
|
+
const tryAdd = (element) => {
|
|
400
|
+
if (current.body.elements.length + 1 > CARD_ELEMENT_SAFE_CAP)
|
|
401
|
+
return false;
|
|
402
|
+
const trial = {
|
|
403
|
+
...current,
|
|
404
|
+
body: { elements: [...current.body.elements, element] },
|
|
405
|
+
};
|
|
406
|
+
return cardContentBytes(trial) <= CARD_CONTENT_SAFE_BYTES;
|
|
407
|
+
};
|
|
408
|
+
const addPiece = (piece) => {
|
|
409
|
+
if (!tryAdd(piece)) {
|
|
410
|
+
if (current.body.elements.length > 0)
|
|
411
|
+
flush();
|
|
412
|
+
}
|
|
413
|
+
current.body.elements.push(piece);
|
|
414
|
+
};
|
|
415
|
+
for (const element of elements) {
|
|
416
|
+
if (tryAdd(element)) {
|
|
417
|
+
current.body.elements.push(element);
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
// The element does not fit alongside what is already in the current
|
|
421
|
+
// card. Flush so the current card seals at its safe size, then retry.
|
|
422
|
+
if (current.body.elements.length > 0)
|
|
423
|
+
flush();
|
|
424
|
+
if (tryAdd(element)) {
|
|
425
|
+
current.body.elements.push(element);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
// The element alone exceeds at least one budget on a fresh card. Split
|
|
429
|
+
// it into pieces that each fit on their own; each piece is then added
|
|
430
|
+
// through the normal packing path so adjacent small pieces can share a
|
|
431
|
+
// card.
|
|
432
|
+
const pieces = splitOversizedElement(element, isFirstCard ? header : undefined);
|
|
433
|
+
for (const piece of pieces)
|
|
434
|
+
addPiece(piece);
|
|
435
|
+
}
|
|
436
|
+
flush();
|
|
437
|
+
if (cards.length === 0)
|
|
438
|
+
cards.push(newCard(header));
|
|
439
|
+
return cards;
|
|
440
|
+
}
|
|
441
|
+
/** Build an empty card, optionally pre-populated with a header slot. */
|
|
442
|
+
function newCard(header) {
|
|
443
|
+
const card = {
|
|
444
|
+
schema: '2.0',
|
|
445
|
+
config: { update_multi: true },
|
|
446
|
+
body: { elements: [] },
|
|
447
|
+
};
|
|
448
|
+
if (header)
|
|
449
|
+
card.header = header;
|
|
450
|
+
return card;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Split one too-big element into pieces that each individually fit under
|
|
454
|
+
* the per-card budgets. A markdown element is split by `splitMarkdownByBytes`;
|
|
455
|
+
* a table element is split by reducing its row count.
|
|
456
|
+
*
|
|
457
|
+
* `headerOnFirstCard` lets the splitter budget the first piece against the
|
|
458
|
+
* card with the header attached, so a piece that just fits next to the
|
|
459
|
+
* header doesn't get oversized for the very first card.
|
|
460
|
+
*/
|
|
461
|
+
function splitOversizedElement(element, headerOnFirstCard) {
|
|
462
|
+
if (element.tag === 'markdown') {
|
|
463
|
+
return splitMarkdownElement(element, headerOnFirstCard);
|
|
464
|
+
}
|
|
465
|
+
if (element.tag === 'table') {
|
|
466
|
+
return splitTableByRows(element, headerOnFirstCard);
|
|
467
|
+
}
|
|
468
|
+
return [element];
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Split a too-big `tag: markdown` element into smaller ones, each whose
|
|
472
|
+
* serialised content fits the per-card byte budget. The body is split with
|
|
473
|
+
* `splitMarkdownByBytes`, which counts UTF-8 bytes — so a 12 K-character
|
|
474
|
+
* CJK paragraph (~36 KB) is correctly split, where a char-based budget
|
|
475
|
+
* would believe it fits and ship one oversized card.
|
|
476
|
+
*/
|
|
477
|
+
function splitMarkdownElement(element, headerOnFirstCard) {
|
|
478
|
+
// Reserve room for the JSON envelope of one card carrying one element:
|
|
479
|
+
// `{"schema":"2.0","config":{"update_multi":true},"body":{"elements":[{"tag":"markdown","content":"..."}]}}`
|
|
480
|
+
// plus the optional header. The envelope is measured with an empty
|
|
481
|
+
// content string; the 256-byte cushion covers JSON-escape expansion of
|
|
482
|
+
// body characters that need escaping (quotes, backslashes, control
|
|
483
|
+
// characters) versus their raw byte count.
|
|
484
|
+
const envelope = cardContentBytes(newCardWithEmpty(headerOnFirstCard));
|
|
485
|
+
const innerBudget = Math.max(256, CARD_CONTENT_SAFE_BYTES - envelope - 256);
|
|
486
|
+
const pieces = splitMarkdownByBytes(element.content, innerBudget);
|
|
487
|
+
return pieces.map((content) => ({ tag: 'markdown', content }));
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Build an empty card carrying one empty markdown element, used to size the
|
|
491
|
+
* fixed envelope overhead when splitting an oversized markdown element. The
|
|
492
|
+
* cushion in `splitMarkdownElement` covers the difference between an empty
|
|
493
|
+
* content string and the brackets/quotes around real content plus any
|
|
494
|
+
* JSON escapes the content contributes on its own.
|
|
495
|
+
*/
|
|
496
|
+
function newCardWithEmpty(header) {
|
|
497
|
+
const card = newCard(header);
|
|
498
|
+
card.body.elements.push({ tag: 'markdown', content: '' });
|
|
499
|
+
return card;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Split a Markdown block's source into pieces whose UTF-8 byte length each
|
|
503
|
+
* stays at or under `byteBudget`. The input is one block's `raw` field from
|
|
504
|
+
* `marked.lexer`, so the block kind drives the split strategy:
|
|
505
|
+
*
|
|
506
|
+
* - A fenced code block (opens with ``` or ~~~ and closes with the same
|
|
507
|
+
* run) is split by line and the open / close lines are repeated on
|
|
508
|
+
* every piece, so each piece is itself a well-formed fenced block.
|
|
509
|
+
* - Any other block is split at line boundaries; a single line longer
|
|
510
|
+
* than the budget is split by grapheme cluster, so a ZWJ-bound emoji
|
|
511
|
+
* cluster or a Hangul syllable is not cut in half.
|
|
512
|
+
*
|
|
513
|
+
* Every returned piece is non-empty and fits the budget — the caller can
|
|
514
|
+
* use them directly as `tag: markdown` element bodies.
|
|
515
|
+
*/
|
|
516
|
+
export function splitMarkdownByBytes(text, byteBudget) {
|
|
517
|
+
if (Buffer.byteLength(text, 'utf8') <= byteBudget)
|
|
518
|
+
return [text];
|
|
519
|
+
const fenceLineMatch = /^([`~]{3,}[^\n]*)\n([\s\S]*?)\n([`~]{3,})\s*$/.exec(text);
|
|
520
|
+
if (fenceLineMatch) {
|
|
521
|
+
const open = fenceLineMatch[1];
|
|
522
|
+
const body = fenceLineMatch[2];
|
|
523
|
+
const close = fenceLineMatch[3];
|
|
524
|
+
const overhead = Buffer.byteLength(open, 'utf8') + Buffer.byteLength(close, 'utf8') + 2; // two \n separators
|
|
525
|
+
const inner = Math.max(64, byteBudget - overhead);
|
|
526
|
+
const bodyPieces = splitLinesByBytes(body.split('\n'), inner);
|
|
527
|
+
return bodyPieces.map((piece) => [open, piece, close].join('\n'));
|
|
528
|
+
}
|
|
529
|
+
return splitLinesByBytes(text.split('\n'), byteBudget);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Pack consecutive lines into pieces that each fit `byteBudget`. A line
|
|
533
|
+
* larger than the budget on its own is split by grapheme cluster — never
|
|
534
|
+
* by code unit, since that risks cutting a UTF-8 sequence or a ZWJ-bound
|
|
535
|
+
* emoji cluster in half.
|
|
536
|
+
*/
|
|
537
|
+
function splitLinesByBytes(lines, byteBudget) {
|
|
538
|
+
const pieces = [];
|
|
539
|
+
let current = [];
|
|
540
|
+
let currentBytes = 0;
|
|
541
|
+
const flush = () => {
|
|
542
|
+
if (current.length === 0)
|
|
543
|
+
return;
|
|
544
|
+
pieces.push(current.join('\n'));
|
|
545
|
+
current = [];
|
|
546
|
+
currentBytes = 0;
|
|
547
|
+
};
|
|
548
|
+
for (const line of lines) {
|
|
549
|
+
const lineBytes = Buffer.byteLength(line, 'utf8');
|
|
550
|
+
const sepBytes = current.length === 0 ? 0 : 1; // joining newline
|
|
551
|
+
if (lineBytes + sepBytes + currentBytes <= byteBudget) {
|
|
552
|
+
current.push(line);
|
|
553
|
+
currentBytes += lineBytes + sepBytes;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
// Adding this line would overflow the current piece.
|
|
557
|
+
flush();
|
|
558
|
+
if (lineBytes <= byteBudget) {
|
|
559
|
+
current.push(line);
|
|
560
|
+
currentBytes = lineBytes;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
// The single line is itself larger than the budget — split it inside.
|
|
564
|
+
for (const sub of splitByGraphemeBytes(line, byteBudget)) {
|
|
565
|
+
pieces.push(sub);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
flush();
|
|
569
|
+
return pieces;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Split one string into UTF-8 byte chunks at grapheme cluster boundaries.
|
|
573
|
+
* Walks `Intl.Segmenter`'s grapheme segments, accumulating bytes until the
|
|
574
|
+
* next grapheme would exceed `byteBudget`. A line of CJK characters or
|
|
575
|
+
* ZWJ-bound emoji clusters therefore splits without ever cutting a
|
|
576
|
+
* code-point or an emoji-cluster in half.
|
|
577
|
+
*/
|
|
578
|
+
function splitByGraphemeBytes(text, byteBudget) {
|
|
579
|
+
// `und` (undetermined locale) gives the Unicode default grapheme rules,
|
|
580
|
+
// which is what we want — clusters formed by combining marks, ZWJ, etc.
|
|
581
|
+
const segmenter = new Intl.Segmenter('und', { granularity: 'grapheme' });
|
|
582
|
+
const pieces = [];
|
|
583
|
+
let current = '';
|
|
584
|
+
let currentBytes = 0;
|
|
585
|
+
for (const { segment } of segmenter.segment(text)) {
|
|
586
|
+
const segBytes = Buffer.byteLength(segment, 'utf8');
|
|
587
|
+
if (currentBytes + segBytes > byteBudget) {
|
|
588
|
+
if (current.length > 0)
|
|
589
|
+
pieces.push(current);
|
|
590
|
+
current = segment;
|
|
591
|
+
currentBytes = segBytes;
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
current += segment;
|
|
595
|
+
currentBytes += segBytes;
|
|
596
|
+
}
|
|
597
|
+
if (current.length > 0)
|
|
598
|
+
pieces.push(current);
|
|
599
|
+
return pieces;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Split a too-big `tag: table` element into smaller tables by row. The
|
|
603
|
+
* header columns are repeated on each split half so every emitted table is
|
|
604
|
+
* independently readable; cell-size validation has already rejected a row
|
|
605
|
+
* whose cells alone make it too big to fit, so the row-count window can
|
|
606
|
+
* always shrink to a piece that fits.
|
|
607
|
+
*/
|
|
608
|
+
function splitTableByRows(table, headerOnFirstCard) {
|
|
609
|
+
if (table.rows.length <= 1)
|
|
610
|
+
return [table];
|
|
611
|
+
const envelope = cardContentBytes(newCardWithEmpty(headerOnFirstCard));
|
|
612
|
+
const budget = Math.max(2048, CARD_CONTENT_SAFE_BYTES - envelope - 256);
|
|
613
|
+
const out = [];
|
|
614
|
+
let start = 0;
|
|
615
|
+
while (start < table.rows.length) {
|
|
616
|
+
let end = table.rows.length;
|
|
617
|
+
while (end > start + 1) {
|
|
618
|
+
const candidate = { ...table, rows: table.rows.slice(start, end) };
|
|
619
|
+
if (Buffer.byteLength(JSON.stringify(candidate), 'utf8') <= budget)
|
|
620
|
+
break;
|
|
621
|
+
end -= 1;
|
|
622
|
+
}
|
|
623
|
+
out.push({ ...table, rows: table.rows.slice(start, end) });
|
|
624
|
+
if (end === start)
|
|
625
|
+
break;
|
|
626
|
+
start = end;
|
|
627
|
+
}
|
|
628
|
+
return out;
|
|
629
|
+
}
|
|
630
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render/render.ts"],"names":[],"mappings":"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"}
|