success pt. 4

This commit is contained in:
2026-02-05 03:46:13 -08:00
parent f4cfca5538
commit b8b99a0f3e
2 changed files with 19 additions and 26 deletions

View File

@@ -7,5 +7,14 @@ import { SQLocalDrizzle } from "sqlocal/drizzle";
const { driver, batchDriver, overwriteDatabaseFile } = new SQLocalDrizzle( const { driver, batchDriver, overwriteDatabaseFile } = new SQLocalDrizzle(
"database.sqlite3", "database.sqlite3",
); );
export const db = drizzle(driver, batchDriver); export const db = drizzle(driver, batchDriver);
/**
* Concrete client DB type (SQLocal-backed Drizzle via sqlite-proxy).
* Exported to allow query helpers to accept the specific type without
* creating circular type references.
*/
export type ClientDb = typeof db;
export { overwriteDatabaseFile }; export { overwriteDatabaseFile };

View File

@@ -1,41 +1,29 @@
import { desc, like } from "drizzle-orm"; import { desc, like } from "drizzle-orm";
import { animeTable } from "$lib/db/schema"; import { animeTable } from "$lib/db/schema";
import type { ClientDb } from "./index";
/** /**
* Client-side DB query helpers (read-only). * Client-side DB query helpers (read-only).
* *
* These functions assume the SQLocal-backed Drizzle `db` is already wired up. * These functions are intentionally side-effect free — seeding/overwriting the DB
* Seeding/overwriting the database file should be handled separately (see `seed.ts`). * file should be done separately (see `seed.ts`).
* *
* Keep these as pure query operations so pages/components can: * We accept the concrete SQLocal-backed Drizzle DB type so callers get full type inference.
* 1) ensure seeded
* 2) query
*/ */
export const DEFAULT_LIST_LIMIT = 20; export const DEFAULT_LIST_LIMIT = 20;
export const MAX_LIST_LIMIT = 200; export const MAX_LIST_LIMIT = 200;
export type AnimeListItem = {
annId: number;
mainName: string;
year: number;
seasonId: number;
malId: number;
};
/** /**
* Get a short list of anime entries for browsing. * Get a short list of anime entries for browsing.
* *
* Sorted by (year desc, seasonId desc, annId desc). * Sorted by (year desc, seasonId desc, annId desc).
*/ */
export async function getAnimeList( export async function getAnimeList(db: ClientDb, limit = DEFAULT_LIST_LIMIT) {
db: { select: (...args: any[]) => any },
limit = DEFAULT_LIST_LIMIT,
): Promise<AnimeListItem[]> {
const safeLimit = clampLimit(limit); const safeLimit = clampLimit(limit);
// NOTE: using explicit selection keeps payload small and stable // Explicit selection keeps payload small and gives a stable, inferred return type
const rows = await (db as any) return db
.select({ .select({
annId: animeTable.annId, annId: animeTable.annId,
mainName: animeTable.mainName, mainName: animeTable.mainName,
@@ -50,8 +38,6 @@ export async function getAnimeList(
desc(animeTable.annId), desc(animeTable.annId),
) )
.limit(safeLimit); .limit(safeLimit);
return rows as AnimeListItem[];
} }
/** /**
@@ -61,17 +47,17 @@ export async function getAnimeList(
* in the snapshot DB, and querying that instead. * in the snapshot DB, and querying that instead.
*/ */
export async function searchAnimeByName( export async function searchAnimeByName(
db: { select: (...args: any[]) => any }, db: ClientDb,
query: string, query: string,
limit = DEFAULT_LIST_LIMIT, limit = DEFAULT_LIST_LIMIT,
): Promise<AnimeListItem[]> { ) {
const q = query.trim(); const q = query.trim();
if (!q) return []; if (!q) return [];
const safeLimit = clampLimit(limit); const safeLimit = clampLimit(limit);
const pattern = `%${q}%`; const pattern = `%${q}%`;
const rows = await (db as any) return db
.select({ .select({
annId: animeTable.annId, annId: animeTable.annId,
mainName: animeTable.mainName, mainName: animeTable.mainName,
@@ -87,8 +73,6 @@ export async function searchAnimeByName(
desc(animeTable.annId), desc(animeTable.annId),
) )
.limit(safeLimit); .limit(safeLimit);
return rows as AnimeListItem[];
} }
function clampLimit(limit: number) { function clampLimit(limit: number) {