feat(db): add playgroundPresets CRUD module

Implements listPlaygroundPresets, getPlaygroundPreset, createPlaygroundPreset,
updatePlaygroundPreset, and deletePlaygroundPreset using db.prepare() (never
raw db.exec or string interpolation). randomUUID() from node:crypto for IDs;
params serialized via JSON.stringify/JSON.parse with fallback to {}.
This commit is contained in:
diegosouzapw
2026-05-27 19:05:21 -03:00
parent 088ad53d79
commit 617d761948

View File

@@ -0,0 +1,167 @@
/**
* db/playgroundPresets.ts — Playground Studio preset persistence.
*
* CRUD operations for the playground_presets table (migration 076).
* All queries use db.prepare() (better-sqlite3) — never raw db.exec() or
* string interpolation.
*
* @module lib/db/playgroundPresets
*/
import { getDbInstance } from "./core";
import { randomUUID } from "node:crypto";
// TODO(F1-merge): swap to import from "@/shared/schemas/playground" after F1 lands
export interface PlaygroundPresetListItem {
id: string;
name: string;
endpoint: string;
model: string;
system: string | null;
params: Record<string, unknown>;
created_at: string;
}
type PlaygroundPresetRow = {
id: string;
name: string;
endpoint: string;
model: string;
system: string | null;
params_json: string;
created_at: string;
};
function rowToItem(row: PlaygroundPresetRow): PlaygroundPresetListItem {
let params: Record<string, unknown> = {};
try {
const parsed = JSON.parse(row.params_json);
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
params = parsed as Record<string, unknown>;
}
} catch {
params = {};
}
return {
id: row.id,
name: row.name,
endpoint: row.endpoint,
model: row.model,
system: row.system,
params,
created_at: row.created_at,
};
}
/**
* Returns all presets ordered by created_at descending (newest first).
*/
export function listPlaygroundPresets(): PlaygroundPresetListItem[] {
const db = getDbInstance();
const rows = db
.prepare("SELECT * FROM playground_presets ORDER BY created_at DESC")
.all() as PlaygroundPresetRow[];
return rows.map(rowToItem);
}
/**
* Returns a single preset by id, or null when not found.
*/
export function getPlaygroundPreset(id: string): PlaygroundPresetListItem | null {
const db = getDbInstance();
const row = db
.prepare("SELECT * FROM playground_presets WHERE id = ? LIMIT 1")
.get(id) as PlaygroundPresetRow | undefined;
if (!row) return null;
return rowToItem(row);
}
/**
* Creates a new preset. Generates a UUID v4 for the id.
* Returns the persisted row via getPlaygroundPreset.
*/
export function createPlaygroundPreset(input: {
name: string;
endpoint: string;
model: string;
system: string | null | undefined;
params: Record<string, unknown>;
}): PlaygroundPresetListItem {
const db = getDbInstance();
const id = randomUUID();
const params_json = JSON.stringify(input.params ?? {});
const system = input.system ?? null;
db.prepare(
"INSERT INTO playground_presets (id, name, endpoint, model, system, params_json) VALUES (?, ?, ?, ?, ?, ?)"
).run(id, input.name, input.endpoint, input.model, system, params_json);
const created = getPlaygroundPreset(id);
// created cannot be null here — we just inserted the row
return created as PlaygroundPresetListItem;
}
/**
* Updates only the supplied fields on an existing preset.
* Returns the updated row, or null when the id does not exist.
*/
export function updatePlaygroundPreset(
id: string,
patch: Partial<{
name: string;
endpoint: string;
model: string;
system: string | null;
params: Record<string, unknown>;
}>
): PlaygroundPresetListItem | null {
const db = getDbInstance();
// Verify row exists before building the dynamic UPDATE
const existing = getPlaygroundPreset(id);
if (!existing) return null;
const setClauses: string[] = [];
const values: unknown[] = [];
if (patch.name !== undefined) {
setClauses.push("name = ?");
values.push(patch.name);
}
if (patch.endpoint !== undefined) {
setClauses.push("endpoint = ?");
values.push(patch.endpoint);
}
if (patch.model !== undefined) {
setClauses.push("model = ?");
values.push(patch.model);
}
if ("system" in patch) {
setClauses.push("system = ?");
values.push(patch.system ?? null);
}
if (patch.params !== undefined) {
setClauses.push("params_json = ?");
values.push(JSON.stringify(patch.params));
}
if (setClauses.length === 0) {
// Empty patch — return current row unchanged
return existing;
}
values.push(id);
db.prepare(`UPDATE playground_presets SET ${setClauses.join(", ")} WHERE id = ?`).run(...values);
return getPlaygroundPreset(id);
}
/**
* Deletes a preset by id.
* Returns true when a row was deleted, false when the id did not exist.
*/
export function deletePlaygroundPreset(id: string): boolean {
const db = getDbInstance();
const result = db.prepare("DELETE FROM playground_presets WHERE id = ?").run(id);
return result.changes > 0;
}