fix(settings): correct connection and query timeout limits

This commit is contained in:
zipg authored and GitHub committed 2026-08-12 12:41:15 +08:00
1 parent 5173bbafd1
commit 2df0530c77
15 files changed
+180 -20

No files matched your search

@@ -33,6 +33,7 @@ import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import * as api from "@/lib/backend/api";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
import { applyParsedConnectionUrl, normalizeMongoConnectionString, parseConnectionUrl } from "@/lib/connection/connectionUrl";
import { MAX_CONNECT_TIMEOUT_SECS, MAX_QUERY_TIMEOUT_SECS } from "@/lib/connection/timeoutLimits";
import { buildOracleTnsConnectionString, normalizeOracleTnsAdminPath, parseOracleTnsConnectionString } from "@/lib/connection/oracleTnsConnection";
import { connectionDeepLinkServiceHydrationValue, parseConnectionDeepLink, parseServiceConnectionUrl, type ConnectionDeepLinkDraft } from "@/lib/connection/connectionDeepLink";
import { connectionUrlPlaceholder as getUrlPlaceholder } from "@/lib/connection/connectionPresentation";
@@ -3703,10 +3704,9 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
// service, SID, and descriptor JDBC strings exactly as before.
config.connection_string = undefined;
}
const connectTimeout = Number(config.connect_timeout_secs);
config.connect_timeout_secs = config.connect_timeout_inherit === true ? normalizeGlobalConnectTimeoutSecs(editGlobalConnectTimeoutSecs.value) : Number.isFinite(connectTimeout) && connectTimeout > 0 ? connectTimeout : 10;
config.connect_timeout_secs = config.connect_timeout_inherit === true ? normalizeGlobalConnectTimeoutSecs(editGlobalConnectTimeoutSecs.value) : normalizeGlobalConnectTimeoutSecs(config.connect_timeout_secs);
const queryTimeout = Number(config.query_timeout_secs);
config.query_timeout_secs = config.query_timeout_inherit === true ? normalizeGlobalQueryTimeoutSecs(editGlobalQueryTimeoutSecs.value) : Number.isFinite(queryTimeout) && queryTimeout >= 0 ? queryTimeout : 30;
config.query_timeout_secs = config.query_timeout_inherit === true ? normalizeGlobalQueryTimeoutSecs(editGlobalQueryTimeoutSecs.value) : normalizeGlobalQueryTimeoutSecs(queryTimeout);
const idleTimeout = Number(config.idle_timeout_secs);
config.idle_timeout_secs = Number.isFinite(idleTimeout) && idleTimeout >= 0 ? idleTimeout : 60;
const keepaliveInterval = Number(config.keepalive_interval_secs);
@@ -5007,6 +5007,26 @@ function validateTransportLayers(config: LegacyConnectionConfig) {
});
}
function clampQueryTimeoutInput(event: Event, target: "global" | "connection") {
const input = event.target as HTMLInputElement;
if (input.value === "") return;
const value = Number(input.value);
if (!Number.isFinite(value) || value <= MAX_QUERY_TIMEOUT_SECS) return;
input.value = String(MAX_QUERY_TIMEOUT_SECS);
if (target === "global") editGlobalQueryTimeoutSecs.value = MAX_QUERY_TIMEOUT_SECS;
else form.value.query_timeout_secs = MAX_QUERY_TIMEOUT_SECS;
}
function clampConnectTimeoutInput(event: Event, target: "global" | "connection") {
const input = event.target as HTMLInputElement;
if (input.value === "") return;
const value = Number(input.value);
if (!Number.isFinite(value) || value <= MAX_CONNECT_TIMEOUT_SECS) return;
input.value = String(MAX_CONNECT_TIMEOUT_SECS);
if (target === "global") editGlobalConnectTimeoutSecs.value = MAX_CONNECT_TIMEOUT_SECS;
else form.value.connect_timeout_secs = MAX_CONNECT_TIMEOUT_SECS;
}
async function persistGlobalTimeoutDrafts() {
const nextConnect = normalizeGlobalConnectTimeoutSecs(editGlobalConnectTimeoutSecs.value);
const nextQuery = normalizeGlobalQueryTimeoutSecs(editGlobalQueryTimeoutSecs.value);
@@ -7728,13 +7748,41 @@ function openExternalUrl(url: string) {
<div class="col-span-3 grid grid-cols-2 gap-2">
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.connect_timeout_inherit === true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
<input id="connect-timeout-global" v-model="form.connect_timeout_inherit" type="radio" name="connect-timeout-scope" :value="true" class="h-3.5 w-3.5 shrink-0 accent-primary" />
<label for="connect-timeout-global" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
<Input v-model.number="editGlobalConnectTimeoutSecs" type="number" min="1" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.connect_timeout_inherit !== true" />
<div class="flex min-w-0 flex-1 items-center gap-1">
<label for="connect-timeout-global" class="min-w-0 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
<Tooltip>
<TooltipTrigger as-child>
<CircleHelp class="h-3.5 w-3.5 shrink-0 cursor-help text-muted-foreground hover:text-foreground" />
</TooltipTrigger>
<TooltipContent side="top" align="center" class="max-w-[280px] text-xs leading-relaxed">
{{ t("connection.globalConnectTimeoutHint") }}
</TooltipContent>
</Tooltip>
</div>
<Input
v-model.number="editGlobalConnectTimeoutSecs"
type="number"
min="1"
:max="MAX_CONNECT_TIMEOUT_SECS"
step="1"
class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20"
:disabled="form.connect_timeout_inherit !== true"
@input="clampConnectTimeoutInput($event, 'global')"
/>
</div>
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.connect_timeout_inherit !== true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
<input id="connect-timeout-connection" v-model="form.connect_timeout_inherit" type="radio" name="connect-timeout-scope" :value="false" class="h-3.5 w-3.5 shrink-0 accent-primary" />
<label for="connect-timeout-connection" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useConnectionQueryTimeout')">{{ t("connection.useConnectionQueryTimeout") }}</label>
<Input v-model.number="form.connect_timeout_secs" type="number" min="1" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.connect_timeout_inherit === true" />
<Input
v-model.number="form.connect_timeout_secs"
type="number"
min="1"
:max="MAX_CONNECT_TIMEOUT_SECS"
step="1"
class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20"
:disabled="form.connect_timeout_inherit === true"
@input="clampConnectTimeoutInput($event, 'connection')"
/>
</div>
</div>
</div>
@@ -7750,13 +7798,23 @@ function openExternalUrl(url: string) {
<div class="col-span-3 grid grid-cols-2 gap-2">
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.query_timeout_inherit === true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
<input id="query-timeout-global" v-model="form.query_timeout_inherit" type="radio" name="query-timeout-scope" :value="true" class="h-3.5 w-3.5 shrink-0 accent-primary" />
<label for="query-timeout-global" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
<Input v-model.number="editGlobalQueryTimeoutSecs" type="number" min="0" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit !== true" />
<div class="flex min-w-0 flex-1 items-center gap-1">
<label for="query-timeout-global" class="min-w-0 cursor-pointer truncate text-xs" :title="t('connection.useGlobalQueryTimeout')">{{ t("connection.useGlobalQueryTimeout") }}</label>
<Tooltip>
<TooltipTrigger as-child>
<CircleHelp class="h-3.5 w-3.5 shrink-0 cursor-help text-muted-foreground hover:text-foreground" />
</TooltipTrigger>
<TooltipContent side="top" align="center" class="max-w-[280px] text-xs leading-relaxed">
{{ t("connection.globalQueryTimeoutHint") }}
</TooltipContent>
</Tooltip>
</div>
<Input v-model.number="editGlobalQueryTimeoutSecs" type="number" min="0" :max="MAX_QUERY_TIMEOUT_SECS" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit !== true" @input="clampQueryTimeoutInput($event, 'global')" />
</div>
<div class="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2 gap-y-1 rounded border px-2 py-1.5 sm:flex" :class="form.query_timeout_inherit !== true ? 'border-primary/60 bg-background' : 'border-border bg-muted/30 text-muted-foreground'">
<input id="query-timeout-connection" v-model="form.query_timeout_inherit" type="radio" name="query-timeout-scope" :value="false" class="h-3.5 w-3.5 shrink-0 accent-primary" />
<label for="query-timeout-connection" class="min-w-0 flex-1 cursor-pointer truncate text-xs" :title="t('connection.useConnectionQueryTimeout')">{{ t("connection.useConnectionQueryTimeout") }}</label>
<Input v-model.number="form.query_timeout_secs" type="number" min="0" max="300" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit === true" />
<Input v-model.number="form.query_timeout_secs" type="number" min="0" :max="MAX_QUERY_TIMEOUT_SECS" step="1" class="col-span-2 h-7 w-full shrink-0 sm:col-span-1 sm:w-20" :disabled="form.query_timeout_inherit === true" @input="clampQueryTimeoutInput($event, 'connection')" />
</div>
</div>
</div>
+2
View File
@@ -1012,6 +1012,8 @@ export default {
connectTimeout: "Connection Timeout (seconds)",
queryTimeout: "Query Timeout (seconds)",
useGlobalQueryTimeout: "Global",
globalConnectTimeoutHint: "Range: 1 to 300 seconds",
globalQueryTimeoutHint: "Range: 0 to 3600 seconds. Set to 0 for no timeout.",
useConnectionQueryTimeout: "Connection",
idleTimeout: "Idle Timeout (seconds)",
keepaliveInterval: "Keepalive Interval (seconds)",
+2
View File
@@ -795,6 +795,8 @@ export default withEnglishFallback({
connectTimeout: "Tiempo de espera de conexión (segundos)",
queryTimeout: "Tiempo de espera de consulta (segundos)",
useGlobalQueryTimeout: "Global",
globalConnectTimeoutHint: "Rango: de 1 a 300 segundos",
globalQueryTimeoutHint: "Rango: de 0 a 3600 segundos. Establece 0 para no aplicar límite.",
useConnectionQueryTimeout: "Conexión",
idleTimeout: "Tiempo de espera inactivo (segundos)",
keepaliveInterval: "Intervalo de keepalive (segundos)",
+2
View File
@@ -793,6 +793,8 @@ export default withEnglishFallback({
connectTimeout: "Timeout Connessione (secondi)",
queryTimeout: "Timeout Query (secondi)",
useGlobalQueryTimeout: "Globale",
globalConnectTimeoutHint: "Intervallo: da 1 a 300 secondi",
globalQueryTimeoutHint: "Intervallo: da 0 a 3600 secondi. Imposta 0 per nessun limite.",
useConnectionQueryTimeout: "Connessione",
idleTimeout: "Timeout Inattività (secondi)",
keepaliveInterval: "Intervallo keepalive (secondi)",
+2
View File
@@ -759,6 +759,8 @@ export default withEnglishFallback({
connectTimeout: "接続タイムアウト(秒)",
queryTimeout: "クエリタイムアウト(秒)",
useGlobalQueryTimeout: "グローバル",
globalConnectTimeoutHint: "1~300 秒の範囲で指定します",
globalQueryTimeoutHint: "0~3600 秒の範囲で指定します。0 はタイムアウトなしです。",
useConnectionQueryTimeout: "接続",
idleTimeout: "アイドルタイムアウト(秒)",
keepaliveInterval: "Keepalive 間隔(秒)",
+2
View File
@@ -909,6 +909,8 @@ export default withEnglishFallback({
connectTimeout: "연결 제한 시간 (초)",
queryTimeout: "쿼리 제한 시간 (초)",
useGlobalQueryTimeout: "전역",
globalConnectTimeoutHint: "범위: 1~300초",
globalQueryTimeoutHint: "범위: 0~3600초. 0으로 설정하면 제한이 없습니다.",
useConnectionQueryTimeout: "연결",
idleTimeout: "유휴 제한 시간 (초)",
keepaliveInterval: "킵얼라이브 간격 (초)",
+2
View File
@@ -794,6 +794,8 @@ export default withEnglishFallback({
connectTimeout: "Timeout de Conexão (segundos)",
queryTimeout: "Timeout de Consulta (segundos)",
useGlobalQueryTimeout: "Global",
globalConnectTimeoutHint: "Intervalo: de 1 a 300 segundos",
globalQueryTimeoutHint: "Intervalo: de 0 a 3600 segundos. Defina 0 para não haver limite.",
useConnectionQueryTimeout: "Conexão",
idleTimeout: "Timeout de Inatividade (segundos)",
keepaliveInterval: "Intervalo de keepalive (segundos)",
+2
View File
@@ -940,6 +940,8 @@ export default withEnglishFallback({
connectTimeout: "连接超时(秒)",
queryTimeout: "查询超时(秒)",
useGlobalQueryTimeout: "全局",
globalConnectTimeoutHint: "范围为1~300秒",
globalQueryTimeoutHint: "范围为0~3600秒。设为0表示不限制",
useConnectionQueryTimeout: "当前连接",
idleTimeout: "空闲超时(秒)",
keepaliveInterval: "保持连接间隔(秒)",
+2
View File
@@ -793,6 +793,8 @@ export default withEnglishFallback({
connectTimeout: "連線逾時(秒)",
queryTimeout: "查詢逾時(秒)",
useGlobalQueryTimeout: "全域",
globalConnectTimeoutHint: "範圍為1~300秒",
globalQueryTimeoutHint: "範圍為0~3600秒。設為0表示不限制",
useConnectionQueryTimeout: "目前連線",
idleTimeout: "閒置逾時(秒)",
keepaliveInterval: "保持連線間隔(秒)",
@@ -0,0 +1,32 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { DEFAULT_CONNECT_TIMEOUT_SECS, normalizeConnectTimeoutSecs } from "@/lib/connection/timeoutLimits";
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
describe("ConnectionDialog timeout controls", () => {
it("uses separate maximums for connection and query timeouts", () => {
expect(dialogSource).toMatch(/v-model\.number="editGlobalConnectTimeoutSecs"[\s\S]*?min="1"[\s\S]*?:max="MAX_CONNECT_TIMEOUT_SECS"[\s\S]*?step="1"/);
expect(dialogSource).toMatch(/v-model\.number="form\.connect_timeout_secs"[\s\S]*?min="1"[\s\S]*?:max="MAX_CONNECT_TIMEOUT_SECS"[\s\S]*?step="1"/);
expect(dialogSource).toMatch(/v-model\.number="editGlobalQueryTimeoutSecs"[\s\S]*?min="0"[\s\S]*?:max="MAX_QUERY_TIMEOUT_SECS"[\s\S]*?step="1"/);
expect(dialogSource).toMatch(/v-model\.number="form\.query_timeout_secs"[\s\S]*?min="0"[\s\S]*?:max="MAX_QUERY_TIMEOUT_SECS"[\s\S]*?step="1"/);
expect(dialogSource).not.toContain('v-model.number="editGlobalQueryTimeoutSecs" type="number" min="0" max="300"');
expect(dialogSource).not.toContain('v-model.number="form.query_timeout_secs" type="number" min="0" max="300"');
expect(dialogSource).toContain("@input=\"clampQueryTimeoutInput($event, 'global')\"");
expect(dialogSource).toContain("@input=\"clampQueryTimeoutInput($event, 'connection')\"");
expect(dialogSource).toContain("@input=\"clampConnectTimeoutInput($event, 'global')\"");
expect(dialogSource).toContain("@input=\"clampConnectTimeoutInput($event, 'connection')\"");
expect(dialogSource).not.toContain('@blur="editGlobalQueryTimeoutSecs = normalizeGlobalQueryTimeoutSecs');
});
it("restores the default when the connection timeout input is cleared", () => {
expect(normalizeConnectTimeoutSecs("")).toBe(DEFAULT_CONNECT_TIMEOUT_SECS);
expect(dialogSource).toContain("normalizeGlobalConnectTimeoutSecs(config.connect_timeout_secs)");
expect(dialogSource).not.toContain("Number(config.connect_timeout_secs)");
});
it("shows range help beside both global timeout labels", () => {
expect(dialogSource).toContain('t("connection.globalConnectTimeoutHint")');
expect(dialogSource).toContain('t("connection.globalQueryTimeoutHint")');
});
});
@@ -0,0 +1,37 @@
import { beforeEach, describe, expect, it } from "vitest";
import { loadTimeoutInheritanceBackup, TIMEOUT_INHERITANCE_BACKUP_STORAGE_KEY } from "@/lib/connection/timeoutInheritanceBackup";
const storage = new Map<string, string>();
Object.defineProperty(globalThis, "localStorage", {
configurable: true,
value: {
clear: () => storage.clear(),
getItem: (key: string) => storage.get(key) ?? null,
setItem: (key: string, value: string) => storage.set(key, value),
},
});
describe("timeout inheritance backup", () => {
beforeEach(() => localStorage.clear());
it("preserves query timeouts up to one hour while connection timeouts remain capped at 300 seconds", () => {
localStorage.setItem(
TIMEOUT_INHERITANCE_BACKUP_STORAGE_KEY,
JSON.stringify({
version: 1,
globalConnectTimeoutSecs: 301,
globalQueryTimeoutSecs: 3600,
connectSnapshots: { connection: 301 },
querySnapshots: { longQuery: 3600, tooLong: 3601, unlimited: 0 },
}),
);
expect(loadTimeoutInheritanceBackup()).toEqual({
version: 1,
globalConnectTimeoutSecs: 300,
globalQueryTimeoutSecs: 3600,
connectSnapshots: { connection: 300 },
querySnapshots: { longQuery: 3600, tooLong: 3600, unlimited: 0 },
});
});
});
@@ -1,3 +1,5 @@
import { normalizeConnectTimeoutSecs, normalizeQueryTimeoutSecs } from "@/lib/connection/timeoutLimits";
export const TIMEOUT_INHERITANCE_BACKUP_STORAGE_KEY = "dbx-timeout-inheritance-backup-v1";
export interface TimeoutInheritanceBackup {
@@ -8,12 +10,12 @@ export interface TimeoutInheritanceBackup {
querySnapshots: Record<string, number>;
}
function normalizeSnapshots(value: unknown, min: number): Record<string, number> {
function normalizeSnapshots(value: unknown, normalize: (timeout: unknown) => number): Record<string, number> {
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
const snapshots: Record<string, number> = {};
for (const [id, timeout] of Object.entries(value)) {
if (!id.trim() || typeof timeout !== "number" || !Number.isFinite(timeout)) continue;
snapshots[id] = Math.min(300, Math.max(min, Math.round(timeout)));
snapshots[id] = normalize(timeout);
}
return snapshots;
}
@@ -29,10 +31,10 @@ export function loadTimeoutInheritanceBackup(): TimeoutInheritanceBackup | null
if (!Number.isFinite(globalConnectTimeoutSecs) || !Number.isFinite(globalQueryTimeoutSecs)) return null;
return {
version: 1,
globalConnectTimeoutSecs: Math.min(300, Math.max(1, Math.round(globalConnectTimeoutSecs))),
globalQueryTimeoutSecs: Math.min(300, Math.max(0, Math.round(globalQueryTimeoutSecs))),
connectSnapshots: normalizeSnapshots(parsed.connectSnapshots, 1),
querySnapshots: normalizeSnapshots(parsed.querySnapshots, 0),
globalConnectTimeoutSecs: normalizeConnectTimeoutSecs(globalConnectTimeoutSecs),
globalQueryTimeoutSecs: normalizeQueryTimeoutSecs(globalQueryTimeoutSecs),
connectSnapshots: normalizeSnapshots(parsed.connectSnapshots, normalizeConnectTimeoutSecs),
querySnapshots: normalizeSnapshots(parsed.querySnapshots, normalizeQueryTimeoutSecs),
};
} catch {
return null;
@@ -0,0 +1,14 @@
export const DEFAULT_CONNECT_TIMEOUT_SECS = 10;
export const MAX_CONNECT_TIMEOUT_SECS = 300;
export const DEFAULT_QUERY_TIMEOUT_SECS = 30;
export const MAX_QUERY_TIMEOUT_SECS = 3600;
export function normalizeConnectTimeoutSecs(value: unknown): number {
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_CONNECT_TIMEOUT_SECS;
return Math.min(MAX_CONNECT_TIMEOUT_SECS, Math.max(1, Math.round(value)));
}
export function normalizeQueryTimeoutSecs(value: unknown): number {
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_QUERY_TIMEOUT_SECS;
return Math.min(MAX_QUERY_TIMEOUT_SECS, Math.max(0, Math.round(value)));
}
+3 -1
View File
@@ -238,7 +238,9 @@ describe("normalizeEditorSettings", () => {
expect(normalizeEditorSettings({}).globalQueryTimeoutSecs).toBe(30);
expect(normalizeEditorSettings({ queryTimeoutSecs: 45 } as any).globalQueryTimeoutSecs).toBe(45);
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: -1 }).globalQueryTimeoutSecs).toBe(0);
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 301 }).globalQueryTimeoutSecs).toBe(300);
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 301 }).globalQueryTimeoutSecs).toBe(301);
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 3600 }).globalQueryTimeoutSecs).toBe(3600);
expect(normalizeEditorSettings({ globalQueryTimeoutSecs: 3601 }).globalQueryTimeoutSecs).toBe(3600);
expect(normalizeEditorSettings({ connectTimeoutInheritConnectionIds: ["one", "one", " ", "two"] }).connectTimeoutInheritConnectionIds).toEqual(["one", "two"]);
expect(normalizeEditorSettings({ queryTimeoutInheritConnectionIds: ["one", "one", " ", "two"] }).queryTimeoutInheritConnectionIds).toEqual(["one", "two"]);
expect(normalizeEditorSettings({}).timeoutInheritanceMigrationVersion).toBe(0);
+3 -4
View File
@@ -9,6 +9,7 @@ import { type ColumnFormatterConfig, type CustomColumnFormatterConfig, normalize
import { type DataGridCopyPreference, type DataGridExtractorOptions, DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS, normalizeDataGridCopyPreference, normalizeDataGridExtractorOptions } from "@/lib/dataGrid/dataGridCopyExtractor";
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
import { DEFAULT_QUERY_RESULT_MAX_ROWS, normalizeQueryResultMaxRows } from "@/lib/dataGrid/queryResultRowLimit";
import { normalizeConnectTimeoutSecs, normalizeQueryTimeoutSecs } from "@/lib/connection/timeoutLimits";
import { normalizeShortcutSettings, type ShortcutSettings } from "@/lib/editor/shortcutRegistry";
import type { SavedSqlOpenTargetMode } from "@/lib/savedSql/savedSqlExecutionTarget";
import type { ConnectionListSortMode } from "@/lib/sidebar/connectionListSort";
@@ -800,13 +801,11 @@ const MIN_UI_SCALE = 0.75;
const MAX_UI_SCALE = 2;
export function normalizeGlobalQueryTimeoutSecs(value: unknown): number {
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_EDITOR_SETTINGS.globalQueryTimeoutSecs;
return Math.min(300, Math.max(0, Math.round(value)));
return normalizeQueryTimeoutSecs(value);
}
export function normalizeGlobalConnectTimeoutSecs(value: unknown): number {
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_EDITOR_SETTINGS.globalConnectTimeoutSecs;
return Math.min(300, Math.max(1, Math.round(value)));
return normalizeConnectTimeoutSecs(value);
}
function normalizeUiScale(value: unknown): number {