1212import crypto from "node:crypto" ;
1313import { getDbInstance } from "./core" ;
1414
15+ interface StatementLike < TRow = unknown > {
16+ all : ( ...params : unknown [ ] ) => TRow [ ] ;
17+ get : ( ...params : unknown [ ] ) => TRow | undefined ;
18+ run : ( ...params : unknown [ ] ) => { lastInsertRowid ?: number | bigint ; changes ?: number } ;
19+ }
20+
21+ interface DbLike {
22+ prepare : < TRow = unknown > ( sql : string ) => StatementLike < TRow > ;
23+ exec : ( sql : string ) => void ;
24+ transaction : ( fn : ( ) => void ) => ( ) => void ;
25+ }
26+
27+ interface PromptRow {
28+ id : unknown ;
29+ slug : unknown ;
30+ version : unknown ;
31+ content : unknown ;
32+ content_hash : unknown ;
33+ variables : unknown ;
34+ description : unknown ;
35+ is_active : unknown ;
36+ created_at : unknown ;
37+ }
38+
39+ interface PromptListRow {
40+ slug : unknown ;
41+ active_version : unknown ;
42+ total_versions : unknown ;
43+ }
44+
45+ function toNumber ( value : unknown , fallback = 0 ) : number {
46+ return typeof value === "number"
47+ ? value
48+ : typeof value === "bigint"
49+ ? Number ( value )
50+ : typeof value === "string" && value . trim ( ) . length > 0
51+ ? Number ( value )
52+ : fallback ;
53+ }
54+
55+ function toString ( value : unknown , fallback = "" ) : string {
56+ return typeof value === "string" ? value : fallback ;
57+ }
58+
59+ function parseVariables ( value : unknown ) : string [ ] | null {
60+ if ( typeof value !== "string" ) return null ;
61+ try {
62+ const parsed = JSON . parse ( value ) as unknown ;
63+ if ( ! Array . isArray ( parsed ) ) return null ;
64+ return parsed . filter ( ( item ) : item is string => typeof item === "string" ) ;
65+ } catch {
66+ return null ;
67+ }
68+ }
69+
1570// ── Schema (auto-created on first access) ──
1671
1772const PROMPT_SCHEMA = `
@@ -37,7 +92,7 @@ let _initialized = false;
3792function ensureSchema ( ) : void {
3893 if ( _initialized ) return ;
3994 try {
40- const db = getDbInstance ( ) ;
95+ const db = getDbInstance ( ) as unknown as DbLike ;
4196 db . exec ( PROMPT_SCHEMA ) ;
4297 _initialized = true ;
4398 } catch {
@@ -74,13 +129,13 @@ export function savePrompt(
74129 options : { variables ?: string [ ] ; description ?: string } = { }
75130) : PromptTemplate {
76131 ensureSchema ( ) ;
77- const db = getDbInstance ( ) ;
132+ const db = getDbInstance ( ) as unknown as DbLike ;
78133 const hash = hashContent ( content ) ;
79134
80135 // Check if identical content already exists for this slug
81136 const existing = db
82- . prepare ( "SELECT * FROM prompt_templates WHERE slug = ? AND content_hash = ?" )
83- . get ( slug , hash ) as any ;
137+ . prepare < PromptRow > ( "SELECT * FROM prompt_templates WHERE slug = ? AND content_hash = ?" )
138+ . get ( slug , hash ) ;
84139
85140 if ( existing ) {
86141 return rowToPrompt ( existing ) ;
@@ -93,9 +148,11 @@ export function savePrompt(
93148
94149 // Get next version number
95150 const maxVersion = db
96- . prepare ( "SELECT MAX(version) as max_v FROM prompt_templates WHERE slug = ?" )
97- . get ( slug ) as any ;
98- const nextVersion = ( maxVersion ?. max_v || 0 ) + 1 ;
151+ . prepare < {
152+ max_v : unknown ;
153+ } > ( "SELECT MAX(version) as max_v FROM prompt_templates WHERE slug = ?" )
154+ . get ( slug ) ;
155+ const nextVersion = toNumber ( maxVersion ?. max_v , 0 ) + 1 ;
99156
100157 // Insert new version
101158 const result = db
@@ -113,7 +170,7 @@ export function savePrompt(
113170 ) ;
114171
115172 return {
116- id : Number ( result . lastInsertRowid ) ,
173+ id : toNumber ( result . lastInsertRowid , 0 ) ,
117174 slug,
118175 version : nextVersion ,
119176 content,
@@ -130,10 +187,10 @@ export function savePrompt(
130187 */
131188export function getActivePrompt ( slug : string ) : PromptTemplate | null {
132189 ensureSchema ( ) ;
133- const db = getDbInstance ( ) ;
190+ const db = getDbInstance ( ) as unknown as DbLike ;
134191 const row = db
135- . prepare ( "SELECT * FROM prompt_templates WHERE slug = ? AND is_active = 1" )
136- . get ( slug ) as any ;
192+ . prepare < PromptRow > ( "SELECT * FROM prompt_templates WHERE slug = ? AND is_active = 1" )
193+ . get ( slug ) ;
137194 return row ? rowToPrompt ( row ) : null ;
138195}
139196
@@ -142,10 +199,10 @@ export function getActivePrompt(slug: string): PromptTemplate | null {
142199 */
143200export function getPromptVersion ( slug : string , version : number ) : PromptTemplate | null {
144201 ensureSchema ( ) ;
145- const db = getDbInstance ( ) ;
202+ const db = getDbInstance ( ) as unknown as DbLike ;
146203 const row = db
147- . prepare ( "SELECT * FROM prompt_templates WHERE slug = ? AND version = ?" )
148- . get ( slug , version ) as any ;
204+ . prepare < PromptRow > ( "SELECT * FROM prompt_templates WHERE slug = ? AND version = ?" )
205+ . get ( slug , version ) ;
149206 return row ? rowToPrompt ( row ) : null ;
150207}
151208
@@ -154,34 +211,38 @@ export function getPromptVersion(slug: string, version: number): PromptTemplate
154211 */
155212export function listPromptVersions ( slug : string ) : PromptTemplate [ ] {
156213 ensureSchema ( ) ;
157- const db = getDbInstance ( ) ;
214+ const db = getDbInstance ( ) as unknown as DbLike ;
158215 const rows = db
159- . prepare ( "SELECT * FROM prompt_templates WHERE slug = ? ORDER BY version DESC" )
160- . all ( slug ) as any [ ] ;
216+ . prepare < PromptRow > ( "SELECT * FROM prompt_templates WHERE slug = ? ORDER BY version DESC" )
217+ . all ( slug ) ;
161218 return rows . map ( rowToPrompt ) ;
162219}
163220
164221/**
165222 * List all prompt slugs with their active version info.
166223 */
167- export function listPrompts ( ) : Array < { slug : string ; activeVersion : number ; totalVersions : number } > {
224+ export function listPrompts ( ) : Array < {
225+ slug : string ;
226+ activeVersion : number ;
227+ totalVersions : number ;
228+ } > {
168229 ensureSchema ( ) ;
169- const db = getDbInstance ( ) ;
230+ const db = getDbInstance ( ) as unknown as DbLike ;
170231 const rows = db
171- . prepare (
232+ . prepare < PromptListRow > (
172233 `SELECT slug,
173234 MAX(CASE WHEN is_active = 1 THEN version ELSE 0 END) as active_version,
174235 COUNT(*) as total_versions
175236 FROM prompt_templates
176237 GROUP BY slug
177238 ORDER BY slug`
178239 )
179- . all ( ) as any [ ] ;
240+ . all ( ) ;
180241
181242 return rows . map ( ( r ) => ( {
182- slug : r . slug ,
183- activeVersion : r . active_version ,
184- totalVersions : r . total_versions ,
243+ slug : toString ( r . slug ) ,
244+ activeVersion : toNumber ( r . active_version , 0 ) ,
245+ totalVersions : toNumber ( r . total_versions , 0 ) ,
185246 } ) ) ;
186247}
187248
@@ -190,11 +251,11 @@ export function listPrompts(): Array<{ slug: string; activeVersion: number; tota
190251 */
191252export function rollbackPrompt ( slug : string , version : number ) : PromptTemplate | null {
192253 ensureSchema ( ) ;
193- const db = getDbInstance ( ) ;
254+ const db = getDbInstance ( ) as unknown as DbLike ;
194255
195256 const target = db
196- . prepare ( "SELECT * FROM prompt_templates WHERE slug = ? AND version = ?" )
197- . get ( slug , version ) as any ;
257+ . prepare < PromptRow > ( "SELECT * FROM prompt_templates WHERE slug = ? AND version = ?" )
258+ . get ( slug , version ) ;
198259
199260 if ( ! target ) return null ;
200261
@@ -226,16 +287,16 @@ export function renderPrompt(slug: string, vars: Record<string, string> = {}): s
226287
227288// ── Internal ──
228289
229- function rowToPrompt ( row : any ) : PromptTemplate {
290+ function rowToPrompt ( row : PromptRow ) : PromptTemplate {
230291 return {
231- id : row . id ,
232- slug : row . slug ,
233- version : row . version ,
234- content : row . content ,
235- contentHash : row . content_hash ,
236- variables : row . variables ? JSON . parse ( row . variables ) : null ,
237- description : row . description ,
238- isActive : row . is_active === 1 ,
239- createdAt : row . created_at ,
292+ id : toNumber ( row . id , 0 ) ,
293+ slug : toString ( row . slug ) ,
294+ version : toNumber ( row . version , 1 ) ,
295+ content : toString ( row . content ) ,
296+ contentHash : toString ( row . content_hash ) ,
297+ variables : parseVariables ( row . variables ) ,
298+ description : typeof row . description === "string" ? row . description : null ,
299+ isActive : row . is_active === 1 || row . is_active === true ,
300+ createdAt : toString ( row . created_at ) ,
240301 } ;
241302}
0 commit comments