feat: expose persisted project summaries

This commit is contained in:
2026-08-02 21:22:32 -04:00
parent ce64381677
commit 2a0b14b26d
10 changed files with 71 additions and 18 deletions

View File

@@ -1,9 +1,10 @@
import sqlite3InitModule, { type Database, type Sqlite3Static } from '@sqlite.org/sqlite-wasm'
import { PROJECT_SCHEMA_MIGRATIONS, PROJECT_SCHEMA_VERSION } from './projectSchema'
import type { DocumentObjectSnapshot, DocumentSnapshot, ModelTreeItem, ObjectPropertySnapshot, PersistenceCapabilities, ProjectRecoveryReport, ProjectResource } from './types'
import type { DocumentObjectSnapshot, DocumentSnapshot, ModelTreeItem, ObjectPropertySnapshot, PersistenceCapabilities, ProjectRecoveryReport, ProjectResource, ProjectSummary } from './types'
type PersistenceRequest =
| { id: number; type: 'initialize' }
| { id: number; type: 'list-projects' }
| { id: number; type: 'save-document'; document: DocumentSnapshot }
| { id: number; type: 'load-document'; documentId: string }
| { id: number; type: 'recovery-report'; documentId: string }
@@ -14,6 +15,7 @@ type PersistenceRequest =
type PersistenceResponse =
| { id: number; ok: true; type: 'initialized'; capabilities: PersistenceCapabilities }
| { id: number; ok: true; type: 'projects-listed'; projects: ProjectSummary[] }
| { id: number; ok: true; type: 'saved'; documentId: string; documentVersion: number; persistedAt: number; mode: PersistenceCapabilities['mode'] }
| { id: number; ok: true; type: 'loaded'; document: DocumentSnapshot | null }
| { id: number; ok: true; type: 'recovery-report'; report: ProjectRecoveryReport }
@@ -101,6 +103,21 @@ const saveDocument = (document: DocumentSnapshot) => {
return { documentId: document.id, documentVersion: document.version, persistedAt: now, mode: capabilities.mode }
}
const listProjects = (): ProjectSummary[] => {
if (!database) throw new Error('Persistence database is not initialized.')
const rows = database.exec({
sql: `SELECT documents.id AS document_id, documents.label, documents.version, documents.dirty, documents.read_only, projects.updated_at, COUNT(objects.id) AS object_count
FROM documents
JOIN projects ON projects.id = documents.project_id
LEFT JOIN objects ON objects.document_id = documents.id
GROUP BY documents.id, documents.label, documents.version, documents.dirty, documents.read_only, projects.updated_at
ORDER BY projects.updated_at DESC`,
rowMode: 'object',
returnValue: 'resultRows',
}) as Array<Record<string, string | number>>
return rows.map((row) => ({ documentId: String(row.document_id), label: String(row.label), documentVersion: Number(row.version), objectCount: Number(row.object_count), dirty: Boolean(row.dirty), readOnly: Boolean(row.read_only), updatedAt: Number(row.updated_at) }))
}
const loadDocument = (documentId: string): DocumentSnapshot | null => {
if (!database) throw new Error('Persistence database is not initialized.')
const documents = database.exec({ sql: 'SELECT id, label, version, dirty, read_only, units, recompute_json FROM documents WHERE id = ?', bind: [documentId], rowMode: 'object', returnValue: 'resultRows' }) as Array<Record<string, string | number | null>>
@@ -208,6 +225,7 @@ const handle = async (request: PersistenceRequest): Promise<PersistenceResponse>
if (request.type === 'initialize') return { id: request.id, ok: true, type: 'initialized', capabilities: await initialize() }
if (request.type === 'dispose') { database?.close(); database = undefined; transientResources.clear(); return { id: request.id, ok: true, type: 'disposed' } }
await initialize()
if (request.type === 'list-projects') return { id: request.id, ok: true, type: 'projects-listed', projects: listProjects() }
if (request.type === 'save-document') { const result = saveDocument(request.document); return { id: request.id, ok: true, type: 'saved', ...result } }
if (request.type === 'load-document') return { id: request.id, ok: true, type: 'loaded', document: loadDocument(request.documentId) }
if (request.type === 'recovery-report') return { id: request.id, ok: true, type: 'recovery-report', report: recoveryReport(request.documentId) }