feat(rag): harden post-LightRAG runtime
Retire legacy OCR/media/evidence fallbacks, add local-folder event bus and Page Aggregate guards, and archive completed design checklists. Validation: cargo test -p mnote-web -- --test-threads=1; cargo test --workspace -- --test-threads=1; git diff --check; codegraph sync .; codegraph_status.
This commit is contained in:
@@ -171,6 +171,7 @@ pub fn PageLayout(
|
||||
<script type="module" src={browser_runtime_src("sidebar-shell-runtime.js")}></script>
|
||||
<script type="module" src={browser_runtime_src("sidebar-tree-runtime.js")}></script>
|
||||
<script inner_html={SIDEBAR_TREE_JS.to_string()}></script>
|
||||
<script type="module" src={browser_runtime_src("local-folder-event-bus-runtime.js")}></script>
|
||||
<script type="module" src={browser_runtime_src("tree-live-controller.js")}></script>
|
||||
</aside>
|
||||
<div class="mnote-sidebar-resizer" data-mnote-sidebar-resizer="true" role="separator" aria-orientation="vertical" aria-label="调整侧栏宽度"></div>
|
||||
@@ -254,6 +255,8 @@ mod tests {
|
||||
const FILETREE_RUNTIME_JS: &str = include_str!("../../../browser/filetree-runtime.js");
|
||||
const FILETREE_SELECTION_RUNTIME_JS: &str =
|
||||
include_str!("../../../browser/filetree-selection-runtime.js");
|
||||
const LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS: &str =
|
||||
include_str!("../../../browser/local-folder-event-bus-runtime.js");
|
||||
const TREE_LIVE_CONTROLLER_JS: &str = include_str!("../../../browser/tree-live-controller.js");
|
||||
|
||||
fn js_function_body(source: &str, name: &str) -> String {
|
||||
@@ -337,7 +340,12 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("readFileTreeObjectIdentity"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("objectIdentity: objectIdentity"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("workspaceId: resolveWorkspaceId(fileRow)"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/media/sign?assetId="));
|
||||
assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/media/sign?assetId="));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
|
||||
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("/api/media/sign?assetId="));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
|
||||
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("/api/media/upload"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-media-upload-retired"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("function localFilePathFromAssetId"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/local-folder/files/open"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("local-file:"));
|
||||
@@ -347,7 +355,7 @@ mod tests {
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/auth/whoami"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("buildOnlyOfficeOpenUrl"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("target.searchParams.set('userId'"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("window.open(officeUrl,"));
|
||||
assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("window.open(officeUrl,"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.internal-drop"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.external-drop"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("beginFileTreeInlineRename"));
|
||||
@@ -429,6 +437,29 @@ mod tests {
|
||||
html.contains("/api/mnote-browser-runtime/sidebar-tree-runtime.js?devHot="),
|
||||
"dev:hot 下 sidebar runtime URL 也必须带 cache buster"
|
||||
);
|
||||
assert!(
|
||||
html.contains("/api/mnote-browser-runtime/local-folder-event-bus-runtime.js?devHot="),
|
||||
"dev:hot 下 local-folder event bus URL 必须带 cache buster,避免复用旧连接编排逻辑"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_layout_loads_local_folder_event_bus_before_tree_live_controller() {
|
||||
let html = crate::ssr::render_view(leptos::view! {
|
||||
<super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
|
||||
<main>"正文"</main>
|
||||
</super::PageLayout>
|
||||
});
|
||||
let bus_index = html
|
||||
.find("/api/mnote-browser-runtime/local-folder-event-bus-runtime.js")
|
||||
.expect("local-folder event bus runtime should be loaded");
|
||||
let controller_index = html
|
||||
.find("/api/mnote-browser-runtime/tree-live-controller.js")
|
||||
.expect("tree live controller runtime should be loaded");
|
||||
assert!(
|
||||
bus_index < controller_index,
|
||||
"local-folder event bus 必须先于 tree-live-controller 加载,确保 local-folder watcher 连接由 bus 接管"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -524,6 +555,8 @@ mod tests {
|
||||
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("publicKnowledgeRagDashboardUrl"));
|
||||
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("label.hidden = status === 'idle'"));
|
||||
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-knowledge-rag-input-status-label"));
|
||||
assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("localOcrAutoEnabled"));
|
||||
assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-mnote-local-ocr-auto-retired"));
|
||||
assert!(
|
||||
SIDEBAR_TREE_RUNTIME_JS.contains("[data-mnote-action=\"open-knowledge-rag-settings\"]")
|
||||
);
|
||||
@@ -534,6 +567,11 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pruneKnowledgeRagRegistry"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("setKnowledgeRagSourceFilter"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote:knowledge-rag-source-updated"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("knowledgeRagSourceRelativePath"));
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("supportsKnowledgeRagSource"));
|
||||
assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("runLocalOcr"));
|
||||
assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("mnote:local-ocr-job-updated"));
|
||||
assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-mnote-local-ocr-menu-status"));
|
||||
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("openPageIndexSettingsPopover();"));
|
||||
assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("openLocalOcrSettingsPopover();"));
|
||||
assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-page-settings-tab=\"index\""));
|
||||
@@ -594,6 +632,14 @@ mod tests {
|
||||
.contains("if (currentSourceKind() === 'local_folder') return false;"),
|
||||
"local source 不应进入 page-ai fast-path,后端会把 run 收敛为文件引用 scope"
|
||||
);
|
||||
assert!(
|
||||
!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiEnrichOcrContextRefs"),
|
||||
"Page AI 目标包不应保留已退役 OCR sidecar context enrichment"
|
||||
);
|
||||
assert!(
|
||||
!SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("ocrRootRelativePath"),
|
||||
"Page AI target runtime 不应再注入旧 OCR sidecar 路径"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -786,7 +832,7 @@ mod tests {
|
||||
.contains("var resolvedSidebarPayload = sidebarPayload ? (sidebarPayload.result || sidebarPayload) : null"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var renderedPage = false;"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("void renderPageProjection(resolvedSidebarPayload);"));
|
||||
.contains("renderedPage = await renderPageProjection(resolvedSidebarPayload);"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("refreshFileTreeParent(fileTreeScope)"));
|
||||
assert!(
|
||||
SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-local-folder-watch-applied")
|
||||
@@ -802,6 +848,25 @@ mod tests {
|
||||
TREE_LIVE_CONTROLLER_JS.contains("bootstrap.transport === 'local-folder-events'"),
|
||||
"本地文件夹 bootstrap transport 应直接选择 /api/local-folder/events"
|
||||
);
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("__mnoteLocalFolderEventBus"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("startLocalFolderWatcher"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("connections.has(key)"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("function pathArrayOf"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("function queueSidebarRefresh"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS
|
||||
.contains("mnote:local-folder:sidebar-refresh-requested"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS
|
||||
.contains("data-mnote-local-folder-event-bus-connections"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("tree:local-folder-watch-batch"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("viaEventBus: true"));
|
||||
assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("emitSyntheticWatchBatch"));
|
||||
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("__mnoteLocalFolderEventBus"));
|
||||
assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("synthetic_page_ai_receipt"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteLocalFolderEventBus"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("startLocalFolderWatcher"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("mnote:local-folder:sidebar-refresh-requested"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("event.detail.viaEventBus === true"));
|
||||
assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/local-folder-watch"));
|
||||
assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("fetch(window.location.href, { headers: { accept: 'text/html' } })"));
|
||||
@@ -896,6 +961,8 @@ mod tests {
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function fetchWithTimeout"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadLocalFolderAsset"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadMediaAsset"));
|
||||
assert!(!LOCAL_UPLOAD_RUNTIME_JS.contains("/api/media/upload"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("data-mnote-media-upload-retired"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function insertUploadedAssetIntoEditor"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function dispatchUploadedEditorChange"));
|
||||
assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function persistUploadedEditorChange"));
|
||||
@@ -1361,7 +1428,7 @@ mod tests {
|
||||
.contains("return patchFileTreeParentChildren(parentRelativePath, rows);"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var renderedPage = false;"));
|
||||
assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.contains("void renderPageProjection(resolvedSidebarPayload);"));
|
||||
.contains("renderedPage = await renderPageProjection(resolvedSidebarPayload);"));
|
||||
let render_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
|
||||
.find("function renderFileProjection(projection)")
|
||||
.expect("renderFileProjection");
|
||||
@@ -1656,9 +1723,8 @@ mod tests {
|
||||
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("recordFileTreeActionStatus('blocked'")
|
||||
);
|
||||
assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("/api/tree/filetree/drop-preflight"));
|
||||
assert!(
|
||||
SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("ensureFileTreeWritableTarget('paste'")
|
||||
);
|
||||
assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
|
||||
.contains("moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("ensureFileTreeWritableTarget('drop'"));
|
||||
}
|
||||
|
||||
@@ -1851,10 +1917,13 @@ mod tests {
|
||||
);
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("function openCodeEditorAttachment"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("function resolveEditorAttachmentUrl"));
|
||||
assert!(!SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("/api/media/sign?assetId="));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
|
||||
.contains("var localFilePath = localFilePathFromAssetId(assetId)"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
|
||||
.contains("var localDownloadUrl = buildLocalFileOpenUrl(localFilePath, true)"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains(
|
||||
"var localDownloadUrl = buildLocalFileOpenUrlForRoot(localFilePath, detail.localRootUri || currentRootUri(), true)"
|
||||
));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("type: 'codeBlock'"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("attrs: { language: language }"));
|
||||
assert!(SIDEBAR_TREE_RUNTIME_JS.contains("inferCodeAttachmentLanguage"));
|
||||
@@ -1862,9 +1931,10 @@ mod tests {
|
||||
.contains("if (isPdfAttachmentFileName(detail.fileName))"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
|
||||
.contains("if (isCodeAttachmentFileName(detail.fileName))"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
|
||||
assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS
|
||||
.contains("if (isCodeAttachmentFileName(fileName) && String(asset.document_id"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("await openCodeEditorAttachment({"));
|
||||
assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("await openCodeEditorAttachment({"));
|
||||
assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
|
||||
let attachment_class_index = SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
|
||||
.find("attachmentClassForFileName(fileName).split")
|
||||
.expect("editor attachment links apply type-specific classes");
|
||||
@@ -1874,10 +1944,7 @@ mod tests {
|
||||
assert!(attachment_class_index < local_refresh_index);
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("'mnote:leptos-tiptap-spike:ready'"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("window.setTimeout(function()"));
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("[120, 500, 1200, 2500]"));
|
||||
assert!(
|
||||
SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("attachmentInitialEnhanceAttempts >= 120")
|
||||
);
|
||||
assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("enhanceEditorAttachmentLinks();"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1889,6 +1956,7 @@ mod tests {
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("data-mnote-tree-live-transport"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("pagehide"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteTreeLiveEventSource"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteLocalFolderEventBus"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:snapshot"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:delta"));
|
||||
assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:resync"));
|
||||
|
||||
Reference in New Issue
Block a user