config cleanup

This commit is contained in:
Michael Freno
2026-01-03 09:49:50 -05:00
parent 36664b24b2
commit 40c022c420
2 changed files with 41 additions and 163 deletions

View File

@@ -436,7 +436,7 @@ const SuggestionDecoration = Extension.create({
const interval = setInterval(() => { const interval = setInterval(() => {
charIndex = (charIndex + 1) % spinnerChars.length; charIndex = (charIndex + 1) % spinnerChars.length;
spinner.textContent = spinnerChars[charIndex]; spinner.textContent = spinnerChars[charIndex];
}, 50); }, TEXT_EDITOR_CONFIG.SPINNER_INTERVAL_MS);
// Store interval on element for cleanup // Store interval on element for cleanup
(spinner as any)._spinnerInterval = interval; (spinner as any)._spinnerInterval = interval;
@@ -834,7 +834,6 @@ export default function TextEditor(props: TextEditorProps) {
createSignal<number>(-1); createSignal<number>(-1);
const [showHistoryModal, setShowHistoryModal] = createSignal(false); const [showHistoryModal, setShowHistoryModal] = createSignal(false);
const [isLoadingHistory, setIsLoadingHistory] = createSignal(false); const [isLoadingHistory, setIsLoadingHistory] = createSignal(false);
const MAX_HISTORY_SIZE = 100; // Match database pruning limit
let historyDebounceTimer: ReturnType<typeof setTimeout> | null = null; let historyDebounceTimer: ReturnType<typeof setTimeout> | null = null;
let isInitialLoad = true; // Flag to prevent capturing history on initial load let isInitialLoad = true; // Flag to prevent capturing history on initial load
let hasAttemptedHistoryLoad = false; // Flag to prevent repeated load attempts let hasAttemptedHistoryLoad = false; // Flag to prevent repeated load attempts
@@ -934,8 +933,8 @@ export default function TextEditor(props: TextEditorProps) {
const requestBody = { const requestBody = {
input_prefix: context.prefix, input_prefix: context.prefix,
input_suffix: context.suffix, input_suffix: context.suffix,
n_predict: 100, n_predict: TEXT_EDITOR_CONFIG.INFILL_MAX_TOKENS,
temperature: 0.3, temperature: TEXT_EDITOR_CONFIG.INFILL_TEMPERATURE,
stop: ["\n\n", "</s>", "<|endoftext|>"], stop: ["\n\n", "</s>", "<|endoftext|>"],
stream: false stream: false
}; };
@@ -1101,7 +1100,7 @@ export default function TextEditor(props: TextEditorProps) {
mermaidValidationTimer = setTimeout(() => { mermaidValidationTimer = setTimeout(() => {
validateAndPreviewMermaid(content); validateAndPreviewMermaid(content);
}, 500); }, TEXT_EDITOR_CONFIG.MERMAID_VALIDATION_DEBOUNCE_MS);
}); });
// Capture history snapshot // Capture history snapshot
@@ -1139,8 +1138,10 @@ export default function TextEditor(props: TextEditorProps) {
// Limit history size // Limit history size
const limitedHistory = const limitedHistory =
updatedHistory.length > MAX_HISTORY_SIZE updatedHistory.length > TEXT_EDITOR_CONFIG.MAX_HISTORY_SIZE
? updatedHistory.slice(updatedHistory.length - MAX_HISTORY_SIZE) ? updatedHistory.slice(
updatedHistory.length - TEXT_EDITOR_CONFIG.MAX_HISTORY_SIZE
)
: updatedHistory; : updatedHistory;
setHistory(limitedHistory); setHistory(limitedHistory);
@@ -1234,7 +1235,7 @@ export default function TextEditor(props: TextEditorProps) {
// Scroll to first change after a brief delay to allow content to render // Scroll to first change after a brief delay to allow content to render
setTimeout(() => { setTimeout(() => {
scrollToFirstChange(instance, oldContent, node.content); scrollToFirstChange(instance, oldContent, node.content);
}, 100); }, TEXT_EDITOR_CONFIG.SCROLL_TO_CHANGE_DELAY_MS);
}; };
// Find and scroll to the first difference between old and new content // Find and scroll to the first difference between old and new content
@@ -1359,11 +1360,11 @@ export default function TextEditor(props: TextEditorProps) {
// Fade out and remove // Fade out and remove
setTimeout(() => { setTimeout(() => {
highlight.style.opacity = "0"; highlight.style.opacity = "0";
}, 100); }, TEXT_EDITOR_CONFIG.HIGHLIGHT_FADE_DELAY_MS);
setTimeout(() => { setTimeout(() => {
highlight.remove(); highlight.remove();
}, 700); }, TEXT_EDITOR_CONFIG.HIGHLIGHT_REMOVE_DELAY_MS);
}; };
// Load history from database // Load history from database
@@ -1570,7 +1571,7 @@ export default function TextEditor(props: TextEditorProps) {
// This ensures infill works regardless of how content was loaded // This ensures infill works regardless of how content was loaded
setTimeout(() => { setTimeout(() => {
isInitialLoad = false; isInitialLoad = false;
}, 1000); }, TEXT_EDITOR_CONFIG.INITIAL_LOAD_DELAY_MS);
// Listen for mermaid edit events // Listen for mermaid edit events
editor.view.dom.addEventListener("edit-mermaid", ((e: CustomEvent) => { editor.view.dom.addEventListener("edit-mermaid", ((e: CustomEvent) => {
@@ -1719,9 +1720,9 @@ export default function TextEditor(props: TextEditorProps) {
setTimeout(() => { setTimeout(() => {
renumberAllReferences(editor); renumberAllReferences(editor);
updateReferencesSection(editor); updateReferencesSection(editor);
}, 100); }, TEXT_EDITOR_CONFIG.REFERENCE_UPDATE_DELAY_MS);
// Debounced history capture (capture after 2 seconds of inactivity) // Debounced history capture
// Skip during initial load // Skip during initial load
if (!isInitialLoad) { if (!isInitialLoad) {
if (historyDebounceTimer) { if (historyDebounceTimer) {
@@ -1729,7 +1730,7 @@ export default function TextEditor(props: TextEditorProps) {
} }
historyDebounceTimer = setTimeout(() => { historyDebounceTimer = setTimeout(() => {
captureHistory(editor); captureHistory(editor);
}, 2000); }, TEXT_EDITOR_CONFIG.HISTORY_DEBOUNCE_MS);
} }
if (infillConfig() && !isInitialLoad && infillEnabled()) { if (infillConfig() && !isInitialLoad && infillEnabled()) {
@@ -1745,7 +1746,7 @@ export default function TextEditor(props: TextEditorProps) {
} }
infillDebounceTimer = setTimeout(() => { infillDebounceTimer = setTimeout(() => {
requestInfill(); requestInfill();
}, 500); }, TEXT_EDITOR_CONFIG.INFILL_DEBOUNCE_MS);
} }
} }
}); });
@@ -1812,7 +1813,7 @@ export default function TextEditor(props: TextEditorProps) {
setTimeout(() => { setTimeout(() => {
migrateLegacyReferences(instance); migrateLegacyReferences(instance);
migrateLegacyMermaidBlocks(instance); migrateLegacyMermaidBlocks(instance);
}, 50); }, TEXT_EDITOR_CONFIG.LEGACY_MIGRATION_DELAY_MS);
// Capture initial state in history only if no history was loaded // Capture initial state in history only if no history was loaded
setTimeout(() => { setTimeout(() => {
@@ -1829,12 +1830,12 @@ export default function TextEditor(props: TextEditorProps) {
); );
} }
isInitialLoad = false; isInitialLoad = false;
}, 200); }, TEXT_EDITOR_CONFIG.INITIAL_HISTORY_CAPTURE_DELAY_MS);
} else { } else {
// Content already matches - this is the initial load case // Content already matches - this is the initial load case
setTimeout(() => { setTimeout(() => {
isInitialLoad = false; isInitialLoad = false;
}, 500); }, TEXT_EDITOR_CONFIG.INITIAL_LOAD_FALLBACK_DELAY_MS);
} }
} }
}, },

View File

@@ -8,21 +8,13 @@
// ============================================================ // ============================================================
export const AUTH_CONFIG = { export const AUTH_CONFIG = {
/** JWT token expiration for regular sessions (with remember me) */
JWT_EXPIRY: "14d" as const, JWT_EXPIRY: "14d" as const,
/** JWT token expiration for sessions without remember me */
JWT_EXPIRY_SHORT: "12h" as const, JWT_EXPIRY_SHORT: "12h" as const,
/** Session cookie max age in seconds (14 days) */
SESSION_COOKIE_MAX_AGE: 60 * 60 * 24 * 14, // 14 days SESSION_COOKIE_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
/** Remember me cookie max age in seconds */
REMEMBER_ME_MAX_AGE: 60 * 60 * 24 * 14, // 14 days REMEMBER_ME_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
/** CSRF token cookie max age in seconds (14 days) */
CSRF_TOKEN_MAX_AGE: 60 * 60 * 24 * 14, // 14 days CSRF_TOKEN_MAX_AGE: 60 * 60 * 24 * 14, // 14 days
/** Email login link JWT expiration (15 minutes - provides reasonable time to check email without being too permissive) */
EMAIL_LOGIN_LINK_EXPIRY: "15m" as const, EMAIL_LOGIN_LINK_EXPIRY: "15m" as const,
/** Email verification link JWT expiration (15 minutes) */
EMAIL_VERIFICATION_LINK_EXPIRY: "15m" as const, EMAIL_VERIFICATION_LINK_EXPIRY: "15m" as const,
/** Lineage JWT expiration for mobile game */
LINEAGE_JWT_EXPIRY: "14d" as const LINEAGE_JWT_EXPIRY: "14d" as const
} as const; } as const;
@@ -31,15 +23,10 @@ export const AUTH_CONFIG = {
// ============================================================ // ============================================================
export const RATE_LIMITS = { export const RATE_LIMITS = {
/** Login: 5 attempts per 15 minutes per IP */
LOGIN_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 }, LOGIN_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 },
/** Login: 5 attempts per hour per email */
LOGIN_EMAIL: { maxAttempts: 5, windowMs: 60 * 60 * 1000 }, LOGIN_EMAIL: { maxAttempts: 5, windowMs: 60 * 60 * 1000 },
/** Password reset: 3 attempts per hour per IP */
PASSWORD_RESET_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 }, PASSWORD_RESET_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
/** Registration: 3 attempts per hour per IP */
REGISTRATION_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 }, REGISTRATION_IP: { maxAttempts: 3, windowMs: 60 * 60 * 1000 },
/** Email verification: 5 attempts per 15 minutes per IP */
EMAIL_VERIFICATION_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 } EMAIL_VERIFICATION_IP: { maxAttempts: 5, windowMs: 15 * 60 * 1000 }
} as const; } as const;
@@ -51,14 +38,11 @@ export const RATE_LIMIT_CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
// ============================================================ // ============================================================
export const ACCOUNT_LOCKOUT = { export const ACCOUNT_LOCKOUT = {
/** Maximum failed login attempts before account lockout */
MAX_FAILED_ATTEMPTS: 5, MAX_FAILED_ATTEMPTS: 5,
/** Account lockout duration in milliseconds (5 minutes) */
LOCKOUT_DURATION_MS: 5 * 60 * 1000 LOCKOUT_DURATION_MS: 5 * 60 * 1000
} as const; } as const;
export const PASSWORD_RESET_CONFIG = { export const PASSWORD_RESET_CONFIG = {
/** Password reset token expiry (1 hour) */
TOKEN_EXPIRY_MS: 60 * 60 * 1000 TOKEN_EXPIRY_MS: 60 * 60 * 1000
} as const; } as const;
@@ -67,21 +51,14 @@ export const PASSWORD_RESET_CONFIG = {
// ============================================================ // ============================================================
export const COOLDOWN_TIMERS = { export const COOLDOWN_TIMERS = {
/** Email login link cooldown (2 minutes) */ // those without * 1000 are in seconds
EMAIL_LOGIN_LINK_MS: 2 * 60 * 1000, EMAIL_LOGIN_LINK_MS: 2 * 60 * 1000,
/** Email login link cookie max age in seconds */
EMAIL_LOGIN_LINK_COOKIE_MAX_AGE: 2 * 60, EMAIL_LOGIN_LINK_COOKIE_MAX_AGE: 2 * 60,
/** Password reset request cooldown (5 minutes) */
PASSWORD_RESET_REQUEST_MS: 5 * 60 * 1000, PASSWORD_RESET_REQUEST_MS: 5 * 60 * 1000,
/** Password reset request cookie max age in seconds */
PASSWORD_RESET_REQUEST_COOKIE_MAX_AGE: 5 * 60, PASSWORD_RESET_REQUEST_COOKIE_MAX_AGE: 5 * 60,
/** Contact form request cooldown (1 minute) */
CONTACT_REQUEST_MS: 1 * 60 * 1000, CONTACT_REQUEST_MS: 1 * 60 * 1000,
/** Contact form request cookie max age in seconds */
CONTACT_REQUEST_COOKIE_MAX_AGE: 1 * 60, CONTACT_REQUEST_COOKIE_MAX_AGE: 1 * 60,
/** Email verification cooldown (15 minutes) */
EMAIL_VERIFICATION_MS: 15 * 60 * 1000, EMAIL_VERIFICATION_MS: 15 * 60 * 1000,
/** Email verification cookie max age in seconds */
EMAIL_VERIFICATION_COOKIE_MAX_AGE: 15 * 60 EMAIL_VERIFICATION_COOKIE_MAX_AGE: 15 * 60
} as const; } as const;
@@ -90,15 +67,10 @@ export const COOLDOWN_TIMERS = {
// ============================================================ // ============================================================
export const CACHE_CONFIG = { export const CACHE_CONFIG = {
/** Blog cache TTL (24 hours) */
BLOG_CACHE_TTL_MS: 24 * 60 * 60 * 1000, BLOG_CACHE_TTL_MS: 24 * 60 * 60 * 1000,
/** Git activity cache TTL (10 minutes) */
GIT_ACTIVITY_CACHE_TTL_MS: 10 * 60 * 1000, GIT_ACTIVITY_CACHE_TTL_MS: 10 * 60 * 1000,
/** Blog posts list cache TTL (5 minutes) */
BLOG_POSTS_LIST_CACHE_TTL_MS: 5 * 60 * 1000, BLOG_POSTS_LIST_CACHE_TTL_MS: 5 * 60 * 1000,
/** Maximum stale data age (7 days) */
MAX_STALE_DATA_MS: 7 * 24 * 60 * 60 * 1000, MAX_STALE_DATA_MS: 7 * 24 * 60 * 60 * 1000,
/** Git activity max stale age (24 hours) */
GIT_ACTIVITY_MAX_STALE_MS: 24 * 60 * 60 * 1000 GIT_ACTIVITY_MAX_STALE_MS: 24 * 60 * 60 * 1000
} as const; } as const;
@@ -107,15 +79,10 @@ export const CACHE_CONFIG = {
// ============================================================ // ============================================================
export const NETWORK_CONFIG = { export const NETWORK_CONFIG = {
/** Default API timeout for email service (15 seconds) */ EMAIL_API_TIMEOUT_MS: 15 * 1000,
EMAIL_API_TIMEOUT_MS: 15000, GITHUB_API_TIMEOUT_MS: 15 * 1000,
/** Default API timeout for GitHub OAuth (15 seconds) */ GOOGLE_API_TIMEOUT_MS: 15 * 1000,
GITHUB_API_TIMEOUT_MS: 15000,
/** Default API timeout for Google OAuth (15 seconds) */
GOOGLE_API_TIMEOUT_MS: 15000,
/** Maximum retry attempts for failed requests */
MAX_RETRIES: 2, MAX_RETRIES: 2,
/** Retry delay between attempts (1 second) */
RETRY_DELAY_MS: 1000 RETRY_DELAY_MS: 1000
} as const; } as const;
@@ -124,23 +91,14 @@ export const NETWORK_CONFIG = {
// ============================================================ // ============================================================
export const TYPEWRITER_CONFIG = { export const TYPEWRITER_CONFIG = {
/** Default typing speed (characters per second) */
DEFAULT_SPEED: 30, DEFAULT_SPEED: 30,
/** Fast typing speed */
FAST_SPEED: 80, FAST_SPEED: 80,
/** Slow typing speed */
SLOW_SPEED: 10, SLOW_SPEED: 10,
/** Very slow typing speed */
VERY_SLOW_SPEED: 100, VERY_SLOW_SPEED: 100,
/** Extra slow typing speed */
EXTRA_SLOW_SPEED: 120, EXTRA_SLOW_SPEED: 120,
/** Default keep alive duration (ms) */
DEFAULT_KEEP_ALIVE_MS: 2000, DEFAULT_KEEP_ALIVE_MS: 2000,
/** Long keep alive duration (ms) */
LONG_KEEP_ALIVE_MS: 10000, LONG_KEEP_ALIVE_MS: 10000,
/** Default initial delay (ms) */
DEFAULT_DELAY_MS: 500, DEFAULT_DELAY_MS: 500,
/** Cursor fade delay after completion (1 second) */
CURSOR_FADE_DELAY_MS: 1000 CURSOR_FADE_DELAY_MS: 1000
} as const; } as const;
@@ -149,21 +107,14 @@ export const TYPEWRITER_CONFIG = {
// ============================================================ // ============================================================
export const COUNTDOWN_CONFIG = { export const COUNTDOWN_CONFIG = {
/** Email login link countdown duration (2 minutes) */ // these are in seconds
EMAIL_LOGIN_LINK_DURATION_S: 120, EMAIL_LOGIN_LINK_DURATION_S: 120,
/** Password reset countdown duration (5 minutes) */
PASSWORD_RESET_DURATION_S: 300, PASSWORD_RESET_DURATION_S: 300,
/** Contact form countdown duration (1 minute) */
CONTACT_FORM_DURATION_S: 60, CONTACT_FORM_DURATION_S: 60,
/** Password reset success redirect countdown (5 seconds) */
PASSWORD_RESET_SUCCESS_DURATION_S: 5, PASSWORD_RESET_SUCCESS_DURATION_S: 5,
/** Default timer size (pixels) */
DEFAULT_TIMER_SIZE_PX: 48, DEFAULT_TIMER_SIZE_PX: 48,
/** Large timer size (pixels) */
LARGE_TIMER_SIZE_PX: 200, LARGE_TIMER_SIZE_PX: 200,
/** Default stroke width */
DEFAULT_STROKE_WIDTH: 6, DEFAULT_STROKE_WIDTH: 6,
/** Large stroke width */
LARGE_STROKE_WIDTH: 12 LARGE_STROKE_WIDTH: 12
} as const; } as const;
@@ -172,11 +123,8 @@ export const COUNTDOWN_CONFIG = {
// ============================================================ // ============================================================
export const BREAKPOINTS = { export const BREAKPOINTS = {
/** Mobile breakpoint (pixels) */
MOBILE_MAX_WIDTH: 768, MOBILE_MAX_WIDTH: 768,
/** Tablet breakpoint (pixels) */
TABLET_MAX_WIDTH: 1024, TABLET_MAX_WIDTH: 1024,
/** Desktop minimum width (pixels) */
DESKTOP_MIN_WIDTH: 1025 DESKTOP_MIN_WIDTH: 1025
} as const; } as const;
@@ -185,25 +133,15 @@ export const BREAKPOINTS = {
// ============================================================ // ============================================================
export const ANIMATION_CONFIG = { export const ANIMATION_CONFIG = {
/** Standard transition duration (ms) */
TRANSITION_DURATION_MS: 300, TRANSITION_DURATION_MS: 300,
/** Fast transition duration (ms) */
FAST_TRANSITION_MS: 200, FAST_TRANSITION_MS: 200,
/** Slow transition duration (ms) */
SLOW_TRANSITION_MS: 500, SLOW_TRANSITION_MS: 500,
/** Extra slow transition duration (ms) */
EXTRA_SLOW_TRANSITION_MS: 600, EXTRA_SLOW_TRANSITION_MS: 600,
/** Sidebar toggle duration (ms) */
SIDEBAR_DURATION_MS: 500, SIDEBAR_DURATION_MS: 500,
/** Menu typing effect delay (ms) */
MENU_TYPING_DELAY_MS: 140, MENU_TYPING_DELAY_MS: 140,
/** Menu initial delay (ms) */
MENU_INITIAL_DELAY_MS: 500, MENU_INITIAL_DELAY_MS: 500,
/** Success message auto-hide duration (ms) */
SUCCESS_MESSAGE_DURATION_MS: 3000, SUCCESS_MESSAGE_DURATION_MS: 3000,
/** Error message auto-hide duration (ms) */
ERROR_MESSAGE_DURATION_MS: 5000, ERROR_MESSAGE_DURATION_MS: 5000,
/** Redirect delay after successful action (ms) */
REDIRECT_DELAY_MS: 500 REDIRECT_DELAY_MS: 500
} as const; } as const;
@@ -212,7 +150,6 @@ export const ANIMATION_CONFIG = {
// ============================================================ // ============================================================
export const PDF_CONFIG = { export const PDF_CONFIG = {
/** PDF rendering scale */
RENDER_SCALE: 1.5 RENDER_SCALE: 1.5
} as const; } as const;
@@ -221,11 +158,8 @@ export const PDF_CONFIG = {
// ============================================================ // ============================================================
export const ERROR_PAGE_CONFIG = { export const ERROR_PAGE_CONFIG = {
/** Glitch effect interval (ms) */
GLITCH_INTERVAL_MS: 300, GLITCH_INTERVAL_MS: 300,
/** Glitch effect duration (ms) */
GLITCH_DURATION_MS: 100, GLITCH_DURATION_MS: 100,
/** Number of particles for background animation */
PARTICLE_COUNT: 45 PARTICLE_COUNT: 45
} as const; } as const;
@@ -243,7 +177,22 @@ export const MOBILE_CONFIG = {
// ============================================================ // ============================================================
export const TEXT_EDITOR_CONFIG = { export const TEXT_EDITOR_CONFIG = {
CONTEXT_SIZE: 256 CONTEXT_SIZE: 256,
MAX_HISTORY_SIZE: 100,
HISTORY_DEBOUNCE_MS: 2000,
INFILL_DEBOUNCE_MS: 500,
INFILL_MAX_TOKENS: 100,
INFILL_TEMPERATURE: 0.3,
MERMAID_VALIDATION_DEBOUNCE_MS: 500,
LEGACY_MIGRATION_DELAY_MS: 50,
INITIAL_HISTORY_CAPTURE_DELAY_MS: 200,
INITIAL_LOAD_FALLBACK_DELAY_MS: 500,
INITIAL_LOAD_DELAY_MS: 1000,
SPINNER_INTERVAL_MS: 50,
HIGHLIGHT_FADE_DELAY_MS: 100,
HIGHLIGHT_REMOVE_DELAY_MS: 700,
REFERENCE_UPDATE_DELAY_MS: 100,
SCROLL_TO_CHANGE_DELAY_MS: 100
} as const; } as const;
// ============================================================ // ============================================================
@@ -251,17 +200,12 @@ export const TEXT_EDITOR_CONFIG = {
// ============================================================ // ============================================================
export const VALIDATION_CONFIG = { export const VALIDATION_CONFIG = {
/** Minimum password length (must match securePasswordSchema in schemas/user.ts) */
MIN_PASSWORD_LENGTH: 8, MIN_PASSWORD_LENGTH: 8,
/** Require at least one uppercase letter in password */
PASSWORD_REQUIRE_UPPERCASE: true, PASSWORD_REQUIRE_UPPERCASE: true,
/** Require at least one number in password */
PASSWORD_REQUIRE_NUMBER: true, PASSWORD_REQUIRE_NUMBER: true,
/** Require at least one special character in password (false = optional but recommended) */ // changed to just recommended, may change again in future
PASSWORD_REQUIRE_SPECIAL: false, PASSWORD_REQUIRE_SPECIAL: false,
/** Maximum message length for contact form */
MAX_CONTACT_MESSAGE_LENGTH: 500, MAX_CONTACT_MESSAGE_LENGTH: 500,
/** Minimum password confirmation match length before showing error */
MIN_PASSWORD_CONF_LENGTH_FOR_ERROR: 6 MIN_PASSWORD_CONF_LENGTH_FOR_ERROR: 6
} as const; } as const;
@@ -270,9 +214,7 @@ export const VALIDATION_CONFIG = {
// ============================================================ // ============================================================
export const LINEAGE_CONFIG = { export const LINEAGE_CONFIG = {
/** Database deletion grace period (24 hours) */
DELETION_GRACE_PERIOD_MS: 24 * 60 * 60 * 1000, DELETION_GRACE_PERIOD_MS: 24 * 60 * 60 * 1000,
/** PvP opponents returned per query */
PVP_OPPONENTS_COUNT: 3 PVP_OPPONENTS_COUNT: 3
} as const; } as const;
@@ -281,71 +223,6 @@ export const LINEAGE_CONFIG = {
// ============================================================ // ============================================================
export const AUDIT_CONFIG = { export const AUDIT_CONFIG = {
/** Default query limit for audit logs */
DEFAULT_QUERY_LIMIT: 100, DEFAULT_QUERY_LIMIT: 100,
/** Maximum audit log retention (90 days) */
MAX_RETENTION_DAYS: 90 MAX_RETENTION_DAYS: 90
} as const; } as const;
// ============================================================
// HELPER FUNCTIONS
// ============================================================
/**
* Convert milliseconds to seconds
*/
export function msToSeconds(ms: number): number {
return Math.floor(ms / 1000);
}
/**
* Convert seconds to milliseconds
*/
export function secondsToMs(seconds: number): number {
return seconds * 1000;
}
/**
* Convert minutes to milliseconds
*/
export function minutesToMs(minutes: number): number {
return minutes * 60 * 1000;
}
/**
* Convert hours to milliseconds
*/
export function hoursToMs(hours: number): number {
return hours * 60 * 60 * 1000;
}
/**
* Convert days to milliseconds
*/
export function daysToMs(days: number): number {
return days * 24 * 60 * 60 * 1000;
}
/**
* Check if screen width is mobile
*/
export function isMobileWidth(width: number): boolean {
return width < BREAKPOINTS.MOBILE_MAX_WIDTH;
}
/**
* Check if screen width is tablet
*/
export function isTabletWidth(width: number): boolean {
return (
width >= BREAKPOINTS.MOBILE_MAX_WIDTH &&
width <= BREAKPOINTS.TABLET_MAX_WIDTH
);
}
/**
* Check if screen width is desktop
*/
export function isDesktopWidth(width: number): boolean {
return width >= BREAKPOINTS.DESKTOP_MIN_WIDTH;
}