Files
gridpilot.gg/apps/website/lib/auth/PathnameInterpreter.ts
2026-01-03 02:42:47 +01:00

97 lines
2.5 KiB
TypeScript

/**
* PathnameInterpreter
*
* Server-only utility for interpreting URL pathnames and extracting locale information.
* Strips locale prefix if present and returns the logical pathname.
*
* Examples:
* - '/de/dashboard' → { locale: 'de', logicalPathname: '/dashboard' }
* - '/dashboard' → { locale: null, logicalPathname: '/dashboard' }
* - '/' → { locale: null, logicalPathname: '/' }
* - '/999/dashboard' → { locale: null, logicalPathname: '/999/dashboard' }
*/
export interface PathnameInterpretation {
locale: string | null;
logicalPathname: string;
}
export class PathnameInterpreter {
/**
* Interprets a pathname and extracts locale information
*
* @param pathname - The URL pathname to interpret
* @returns Object with locale (if valid 2-letter code) and logical pathname
*/
interpret(pathname: string): PathnameInterpretation {
// Handle empty path
if (pathname === '') {
return {
locale: null,
logicalPathname: '',
};
}
// Handle root path
if (pathname === '/') {
return {
locale: null,
logicalPathname: '/',
};
}
// Normalize pathname (remove trailing slash for consistent processing)
const normalizedPathname = pathname.endsWith('/') && pathname.length > 1
? pathname.slice(0, -1)
: pathname;
// Split into segments
const segments = normalizedPathname.split('/').filter(Boolean);
// No segments to process
if (segments.length === 0) {
return {
locale: null,
logicalPathname: '/',
};
}
// Check if first segment is a valid 2-letter locale code
const firstSegment = segments[0];
if (this.isValidLocale(firstSegment)) {
// Valid locale detected - strip it
const remainingSegments = segments.slice(1);
const logicalPathname = remainingSegments.length > 0
? '/' + remainingSegments.join('/')
: '/';
return {
locale: firstSegment,
logicalPathname,
};
}
// No valid locale prefix found
return {
locale: null,
logicalPathname: pathname,
};
}
/**
* Validates if a string is a valid 2-letter locale code
* Must be exactly 2 lowercase letters (a-z)
*
* @param segment - The segment to validate
* @returns True if valid locale code
*/
private isValidLocale(segment: string): boolean {
// Must be exactly 2 characters
if (segment.length !== 2) {
return false;
}
// Must be lowercase letters only (a-z)
return /^[a-z]{2}$/.test(segment);
}
}