(function(){ 'use strict'; if (window.__mnoteLocalFolderEventBus) { return; } var connections = new Map(); var sidebarRefreshQueues = new Map(); var lastSource = ''; var lastReason = ''; function root() { return document.documentElement; } function setDiagnostics(source, reason) { lastSource = source || lastSource || ''; lastReason = reason || lastReason || ''; root().setAttribute('data-mnote-local-folder-event-bus', 'ready'); root().setAttribute('data-mnote-local-folder-event-bus-connections', String(connections.size)); root().setAttribute('data-mnote-local-folder-event-bus-last-source', lastSource); root().setAttribute('data-mnote-local-folder-event-bus-last-reason', lastReason); } function normalizeRootUri(rootUri) { return String(rootUri || '').trim(); } function normalizeWorkspaceId(workspaceId) { return String(workspaceId || 'default').trim() || 'default'; } function emit(name, detail) { window.dispatchEvent(new CustomEvent(name, { detail: detail || {} })); } function parseEventPayload(event) { try { return JSON.parse((event && event.data) || '{}') || {}; } catch (_) { return {}; } } function revisionOf(payload, event) { return payload.revision || payload.cursor || (event && event.lastEventId) || null; } function arrayOf(value) { if (!value) return []; if (Array.isArray(value)) return value.filter(Boolean).map(String); return [String(value)].filter(Boolean); } function pathArrayOf(value) { if (!value) return []; var list = Array.isArray(value) ? value : [value]; return list.map(function(item) { if (typeof item === 'string') return item.trim(); if (!item || typeof item !== 'object') return ''; return String(item.relativePath || item.relative_path || item.path || item.sourcePath || item.source_path || '').trim(); }).filter(Boolean); } function collectChangedPaths(payload) { var paths = [] .concat(pathArrayOf(payload.changedPaths)) .concat(pathArrayOf(payload.changed_paths)) .concat(pathArrayOf(payload.paths)); var events = Array.isArray(payload.events) ? payload.events : []; events.forEach(function(item) { if (!item || typeof item !== 'object') return; paths = paths .concat(arrayOf(item.path)) .concat(arrayOf(item.relativePath)) .concat(arrayOf(item.relative_path)) .concat(arrayOf(item.sourcePath)) .concat(arrayOf(item.source_path)); }); return Array.from(new Set(paths)); } function collectChangedPathItems(payload, reason) { var raw = [] .concat(Array.isArray(payload.changedPaths) ? payload.changedPaths : []) .concat(Array.isArray(payload.changed_paths) ? payload.changed_paths : []) .concat(Array.isArray(payload.paths) ? payload.paths : []) .concat(Array.isArray(payload.events) ? payload.events : []); var seen = new Set(); var items = []; raw.forEach(function(item) { var relativePath = typeof item === 'string' ? item.trim() : String(item && (item.relativePath || item.relative_path || item.path || item.sourcePath || item.source_path || '') || '').trim(); if (!relativePath || seen.has(relativePath)) return; seen.add(relativePath); items.push({ relativePath: relativePath, reason: reason || 'event-bus', kind: typeof item === 'object' && item ? String(item.kind || '') : '', eventKind: typeof item === 'object' && item ? String(item.eventKind || item.event_kind || item.changeType || item.change_type || '') : '' }); }); if (items.length) return items; return pathItems(collectChangedPaths(payload), reason); } function collectAffectedParents(payload) { var parents = [] .concat(pathArrayOf(payload.affectedParents)) .concat(pathArrayOf(payload.affected_parents)) .concat(pathArrayOf(payload.parentRelativePaths)) .concat(pathArrayOf(payload.parent_relative_paths)); return Array.from(new Set(parents)); } function pathItems(paths, reason) { return (paths || []).map(function(relativePath) { return { relativePath: String(relativePath || '').trim(), reason: reason || 'event-bus' }; }).filter(function(item) { return item.relativePath || item.relativePath === ''; }); } function queueSidebarRefresh(detail) { var key = String(detail.rootUri || '').trim(); if (!key) return; var queue = sidebarRefreshQueues.get(key); if (!queue) { queue = { rootUri: detail.rootUri, workspaceId: detail.workspaceId, revisions: new Set(), reasons: new Set(), changedPaths: new Map(), affectedParents: new Set(), resyncRequired: false, timer: 0 }; sidebarRefreshQueues.set(key, queue); } if (detail.revision) queue.revisions.add(String(detail.revision)); if (detail.reason) queue.reasons.add(String(detail.reason)); (detail.changedPaths || []).forEach(function(item) { var path = typeof item === 'string' ? item : String(item && (item.relativePath || item.relative_path || '') || ''); if (!path && path !== '') return; var existing = queue.changedPaths.get(path) || { relativePath: path }; queue.changedPaths.set(path, { relativePath: path, reason: String((item && item.reason) || existing.reason || detail.reason || 'event-bus-orchestrated'), kind: String((item && item.kind) || existing.kind || ''), eventKind: String((item && item.eventKind) || (item && item.event_kind) || existing.eventKind || '') }); }); (detail.affectedParents || []).forEach(function(item) { var parent = typeof item === 'string' ? item : String(item && (item.relativePath || item.relative_path || '') || ''); if (parent || parent === '') queue.affectedParents.add(parent); }); queue.resyncRequired = queue.resyncRequired || detail.resyncRequired === true; if (!queue.timer) { queue.timer = window.setTimeout(function() { flushSidebarRefresh(key); }, 0); } root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-pending', String(queue.affectedParents.size)); } function flushSidebarRefresh(key) { var queue = sidebarRefreshQueues.get(key); if (!queue) return; sidebarRefreshQueues.delete(key); var reasons = Array.from(queue.reasons); var detail = { schema: 'mnote.local_folder.event_bus.sidebar_refresh.v1', source: 'event_bus_orchestrator', reason: reasons.join(',') || 'watch_batch', rootUri: queue.rootUri, workspaceId: queue.workspaceId, revision: Array.from(queue.revisions).pop() || null, reasons: reasons, changedPaths: Array.from(queue.changedPaths.values()), affectedParents: pathItems(Array.from(queue.affectedParents), 'event-bus-orchestrated'), resyncRequired: queue.resyncRequired, viaEventBus: true }; root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-applied', detail.reason); root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-parents', String(detail.affectedParents.length)); emit('mnote:local-folder:sidebar-refresh-requested', detail); } function dispatchWatchBatch(entry, event, payload, meta) { var revision = revisionOf(payload, event); var changedPaths = collectChangedPaths(payload); var affectedParents = collectAffectedParents(payload); var source = String((meta && meta.source) || payload.source || 'watcher_sse').trim() || 'watcher_sse'; var reason = String((meta && meta.reason) || payload.reason || 'watch_batch').trim() || 'watch_batch'; var resyncRequired = payload.fallbackResync === true || payload.requiresResync === true || payload.resyncRequired === true || payload.resync_required === true; var detail = { schema: 'mnote.local_folder.event_bus.watch_batch.v1', source: source, reason: reason, rootUri: entry.rootUri, workspaceId: entry.workspaceId, revision: revision, payload: payload, bootstrap: entry.bootstrap || null, changedPaths: collectChangedPathItems(payload, reason), affectedParents: pathItems(affectedParents, reason), resyncRequired: resyncRequired, viaEventBus: true }; setDiagnostics(source, reason); root().setAttribute('data-mnote-tree-live-revision', String(revision || '')); emit('mnote:local-folder:watch-batch', detail); emit('tree:local-folder-watch-batch', detail); if (affectedParents.length > 0) { emit('mnote:local-folder:filetree-parent-changed', detail); } if (changedPaths.length > 0) { emit('mnote:local-folder:document-changed', detail); emit('mnote:local-folder:resource-changed', detail); emit('mnote:local-folder:knowledge-rag-source-updated', detail); } queueSidebarRefresh(detail); if (resyncRequired) { emit('mnote:local-folder:resync-required', detail); } } function closeEntry(entry) { if (!entry || !entry.source || typeof entry.source.close !== 'function') return; entry.source.close(); } function startLocalFolderWatcher(options) { var rootUri = normalizeRootUri(options && options.rootUri); if (!rootUri || typeof window.EventSource !== 'function') return null; var workspaceId = normalizeWorkspaceId(options && options.workspaceId); var key = rootUri; if (connections.has(key)) { var existing = connections.get(key); if ((!existing.workspaceId || existing.workspaceId === 'default') && workspaceId && workspaceId !== 'default') { existing.workspaceId = workspaceId; if (existing.handle) existing.handle.workspaceId = workspaceId; } setDiagnostics(existing.lastSource || 'watcher_sse', 'reuse_connection'); return existing.handle; } var url = new URL('/api/local-folder/events', window.location.origin); url.searchParams.set('rootUri', rootUri); url.searchParams.set('treeLive', 'true'); var eventSource = new EventSource(url.toString()); var entry = { key: key, rootUri: rootUri, workspaceId: workspaceId, bootstrap: (options && options.bootstrap) || null, source: eventSource, lastSource: 'watcher_sse', close: function() { connections.delete(key); closeEntry(entry); setDiagnostics('watcher_sse', 'closed'); } }; entry.handle = { key: key, rootUri: rootUri, workspaceId: workspaceId, source: eventSource, close: entry.close }; connections.set(key, entry); setDiagnostics('watcher_sse', 'connect'); emit('mnote:local-folder:event-bus-ready', { schema: 'mnote.local_folder.event_bus.ready.v1', source: 'watcher_sse', reason: 'connect', rootUri: rootUri, workspaceId: workspaceId, connections: connections.size }); eventSource.addEventListener('open', function() { setDiagnostics('watcher_sse', 'open'); }); eventSource.addEventListener('watch_batch', function(event) { dispatchWatchBatch(entry, event, parseEventPayload(event), { source: 'watcher_sse', reason: 'watch_batch' }); }); eventSource.addEventListener('tree_error', function(event) { var payload = parseEventPayload(event); setDiagnostics('watcher_sse', 'tree_error'); emit('tree:error', { payload: payload, bootstrap: entry.bootstrap || null, viaEventBus: true }); }); eventSource.onerror = function() { setDiagnostics('watcher_sse', 'error'); }; return entry.handle; } function emitSyntheticWatchBatch(detail) { var payload = detail && detail.payload ? detail.payload : (detail || {}); var rootUri = normalizeRootUri(detail && detail.rootUri); var workspaceId = normalizeWorkspaceId(detail && detail.workspaceId); var entry = { rootUri: rootUri, workspaceId: workspaceId, bootstrap: (detail && detail.bootstrap) || null }; dispatchWatchBatch(entry, { lastEventId: detail && detail.revision }, payload, { source: (detail && detail.source) || 'synthetic', reason: (detail && detail.reason) || payload.source || 'synthetic_watch_batch' }); } function closeAll() { Array.from(connections.values()).forEach(function(entry) { closeEntry(entry); }); connections.clear(); setDiagnostics('watcher_sse', 'closed'); } window.__mnoteLocalFolderEventBus = { startLocalFolderWatcher: startLocalFolderWatcher, emitSyntheticWatchBatch: emitSyntheticWatchBatch, closeAll: closeAll, flushSidebarRefresh: function(rootUri) { flushSidebarRefresh(normalizeRootUri(rootUri)); }, connectionCount: function() { return connections.size; }, diagnostics: function() { return { connections: connections.size, lastSource: lastSource, lastReason: lastReason }; } }; setDiagnostics('', 'ready'); })();