import type { CommandTemplate } from '@shared/ipc' import { isTemplateLike } from './core/validation' import { createJsonStore } from './jsonStore' import { TEMPLATES_MAX } from './constants' // Plain JSON in userData, same shape as history.ts. Atomic writes / corruption // backup / caching come from the shared jsonStore (R1–R3). // A persisted template entry must at least be an object carrying an id (see // isTemplateLike in validation.ts); everything else is coerced by sanitize(). // Drop anything that isn't, so a hand-edited templates.json can't inject // malformed entries. (audit S5) const store = createJsonStore('templates.json', isTemplateLike, TEMPLATES_MAX) export function listTemplates(): CommandTemplate[] { // Normalise each surviving entry through sanitize() so name/args are always // well-formed strings regardless of what was on disk. return store.read().map(sanitize) } function sanitize(t: CommandTemplate): CommandTemplate { const urlPattern = typeof t.urlPattern === 'string' ? t.urlPattern.trim() : '' return { id: String(t.id), name: (t.name ?? '').trim() || 'Untitled command', args: (t.args ?? '').trim(), // Only persist a urlPattern when one was provided, keeping older files clean. ...(urlPattern ? { urlPattern } : {}) } } /** Add a new template, or update an existing one (matched by id). */ export function saveTemplate(template: CommandTemplate): CommandTemplate[] { const clean = sanitize(template) return store.write([clean, ...listTemplates().filter((t) => t.id !== clean.id)]) } export function removeTemplate(id: string): CommandTemplate[] { return store.write(listTemplates().filter((t) => t.id !== id)) } /** Replace the entire template list (backup restore) rather than merge-by-id. */ export function replaceTemplates(templates: CommandTemplate[]): CommandTemplate[] { return store.write(templates.map(sanitize)) }