kammer/src/lib/data/db.ts
Christopher Mühl d56eb2874b feat: data layer, algorithms, and barcode parser with full test suite
- IndexedDB data layer: db.ts, items.ts, locations.ts, sightings.ts, labels.ts
- Pure algorithms: confidence decay, custody state transitions
- Barcode parser: HTTPS URI, haus:// scheme, raw ID parsing
- 150 tests all passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:32:56 +01:00

109 lines
2.6 KiB
TypeScript

import { openDB, type IDBPDatabase, type DBSchema } from 'idb';
interface SolidHausDB extends DBSchema {
items: {
key: string;
value: import('$lib/types').Item;
indexes: {
'by-name': string;
'by-category': string;
'by-location': string;
'by-type': string;
'by-custody': string;
};
};
locations: {
key: string;
value: import('$lib/types').Location;
indexes: {
'by-parent': string;
'by-name': string;
};
};
sightings: {
key: string;
value: import('$lib/types').Sighting;
indexes: {
'by-item': string;
'by-location': string;
'by-timestamp': string;
};
};
photos: {
key: string;
value: import('$lib/types').Photo;
indexes: {
'by-item': string;
};
};
preGeneratedIds: {
key: string;
value: import('$lib/types').PreGeneratedId;
indexes: {
'by-batch': string;
'by-assigned': string;
};
};
settings: {
key: string;
value: {
key: string;
value: unknown;
};
};
}
export type { SolidHausDB };
const DB_NAME = 'solidhaus';
const DB_VERSION = 1;
let dbPromise: Promise<IDBPDatabase<SolidHausDB>> | null = null;
export function getDB(): Promise<IDBPDatabase<SolidHausDB>> {
if (!dbPromise) {
dbPromise = openDB<SolidHausDB>(DB_NAME, DB_VERSION, {
upgrade(db) {
// Items
const itemStore = db.createObjectStore('items', { keyPath: 'shortId' });
itemStore.createIndex('by-name', 'name');
itemStore.createIndex('by-category', 'category');
itemStore.createIndex('by-location', 'lastSeenAt');
itemStore.createIndex('by-type', 'itemType');
itemStore.createIndex('by-custody', 'custodyState');
// Locations
const locStore = db.createObjectStore('locations', { keyPath: 'id' });
locStore.createIndex('by-parent', 'parentId');
locStore.createIndex('by-name', 'name');
// Sightings
const sightStore = db.createObjectStore('sightings', { keyPath: 'id' });
sightStore.createIndex('by-item', 'itemId');
sightStore.createIndex('by-location', 'locationId');
sightStore.createIndex('by-timestamp', 'timestamp');
// Photos
const photoStore = db.createObjectStore('photos', { keyPath: 'id' });
photoStore.createIndex('by-item', 'itemId');
// Pre-generated IDs
const pregenStore = db.createObjectStore('preGeneratedIds', { keyPath: 'id' });
pregenStore.createIndex('by-batch', 'batchId');
pregenStore.createIndex('by-assigned', 'assignedTo');
// Settings
db.createObjectStore('settings', { keyPath: 'key' });
},
});
}
return dbPromise;
}
export async function resetDBPromise(): Promise<void> {
if (dbPromise) {
const db = await dbPromise;
db.close();
dbPromise = null;
}
}