function requiredString(value, fieldName) { if (typeof value !== "string") { throw new Error(`${fieldName} must be a string.`); } return value; } export function normalizeSearchQuery(value) { return requiredString(value, "Search query") .trim() .normalize("NFKC") .replace(/[^\p{L}\p{N}\s]+/gu, " ") .replace(/\s+/g, " ") .toLowerCase(); } export function createSearchQuery({ query = "", category, conditionId, setName, eventName, tag, status, newlyAdded = false, minPriceMinor, maxPriceMinor, page = 1, limit = 20, includeSold = false, sort = "recent", }) { const text = normalizeSearchQuery(query); if (!Number.isInteger(page) || page < 1) { throw new Error("page must be >= 1"); } if (!Number.isInteger(limit) || limit < 1 || limit > 100) { throw new Error("limit must be between 1 and 100"); } if (status && !["AVAILABLE", "PENDING", "SOLD", "ALL"].includes(status)) { throw new Error("status must be AVAILABLE, PENDING, SOLD, or ALL"); } if ( typeof minPriceMinor !== "undefined" && (!Number.isInteger(minPriceMinor) || minPriceMinor < 0) ) { throw new Error("minPriceMinor must be a non-negative integer"); } if ( typeof maxPriceMinor !== "undefined" && (!Number.isInteger(maxPriceMinor) || maxPriceMinor < 0) ) { throw new Error("maxPriceMinor must be a non-negative integer"); } if ( typeof minPriceMinor !== "undefined" && typeof maxPriceMinor !== "undefined" && minPriceMinor > maxPriceMinor ) { throw new Error("minPriceMinor must be <= maxPriceMinor"); } if (!["recent", "updated", "name_asc", "name_desc", "price_low", "price_high", "market_low", "market_high"].includes(sort)) { throw new Error("sort must be recent, updated, name_asc, name_desc, price_low, price_high, market_low, or market_high"); } return { text, ...(typeof category === "string" && category.trim().length > 0 ? { category: category.trim() } : {}), ...(typeof conditionId === "string" && conditionId.trim().length > 0 ? { conditionId: conditionId.trim() } : {}), ...(typeof setName === "string" && setName.trim().length > 0 ? { setName: setName.trim() } : {}), ...(typeof eventName === "string" && eventName.trim().length > 0 ? { eventName: eventName.trim() } : {}), ...(typeof tag === "string" && tag.trim().length > 0 ? { tag: tag.trim().toLowerCase() } : {}), ...(typeof minPriceMinor === "number" ? { minPriceMinor } : {}), ...(typeof maxPriceMinor === "number" ? { maxPriceMinor } : {}), page, limit, includeSold, ...(status ? { status } : {}), ...(newlyAdded ? { newlyAdded: true } : {}), ...(sort !== "recent" ? { sort } : {}), }; } export function buildSearchFilters({ includeSold = false, status = "AVAILABLE", sort = ["recently_added"], }) { return { status: includeSold ? "ALL" : status, includeSold, sort, }; }