@atikk-co-jp/notion-mcp-server 0.1.0 → 0.2.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.
- package/README.ja.md +95 -4
- package/README.md +95 -4
- package/dist/bin/cli.js +10 -10
- package/dist/src/converters/__tests__/block-to-markdown.test.d.ts +2 -0
- package/dist/src/converters/__tests__/block-to-markdown.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/block-to-markdown.test.js +611 -0
- package/dist/src/converters/__tests__/page-to-markdown.test.d.ts +2 -0
- package/dist/src/converters/__tests__/page-to-markdown.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/page-to-markdown.test.js +567 -0
- package/dist/src/converters/__tests__/rich-text-to-markdown.test.d.ts +2 -0
- package/dist/src/converters/__tests__/rich-text-to-markdown.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/rich-text-to-markdown.test.js +353 -0
- package/dist/src/converters/block-to-markdown.d.ts +38 -0
- package/dist/src/converters/block-to-markdown.d.ts.map +1 -0
- package/dist/src/converters/block-to-markdown.js +484 -0
- package/dist/src/converters/index.d.ts +9 -0
- package/dist/src/converters/index.d.ts.map +1 -0
- package/dist/src/converters/index.js +11 -0
- package/dist/src/converters/page-to-markdown.d.ts +64 -0
- package/dist/src/converters/page-to-markdown.d.ts.map +1 -0
- package/dist/src/converters/page-to-markdown.js +189 -0
- package/dist/src/converters/rich-text-to-markdown.d.ts +61 -0
- package/dist/src/converters/rich-text-to-markdown.d.ts.map +1 -0
- package/dist/src/converters/rich-text-to-markdown.js +95 -0
- package/dist/src/index.d.ts +6 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -5
- package/dist/src/notion-client.d.ts +58 -15
- package/dist/src/notion-client.d.ts.map +1 -1
- package/dist/src/notion-client.js +23 -13
- package/dist/src/schemas/block.d.ts +2158 -622
- package/dist/src/schemas/block.d.ts.map +1 -1
- package/dist/src/schemas/block.js +283 -76
- package/dist/src/schemas/common.d.ts +518 -6
- package/dist/src/schemas/common.d.ts.map +1 -1
- package/dist/src/schemas/common.js +120 -42
- package/dist/src/schemas/database.d.ts +687 -0
- package/dist/src/schemas/database.d.ts.map +1 -0
- package/dist/src/schemas/database.js +264 -0
- package/dist/src/schemas/filter.d.ts +509 -3
- package/dist/src/schemas/filter.d.ts.map +1 -1
- package/dist/src/schemas/filter.js +110 -13
- package/dist/src/schemas/index.d.ts +5 -4
- package/dist/src/schemas/index.d.ts.map +1 -1
- package/dist/src/schemas/index.js +7 -5
- package/dist/src/schemas/page.d.ts +2152 -19
- package/dist/src/schemas/page.d.ts.map +1 -1
- package/dist/src/schemas/page.js +216 -22
- package/dist/src/schemas/schemas.test.d.ts +2 -0
- package/dist/src/schemas/schemas.test.d.ts.map +1 -0
- package/dist/src/schemas/schemas.test.js +418 -0
- package/dist/src/server.d.ts +2 -2
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +6 -6
- package/dist/src/tools/append-block-children.d.ts +2 -2
- package/dist/src/tools/append-block-children.d.ts.map +1 -1
- package/dist/src/tools/append-block-children.js +16 -10
- package/dist/src/tools/create-comment.d.ts +2 -2
- package/dist/src/tools/create-comment.d.ts.map +1 -1
- package/dist/src/tools/create-comment.js +15 -9
- package/dist/src/tools/create-database.d.ts +4 -0
- package/dist/src/tools/create-database.d.ts.map +1 -0
- package/dist/src/tools/create-database.js +57 -0
- package/dist/src/tools/create-page.d.ts +2 -2
- package/dist/src/tools/create-page.d.ts.map +1 -1
- package/dist/src/tools/create-page.js +22 -24
- package/dist/src/tools/get-block-children.d.ts +2 -2
- package/dist/src/tools/get-block-children.d.ts.map +1 -1
- package/dist/src/tools/get-block-children.js +39 -6
- package/dist/src/tools/index.d.ts +13 -11
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +13 -9
- package/dist/src/tools/query-database.d.ts +2 -2
- package/dist/src/tools/query-database.d.ts.map +1 -1
- package/dist/src/tools/query-database.js +27 -18
- package/dist/src/tools/retrieve-page.d.ts +2 -2
- package/dist/src/tools/retrieve-page.d.ts.map +1 -1
- package/dist/src/tools/retrieve-page.js +44 -4
- package/dist/src/tools/search.d.ts +2 -2
- package/dist/src/tools/search.d.ts.map +1 -1
- package/dist/src/tools/search.js +18 -12
- package/dist/src/tools/update-database.d.ts +4 -0
- package/dist/src/tools/update-database.d.ts.map +1 -0
- package/dist/src/tools/update-database.js +74 -0
- package/dist/src/tools/update-page.d.ts +2 -2
- package/dist/src/tools/update-page.d.ts.map +1 -1
- package/dist/src/tools/update-page.js +23 -18
- package/dist/src/utils/error-handler.d.ts +1 -1
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/error-handler.js +14 -14
- package/dist/src/utils/index.d.ts +2 -2
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -2
- package/dist/src/utils/response-formatter.d.ts +13 -1
- package/dist/src/utils/response-formatter.d.ts.map +1 -1
- package/dist/src/utils/response-formatter.js +46 -3
- package/package.json +11 -2
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notionブロックをマークダウン文字列に変換するモジュール
|
|
3
|
+
*/
|
|
4
|
+
import { richTextToMarkdown } from './rich-text-to-markdown.js';
|
|
5
|
+
/**
|
|
6
|
+
* ファイルオブジェクトからURLを抽出
|
|
7
|
+
*/
|
|
8
|
+
function extractFileUrl(fileObj) {
|
|
9
|
+
if (!fileObj)
|
|
10
|
+
return '';
|
|
11
|
+
if (fileObj.type === 'external' && fileObj.external?.url) {
|
|
12
|
+
return fileObj.external.url;
|
|
13
|
+
}
|
|
14
|
+
if (fileObj.type === 'file' && fileObj.file?.url) {
|
|
15
|
+
return fileObj.file.url;
|
|
16
|
+
}
|
|
17
|
+
// type が指定されていない場合のフォールバック
|
|
18
|
+
if (fileObj.external?.url)
|
|
19
|
+
return fileObj.external.url;
|
|
20
|
+
if (fileObj.file?.url)
|
|
21
|
+
return fileObj.file.url;
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* アイコンからテキストを抽出
|
|
26
|
+
*/
|
|
27
|
+
function extractIconText(icon) {
|
|
28
|
+
if (!icon)
|
|
29
|
+
return '';
|
|
30
|
+
if (icon.type === 'emoji' && icon.emoji) {
|
|
31
|
+
return icon.emoji;
|
|
32
|
+
}
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 単一のブロックをマークダウンに変換
|
|
37
|
+
*/
|
|
38
|
+
async function convertBlock(block, options = {}) {
|
|
39
|
+
const indent = ' '.repeat(options.indentLevel ?? 0);
|
|
40
|
+
const blockData = block[block.type];
|
|
41
|
+
if (!blockData && block.type !== 'divider') {
|
|
42
|
+
// 未対応ブロックタイプの場合
|
|
43
|
+
return `${indent}<!-- Unsupported block type: ${block.type} -->`;
|
|
44
|
+
}
|
|
45
|
+
switch (block.type) {
|
|
46
|
+
case 'paragraph': {
|
|
47
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
48
|
+
return text ? `${indent}${text}` : '';
|
|
49
|
+
}
|
|
50
|
+
case 'heading_1': {
|
|
51
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
52
|
+
return `${indent}# ${text}`;
|
|
53
|
+
}
|
|
54
|
+
case 'heading_2': {
|
|
55
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
56
|
+
return `${indent}## ${text}`;
|
|
57
|
+
}
|
|
58
|
+
case 'heading_3': {
|
|
59
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
60
|
+
return `${indent}### ${text}`;
|
|
61
|
+
}
|
|
62
|
+
case 'bulleted_list_item': {
|
|
63
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
64
|
+
let result = `${indent}- ${text}`;
|
|
65
|
+
// 子ブロックがある場合
|
|
66
|
+
if (block.has_children && options.fetchChildren && block.id) {
|
|
67
|
+
const children = await options.fetchChildren(block.id);
|
|
68
|
+
const childMarkdown = await blocksToMarkdown(children, {
|
|
69
|
+
...options,
|
|
70
|
+
indentLevel: (options.indentLevel ?? 0) + 1,
|
|
71
|
+
});
|
|
72
|
+
if (childMarkdown) {
|
|
73
|
+
result += `\n${childMarkdown}`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
case 'numbered_list_item': {
|
|
79
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
80
|
+
const index = options.listIndex ?? 1;
|
|
81
|
+
let result = `${indent}${index}. ${text}`;
|
|
82
|
+
// 子ブロックがある場合
|
|
83
|
+
if (block.has_children && options.fetchChildren && block.id) {
|
|
84
|
+
const children = await options.fetchChildren(block.id);
|
|
85
|
+
const childMarkdown = await blocksToMarkdown(children, {
|
|
86
|
+
...options,
|
|
87
|
+
indentLevel: (options.indentLevel ?? 0) + 1,
|
|
88
|
+
});
|
|
89
|
+
if (childMarkdown) {
|
|
90
|
+
result += `\n${childMarkdown}`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
case 'to_do': {
|
|
96
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
97
|
+
const checked = blockData?.checked ? 'x' : ' ';
|
|
98
|
+
return `${indent}- [${checked}] ${text}`;
|
|
99
|
+
}
|
|
100
|
+
case 'toggle': {
|
|
101
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
102
|
+
let result = `${indent}<details>\n${indent}<summary>${text}</summary>\n`;
|
|
103
|
+
// 子ブロックがある場合
|
|
104
|
+
if (block.has_children && options.fetchChildren && block.id) {
|
|
105
|
+
const children = await options.fetchChildren(block.id);
|
|
106
|
+
const childMarkdown = await blocksToMarkdown(children, {
|
|
107
|
+
...options,
|
|
108
|
+
indentLevel: (options.indentLevel ?? 0) + 1,
|
|
109
|
+
});
|
|
110
|
+
if (childMarkdown) {
|
|
111
|
+
result += `\n${childMarkdown}\n`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
result += `${indent}</details>`;
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
case 'code': {
|
|
118
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
119
|
+
const language = blockData?.language || '';
|
|
120
|
+
const caption = blockData?.caption
|
|
121
|
+
? richTextToMarkdown(blockData.caption)
|
|
122
|
+
: '';
|
|
123
|
+
let result = `${indent}\`\`\`${language}\n${text}\n${indent}\`\`\``;
|
|
124
|
+
if (caption) {
|
|
125
|
+
result += `\n${indent}*${caption}*`;
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
case 'quote': {
|
|
130
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
131
|
+
// 複数行の場合は各行に > を付ける
|
|
132
|
+
const lines = text.split('\n');
|
|
133
|
+
return lines.map((line) => `${indent}> ${line}`).join('\n');
|
|
134
|
+
}
|
|
135
|
+
case 'callout': {
|
|
136
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
137
|
+
const icon = extractIconText(blockData?.icon);
|
|
138
|
+
const prefix = icon ? `${icon} ` : '';
|
|
139
|
+
return `${indent}> ${prefix}**Note:** ${text}`;
|
|
140
|
+
}
|
|
141
|
+
case 'divider': {
|
|
142
|
+
return `${indent}---`;
|
|
143
|
+
}
|
|
144
|
+
case 'bookmark': {
|
|
145
|
+
const url = blockData?.url || '';
|
|
146
|
+
const caption = blockData?.caption
|
|
147
|
+
? richTextToMarkdown(blockData.caption)
|
|
148
|
+
: '';
|
|
149
|
+
const displayText = caption || url;
|
|
150
|
+
return `${indent}[${displayText}](${url})`;
|
|
151
|
+
}
|
|
152
|
+
case 'image': {
|
|
153
|
+
const url = extractFileUrl(blockData);
|
|
154
|
+
const caption = blockData?.caption
|
|
155
|
+
? richTextToMarkdown(blockData.caption)
|
|
156
|
+
: '';
|
|
157
|
+
return `${indent}`;
|
|
158
|
+
}
|
|
159
|
+
case 'video': {
|
|
160
|
+
const url = extractFileUrl(blockData);
|
|
161
|
+
const caption = blockData?.caption
|
|
162
|
+
? richTextToMarkdown(blockData.caption)
|
|
163
|
+
: '';
|
|
164
|
+
const displayText = caption || 'Video';
|
|
165
|
+
return `${indent}[${displayText}](${url})`;
|
|
166
|
+
}
|
|
167
|
+
case 'audio': {
|
|
168
|
+
const url = extractFileUrl(blockData);
|
|
169
|
+
const caption = blockData?.caption
|
|
170
|
+
? richTextToMarkdown(blockData.caption)
|
|
171
|
+
: '';
|
|
172
|
+
const displayText = caption || 'Audio';
|
|
173
|
+
return `${indent}[${displayText}](${url})`;
|
|
174
|
+
}
|
|
175
|
+
case 'file':
|
|
176
|
+
case 'pdf': {
|
|
177
|
+
const url = extractFileUrl(blockData);
|
|
178
|
+
const caption = blockData?.caption
|
|
179
|
+
? richTextToMarkdown(blockData.caption)
|
|
180
|
+
: '';
|
|
181
|
+
const name = blockData?.name || caption || 'File';
|
|
182
|
+
return `${indent}[${name}](${url})`;
|
|
183
|
+
}
|
|
184
|
+
case 'embed': {
|
|
185
|
+
const url = blockData?.url || '';
|
|
186
|
+
const caption = blockData?.caption
|
|
187
|
+
? richTextToMarkdown(blockData.caption)
|
|
188
|
+
: '';
|
|
189
|
+
const displayText = caption || 'Embed';
|
|
190
|
+
return `${indent}[${displayText}](${url})`;
|
|
191
|
+
}
|
|
192
|
+
case 'table_of_contents': {
|
|
193
|
+
return `${indent}[TOC]`;
|
|
194
|
+
}
|
|
195
|
+
case 'equation': {
|
|
196
|
+
const expression = blockData?.expression || '';
|
|
197
|
+
return `${indent}$$\n${expression}\n$$`;
|
|
198
|
+
}
|
|
199
|
+
case 'child_page': {
|
|
200
|
+
const title = blockData?.title || 'Untitled';
|
|
201
|
+
return `${indent}📄 [${title}]`;
|
|
202
|
+
}
|
|
203
|
+
case 'child_database': {
|
|
204
|
+
const title = blockData?.title || 'Untitled Database';
|
|
205
|
+
return `${indent}📊 [${title}]`;
|
|
206
|
+
}
|
|
207
|
+
case 'link_preview': {
|
|
208
|
+
const url = blockData?.url || '';
|
|
209
|
+
return `${indent}[${url}](${url})`;
|
|
210
|
+
}
|
|
211
|
+
case 'synced_block': {
|
|
212
|
+
// 同期ブロックの内容は子ブロックとして取得される
|
|
213
|
+
if (block.has_children && options.fetchChildren && block.id) {
|
|
214
|
+
const children = await options.fetchChildren(block.id);
|
|
215
|
+
return await blocksToMarkdown(children, options);
|
|
216
|
+
}
|
|
217
|
+
return '';
|
|
218
|
+
}
|
|
219
|
+
case 'column_list': {
|
|
220
|
+
// カラムリストは子ブロック(column)として処理
|
|
221
|
+
if (block.has_children && options.fetchChildren && block.id) {
|
|
222
|
+
const children = await options.fetchChildren(block.id);
|
|
223
|
+
const columnContents = [];
|
|
224
|
+
for (const column of children) {
|
|
225
|
+
if (column.type === 'column' && column.has_children && options.fetchChildren) {
|
|
226
|
+
const columnChildren = await options.fetchChildren(column.id);
|
|
227
|
+
const content = await blocksToMarkdown(columnChildren, options);
|
|
228
|
+
if (content) {
|
|
229
|
+
columnContents.push(content);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// カラムを | で区切って表示
|
|
234
|
+
return columnContents.join('\n\n---\n\n');
|
|
235
|
+
}
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
case 'column': {
|
|
239
|
+
// column自体は直接変換しない(column_listで処理)
|
|
240
|
+
return '';
|
|
241
|
+
}
|
|
242
|
+
case 'table': {
|
|
243
|
+
// テーブルは子ブロック(table_row)として処理
|
|
244
|
+
if (block.has_children && options.fetchChildren && block.id) {
|
|
245
|
+
const children = await options.fetchChildren(block.id);
|
|
246
|
+
const rows = [];
|
|
247
|
+
const hasColumnHeader = blockData?.has_column_header;
|
|
248
|
+
for (let i = 0; i < children.length; i++) {
|
|
249
|
+
const row = children[i];
|
|
250
|
+
if (row.type === 'table_row') {
|
|
251
|
+
const rowData = row.table_row;
|
|
252
|
+
const cells = rowData?.cells || [];
|
|
253
|
+
const cellTexts = cells.map((cell) => richTextToMarkdown(cell));
|
|
254
|
+
rows.push(`${indent}| ${cellTexts.join(' | ')} |`);
|
|
255
|
+
// ヘッダー行の後にセパレーターを追加
|
|
256
|
+
if (i === 0 && hasColumnHeader) {
|
|
257
|
+
const separator = cells.map(() => '---').join(' | ');
|
|
258
|
+
rows.push(`${indent}| ${separator} |`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return rows.join('\n');
|
|
263
|
+
}
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
case 'table_row': {
|
|
267
|
+
// table_rowは直接変換しない(tableで処理)
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
270
|
+
case 'breadcrumb': {
|
|
271
|
+
return `${indent}<!-- Breadcrumb -->`;
|
|
272
|
+
}
|
|
273
|
+
case 'template': {
|
|
274
|
+
// テンプレートブロックは非推奨
|
|
275
|
+
return `${indent}<!-- Template block -->`;
|
|
276
|
+
}
|
|
277
|
+
default: {
|
|
278
|
+
// 未対応ブロックタイプ
|
|
279
|
+
return `${indent}<!-- Unsupported block type: ${block.type} -->`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* ブロック配列をマークダウン文字列に変換
|
|
285
|
+
* @param blocks - Notion APIから取得したブロック配列
|
|
286
|
+
* @param options - 変換オプション
|
|
287
|
+
* @returns マークダウン文字列
|
|
288
|
+
*/
|
|
289
|
+
export async function blocksToMarkdown(blocks, options = {}) {
|
|
290
|
+
if (!blocks || blocks.length === 0) {
|
|
291
|
+
return '';
|
|
292
|
+
}
|
|
293
|
+
const lines = [];
|
|
294
|
+
let numberedListIndex = 1;
|
|
295
|
+
for (const block of blocks) {
|
|
296
|
+
// 番号付きリストのインデックス管理
|
|
297
|
+
const isNumberedList = block.type === 'numbered_list_item';
|
|
298
|
+
const currentOptions = {
|
|
299
|
+
...options,
|
|
300
|
+
listIndex: isNumberedList ? numberedListIndex : undefined,
|
|
301
|
+
};
|
|
302
|
+
const markdown = await convertBlock(block, currentOptions);
|
|
303
|
+
// 番号付きリストのインデックスをインクリメント
|
|
304
|
+
if (isNumberedList) {
|
|
305
|
+
numberedListIndex++;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
numberedListIndex = 1; // リセット
|
|
309
|
+
}
|
|
310
|
+
// 空でない結果のみ追加
|
|
311
|
+
if (markdown) {
|
|
312
|
+
lines.push(markdown);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return lines.join('\n');
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* ブロック配列をマークダウン文字列に変換(同期版、子ブロック取得なし)
|
|
319
|
+
* @param blocks - Notion APIから取得したブロック配列
|
|
320
|
+
* @returns マークダウン文字列
|
|
321
|
+
*/
|
|
322
|
+
export function blocksToMarkdownSync(blocks) {
|
|
323
|
+
if (!blocks || blocks.length === 0) {
|
|
324
|
+
return '';
|
|
325
|
+
}
|
|
326
|
+
const lines = [];
|
|
327
|
+
let numberedListIndex = 1;
|
|
328
|
+
for (const block of blocks) {
|
|
329
|
+
const indent = '';
|
|
330
|
+
const blockData = block[block.type];
|
|
331
|
+
let markdown = '';
|
|
332
|
+
switch (block.type) {
|
|
333
|
+
case 'paragraph': {
|
|
334
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
335
|
+
markdown = text ? `${indent}${text}` : '';
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
case 'heading_1': {
|
|
339
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
340
|
+
markdown = `${indent}# ${text}`;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
case 'heading_2': {
|
|
344
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
345
|
+
markdown = `${indent}## ${text}`;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
case 'heading_3': {
|
|
349
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
350
|
+
markdown = `${indent}### ${text}`;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
case 'bulleted_list_item': {
|
|
354
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
355
|
+
markdown = `${indent}- ${text}`;
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
case 'numbered_list_item': {
|
|
359
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
360
|
+
markdown = `${indent}${numberedListIndex}. ${text}`;
|
|
361
|
+
numberedListIndex++;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case 'to_do': {
|
|
365
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
366
|
+
const checked = blockData?.checked ? 'x' : ' ';
|
|
367
|
+
markdown = `${indent}- [${checked}] ${text}`;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
case 'toggle': {
|
|
371
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
372
|
+
markdown = `${indent}<details>\n${indent}<summary>${text}</summary>\n${indent}</details>`;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case 'code': {
|
|
376
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
377
|
+
const language = blockData?.language || '';
|
|
378
|
+
markdown = `${indent}\`\`\`${language}\n${text}\n${indent}\`\`\``;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case 'quote': {
|
|
382
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
383
|
+
const lines = text.split('\n');
|
|
384
|
+
markdown = lines.map((line) => `${indent}> ${line}`).join('\n');
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case 'callout': {
|
|
388
|
+
const text = richTextToMarkdown(blockData?.rich_text);
|
|
389
|
+
const icon = extractIconText(blockData?.icon);
|
|
390
|
+
const prefix = icon ? `${icon} ` : '';
|
|
391
|
+
markdown = `${indent}> ${prefix}**Note:** ${text}`;
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case 'divider': {
|
|
395
|
+
markdown = `${indent}---`;
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case 'bookmark': {
|
|
399
|
+
const url = blockData?.url || '';
|
|
400
|
+
const caption = blockData?.caption
|
|
401
|
+
? richTextToMarkdown(blockData.caption)
|
|
402
|
+
: '';
|
|
403
|
+
const displayText = caption || url;
|
|
404
|
+
markdown = `${indent}[${displayText}](${url})`;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case 'image': {
|
|
408
|
+
const url = extractFileUrl(blockData);
|
|
409
|
+
const caption = blockData?.caption
|
|
410
|
+
? richTextToMarkdown(blockData.caption)
|
|
411
|
+
: '';
|
|
412
|
+
markdown = `${indent}`;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
case 'video':
|
|
416
|
+
case 'audio': {
|
|
417
|
+
const url = extractFileUrl(blockData);
|
|
418
|
+
const caption = blockData?.caption
|
|
419
|
+
? richTextToMarkdown(blockData.caption)
|
|
420
|
+
: '';
|
|
421
|
+
const displayText = caption || (block.type === 'video' ? 'Video' : 'Audio');
|
|
422
|
+
markdown = `${indent}[${displayText}](${url})`;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case 'file':
|
|
426
|
+
case 'pdf': {
|
|
427
|
+
const url = extractFileUrl(blockData);
|
|
428
|
+
const caption = blockData?.caption
|
|
429
|
+
? richTextToMarkdown(blockData.caption)
|
|
430
|
+
: '';
|
|
431
|
+
const name = blockData?.name || caption || 'File';
|
|
432
|
+
markdown = `${indent}[${name}](${url})`;
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case 'embed': {
|
|
436
|
+
const url = blockData?.url || '';
|
|
437
|
+
const caption = blockData?.caption
|
|
438
|
+
? richTextToMarkdown(blockData.caption)
|
|
439
|
+
: '';
|
|
440
|
+
const displayText = caption || 'Embed';
|
|
441
|
+
markdown = `${indent}[${displayText}](${url})`;
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
case 'table_of_contents': {
|
|
445
|
+
markdown = `${indent}[TOC]`;
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case 'equation': {
|
|
449
|
+
const expression = blockData?.expression || '';
|
|
450
|
+
markdown = `${indent}$$\n${expression}\n$$`;
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
case 'child_page': {
|
|
454
|
+
const title = blockData?.title || 'Untitled';
|
|
455
|
+
markdown = `${indent}📄 [${title}]`;
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
case 'child_database': {
|
|
459
|
+
const title = blockData?.title || 'Untitled Database';
|
|
460
|
+
markdown = `${indent}📊 [${title}]`;
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case 'link_preview': {
|
|
464
|
+
const url = blockData?.url || '';
|
|
465
|
+
markdown = `${indent}[${url}](${url})`;
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
default: {
|
|
469
|
+
if (blockData) {
|
|
470
|
+
markdown = `${indent}<!-- Unsupported block type: ${block.type} -->`;
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// 番号付きリスト以外の場合はインデックスをリセット
|
|
476
|
+
if (block.type !== 'numbered_list_item') {
|
|
477
|
+
numberedListIndex = 1;
|
|
478
|
+
}
|
|
479
|
+
if (markdown) {
|
|
480
|
+
lines.push(markdown);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return lines.join('\n');
|
|
484
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notion データ変換モジュール
|
|
3
|
+
*
|
|
4
|
+
* Notion APIから取得したデータをマークダウンやシンプルな形式に変換する機能を提供
|
|
5
|
+
*/
|
|
6
|
+
export { blocksToMarkdown, blocksToMarkdownSync, type ConvertOptions, type NotionBlock, } from './block-to-markdown.js';
|
|
7
|
+
export { type NotionProperty, type PropertyValue, pagePropertiesToObject, pagePropertiesToSimple, pagesToSimple, pageToSimple, type SimplePage, type SimpleProperty, } from './page-to-markdown.js';
|
|
8
|
+
export { type RichTextItem, richTextToMarkdown, richTextToPlain, } from './rich-text-to-markdown.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/converters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,KAAK,YAAY,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,4BAA4B,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notion データ変換モジュール
|
|
3
|
+
*
|
|
4
|
+
* Notion APIから取得したデータをマークダウンやシンプルな形式に変換する機能を提供
|
|
5
|
+
*/
|
|
6
|
+
// ブロック変換
|
|
7
|
+
export { blocksToMarkdown, blocksToMarkdownSync, } from './block-to-markdown.js';
|
|
8
|
+
// ページプロパティ変換
|
|
9
|
+
export { pagePropertiesToObject, pagePropertiesToSimple, pagesToSimple, pageToSimple, } from './page-to-markdown.js';
|
|
10
|
+
// RichText変換
|
|
11
|
+
export { richTextToMarkdown, richTextToPlain, } from './rich-text-to-markdown.js';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notionページプロパティをシンプルな形式に変換するモジュール
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* シンプル化されたプロパティの型
|
|
6
|
+
*/
|
|
7
|
+
export interface SimpleProperty {
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
value: PropertyValue;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* プロパティ値の型
|
|
14
|
+
*/
|
|
15
|
+
export type PropertyValue = string | number | boolean | string[] | null | Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Notionプロパティオブジェクトの型
|
|
18
|
+
*/
|
|
19
|
+
export interface NotionProperty {
|
|
20
|
+
id?: string;
|
|
21
|
+
type: string;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* ページプロパティをシンプルな形式に変換
|
|
26
|
+
* @param properties - Notion APIから取得したプロパティオブジェクト
|
|
27
|
+
* @returns シンプル化されたプロパティの配列
|
|
28
|
+
*/
|
|
29
|
+
export declare function pagePropertiesToSimple(properties: Record<string, NotionProperty>): SimpleProperty[];
|
|
30
|
+
/**
|
|
31
|
+
* ページプロパティをオブジェクト形式に変換(キー: プロパティ名、値: 値)
|
|
32
|
+
* @param properties - Notion APIから取得したプロパティオブジェクト
|
|
33
|
+
* @returns シンプル化されたプロパティオブジェクト
|
|
34
|
+
*/
|
|
35
|
+
export declare function pagePropertiesToObject(properties: Record<string, NotionProperty>): Record<string, PropertyValue>;
|
|
36
|
+
/**
|
|
37
|
+
* シンプル化されたページオブジェクトの型
|
|
38
|
+
*/
|
|
39
|
+
export interface SimplePage {
|
|
40
|
+
id: string;
|
|
41
|
+
url: string;
|
|
42
|
+
properties: Record<string, PropertyValue>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* ページオブジェクトをシンプルな形式に変換
|
|
46
|
+
* @param page - Notion APIから取得したページオブジェクト
|
|
47
|
+
* @returns シンプル化されたページオブジェクト
|
|
48
|
+
*/
|
|
49
|
+
export declare function pageToSimple(page: {
|
|
50
|
+
id: string;
|
|
51
|
+
url?: string;
|
|
52
|
+
properties: Record<string, NotionProperty>;
|
|
53
|
+
}): SimplePage;
|
|
54
|
+
/**
|
|
55
|
+
* ページ配列をシンプルな形式に変換
|
|
56
|
+
* @param pages - Notion APIから取得したページ配列
|
|
57
|
+
* @returns シンプル化されたページ配列
|
|
58
|
+
*/
|
|
59
|
+
export declare function pagesToSimple(pages: {
|
|
60
|
+
id: string;
|
|
61
|
+
url?: string;
|
|
62
|
+
properties: Record<string, NotionProperty>;
|
|
63
|
+
}[]): SimplePage[];
|
|
64
|
+
//# sourceMappingURL=page-to-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-to-markdown.d.ts","sourceRoot":"","sources":["../../../src/converters/page-to-markdown.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,aAAa,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEjG;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAwKD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GACzC,cAAc,EAAE,CAUlB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GACzC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAU/B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CAC3C,GAAG,UAAU,CAMb;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CAAE,EAAE,GAChF,UAAU,EAAE,CAKd"}
|