@jacobbubu/md-to-lark 1.0.0

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.
Files changed (58) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +171 -0
  3. package/dist/btt/build-tree.js +79 -0
  4. package/dist/btt/index.js +1 -0
  5. package/dist/btt/types.js +1 -0
  6. package/dist/cli/publish-md-to-lark.js +15 -0
  7. package/dist/commands/publish-md/args.js +224 -0
  8. package/dist/commands/publish-md/command.js +97 -0
  9. package/dist/commands/publish-md/index.js +1 -0
  10. package/dist/commands/publish-md/input-resolver.js +48 -0
  11. package/dist/commands/publish-md/mermaid-render.js +17 -0
  12. package/dist/commands/publish-md/pipeline-transform.js +4 -0
  13. package/dist/commands/publish-md/preset-loader.js +113 -0
  14. package/dist/commands/publish-md/presets/medium.js +7 -0
  15. package/dist/commands/publish-md/presets/zh-format.js +8 -0
  16. package/dist/commands/publish-md/title-policy.js +93 -0
  17. package/dist/index.js +1 -0
  18. package/dist/interop/btt-to-last.js +79 -0
  19. package/dist/interop/codec-btt-to-last.js +435 -0
  20. package/dist/interop/codec-last-to-btt.js +383 -0
  21. package/dist/interop/codec-shared.js +722 -0
  22. package/dist/interop/index.js +2 -0
  23. package/dist/interop/last-to-btt.js +17 -0
  24. package/dist/lark/block-types.js +42 -0
  25. package/dist/lark/client.js +36 -0
  26. package/dist/lark/docx/ops.js +596 -0
  27. package/dist/lark/docx/render-btt.js +156 -0
  28. package/dist/lark/docx/render-models.js +1 -0
  29. package/dist/lark/docx/render-payload.js +338 -0
  30. package/dist/lark/docx/render-post-process.js +98 -0
  31. package/dist/lark/docx/render-table.js +87 -0
  32. package/dist/lark/docx/render-types.js +7 -0
  33. package/dist/lark/index.js +2 -0
  34. package/dist/lark/types.js +1 -0
  35. package/dist/last/api.js +1687 -0
  36. package/dist/last/index.js +3 -0
  37. package/dist/last/preview-terminal.js +296 -0
  38. package/dist/last/textual-block-types.js +19 -0
  39. package/dist/last/to-markdown.js +303 -0
  40. package/dist/last/types.js +11 -0
  41. package/dist/pipeline/hast-to-last.js +946 -0
  42. package/dist/pipeline/index.js +3 -0
  43. package/dist/pipeline/markdown/md-to-hast.js +34 -0
  44. package/dist/pipeline/markdown/prepare-markdown.js +1049 -0
  45. package/dist/preview/index.js +1 -0
  46. package/dist/preview/markdown-terminal.js +350 -0
  47. package/dist/publish/asset-adapter.js +123 -0
  48. package/dist/publish/btt-patch.js +65 -0
  49. package/dist/publish/common.js +139 -0
  50. package/dist/publish/ids.js +9 -0
  51. package/dist/publish/index.js +7 -0
  52. package/dist/publish/last-normalize.js +327 -0
  53. package/dist/publish/process-file.js +228 -0
  54. package/dist/publish/runtime.js +133 -0
  55. package/dist/publish/stage-cache.js +56 -0
  56. package/dist/shared/rate-limiter.js +18 -0
  57. package/dist/shared/retry.js +141 -0
  58. package/package.json +78 -0
@@ -0,0 +1,435 @@
1
+ import { LARK_TO_LAST_BLOCK_TYPE, NUM_TO_CALLOUT_BG, NUM_TO_CALLOUT_BORDER, NUM_TO_IFRAME_TYPE, deepClone, fromAlignNumber, isTextualType, larkTextPayloadToLast, } from './codec-shared.js';
2
+ import { sortChildrenByTreeOrder } from './codec-last-to-btt.js';
3
+ function toSearchText(inline) {
4
+ switch (inline.kind) {
5
+ case 'text_run':
6
+ return { text: inline.text ?? '', editable: true };
7
+ case 'mention_user':
8
+ return { text: inline.userId ?? '', editable: false };
9
+ case 'equation':
10
+ return { text: inline.latex ?? '', editable: false };
11
+ case 'mention_doc':
12
+ return { text: inline.title ?? '', editable: false };
13
+ case 'reminder':
14
+ return { text: '', editable: false };
15
+ case 'inline_block':
16
+ return { text: '', editable: false };
17
+ case 'inline_file':
18
+ return { text: '', editable: false };
19
+ case 'link_preview':
20
+ return { text: inline.title ?? inline.url ?? '', editable: false };
21
+ default:
22
+ return { text: '', editable: false };
23
+ }
24
+ }
25
+ function isTextualBlockNode(block) {
26
+ return isTextualType(block.type);
27
+ }
28
+ function buildScopeForTopLevelTextBlock(scopeId, block) {
29
+ let normalizedText = '';
30
+ const segments = [];
31
+ for (const inline of block.payload.inlines) {
32
+ const projection = toSearchText(inline);
33
+ if (projection.text.length === 0)
34
+ continue;
35
+ const from = normalizedText.length;
36
+ normalizedText += projection.text;
37
+ const to = normalizedText.length;
38
+ segments.push({
39
+ inlineId: inline.id,
40
+ inlineKind: inline.kind,
41
+ from,
42
+ to,
43
+ editable: projection.editable,
44
+ });
45
+ }
46
+ return {
47
+ id: scopeId,
48
+ blockId: block.id,
49
+ blockType: block.type,
50
+ normalizedText,
51
+ segments,
52
+ };
53
+ }
54
+ export function buildLASTIndexes(doc) {
55
+ const byType = {};
56
+ for (const block of Object.values(doc.blocks)) {
57
+ const list = byType[block.type] ?? [];
58
+ list.push(block.id);
59
+ byType[block.type] = list;
60
+ }
61
+ const textScopes = {};
62
+ const textScopeByBlockId = {};
63
+ const topLevel = sortChildrenByTreeOrder(doc);
64
+ let scopeCounter = 1;
65
+ for (const childId of topLevel) {
66
+ const child = doc.blocks[childId];
67
+ if (!child || !isTextualBlockNode(child) || child.type === 'page') {
68
+ continue;
69
+ }
70
+ const scopeId = `scope_${scopeCounter}`;
71
+ scopeCounter += 1;
72
+ const scope = buildScopeForTopLevelTextBlock(scopeId, child);
73
+ textScopes[scopeId] = scope;
74
+ textScopeByBlockId[child.id] = scopeId;
75
+ }
76
+ return {
77
+ byType,
78
+ textScopes,
79
+ textScopeByBlockId,
80
+ };
81
+ }
82
+ function nextInlineId(counter) {
83
+ const id = `i_${counter.value}`;
84
+ counter.value += 1;
85
+ return id;
86
+ }
87
+ export function fromRawBlockToLAST(rawBlock, inlineCounter, bttToLastBlockId) {
88
+ const type = LARK_TO_LAST_BLOCK_TYPE[rawBlock.block_type];
89
+ if (!type) {
90
+ throw new Error(`Unsupported lark block_type: ${rawBlock.block_type}`);
91
+ }
92
+ const rawBlockId = String(rawBlock.block_id);
93
+ const id = bttToLastBlockId[rawBlockId];
94
+ if (!id) {
95
+ throw new Error(`Missing LAST block id mapping for BTT block "${rawBlockId}".`);
96
+ }
97
+ const parentIdRaw = String(rawBlock.parent_id ?? '');
98
+ const parentId = parentIdRaw ? (bttToLastBlockId[parentIdRaw] ?? parentIdRaw) : null;
99
+ const childrenDefined = Object.prototype.hasOwnProperty.call(rawBlock, 'children');
100
+ const children = (Array.isArray(rawBlock.children) ? rawBlock.children : []).map((item) => {
101
+ const rawChildId = String(item);
102
+ return (bttToLastBlockId[rawChildId] ?? rawChildId);
103
+ });
104
+ if (isTextualType(type)) {
105
+ return {
106
+ id,
107
+ bttId: rawBlockId,
108
+ type,
109
+ parentId,
110
+ children,
111
+ childrenDefined,
112
+ payload: larkTextPayloadToLast(rawBlock, type, () => nextInlineId(inlineCounter), bttToLastBlockId),
113
+ };
114
+ }
115
+ if (type === 'callout') {
116
+ const callout = (rawBlock.callout ?? {});
117
+ return {
118
+ id,
119
+ bttId: rawBlockId,
120
+ type,
121
+ parentId,
122
+ children,
123
+ childrenDefined,
124
+ payload: {
125
+ ...(typeof callout.background_color === 'number'
126
+ ? {
127
+ backgroundColor: NUM_TO_CALLOUT_BG[callout.background_color] ?? callout.background_color,
128
+ }
129
+ : {}),
130
+ ...(typeof callout.border_color === 'number'
131
+ ? {
132
+ borderColor: NUM_TO_CALLOUT_BORDER[callout.border_color] ?? callout.border_color,
133
+ }
134
+ : {}),
135
+ ...(typeof callout.text_color === 'number'
136
+ ? {
137
+ textColor: NUM_TO_CALLOUT_BORDER[callout.text_color] ?? callout.text_color,
138
+ }
139
+ : {}),
140
+ ...(typeof callout.emoji_id === 'string' ? { emojiId: callout.emoji_id } : {}),
141
+ },
142
+ };
143
+ }
144
+ if (type === 'divider') {
145
+ return {
146
+ id,
147
+ bttId: rawBlockId,
148
+ type,
149
+ parentId,
150
+ children,
151
+ childrenDefined,
152
+ payload: {},
153
+ };
154
+ }
155
+ if (type === 'file') {
156
+ const file = (rawBlock.file ?? {});
157
+ return {
158
+ id,
159
+ bttId: rawBlockId,
160
+ type,
161
+ parentId,
162
+ children,
163
+ childrenDefined,
164
+ payload: {
165
+ ...(typeof file.name === 'string' ? { name: file.name } : {}),
166
+ ...(typeof file.token === 'string' ? { token: file.token } : {}),
167
+ ...(typeof file.view_type === 'number' ? { viewType: file.view_type } : {}),
168
+ },
169
+ };
170
+ }
171
+ if (type === 'bitable') {
172
+ const bitable = (rawBlock.bitable ?? {});
173
+ return {
174
+ id,
175
+ bttId: rawBlockId,
176
+ type,
177
+ parentId,
178
+ children,
179
+ childrenDefined,
180
+ payload: {
181
+ ...(typeof bitable.token === 'string' ? { token: bitable.token } : {}),
182
+ ...(typeof bitable.view_type === 'number' ? { viewType: bitable.view_type } : {}),
183
+ },
184
+ };
185
+ }
186
+ if (type === 'chat_card') {
187
+ const chatCard = (rawBlock.chat_card ?? {});
188
+ const align = fromAlignNumber(chatCard.align);
189
+ return {
190
+ id,
191
+ bttId: rawBlockId,
192
+ type,
193
+ parentId,
194
+ children,
195
+ childrenDefined,
196
+ payload: {
197
+ chatId: typeof chatCard.chat_id === 'string' ? chatCard.chat_id : '',
198
+ ...(align === undefined ? {} : { align }),
199
+ },
200
+ };
201
+ }
202
+ if (type === 'diagram') {
203
+ const diagram = (rawBlock.diagram ?? {});
204
+ return {
205
+ id,
206
+ bttId: rawBlockId,
207
+ type,
208
+ parentId,
209
+ children,
210
+ childrenDefined,
211
+ payload: {
212
+ ...(typeof diagram.diagram_type === 'number' ? { diagramType: diagram.diagram_type } : {}),
213
+ },
214
+ };
215
+ }
216
+ if (type === 'grid') {
217
+ const grid = (rawBlock.grid ?? {});
218
+ return {
219
+ id,
220
+ bttId: rawBlockId,
221
+ type,
222
+ parentId,
223
+ children,
224
+ childrenDefined,
225
+ payload: {
226
+ ...(typeof grid.column_size === 'number' ? { columnSize: grid.column_size } : {}),
227
+ },
228
+ };
229
+ }
230
+ if (type === 'grid_column') {
231
+ const gridColumn = (rawBlock.grid_column ?? {});
232
+ return {
233
+ id,
234
+ bttId: rawBlockId,
235
+ type,
236
+ parentId,
237
+ children,
238
+ childrenDefined,
239
+ payload: {
240
+ ...(typeof gridColumn.width_ratio === 'number' ? { widthRatio: gridColumn.width_ratio } : {}),
241
+ },
242
+ };
243
+ }
244
+ if (type === 'iframe') {
245
+ const iframe = (rawBlock.iframe ?? {});
246
+ const component = (iframe.component ?? {});
247
+ return {
248
+ id,
249
+ bttId: rawBlockId,
250
+ type,
251
+ parentId,
252
+ children,
253
+ childrenDefined,
254
+ payload: {
255
+ component: {
256
+ ...(typeof component.iframe_type === 'number'
257
+ ? {
258
+ iframeType: NUM_TO_IFRAME_TYPE[component.iframe_type] ?? component.iframe_type,
259
+ }
260
+ : {}),
261
+ ...(typeof component.url === 'string' ? { url: component.url } : {}),
262
+ },
263
+ },
264
+ };
265
+ }
266
+ if (type === 'image' || type === 'board') {
267
+ const payloadRaw = (rawBlock[type] ?? {});
268
+ const captionRaw = (payloadRaw.caption ?? {});
269
+ const align = fromAlignNumber(payloadRaw.align);
270
+ return {
271
+ id,
272
+ bttId: rawBlockId,
273
+ type,
274
+ parentId,
275
+ children,
276
+ childrenDefined,
277
+ payload: {
278
+ ...(typeof payloadRaw.width === 'number' ? { width: payloadRaw.width } : {}),
279
+ ...(typeof payloadRaw.height === 'number' ? { height: payloadRaw.height } : {}),
280
+ ...(typeof payloadRaw.token === 'string' ? { token: payloadRaw.token } : {}),
281
+ ...(align === undefined ? {} : { align }),
282
+ ...(typeof payloadRaw.scale === 'number' ? { scale: payloadRaw.scale } : {}),
283
+ ...(Object.prototype.hasOwnProperty.call(payloadRaw, 'caption')
284
+ ? {
285
+ caption: {
286
+ ...(typeof captionRaw.content === 'string' ? { content: captionRaw.content } : {}),
287
+ },
288
+ }
289
+ : {}),
290
+ },
291
+ };
292
+ }
293
+ if (type === 'table') {
294
+ const table = (rawBlock.table ?? {});
295
+ const property = (table.property ?? {});
296
+ const rawCells = Array.isArray(table.cells) ? table.cells : null;
297
+ const mergeInfoRaw = Array.isArray(property.merge_info) ? property.merge_info : null;
298
+ const payload = {
299
+ ...(rawCells
300
+ ? {
301
+ cells: rawCells.map((cell) => {
302
+ const rawCellId = String(cell);
303
+ return (bttToLastBlockId[rawCellId] ?? rawCellId);
304
+ }),
305
+ }
306
+ : {}),
307
+ ...(typeof property.row_size === 'number' ? { rowSize: property.row_size } : {}),
308
+ ...(typeof property.column_size === 'number' ? { columnSize: property.column_size } : {}),
309
+ ...(mergeInfoRaw
310
+ ? {
311
+ mergeInfo: mergeInfoRaw.map((item) => {
312
+ const entry = item;
313
+ return {
314
+ ...(typeof entry.row_span === 'number' ? { rowSpan: entry.row_span } : {}),
315
+ ...(typeof entry.col_span === 'number' ? { colSpan: entry.col_span } : {}),
316
+ };
317
+ }),
318
+ }
319
+ : {}),
320
+ ...(Array.isArray(property.column_width)
321
+ ? {
322
+ columnWidth: property.column_width.filter((item) => typeof item === 'number'),
323
+ }
324
+ : {}),
325
+ ...(Object.prototype.hasOwnProperty.call(property, 'header_column')
326
+ ? { headerColumn: Boolean(property.header_column) }
327
+ : {}),
328
+ ...(Object.prototype.hasOwnProperty.call(property, 'header_row')
329
+ ? { headerRow: Boolean(property.header_row) }
330
+ : {}),
331
+ };
332
+ return {
333
+ id,
334
+ bttId: rawBlockId,
335
+ type,
336
+ parentId,
337
+ children,
338
+ childrenDefined,
339
+ payload,
340
+ };
341
+ }
342
+ if (type === 'table_cell') {
343
+ return {
344
+ id,
345
+ bttId: rawBlockId,
346
+ type,
347
+ parentId,
348
+ children,
349
+ childrenDefined,
350
+ payload: {},
351
+ };
352
+ }
353
+ if (type === 'mindnote') {
354
+ const mindnote = (rawBlock.mindnote ?? {});
355
+ return {
356
+ id,
357
+ bttId: rawBlockId,
358
+ type,
359
+ parentId,
360
+ children,
361
+ childrenDefined,
362
+ payload: {
363
+ ...(typeof mindnote.token === 'string' ? { token: mindnote.token } : {}),
364
+ },
365
+ };
366
+ }
367
+ if (type === 'sheet') {
368
+ const sheet = (rawBlock.sheet ?? {});
369
+ return {
370
+ id,
371
+ bttId: rawBlockId,
372
+ type,
373
+ parentId,
374
+ children,
375
+ childrenDefined,
376
+ payload: {
377
+ ...(typeof sheet.token === 'string' ? { token: sheet.token } : {}),
378
+ ...(typeof sheet.row_size === 'number' ? { rowSize: sheet.row_size } : {}),
379
+ ...(typeof sheet.column_size === 'number' ? { columnSize: sheet.column_size } : {}),
380
+ },
381
+ };
382
+ }
383
+ if (type === 'view') {
384
+ const view = (rawBlock.view ?? {});
385
+ return {
386
+ id,
387
+ bttId: rawBlockId,
388
+ type,
389
+ parentId,
390
+ children,
391
+ childrenDefined,
392
+ payload: {
393
+ ...(typeof view.view_type === 'number' ? { viewType: view.view_type } : {}),
394
+ },
395
+ };
396
+ }
397
+ if (type === 'quote_container') {
398
+ return {
399
+ id,
400
+ bttId: rawBlockId,
401
+ type,
402
+ parentId,
403
+ children,
404
+ childrenDefined,
405
+ payload: {},
406
+ };
407
+ }
408
+ if (type === 'synced_block') {
409
+ const synced = (rawBlock.reference_synced ?? rawBlock.synced_block ?? {});
410
+ return {
411
+ id,
412
+ bttId: rawBlockId,
413
+ type,
414
+ parentId,
415
+ children,
416
+ childrenDefined,
417
+ payload: {
418
+ ...(typeof synced.source_document_id === 'string' ? { sourceDocumentId: synced.source_document_id } : {}),
419
+ ...(typeof synced.source_block_id === 'string' ? { sourceBlockId: synced.source_block_id } : {}),
420
+ },
421
+ };
422
+ }
423
+ throw new Error(`Unsupported lark block type mapping in fromRawBlockToLAST: ${type}`);
424
+ }
425
+ export function flattenTreeBlocks(root) {
426
+ const list = [];
427
+ const visit = (node) => {
428
+ list.push(deepClone(node.rawBlock));
429
+ for (const child of node.children) {
430
+ visit(child);
431
+ }
432
+ };
433
+ visit(root);
434
+ return list;
435
+ }