This commit is contained in:
2025-10-26 10:52:27 +04:00
parent d48e25ce90
commit 8750e0af39
10 changed files with 482 additions and 193 deletions

View File

@@ -6,53 +6,45 @@
class MacroProcessor {
constructor(webdavClient) {
this.webdavClient = webdavClient;
this.plugins = new Map();
this.includeStack = []; // Track includes to detect cycles
this.macroRegistry = new MacroRegistry();
this.includeStack = [];
this.faqItems = [];
this.registerDefaultPlugins();
console.log('[MacroProcessor] Initialized');
}
/**
* Register a macro plugin
* Plugin must implement: { canHandle(actor, method), process(macro, webdavClient) }
*/
registerPlugin(actor, method, plugin) {
const key = `${actor}.${method}`;
this.plugins.set(key, plugin);
}
/**
* Process all macros in content
* Returns { success: boolean, content: string, errors: [] }
* Process all macros in markdown
*/
async processMacros(content) {
console.log('MacroProcessor: Starting macro processing for content:', content);
console.log('[MacroProcessor] Processing content, length:', content.length);
const macros = MacroParser.extractMacros(content);
console.log('MacroProcessor: Extracted macros:', macros);
console.log(`[MacroProcessor] Found ${macros.length} macros`);
const errors = [];
let processedContent = content;
let faqOutput = '';
// Process macros in reverse order to preserve positions
// Process in reverse to preserve positions
for (let i = macros.length - 1; i >= 0; i--) {
const macro = macros[i];
console.log('MacroProcessor: Processing macro:', macro);
console.log(`[MacroProcessor] Processing macro ${i}:`, macro.actor, macro.method);
try {
const result = await this.processMacro(macro);
console.log('MacroProcessor: Macro processing result:', result);
if (result.success) {
// Replace macro with result
console.log(`[MacroProcessor] Macro succeeded, replacing content`);
processedContent =
processedContent.substring(0, macro.start) +
result.content +
processedContent.substring(macro.end);
} else {
errors.push({
macro: macro.fullMatch,
error: result.error
});
console.error(`[MacroProcessor] Macro failed:`, result.error);
errors.push({ macro: macro.fullMatch, error: result.error });
// Replace with error message
const errorMsg = `\n\n⚠️ **Macro Error**: ${result.error}\n\n`;
processedContent =
processedContent.substring(0, macro.start) +
@@ -60,12 +52,10 @@ class MacroProcessor {
processedContent.substring(macro.end);
}
} catch (error) {
errors.push({
macro: macro.fullMatch,
error: error.message
});
console.error(`[MacroProcessor] Macro exception:`, error);
errors.push({ macro: macro.fullMatch, error: error.message });
const errorMsg = `\n\n⚠️ **Macro Error**: ${error.message}\n\n`;
const errorMsg = `\n\n⚠️ **Macro Exception**: ${error.message}\n\n`;
processedContent =
processedContent.substring(0, macro.start) +
errorMsg +
@@ -73,7 +63,17 @@ class MacroProcessor {
}
}
console.log('MacroProcessor: Final processed content:', processedContent);
// Append FAQ if any FAQ macros were used
if (this.faqItems.length > 0) {
faqOutput = '\n\n---\n\n## FAQ\n\n';
faqOutput += this.faqItems
.map((item, idx) => `### ${idx + 1}. ${item.title}\n\n${item.response}`)
.join('\n\n');
processedContent += faqOutput;
}
console.log('[MacroProcessor] Processing complete, errors:', errors.length);
return {
success: errors.length === 0,
content: processedContent,
@@ -85,37 +85,30 @@ class MacroProcessor {
* Process single macro
*/
async processMacro(macro) {
const key = `${macro.actor}.${macro.method}`;
const plugin = this.plugins.get(key);
// Check for circular includes
if (macro.method === 'include') {
const path = macro.params.path || macro.params[''];
if (this.includeStack.includes(path)) {
return {
success: false,
error: `Circular include detected: ${this.includeStack.join(' → ')}${path}`
};
}
}
const plugin = this.macroRegistry.resolve(macro.actor, macro.method);
if (!plugin) {
return {
success: false,
error: `Unknown macro: !!${key}`
error: `Unknown macro: !!${macro.actor}.${macro.method}`
};
}
// Validate macro
const validation = MacroParser.validateMacro(macro);
if (!validation.valid) {
return { success: false, error: validation.error };
// Check for circular includes
if (macro.method === 'include') {
const path = macro.params.path;
if (this.includeStack.includes(path)) {
return {
success: false,
error: `Circular include: ${this.includeStack.join(' → ')}${path}`
};
}
}
// Execute plugin
try {
return await plugin.process(macro, this.webdavClient);
} catch (error) {
console.error('[MacroProcessor] Plugin error:', error);
return {
success: false,
error: `Plugin error: ${error.message}`
@@ -128,34 +121,12 @@ class MacroProcessor {
*/
registerDefaultPlugins() {
// Include plugin
this.registerPlugin('core', 'include', {
process: async (macro, webdavClient) => {
const path = macro.params.path || macro.params[''];
if (!path) {
return {
success: false,
error: 'include macro requires "path" parameter'
};
}
try {
// Add to include stack
this.includeStack.push(path);
const content = await webdavClient.includeFile(path);
// Remove from include stack
this.includeStack.pop();
return { success: true, content };
} catch (error) {
// Remove from include stack on error
this.includeStack.pop();
return {
success: false,
error: `Failed to include "${path}": ${error.message}`
};
}
}
});
this.macroRegistry.register('core', 'include', new IncludePlugin(this));
// FAQ plugin
this.macroRegistry.register('core', 'faq', new FAQPlugin(this));
console.log('[MacroProcessor] Registered default plugins');
}
}