From b76d115d7e193973e471092ac806c16e0843c639 Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Mon, 14 Sep 2026 16:56:59 +0300 Subject: [PATCH] getting words --- src/db-types.ts | 14 +++++++++ src/db-worker.ts | 43 ++++++++++++++------------ src/home-page.ts | 5 +++ src/main.ts | 24 +++------------ src/sqlite-api.ts | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 src/sqlite-api.ts diff --git a/src/db-types.ts b/src/db-types.ts index 12b066a..078ad28 100644 --- a/src/db-types.ts +++ b/src/db-types.ts @@ -7,6 +7,8 @@ // т.е. количество дней с 1970-01-01, без времени; // - states.fsrs_memory: сырое INTEGER-значение FSRS (не дата, не трогаем). +import { query } from "./sqlite-api"; + export interface CustomField { id: number; name: string; @@ -40,6 +42,18 @@ export interface Word { group_id: number; } +export async function wordList() { + let rows = await query("SELECT id, key, value, tags, more, group_id FROM words"); + return rows.map((row: any) => ({ + id: row.id, + key: row.key, + value: row.value, + tags: row.tags, + more: row.more, + group_id: row.group_id, + })) as Word[]; +} + export interface HistoryEntry { id: number; deck_id: number; diff --git a/src/db-worker.ts b/src/db-worker.ts index 8fbc590..b4fb8a8 100644 --- a/src/db-worker.ts +++ b/src/db-worker.ts @@ -4,47 +4,52 @@ import { DB_FILENAME } from './opfs'; let db: any | null = null; self.onmessage = async (e) => { + const { type, id, sql, params } = e.data; + try { console.log(e.data.type) - switch (e.data.type) { + switch (type) { case 'start': { - try { - const sqlite3 = await sqlite3InitModule({ - print: console.log, - printErr: console.error, // Сюда будут падать детали ошибок VFS - }); - const filename = '/' + DB_FILENAME; - db = new sqlite3.oo1.OpfsDb(filename, 'rwt'); - - db.exec('PRAGMA foreign_keys = ON;'); - self.postMessage({ type: 'success' }); - } catch (err: any) { - console.error(err); - self.postMessage({ type: 'error', message: err.message }); + if (db != null) { + self.postMessage({ type: 'success', id, data: null }); + return; } + + const sqlite3 = await sqlite3InitModule({ + print: console.log, + printErr: console.error, // Сюда будут падать детали ошибок VFS + }); + const filename = '/' + DB_FILENAME; + db = new sqlite3.oo1.OpfsDb(filename, 'rwt'); + + db.exec('PRAGMA foreign_keys = ON;'); + + self.postMessage({ type: 'success', id, data: null }); + break; } case 'query': { if (db === null) throw new Error('БД не инициализирована'); - const result = db.selectObjects(e.data.sql, e.data.params); - self.postMessage({ type: 'success', result }); + const result = db.selectObjects(sql, params); + self.postMessage({ type: 'success', id, data: result }); break; } case 'command': { if (db === null) throw new Error('БД не инициализирована'); - db.exec(e.data.sql, e.data.params); - self.postMessage({ type: 'success' }); + db.exec(sql, params); + self.postMessage({ type: 'success', id, data: null }); break; } case 'close': { if (db === null) throw new Error('БД не инициализирована'); db.close(); + db = null; break; } } } catch (err: any) { console.error(err); - self.postMessage({ type: 'error', message: err.message }); + self.postMessage({ type: 'error', message: err.message , id}); } }; \ No newline at end of file diff --git a/src/home-page.ts b/src/home-page.ts index 44c848f..8b727b6 100644 --- a/src/home-page.ts +++ b/src/home-page.ts @@ -1,6 +1,7 @@ // Главная страница: статус локальной базы + быстрый переход к синхронизации. // Никакой БД-логики кроме проверки наличия файла в OPFS. +import { wordList } from './db-types.ts'; import { worker } from './main.ts'; import { hasDatabaseFile, readDatabaseFile } from './opfs.ts'; import { looksLikeSQLite } from './zstd.ts'; @@ -48,6 +49,10 @@ export function renderHomePage(root: HTMLElement, nav: HomeNav): void { ? `Локальная база на месте: ${(raw.length / 1024).toFixed(1)} КБ, сигнатура SQLite OK.` : 'Файл в OPFS есть, но это не похоже на SQLite — перекачай сохранение.'; actions.hidden = !ok; + if (ok) { + let list = await wordList(); + console.log(list); + } } catch (err) { statusEl.textContent = `Не удалось прочитать базу: ${err instanceof Error ? err.message : String(err)}`; actions.hidden = true; diff --git a/src/main.ts b/src/main.ts index a258e14..bbd80a5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,31 +1,12 @@ import './style.css'; import { renderHomePage } from './home-page.ts'; import { renderSyncPage } from './sync-page.ts'; +import { initWorker } from './sqlite-api.ts'; export const worker = new Worker(new URL('./db-worker.ts', import.meta.url), { type: 'module', }); -worker.onerror = (ee) => { - console.log(ee) -}; - -worker.onmessageerror = (ee) => { - console.log(ee) -}; -worker.onmessage = (e) => { - if (e.data.type === 'success') { - const app = document.getElementById('app'); - if (app) { - console.log(e.data.rows) - } - } else if (e.data.type === 'error') { - console.error(e.data.message); - } -}; - -worker.postMessage({ type: 'start' }); - // Каркас приложения: топ-бар + бургер-меню + две страницы (Главная / Синхронизация). type Page = 'home' | 'sync'; @@ -94,7 +75,10 @@ drawer.querySelectorAll('.drawer-btn').forEach((btn) => { }); }); + + // Старт: главная страница, меню закрыто +await initWorker(); setDrawer(false); renderPage('home'); console.log('[JapLearn] crossOriginIsolated =', window.crossOriginIsolated); diff --git a/src/sqlite-api.ts b/src/sqlite-api.ts new file mode 100644 index 0000000..bce10a1 --- /dev/null +++ b/src/sqlite-api.ts @@ -0,0 +1,77 @@ +import { worker } from "./main"; + +let requestId = 0; + +type WorkerRequest = { + resolve: (data: any) => void; + reject: (error: Error) => void; +}; +let initPromise: Promise | null = null; +const pending = new Map(); + +export function initWorker(): Promise { + if (initPromise) return initPromise; + + initPromise = new Promise((resolve, reject) => { + worker.onmessage = (e: MessageEvent) => { + console.log("Handling worker output"); + const msg = e.data; + + if (msg.type === 'ready') { + // Готов — запрос init уже обработан + return; + } + + const pendingReq = pending.get(msg.id); + if (!pendingReq) return; + pending.delete(msg.id); + + if (msg.type == "success") { + pendingReq.resolve(msg.data); + } else { + console.log(msg) + pendingReq.reject(new Error(msg.error || 'Неизвестная ошибка Worker')); + } + }; + + worker.onerror = (err) => { + console.error('[SQLite Worker] Ошибка:', err); + reject(new Error('Ошибка Worker')); + }; + + // Отправляем init + const id = ++requestId; + pending.set(id, { resolve, reject }); + worker.postMessage({ type: 'start', id }); + }); + + return initPromise; +} + +function sendToWorker(type: string, payload?: any): Promise { + return new Promise((resolve, reject) => { + if (!worker) { + reject(new Error('Worker не создан')); + return; + } + + const id = ++requestId; + pending.set(id, { resolve, reject }); + + worker.postMessage({ type, id, ...payload }); + }); +} + +export async function query(sql: string, params?: any[]): Promise { + const result = await sendToWorker('query', { sql, params }); + return result; +} + +export async function command(sql: string, params?: any[]): Promise { + await sendToWorker('command', { sql, params }); +} + +export async function closeDb(): Promise { + await sendToWorker('close'); + worker.terminate(); +} \ No newline at end of file