refactor frontend storage access through safeStorage
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
const STORAGE_MODULES = [
|
||||
'web/resolver/model_resolver.js',
|
||||
'web/resolver/actions/queue_storage_methods.js',
|
||||
'web/resolver/actions/queue_methods.js',
|
||||
];
|
||||
|
||||
test('resolver storage modules use safeStorage instead of direct localStorage access', async () => {
|
||||
for (const relativePath of STORAGE_MODULES) {
|
||||
const source = await readFile(new URL(`../${relativePath}`, import.meta.url), 'utf8');
|
||||
assert.match(source, /safeStorage\.(?:getItem|setItem|removeItem)/, relativePath);
|
||||
assert.doesNotMatch(source, /\blocalStorage\s*\./, relativePath);
|
||||
}
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import { getSvgIcon } from "../../utils/icon_utils.js";
|
||||
import { startSplitterDrag } from "../utils/splitter_drag.js";
|
||||
import { syncElementAttributes } from "../utils/dom_patch_utils.js";
|
||||
import { bindDownloadActionHandlers } from "../utils/download_action_handlers.js";
|
||||
import { safeStorage } from "../utils/html_utils.js";
|
||||
const FOOTER_VERSION_REFRESH_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
||||
const FOOTER_VERSION_FAILURE_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
||||
|
||||
@@ -36,18 +37,16 @@ export const queueMethods = {
|
||||
this.observeQueueSplitContainer(body);
|
||||
|
||||
// Restore saved queue width and wire splitter
|
||||
try {
|
||||
const savedSplit = localStorage.getItem('model_resolver_split_w');
|
||||
if (savedSplit) {
|
||||
const w = parseInt(savedSplit, 10);
|
||||
if (!isNaN(w) && w > 0) {
|
||||
this.queueElement.style.width = `${w}px`;
|
||||
this.queueElement.style.flexBasis = `${w}px`;
|
||||
this.queueElement.style.flexGrow = '0';
|
||||
this.queueElement.style.flexShrink = '0';
|
||||
}
|
||||
const savedSplit = safeStorage.getItem('model_resolver_split_w');
|
||||
if (savedSplit) {
|
||||
const w = parseInt(savedSplit, 10);
|
||||
if (!isNaN(w) && w > 0) {
|
||||
this.queueElement.style.width = `${w}px`;
|
||||
this.queueElement.style.flexBasis = `${w}px`;
|
||||
this.queueElement.style.flexGrow = '0';
|
||||
this.queueElement.style.flexShrink = '0';
|
||||
}
|
||||
} catch (_e) { }
|
||||
}
|
||||
|
||||
try {
|
||||
const onSplitMouseDown = (e) => this.startSplitDrag(e);
|
||||
@@ -72,10 +71,8 @@ export const queueMethods = {
|
||||
} catch (_e) { }
|
||||
|
||||
// Restore queue collapsed state
|
||||
try {
|
||||
const col = localStorage.getItem('model_resolver_queue_collapsed');
|
||||
if (col === '1') this.setQueueCollapsed(true, { persist: false, updatePanel: false });
|
||||
} catch (_e) { }
|
||||
const col = safeStorage.getItem('model_resolver_queue_collapsed');
|
||||
if (col === '1') this.setQueueCollapsed(true, { persist: false, updatePanel: false });
|
||||
|
||||
return body;
|
||||
},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { safeStorage } from '../utils/html_utils.js';
|
||||
|
||||
export const queueStorageMethods = {
|
||||
getActiveDownloadRecoveryStorageKey() {
|
||||
return this.activeDownloadsStorageKey || 'model_resolver_active_downloads';
|
||||
@@ -10,7 +12,7 @@ export const queueStorageMethods = {
|
||||
|
||||
this._activeDownloadRecoveryLoaded = true;
|
||||
try {
|
||||
const raw = localStorage.getItem(this.getActiveDownloadRecoveryStorageKey());
|
||||
const raw = safeStorage.getItem(this.getActiveDownloadRecoveryStorageKey());
|
||||
const parsed = raw ? JSON.parse(raw) : {};
|
||||
this._activeDownloadRecovery = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||
? parsed
|
||||
@@ -24,14 +26,10 @@ export const queueStorageMethods = {
|
||||
|
||||
saveActiveDownloadRecovery() {
|
||||
const recovery = this.loadActiveDownloadRecovery();
|
||||
try {
|
||||
localStorage.setItem(
|
||||
this.getActiveDownloadRecoveryStorageKey(),
|
||||
JSON.stringify(recovery)
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn('Model Resolver: failed to save active download recovery', error);
|
||||
}
|
||||
safeStorage.setItem(
|
||||
this.getActiveDownloadRecoveryStorageKey(),
|
||||
JSON.stringify(recovery)
|
||||
);
|
||||
},
|
||||
|
||||
persistActiveDownloadRecovery(downloadId, info = {}) {
|
||||
@@ -88,9 +86,7 @@ export const queueStorageMethods = {
|
||||
}
|
||||
const writeState = () => {
|
||||
this._queueCollapsedPersistTimer = null;
|
||||
try {
|
||||
localStorage.setItem('model_resolver_queue_collapsed', collapsed ? '1' : '0');
|
||||
} catch (_e) { }
|
||||
safeStorage.setItem('model_resolver_queue_collapsed', collapsed ? '1' : '0');
|
||||
};
|
||||
const delay = Math.max(0, Number(delayMs) || 0);
|
||||
if (delay > 0) {
|
||||
@@ -109,9 +105,7 @@ export const queueStorageMethods = {
|
||||
}
|
||||
const writeWidth = () => {
|
||||
this._queueSplitPersistTimer = null;
|
||||
try {
|
||||
localStorage.setItem('model_resolver_split_w', String(nextWidth));
|
||||
} catch (_e) { }
|
||||
safeStorage.setItem('model_resolver_split_w', String(nextWidth));
|
||||
};
|
||||
const delay = Math.max(0, Number(delayMs) || 0);
|
||||
if (delay > 0) {
|
||||
@@ -125,7 +119,7 @@ export const queueStorageMethods = {
|
||||
if (this._downloadHistoryLoaded) return this.downloadHistory || [];
|
||||
this._downloadHistoryLoaded = true;
|
||||
try {
|
||||
const raw = localStorage.getItem(this.downloadHistoryStorageKey || 'model_resolver_download_history');
|
||||
const raw = safeStorage.getItem(this.downloadHistoryStorageKey || 'model_resolver_download_history');
|
||||
const parsed = raw ? JSON.parse(raw) : [];
|
||||
this.downloadHistory = Array.isArray(parsed)
|
||||
? parsed.filter(item => item && typeof item === 'object')
|
||||
@@ -147,14 +141,10 @@ export const queueStorageMethods = {
|
||||
saveDownloadHistory() {
|
||||
const history = this.getDownloadHistory().slice(0, this.downloadHistoryLimit || 200);
|
||||
this.downloadHistory = history;
|
||||
try {
|
||||
localStorage.setItem(
|
||||
this.downloadHistoryStorageKey || 'model_resolver_download_history',
|
||||
JSON.stringify(history)
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn('Model Resolver: failed to save download history', error);
|
||||
}
|
||||
safeStorage.setItem(
|
||||
this.downloadHistoryStorageKey || 'model_resolver_download_history',
|
||||
JSON.stringify(history)
|
||||
);
|
||||
},
|
||||
|
||||
getDownloadHistoryIdentity(entry = {}) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isSidebarButtonActive } from "./utils/dom_patch_utils.js";
|
||||
import { fetchJson as fetchJsonRequest } from "./utils/api_client.js";
|
||||
import { ResolverManagerDialog } from "./resolver_dialog.js";
|
||||
import { showNotification } from "../utils/notification_utils.js";
|
||||
import { safeStorage } from "./utils/html_utils.js";
|
||||
import {
|
||||
serializeKeybindingCombo,
|
||||
keybindingsEqual,
|
||||
@@ -60,11 +61,11 @@ function getKeybindingSetting(id) {
|
||||
}
|
||||
|
||||
function applyStoredFrontendLoggingPreference() {
|
||||
const stored = localStorage.getItem('ModelResolver.frontendLogsEnabled');
|
||||
const stored = safeStorage.getItem('ModelResolver.frontendLogsEnabled');
|
||||
if (stored !== null) {
|
||||
frontendLogger.setEnabled(stored !== 'false');
|
||||
}
|
||||
const storedLevel = localStorage.getItem('ModelResolver.frontendLogLevel');
|
||||
const storedLevel = safeStorage.getItem('ModelResolver.frontendLogLevel');
|
||||
if (storedLevel) {
|
||||
frontendLogger.setGlobalAndModuleLevel(frontendLogger.normalizeLevel(storedLevel));
|
||||
}
|
||||
@@ -716,11 +717,7 @@ export class ModelResolver {
|
||||
this.dialog.showResolvedModels = true;
|
||||
this.dialog.missingModelsTypeFilter = 'all';
|
||||
this.dialog.missingModelsTypeFilterMenuOpen = false;
|
||||
try {
|
||||
localStorage.setItem(this.dialog.showResolvedModelsStorageKey, '1');
|
||||
} catch (error) {
|
||||
log.debug('Model Resolver: failed to persist resolved-model visibility.', error);
|
||||
}
|
||||
safeStorage.setItem(this.dialog.showResolvedModelsStorageKey, '1');
|
||||
|
||||
const selectionRequest = this.dialog.queueWorkflowModelReferenceSelection?.(reference);
|
||||
if (!wasVisible) {
|
||||
@@ -961,11 +958,11 @@ export class ModelResolver {
|
||||
}
|
||||
|
||||
isWorkflowHashMetadataEnabled() {
|
||||
return localStorage.getItem('ModelResolver.workflowHashMetadataEnabled') !== 'false';
|
||||
return safeStorage.getItem('ModelResolver.workflowHashMetadataEnabled') !== 'false';
|
||||
}
|
||||
|
||||
isWorkflowDependencyMarkerEnabled() {
|
||||
return localStorage.getItem('ModelResolver.workflowDependencyMarkerEnabled') === 'true';
|
||||
return safeStorage.getItem('ModelResolver.workflowDependencyMarkerEnabled') === 'true';
|
||||
}
|
||||
|
||||
setupWorkflowHashMetadataInjection() {
|
||||
@@ -1605,7 +1602,7 @@ export class ModelResolver {
|
||||
* Check if auto-open preference is enabled
|
||||
*/
|
||||
isAutoOpenEnabled() {
|
||||
return localStorage.getItem('ModelResolver.autoOpenOnMissing') === 'true';
|
||||
return safeStorage.getItem('ModelResolver.autoOpenOnMissing') === 'true';
|
||||
}
|
||||
|
||||
openResolverForDetectedMissingModels() {
|
||||
|
||||
Reference in New Issue
Block a user