Files
gridpilot.gg/apps/website/lib/routing/search-params/SearchParamParser.ts
2026-01-14 02:02:24 +01:00

209 lines
5.4 KiB
TypeScript

/**
* @file SearchParamParser.ts
* Type-safe parser for search parameters from URL
* Returns Result type for clean error handling
*/
import { Result } from '@/lib/contracts/Result';
import { SearchParamValidators } from './SearchParamValidators';
export interface ParsedAuthParams {
returnTo?: string | null;
token?: string | null;
email?: string | null;
error?: string | null;
message?: string | null;
}
export interface ParsedSponsorParams {
type?: string | null;
campaignId?: string | null;
}
export interface ParsedPaginationParams {
page?: number;
limit?: number;
offset?: number;
}
export interface ParsedSortingParams {
sortBy?: string | null;
order?: 'asc' | 'desc';
}
export interface ParsedFilterParams {
status?: string | null;
role?: string | null;
tier?: string | null;
}
export class SearchParamParser {
// Parse auth parameters
static parseAuth(params: URLSearchParams): Result<ParsedAuthParams, string> {
const errors: string[] = [];
const returnTo = params.get('returnTo');
if (returnTo !== null) {
const validation = SearchParamValidators.validateReturnTo(returnTo);
if (!validation.isValid) {
errors.push(...validation.errors);
}
}
const token = params.get('token');
if (token !== null) {
const validation = SearchParamValidators.validateToken(token);
if (!validation.isValid) {
errors.push(...validation.errors);
}
}
const email = params.get('email');
if (email !== null) {
const validation = SearchParamValidators.validateEmail(email);
if (!validation.isValid) {
errors.push(...validation.errors);
}
}
if (errors.length > 0) {
return Result.err(errors.join(', '));
}
return Result.ok({
returnTo: params.get('returnTo'),
token: params.get('token'),
email: params.get('email'),
error: params.get('error'),
message: params.get('message'),
});
}
// Parse sponsor parameters
static parseSponsor(params: URLSearchParams): Result<ParsedSponsorParams, string> {
const errors: string[] = [];
const type = params.get('type');
if (type !== null) {
const validation = SearchParamValidators.validateCampaignType(type);
if (!validation.isValid) {
errors.push(...validation.errors);
}
}
if (errors.length > 0) {
return Result.err(errors.join(', '));
}
return Result.ok({
type: params.get('type'),
campaignId: params.get('campaignId'),
});
}
// Parse pagination parameters
static parsePagination(params: URLSearchParams): Result<ParsedPaginationParams, string> {
const result: ParsedPaginationParams = {};
const errors: string[] = [];
const page = params.get('page');
if (page !== null) {
const validation = SearchParamValidators.validatePage(page);
if (!validation.isValid) {
errors.push(...validation.errors);
} else {
result.page = parseInt(page);
}
}
const limit = params.get('limit');
if (limit !== null) {
const validation = SearchParamValidators.validateLimit(limit);
if (!validation.isValid) {
errors.push(...validation.errors);
} else {
result.limit = parseInt(limit);
}
}
const offset = params.get('offset');
if (offset !== null) {
const num = parseInt(offset);
if (!isNaN(num)) {
result.offset = num;
}
}
if (errors.length > 0) {
return Result.err(errors.join(', '));
}
return Result.ok(result);
}
// Parse sorting parameters
static parseSorting(params: URLSearchParams): Result<ParsedSortingParams, string> {
const errors: string[] = [];
const order = params.get('order');
if (order !== null) {
const validation = SearchParamValidators.validateOrder(order);
if (!validation.isValid) {
errors.push(...validation.errors);
}
}
if (errors.length > 0) {
return Result.err(errors.join(', '));
}
return Result.ok({
sortBy: params.get('sortBy'),
order: (params.get('order') as 'asc' | 'desc') || undefined,
});
}
// Parse filter parameters
static parseFilters(params: URLSearchParams): Result<ParsedFilterParams, string> {
return Result.ok({
status: params.get('status'),
role: params.get('role'),
tier: params.get('tier'),
});
}
// Parse all parameters at once
static parseAll(params: URLSearchParams): Result<
{
auth: ParsedAuthParams;
sponsor: ParsedSponsorParams;
pagination: ParsedPaginationParams;
sorting: ParsedSortingParams;
filters: ParsedFilterParams;
},
string
> {
const authResult = this.parseAuth(params);
if (authResult.isErr()) return Result.err(authResult.getError());
const sponsorResult = this.parseSponsor(params);
if (sponsorResult.isErr()) return Result.err(sponsorResult.getError());
const paginationResult = this.parsePagination(params);
if (paginationResult.isErr()) return Result.err(paginationResult.getError());
const sortingResult = this.parseSorting(params);
if (sortingResult.isErr()) return Result.err(sortingResult.getError());
const filtersResult = this.parseFilters(params);
return Result.ok({
auth: authResult.unwrap(),
sponsor: sponsorResult.unwrap(),
pagination: paginationResult.unwrap(),
sorting: sortingResult.unwrap(),
filters: filtersResult.unwrap(),
});
}
}