refactor: reduce cyclomatic complexity of settings, tool data and layout sanitizers

Add characterization tests first, then split the hot spots into small helpers:
- STT/TTS settings updates move to extensions/piagentui-speech-settings.ts (rule tables)
- defaultExtractData is split into diff/diagnostic/output extractors
- getToolSummaryCategory becomes an ordered rule table (toolSummary.ts)
- sanitizePersistedTerminalLayoutMap is split per level
- duplicated diff stats helpers are merged into utils/diffStats.ts
This commit is contained in:
evandro committed 2026-10-10 04:19:34 -03:00
1 parent da09846708
commit 2d2cdf3b19
13 files changed
+962 -404

No files matched your search

+9 -102
View File
@@ -21,6 +21,7 @@ import {
type StoredSttSettings,
type StoredTtsSettings,
} from './piagentui-settings-store.js'
import { applySttSettingsUpdate, applyTtsSettingsUpdate, isTtsResponseFormat } from './piagentui-speech-settings.js'
import {
buildExtensionCatalog,
executeExtensionInstallCommand,
@@ -774,56 +775,11 @@ export default function (pi: ExtensionAPI) {
function updateSttSettings(update: any): { ok: true; settings: SanitizedSttSettings } | { ok: false; error: string } {
const current = getLocalSettings()
const existing = current.stt || {}
const next: StoredSttSettings = { ...existing }
const result = applySttSettingsUpdate(current.stt || {}, update)
if (!result.ok) return result
if (update.enabled !== undefined) next.enabled = update.enabled === true
if (update.providerKind !== undefined) {
if (update.providerKind !== 'openai' && update.providerKind !== 'openai-compatible') {
return { ok: false, error: 'Invalid providerKind' }
}
next.providerKind = update.providerKind
}
if (update.mode !== undefined) {
if (update.mode !== 'file' && update.mode !== 'realtime') return { ok: false, error: 'Invalid mode' }
next.mode = update.mode
}
if (update.baseUrl !== undefined) {
if (typeof update.baseUrl !== 'string' || !validateSttBaseUrl(update.baseUrl)) {
return { ok: false, error: 'Invalid baseUrl' }
}
next.baseUrl = update.baseUrl.replace(/\/+$/, '')
}
if (update.apiKeyRequired !== undefined) next.apiKeyRequired = update.apiKeyRequired === true
if (update.transcriptionEndpoint !== undefined) {
if (typeof update.transcriptionEndpoint !== 'string' || !update.transcriptionEndpoint.startsWith('/')) {
return { ok: false, error: 'Invalid transcriptionEndpoint' }
}
next.transcriptionEndpoint = update.transcriptionEndpoint
}
if (update.transcriptionModel !== undefined) {
if (typeof update.transcriptionModel !== 'string' || !update.transcriptionModel.trim()) {
return { ok: false, error: 'Invalid transcriptionModel' }
}
next.transcriptionModel = update.transcriptionModel.trim()
}
if (update.language !== undefined) {
if (update.language === null || update.language === '') delete next.language
else if (typeof update.language === 'string') next.language = update.language.trim()
else return { ok: false, error: 'Invalid language' }
}
if (update.insertMode !== undefined) {
if (update.insertMode !== 'append' && update.insertMode !== 'replace')
return { ok: false, error: 'Invalid insertMode' }
next.insertMode = update.insertMode
}
if (Object.prototype.hasOwnProperty.call(update, 'apiKey')) {
if (update.apiKey === null) delete next.apiKey
else if (typeof update.apiKey === 'string' && update.apiKey.trim()) next.apiKey = update.apiKey.trim()
}
saveLocalSettings({ ...current, stt: next })
return { ok: true, settings: sanitizeSttSettings(next) }
saveLocalSettings({ ...current, stt: result.next })
return { ok: true, settings: sanitizeSttSettings(result.next) }
}
function getEffectiveTtsSettings(): StoredTtsSettings {
@@ -857,62 +813,13 @@ export default function (pi: ExtensionAPI) {
}
}
function isTtsResponseFormat(value: unknown): value is NonNullable<StoredTtsSettings['responseFormat']> {
return (
value === 'mp3' || value === 'opus' || value === 'aac' || value === 'flac' || value === 'wav' || value === 'pcm'
)
}
function updateTtsSettings(update: any): { ok: true; settings: SanitizedTtsSettings } | { ok: false; error: string } {
const current = getLocalSettings()
const existing = current.tts || {}
const next: StoredTtsSettings = { ...existing }
const result = applyTtsSettingsUpdate(current.tts || {}, update)
if (!result.ok) return result
if (update.enabled !== undefined) next.enabled = update.enabled === true
if (update.providerKind !== undefined) {
if (update.providerKind !== 'openai' && update.providerKind !== 'openai-compatible') {
return { ok: false, error: 'Invalid providerKind' }
}
next.providerKind = update.providerKind
}
if (update.baseUrl !== undefined) {
if (typeof update.baseUrl !== 'string' || !validateSttBaseUrl(update.baseUrl)) {
return { ok: false, error: 'Invalid baseUrl' }
}
next.baseUrl = update.baseUrl.replace(/\/+$/, '')
}
if (update.apiKeyRequired !== undefined) next.apiKeyRequired = update.apiKeyRequired === true
if (update.speechEndpoint !== undefined) {
if (typeof update.speechEndpoint !== 'string' || !update.speechEndpoint.startsWith('/')) {
return { ok: false, error: 'Invalid speechEndpoint' }
}
next.speechEndpoint = update.speechEndpoint
}
if (update.model !== undefined) {
if (typeof update.model !== 'string' || !update.model.trim()) return { ok: false, error: 'Invalid model' }
next.model = update.model.trim()
}
if (update.voice !== undefined) {
if (typeof update.voice !== 'string' || !update.voice.trim()) return { ok: false, error: 'Invalid voice' }
next.voice = update.voice.trim()
}
if (update.responseFormat !== undefined) {
if (!isTtsResponseFormat(update.responseFormat)) return { ok: false, error: 'Invalid responseFormat' }
next.responseFormat = update.responseFormat
}
if (update.speed !== undefined) {
if (typeof update.speed !== 'number' || update.speed < 0.25 || update.speed > 4) {
return { ok: false, error: 'Invalid speed' }
}
next.speed = update.speed
}
if (Object.prototype.hasOwnProperty.call(update, 'apiKey')) {
if (update.apiKey === null) delete next.apiKey
else if (typeof update.apiKey === 'string' && update.apiKey.trim()) next.apiKey = update.apiKey.trim()
}
saveLocalSettings({ ...current, tts: next })
return { ok: true, settings: sanitizeTtsSettings(next) }
saveLocalSettings({ ...current, tts: result.next })
return { ok: true, settings: sanitizeTtsSettings(result.next) }
}
async function readRequestBuffer(req: http.IncomingMessage): Promise<Buffer> {
@@ -0,0 +1,153 @@
import { describe, expect, it } from 'vitest'
import { applySttSettingsUpdate, applyTtsSettingsUpdate, isTtsResponseFormat } from './piagentui-speech-settings.js'
function stt(update: Record<string, unknown>, existing = {}) {
return applySttSettingsUpdate(existing, update)
}
function tts(update: Record<string, unknown>, existing = {}) {
return applyTtsSettingsUpdate(existing, update)
}
describe('applySttSettingsUpdate', () => {
it('returns the existing settings untouched for an empty update', () => {
const existing = { enabled: true, language: 'pt' }
expect(stt({}, existing)).toEqual({ ok: true, next: existing })
})
it('applies a valid update of every field', () => {
const result = stt({
enabled: true,
providerKind: 'openai',
mode: 'realtime',
baseUrl: 'https://api.example.com/v1///',
apiKeyRequired: true,
transcriptionEndpoint: '/audio/transcriptions',
transcriptionModel: ' whisper-1 ',
language: ' en ',
insertMode: 'replace',
apiKey: ' secret ',
})
expect(result).toEqual({
ok: true,
next: {
enabled: true,
providerKind: 'openai',
mode: 'realtime',
baseUrl: 'https://api.example.com/v1',
apiKeyRequired: true,
transcriptionEndpoint: '/audio/transcriptions',
transcriptionModel: 'whisper-1',
language: 'en',
insertMode: 'replace',
apiKey: 'secret',
},
})
})
it('does not mutate the existing settings', () => {
const existing = { enabled: false }
stt({ enabled: true }, existing)
expect(existing).toEqual({ enabled: false })
})
it.each([
['providerKind', { providerKind: 'other' }],
['mode', { mode: 'stream' }],
['baseUrl', { baseUrl: 'ftp://host' }],
['baseUrl', { baseUrl: 42 }],
['transcriptionEndpoint', { transcriptionEndpoint: 'audio' }],
['transcriptionModel', { transcriptionModel: ' ' }],
['language', { language: 5 }],
['insertMode', { insertMode: 'prepend' }],
])('rejects an invalid %s', (field, update) => {
expect(stt(update)).toEqual({ ok: false, error: `Invalid ${field}` })
})
it('coerces enabled/apiKeyRequired to booleans', () => {
expect(stt({ enabled: 'yes', apiKeyRequired: 1 })).toEqual({
ok: true,
next: { enabled: false, apiKeyRequired: false },
})
})
it('clears language with null or empty string', () => {
expect(stt({ language: null }, { language: 'pt' })).toEqual({ ok: true, next: {} })
expect(stt({ language: '' }, { language: 'pt' })).toEqual({ ok: true, next: {} })
})
it('handles apiKey: null clears, blank string keeps, non-string keeps', () => {
expect(stt({ apiKey: null }, { apiKey: 'old' })).toEqual({ ok: true, next: {} })
expect(stt({ apiKey: ' ' }, { apiKey: 'old' })).toEqual({ ok: true, next: { apiKey: 'old' } })
expect(stt({ apiKey: 12 }, { apiKey: 'old' })).toEqual({ ok: true, next: { apiKey: 'old' } })
})
})
describe('applyTtsSettingsUpdate', () => {
it('returns the existing settings untouched for an empty update', () => {
const existing = { voice: 'alloy' }
expect(tts({}, existing)).toEqual({ ok: true, next: existing })
})
it('applies a valid update of every field', () => {
const result = tts({
enabled: true,
providerKind: 'openai-compatible',
baseUrl: 'http://localhost:8880/v1/',
apiKeyRequired: true,
speechEndpoint: '/audio/speech',
model: ' tts-1-hd ',
voice: ' nova ',
responseFormat: 'wav',
speed: 1.5,
apiKey: ' k ',
})
expect(result).toEqual({
ok: true,
next: {
enabled: true,
providerKind: 'openai-compatible',
baseUrl: 'http://localhost:8880/v1',
apiKeyRequired: true,
speechEndpoint: '/audio/speech',
model: 'tts-1-hd',
voice: 'nova',
responseFormat: 'wav',
speed: 1.5,
apiKey: 'k',
},
})
})
it.each([
['providerKind', { providerKind: 'x' }],
['baseUrl', { baseUrl: 'not a url' }],
['speechEndpoint', { speechEndpoint: 'speech' }],
['model', { model: '' }],
['voice', { voice: 3 }],
['responseFormat', { responseFormat: 'ogg' }],
['speed', { speed: '1' }],
['speed', { speed: 0.1 }],
['speed', { speed: 4.5 }],
])('rejects an invalid %s', (field, update) => {
expect(tts(update)).toEqual({ ok: false, error: `Invalid ${field}` })
})
it('accepts speed at both bounds', () => {
expect(tts({ speed: 0.25 })).toEqual({ ok: true, next: { speed: 0.25 } })
expect(tts({ speed: 4 })).toEqual({ ok: true, next: { speed: 4 } })
})
it('handles apiKey: null clears, blank string keeps', () => {
expect(tts({ apiKey: null }, { apiKey: 'old' })).toEqual({ ok: true, next: {} })
expect(tts({ apiKey: ' ' }, { apiKey: 'old' })).toEqual({ ok: true, next: { apiKey: 'old' } })
})
})
describe('isTtsResponseFormat', () => {
it('accepts supported formats only', () => {
for (const f of ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm']) expect(isTtsResponseFormat(f)).toBe(true)
expect(isTtsResponseFormat('ogg')).toBe(false)
expect(isTtsResponseFormat(undefined)).toBe(false)
})
})
+126
View File
@@ -0,0 +1,126 @@
import { validateSttBaseUrl } from './piagentui-server-core.js'
import type { StoredSttSettings, StoredTtsSettings } from './piagentui-settings-store.js'
export type SettingsUpdateResult<T> = { ok: true; next: T } | { ok: false; error: string }
type Fields = Record<string, unknown>
/** Assigns a validated value to `next`; returns false when the value is invalid. */
type FieldRule = (value: unknown, next: Fields) => boolean
const TTS_RESPONSE_FORMATS = ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'] as const
export function isTtsResponseFormat(value: unknown): value is NonNullable<StoredTtsSettings['responseFormat']> {
return typeof value === 'string' && (TTS_RESPONSE_FORMATS as readonly string[]).includes(value)
}
const booleanField =
(key: string): FieldRule =>
(value, next) => {
next[key] = value === true
return true
}
const oneOfField =
(key: string, allowed: readonly string[]): FieldRule =>
(value, next) => {
if (typeof value !== 'string' || !allowed.includes(value)) return false
next[key] = value
return true
}
const endpointField =
(key: string): FieldRule =>
(value, next) => {
if (typeof value !== 'string' || !value.startsWith('/')) return false
next[key] = value
return true
}
const trimmedField =
(key: string): FieldRule =>
(value, next) => {
if (typeof value !== 'string' || !value.trim()) return false
next[key] = value.trim()
return true
}
const baseUrlField: FieldRule = (value, next) => {
if (typeof value !== 'string' || !validateSttBaseUrl(value)) return false
next.baseUrl = value.replace(/\/+$/, '')
return true
}
const languageField: FieldRule = (value, next) => {
if (value === null || value === '') delete next.language
else if (typeof value === 'string') next.language = value.trim()
else return false
return true
}
const speedField: FieldRule = (value, next) => {
if (typeof value !== 'number' || value < 0.25 || value > 4) return false
next.speed = value
return true
}
const PROVIDER_KINDS = ['openai', 'openai-compatible'] as const
const STT_RULES: Array<[string, FieldRule]> = [
['enabled', booleanField('enabled')],
['providerKind', oneOfField('providerKind', PROVIDER_KINDS)],
['mode', oneOfField('mode', ['file', 'realtime'])],
['baseUrl', baseUrlField],
['apiKeyRequired', booleanField('apiKeyRequired')],
['transcriptionEndpoint', endpointField('transcriptionEndpoint')],
['transcriptionModel', trimmedField('transcriptionModel')],
['language', languageField],
['insertMode', oneOfField('insertMode', ['append', 'replace'])],
]
const TTS_RULES: Array<[string, FieldRule]> = [
['enabled', booleanField('enabled')],
['providerKind', oneOfField('providerKind', PROVIDER_KINDS)],
['baseUrl', baseUrlField],
['apiKeyRequired', booleanField('apiKeyRequired')],
['speechEndpoint', endpointField('speechEndpoint')],
['model', trimmedField('model')],
['voice', trimmedField('voice')],
['responseFormat', oneOfField('responseFormat', TTS_RESPONSE_FORMATS)],
['speed', speedField],
]
function applyUpdate<T extends object>(
existing: T,
update: Record<string, unknown>,
rules: Array<[string, FieldRule]>,
): SettingsUpdateResult<T> {
const next: Fields = { ...(existing as Fields) }
for (const [field, rule] of rules) {
if (update[field] === undefined) continue
if (!rule(update[field], next)) return { ok: false, error: `Invalid ${field}` }
}
if (Object.prototype.hasOwnProperty.call(update, 'apiKey')) {
if (update.apiKey === null) delete next.apiKey
else if (typeof update.apiKey === 'string' && update.apiKey.trim()) next.apiKey = update.apiKey.trim()
}
return { ok: true, next: next as T }
}
/** Validates a PATCH-style STT update against the stored settings. Pure: does not persist anything. */
export function applySttSettingsUpdate(
existing: StoredSttSettings,
update: Record<string, unknown>,
): SettingsUpdateResult<StoredSttSettings> {
return applyUpdate(existing, update, STT_RULES)
}
/** Validates a PATCH-style TTS update against the stored settings. Pure: does not persist anything. */
export function applyTtsSettingsUpdate(
existing: StoredTtsSettings,
update: Record<string, unknown>,
): SettingsUpdateResult<StoredTtsSettings> {
return applyUpdate(existing, update, TTS_RULES)
}