feat: add search query domain rules

This commit is contained in:
2026-09-08 17:56:41 -04:00
parent 34626332f2
commit 0350d88d7c
2 changed files with 116 additions and 0 deletions
@@ -0,0 +1,70 @@
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,
minPriceMinor,
maxPriceMinor,
page = 1,
limit = 20,
includeSold = false,
}) {
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 (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');
}
return {
text,
...(typeof category === 'string' && category.trim().length > 0 ? { category: category.trim() } : {}),
...(typeof minPriceMinor === 'number' ? { minPriceMinor } : {}),
...(typeof maxPriceMinor === 'number' ? { maxPriceMinor } : {}),
page,
limit,
includeSold,
};
}
export function buildSearchFilters({ includeSold = false, status = 'AVAILABLE', sort = ['recently_added'] }) {
return {
status: includeSold ? 'ALL' : status,
includeSold,
sort,
};
}