@n24q02m/better-notion-mcp 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.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/bin/cli.mjs +117 -0
- package/build/scripts/start-server.d.ts +6 -0
- package/build/scripts/start-server.d.ts.map +1 -0
- package/build/scripts/start-server.js +21 -0
- package/build/scripts/start-server.js.map +1 -0
- package/build/src/init-server.d.ts +29 -0
- package/build/src/init-server.d.ts.map +1 -0
- package/build/src/init-server.js +32 -0
- package/build/src/init-server.js.map +1 -0
- package/build/src/tools/composite/blocks.d.ts +16 -0
- package/build/src/tools/composite/blocks.d.ts.map +1 -0
- package/build/src/tools/composite/blocks.js +104 -0
- package/build/src/tools/composite/blocks.js.map +1 -0
- package/build/src/tools/composite/comments.d.ts +16 -0
- package/build/src/tools/composite/comments.d.ts.map +1 -0
- package/build/src/tools/composite/comments.js +69 -0
- package/build/src/tools/composite/comments.js.map +1 -0
- package/build/src/tools/composite/content.d.ts +13 -0
- package/build/src/tools/composite/content.d.ts.map +1 -0
- package/build/src/tools/composite/content.js +50 -0
- package/build/src/tools/composite/content.js.map +1 -0
- package/build/src/tools/composite/databases.d.ts +33 -0
- package/build/src/tools/composite/databases.d.ts.map +1 -0
- package/build/src/tools/composite/databases.js +419 -0
- package/build/src/tools/composite/databases.js.map +1 -0
- package/build/src/tools/composite/pages.d.ts +24 -0
- package/build/src/tools/composite/pages.d.ts.map +1 -0
- package/build/src/tools/composite/pages.js +316 -0
- package/build/src/tools/composite/pages.js.map +1 -0
- package/build/src/tools/composite/search.d.ts +23 -0
- package/build/src/tools/composite/search.d.ts.map +1 -0
- package/build/src/tools/composite/search.js +94 -0
- package/build/src/tools/composite/search.js.map +1 -0
- package/build/src/tools/composite/users.d.ts +15 -0
- package/build/src/tools/composite/users.d.ts.map +1 -0
- package/build/src/tools/composite/users.js +93 -0
- package/build/src/tools/composite/users.js.map +1 -0
- package/build/src/tools/composite/workspace.d.ts +25 -0
- package/build/src/tools/composite/workspace.d.ts.map +1 -0
- package/build/src/tools/composite/workspace.js +72 -0
- package/build/src/tools/composite/workspace.js.map +1 -0
- package/build/src/tools/helpers/errors.d.ts +43 -0
- package/build/src/tools/helpers/errors.d.ts.map +1 -0
- package/build/src/tools/helpers/errors.js +162 -0
- package/build/src/tools/helpers/errors.js.map +1 -0
- package/build/src/tools/helpers/markdown.d.ts +45 -0
- package/build/src/tools/helpers/markdown.d.ts.map +1 -0
- package/build/src/tools/helpers/markdown.js +320 -0
- package/build/src/tools/helpers/markdown.js.map +1 -0
- package/build/src/tools/helpers/pagination.d.ts +42 -0
- package/build/src/tools/helpers/pagination.d.ts.map +1 -0
- package/build/src/tools/helpers/pagination.js +72 -0
- package/build/src/tools/helpers/pagination.js.map +1 -0
- package/build/src/tools/helpers/properties.d.ts +10 -0
- package/build/src/tools/helpers/properties.d.ts.map +1 -0
- package/build/src/tools/helpers/properties.js +57 -0
- package/build/src/tools/helpers/properties.js.map +1 -0
- package/build/src/tools/helpers/richtext.d.ts +85 -0
- package/build/src/tools/helpers/richtext.d.ts.map +1 -0
- package/build/src/tools/helpers/richtext.js +146 -0
- package/build/src/tools/helpers/richtext.js.map +1 -0
- package/build/src/tools/registry.d.ts +10 -0
- package/build/src/tools/registry.d.ts.map +1 -0
- package/build/src/tools/registry.js +342 -0
- package/build/src/tools/registry.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Mega Tool
|
|
3
|
+
* Workspace exploration and info
|
|
4
|
+
*/
|
|
5
|
+
import { NotionMCPError, withErrorHandling } from '../helpers/errors.js';
|
|
6
|
+
import { autoPaginate } from '../helpers/pagination.js';
|
|
7
|
+
/**
|
|
8
|
+
* Unified workspace tool
|
|
9
|
+
* Maps to: GET /v1/users/me and POST /v1/search
|
|
10
|
+
*/
|
|
11
|
+
export async function workspace(notion, input) {
|
|
12
|
+
return withErrorHandling(async () => {
|
|
13
|
+
switch (input.action) {
|
|
14
|
+
case 'info': {
|
|
15
|
+
const botUser = await notion.users.retrieve({ user_id: 'me' });
|
|
16
|
+
return {
|
|
17
|
+
action: 'info',
|
|
18
|
+
bot: {
|
|
19
|
+
id: botUser.id,
|
|
20
|
+
name: botUser.name || 'Bot',
|
|
21
|
+
type: botUser.type,
|
|
22
|
+
owner: botUser.bot?.owner
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
case 'search': {
|
|
27
|
+
if (!input.query) {
|
|
28
|
+
throw new NotionMCPError('query required for search action', 'VALIDATION_ERROR', 'Provide search query');
|
|
29
|
+
}
|
|
30
|
+
const searchParams = {
|
|
31
|
+
query: input.query
|
|
32
|
+
};
|
|
33
|
+
if (input.filter?.object) {
|
|
34
|
+
searchParams.filter = {
|
|
35
|
+
value: input.filter.object,
|
|
36
|
+
property: 'object'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (input.sort) {
|
|
40
|
+
searchParams.sort = {
|
|
41
|
+
direction: input.sort.direction || 'descending',
|
|
42
|
+
timestamp: input.sort.timestamp || 'last_edited_time'
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Fetch results with pagination
|
|
46
|
+
const allResults = await autoPaginate((cursor) => notion.search({
|
|
47
|
+
...searchParams,
|
|
48
|
+
start_cursor: cursor,
|
|
49
|
+
page_size: 100
|
|
50
|
+
}));
|
|
51
|
+
const results = input.limit ? allResults.slice(0, input.limit) : allResults;
|
|
52
|
+
return {
|
|
53
|
+
action: 'search',
|
|
54
|
+
query: input.query,
|
|
55
|
+
total: results.length,
|
|
56
|
+
results: results.map((item) => ({
|
|
57
|
+
id: item.id,
|
|
58
|
+
object: item.object,
|
|
59
|
+
title: item.object === 'page'
|
|
60
|
+
? (item.properties?.title?.title?.[0]?.plain_text || item.properties?.Name?.title?.[0]?.plain_text || 'Untitled')
|
|
61
|
+
: (item.title?.[0]?.plain_text || 'Untitled'),
|
|
62
|
+
url: item.url,
|
|
63
|
+
last_edited_time: item.last_edited_time
|
|
64
|
+
}))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
throw new NotionMCPError(`Unknown action: ${input.action}`, 'VALIDATION_ERROR', 'Supported actions: info, search');
|
|
69
|
+
}
|
|
70
|
+
})();
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../../src/tools/composite/workspace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
* AI-friendly error messages and suggestions
|
|
4
|
+
*/
|
|
5
|
+
export declare class NotionMCPError extends Error {
|
|
6
|
+
code: string;
|
|
7
|
+
suggestion?: string | undefined;
|
|
8
|
+
details?: any | undefined;
|
|
9
|
+
constructor(message: string, code: string, suggestion?: string | undefined, details?: any | undefined);
|
|
10
|
+
toJSON(): {
|
|
11
|
+
error: string;
|
|
12
|
+
code: string;
|
|
13
|
+
message: string;
|
|
14
|
+
suggestion: string | undefined;
|
|
15
|
+
details: any;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Enhance Notion API error with helpful context
|
|
20
|
+
*/
|
|
21
|
+
export declare function enhanceError(error: any): NotionMCPError;
|
|
22
|
+
/**
|
|
23
|
+
* Create AI-readable error message
|
|
24
|
+
*/
|
|
25
|
+
export declare function aiReadableMessage(error: NotionMCPError): string;
|
|
26
|
+
/**
|
|
27
|
+
* Suggest fixes based on error
|
|
28
|
+
*/
|
|
29
|
+
export declare function suggestFixes(error: NotionMCPError): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Wrap async function with error handling
|
|
32
|
+
*/
|
|
33
|
+
export declare function withErrorHandling<T extends (...args: any[]) => Promise<any>>(fn: T): (...args: Parameters<T>) => Promise<ReturnType<T>>;
|
|
34
|
+
/**
|
|
35
|
+
* Retry with exponential backoff
|
|
36
|
+
*/
|
|
37
|
+
export declare function retryWithBackoff<T>(fn: () => Promise<T>, options?: {
|
|
38
|
+
maxRetries?: number;
|
|
39
|
+
initialDelay?: number;
|
|
40
|
+
maxDelay?: number;
|
|
41
|
+
backoffMultiplier?: number;
|
|
42
|
+
}): Promise<T>;
|
|
43
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,cAAe,SAAQ,KAAK;IAG9B,IAAI,EAAE,MAAM;IACZ,UAAU,CAAC,EAAE,MAAM;IACnB,OAAO,CAAC,EAAE,GAAG;gBAHpB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,OAAO,CAAC,EAAE,GAAG,YAAA;IAMtB,MAAM;;;;;;;CASP;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,cAAc,CAsBvD;AA6ED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAY/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,CAyC5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAC1E,EAAE,EAAE,CAAC,GACJ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAQpD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CACtB,GACL,OAAO,CAAC,CAAC,CAAC,CAkCZ"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
* AI-friendly error messages and suggestions
|
|
4
|
+
*/
|
|
5
|
+
export class NotionMCPError extends Error {
|
|
6
|
+
constructor(message, code, suggestion, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.suggestion = suggestion;
|
|
10
|
+
this.details = details;
|
|
11
|
+
this.name = 'NotionMCPError';
|
|
12
|
+
}
|
|
13
|
+
toJSON() {
|
|
14
|
+
return {
|
|
15
|
+
error: this.name,
|
|
16
|
+
code: this.code,
|
|
17
|
+
message: this.message,
|
|
18
|
+
suggestion: this.suggestion,
|
|
19
|
+
details: this.details
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Enhance Notion API error with helpful context
|
|
25
|
+
*/
|
|
26
|
+
export function enhanceError(error) {
|
|
27
|
+
// Notion API error
|
|
28
|
+
if (error.code) {
|
|
29
|
+
return handleNotionError(error);
|
|
30
|
+
}
|
|
31
|
+
// Network error
|
|
32
|
+
if (error.message?.includes('ECONNREFUSED') || error.message?.includes('ENOTFOUND')) {
|
|
33
|
+
return new NotionMCPError('Cannot connect to Notion API', 'NETWORK_ERROR', 'Check your internet connection and try again');
|
|
34
|
+
}
|
|
35
|
+
// Generic error
|
|
36
|
+
return new NotionMCPError(error.message || 'Unknown error occurred', 'UNKNOWN_ERROR', 'Please check your request and try again', error);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Handle specific Notion API errors
|
|
40
|
+
*/
|
|
41
|
+
function handleNotionError(error) {
|
|
42
|
+
const code = error.code;
|
|
43
|
+
const message = error.message || 'Unknown Notion API error';
|
|
44
|
+
// Log full error for debugging
|
|
45
|
+
console.error('Notion API Error:', JSON.stringify({
|
|
46
|
+
code,
|
|
47
|
+
message,
|
|
48
|
+
body: error.body,
|
|
49
|
+
status: error.status
|
|
50
|
+
}, null, 2));
|
|
51
|
+
switch (code) {
|
|
52
|
+
case 'unauthorized':
|
|
53
|
+
return new NotionMCPError('Invalid or missing Notion API token', 'UNAUTHORIZED', 'Set NOTION_TOKEN environment variable with a valid integration token from https://www.notion.so/my-integrations');
|
|
54
|
+
case 'restricted_resource':
|
|
55
|
+
return new NotionMCPError('Integration does not have access to this resource', 'RESTRICTED_RESOURCE', 'Share the page/database with your integration in Notion settings');
|
|
56
|
+
case 'object_not_found':
|
|
57
|
+
return new NotionMCPError('Page or database not found', 'NOT_FOUND', 'Check that the ID is correct and the resource exists');
|
|
58
|
+
case 'validation_error':
|
|
59
|
+
return new NotionMCPError('Invalid request parameters', 'VALIDATION_ERROR', 'Check the API documentation for valid parameter formats', error.details);
|
|
60
|
+
case 'rate_limited':
|
|
61
|
+
return new NotionMCPError('Too many requests to Notion API', 'RATE_LIMITED', 'Wait a few seconds and try again. Consider batching operations.');
|
|
62
|
+
case 'conflict_error':
|
|
63
|
+
return new NotionMCPError('Conflict with existing data', 'CONFLICT', 'The resource may have been modified. Refresh and try again.');
|
|
64
|
+
case 'service_unavailable':
|
|
65
|
+
return new NotionMCPError('Notion API is temporarily unavailable', 'SERVICE_UNAVAILABLE', 'Wait a moment and try again. Check https://status.notion.so for updates.');
|
|
66
|
+
default:
|
|
67
|
+
return new NotionMCPError(message, code.toUpperCase(), 'Check the Notion API documentation for this error code');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create AI-readable error message
|
|
72
|
+
*/
|
|
73
|
+
export function aiReadableMessage(error) {
|
|
74
|
+
let message = `❌ ${error.message}`;
|
|
75
|
+
if (error.suggestion) {
|
|
76
|
+
message += `\n\n💡 Suggestion: ${error.suggestion}`;
|
|
77
|
+
}
|
|
78
|
+
if (error.details) {
|
|
79
|
+
message += `\n\n📋 Details: ${JSON.stringify(error.details, null, 2)}`;
|
|
80
|
+
}
|
|
81
|
+
return message;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Suggest fixes based on error
|
|
85
|
+
*/
|
|
86
|
+
export function suggestFixes(error) {
|
|
87
|
+
const suggestions = [];
|
|
88
|
+
switch (error.code) {
|
|
89
|
+
case 'UNAUTHORIZED':
|
|
90
|
+
suggestions.push('Check that NOTION_TOKEN is set in your environment');
|
|
91
|
+
suggestions.push('Verify token at https://www.notion.so/my-integrations');
|
|
92
|
+
suggestions.push('Create a new integration token if needed');
|
|
93
|
+
break;
|
|
94
|
+
case 'RESTRICTED_RESOURCE':
|
|
95
|
+
suggestions.push('Open the page/database in Notion');
|
|
96
|
+
suggestions.push('Click "..." menu → Add connections → Select your integration');
|
|
97
|
+
suggestions.push('Grant access to parent pages if needed');
|
|
98
|
+
break;
|
|
99
|
+
case 'NOT_FOUND':
|
|
100
|
+
suggestions.push('Verify the page/database ID is correct');
|
|
101
|
+
suggestions.push('Check that the resource was not deleted');
|
|
102
|
+
suggestions.push('Ensure you have access permissions');
|
|
103
|
+
break;
|
|
104
|
+
case 'VALIDATION_ERROR':
|
|
105
|
+
suggestions.push('Check parameter types and formats');
|
|
106
|
+
suggestions.push('Review required vs optional parameters');
|
|
107
|
+
suggestions.push('Verify property names match database schema');
|
|
108
|
+
break;
|
|
109
|
+
case 'RATE_LIMITED':
|
|
110
|
+
suggestions.push('Reduce request frequency');
|
|
111
|
+
suggestions.push('Implement exponential backoff retry logic');
|
|
112
|
+
suggestions.push('Batch multiple operations together');
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
suggestions.push('Check Notion API status at https://status.notion.so');
|
|
116
|
+
suggestions.push('Review request parameters');
|
|
117
|
+
suggestions.push('Try again in a few moments');
|
|
118
|
+
}
|
|
119
|
+
return suggestions;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Wrap async function with error handling
|
|
123
|
+
*/
|
|
124
|
+
export function withErrorHandling(fn) {
|
|
125
|
+
return async (...args) => {
|
|
126
|
+
try {
|
|
127
|
+
return await fn(...args);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
throw enhanceError(error);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Retry with exponential backoff
|
|
136
|
+
*/
|
|
137
|
+
export async function retryWithBackoff(fn, options = {}) {
|
|
138
|
+
const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000, backoffMultiplier = 2 } = options;
|
|
139
|
+
let lastError;
|
|
140
|
+
let delay = initialDelay;
|
|
141
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
142
|
+
try {
|
|
143
|
+
return await fn();
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
lastError = error;
|
|
147
|
+
// Don't retry on certain errors
|
|
148
|
+
if (error.code === 'UNAUTHORIZED' || error.code === 'NOT_FOUND') {
|
|
149
|
+
throw enhanceError(error);
|
|
150
|
+
}
|
|
151
|
+
// Last attempt
|
|
152
|
+
if (attempt === maxRetries) {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
// Wait with exponential backoff
|
|
156
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
157
|
+
delay = Math.min(delay * backoffMultiplier, maxDelay);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw enhanceError(lastError);
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown to Notion Blocks Converter
|
|
3
|
+
* Converts markdown text to Notion block format
|
|
4
|
+
*/
|
|
5
|
+
export interface NotionBlock {
|
|
6
|
+
object: 'block';
|
|
7
|
+
type: string;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
export interface RichText {
|
|
11
|
+
type: 'text';
|
|
12
|
+
text: {
|
|
13
|
+
content: string;
|
|
14
|
+
link?: {
|
|
15
|
+
url: string;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
annotations: {
|
|
19
|
+
bold: boolean;
|
|
20
|
+
italic: boolean;
|
|
21
|
+
strikethrough: boolean;
|
|
22
|
+
underline: boolean;
|
|
23
|
+
code: boolean;
|
|
24
|
+
color: string;
|
|
25
|
+
};
|
|
26
|
+
plain_text?: string;
|
|
27
|
+
href?: string | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Convert markdown string to Notion blocks
|
|
31
|
+
*/
|
|
32
|
+
export declare function markdownToBlocks(markdown: string): NotionBlock[];
|
|
33
|
+
/**
|
|
34
|
+
* Convert Notion blocks to markdown
|
|
35
|
+
*/
|
|
36
|
+
export declare function blocksToMarkdown(blocks: NotionBlock[]): string;
|
|
37
|
+
/**
|
|
38
|
+
* Parse inline markdown formatting to rich text
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseRichText(text: string): RichText[];
|
|
41
|
+
/**
|
|
42
|
+
* Extract plain text from rich text
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractPlainText(richText: RichText[]): string;
|
|
45
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/markdown.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;KAC9B,CAAA;IACD,WAAW,EAAE;QACX,IAAI,EAAE,OAAO,CAAA;QACb,MAAM,EAAE,OAAO,CAAA;QACf,aAAa,EAAE,OAAO,CAAA;QACtB,SAAS,EAAE,OAAO,CAAA;QAClB,IAAI,EAAE,OAAO,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;IACD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAwEhE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAyC9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA0FtD;AAuBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAE7D"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown to Notion Blocks Converter
|
|
3
|
+
* Converts markdown text to Notion block format
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Convert markdown string to Notion blocks
|
|
7
|
+
*/
|
|
8
|
+
export function markdownToBlocks(markdown) {
|
|
9
|
+
const lines = markdown.split('\n');
|
|
10
|
+
const blocks = [];
|
|
11
|
+
let currentList = [];
|
|
12
|
+
let currentListType = null;
|
|
13
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14
|
+
const line = lines[i];
|
|
15
|
+
// Flush list if we're not in a list anymore
|
|
16
|
+
if (currentListType && !isListItem(line)) {
|
|
17
|
+
blocks.push(...currentList);
|
|
18
|
+
currentList = [];
|
|
19
|
+
currentListType = null;
|
|
20
|
+
}
|
|
21
|
+
// Skip empty lines
|
|
22
|
+
if (!line.trim()) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Heading
|
|
26
|
+
if (line.startsWith('# ')) {
|
|
27
|
+
blocks.push(createHeading(1, line.slice(2)));
|
|
28
|
+
}
|
|
29
|
+
else if (line.startsWith('## ')) {
|
|
30
|
+
blocks.push(createHeading(2, line.slice(3)));
|
|
31
|
+
}
|
|
32
|
+
else if (line.startsWith('### ')) {
|
|
33
|
+
blocks.push(createHeading(3, line.slice(4)));
|
|
34
|
+
}
|
|
35
|
+
// Code block
|
|
36
|
+
else if (line.startsWith('```')) {
|
|
37
|
+
const language = line.slice(3).trim();
|
|
38
|
+
const codeLines = [];
|
|
39
|
+
i++;
|
|
40
|
+
while (i < lines.length && !lines[i].startsWith('```')) {
|
|
41
|
+
codeLines.push(lines[i]);
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
blocks.push(createCodeBlock(codeLines.join('\n'), language));
|
|
45
|
+
}
|
|
46
|
+
// Bulleted list
|
|
47
|
+
else if (line.match(/^[\-\*]\s/)) {
|
|
48
|
+
const text = line.slice(2);
|
|
49
|
+
currentListType = 'bulleted';
|
|
50
|
+
currentList.push(createBulletedListItem(text));
|
|
51
|
+
}
|
|
52
|
+
// Numbered list
|
|
53
|
+
else if (line.match(/^\d+\.\s/)) {
|
|
54
|
+
const text = line.replace(/^\d+\.\s/, '');
|
|
55
|
+
currentListType = 'numbered';
|
|
56
|
+
currentList.push(createNumberedListItem(text));
|
|
57
|
+
}
|
|
58
|
+
// Quote
|
|
59
|
+
else if (line.startsWith('> ')) {
|
|
60
|
+
blocks.push(createQuote(line.slice(2)));
|
|
61
|
+
}
|
|
62
|
+
// Divider
|
|
63
|
+
else if (line.match(/^[\-\*]{3,}$/)) {
|
|
64
|
+
blocks.push(createDivider());
|
|
65
|
+
}
|
|
66
|
+
// Regular paragraph
|
|
67
|
+
else {
|
|
68
|
+
blocks.push(createParagraph(line));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Flush remaining list
|
|
72
|
+
if (currentList.length > 0) {
|
|
73
|
+
blocks.push(...currentList);
|
|
74
|
+
}
|
|
75
|
+
return blocks;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Convert Notion blocks to markdown
|
|
79
|
+
*/
|
|
80
|
+
export function blocksToMarkdown(blocks) {
|
|
81
|
+
const lines = [];
|
|
82
|
+
for (const block of blocks) {
|
|
83
|
+
switch (block.type) {
|
|
84
|
+
case 'heading_1':
|
|
85
|
+
lines.push(`# ${richTextToMarkdown(block.heading_1.rich_text)}`);
|
|
86
|
+
break;
|
|
87
|
+
case 'heading_2':
|
|
88
|
+
lines.push(`## ${richTextToMarkdown(block.heading_2.rich_text)}`);
|
|
89
|
+
break;
|
|
90
|
+
case 'heading_3':
|
|
91
|
+
lines.push(`### ${richTextToMarkdown(block.heading_3.rich_text)}`);
|
|
92
|
+
break;
|
|
93
|
+
case 'paragraph':
|
|
94
|
+
lines.push(richTextToMarkdown(block.paragraph.rich_text));
|
|
95
|
+
break;
|
|
96
|
+
case 'bulleted_list_item':
|
|
97
|
+
lines.push(`- ${richTextToMarkdown(block.bulleted_list_item.rich_text)}`);
|
|
98
|
+
break;
|
|
99
|
+
case 'numbered_list_item':
|
|
100
|
+
lines.push(`1. ${richTextToMarkdown(block.numbered_list_item.rich_text)}`);
|
|
101
|
+
break;
|
|
102
|
+
case 'code':
|
|
103
|
+
lines.push('```' + (block.code.language || ''));
|
|
104
|
+
lines.push(richTextToMarkdown(block.code.rich_text));
|
|
105
|
+
lines.push('```');
|
|
106
|
+
break;
|
|
107
|
+
case 'quote':
|
|
108
|
+
lines.push(`> ${richTextToMarkdown(block.quote.rich_text)}`);
|
|
109
|
+
break;
|
|
110
|
+
case 'divider':
|
|
111
|
+
lines.push('---');
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
// Unsupported block type, skip
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Parse inline markdown formatting to rich text
|
|
122
|
+
*/
|
|
123
|
+
export function parseRichText(text) {
|
|
124
|
+
const richText = [];
|
|
125
|
+
let current = '';
|
|
126
|
+
let bold = false;
|
|
127
|
+
let italic = false;
|
|
128
|
+
let code = false;
|
|
129
|
+
let strikethrough = false;
|
|
130
|
+
for (let i = 0; i < text.length; i++) {
|
|
131
|
+
const char = text[i];
|
|
132
|
+
const next = text[i + 1];
|
|
133
|
+
// Link [text](url)
|
|
134
|
+
if (char === '[') {
|
|
135
|
+
const closeBracket = text.indexOf(']', i);
|
|
136
|
+
const openParen = closeBracket !== -1 ? text.indexOf('(', closeBracket) : -1;
|
|
137
|
+
const closeParen = openParen !== -1 ? text.indexOf(')', openParen) : -1;
|
|
138
|
+
if (closeBracket !== -1 && openParen === closeBracket + 1 && closeParen !== -1) {
|
|
139
|
+
if (current) {
|
|
140
|
+
richText.push(createRichText(current, { bold, italic, code, strikethrough }));
|
|
141
|
+
current = '';
|
|
142
|
+
}
|
|
143
|
+
const linkText = text.slice(i + 1, closeBracket);
|
|
144
|
+
const linkUrl = text.slice(openParen + 1, closeParen);
|
|
145
|
+
richText.push({
|
|
146
|
+
type: 'text',
|
|
147
|
+
text: { content: linkText, link: { url: linkUrl } },
|
|
148
|
+
annotations: {
|
|
149
|
+
bold, italic, strikethrough,
|
|
150
|
+
underline: false,
|
|
151
|
+
code,
|
|
152
|
+
color: 'default'
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
i = closeParen;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Bold **text**
|
|
160
|
+
if (char === '*' && next === '*') {
|
|
161
|
+
if (current) {
|
|
162
|
+
richText.push(createRichText(current, { bold, italic, code, strikethrough }));
|
|
163
|
+
current = '';
|
|
164
|
+
}
|
|
165
|
+
bold = !bold;
|
|
166
|
+
i++; // Skip next *
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// Italic *text*
|
|
170
|
+
else if (char === '*' && next !== '*') {
|
|
171
|
+
if (current) {
|
|
172
|
+
richText.push(createRichText(current, { bold, italic, code, strikethrough }));
|
|
173
|
+
current = '';
|
|
174
|
+
}
|
|
175
|
+
italic = !italic;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
// Code `text`
|
|
179
|
+
else if (char === '`') {
|
|
180
|
+
if (current) {
|
|
181
|
+
richText.push(createRichText(current, { bold, italic, code, strikethrough }));
|
|
182
|
+
current = '';
|
|
183
|
+
}
|
|
184
|
+
code = !code;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
// Strikethrough ~~text~~
|
|
188
|
+
else if (char === '~' && next === '~') {
|
|
189
|
+
if (current) {
|
|
190
|
+
richText.push(createRichText(current, { bold, italic, code, strikethrough }));
|
|
191
|
+
current = '';
|
|
192
|
+
}
|
|
193
|
+
strikethrough = !strikethrough;
|
|
194
|
+
i++; // Skip next ~
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
current += char;
|
|
198
|
+
}
|
|
199
|
+
if (current) {
|
|
200
|
+
richText.push(createRichText(current, { bold, italic, code, strikethrough }));
|
|
201
|
+
}
|
|
202
|
+
return richText.length > 0 ? richText : [createRichText(text)];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Convert rich text array to plain markdown
|
|
206
|
+
*/
|
|
207
|
+
function richTextToMarkdown(richText) {
|
|
208
|
+
if (!richText || !Array.isArray(richText))
|
|
209
|
+
return '';
|
|
210
|
+
return richText.map(rt => {
|
|
211
|
+
if (!rt || !rt.text)
|
|
212
|
+
return '';
|
|
213
|
+
let text = rt.text.content || '';
|
|
214
|
+
const annotations = rt.annotations || {};
|
|
215
|
+
if (annotations.bold)
|
|
216
|
+
text = `**${text}**`;
|
|
217
|
+
if (annotations.italic)
|
|
218
|
+
text = `*${text}*`;
|
|
219
|
+
if (annotations.code)
|
|
220
|
+
text = `\`${text}\``;
|
|
221
|
+
if (annotations.strikethrough)
|
|
222
|
+
text = `~~${text}~~`;
|
|
223
|
+
if (rt.text.link)
|
|
224
|
+
text = `[${text}](${rt.text.link.url})`;
|
|
225
|
+
return text;
|
|
226
|
+
}).join('');
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Extract plain text from rich text
|
|
230
|
+
*/
|
|
231
|
+
export function extractPlainText(richText) {
|
|
232
|
+
return richText.map(rt => rt.text.content).join('');
|
|
233
|
+
}
|
|
234
|
+
// Helper creators
|
|
235
|
+
function createRichText(content, annotations = {}) {
|
|
236
|
+
return {
|
|
237
|
+
type: 'text',
|
|
238
|
+
text: { content, link: null },
|
|
239
|
+
annotations: {
|
|
240
|
+
bold: annotations.bold || false,
|
|
241
|
+
italic: annotations.italic || false,
|
|
242
|
+
strikethrough: annotations.strikethrough || false,
|
|
243
|
+
underline: false,
|
|
244
|
+
code: annotations.code || false,
|
|
245
|
+
color: 'default'
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function createHeading(level, text) {
|
|
250
|
+
const type = `heading_${level}`;
|
|
251
|
+
return {
|
|
252
|
+
object: 'block',
|
|
253
|
+
type,
|
|
254
|
+
[type]: {
|
|
255
|
+
rich_text: parseRichText(text),
|
|
256
|
+
color: 'default'
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function createParagraph(text) {
|
|
261
|
+
return {
|
|
262
|
+
object: 'block',
|
|
263
|
+
type: 'paragraph',
|
|
264
|
+
paragraph: {
|
|
265
|
+
rich_text: parseRichText(text),
|
|
266
|
+
color: 'default'
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function createBulletedListItem(text) {
|
|
271
|
+
return {
|
|
272
|
+
object: 'block',
|
|
273
|
+
type: 'bulleted_list_item',
|
|
274
|
+
bulleted_list_item: {
|
|
275
|
+
rich_text: parseRichText(text),
|
|
276
|
+
color: 'default'
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function createNumberedListItem(text) {
|
|
281
|
+
return {
|
|
282
|
+
object: 'block',
|
|
283
|
+
type: 'numbered_list_item',
|
|
284
|
+
numbered_list_item: {
|
|
285
|
+
rich_text: parseRichText(text),
|
|
286
|
+
color: 'default'
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function createCodeBlock(code, language) {
|
|
291
|
+
return {
|
|
292
|
+
object: 'block',
|
|
293
|
+
type: 'code',
|
|
294
|
+
code: {
|
|
295
|
+
rich_text: [createRichText(code)],
|
|
296
|
+
language: language || 'plain text'
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function createQuote(text) {
|
|
301
|
+
return {
|
|
302
|
+
object: 'block',
|
|
303
|
+
type: 'quote',
|
|
304
|
+
quote: {
|
|
305
|
+
rich_text: parseRichText(text),
|
|
306
|
+
color: 'default'
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function createDivider() {
|
|
311
|
+
return {
|
|
312
|
+
object: 'block',
|
|
313
|
+
type: 'divider',
|
|
314
|
+
divider: {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function isListItem(line) {
|
|
318
|
+
return line.match(/^[\-\*]\s/) !== null || line.match(/^\d+\.\s/) !== null;
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=markdown.js.map
|