(dbPath?: string)
| 236 | // ----------------------------------------------------------------------------- |
| 237 | |
| 238 | export function createSQLiteStore(dbPath?: string): AFSStore { |
| 239 | const db = new Database(dbPath ?? getDbPath()); |
| 240 | db.pragma("journal_mode = WAL"); |
| 241 | initDatabase(db); |
| 242 | |
| 243 | // Safe migrations for new columns (no-ops if already exist) |
| 244 | try { db.exec("ALTER TABLE annotations ADD COLUMN kind TEXT DEFAULT 'feedback'"); } catch {} |
| 245 | try { db.exec("ALTER TABLE annotations ADD COLUMN extra TEXT"); } catch {} |
| 246 | |
| 247 | // Restore event sequence from last event |
| 248 | const lastEvent = db.prepare("SELECT MAX(sequence) as seq FROM events").get() as { seq: number | null }; |
| 249 | if (lastEvent?.seq) { |
| 250 | eventBus.setSequence(lastEvent.seq); |
| 251 | } |
| 252 | |
| 253 | // Prepared statements |
| 254 | const stmts = { |
| 255 | // Sessions |
| 256 | insertSession: db.prepare(` |
| 257 | INSERT INTO sessions (id, url, status, created_at, project_id, metadata) |
| 258 | VALUES (@id, @url, @status, @createdAt, @projectId, @metadata) |
| 259 | `), |
| 260 | getSession: db.prepare("SELECT * FROM sessions WHERE id = ?"), |
| 261 | updateSessionStatus: db.prepare(` |
| 262 | UPDATE sessions SET status = @status, updated_at = @updatedAt WHERE id = @id |
| 263 | `), |
| 264 | listSessions: db.prepare("SELECT * FROM sessions ORDER BY created_at DESC"), |
| 265 | |
| 266 | // Annotations |
| 267 | insertAnnotation: db.prepare(` |
| 268 | INSERT INTO annotations ( |
| 269 | id, session_id, x, y, comment, element, element_path, timestamp, |
| 270 | selected_text, bounding_box, nearby_text, css_classes, nearby_elements, |
| 271 | computed_styles, full_path, accessibility, is_multi_select, is_fixed, |
| 272 | react_components, url, intent, severity, status, thread, created_at, |
| 273 | updated_at, resolved_at, resolved_by, author_id, kind, extra |
| 274 | ) VALUES ( |
| 275 | @id, @sessionId, @x, @y, @comment, @element, @elementPath, @timestamp, |
| 276 | @selectedText, @boundingBox, @nearbyText, @cssClasses, @nearbyElements, |
| 277 | @computedStyles, @fullPath, @accessibility, @isMultiSelect, @isFixed, |
| 278 | @reactComponents, @url, @intent, @severity, @status, @thread, @createdAt, |
| 279 | @updatedAt, @resolvedAt, @resolvedBy, @authorId, @kind, @extra |
| 280 | ) |
| 281 | `), |
| 282 | getAnnotation: db.prepare("SELECT * FROM annotations WHERE id = ?"), |
| 283 | getAnnotationsBySession: db.prepare("SELECT * FROM annotations WHERE session_id = ? ORDER BY timestamp"), |
| 284 | getPendingAnnotations: db.prepare("SELECT * FROM annotations WHERE session_id = ? AND status = 'pending' ORDER BY timestamp"), |
| 285 | deleteAnnotation: db.prepare("DELETE FROM annotations WHERE id = ?"), |
| 286 | updateAnnotation: db.prepare(` |
| 287 | UPDATE annotations SET |
| 288 | comment = COALESCE(@comment, comment), |
| 289 | status = COALESCE(@status, status), |
| 290 | updated_at = @updatedAt, |
| 291 | resolved_at = COALESCE(@resolvedAt, resolved_at), |
| 292 | resolved_by = COALESCE(@resolvedBy, resolved_by), |
| 293 | thread = COALESCE(@thread, thread), |
| 294 | intent = COALESCE(@intent, intent), |
| 295 | severity = COALESCE(@severity, severity) |
no test coverage detected
searching dependent graphs…