import { chromium } from '@playwright/test'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { createServer as createViteServer } from 'vite'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const memoryKey = '__bonsai_persistence_probe__'; const conversationId = '__bonsai_conversation_probe__'; let browser; let page; let vite; try { vite = await createViteServer({ root, logLevel: 'error', server: { host: '127.0.0.1', port: 0 }, plugins: [{ name: 'persistence-probe-page', configureServer(server) { server.middlewares.use('/persistence-probe.html', (_request, response) => { response.setHeader('Content-Type', 'text/html; charset=utf-8'); response.end('Persistence probe'); }); }, }], }); await vite.listen(); const address = vite.httpServer?.address(); if (!address || typeof address === 'string') throw new Error('Vite persistence probe did not expose a TCP port'); browser = await chromium.launch({ headless: true }); page = await browser.newPage(); await page.goto(`http://127.0.0.1:${address.port}/persistence-probe.html`); await page.evaluate(async ({ memoryKey, conversationId }) => { const idb = await import('/src/lib/idb.ts'); await idb.deleteMemory(memoryKey); await idb.putMemory(memoryKey, 'survives reload'); await idb.saveConversation({ id: conversationId, title: 'Persistence probe', modelId: 'bonsai-1.7b', updatedAt: 0, messages: [{ role: 'user', content: 'keep locally' }], }); }, { memoryKey, conversationId }); await page.reload({ waitUntil: 'domcontentloaded' }); const persisted = await page.evaluate(async ({ memoryKey, conversationId }) => { const idb = await import('/src/lib/idb.ts'); const memory = await idb.getMemory(memoryKey); const conversation = await idb.loadConversation(conversationId); return { memory, conversation }; }, { memoryKey, conversationId }); if (persisted.memory?.value !== 'survives reload') { throw new Error(`Memory did not survive reload: ${JSON.stringify(persisted.memory)}`); } if (persisted.conversation?.messages?.[0]?.content !== 'keep locally') { throw new Error(`Conversation did not survive reload: ${JSON.stringify(persisted.conversation)}`); } console.log('IndexedDB persistence passed: memory and conversation survived a page reload'); } finally { if (page) { await page.evaluate(async ({ memoryKey, conversationId }) => { const idb = await import('/src/lib/idb.ts'); await idb.deleteMemory(memoryKey).catch(() => undefined); const request = indexedDB.open('bonsai-webgpu-chat', 1); await new Promise((resolve) => { request.onsuccess = () => { const database = request.result; const transaction = database.transaction('conversations', 'readwrite'); transaction.objectStore('conversations').delete(conversationId); transaction.oncomplete = () => { database.close(); resolve(); }; transaction.onerror = () => { database.close(); resolve(); }; }; request.onerror = () => resolve(); }); }, { memoryKey, conversationId }).catch(() => undefined); } await browser?.close(); await vite?.close(); }