import { MutationCtx, QueryCtx } from "../_generated/server"; import { Id } from "../_generated/dataModel"; const CART_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000; // 30 days type CartReadCtx = Pick; /** * Get cart by userId or sessionId (read-only). Returns null if not found. */ export async function getCart( ctx: CartReadCtx, userId?: Id<"users">, sessionId?: string ) { if (userId) { return await ctx.db .query("carts") .withIndex("by_user", (q) => q.eq("userId", userId)) .unique(); } if (sessionId) { return await ctx.db .query("carts") .withIndex("by_session", (q) => q.eq("sessionId", sessionId)) .unique(); } return null; } /** * Get existing cart or create a new one. Mutation-only; use from addItem, updateItem, removeItem, clear, merge. * At least one of userId or sessionId must be provided. */ export async function getOrCreateCart( ctx: MutationCtx, userId?: Id<"users">, sessionId?: string ): Promise<{ _id: Id<"carts">; items: { productId: Id<"products">; variantId?: Id<"productVariants">; quantity: number; price: number }[] }> { if (!userId && !sessionId) { throw new Error("Either userId or sessionId must be provided"); } const now = Date.now(); const expiresAt = now + CART_EXPIRY_MS; if (userId) { const existing = await ctx.db .query("carts") .withIndex("by_user", (q) => q.eq("userId", userId)) .unique(); if (existing) return existing; const id = await ctx.db.insert("carts", { userId, items: [], createdAt: now, updatedAt: now, expiresAt, }); const cart = await ctx.db.get(id); if (!cart) throw new Error("Failed to create cart"); return cart; } const existing = await ctx.db .query("carts") .withIndex("by_session", (q) => q.eq("sessionId", sessionId!)) .unique(); if (existing) return existing; const id = await ctx.db.insert("carts", { sessionId: sessionId!, items: [], createdAt: now, updatedAt: now, expiresAt, }); const cart = await ctx.db.get(id); if (!cart) throw new Error("Failed to create cart"); return cart; }