feat(offline): add IndexedDB schema for queue + cache

This commit is contained in:
Tyler
2026-06-17 14:03:14 -06:00
parent 182ccf2c72
commit 6a9807a3be
2 changed files with 51 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
import { openDB, type DBSchema, type IDBPDatabase } from "idb";
export interface QueuedAction {
id: string; // clientActionId (uuid v4)
actionName: string; // e.g. "markOrderReady"
payload: unknown; // JSON-serializable args
createdAt: number; // Date.now()
attempts: number; // sync attempts
lastAttemptAt: number | null;
status: "pending" | "in-flight" | "synced" | "conflict" | "failed";
error?: string;
}
interface RouteCommerceOfflineDB extends DBSchema {
queue: {
key: string;
value: QueuedAction;
indexes: { "by-status": string; "by-created": number };
};
cache: {
key: string; // e.g. "orders:brandId:abc"
value: { key: string; data: unknown; cachedAt: number };
};
}
const DB_NAME = "route-commerce-offline";
const DB_VERSION = 1;
let dbPromise: Promise<IDBPDatabase<RouteCommerceOfflineDB>> | null = null;
export function getOfflineDB(): Promise<IDBPDatabase<RouteCommerceOfflineDB>> {
if (typeof window === "undefined") {
throw new Error("getOfflineDB called on the server");
}
if (!dbPromise) {
dbPromise = openDB<RouteCommerceOfflineDB>(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains("queue")) {
const queue = db.createObjectStore("queue", { keyPath: "id" });
queue.createIndex("by-status", "status");
queue.createIndex("by-created", "createdAt");
}
if (!db.objectStoreNames.contains("cache")) {
db.createObjectStore("cache", { keyPath: "key" });
}
},
});
}
return dbPromise;
}