ai-knowledge-hub
Version:
MCP server that provides unified access to organizational knowledge across multiple platforms (local docs, Guru, Notion)
175 lines • 5.7 kB
JavaScript
/**
* High-Level Conversion Utilities
* Main entry points for markdown <-> notion conversions plus helper utilities
*/
import { MarkdownParser } from './markdown-parser.js';
import { markdownASTToNotionBlocks } from './markdown-to-notion.js';
import { notionBlocksToMarkdown } from './notion-to-markdown.js';
import { DEFAULT_CONVERSION_OPTIONS, } from '../types/markdown.js';
/**
* Convert markdown content to Notion blocks
*/
export function markdownToNotion(markdown, options = {}) {
try {
const conversionOptions = {
...DEFAULT_CONVERSION_OPTIONS,
...options,
};
const parser = new MarkdownParser({
extractMetadata: true,
validateSyntax: true,
});
// Parse markdown to AST
const ast = parser.parseToAST(markdown);
// Convert AST to Notion blocks
const result = markdownASTToNotionBlocks(ast, conversionOptions);
return result;
}
catch (error) {
throw new Error(`Markdown to Notion conversion failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Convert Notion blocks to markdown
*/
export function notionToMarkdown(blocks, options = {}) {
try {
const conversionOptions = {
...DEFAULT_CONVERSION_OPTIONS,
...options,
};
const result = notionBlocksToMarkdown(blocks, conversionOptions);
return result;
}
catch (error) {
throw new Error(`Notion to Markdown conversion failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
// ========================================
// UTILITY FUNCTIONS
// ========================================
/**
* Parse markdown and extract title
*/
export function extractTitleFromMarkdown(markdown) {
const parser = new MarkdownParser({
extractMetadata: true,
validateSyntax: true,
});
const doc = parser.parseDocument(markdown, 'temp.md');
return doc.metadata.title ?? null;
}
/**
* Extract page title from Notion page properties
*/
export function extractPageTitle(page) {
const properties = page.properties ?? {};
// Find the property with type "title"
for (const [_propertyName, property] of Object.entries(properties)) {
const typedProperty = property;
if (typedProperty?.type === 'title') {
const titleContent = typedProperty.title?.[0]?.text?.content;
if (titleContent !== null && titleContent !== undefined && titleContent.length > 0) {
return titleContent;
}
}
}
// Fallback to common property names
const titleProperty = (properties.title ?? properties.Title ?? properties.Name);
return titleProperty?.title?.[0]?.text?.content ?? 'Untitled';
}
/**
* Validate markdown content
*/
export function validateMarkdown(content) {
try {
const parser = new MarkdownParser({
extractMetadata: true,
validateSyntax: true,
});
const _document = parser.parseDocument(content, 'temp.md');
const errors = [];
const warnings = [];
// Basic validation
if (!content || content.trim().length === 0) {
errors.push('Content is empty');
}
if (content.length > 100000) { // 100KB limit
warnings.push('Content is very large and may cause performance issues');
}
return {
isValid: errors.length === 0,
errors,
warnings,
};
}
catch (error) {
return {
isValid: false,
errors: [`Parsing failed: ${error instanceof Error ? error.message : String(error)}`],
warnings: [],
};
}
}
/**
* Extract headings from markdown content
*/
export function extractHeadings(markdown) {
const parser = new MarkdownParser({
extractMetadata: true,
validateSyntax: true,
});
const doc = parser.parseDocument(markdown, 'temp.md');
return (doc.metadata.headings ?? []).map(h => ({ ...h, anchor: h.anchor ?? '' }));
}
/**
* Calculate word count from markdown content
*/
export function getWordCount(markdown) {
return markdown
.replace(/[#*`_[\]()]/g, '') // Remove markdown syntax
.split(/\s+/)
.filter(word => word.length > 0).length;
}
/**
* Check if content contains frontmatter
*/
export function hasFrontmatter(markdown) {
return /^---\s*\n[\s\S]*?\n---\s*\n/.test(markdown);
}
/**
* Remove frontmatter from markdown content
*/
export function removeFrontmatter(markdown) {
return markdown.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, '').trim();
}
/**
* Extract frontmatter as object
*/
export function extractFrontmatter(markdown) {
const frontMatterMatch = markdown.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
if (!frontMatterMatch) {
return {};
}
const frontMatter = {};
const yamlContent = frontMatterMatch[1];
if (yamlContent) {
yamlContent.split('\n').forEach(line => {
const match = line.match(/^(\w+):\s*(.+)$/);
if (match) {
const [, key, value] = match;
if (key && value) {
// Handle arrays (simple case)
if (value.startsWith('[') && value.endsWith(']')) {
frontMatter[key] = value.slice(1, -1).split(',').map(s => s.trim().replace(/['"]/g, ''));
}
else {
frontMatter[key] = value.replace(/['"]/g, '');
}
}
}
});
}
return frontMatter;
}
//# sourceMappingURL=converters.js.map