import Dexie, { type Table } from 'dexie' import type { List, ListItem, CreateListPayload, CreateListItemPayload, SetListItemCompletedPayload, SetListItemTitlePayload, OrderListsPayload, } from '@/types/list' export interface LocalList extends List { pendingSync?: boolean } export interface LocalListItem extends ListItem { pendingSync?: boolean } export interface DeleteListPayload { id: string } export interface DeleteListItemPayload { id: string } interface SyncQueueEntryBase { id?: number localListId?: string localListItemId?: string createdAt: number attempts: number lastError?: string } type SyncOperationPayloads = { createList: CreateListPayload createListItem: CreateListItemPayload updateListItemTitle: SetListItemTitlePayload setListItemCompleted: SetListItemCompletedPayload deleteList: DeleteListPayload deleteListItem: DeleteListItemPayload orderLists: OrderListsPayload } export type SyncOperationType = keyof SyncOperationPayloads // A discriminated union keyed on `type` instead of a single `payload: unknown` // shape, so `processSyncEntry`'s switch narrows `entry.payload` to the right // type per case without a manual cast - and adding/changing an operation type // here is a compile error everywhere it's handled inconsistently. export type SyncQueueEntry = { [K in SyncOperationType]: SyncQueueEntryBase & { type: K; payload: SyncOperationPayloads[K] } }[SyncOperationType] type DistributiveOmit = T extends unknown ? Omit : never export type NewSyncQueueEntry = DistributiveOmit class AppDatabase extends Dexie { lists!: Table listItems!: Table syncQueue!: Table constructor() { super('dttmrdb') this.version(1).stores({ lists: 'id, name, pendingSync', listItems: 'id, list_id, title, is_completed, pendingSync', syncQueue: '++id, type, createdAt', }) } } export const db = new AppDatabase()