diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index 08adf93..fc4d044 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -2,6 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import assert from 'node:assert/strict'; +import { normalizePathIdentity } from '../web/resolver/utils/html_utils.js'; const projectRoot = path.resolve(import.meta.dirname, '..'); const queueMethodsSource = fs.readFileSync( diff --git a/web/resolver/actions/queue_methods.js b/web/resolver/actions/queue_methods.js index 70d5410..e0869c6 100644 --- a/web/resolver/actions/queue_methods.js +++ b/web/resolver/actions/queue_methods.js @@ -495,7 +495,7 @@ export const queueMethods = { if (typeof document === 'undefined') return null; for (const selector of this.getWorkflowTabActiveSelectors()) { - let elements = []; + let elements; try { elements = Array.from(document.querySelectorAll(selector)); } catch (error) { @@ -900,7 +900,7 @@ export const queueMethods = { let bestMatch = null; for (const selector of this.getWorkflowTabSearchSelectors()) { - let elements = []; + let elements; try { elements = Array.from(document.querySelectorAll(selector)); } catch (error) { diff --git a/web/resolver/actions/resolve_download_methods.js b/web/resolver/actions/resolve_download_methods.js index 346fc07..dc9f226 100644 --- a/web/resolver/actions/resolve_download_methods.js +++ b/web/resolver/actions/resolve_download_methods.js @@ -335,6 +335,9 @@ export const resolveDownloadMethods = { const batchWorkflowKey = this.getWorkflowScopedQueueKey(); let completed = 0; let failed = 0; + let cancelled; + let batchError; + let hasBatchError = false; try { for (const missing of targets) { if (this.batchSearchCancelRequested) { @@ -351,20 +354,25 @@ export const resolveDownloadMethods = { completed += 1; this.updateBatchFooterButtons(); } + } catch (error) { + batchError = error; + hasBatchError = true; } finally { - const cancelled = this.batchSearchCancelRequested; + cancelled = this.batchSearchCancelRequested; this.batchSearchRunning = false; this.batchSearchCancelRequested = false; this.persistSearchStateForActiveWorkflow(); this.updateBatchFooterButtons(); - const suffix = failed ? `, ${failed} failed` : ''; - if (cancelled) { - this.showNotification(`Stopped search after ${completed} of ${targets.length} model${targets.length === 1 ? '' : 's'}${suffix}.`, 'info'); - return; - } } const suffix = failed ? `, ${failed} failed` : ''; + if (cancelled) { + this.showNotification(`Stopped search after ${completed} of ${targets.length} model${targets.length === 1 ? '' : 's'}${suffix}.`, 'info'); + return; + } + if (hasBatchError) { + throw batchError; + } this.showNotification(`Finished search for ${completed} model${completed === 1 ? '' : 's'}${suffix}.`, failed ? 'error' : 'success'); }, @@ -2904,7 +2912,7 @@ export const resolveDownloadMethods = { const url = btn.dataset.url; const filename = btn.dataset.filename; const category = btn.dataset.category; - let pathMetadata = null; + let pathMetadata; try { pathMetadata = btn.dataset.pathMetadata ? JSON.parse(decodeURIComponent(btn.dataset.pathMetadata)) @@ -2912,7 +2920,7 @@ export const resolveDownloadMethods = { } catch (_error) { pathMetadata = null; } - let downloadMetadata = null; + let downloadMetadata; try { downloadMetadata = btn.dataset.downloadMetadata ? JSON.parse(decodeURIComponent(btn.dataset.downloadMetadata)) diff --git a/web/resolver/model_resolver.js b/web/resolver/model_resolver.js index eb800df..096bedb 100644 --- a/web/resolver/model_resolver.js +++ b/web/resolver/model_resolver.js @@ -885,7 +885,7 @@ export class ModelResolver { const currentWorkflow = workflow || app?.graph?.serialize?.(); if (!currentWorkflow) return; - let signature = ''; + let signature; try { signature = JSON.stringify((currentWorkflow.nodes || []).map((node) => [ node?.id, @@ -1017,7 +1017,7 @@ export class ModelResolver { for (const el of allElements) { if (el.textContent?.includes('following models were not found') && el.children.length === 0) { - el.parentElement?.insertBefore(btnContainer, el.nextSibling); + el.parentElement?.insertBefore(autoResolveBtn, el.nextSibling); break; } } @@ -1082,7 +1082,7 @@ export class ModelResolver { ); let node; - while (node = walker.nextNode()) { + while ((node = walker.nextNode())) { if (node.textContent?.toLowerCase().includes(filename)) { // Found text containing filename - now find parent with Download button let parent = node.parentElement; diff --git a/web/resolver/search/download_target_methods.js b/web/resolver/search/download_target_methods.js index da0b288..2ea3ed1 100644 --- a/web/resolver/search/download_target_methods.js +++ b/web/resolver/search/download_target_methods.js @@ -5,6 +5,10 @@ import { getSvgIcon } from "../../utils/icon_utils.js"; import { createFloatingTreePicker } from "../utils/tree_picker.js"; import { safeStorage } from "../utils/html_utils.js"; const localStorage = safeStorage; +const invalidWindowsPathCharacters = new RegExp( + `[<>:"|?*${Array.from({ length: 32 }, (_, code) => String.fromCharCode(code)).join('')}]+`, + 'g' +); export const downloadTargetMethods = { /** * Ensure all models are loaded for the dropdown. @@ -1198,7 +1202,7 @@ export const downloadTargetMethods = { let text = String(value || '').trim() || fallback; text = text .replace(/[\\/]+/g, '_') - .replace(/[<>:"|?*\x00-\x1f]+/g, '_') + .replace(invalidWindowsPathCharacters, '_') .replace(/\s+/g, ' ') .replace(/^[\s.]+|[\s.]+$/g, ''); if (!text || text === '.' || text === '..') { diff --git a/web/resolver/shell/workflow_state_methods.js b/web/resolver/shell/workflow_state_methods.js index 6713bc3..3c3bd1a 100644 --- a/web/resolver/shell/workflow_state_methods.js +++ b/web/resolver/shell/workflow_state_methods.js @@ -453,7 +453,7 @@ export const workflowStateMethods = { }, cloneSearchState(state = {}, { preserveActive = false } = {}) { - let clone = {}; + let clone; try { clone = JSON.parse(JSON.stringify(state || {})); } catch (error) {