Something went wrong. Try again.
An atproto Tilemap editor
Something went wrong. Try again.
3.3 kB · 82 lines
TypeScript
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283// ---------------------------------------------------------------------------// Tiny promise-based IndexedDB wrapper.//// One database with several key→value object stores:// • tilesets — one record per tileset, keyed by its id (the embedded image// makes these large, so we never scan them just to list names).// • maps — one record per map, keyed by its id (assumed map-persistence// feature; the store exists so worlds can reference saved maps by id).// • worlds — one record per world, keyed by its id. A world is a graph of// map placements in a shared cell space; it references maps by id.// • meta — small bookkeeping: per-domain `'…Index'` listings for the// pickers, and per-domain `'active…Id'` (what to restore on load).//// Values are whatever structured-cloneable object you hand it. All access is by// (store, key), so there's nothing to index within a store.// ---------------------------------------------------------------------------
const DB_NAME = 'tilemap-world'const DB_VERSION = 3
export const STORE_TILESETS = 'tilesets'export const STORE_MAPS = 'maps'export const STORE_WORLDS = 'worlds'export const STORE_META = 'meta'
type StoreName = | typeof STORE_TILESETS | typeof STORE_MAPS | typeof STORE_WORLDS | typeof STORE_META
let dbPromise: Promise<IDBDatabase> | null = null
function openDb(): Promise<IDBDatabase> { if (dbPromise) return dbPromise dbPromise = new Promise((resolve, reject) => { const req = indexedDB.open(DB_NAME, DB_VERSION) req.onupgradeneeded = () => { const db = req.result // v1 kept a combined map+tileset `documents` store — drop it. if (db.objectStoreNames.contains('documents')) db.deleteObjectStore('documents') if (!db.objectStoreNames.contains(STORE_TILESETS)) db.createObjectStore(STORE_TILESETS) // v3 added the maps + worlds stores. Additive — existing data is untouched. if (!db.objectStoreNames.contains(STORE_MAPS)) db.createObjectStore(STORE_MAPS) if (!db.objectStoreNames.contains(STORE_WORLDS)) db.createObjectStore(STORE_WORLDS) if (!db.objectStoreNames.contains(STORE_META)) db.createObjectStore(STORE_META) } req.onsuccess = () => resolve(req.result) req.onerror = () => reject(req.error) }) return dbPromise}
function tx<T>( store: StoreName, mode: IDBTransactionMode, run: (store: IDBObjectStore) => IDBRequest<T>,): Promise<T> { return openDb().then( (db) => new Promise<T>((resolve, reject) => { const os = db.transaction(store, mode).objectStore(store) const req = run(os) req.onsuccess = () => resolve(req.result) req.onerror = () => reject(req.error) }), )}
export function idbGet<T>(store: StoreName, key: string): Promise<T | undefined> { return tx<T | undefined>(store, 'readonly', (s) => s.get(key) as IDBRequest<T | undefined>)}
export function idbPut<T>(store: StoreName, key: string, value: T): Promise<void> { return tx<IDBValidKey>(store, 'readwrite', (s) => s.put(value, key)).then(() => undefined)}
export function idbDelete(store: StoreName, key: string): Promise<void> { return tx<undefined>(store, 'readwrite', (s) => s.delete(key) as IDBRequest<undefined>).then( () => undefined, )}