From fbc5a3c981b8ba07bcb2af09d54d73ed7a43b902 Mon Sep 17 00:00:00 2001 From: DrSmoothl <1787882683@qq.com> Date: Thu, 25 Jun 2026 00:05:46 +0800 Subject: [PATCH] refactor(core): use @snowluma/common/coerce in config normalizers Phase 2. notifications/config and webui/ui-config dropped their verbatim- duplicated isObject/boolOr/clampInt (+ ui-config clampNum) in favor of the shared @snowluma/common/coerce. Pure delete-and-import: the shared primitives are byte-identical to the originals (adversarially verified), so behavior is unchanged. Kept local: strOr (notifications-only), oneOf/isFiniteNum/hexOr (ui-specific). notifications-config suite green (21), core+common typecheck clean. --- packages/core/src/notifications/config.ts | 19 ++++--------------- packages/core/src/webui/ui-config.ts | 21 +++------------------ 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/packages/core/src/notifications/config.ts b/packages/core/src/notifications/config.ts index 2ec24437..20380a02 100644 --- a/packages/core/src/notifications/config.ts +++ b/packages/core/src/notifications/config.ts @@ -7,6 +7,7 @@ // // Channels are GLOBAL (defined once here); each UIN opts into a subset via // `OneBotConfig.notifications.channelIds` (see packages/onebot/src/config.ts). +import { boolOr, clampInt, isObject } from '@snowluma/common/coerce'; import { createLogger } from '@snowluma/common/logger'; import fs from 'fs'; import path from 'path'; @@ -92,21 +93,9 @@ export function renderTemplate(template: string, vars: Record): }); } -// ─── Normalization helpers (replicated from ui-config conventions) ────────── - -function isObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -function boolOr(value: unknown, fallback: boolean): boolean { - return typeof value === 'boolean' ? value : fallback; -} - -function clampInt(value: unknown, min: number, max: number, fallback: number): number { - const n = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN; - if (!Number.isFinite(n)) return fallback; - return Math.trunc(Math.min(max, Math.max(min, n))); -} +// ─── Normalization helpers ────────────────────────────────────────────────── +// isObject / boolOr / clampInt come from @snowluma/common/coerce. strOr is +// notifications-only (string truncation to maxLen), so it stays here. function strOr(value: unknown, fallback: string, maxLen: number): string { return typeof value === 'string' ? value.slice(0, maxLen) : fallback; diff --git a/packages/core/src/webui/ui-config.ts b/packages/core/src/webui/ui-config.ts index 6fcf68a8..240d2970 100644 --- a/packages/core/src/webui/ui-config.ts +++ b/packages/core/src/webui/ui-config.ts @@ -1,3 +1,4 @@ +import { boolOr, clampInt, clampNum, isObject } from '@snowluma/common/coerce'; import { createLogger } from '@snowluma/common/logger'; import fs from 'fs'; import path from 'path'; @@ -308,10 +309,8 @@ function defaultPages(): UiPages { } // ─── Normalization helpers ───────────────────────────────────────────────── - -function isObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} +// isObject / boolOr / clampNum / clampInt come from @snowluma/common/coerce. +// oneOf (enum), isFiniteNum, and hexOr (CSS color) are ui-specific and stay. function oneOf(value: unknown, allowed: readonly T[], fallback: T): T { return typeof value === 'string' && (allowed as readonly string[]).includes(value) @@ -319,25 +318,11 @@ function oneOf(value: unknown, allowed: readonly T[], fallback : fallback; } -function clampNum(value: unknown, min: number, max: number, fallback: number): number { - const n = typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN; - if (!Number.isFinite(n)) return fallback; - return Math.min(max, Math.max(min, n)); -} - -function clampInt(value: unknown, min: number, max: number, fallback: number): number { - return Math.trunc(clampNum(value, min, max, fallback)); -} - function isFiniteNum(value: unknown): boolean { if (typeof value === 'number') return Number.isFinite(value); return typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value)); } -function boolOr(value: unknown, fallback: boolean): boolean { - return typeof value === 'boolean' ? value : fallback; -} - const HEX_RE = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/; function hexOr(value: unknown, fallback: string): string { return typeof value === 'string' && HEX_RE.test(value.trim()) ? value.trim() : fallback;