From 549867821a4ee5ef969d86f8d1b246354aa1a573 Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 18 Oct 2025 02:58:35 +0200 Subject: [PATCH] - refactorizadas y comentadas las clases bases , cvalue, cprovider, y cerror --- src/{lib => }/assets/favicon.svg | 0 src/lib/config/cerror.ts | 253 +++++++++++++++++++ src/lib/config/cprovider.ts | 403 +++++++++++++++++++++++++++++++ src/lib/config/cvalue.ts | 148 ++++++++++++ src/lib/index.ts | 1 - src/lib/types/cfg/provider.ts | 12 +- src/routes/+layout.svelte | 2 +- 7 files changed, 811 insertions(+), 8 deletions(-) rename src/{lib => }/assets/favicon.svg (100%) create mode 100644 src/lib/config/cerror.ts create mode 100644 src/lib/config/cprovider.ts create mode 100644 src/lib/config/cvalue.ts delete mode 100644 src/lib/index.ts diff --git a/src/lib/assets/favicon.svg b/src/assets/favicon.svg similarity index 100% rename from src/lib/assets/favicon.svg rename to src/assets/favicon.svg diff --git a/src/lib/config/cerror.ts b/src/lib/config/cerror.ts new file mode 100644 index 0000000..21e7737 --- /dev/null +++ b/src/lib/config/cerror.ts @@ -0,0 +1,253 @@ +// cerror.ts + +/** + * Clase de error general para normalizar el manejo de errores en toda la aplicación. + * + * @extends Error + * + * @description + * CError proporciona una estructura consistente para errores con: + * - **code**: Identificador único del error (útil para i18n y lógica de negocio) + * - **message**: Descripción legible del error (opcional) + * - **cause**: Error original que causó este error (para stack trace completo) + * - **details**: Metadatos adicionales como contexto (serializable para logs) + * + * Todos los módulos del sistema usan CError para facilitar debugging, + * logging estructurado y manejo de errores tipado. + * + * @example + * // Error básico con código + * throw new CError('user_not_found', 'User does not exist'); + * + * @example + * // Con causa original (wrapping de errores) + * try { + * await database.query('SELECT * FROM users'); + * } catch (err) { + * throw new CError( + * 'database_error', + * 'Failed to fetch users', + * err instanceof Error ? err : undefined + * ); + * } + * + * @example + * // Con detalles contextuales + * throw new CError( + * 'validation_failed', + * 'Invalid user input', + * undefined, + * { field: 'email', value: 'invalid-email', expected: 'valid email format' } + * ); + * + * @example + * // Manejo tipado de errores + * try { + * await riskyOperation(); + * } catch (error) { + * if (error instanceof CError) { + * switch (error.code) { + * case 'timeout': + * console.error('Operation timed out:', error.details?.timeout); + * break; + * case 'unauthorized': + * redirectToLogin(); + * break; + * default: + * logError(error); + * } + * } + * } + * + * @example + * // Logging estructurado + * function logError(error: CError) { + * console.error({ + * code: error.code, + * message: error.message, + * details: error.details, + * stack: error.stack, + * cause: error.cause?.message + * }); + * } + * + * @example + * // Con cprovider (timeout) + * // cprovider lanza CError con code 'provider_timeout' + * try { + * const data = await cprovide.get(slowProvider, 1000); + * } catch (error) { + * if (error instanceof CError && error.code === 'provider_timeout') { + * console.error('Timeout after', error.details?.timeout, 'ms'); + * } + * } + */ +export class CError extends Error { + /** + * Código único que identifica el tipo de error. + * + * @description + * Usado para: + * - Lógica de negocio (switch/if por tipo de error) + * - Internacionalización (mapear código a mensaje traducido) + * - Métricas y monitoring (agrupar errores por código) + * - Testing (verificar errores específicos) + * + * @example + * // Códigos comunes en el sistema + * 'provider_timeout' // cprovider: timeout excedido + * 'provider_resolve' // cprovider: error al resolver + * 'undefined_provider' // cprovider: provider es undefined + * 'invalid_cvalue' // cvalue: tipo inválido + * 'memoize_resolution_failed' // cprovider: fallo en memoize + */ + public code: string; + + /** + * Error original que causó este error (opcional). + * + * @description + * Permite preservar el stack trace completo cuando se wrappean errores. + * Útil para debugging y para entender la cadena de errores. + * + * @example + * try { + * JSON.parse(invalidJson); + * } catch (err) { + * throw new CError( + * 'parse_error', + * 'Failed to parse configuration', + * err instanceof Error ? err : undefined + * ); + * } + * // Ahora puedes acceder al SyntaxError original via error.cause + */ + public cause?: Error; + + /** + * Metadatos adicionales sobre el error (opcional). + * + * @description + * Objeto serializable con información contextual del error. + * Útil para: + * - Debugging (ver valores que causaron el error) + * - Logging estructurado (enviar a servicios de monitoreo) + * - UI (mostrar información específica al usuario) + * + * **IMPORTANTE**: Solo incluir datos serializables (JSON-safe). + * Evitar referencias circulares, funciones, o datos sensibles. + * + * @example + * // Contexto de validación + * { field: 'email', value: 'test', rule: 'must be valid email' } + * + * @example + * // Contexto de timeout + * { timeout: 5000, operation: 'fetch_user', userId: 123 } + * + * @example + * // Contexto de tipo inválido + * { expected: 'CValue', received: 'object', value: '{"invalid":true}' } + */ + public details?: Record; + + /** + * Crea una nueva instancia de CError. + * + * @param {string} code - Código único que identifica el tipo de error + * @param {string} [message] - Mensaje descriptivo del error (opcional) + * @param {Error} [cause] - Error original que causó este error (opcional) + * @param {Record} [details] - Metadatos adicionales serializables (opcional) + * + * @example + * // Solo código y mensaje + * new CError('not_found', 'Resource not found'); + * + * @example + * // Código sin mensaje (útil cuando el código es autoexplicativo) + * new CError('network_error'); + * + * @example + * // Con todos los parámetros + * new CError( + * 'validation_error', + * 'Email format is invalid', + * undefined, + * { field: 'email', value: inputEmail, pattern: EMAIL_REGEX } + * ); + * + * @example + * // Wrapping error con causa + * try { + * await fetch('/api/data'); + * } catch (err) { + * throw new CError( + * 'fetch_failed', + * 'Could not retrieve data from API', + * err instanceof Error ? err : new Error(String(err)) + * ); + * } + */ + constructor( + code: string, + message?: string, + cause?: Error, + details?: Record + ) { + super(message); + this.name = 'AppError'; + this.code = code; + this.cause = cause; + this.details = details; + } +} + +/** + * @example + * // Pattern: Factory functions para errores comunes + * export class ValidationError extends CError { + * constructor(field: string, value: any, rule: string) { + * super( + * 'validation_error', + * `Validation failed for field "${field}"`, + * undefined, + * { field, value, rule } + * ); + * } + * } + * + * // Uso + * throw new ValidationError('email', userInput, 'must be valid email'); + * + * @example + * // Pattern: Helper para convertir errores desconocidos + * export function toCError(error: unknown, code: string = 'unknown_error'): CError { + * if (error instanceof CError) { + * return error; + * } + * if (error instanceof Error) { + * return new CError(code, error.message, error); + * } + * return new CError(code, String(error)); + * } + * + * // Uso + * try { + * riskyOperation(); + * } catch (err) { + * throw toCError(err, 'operation_failed'); + * } + * + * @example + * // Pattern: Type guard para CError + * export function isCError(error: unknown): error is CError { + * return error instanceof CError; + * } + * + * // Uso + * catch (error) { + * if (isCError(error) && error.code === 'timeout') { + * handleTimeout(error); + * } + * } + */ \ No newline at end of file diff --git a/src/lib/config/cprovider.ts b/src/lib/config/cprovider.ts new file mode 100644 index 0000000..9b90296 --- /dev/null +++ b/src/lib/config/cprovider.ts @@ -0,0 +1,403 @@ +// cprovider.ts +import { checkTypeCValue, type CValue } from './cvalue'; +import { CError } from '$lib/config/cerror'; + +/** + * Tipo unificado que maneja valores síncronos y asíncronos de manera consistente. + * + * @template T - Tipo del valor a resolver + * + * @description + * Puede ser: + * - Un valor directo de tipo T (síncrono) + * - Una función que retorna T o Promise (síncrona o asíncrona) + * - Una Promise directa + * + * @example + * // Valor directo + * const directValue: CProvider = 42; + * + * // Función síncrona + * const syncFunction: CProvider = () => 'hello'; + * + * // Función asíncrona + * const asyncFunction: CProvider = async () => true; + * + * // Promise directa + * const promise: CProvider = Promise.resolve(123); + */ +export type CProvider = T | (() => T | Promise) | Promise; + +/** + * Provider que resuelve a un valor primitivo (string | boolean | number) + */ +export type CValueProvider = CProvider; + +/** + * Provider que resuelve a string + */ +export type StringProvider = CProvider; + +/** + * Provider que resuelve a boolean + */ +export type BooleanProvider = CProvider; + +/** + * Provider que resuelve a number + */ +export type NumberProvider = CProvider; + +/** + * Verifica si un valor es Promise-like (tiene método .then) + * + * @template T + * @param {any} value - Valor a verificar + * @returns {value is Promise} true si el valor es una Promise + * @private + */ +function isPromise(value: any): value is Promise { + return value && typeof value.then === 'function'; +} + +/** + * Aplica un timeout a una Promise + * + * @template T + * @param {Promise} promise - Promise a la que aplicar timeout + * @param {number} timeoutMs - Tiempo máximo en milisegundos + * @returns {Promise} Promise que se rechaza si se excede el timeout + * @throws {CError} Con código 'provider_timeout' si se excede el tiempo límite + * @private + * + * @example + * const result = await withTimeout( + * fetch('/api/data'), + * 5000 // 5 segundos + * ); + */ +async function withTimeout(promise: Promise, timeoutMs: number): Promise { + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject( + new CError( + 'provider_timeout', + 'Cprovider resolution timed out', + undefined, + { timeout: timeoutMs } + ) + ), timeoutMs) + ); + return Promise.race([promise, timeoutPromise]); +} + +/** + * Timeout por defecto en milisegundos + * - Producción: 5000ms (5 segundos) + * - Desarrollo: Infinity (sin timeout) + */ +const DEFAULT_TIMEOUT = process.env.NODE_ENV === 'production' ? 5000 : Infinity; + +/** + * API principal para resolver providers de manera uniforme + */ +interface CProvide { + /** + * Timeout por defecto para todas las operaciones + */ + defaultTimeout: number; + + /** + * Resuelve un provider de forma asíncrona, manejando valores directos, + * funciones síncronas/asíncronas y promesas de manera uniforme + * + * @template T + * @param {CProvider | undefined} p - Provider a resolver + * @param {number} [timeoutMs=DEFAULT_TIMEOUT] - Timeout opcional en milisegundos + * @returns {Promise} Valor resuelto + * @throws {CError} 'undefined_provider' si p es undefined + * @throws {CError} 'provider_resolve' si falla la resolución + * @throws {CError} 'provider_timeout' si se excede el timeout + * + * @example + * // Valor directo + * const value = await cprovide.get(42); // 42 + * + * @example + * // Función síncrona + * const result = await cprovide.get(() => 'hello'); // 'hello' + * + * @example + * // Función asíncrona con timeout + * const data = await cprovide.get( + * async () => fetchData(), + * 2000 // 2 segundos máximo + * ); + * + * @example + * // Promise directa + * const num = await cprovide.get(Promise.resolve(123)); // 123 + */ + get(p: CProvider | undefined, timeoutMs?: number): Promise; + + /** + * Resuelve un provider y valida que el resultado sea un CValue válido + * (string | boolean | number) + * + * @param {CValueProvider | undefined} p - Provider a resolver + * @param {number} [timeoutMs] - Timeout opcional en milisegundos + * @returns {Promise} Valor primitivo validado + * @throws {CError} Si el valor no es string, boolean o number + * @throws {CError} Errores de resolución de get() + * + * @example + * const str = await cprovide.asCValue('test'); // 'test' + * const num = await cprovide.asCValue(() => 42); // 42 + * const bool = await cprovide.asCValue(Promise.resolve(true)); // true + * + * @example + * // Lanza error - tipo inválido + * await cprovide.asCValue({ invalid: true }); // CError + */ + asCValue(p: CValueProvider | undefined, timeoutMs?: number): Promise; + + /** + * Resuelve un provider a string + * + * @param {StringProvider | undefined} p - Provider a resolver + * @param {number} [timeoutMs] - Timeout opcional en milisegundos + * @returns {Promise} String resuelto + * @throws {CError} Errores de resolución de get() + * + * @example + * const name = await cprovide.asString(() => 'John'); // 'John' + */ + asString(p: StringProvider | undefined, timeoutMs?: number): Promise; + + /** + * Resuelve un provider a number + * + * @param {NumberProvider | undefined} p - Provider a resolver + * @param {number} [timeoutMs] - Timeout opcional en milisegundos + * @returns {Promise} Number resuelto + * @throws {CError} Errores de resolución de get() + * + * @example + * const age = await cprovide.asNumber(42); // 42 + * const computed = await cprovide.asNumber(() => 10 + 32); // 42 + */ + asNumber(p: NumberProvider | undefined, timeoutMs?: number): Promise; + + /** + * Resuelve un provider a boolean + * + * @param {BooleanProvider | undefined} p - Provider a resolver + * @param {number} [timeoutMs] - Timeout opcional en milisegundos + * @returns {Promise} Boolean resuelto + * @throws {CError} Errores de resolución de get() + * + * @example + * const isActive = await cprovide.asBoolean(true); // true + * const hasAccess = await cprovide.asBoolean(async () => checkPermissions()); // true/false + */ + asBoolean(p: BooleanProvider | undefined, timeoutMs?: number): Promise; + + /** + * Crea un provider memoizado que cachea el resultado de la primera resolución + * + * @template T + * @param {CProvider} p - Provider a memoizar + * @param {Object} [options] - Opciones de memoización + * @param {number} [options.timeoutMs] - Timeout para la resolución inicial + * @param {number} [options.ttl] - Tiempo de vida del caché en milisegundos + * @returns {CProvider} Nuevo provider que retorna el valor cacheado + * @throws {CError} 'memoize_resolution_failed' si falla la resolución inicial + * + * @example + * // Memoización básica (caché permanente) + * const config = cprovide.memoize(async () => { + * const res = await fetch('/api/config'); + * return res.json(); + * }); + * + * const firstCall = await config(); // Hace fetch + * const secondCall = await config(); // Retorna de caché + * + * @example + * // Con TTL (Time To Live) + * const userData = cprovide.memoize( + * () => fetchUser(), + * { + * timeoutMs: 3000, // 3 segundos timeout + * ttl: 5 * 60 * 1000 // Caché válido por 5 minutos + * } + * ); + */ + memoize(p: CProvider, options?: { timeoutMs?: number; ttl?: number }): CProvider; + + /** + * Resuelve múltiples providers en paralelo + * + * @template T + * @param {T} providers - Objeto con providers como valores + * @param {number} [timeoutMs] - Timeout global opcional en milisegundos + * @returns {Promise<{[K in keyof T]: Awaited>}>} + * Objeto con los valores resueltos + * @throws {CError} Propaga cualquier error de get() + * + * @example + * // Carga paralela de recursos + * const resources = await cprovide.batch({ + * user: () => fetchUser(), + * settings: () => fetchSettings(), + * permissions: async () => checkPermissions(), + * count: 42 + * }, 5000); + * + * console.log(resources); + * // { user: {...}, settings: {...}, permissions: true, count: 42 } + * + * @example + * // Útil para optimizar múltiples llamadas independientes + * const data = await cprovide.batch({ + * id: userId, + * name: () => getName(userId), + * avatar: fetchAvatar(userId) + * }); + */ + batch>>( + providers: T, + timeoutMs?: number + ): Promise<{ [K in keyof T]: Awaited> }>; +} + +/** + * API principal para resolver providers de manera uniforme + * + * @summary + * Proporciona métodos para resolver valores síncronos y asíncronos con una API consistente. + * Todas las funciones retornan Promise para uniformidad, incluso con valores directos. + * + * @see {@link CProvide} para la interfaz completa + * + * @example + * // Configuración + * cprovide.defaultTimeout; // 5000 en producción, Infinity en desarrollo + * + * @example + * // Resolución básica + * const value = await cprovide.get(42); + * const str = await cprovide.get(() => 'hello'); + * const data = await cprovide.get(fetchData()); + * + * @example + * // Con validación de tipos + * const primitive = await cprovide.asCValue('test'); // string | boolean | number + * const name = await cprovide.asString(() => 'John'); + * const age = await cprovide.asNumber(42); + * const active = await cprovide.asBoolean(true); + * + * @example + * // Memoización para operaciones costosas + * const expensiveData = cprovide.memoize( + * async () => computeExpensive(), + * { ttl: 10 * 60 * 1000 } // 10 minutos + * ); + * + * @example + * // Resolución en lote (paralelo) + * const results = await cprovide.batch({ + * user: fetchUser(), + * posts: fetchPosts(), + * stats: () => calculateStats() + * }); + */ +export const cprovide: CProvide = { + defaultTimeout: DEFAULT_TIMEOUT, + + async get(p: CProvider | undefined, timeoutMs = DEFAULT_TIMEOUT): Promise { + if (p === undefined) { + throw new CError('Undefined provider'); + } + try { + if (typeof p === 'function') { + const result = (p as () => T | Promise)(); + if (isPromise(result)) { + return timeoutMs !== Infinity ? await withTimeout(result, timeoutMs) : await result; + } + return result; + } + if (isPromise(p)) { + return timeoutMs !== Infinity ? await withTimeout(p, timeoutMs) : await p; + } + return p; + } catch (cause) { + throw new CError( + 'provider_resolve', + `Error resolving provider: ${cause instanceof Error ? cause.message : 'Unknown error'}`, + cause instanceof Error ? cause : undefined + ); + } + }, + + async asCValue(p: CValueProvider | undefined, timeoutMs?: number): Promise { + const value = await cprovide.get(p, timeoutMs); + checkTypeCValue(value); + return value; + }, + + async asString(p: StringProvider | undefined, timeoutMs?: number): Promise { + return cprovide.get(p, timeoutMs); + }, + + async asNumber(p: NumberProvider | undefined, timeoutMs?: number): Promise { + return cprovide.get(p, timeoutMs); + }, + + async asBoolean(p: BooleanProvider | undefined, timeoutMs?: number): Promise { + return cprovide.get(p, timeoutMs); + }, + + memoize(p: CProvider, options: { timeoutMs?: number; ttl?: number } = {}): CProvider { + let cache: T | undefined; + let resolved = false; + let error: CError | undefined; + let expiry: number | null = options.ttl ? Date.now() + options.ttl : null; + + return async () => { + // Verificar validez del caché + if (expiry && Date.now() > expiry) { + resolved = false; + cache = undefined; + error = undefined; + expiry = options.ttl ? Date.now() + options.ttl : null; + } + + if (resolved) { + if (error) throw error; + return cache!; + } + + try { + cache = await cprovide.get(p, options.timeoutMs); + resolved = true; + return cache; + } catch (err) { + throw new CError( + 'memoize_resolution_failed', + 'Memoize resolution failed', + err instanceof Error ? err : undefined + ); + } + }; + }, + + async batch>>( + providers: T, + timeoutMs?: number + ): Promise<{ [K in keyof T]: Awaited> }> { + const entries = await Promise.all( + Object.entries(providers).map(async ([key, p]) => [key, await cprovide.get(p as any, timeoutMs)]) + ); + return Object.fromEntries(entries) as any; + }, +}; \ No newline at end of file diff --git a/src/lib/config/cvalue.ts b/src/lib/config/cvalue.ts new file mode 100644 index 0000000..eb7cf7d --- /dev/null +++ b/src/lib/config/cvalue.ts @@ -0,0 +1,148 @@ +// cvalue.ts +import { CError } from './cerror'; + +/** + * Tipo unión que representa valores primitivos permitidos en el sistema. + * + * @description + * CValue es la unidad básica de datos primitivos que el sistema puede manejar. + * Restringe los valores a tipos simples y serializables. + * + * @example + * const name: CValue = 'John'; // string ✓ + * const age: CValue = 30; // number ✓ + * const active: CValue = true; // boolean ✓ + * const obj: CValue = {}; // ✗ Error de compilación + * const arr: CValue = []; // ✗ Error de compilación + */ +export type CValue = string | number | boolean; + +/** + * Verifica si un valor es un CValue válido en runtime. + * + * @param {any} val - Valor a verificar + * @returns {boolean} `true` si el valor es string, number o boolean; `false` en caso contrario + * + * @description + * Esta función realiza una verificación de tipo en tiempo de ejecución. + * Es útil para validar datos provenientes de fuentes externas (API, localStorage, etc.) + * antes de usarlos como CValue. + * + * No lanza errores, solo retorna un booleano. + * + * @example + * // Casos válidos + * isCValue('hello'); // true + * isCValue(42); // true + * isCValue(true); // true + * isCValue(false); // true + * isCValue(0); // true + * isCValue(''); // true (string vacío es válido) + * + * @example + * // Casos inválidos + * isCValue(null); // false + * isCValue(undefined); // false + * isCValue({}); // false + * isCValue([]); // false + * isCValue(() => {}); // false + * isCValue(Symbol('test')); // false + * + * @example + * // Uso práctico con validación + * function processValue(input: unknown) { + * if (isCValue(input)) { + * // TypeScript aún no sabe que input es CValue aquí + * // Para type narrowing, usa checkTypeCValue + * console.log('Valid CValue:', input); + * } else { + * console.error('Invalid input'); + * } + * } + * + * @see {@link checkTypeCValue} para validación con type assertion + */ +export function isCValue(val: any): boolean { + return typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number'; +} + +/** + * Valida que un valor sea un CValue y realiza type assertion. + * + * @param {unknown} value - Valor a validar + * @throws {CError} Con código 'invalid_cvalue' si el valor no es un CValue válido + * + * @description + * Esta función es un **type guard con assertion** (asserts). Si no lanza error, + * TypeScript garantiza que el valor es de tipo CValue en el scope posterior. + * + * Útil cuando necesitas validar y estrechar el tipo en una sola operación. + * + * @example + * // Uso básico con type narrowing automático + * function processUnknown(input: unknown){ + * checkTypeCValue(input); + * // Después de esta línea, TypeScript sabe que input es CValue + * const upper = input.toUpperCase(); // ✓ Si es string + * const sum = input + 10; // ✓ Si es number + * } + * * + * @example + * // Validación de datos externos + * async function loadConfig() { + * const rawValue = localStorage.getItem('config'); + * const parsed = JSON.parse(rawValue); + * + * checkTypeCValue(parsed.timeout); + * // Ahora parsed.timeout es garantizado CValue + * return parsed.timeout; + * } + * + * @example + * // Manejo de errores + * try { + * const userInput: unknown = getUserInput(); + * checkTypeCValue(userInput); + * // userInput es CValue aquí + * saveToDatabase(userInput); + * } catch (error) { + * if (error instanceof CError && error.code === 'invalid_cvalue') { + * console.error('Invalid value:', error.details?.value); + * } + * } + * + * @example + * // Validación en array + * const values: unknown[] = [1, 'test', true, {}, 'hello']; + * const validValues: CValue[] = []; + * + * for (const val of values) { + * try { + * checkTypeCValue(val); + * validValues.push(val); // val es CValue aquí + * } catch { + * console.warn('Skipping invalid value'); + * } + * } + * + * @example + * // En combinación con cprovide + * import {cprovide} from './cprovider'; + * + * const config = await cprovide.asCValue(() => fetchConfig()); + * + * // cprovide.asCValue usa checkTypeCValue internamente + * + * @see {@link isCValue} para verificación sin lanzar errores + * @see {@link CValue} para el tipo + */ +export function checkTypeCValue(value: unknown): asserts value is CValue { + if (!isCValue(value)) { + throw new CError( + 'invalid_cvalue', + 'Invalid cvalue', + undefined, + { value: JSON.stringify(value) } + ); + } +} \ No newline at end of file diff --git a/src/lib/index.ts b/src/lib/index.ts deleted file mode 100644 index 856f2b6..0000000 --- a/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/types/cfg/provider.ts b/src/lib/types/cfg/provider.ts index 67d47f1..5f35f55 100644 --- a/src/lib/types/cfg/provider.ts +++ b/src/lib/types/cfg/provider.ts @@ -6,23 +6,23 @@ export type Value = string | number | boolean; -export type ValueProvider = Value | (() => Value); -export type AsyncValueProvider = ValueProvider | Promise; +export type Provider = Value | (() => Value); +export type AsyncProvider = Provider | Promise; // ===================== // Providers específicos // ===================== -export type BooleanProvider = boolean | (() => boolean); -export type StringProvider = string | (() => string ); -export type NumberProvider = number | (() => number ); +export type BooleanProvider = Provider; +export type StringProvider = Provider; +export type NumberProvider = Provider; // ===================== // Helper sincrónico // ===================== export const provide = { - value(p: ValueProvider): Value { + value(p: Provider): Value { return typeof p === 'function' ? (p as () => Value)() : p; }, string: (p: ValueProvider) => String(provide.value(p)), diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 8c56a3c..86f5a85 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,6 +1,6 @@