diff --git a/app/(setup)/login/components/main-components/AuthForm.tsx b/app/(setup)/login/components/main-components/AuthForm.tsx index 40fde5e2..42a8776c 100644 --- a/app/(setup)/login/components/main-components/AuthForm.tsx +++ b/app/(setup)/login/components/main-components/AuthForm.tsx @@ -8,7 +8,8 @@ import { SignInForm } from '@/app/(setup)/login/components/sing-in/SignInForm'; import { SignUpForm } from '@/app/(setup)/login/components/sing-up/SignUpForm'; import { ForgotPasswordForm } from '@/app/(setup)/login/components/forgot-password/ForgotPasswordForm'; import { VerificationForm } from '@/app/(setup)/login/components/verification-form/VerificationForm'; -import { signInWithRedirect } from 'aws-amplify/auth'; +import { signInWithRedirect, AuthError } from 'aws-amplify/auth'; +import { getSignInErrorMessage } from '@/lib/auth/auth-error-messages'; type AuthState = 'signin' | 'signup' | 'forgot-password' | 'verification'; @@ -16,6 +17,11 @@ export function AuthForm() { const [authState, setAuthState] = useState('signin'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [googleError, setGoogleError] = useState(null); + + const clearGoogleError = useCallback(() => { + setGoogleError(null); + }, []); const renderForm = useCallback(() => { switch (authState) { @@ -24,11 +30,13 @@ export function AuthForm() { { setAuthState('forgot-password'); + clearGoogleError(); }} onVerificationNeeded={(email, password) => { setEmail(email); setPassword(password); setAuthState('verification'); + clearGoogleError(); }} /> ); @@ -39,15 +47,32 @@ export function AuthForm() { setEmail(email); setPassword(password); setAuthState('verification'); + clearGoogleError(); }} /> ); case 'forgot-password': - return setAuthState('signin')} />; + return ( + { + setAuthState('signin'); + clearGoogleError(); + }} + /> + ); case 'verification': - return setAuthState('signup')} />; + return ( + { + setAuthState('signup'); + clearGoogleError(); + }} + /> + ); } - }, [authState, email, password]); + }, [authState, email, password, clearGoogleError]); const handleLoginClick = async () => { try { @@ -56,6 +81,10 @@ export function AuthForm() { }); } catch (error) { console.error('Error during sign-in:', error); + + if (error instanceof AuthError) { + setGoogleError(getSignInErrorMessage(error)); + } } }; @@ -117,6 +146,9 @@ export function AuthForm() { + {/* Mostrar error de Google Auth */} + {googleError &&
{googleError}
} + {(authState === 'signin' || authState === 'signup') && ( <>
@@ -155,14 +187,26 @@ export function AuthForm() { {authState === 'signin' ? ( <> ¿No tienes una cuenta?{' '} - ) : ( <> ¿Ya tienes una cuenta?{' '} - diff --git a/app/(setup)/login/hooks/SignIn.ts b/app/(setup)/login/hooks/SignIn.ts index 8ac88ba7..7ab18e9f 100644 --- a/app/(setup)/login/hooks/SignIn.ts +++ b/app/(setup)/login/hooks/SignIn.ts @@ -1,4 +1,4 @@ -import { resendSignUpCode, signIn, type SignInInput } from 'aws-amplify/auth'; +import { resendSignUpCode, signIn, type SignInInput, AuthError } from 'aws-amplify/auth'; import { useRouter } from 'next/navigation'; import { useCallback, useState } from 'react'; import { useAuth } from '@/context/hooks/useAuth'; @@ -14,11 +14,6 @@ interface UseAuthReturn { resendConfirmationCode: (email: string) => Promise; } -interface AuthError { - code: string; - message: string; -} - interface UseAuthProps { redirectPath?: string; onVerificationNeeded?: (email: string, password: string) => void; diff --git a/app/[store]/page.tsx b/app/[store]/page.tsx index 39384d61..4725c65c 100644 --- a/app/[store]/page.tsx +++ b/app/[store]/page.tsx @@ -1,20 +1,11 @@ -import DevAutoReloadScript from '@/app/[store]/components/DevAutoReloadScript'; -import { generateStoreMetadata, getCachedRenderResult, isAssetPath } from '@/app/[store]/lib/store-page-utils'; -import { logger } from '@fasttify/liquid-forge'; -import { storeViewsTracker } from '@fasttify/liquid-forge'; -import { domainResolver } from '@fasttify/liquid-forge'; +import DevAutoReloadScript from '@/app/[store]/src/components/DevAutoReloadScript'; +import { StorePageController } from '@/app/[store]/src/_lib/controllers/store-page-controller'; +import { StoreMetadataController } from '@/app/[store]/src/_lib/controllers/store-metadata-controller'; +import { isAssetPath } from '@/app/[store]/src/lib/store-page-utils'; import { Metadata } from 'next'; -import { headers } from 'next/headers'; -import { notFound } from 'next/navigation'; -import { Suspense } from 'react'; export const dynamic = 'force-dynamic'; -/** - * Función cacheada usando React.cache() que persiste entre generateMetadata y StorePage - * Esta es la forma oficial de Next.js para compartir datos entre estas funciones - */ - interface StorePageProps { params: Promise<{ store: string; @@ -28,54 +19,14 @@ interface StorePageProps { /** * Página principal de tienda con SSR * Maneja todas las rutas de tienda: /, /products/slug, /collections/slug, etc + * + * Esta página solo orquesta la llamada al controller, delegando toda la lógica */ -export default async function StorePage({ params, searchParams }: StorePageProps) { - const requestHeaders = await headers(); - const xOriginalHost = requestHeaders.get('x-original-host'); - - const hostname = - xOriginalHost || - (requestHeaders.get('cf-connecting-ip') - ? requestHeaders.get('x-forwarded-host') || requestHeaders.get('host') || '' - : requestHeaders.get('host') || ''); - - const cleanHostname = hostname?.split(':')[0] || ''; - - const isMainDomain = - cleanHostname === 'fasttify.com' || cleanHostname === 'www.fasttify.com' || cleanHostname === 'localhost'; - - if (isMainDomain) { - notFound(); - } - - const { store } = await params; - const awaitedSearchParams = await searchParams; - const path = awaitedSearchParams.path || '/'; - - if (isAssetPath(path)) { - notFound(); - } +export default async function StorePage(props: StorePageProps) { + const controller = new StorePageController(); try { - const domain = store.includes('.') ? store : `${store}.fasttify.com`; - - const result = await getCachedRenderResult(domain, path, awaitedSearchParams); - - // Trackear la vista de la tienda de forma asíncrona - try { - const headersObj = Object.fromEntries(requestHeaders.entries()); - const fullUrl = `https://${cleanHostname}${path}`; - - const storeRecord = await domainResolver.resolveStoreByDomain(domain); - const realStoreId = storeRecord.storeId; - - const viewData = storeViewsTracker.captureStoreView(realStoreId, path, headersObj, fullUrl); - storeViewsTracker.trackStoreView(viewData).catch((trackError) => { - logger.error(`[StorePage] Failed to track store view for ${realStoreId}`, trackError, 'StorePage'); - }); - } catch (trackError) { - logger.error(`[StorePage] Error capturing store view for ${domain}`, trackError, 'StorePage'); - } + const result = await controller.handle(props); return ( <> @@ -84,16 +35,12 @@ export default async function StorePage({ params, searchParams }: StorePageProps ); } catch (error: any) { - logger.error(`Error rendering store page ${store}${path}`, error, 'StorePage'); - + // Si hay HTML de error disponible, renderizarlo if (error.html) { return
; } - if (error.type === 'STORE_NOT_FOUND' && error.statusCode === 404) { - notFound(); - } - + // Re-lanzar otros errores throw error; } } @@ -101,17 +48,16 @@ export default async function StorePage({ params, searchParams }: StorePageProps /** * Genera metadata SEO para la página */ -export async function generateMetadata({ params, searchParams }: StorePageProps): Promise { - const { store } = await params; - const awaitedSearchParams = await searchParams; - const path = awaitedSearchParams.path || '/'; +export async function generateMetadata(props: StorePageProps): Promise { + const { path } = await props.searchParams; - if (isAssetPath(path)) { + if (path && isAssetPath(path)) { return { title: 'Asset', description: 'Static asset', }; } - return generateStoreMetadata(store, path, awaitedSearchParams); + const controller = new StoreMetadataController(); + return controller.handle(props); } diff --git a/app/[store]/src/_lib/constants/store.constants.ts b/app/[store]/src/_lib/constants/store.constants.ts new file mode 100644 index 00000000..fbac1bac --- /dev/null +++ b/app/[store]/src/_lib/constants/store.constants.ts @@ -0,0 +1,87 @@ +/** + * Constantes para el módulo de tiendas + */ + +/** + * Configuración de dominios + */ +export const DOMAIN_CONFIG = { + BASE_DOMAIN: 'fasttify.com', + WWW_DOMAIN: 'www.fasttify.com', + LOCALHOST: 'localhost', +} as const; + +/** + * Header names para extracción de hostname + */ +export const HEADER_NAMES = { + X_ORIGINAL_HOST: 'x-original-host', + X_FORWARDED_HOST: 'x-forwarded-host', + HOST: 'host', + CF_CONNECTING_IP: 'cf-connecting-ip', +} as const; + +/** + * Protocolos y separadores + */ +export const URL_CONFIG = { + PROTOCOL: 'https://', + PORT_SEPARATOR: ':', +} as const; + +/** + * Tipos de errores + */ +export const ERROR_TYPES = { + STORE_NOT_FOUND: 'STORE_NOT_FOUND', +} as const; + +/** + * Códigos de estado HTTP + */ +export const HTTP_STATUS = { + NOT_FOUND: 404, +} as const; + +/** + * Assets comunes que los navegadores solicitan automáticamente + */ +export const COMMON_ASSETS = [ + 'favicon.ico', + 'robots.txt', + 'sitemap.xml', + 'apple-touch-icon.png', + 'manifest.json', +] as const; + +/** + * Extensiones de archivos consideradas como assets estáticos + */ +export const ASSET_EXTENSIONS = [ + '.png', + '.jpg', + '.jpeg', + '.gif', + '.svg', + '.webp', + '.ico', + '.css', + '.js', + '.woff', + '.woff2', + '.ttf', + '.eot', + '.otf', + '.json', + '.xml', + '.txt', +] as const; + +/** + * Patrones de paths que indican assets + */ +export const ASSET_PATH_PATTERNS = { + ASSETS_FOLDER: '/assets/', + NEXT_FOLDER: '/_next/', + ICONS_FOLDER: '/icons/', +} as const; diff --git a/app/[store]/src/_lib/controllers/store-metadata-controller.ts b/app/[store]/src/_lib/controllers/store-metadata-controller.ts new file mode 100644 index 00000000..9919d319 --- /dev/null +++ b/app/[store]/src/_lib/controllers/store-metadata-controller.ts @@ -0,0 +1,23 @@ +import { Metadata } from 'next'; +import { generateStoreMetadata as generateMetadataUtil } from '@/app/[store]/src/lib/store-page-utils'; + +interface StoreMetadataProps { + params: Promise<{ store: string }>; + searchParams: Promise<{ path?: string; [key: string]: string | string[] | undefined }>; +} + +/** + * Controlador para generar metadata SEO de las páginas de tiendas + */ +export class StoreMetadataController { + /** + * Genera metadata SEO para la página + */ + async handle(props: StoreMetadataProps): Promise { + const { store } = await props.params; + const awaitedSearchParams = await props.searchParams; + const path = awaitedSearchParams.path || '/'; + + return generateMetadataUtil(store, path, awaitedSearchParams); + } +} diff --git a/app/[store]/src/_lib/controllers/store-page-controller.ts b/app/[store]/src/_lib/controllers/store-page-controller.ts new file mode 100644 index 00000000..14cd3d97 --- /dev/null +++ b/app/[store]/src/_lib/controllers/store-page-controller.ts @@ -0,0 +1,110 @@ +import { logger } from '@fasttify/liquid-forge'; +import { headers } from 'next/headers'; +import { notFound } from 'next/navigation'; +import { DomainService } from '@/app/[store]/src/_lib/services/domain.service'; +import { StoreTrackingService } from '@/app/[store]/src/_lib/services/store-tracking.service'; +import { getCachedRenderResult, isAssetPath } from '@/app/[store]/src/lib/store-page-utils'; +import { HEADER_NAMES, URL_CONFIG, ERROR_TYPES, HTTP_STATUS } from '@/app/[store]/src/_lib/constants/store.constants'; + +interface StorePageProps { + params: Promise<{ store: string }>; + searchParams: Promise<{ path?: string; [key: string]: string | string[] | undefined }>; +} + +/** + * Controlador para manejar el renderizado de páginas de tiendas + * Contiene toda la lógica de negocio, dejando page.tsx limpio + */ +export class StorePageController { + private domainService: DomainService; + private storeTrackingService: StoreTrackingService; + + constructor() { + this.domainService = new DomainService(); + this.storeTrackingService = new StoreTrackingService(); + } + + /** + * Maneja la petición de renderizado de una página de tienda + */ + async handle(props: StorePageProps) { + const requestHeaders = await headers(); + + // Obtener hostname + const hostname = this.getHostname(requestHeaders); + const { PORT_SEPARATOR } = URL_CONFIG; + const cleanHostname = hostname?.split(PORT_SEPARATOR)[0] || ''; + + // Validar dominio principal + if (this.domainService.isMainDomain(cleanHostname)) { + notFound(); + } + + const { store } = await props.params; + const awaitedSearchParams = await props.searchParams; + const path = awaitedSearchParams.path || '/'; + + // Validar si el parámetro store es un asset común (favicon.ico, robots.txt, etc.) + if (this.domainService.isCommonAsset(store)) { + notFound(); + } + + // Validar path de assets + if (isAssetPath(path)) { + notFound(); + } + + // Resolver dominio + const domain = this.domainService.resolveDomainFromParam(store); + + try { + // Renderizar página + const result = await getCachedRenderResult(domain, path, awaitedSearchParams); + + // Trackear vista de forma asíncrona + await this.trackStoreView(domain, path, cleanHostname, requestHeaders); + + return { html: result.html }; + } catch (error: any) { + // Si es 404 de tienda no encontrada, lanzar notFound + if (error.type === ERROR_TYPES.STORE_NOT_FOUND && error.statusCode === HTTP_STATUS.NOT_FOUND) { + notFound(); + } + + // Re-lanzar el error para que page.tsx lo maneje + throw error; + } + } + + /** + * Obtiene el hostname desde los headers + */ + private getHostname(requestHeaders: Headers): string { + const { X_ORIGINAL_HOST, CF_CONNECTING_IP, X_FORWARDED_HOST, HOST } = HEADER_NAMES; + + const xOriginalHost = requestHeaders.get(X_ORIGINAL_HOST); + + return ( + xOriginalHost || + (requestHeaders.get(CF_CONNECTING_IP) + ? requestHeaders.get(X_FORWARDED_HOST) || requestHeaders.get(HOST) || '' + : requestHeaders.get(HOST) || '') + ); + } + + /** + * Trackea la vista de la tienda + */ + private async trackStoreView(domain: string, path: string, hostname: string, requestHeaders: Headers): Promise { + try { + const { PROTOCOL } = URL_CONFIG; + const headersObj = Object.fromEntries(requestHeaders.entries()); + const fullUrl = `${PROTOCOL}${hostname}${path}`; + + await this.storeTrackingService.trackStoreView(domain, path, headersObj, fullUrl); + } catch (error) { + // El tracking falla silenciosamente para no afectar el renderizado + logger.error(`[StorePageController] Error in tracking for ${domain}`, error, 'StorePageController'); + } + } +} diff --git a/app/[store]/src/_lib/services/domain.service.ts b/app/[store]/src/_lib/services/domain.service.ts new file mode 100644 index 00000000..72a3135e --- /dev/null +++ b/app/[store]/src/_lib/services/domain.service.ts @@ -0,0 +1,30 @@ +import { DOMAIN_CONFIG, COMMON_ASSETS } from '@/app/[store]/src/_lib/constants/store.constants'; + +/** + * Servicio para manejar la lógica de dominios + */ +export class DomainService { + /** + * Resuelve el dominio de la tienda a partir del parámetro store + */ + resolveDomainFromParam(store: string): string { + const { BASE_DOMAIN } = DOMAIN_CONFIG; + return store.includes('.') ? store : `${store}.${BASE_DOMAIN}`; + } + + /** + * Verifica si el dominio es el dominio principal (no debe ser renderizado) + */ + isMainDomain(hostname: string): boolean { + const { BASE_DOMAIN, WWW_DOMAIN, LOCALHOST } = DOMAIN_CONFIG; + return hostname === BASE_DOMAIN || hostname === WWW_DOMAIN || hostname === LOCALHOST; + } + + /** + * Verifica si el parámetro store es un asset común (favicon.ico, robots.txt, etc.) + * Estos son solicitados automáticamente por navegadores y no deberían procesarse como tiendas + */ + isCommonAsset(store: string): boolean { + return COMMON_ASSETS.includes(store as any); + } +} diff --git a/app/[store]/src/_lib/services/store-tracking.service.ts b/app/[store]/src/_lib/services/store-tracking.service.ts new file mode 100644 index 00000000..9f3916cb --- /dev/null +++ b/app/[store]/src/_lib/services/store-tracking.service.ts @@ -0,0 +1,25 @@ +import { storeViewsTracker, logger, domainResolver } from '@fasttify/liquid-forge'; + +/** + * Servicio para manejar el tracking de vistas de tiendas + */ +export class StoreTrackingService { + /** + * Captura y registra una vista de la tienda de forma asíncrona + */ + async trackStoreView(domain: string, path: string, headers: Record, fullUrl: string): Promise { + try { + const storeRecord = await domainResolver.resolveStoreByDomain(domain); + const realStoreId = storeRecord.storeId; + + const viewData = storeViewsTracker.captureStoreView(realStoreId, path, headers, fullUrl); + + // Trackear de forma asíncrona (fire and forget) + storeViewsTracker.trackStoreView(viewData).catch((trackError) => { + logger.error(`Failed to track store view for ${realStoreId}`, trackError, 'StoreTrackingService'); + }); + } catch (trackError) { + logger.error(`Error capturing store view for ${domain}`, trackError, 'StoreTrackingService'); + } + } +} diff --git a/app/[store]/components/DevAutoReloadScript.tsx b/app/[store]/src/components/DevAutoReloadScript.tsx similarity index 100% rename from app/[store]/components/DevAutoReloadScript.tsx rename to app/[store]/src/components/DevAutoReloadScript.tsx diff --git a/app/[store]/lib/store-page-utils.ts b/app/[store]/src/lib/store-page-utils.ts similarity index 84% rename from app/[store]/lib/store-page-utils.ts rename to app/[store]/src/lib/store-page-utils.ts index d6e5901d..33a85086 100644 --- a/app/[store]/lib/store-page-utils.ts +++ b/app/[store]/src/lib/store-page-utils.ts @@ -1,34 +1,21 @@ import { storeRenderer, logger } from '@fasttify/liquid-forge'; import { Metadata } from 'next'; import { cache } from 'react'; +import { ASSET_EXTENSIONS, ASSET_PATH_PATTERNS, DOMAIN_CONFIG } from '@/app/[store]/src/_lib/constants/store.constants'; /** * Verifica si el path corresponde a un asset estático para evitar procesarlo como una página de tienda. */ export function isAssetPath(path: string): boolean { - const assetExtensions = [ - '.png', - '.jpg', - '.jpeg', - '.gif', - '.svg', - '.webp', - '.ico', - '.css', - '.js', - '.woff', - '.woff2', - '.ttf', - '.eot', - ]; - return ( - assetExtensions.some((ext) => path.toLowerCase().endsWith(ext)) || - path.startsWith('/assets/') || - path.startsWith('/_next/') || - path.includes('/icons/') - ); -} + const { ASSETS_FOLDER, NEXT_FOLDER, ICONS_FOLDER } = ASSET_PATH_PATTERNS; + + const hasAssetExtension = ASSET_EXTENSIONS.some((ext) => path.toLowerCase().endsWith(ext)); + const matchesPathPattern = + path.startsWith(ASSETS_FOLDER) || path.startsWith(NEXT_FOLDER) || path.includes(ICONS_FOLDER); + + return hasAssetExtension || matchesPathPattern; +} /** * Función cacheada usando React.cache() que persiste entre generateMetadata y StorePage. * Esta es la forma oficial de Next.js para compartir datos entre estas funciones. @@ -47,7 +34,8 @@ export async function generateStoreMetadata( searchParams: Record ): Promise { try { - const domain = store.includes('.') ? store : `${store}.fasttify.com`; + const { BASE_DOMAIN } = DOMAIN_CONFIG; + const domain = store.includes('.') ? store : `${store}.${BASE_DOMAIN}`; const result = await getCachedRenderResult(domain, path, searchParams); const { metadata } = result; diff --git a/app/api/domain-validation/_lib/controllers/verify-controller.ts b/app/api/domain-validation/_lib/controllers/verify-controller.ts index 3a63263e..0b995969 100644 --- a/app/api/domain-validation/_lib/controllers/verify-controller.ts +++ b/app/api/domain-validation/_lib/controllers/verify-controller.ts @@ -16,8 +16,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getNextCorsHeaders } from '@/lib/utils/next-cors'; -import { CustomDomainService } from '@fasttify/tenant-domains'; -import { SecurityConfig } from '@fasttify/tenant-domains'; +import { CustomDomainService, SecurityConfig } from '@fasttify/tenant-domains'; import { SecureLogger } from '@/lib/utils/secure-logger'; const customDomainService = new CustomDomainService(); diff --git a/app/store/components/notifications/components/NotificationPopover.tsx b/app/store/components/notifications/components/NotificationPopover.tsx index 7e38430e..cb0302ef 100644 --- a/app/store/components/notifications/components/NotificationPopover.tsx +++ b/app/store/components/notifications/components/NotificationPopover.tsx @@ -1,12 +1,12 @@ import { memo, useRef, useMemo } from 'react'; import { Popover, Badge, Spinner, TopBar, Icon } from '@shopify/polaris'; -import { NotificationHeader } from './NotificationHeader'; -import { NotificationList } from './NotificationList'; -import { NotificationFooter } from './NotificationFooter'; -import { NotificationEmptyState } from './NotificationEmptyState'; +import { NotificationHeader } from '@/app/store/components/notifications/components/NotificationHeader'; +import { NotificationList } from '@/app/store/components/notifications/components/NotificationList'; +import { NotificationFooter } from '@/app/store/components/notifications/components/NotificationFooter'; +import { NotificationEmptyState } from '@/app/store/components/notifications/components/NotificationEmptyState'; import { NotificationIcon } from '@shopify/polaris-icons'; -import { useNotificationPopover } from '../hooks'; -import { formatNotificationTime } from '../utils/formatNotificationTime'; +import { useNotificationPopover } from '@/app/store/components/notifications/hooks'; +import { formatNotificationTime } from '@/app/store/components/notifications/utils/formatNotificationTime'; interface NotificationPopoverProps { storeId?: string; diff --git a/app/store/components/notifications/hooks/useNotificationPopover.ts b/app/store/components/notifications/hooks/useNotificationPopover.ts index 56a8467f..6fd6e14b 100644 --- a/app/store/components/notifications/hooks/useNotificationPopover.ts +++ b/app/store/components/notifications/hooks/useNotificationPopover.ts @@ -1,7 +1,7 @@ import { useState, useCallback, useEffect, useRef } from 'react'; import { useNotifications } from '@/app/store/hooks/data/useNotifications'; import useStoreDataStore from '@/context/core/storeDataStore'; -import { useNotificationSound } from './useNotificationSound'; +import { useNotificationSound } from '@/app/store/components/notifications/hooks/useNotificationSound'; interface UseNotificationPopoverProps { storeId?: string; diff --git a/app/store/components/profile/components/SubscriptionSection.tsx b/app/store/components/profile/components/SubscriptionSection.tsx index 01723bf4..926cfb7b 100644 --- a/app/store/components/profile/components/SubscriptionSection.tsx +++ b/app/store/components/profile/components/SubscriptionSection.tsx @@ -3,7 +3,7 @@ import { ExternalIcon, CheckCircleIcon } from '@shopify/polaris-icons'; import { useState, memo } from 'react'; import { plans } from '@/app/(www)/pricing/components/plans'; import type { UserProps } from '@/app/store/components/profile/types'; -import { useSubscriptionLogic } from '@/app/store/hooks/useSubscriptionLogic'; +import { useSubscriptionLogic } from '@/app/store/hooks/utils/useSubscriptionLogic'; interface SubscriptionSectionProps extends UserProps { storeId: string; diff --git a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts index 46373cb5..f89da716 100644 --- a/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts +++ b/app/store/hooks/data/useOrders/notifications/useOrderNotifications.ts @@ -1,8 +1,11 @@ import { useCallback } from 'react'; import { useEmailNotifications } from '@/app/store/hooks/api/useEmailNotifications'; import type { OrderStatus, PaymentStatus } from '@/app/store/hooks/data/useOrders/types'; -import { EmailFormattingUtils } from '@/packages/liquid-forge/services/notifications/client-utils'; -import { getOrderStatus, getPaymentStatus } from '@/packages/liquid-forge/services/notifications/status-translations'; +import { + EmailFormattingUtils, + getOrderStatus, + getPaymentStatus, +} from '@fasttify/liquid-forge/services/notifications/client-utils'; /** * Hook para manejar las notificaciones de email relacionadas con órdenes diff --git a/app/store/hooks/useSubscriptionLogic.ts b/app/store/hooks/utils/useSubscriptionLogic.ts similarity index 100% rename from app/store/hooks/useSubscriptionLogic.ts rename to app/store/hooks/utils/useSubscriptionLogic.ts diff --git a/lib/auth/auth-error-messages.ts b/lib/auth/auth-error-messages.ts index 4fe98c93..b5f483ca 100644 --- a/lib/auth/auth-error-messages.ts +++ b/lib/auth/auth-error-messages.ts @@ -2,18 +2,15 @@ * Mensajes de error centralizados para autenticación */ -export interface AuthError { - code?: string; - message?: string; -} +import { AuthError } from 'aws-amplify/auth'; +export type { AuthError }; /** * Obtiene el mensaje de error traducido para errores de registro */ export function getSignUpErrorMessage(error: AuthError): string { - // Manejo por código de error - if (error.code) { - switch (error.code) { + if (error.name) { + switch (error.name) { case 'UsernameExistsException': return 'Este correo electrónico ya está registrado'; case 'InvalidParameterException': @@ -27,7 +24,7 @@ export function getSignUpErrorMessage(error: AuthError): string { } } - // Manejo por mensaje de error + // Fallback al mensaje si no hay nombre específico switch (error.message) { case 'Username should be an email.': return 'El correo electrónico no tiene un formato válido'; @@ -44,7 +41,7 @@ export function getSignUpErrorMessage(error: AuthError): string { if (error.message?.includes('Exceeded daily email limit')) { return 'Se ha excedido el límite diario de envío de correos. Por favor, intenta mañana o contacta al soporte'; } - return 'Ha ocurrido un error. Por favor, intenta nuevamente'; + return error.recoverySuggestion || 'Ha ocurrido un error. Por favor, intenta nuevamente'; } } @@ -52,9 +49,9 @@ export function getSignUpErrorMessage(error: AuthError): string { * Obtiene el mensaje de error traducido para errores de confirmación de registro */ export function getConfirmSignUpErrorMessage(error: AuthError): string { - // Manejo por código de error - if (error.code) { - switch (error.code) { + // Usar el nombre del error (más confiable que el mensaje) + if (error.name) { + switch (error.name) { case 'CodeMismatchException': return 'El código ingresado no es válido. Por favor, verifica e intenta nuevamente'; case 'ExpiredCodeException': @@ -70,7 +67,7 @@ export function getConfirmSignUpErrorMessage(error: AuthError): string { } } - // Manejo por mensaje de error + // Fallback al mensaje si no hay nombre específico switch (error.message) { case 'Invalid verification code provided, please try again.': return 'Código de verificación inválido, por favor intenta nuevamente'; @@ -85,7 +82,7 @@ export function getConfirmSignUpErrorMessage(error: AuthError): string { if (error.message?.includes('Exceeded daily email limit')) { return 'Se ha excedido el límite diario de envío de correos. Por favor, intenta mañana o contacta al soporte'; } - return 'Ha ocurrido un error durante la verificación. Por favor, intenta nuevamente'; + return error.recoverySuggestion || 'Ha ocurrido un error durante la verificación. Por favor, intenta nuevamente'; } } @@ -93,6 +90,27 @@ export function getConfirmSignUpErrorMessage(error: AuthError): string { * Obtiene el mensaje de error traducido para errores de inicio de sesión */ export function getSignInErrorMessage(error: AuthError): string { + // Usar el nombre del error (más confiable que el mensaje) + if (error.name) { + switch (error.name) { + case 'NotAuthorizedException': + return 'Email o contraseña incorrectos'; + case 'UserNotConfirmedException': + return 'Por favor confirma tu cuenta primero'; + case 'TooManyRequestsException': + return 'Demasiados intentos. Por favor, intenta más tarde'; + case 'UserNotFoundException': + return 'No se encontró una cuenta con este correo electrónico'; + case 'InvalidParameterException': + return 'Uno o más campos contienen datos inválidos'; + case 'LimitExceededException': + return 'Se ha excedido el límite de intentos. Por favor, intenta más tarde'; + case 'NetworkError': + return 'Error de conexión. Por favor, verifica tu internet'; + } + } + + // Fallback al mensaje si no hay nombre específico switch (error.message) { case 'Incorrect username or password.': return 'Email o contraseña incorrectos'; @@ -107,6 +125,7 @@ export function getSignInErrorMessage(error: AuthError): string { case 'There is already a signed in user.': return 'Ya hay un usuario autenticado. Por favor, cierra la sesión primero'; default: - return error.message || 'Ha ocurrido un error durante el inicio de sesión'; + // Usar recoverySuggestion si está disponible, sino el mensaje por defecto + return error.recoverySuggestion || error.message || 'Ha ocurrido un error durante el inicio de sesión'; } } diff --git a/next.config.ts b/next.config.ts index be00efae..d70f5890 100644 --- a/next.config.ts +++ b/next.config.ts @@ -7,6 +7,13 @@ const nextConfig: NextConfig = { ignoreBuildErrors: true, }, + transpilePackages: [ + '@fasttify/liquid-forge', + '@fasttify/tenant-domains', + '@fasttify/orders-app', + '@fasttify/theme-editor', + ], + serverExternalPackages: [ '@aws-sdk/client-acm', '@aws-sdk/client-bedrock-runtime', diff --git a/packages/liquid-forge/config/page-config.ts b/packages/liquid-forge/config/page-config.ts index 7f07c95b..e7485910 100644 --- a/packages/liquid-forge/config/page-config.ts +++ b/packages/liquid-forge/config/page-config.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import type { PageType } from '@/liquid-forge/types/template'; +import { cacheManager } from '../services/core/cache'; +import type { PageType } from '../types/template'; const templatePaths: Record = { index: 'templates/index.json', diff --git a/packages/liquid-forge/config/route-matchers.ts b/packages/liquid-forge/config/route-matchers.ts index dae97e55..dcc007ff 100644 --- a/packages/liquid-forge/config/route-matchers.ts +++ b/packages/liquid-forge/config/route-matchers.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import type { PageRenderOptions } from '../types/template'; /** * Tipo para matchers de rutas diff --git a/packages/liquid-forge/factories/store-renderer-factory.ts b/packages/liquid-forge/factories/store-renderer-factory.ts index 255fb65c..e784f622 100644 --- a/packages/liquid-forge/factories/store-renderer-factory.ts +++ b/packages/liquid-forge/factories/store-renderer-factory.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { pathToRenderOptions } from '@/liquid-forge/config/route-matchers'; -import { logger } from '@/liquid-forge/lib/logger'; -import { DynamicPageRenderer } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import type { RenderResult } from '@/liquid-forge/types'; +import { pathToRenderOptions } from '../config/route-matchers'; +import { logger } from '../lib/logger'; +import { DynamicPageRenderer } from '../renderers/dynamic-page-renderer'; +import type { RenderResult } from '../types'; /** * Factory principal del sistema de renderizado de tiendas diff --git a/packages/liquid-forge/index.ts b/packages/liquid-forge/index.ts index 22c5d7f3..eac6b028 100644 --- a/packages/liquid-forge/index.ts +++ b/packages/liquid-forge/index.ts @@ -23,7 +23,7 @@ export * from './services/core/cache'; * * @example * ```typescript - * import { storeRenderer } from '@/liquid-forge'; + * import { storeRenderer } from './'; * * const result = await storeRenderer.renderPage('mystore.com', '/products/my-product'); * ``` diff --git a/packages/liquid-forge/instances.ts b/packages/liquid-forge/instances.ts index fb194f9a..789f661f 100644 --- a/packages/liquid-forge/instances.ts +++ b/packages/liquid-forge/instances.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { StoreRendererFactory } from '@/liquid-forge/factories/store-renderer-factory'; +import { StoreRendererFactory } from './factories/store-renderer-factory'; /** * Instancia singleton del renderizador de tiendas diff --git a/packages/liquid-forge/lib/regex-patterns/README.md b/packages/liquid-forge/lib/regex-patterns/README.md new file mode 100644 index 00000000..cd892674 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/README.md @@ -0,0 +1,305 @@ +# Documentación de Patrones Regex + +Este directorio contiene todos los patrones de expresiones regulares centralizados utilizados en todo el paquete `liquid-forge`. Todos los patrones están organizados por categoría y exportados a través del archivo principal `index.ts`. + +## Estructura del Directorio + +``` +lib/regex-patterns/ +├── index.ts # Archivo de exportación principal +├── liquid-syntax.ts # Patrones de sintaxis Liquid +├── minification.ts # Patrones de minificación +├── filters.ts # Patrones de filtros +├── validation.ts # Patrones de validación y seguridad +├── utility.ts # Patrones de utilidad +└── README.md # Este archivo +``` + +## Categorías de Patrones + +### 1. Sintaxis Liquid (`liquid-syntax.ts`) + +Patrones para detectar y analizar la sintaxis de plantillas Liquid. + +#### LIQUID_OBJECT_PATTERNS + +Detecta objetos Liquid en plantillas: + +- `products`: Detecta `{{ products |` +- `collections`: Detecta `{{ collections |` +- `product`: Detecta `{{ product.` +- `collection`: Detecta `{{ collection.` +- `linklists`: Detecta `{{ linklists.` +- `shop`: Detecta `{{ shop.` +- `pages`: Detecta `{{ pages |` +- `page`: Detecta `{{ page.` +- `blog`: Detecta `{{ blog.` +- `checkout`: Detecta `{{ checkout.` +- `relatedProducts`: Detecta uso de productos relacionados +- `pagination`: Detecta `{% paginate` + +#### LIQUID_TAG_PATTERNS + +Detecta etiquetas Liquid: + +- `paginate`: Detecta etiquetas de paginación +- `section`: Detecta etiquetas de sección +- `render`: Detecta etiquetas de render +- `include`: Detecta etiquetas include + +#### LIQUID_COLLECTION_ACCESS_PATTERNS + +Patrones para acceder a colecciones y páginas: + +- `bracketNotation`: `collections['handle']` +- `dotNotation`: `collections.handle` +- `specificAccess`: Combina notación de corchetes y punto +- `collectionProducts`: `collections.handle.products` +- `specificPage`: Acceso a páginas específicas +- `specificProduct`: Acceso a productos específicos +- `extractCollectionHandle`: Extraer handle de colección del match +- `extractPagesBracket`: Buscar notación de corchetes de páginas +- `extractPagesDot`: Buscar notación de punto de páginas +- `extractPagesHandle`: Extraer handle de página +- `pagesBracketExtract`: Extraer handle de notación de corchetes + +#### LIQUID_FILTER_PATTERNS + +Patrones para análisis de filtros: + +- `productRelated`: Extraer límite de productos relacionados +- `productsLimit`: Extraer límite de productos +- `collectionsLimit`: Extraer límite de colecciones +- `generalLimit`: Extraer límite general +- `paginateBy`: Dividir cláusula de paginación + +#### LIQUID_OPTION_EXTRACTOR_PATTERNS + +Patrones para extraer opciones: + +- `sectionName`: Extraer nombre de sección +- `snippetName`: Extraer nombre de snippet +- `collectionHandle`: Extraer handle de colección + +#### LIQUID_PAGINATION_PATTERNS + +Patrones para paginación: + +- `paginate`: Etiqueta principal de paginación +- `policies`: Detectar bucles de políticas +- `pagesLimit`: Extraer límite de páginas + +#### LIQUID_VARIABLE_PATTERNS + +Patrones para variables: + +- `variable`: Coincidir variables Liquid +- `cleanVariableTags`: Limpiar etiquetas de variables + +--- + +### 2. Minificación (`minification.ts`) + +Patrones para minificación y optimización de código. + +#### MINIFICATION_PATTERNS + +Patrones generales de minificación: + +- `htmlComment`: Eliminar comentarios HTML +- `blockComment`: Eliminar comentarios de bloque `/* */` +- `lineComment`: Eliminar comentarios de línea `//` +- `trimLines`: Eliminar espacios iniciales y finales +- `multipleNewlines`: Eliminar líneas vacías múltiples +- `multipleSpaces`: Colapsar espacios múltiples +- `operatorSpaces`: Limpiar espacios de operadores +- `liquidComment`: Eliminar comentarios Liquid + +#### CSS_MINIFICATION_PATTERNS + +Patrones específicos de CSS: + +- `comments`: Eliminar comentarios CSS +- `spaces`: Colapsar espacios +- `syntax`: Limpiar espacios alrededor de sintaxis + +#### CSS_OPTIMIZATION_PATTERNS + +Optimización CSS: + +- `comments`: Eliminar comentarios +- `braces`: Normalizar espacios alrededor de llaves +- `semicolons`: Normalizar espacios alrededor de punto y coma +- `emptyLines`: Eliminar líneas vacías + +--- + +### 3. Filtros (`filters.ts`) + +Patrones utilizados por los filtros Liquid. + +#### ESCAPE_PATTERNS + +Escapado HTML: + +- `ampersand`: Escapar `&` a `&` +- `lessThan`: Escapar `<` a `<` +- `greaterThan`: Escapar `>` a `>` +- `doubleQuote`: Escapar `"` a `"` +- `apostrophe`: Escapar `'` a `'` + +#### HANDLE_PATTERNS + +Creación de handles (slugs amigables para SEO): + +- `aVariants`: Normalizar á, à, ä, â, ã a 'a' +- `eVariants`: Normalizar é, è, ë, ê a 'e' +- `iVariants`: Normalizar í, ì, ï, î a 'i' +- `oVariants`: Normalizar ó, ò, ö, ô, õ a 'o' +- `uVariants`: Normalizar ú, ù, ü, û a 'u' +- `enye`: Normalizar ñ a 'n' +- `cCedilla`: Normalizar ç a 'c' +- `nonAlphanumeric`: Reemplazar no alfanuméricos con `-` +- `multipleDashes`: Colapsar guiones múltiples +- `leadingTrailingDash`: Eliminar guiones iniciales/finales + +#### URL_PATTERNS + +Manipulación de URLs: + +- `url`: Coincidir URLs +- `urlProtocol`: Extraer protocolo +- `urlDomain`: Extraer dominio +- `urlPath`: Extraer ruta +- `urlQuery`: Extraer cadena de consulta +- `urlHash`: Extraer hash +- `isAbsolute`: Verificar si URL es absoluta +- `makeAbsolute`: Hacer URL absoluta +- `ensureProtocol`: Asegurar que URL tenga protocolo +- `cleanQuery`: Limpiar cadena de consulta +- `removeHash`: Eliminar hash de URL +- `urlEncode`: Codificar URL +- `urlDecode`: Decodificar URL +- `sanitizeUrl`: Sanitizar URL + +--- + +### 4. Validación (`validation.ts`) + +Patrones para seguridad y validación. + +#### SECURITY_PATTERNS + +Comprobaciones de seguridad: + +- `externalScript`: Detectar etiquetas de script externas +- `externalCss`: Detectar enlaces CSS externos +- `externalImage`: Detectar imágenes externas +- `externalUrl`: Coincidir URLs externas + +#### DANGEROUS_FUNCTION_PATTERNS + +Funciones de JavaScript peligrosas: + +- `eval`: Detectar eval() +- `functionConstructor`: Detectar Function() +- `setTimeout`: Detectar setTimeout() +- `setInterval`: Detectar setInterval() +- `documentWrite`: Detectar document.write +- `documentWriteln`: Detectar document.writeln +- `innerHTML`: Detectar asignaciones innerHTML +- `outerHTML`: Detectar asignaciones outerHTML + +#### SENSITIVE_DATA_PATTERNS + +Detección de datos sensibles: + +- `apiKey`: Detectar referencias a API key +- `secret`: Detectar referencias a secretos +- `password`: Detectar referencias a contraseñas +- `token`: Detectar referencias a tokens +- `privateKey`: Detectar referencias a clave privada +- `accessKey`: Detectar referencias a clave de acceso + +--- + +### 5. Utilidad (`utility.ts`) + +Patrones de utilidad general. + +#### PATH_PATTERNS + +Manipulación de rutas: + +- `backslashes`: Coincidir barras invertidas +- `leadingSlash`: Eliminar barras iniciales de rutas + +#### SANITIZATION_PATTERNS + +Sanitización de texto: + +- `themeName`: Sanitizar nombres de temas a alfanuméricos + +#### JSON_PARSING_PATTERNS + +Análisis JSON: + +- `liquidSchema`: Extraer bloques de esquema Liquid +- `trailingComma`: Eliminar comas finales +- `multipleCommas`: Corregir comas múltiples +- `lineComment`: Coincidir comentarios de línea +- `blockComment`: Coincidir comentarios de bloque + +--- + +## Uso + +Importar patrones desde el índice principal: + +```typescript +import { + LIQUID_OBJECT_PATTERNS, + MINIFICATION_PATTERNS, + ESCAPE_PATTERNS, + // ... otros patrones +} from '../../../lib/regex-patterns'; + +// Usar en código +const match = content.match(LIQUID_OBJECT_PATTERNS.products); +``` + +## Modificadores de Patrones + +Los patrones utilizan estas banderas comunes: + +- `g`: Coincidencia global (buscar todas las coincidencias) +- `i`: Insensible a mayúsculas +- `m`: Modo multilínea +- `u`: Modo unicode + +## Beneficios de la Centralización + +1. **Mantenibilidad**: Actualizar patrones en un solo lugar +2. **Consistencia**: Mismo patrón en todos los usos +3. **Testabilidad**: Probar patrones de forma independiente +4. **Documentación**: Propósito claro para cada patrón +5. **Reutilización**: Compartir patrones entre módulos + +## Actualización de Patrones + +Al actualizar patrones: + +1. Documentar el cambio en este README +2. Actualizar comentarios JSDoc en el archivo de patrones +3. Ejecutar linter para verificar que no hay errores +4. Probar módulos afectados + +## Contribuir + +Al añadir nuevos patrones: + +1. Colocar en el archivo de categoría apropiado +2. Exportar desde ese archivo +3. Exportar desde `index.ts` +4. Documentar en este README +5. Usar nombres descriptivos diff --git a/packages/liquid-forge/lib/regex-patterns/filters.ts b/packages/liquid-forge/lib/regex-patterns/filters.ts new file mode 100644 index 00000000..815283b5 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/filters.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const ESCAPE_PATTERNS = { + ampersand: /&/g, + lessThan: //g, + doubleQuote: /"/g, + apostrophe: /'/g, +} as const; + +export const HANDLE_PATTERNS = { + aVariants: /[áàäâã]/g, + eVariants: /[éèëê]/g, + iVariants: /[íìïî]/g, + oVariants: /[óòöôõ]/g, + uVariants: /[úùüû]/g, + enye: /[ñ]/g, + cCedilla: /[ç]/g, + nonAlphanumeric: /[^a-z0-9]/g, + multipleDashes: /-+/g, + leadingTrailingDash: /^-|-$/g, +} as const; + +export const URL_PATTERNS = { + trailingSlashes: /\/+$/, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/index.ts b/packages/liquid-forge/lib/regex-patterns/index.ts new file mode 100644 index 00000000..fcffa7f0 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './liquid-syntax'; +export * from './minification'; +export * from './filters'; +export * from './validation'; +export * from './utility'; diff --git a/packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts b/packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts new file mode 100644 index 00000000..1ce6bec9 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/liquid-syntax.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Patrones para detección de sintaxis Liquid + */ +export const LIQUID_OBJECT_PATTERNS = { + products: /\{\{\s*products\s*[\|\}]/g, + collections: /\{\{\s*collections\s*[\|\}]/g, + product: /\{\{\s*product\./g, + collection: /\{\{\s*collection\./g, + linklists: /\{\{\s*linklists\./g, + shop: /\{\{\s*shop\./g, + pages: /\{\{\s*pages\s*[\|\}]/g, + page: /\{\{\s*page\./g, + blog: /\{\{\s*blog\./g, + checkout: /\{\{\s*checkout\./g, + relatedProducts: /related_products|product\s*\|\s*related/g, + pagination: /\{\%\s*paginate/g, +} as const; + +export const LIQUID_TAG_PATTERNS = { + paginate: /\{\%\s*paginate\s+([^%]+)\%\}/g, + section: /\{\%\s*section\s+['"]([^'"]+)['"]\s*\%\}/g, + render: /\{\%\s*render\s+['"]([^'"]+)['"]/g, + include: /\{\%\s*include\s+['"]([^'"]+)['"]/g, +} as const; + +export const LIQUID_COLLECTION_ACCESS_PATTERNS = { + bracketNotation: /collections\[['"]([^'"]+)['"]\]/g, + dotNotation: /collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + specificAccess: /collections\[['"]([^'"]+)['"]\]|collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + collectionProducts: /collections\.([a-zA-Z0-9_-]+)\.products/g, + specificPage: /pages\[['"]([^'"]+)['"]\]|pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + specificProduct: /products\[['"]([^'"]+)['"]\]/g, + extractCollectionHandle: /collections\.([a-zA-Z0-9_-]+)/, + extractPagesBracket: /pages\[['"]([^'"]+)['"]\]/g, + extractPagesDot: /pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + extractPagesHandle: /pages\.([a-zA-Z0-9_-]+)/, + pagesBracketExtract: /pages\[['"]([^'"]+)['"]\]/, +} as const; + +export const LIQUID_FILTER_PATTERNS = { + productRelated: /related_products[^}]*limit:\s*(\d+)/i, + productsLimit: /products[^}]*limit:\s*(\d+)/i, + collectionsLimit: /collections[^}]*limit:\s*(\d+)/i, + generalLimit: /limit:\s*(\d+)/i, + paginateBy: /\s+by\s+/, +} as const; + +export const LIQUID_OPTION_EXTRACTOR_PATTERNS = { + sectionName: /section\s+['"]([^'"]+)['"]/i, + snippetName: /(?:render|include)\s+['"]([^'"]+)['"]/i, + collectionHandle: /collections\[['"]([^'"]+)['"]\]/, +} as const; + +export const LIQUID_PAGINATION_PATTERNS = { + paginate: /\{%\s*paginate\s+(.+?)\s*%\}/, + policies: /for\s+item\s+in\s+policies|for\s+policy\s+in\s+policies|\{\{\s*policies\s*[\|\}]/g, + pagesLimit: /pages[^}]*limit:\s*(\d+)/i, +} as const; + +export const LIQUID_VARIABLE_PATTERNS = { + variable: /\{\{\s*([^}]+)\s*\}\}/g, + cleanVariableTags: /\{\{\s*|\s*\}\}/g, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/minification.ts b/packages/liquid-forge/lib/regex-patterns/minification.ts new file mode 100644 index 00000000..e4769c55 --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/minification.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const MINIFICATION_PATTERNS = { + htmlComment: //g, + blockComment: /\/\*[\s\S]*?\*\//g, + lineComment: /\/\/.*$/gm, + trimLines: /^[ \t]+|[ \t]+$/gm, + multipleNewlines: /\n\s*\n\s*\n/g, + multipleSpaces: /\s+/g, + operatorSpaces: /\s*([=+\-*/%&|^!~?:,;{}()\[\]<>])\s*/g, + liquidComment: /\{%\s*comment\s*%\}[\s\S]*?\{%\s*endcomment\s*%\}/g, + styleBlock: /]*>([\s\S]*?)<\/style\b[^>]*>/gi, + scriptBlock: /]*>([\s\S]*?)<\/script\b[^>]*>/gi, +} as const; + +export const CSS_MINIFICATION_PATTERNS = { + comments: /\/\*[\s\S]*?\*\//g, + spaces: /\s+/g, + syntax: /\s*([{}|:;,])\s*/g, +} as const; + +export const CSS_OPTIMIZATION_PATTERNS = { + comments: /\/\*[\s\S]*?\*\//g, + braces: /\s*{\s*|\s*}\s*/g, + semicolons: /\s*;\s*/g, + emptyLines: /^\s*[\r\n]/gm, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/utility.ts b/packages/liquid-forge/lib/regex-patterns/utility.ts new file mode 100644 index 00000000..28e840cc --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/utility.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const PATH_PATTERNS = { + backslashes: /\\/g, + leadingSlash: /^\/+/g, +} as const; + +export const SANITIZATION_PATTERNS = { + themeName: /[^a-z0-9]/g, +} as const; + +export const JSON_PARSING_PATTERNS = { + liquidSchema: /{%\s*schema\s*%}([\s\S]*?){%\s*endschema\s*%}/i, + trailingComma: /,(\s*[}\]])/g, + multipleCommas: /,,+/g, + lineComment: /\/\/.*$/gm, + blockComment: /\/\*[\s\S]*?\*\//g, +} as const; diff --git a/packages/liquid-forge/lib/regex-patterns/validation.ts b/packages/liquid-forge/lib/regex-patterns/validation.ts new file mode 100644 index 00000000..7ad46d9d --- /dev/null +++ b/packages/liquid-forge/lib/regex-patterns/validation.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const SECURITY_PATTERNS = { + externalScript: /]*src=["'](https?:\/\/[^"']+)["'][^>]*>/gi, + externalCss: /]*href=["'](https?:\/\/[^"']+)["'][^>]*>/gi, + externalImage: /src=["'](https?:\/\/[^"']+)["']/gi, + externalUrl: /https?:\/\/[^\s"']+/gi, +} as const; + +export const DANGEROUS_FUNCTION_PATTERNS = { + eval: /eval\s*\(/, + functionConstructor: /Function\s*\(/, + setTimeout: /setTimeout\s*\(/, + setInterval: /setInterval\s*\(/, + documentWrite: /document\.write/, + documentWriteln: /document\.writeln/, + innerHTML: /innerHTML\s*=/, + outerHTML: /outerHTML\s*=/, +} as const; + +export const SENSITIVE_DATA_PATTERNS = { + apiKey: /api_key/, + secret: /secret/, + password: /password/, + token: /token/, + privateKey: /private_key/, + accessKey: /access_key/, +} as const; + +export const IMAGE_PATTERNS = { + imgTag: /]*src=["']([^"']+)["'][^>]*>/gi, + imgSrc: /src=["']([^"']+)["']/, +} as const; + +export const SPACE_PATTERNS = { + multipleSpaces: /\s{2,}/g, +} as const; diff --git a/packages/liquid-forge/liquid/engine.ts b/packages/liquid-forge/liquid/engine.ts index 07f50a5c..b4a5b6dd 100644 --- a/packages/liquid-forge/liquid/engine.ts +++ b/packages/liquid-forge/liquid/engine.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { allFilters } from '@/liquid-forge/liquid/filters'; +import { allFilters } from './filters'; import { FiltersTag, FormTag, @@ -28,9 +28,9 @@ import { SectionsTag, StyleTag, StylesheetTag, -} from '@/liquid-forge/liquid/tags'; -import { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; -import type { LiquidContext, LiquidEngineConfig, TemplateError } from '@/liquid-forge/types'; +} from './tags'; +import { AssetCollector } from '../services/rendering/asset-collector'; +import type { LiquidContext, LiquidEngineConfig, TemplateError } from '../types'; import { Liquid, Template } from 'liquidjs'; class LiquidEngine { diff --git a/packages/liquid-forge/liquid/filters/base-filters.ts b/packages/liquid-forge/liquid/filters/base-filters.ts index bbcc568b..02e9f094 100644 --- a/packages/liquid-forge/liquid/filters/base-filters.ts +++ b/packages/liquid-forge/liquid/filters/base-filters.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; +import { ESCAPE_PATTERNS, HANDLE_PATTERNS, URL_PATTERNS } from '../../lib/regex-patterns'; /** * Filtro para formatear fechas @@ -67,16 +68,16 @@ export const handleizeFilter: LiquidFilter = { return text .toLowerCase() .trim() - .replace(/[áàäâã]/g, 'a') - .replace(/[éèëê]/g, 'e') - .replace(/[íìïî]/g, 'i') - .replace(/[óòöôõ]/g, 'o') - .replace(/[úùüû]/g, 'u') - .replace(/[ñ]/g, 'n') - .replace(/[ç]/g, 'c') - .replace(/[^a-z0-9]/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); + .replace(HANDLE_PATTERNS.aVariants, 'a') + .replace(HANDLE_PATTERNS.eVariants, 'e') + .replace(HANDLE_PATTERNS.iVariants, 'i') + .replace(HANDLE_PATTERNS.oVariants, 'o') + .replace(HANDLE_PATTERNS.uVariants, 'u') + .replace(HANDLE_PATTERNS.enye, 'n') + .replace(HANDLE_PATTERNS.cCedilla, 'c') + .replace(HANDLE_PATTERNS.nonAlphanumeric, '-') + .replace(HANDLE_PATTERNS.multipleDashes, '-') + .replace(HANDLE_PATTERNS.leadingTrailingDash, ''); }, }; @@ -119,11 +120,11 @@ export const escapeFilter: LiquidFilter = { } return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + .replace(ESCAPE_PATTERNS.ampersand, '&') + .replace(ESCAPE_PATTERNS.lessThan, '<') + .replace(ESCAPE_PATTERNS.greaterThan, '>') + .replace(ESCAPE_PATTERNS.doubleQuote, '"') + .replace(ESCAPE_PATTERNS.apostrophe, '''); }, }; diff --git a/packages/liquid-forge/liquid/filters/cart-filters.ts b/packages/liquid-forge/liquid/filters/cart-filters.ts index 8dd83222..dcd6a6e3 100644 --- a/packages/liquid-forge/liquid/filters/cart-filters.ts +++ b/packages/liquid-forge/liquid/filters/cart-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro cart_url - Genera URL del carrito diff --git a/packages/liquid-forge/liquid/filters/data-access-filters.ts b/packages/liquid-forge/liquid/filters/data-access-filters.ts index 43269222..9c851b06 100644 --- a/packages/liquid-forge/liquid/filters/data-access-filters.ts +++ b/packages/liquid-forge/liquid/filters/data-access-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro para obtener una colección específica por handle desde el contexto diff --git a/packages/liquid-forge/liquid/filters/ecommerce-filters.ts b/packages/liquid-forge/liquid/filters/ecommerce-filters.ts index 94fa0d25..3c5ab4fe 100644 --- a/packages/liquid-forge/liquid/filters/ecommerce-filters.ts +++ b/packages/liquid-forge/liquid/filters/ecommerce-filters.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; +import { PATH_PATTERNS } from '../../lib/regex-patterns'; /** * Interfaz para parámetros de optimización de imágenes @@ -135,7 +136,7 @@ export const imgUrlFilter: LiquidFilter = { // Si es una ruta relativa, construir URL completa const baseImageUrl = '/images'; - const cleanImageUrl = url.replace(/^\/+/, ''); + const cleanImageUrl = url.replace(PATH_PATTERNS.leadingSlash, ''); const fullUrl = `${baseImageUrl}/${cleanImageUrl}`; return buildOptimizedImageUrl(fullUrl, params); diff --git a/packages/liquid-forge/liquid/filters/html-filters.ts b/packages/liquid-forge/liquid/filters/html-filters.ts index dfdeb167..c28df14d 100644 --- a/packages/liquid-forge/liquid/filters/html-filters.ts +++ b/packages/liquid-forge/liquid/filters/html-filters.ts @@ -14,8 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { LiquidFilter } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import type { LiquidFilter } from '../../types'; +import { PATH_PATTERNS } from '../../lib/regex-patterns'; /** * Filtro asset_url - Para archivos estáticos (CSS, JS, imágenes de tema) @@ -28,7 +29,7 @@ export const assetUrlFilter: LiquidFilter = { return ''; } - const cleanFilename = filename.replace(/^\/+/, ''); + const cleanFilename = filename.replace(PATH_PATTERNS.leadingSlash, ''); // Acceder al storeId desde el contexto de LiquidJS const storeId = this.context?.getSync(['shop', 'storeId']); @@ -199,7 +200,7 @@ export const inlineAssetContentFilter: LiquidFilter = { return ''; } - const cleanFilename = filename.replace(/^\/+/, ''); + const cleanFilename = filename.replace(PATH_PATTERNS.leadingSlash, ''); // Acceder al storeId desde el contexto de LiquidJS const storeId = this.context?.getSync(['shop', 'storeId']); @@ -213,7 +214,7 @@ export const inlineAssetContentFilter: LiquidFilter = { try { // Importar templateLoader dinámicamente para evitar dependencias circulares - const { templateLoader } = await import('@/liquid-forge/services/templates/template-loader'); + const { templateLoader } = await import('../../services/templates/template-loader'); // Cargar el contenido del asset const assetContent = await templateLoader.loadAsset(storeId, cleanFilename); diff --git a/packages/liquid-forge/liquid/filters/money-filters.ts b/packages/liquid-forge/liquid/filters/money-filters.ts index 7c505c00..3577ad11 100644 --- a/packages/liquid-forge/liquid/filters/money-filters.ts +++ b/packages/liquid-forge/liquid/filters/money-filters.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { LiquidFilter } from '@/liquid-forge/types'; +import type { LiquidFilter } from '../../types'; /** * Filtro para extraer el símbolo de la moneda diff --git a/packages/liquid-forge/liquid/tags/core/paginate-tag.ts b/packages/liquid-forge/liquid/tags/core/paginate-tag.ts index 4633816a..308e6505 100644 --- a/packages/liquid-forge/liquid/tags/core/paginate-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/paginate-tag.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { Context, Emitter, Liquid, Tag, TagToken, Template, TopLevelToken } from 'liquidjs'; /** diff --git a/packages/liquid-forge/liquid/tags/core/render-tag.ts b/packages/liquid-forge/liquid/tags/core/render-tag.ts index 0f361357..49516b37 100644 --- a/packages/liquid-forge/liquid/tags/core/render-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/render-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Render Tag para manejar {% render 'snippet' %} en LiquidJS @@ -121,7 +121,7 @@ export class RenderTag extends Tag { } // Usar el TemplateLoader para cargar el snippet - const { TemplateLoader } = await import('@/liquid-forge/services/templates/template-loader'); + const { TemplateLoader } = await import('../../../services/templates/template-loader'); const templateLoader = TemplateLoader.getInstance(); // Los snippets están en la carpeta 'snippets' diff --git a/packages/liquid-forge/liquid/tags/core/section-tag.ts b/packages/liquid-forge/liquid/tags/core/section-tag.ts index 4f26bdb9..77b457f1 100644 --- a/packages/liquid-forge/liquid/tags/core/section-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/section-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Section Tag para manejar {% section 'section-name' %} en LiquidJS diff --git a/packages/liquid-forge/liquid/tags/core/sections-tag.ts b/packages/liquid-forge/liquid/tags/core/sections-tag.ts index 3458a1d9..94e132e0 100644 --- a/packages/liquid-forge/liquid/tags/core/sections-tag.ts +++ b/packages/liquid-forge/liquid/tags/core/sections-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Sections Tag para manejar {% sections 'section-group' %} en LiquidJS diff --git a/packages/liquid-forge/liquid/tags/data/javascript-tag.ts b/packages/liquid-forge/liquid/tags/data/javascript-tag.ts index b50b9c32..da21d946 100644 --- a/packages/liquid-forge/liquid/tags/data/javascript-tag.ts +++ b/packages/liquid-forge/liquid/tags/data/javascript-tag.ts @@ -15,25 +15,20 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; -import { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; -import { logger } from '@/liquid-forge/lib/logger'; +import { AssetCollector } from '../../../services/rendering/asset-collector'; +import { logger } from '../../../lib/logger'; +import { MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; /** * Optimiza y limpia el JavaScript generado */ function optimizeJS(js: string): string { - return ( - js - // Remover comentarios de línea (// ...) - .replace(/\/\/.*$/gm, '') - // Remover comentarios de bloque (/* ... */) - .replace(/\/\*[\s\S]*?\*\//g, '') - // Remover espacios múltiples - .replace(/\s+/g, ' ') - // Limpiar líneas vacías - .replace(/^\s*[\r\n]/gm, '') - .trim() - ); + return js + .replace(MINIFICATION_PATTERNS.lineComment, '') + .replace(MINIFICATION_PATTERNS.blockComment, '') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') + .replace(/^\s*[\r\n]/gm, '') + .trim(); } /** diff --git a/packages/liquid-forge/liquid/tags/data/schema-tag.ts b/packages/liquid-forge/liquid/tags/data/schema-tag.ts index de7c6847..62f6c1d1 100644 --- a/packages/liquid-forge/liquid/tags/data/schema-tag.ts +++ b/packages/liquid-forge/liquid/tags/data/schema-tag.ts @@ -15,7 +15,7 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; /** * Custom Schema Tag para manejar {% schema %} de Shopify en LiquidJS diff --git a/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts b/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts index b8c41a3b..982acfb0 100644 --- a/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts +++ b/packages/liquid-forge/liquid/tags/filters/data-fetcher.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; +import { dataFetcher } from '../../../services/fetchers/data-fetcher'; import { AvailableFilters } from './types'; export class FilterDataFetcher { diff --git a/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts b/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts index 5d4e405c..722334b2 100644 --- a/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts +++ b/packages/liquid-forge/liquid/tags/filters/javascript-generator.ts @@ -65,7 +65,7 @@ export class FilterJavaScriptGenerator { */ private static buildScriptContent(filterConfig: string): string { return ` - import { initFilterSystem } from 'https://cdn.fasttify.com/assets/filter-main.js'; + import { initFilterSystem } from 'https://cdn.fasttify.com/assets/global-static/filter-main.js'; const FILTER_CONFIG = ${filterConfig}; initFilterSystem(FILTER_CONFIG); `; diff --git a/packages/liquid-forge/liquid/tags/filters/js/filter-main.js b/packages/liquid-forge/liquid/tags/filters/js/filter-main.js index 3a354a66..65bf5f71 100644 --- a/packages/liquid-forge/liquid/tags/filters/js/filter-main.js +++ b/packages/liquid-forge/liquid/tags/filters/js/filter-main.js @@ -17,15 +17,15 @@ /** * Sistema principal de filtros */ -import { DOMManager } from 'https://cdn.fasttify.com/assets/dom-manager.js'; -import { EventHandler } from 'https://cdn.fasttify.com/assets/event-handler.js'; -import { FilterAPI } from 'https://cdn.fasttify.com/assets/filter-api.js'; -import { createFilterConfig } from 'https://cdn.fasttify.com/assets/filter-config.js'; -import { FilterStateManager } from 'https://cdn.fasttify.com/assets/filter-state.js'; -import { PriceSlider } from 'https://cdn.fasttify.com/assets/price-slider.js'; -import { ProductRenderer } from 'https://cdn.fasttify.com/assets/product-renderer.js'; -import { URLLoader } from 'https://cdn.fasttify.com/assets/url-loader.js'; -import { FilterUtils } from 'https://cdn.fasttify.com/assets/utils.js'; +import { DOMManager } from 'https://cdn.fasttify.com/assets/global-static/dom-manager.js'; +import { EventHandler } from 'https://cdn.fasttify.com/assets/global-static/event-handler.js'; +import { FilterAPI } from 'https://cdn.fasttify.com/assets/global-static/filter-api.js'; +import { createFilterConfig } from 'https://cdn.fasttify.com/assets/global-static/filter-config.js'; +import { FilterStateManager } from 'https://cdn.fasttify.com/assets/global-static/filter-state.js'; +import { PriceSlider } from 'https://cdn.fasttify.com/assets/global-static/price-slider.js'; +import { ProductRenderer } from 'https://cdn.fasttify.com/assets/global-static/product-renderer.js'; +import { URLLoader } from 'https://cdn.fasttify.com/assets/global-static/url-loader.js'; +import { FilterUtils } from 'https://cdn.fasttify.com/assets/global-static/utils.js'; /** * Clase principal del sistema de filtros diff --git a/packages/liquid-forge/liquid/tags/filters/js/filter-state.js b/packages/liquid-forge/liquid/tags/filters/js/filter-state.js index b846ccdf..0d475bfd 100644 --- a/packages/liquid-forge/liquid/tags/filters/js/filter-state.js +++ b/packages/liquid-forge/liquid/tags/filters/js/filter-state.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import { INITIAL_FILTER_STATE } from 'https://cdn.fasttify.com/assets/filter-config.js'; +import { INITIAL_FILTER_STATE } from 'https://cdn.fasttify.com/assets/global-static/filter-config.js'; /** * Gestor del estado del filtro diff --git a/packages/liquid-forge/liquid/tags/styling/style-tag.ts b/packages/liquid-forge/liquid/tags/styling/style-tag.ts index 67e73f1e..ac68174b 100644 --- a/packages/liquid-forge/liquid/tags/styling/style-tag.ts +++ b/packages/liquid-forge/liquid/tags/styling/style-tag.ts @@ -15,27 +15,22 @@ */ import { Tag, TagToken, Context, TopLevelToken, Liquid, TokenKind } from 'liquidjs'; -import { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; -import { logger } from '@/liquid-forge/lib/logger'; +import { AssetCollector } from '../../../services/rendering/asset-collector'; +import { logger } from '../../../lib/logger'; +import { CSS_OPTIMIZATION_PATTERNS, MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; /** * Optimiza y limpia el CSS generado */ function optimizeCSS(css: string): string { - return ( - css - // Remover comentarios CSS - .replace(/\/\*[\s\S]*?\*\//g, '') - // Remover espacios múltiples (pero preservar selectores) - .replace(/\s+/g, ' ') - // Limpiar espacios alrededor de llaves y punto y coma (pero NO en selectores) - .replace(/\s*{\s*/g, '{') - .replace(/\s*}\s*/g, '}') - .replace(/\s*;\s*/g, ';') - // Limpiar líneas vacías - .replace(/^\s*[\r\n]/gm, '') - .trim() - ); + return css + .replace(CSS_OPTIMIZATION_PATTERNS.comments, '') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') + .replace(/\s*{\s*/g, '{') + .replace(/\s*}\s*/g, '}') + .replace(/\s*;\s*/g, ';') + .replace(CSS_OPTIMIZATION_PATTERNS.emptyLines, '') + .trim(); } /** diff --git a/packages/liquid-forge/package.json b/packages/liquid-forge/package.json index 38b47b55..3d387464 100644 --- a/packages/liquid-forge/package.json +++ b/packages/liquid-forge/package.json @@ -12,7 +12,9 @@ "test": "jest", "lint": "eslint .", "lint:fix": "eslint . --fix", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "convert-imports": "tsx scripts/convert-imports-to-relative.ts", + "convert-imports:dry": "tsx scripts/convert-imports-to-relative.ts --dry-run" }, "dependencies": { "browserslist": "^4.26.3", @@ -27,17 +29,18 @@ "cssnano": "^7.1.1", "eslint": "^8.0.0", "jest": "^30.0.4", + "tsx": "^4.20.6", "typescript": "^5.8.3" }, "peerDependencies": { - "dotenv": "^17.2.0", - "autoprefixer": "^10.4.21", - "postcss": "^8.5.6", - "@aws-sdk/lib-storage": "^3.903.0", "@aws-sdk/client-lambda": "^3.901.0", "@aws-sdk/client-s3": "^3.844.0", - "uuid": "^11.1.0", + "@aws-sdk/lib-storage": "^3.903.0", + "autoprefixer": "^10.4.21", + "dotenv": "^17.2.0", + "liquidjs": "^10.21.1", "node-cache": "^5.1.2", - "liquidjs": "^10.21.1" + "postcss": "^8.5.6", + "uuid": "^11.1.0" } } diff --git a/packages/liquid-forge/renderers/dynamic-page-renderer.ts b/packages/liquid-forge/renderers/dynamic-page-renderer.ts index ded4c1f2..e62e9d8f 100644 --- a/packages/liquid-forge/renderers/dynamic-page-renderer.ts +++ b/packages/liquid-forge/renderers/dynamic-page-renderer.ts @@ -15,26 +15,22 @@ */ // Core utilities -import { injectAssets } from '@/liquid-forge/lib/inject-assets'; -import { logger } from '@/liquid-forge/lib/logger'; +import { injectAssets } from '../lib/inject-assets'; +import { logger } from '../lib/logger'; // Liquid engine -import { liquidEngine } from '@/liquid-forge/liquid/engine'; +import { liquidEngine } from '../liquid/engine'; // Services -import { pageConfig } from '@/liquid-forge/config/page-config'; +import { pageConfig } from '../config/page-config'; // Clave y caché de HTML gestionados en utilidades dedicadas -import { - getCachedPageRender, - makePageCacheKey, - setCachedPageRender, -} from '@/liquid-forge/services/rendering/page-html-cache'; -import { domainResolver } from '@/liquid-forge/services/core/domain-resolver'; -import { errorRenderer } from '@/liquid-forge/services/errors/error-renderer'; -import { createTemplateError } from '@/liquid-forge/services/errors/error-utils'; -import { metadataGenerator } from '@/liquid-forge/services/rendering/metadata-generator'; -import { sectionRenderer } from '@/liquid-forge/services/rendering/section-renderer'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; +import { getCachedPageRender, makePageCacheKey, setCachedPageRender } from '../services/rendering/page-html-cache'; +import { domainResolver } from '../services/core/domain-resolver'; +import { errorRenderer } from '../services/errors/error-renderer'; +import { createTemplateError } from '../services/errors/error-utils'; +import { metadataGenerator } from '../services/rendering/metadata-generator'; +import { sectionRenderer } from '../services/rendering/section-renderer'; +import { templateLoader } from '../services/templates/template-loader'; // Pipeline steps import { @@ -43,11 +39,11 @@ import { loadDataStep, renderContentStep, resolveStoreStep, -} from '@/liquid-forge/renderers/pipeline-steps'; +} from './pipeline-steps'; // Types -import type { RenderResult, ShopContext, TemplateError } from '@/liquid-forge/types'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import type { RenderResult, ShopContext, TemplateError } from '../types'; +import type { PageRenderOptions } from '../types/template'; import type { Template } from 'liquidjs'; /** diff --git a/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts b/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts index 64f9c75e..6a3ee81a 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/build-context-step.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { contextBuilder } from '@/liquid-forge/services/rendering/global-context'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { contextBuilder } from '../../services/rendering/global-context'; /** * Helper para exponer solo propiedades definidas diff --git a/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts b/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts index 19d7922e..38e7841a 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; +import { liquidEngine } from '../../liquid/engine'; +import type { RenderingData } from '../dynamic-page-renderer'; /** * Paso 2: Inicializar motor de rendering diff --git a/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts b/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts index 54eeb95e..bebe8618 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/load-data-step.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { pageConfig } from '@/liquid-forge/config/page-config'; -import { logger } from '@/liquid-forge/lib/logger'; -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import { dynamicDataLoader } from '@/liquid-forge/services/page/dynamic-data-loader'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; +import { pageConfig } from '../../config/page-config'; +import { logger } from '../../lib/logger'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { dataFetcher } from '../../services/fetchers/data-fetcher'; +import { dynamicDataLoader } from '../../services/page/dynamic-data-loader'; +import { templateLoader } from '../../services/templates/template-loader'; /** * Paso 4: Cargar todos los datos en paralelo diff --git a/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts b/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts index 950593e2..d8b845b2 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/render-content-step.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { pageConfig } from '@/liquid-forge/config/page-config'; -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { renderPageContent } from '@/liquid-forge/services/rendering/render-page-content'; +import { pageConfig } from '../../config/page-config'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { renderPageContent } from '../../services/rendering/render-page-content'; /** * Paso 6: Renderizar contenido de la página diff --git a/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts b/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts index b1feede1..1f78e8f5 100644 --- a/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts +++ b/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { RenderingData } from '@/liquid-forge/renderers/dynamic-page-renderer'; -import { domainResolver } from '@/liquid-forge/services/core/domain-resolver'; +import type { RenderingData } from '../dynamic-page-renderer'; +import { domainResolver } from '../../services/core/domain-resolver'; /** * Paso 1: Resolver dominio a tienda diff --git a/packages/liquid-forge/scripts/convert-imports-to-relative.ts b/packages/liquid-forge/scripts/convert-imports-to-relative.ts new file mode 100644 index 00000000..00a7e77a --- /dev/null +++ b/packages/liquid-forge/scripts/convert-imports-to-relative.ts @@ -0,0 +1,331 @@ +#!/usr/bin/env node +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; + +/** + * Configuración de los alias del proyecto + * Solo convertimos aliases que apuntan dentro del paquete (@/liquid-forge y @/) + * Los demás se mantienen como alias ya que apuntan fuera del paquete + */ +const ALIAS_CONFIG: Record = { + '@/liquid-forge': '.', + '@/': '.', +}; + +/** + * Aliases que NO se deben convertir (rutas externas al paquete) + */ +const EXTERNAL_ALIASES = ['@/utils', '@/lib', '@/amplify', '@/data-schema', '@/amplify_outputs.json', '@/api']; + +/** + * Verifica si un import es de fuera del paquete + */ +function isExternalAlias(importPath: string): boolean { + return EXTERNAL_ALIASES.some((alias) => importPath.startsWith(alias)); +} + +/** + * Tipos de archivos que procesar + */ +const FILE_EXTENSIONS = ['.ts', '.tsx']; + +/** + * Directorios a excluir + */ +const EXCLUDE_DIRS = ['node_modules', 'dist', '.git', 'scripts', '__tests__', 'test', 'tests']; + +/** + * Estadísticas del proceso + */ +interface ProcessStats { + filesScanned: number; + filesModified: number; + importsConverted: number; + errors: number; +} + +/** + * Verifica si una ruta debe ser excluida + */ +function shouldExclude(filePath: string): boolean { + const pathParts = filePath.split(path.sep); + return EXCLUDE_DIRS.some((excludeDir) => pathParts.includes(excludeDir)); +} + +/** + * Convierte un import con alias a una ruta relativa + */ +function convertAliasToRelative(importPath: string, fromFile: string, baseDir: string): string { + // Buscar el alias más largo que coincida + let matchedAlias = ''; + let matchedPath = ''; + + for (const [alias, target] of Object.entries(ALIAS_CONFIG)) { + if (importPath.startsWith(alias)) { + if (alias.length > matchedAlias.length) { + matchedAlias = alias; + matchedPath = target; + } + } + } + + if (!matchedAlias) { + return importPath; // No hay alias, mantener el import original + } + + // Remover el alias del path + const remainingPath = importPath.substring(matchedAlias.length); + + // Construir la ruta completa del alias + // Si el path empieza con /, removerlo + const cleanRemaining = remainingPath.startsWith('/') ? remainingPath.substring(1) : remainingPath; + const fullTargetPath = path.resolve(baseDir, matchedPath, cleanRemaining); + + // Obtener la ruta relativa desde el archivo origen + const relativePath = path.relative(path.dirname(fromFile), fullTargetPath); + + // Normalizar la ruta relativa para import statements + let normalizedPath = relativePath.replace(/\\/g, '/'); + + // Asegurar que las rutas relativas empiecen con ./ + if (!normalizedPath.startsWith('.') && !normalizedPath.startsWith('@')) { + normalizedPath = './' + normalizedPath; + } + + // Remover la extensión .ts o .tsx + normalizedPath = normalizedPath.replace(/\.tsx?$/, ''); + + return normalizedPath; +} + +/** + * Verifica si una ruta de archivo existe + */ +function fileExists(filePath: string): boolean { + try { + // Probar con diferentes extensiones + const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '.json']; + for (const ext of extensions) { + if (fs.existsSync(filePath + ext)) { + return true; + } + } + return false; + } catch { + return false; + } +} + +/** + * Convierte imports en un archivo + */ +function convertFileImports( + filePath: string, + baseDir: string, + dryRun: boolean = false +): { modified: boolean; importsConverted: number; changes: Array<{ old: string; new: string }> } { + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const lines = content.split('\n'); + let modified = false; + let importsConverted = 0; + const changes: Array<{ old: string; new: string }> = []; + + // Procesar el contenido completo para capturar imports multi-línea + const contentStr = lines.join('\n'); + + // Buscar todos los imports estáticos (pueden ser multi-línea) + const staticImportRegex = /import\s+(?:[^'"]*?from\s+['"])([@\/\w_-]+)(['"])/g; + // Buscar imports dinámicos (import(), require(), etc.) + const dynamicImportRegex = /(?:import|require)\s*\(['"]([@\/\w_-]+)['"]\)/g; + + let newContent = contentStr; + + // Procesar imports estáticos + newContent = contentStr.replace(staticImportRegex, (match, importPath) => { + // NO convertir imports externos al paquete + if (isExternalAlias(importPath)) { + return match; + } + + // Verificar si es un alias que necesitamos convertir + let shouldConvert = false; + for (const alias of Object.keys(ALIAS_CONFIG)) { + if (importPath.startsWith(alias)) { + shouldConvert = true; + break; + } + } + + if (shouldConvert) { + const relativePath = convertAliasToRelative(importPath, filePath, baseDir); + + if (relativePath !== importPath) { + modified = true; + importsConverted++; + + changes.push({ old: importPath, new: relativePath }); + + return match.replace(importPath, relativePath); + } + } + + return match; + }); + + // Procesar imports dinámicos + newContent = newContent.replace(dynamicImportRegex, (match, importPath) => { + // NO convertir imports externos al paquete + if (isExternalAlias(importPath)) { + return match; + } + + // Verificar si es un alias que necesitamos convertir + let shouldConvert = false; + for (const alias of Object.keys(ALIAS_CONFIG)) { + if (importPath.startsWith(alias)) { + shouldConvert = true; + break; + } + } + + if (shouldConvert) { + const relativePath = convertAliasToRelative(importPath, filePath, baseDir); + + if (relativePath !== importPath) { + modified = true; + importsConverted++; + + changes.push({ old: importPath, new: relativePath }); + + return match.replace(importPath, relativePath); + } + } + + return match; + }); + + const newLines = newContent.split('\n'); + + if (modified && !dryRun) { + fs.writeFileSync(filePath, newLines.join('\n'), 'utf-8'); + console.log(`Convertido: ${path.relative(baseDir, filePath)} (${importsConverted} imports)`); + } else if (modified && dryRun) { + console.log(` - ${path.relative(baseDir, filePath)} (${importsConverted} imports)`); + } + + return { modified, importsConverted, changes }; + } catch (error) { + console.error(`Error procesando ${filePath}:`, error); + return { modified: false, importsConverted: 0, changes: [] }; + } +} + +/** + * Encuentra todos los archivos TypeScript en un directorio + */ +function findTsFiles(dir: string, baseDir: string, fileList: string[] = []): string[] { + if (!fs.existsSync(dir)) { + return fileList; + } + + const items = fs.readdirSync(dir); + + for (const item of items) { + const fullPath = path.join(dir, item); + + if (shouldExclude(fullPath)) { + continue; + } + + const stats = fs.statSync(fullPath); + + if (stats.isDirectory()) { + findTsFiles(fullPath, baseDir, fileList); + } else if (stats.isFile()) { + const ext = path.extname(item); + if (FILE_EXTENSIONS.includes(ext)) { + fileList.push(fullPath); + } + } + } + + return fileList; +} + +/** + * Función principal + */ +function main() { + const args = process.argv.slice(2); + const dryRun = args.includes('--dry-run') || args.includes('-d'); + + const packageDir = path.resolve(__dirname, '..'); + const stats: ProcessStats = { + filesScanned: 0, + filesModified: 0, + importsConverted: 0, + errors: 0, + }; + + if (dryRun) { + console.log('Modo DRY-RUN: No se realizarán cambios\n'); + } else { + console.log('Iniciando conversión de imports...\n'); + } + + console.log(`Directorio del paquete: ${packageDir}\n`); + + // Encontrar todos los archivos TypeScript + const files = findTsFiles(packageDir, packageDir); + stats.filesScanned = files.length; + + console.log(`${files.length} archivos encontrados\n`); + + // Procesar cada archivo + for (const file of files) { + const result = convertFileImports(file, packageDir, dryRun); + + if (result.modified) { + stats.filesModified++; + stats.importsConverted += result.importsConverted; + } + } + + // Mostrar estadísticas + console.log('\nConversión completada\n'); + console.log('Estadísticas:'); + console.log(` • Archivos escaneados: ${stats.filesScanned}`); + console.log(` • Archivos que serían modificados: ${stats.filesModified}`); + console.log(` • Imports convertidos: ${stats.importsConverted}`); + console.log(` • Errores: ${stats.errors}`); + + if (dryRun) { + console.log('\nPara aplicar los cambios, ejecuta sin --dry-run'); + } else if (stats.filesModified > 0) { + console.log('\nRecomendación: Revisa los cambios con git diff antes de hacer commit'); + } +} + +// Ejecutar si es llamado directamente +if (require.main === module) { + main(); +} + +export { convertAliasToRelative, ALIAS_CONFIG }; diff --git a/packages/liquid-forge/services/analytics/analytics-webhook.service.ts b/packages/liquid-forge/services/analytics/analytics-webhook.service.ts index 5409756f..c6496ef4 100644 --- a/packages/liquid-forge/services/analytics/analytics-webhook.service.ts +++ b/packages/liquid-forge/services/analytics/analytics-webhook.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../lib/logger'; import { AnalyticsWebhookJWTAuth } from '@/api/webhooks/_lib/middleware/jwt-auth.middleware'; /** diff --git a/packages/liquid-forge/services/analytics/store-views-tracker.service.ts b/packages/liquid-forge/services/analytics/store-views-tracker.service.ts index 615b8c63..008935bf 100644 --- a/packages/liquid-forge/services/analytics/store-views-tracker.service.ts +++ b/packages/liquid-forge/services/analytics/store-views-tracker.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../lib/logger'; import { randomUUID } from 'crypto'; import { AnalyticsWebhookJWTAuth } from '@/api/webhooks/_lib/middleware/jwt-auth.middleware'; diff --git a/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts b/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts index 90f1ea5e..7367fa2a 100644 --- a/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts +++ b/packages/liquid-forge/services/core/cache/cache-invalidation-service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { cacheManager, getCollectionPrefix, @@ -29,7 +29,7 @@ import { getProductsPrefix, getNavigationPrefix, getNavigationMenuPrefix, -} from '@/liquid-forge/services/core/cache'; +} from './'; /** * Tipos de cambios que pueden ocurrir en una tienda diff --git a/packages/liquid-forge/services/core/cache/cache-manager.ts b/packages/liquid-forge/services/core/cache/cache-manager.ts index 2a454a0a..63639cb6 100644 --- a/packages/liquid-forge/services/core/cache/cache-manager.ts +++ b/packages/liquid-forge/services/core/cache/cache-manager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import NodeCache from 'node-cache'; // Configuración de TTLs por categoría diff --git a/packages/liquid-forge/services/core/data-transformer.ts b/packages/liquid-forge/services/core/data-transformer.ts index 359b1a10..074f15ff 100644 --- a/packages/liquid-forge/services/core/data-transformer.ts +++ b/packages/liquid-forge/services/core/data-transformer.ts @@ -14,24 +14,23 @@ * limitations under the License. */ +import { HANDLE_PATTERNS } from '../../lib/regex-patterns'; + export class DataTransformer { - /** - * Crea un handle SEO-friendly a partir de un texto - */ public static createHandle(text: string): string { return text .toLowerCase() .trim() - .replace(/[áàäâã]/g, 'a') - .replace(/[éèëê]/g, 'e') - .replace(/[íìïî]/g, 'i') - .replace(/[óòöôõ]/g, 'o') - .replace(/[úùüû]/g, 'u') - .replace(/[ñ]/g, 'n') - .replace(/[ç]/g, 'c') - .replace(/[^a-z0-9]/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, ''); + .replace(HANDLE_PATTERNS.aVariants, 'a') + .replace(HANDLE_PATTERNS.eVariants, 'e') + .replace(HANDLE_PATTERNS.iVariants, 'i') + .replace(HANDLE_PATTERNS.oVariants, 'o') + .replace(HANDLE_PATTERNS.uVariants, 'u') + .replace(HANDLE_PATTERNS.enye, 'n') + .replace(HANDLE_PATTERNS.cCedilla, 'c') + .replace(HANDLE_PATTERNS.nonAlphanumeric, '-') + .replace(HANDLE_PATTERNS.multipleDashes, '-') + .replace(HANDLE_PATTERNS.leadingTrailingDash, ''); } /** diff --git a/packages/liquid-forge/services/core/domain-resolver.ts b/packages/liquid-forge/services/core/domain-resolver.ts index b5d84f7d..bded2985 100644 --- a/packages/liquid-forge/services/core/domain-resolver.ts +++ b/packages/liquid-forge/services/core/domain-resolver.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { cacheManager, getDomainCacheKey } from '@/liquid-forge/services/core/cache'; -import { logger } from '@/liquid-forge/lib/logger'; -import type { Store, TemplateError } from '@/liquid-forge/types'; +import { cacheManager, getDomainCacheKey } from './cache'; +import { logger } from '../../lib/logger'; +import type { Store, TemplateError } from '../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; class DomainResolver { diff --git a/packages/liquid-forge/services/core/navigation-service.ts b/packages/liquid-forge/services/core/navigation-service.ts index 95e224bb..b7e988e4 100644 --- a/packages/liquid-forge/services/core/navigation-service.ts +++ b/packages/liquid-forge/services/core/navigation-service.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import type { ProcessedNavigationMenu } from '@/liquid-forge/types/store'; -import { logger } from '@/liquid-forge/lib/logger'; +import { dataFetcher } from '../fetchers/data-fetcher'; +import type { ProcessedNavigationMenu } from '../../types/store'; +import { logger } from '../../lib/logger'; export interface LinkListItem { title: string; diff --git a/packages/liquid-forge/services/errors/error-messages.ts b/packages/liquid-forge/services/errors/error-messages.ts index 7fd2ea11..982e7770 100644 --- a/packages/liquid-forge/services/errors/error-messages.ts +++ b/packages/liquid-forge/services/errors/error-messages.ts @@ -15,7 +15,7 @@ */ // Helpers para mensajes, títulos, descripciones y sugerencias de errores -import type { TemplateError } from '@/liquid-forge/types'; +import type { TemplateError } from '../../types'; export function getFriendlyMessage(errorType: TemplateError['type']): string { const messages = { diff --git a/packages/liquid-forge/services/errors/error-renderer.ts b/packages/liquid-forge/services/errors/error-renderer.ts index ffd8d24c..8fc52064 100644 --- a/packages/liquid-forge/services/errors/error-renderer.ts +++ b/packages/liquid-forge/services/errors/error-renderer.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import type { RenderResult, ShopContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import { liquidEngine } from '../../liquid/engine'; +import type { RenderResult, ShopContext, TemplateError } from '../../types'; import { extractStoreName, getErrorDescription, getErrorSuggestions, getErrorTitle, getFriendlyMessage, -} from '@/liquid-forge/services/errors/error-messages'; +} from './error-messages'; import { getDataErrorTemplate, getFallbackErrorPageTemplate, @@ -31,7 +31,7 @@ import { getStoreNotActiveTemplate, getStoreNotFoundTemplate, getTemplateNotFoundTemplate, -} from '@/liquid-forge/services/errors/error-templates'; +} from './error-templates'; export interface ErrorRenderOptions { storeId?: string; diff --git a/packages/liquid-forge/services/errors/error-utils.ts b/packages/liquid-forge/services/errors/error-utils.ts index 11d54d96..a9097484 100644 --- a/packages/liquid-forge/services/errors/error-utils.ts +++ b/packages/liquid-forge/services/errors/error-utils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { TemplateError } from '@/liquid-forge/types'; +import type { TemplateError } from '../../types'; /** * Crea un objeto TemplateError estándar diff --git a/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts b/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts index ee928332..c4be5b82 100644 --- a/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts +++ b/packages/liquid-forge/services/fetchers/cart/cart-context-transformer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { Cart, CartContext } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { Cart, CartContext } from '../../../types'; export class CartContextTransformer { /** diff --git a/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts b/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts index c06a32bb..84455c8b 100644 --- a/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/cart/cart-fetcher.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import type { Cart, CartRaw } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import { dataFetcher } from '../data-fetcher'; +import type { Cart, CartRaw } from '../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import { cartContextTransformer } from './cart-context-transformer'; import { cartItemTransformer } from './cart-item-transformer'; diff --git a/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts b/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts index a5c9e960..63b0c2b0 100644 --- a/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts +++ b/packages/liquid-forge/services/fetchers/cart/cart-stock-validator.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; +import { logger } from '../../../lib/logger'; +import { dataFetcher } from '../data-fetcher'; import { cookiesClient } from '@/utils/server/AmplifyServer'; export interface StockValidationResult { diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts index 0194ce5e..7404747c 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-data-transformer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { CheckoutContext, CheckoutSession } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { CheckoutContext, CheckoutSession } from '../../../types'; /** * Transformador de datos específico para checkout diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts index 418c5370..90f84b74 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-fetcher.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import type { Cart, CartSnapshot, @@ -24,7 +24,7 @@ import type { CheckoutStatus, StartCheckoutRequest, UpdateCustomerInfoRequest, -} from '@/liquid-forge/types'; +} from '../../../types'; import { checkoutDataTransformer } from './checkout-data-transformer'; import { checkoutOrderCreator } from './checkout-order-creator'; import { checkoutSessionManager } from './checkout-session-manager'; diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts index a10a2347..d24ede9e 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-order-creator.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { CheckoutResponse, CheckoutSession } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { CheckoutResponse, CheckoutSession } from '../../../types'; import { orderFetcher, type CreateOrderRequest } from '../order'; export class CheckoutOrderCreator { diff --git a/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts b/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts index 9a29f2f8..5ee0ece5 100644 --- a/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts +++ b/packages/liquid-forge/services/fetchers/checkout/checkout-session-manager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import crypto from 'crypto'; import type { CheckoutSessionData, UserStoreCurrency } from './types/checkout-types'; diff --git a/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts b/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts index f300cdab..57eb347f 100644 --- a/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/collection/collection-cache-manager.ts @@ -16,7 +16,7 @@ import { getCollectionPrefix, getCollectionsCacheKey, getCollectionsPrefix, -} from '@/liquid-forge/services/core/cache'; +} from '../../core/cache'; import type { CollectionContext, CollectionsResponse } from './types/collection-types'; export class CollectionCacheManager { diff --git a/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts b/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts index 5e2ea6c6..54948b05 100644 --- a/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/collection/collection-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { CollectionContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { CollectionContext, TemplateError } from '../../../types'; import { productFetcher } from '../product'; import { collectionCacheManager } from './collection-cache-manager'; import { collectionQueryManager } from './collection-query-manager'; diff --git a/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts b/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts index a5dbd6dc..b3fc0bcf 100644 --- a/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts +++ b/packages/liquid-forge/services/fetchers/collection/collection-transformer.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; -import type { CollectionContext, ProductContext } from '@/liquid-forge/types'; +import { dataTransformer } from '../../core/data-transformer'; +import type { CollectionContext, ProductContext } from '../../../types'; import type { CollectionData } from './types/collection-types'; export class CollectionTransformer { diff --git a/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts b/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts index 18640f00..659513ba 100644 --- a/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts +++ b/packages/liquid-forge/services/fetchers/collection/types/collection-types.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { CollectionContext } from '@/liquid-forge/types'; +import type { CollectionContext } from '../../../../types'; export type { CollectionContext }; diff --git a/packages/liquid-forge/services/fetchers/data-fetcher.ts b/packages/liquid-forge/services/fetchers/data-fetcher.ts index a7651a21..b011b8c7 100644 --- a/packages/liquid-forge/services/fetchers/data-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/data-fetcher.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import { cartFetcher } from '@/liquid-forge/services/fetchers/cart'; -import { collectionFetcher } from '@/liquid-forge/services/fetchers/collection'; -import { navigationFetcher } from '@/liquid-forge/services/fetchers/navigation'; -import { pageFetcher } from '@/liquid-forge/services/fetchers/page'; -import { checkoutFetcher } from '@/liquid-forge/services/fetchers/checkout'; -import { productFetcher } from '@/liquid-forge/services/fetchers/product'; +import { cacheManager } from '../core/cache'; +import { cartFetcher } from './cart'; +import { collectionFetcher } from './collection'; +import { navigationFetcher } from './navigation'; +import { pageFetcher } from './page'; +import { checkoutFetcher } from './checkout'; +import { productFetcher } from './product'; import type { AddToCartRequest, Cart, @@ -35,7 +35,7 @@ import type { CheckoutResponse, CheckoutSession, CheckoutStatus, -} from '@/liquid-forge/types'; +} from '../../types'; interface PaginationOptions { limit?: number; diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts index 53f33dbe..2a7bd8a9 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-cache-manager.ts @@ -16,7 +16,7 @@ import { getNavigationMenuCacheKey, getNavigationMenuPrefix, getNavigationPrefix, -} from '@/liquid-forge/services/core/cache'; +} from '../../core/cache'; import type { NavigationMenusResponse, ProcessedNavigationMenu } from './types/navigation-types'; export class NavigationCacheManager { diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts index 2f41326b..c4cc8313 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { ProcessedNavigationMenu } from '@/liquid-forge/types/store'; +import { logger } from '../../../lib/logger'; +import type { ProcessedNavigationMenu } from '../../../types/store'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import { navigationCacheManager } from './navigation-cache-manager'; import { navigationMenuProcessor } from './navigation-menu-processor'; diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts index 21b438cc..31c0cef2 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-menu-processor.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { NavigationMenuItem, ProcessedNavigationMenu } from '@/liquid-forge/types/store'; +import { logger } from '../../../lib/logger'; +import type { NavigationMenuItem, ProcessedNavigationMenu } from '../../../types/store'; import type { NavigationMenuData, ProcessedMenuItemData } from './types/navigation-types'; export class NavigationMenuProcessor { diff --git a/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts b/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts index 9bd4a97a..13982dcd 100644 --- a/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts +++ b/packages/liquid-forge/services/fetchers/navigation/navigation-url-resolver.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../lib/logger'; import { cookiesClient } from '@/utils/server/AmplifyServer'; export class NavigationUrlResolver { diff --git a/packages/liquid-forge/services/fetchers/order/order-fetcher.ts b/packages/liquid-forge/services/fetchers/order/order-fetcher.ts index 5b6af23f..85d68a62 100644 --- a/packages/liquid-forge/services/fetchers/order/order-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/order/order-fetcher.ts @@ -10,16 +10,16 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; -import { customerInfoManager } from '@/liquid-forge/services/fetchers/order/customer-info-manager'; -import { orderItemCreator } from '@/liquid-forge/services/fetchers/order/order-item-creator'; -import { orderNumberGenerator } from '@/liquid-forge/services/fetchers/order/order-number-generator'; -import { orderValidator } from '@/liquid-forge/services/fetchers/order/order-validator'; +import { customerInfoManager } from './customer-info-manager'; +import { orderItemCreator } from './order-item-creator'; +import { orderNumberGenerator } from './order-number-generator'; +import { orderValidator } from './order-validator'; import type { CreateOrderRequest, CreateOrderResponse } from './types/order-types'; -import { EmailOrderService } from '@/liquid-forge/services/notifications/email-order-service'; -import { notificationCreator } from '@/liquid-forge/services/notifications'; -import { analyticsWebhookService } from '@/liquid-forge/services/analytics'; +import { EmailOrderService } from '../../notifications/email-order-service'; +import { notificationCreator } from '../../notifications/server'; +import { analyticsWebhookService } from '../../analytics'; export class OrderFetcher { /** diff --git a/packages/liquid-forge/services/fetchers/order/order-item-creator.ts b/packages/liquid-forge/services/fetchers/order/order-item-creator.ts index 56d8d86c..49c11233 100644 --- a/packages/liquid-forge/services/fetchers/order/order-item-creator.ts +++ b/packages/liquid-forge/services/fetchers/order/order-item-creator.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { CartSnapshot, OrderItem } from '@/liquid-forge/types'; +import type { CartSnapshot, OrderItem } from '../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import type { OrderItemData, ProductSnapshotData } from './types/order-types'; diff --git a/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts b/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts index 76fb0e4c..f6213d01 100644 --- a/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/page/page-cache-manager.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { cacheManager, getPageCacheKey, getPagesCacheKey, getPagesPrefix } from '@/liquid-forge/services/core/cache'; +import { cacheManager, getPageCacheKey, getPagesCacheKey, getPagesPrefix } from '../../core/cache'; import type { PageContext, PagesResponse } from './types/page-types'; export class PageCacheManager { diff --git a/packages/liquid-forge/services/fetchers/page/page-fetcher.ts b/packages/liquid-forge/services/fetchers/page/page-fetcher.ts index 8ba44213..506ca65d 100644 --- a/packages/liquid-forge/services/fetchers/page/page-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/page/page-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { PageContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { PageContext, TemplateError } from '../../../types'; import { pageCacheManager } from './page-cache-manager'; import { pageQueryManager } from './page-query-manager'; import { pageTransformer } from './page-transformer'; diff --git a/packages/liquid-forge/services/fetchers/page/page-transformer.ts b/packages/liquid-forge/services/fetchers/page/page-transformer.ts index e59d1be5..c0ac5724 100644 --- a/packages/liquid-forge/services/fetchers/page/page-transformer.ts +++ b/packages/liquid-forge/services/fetchers/page/page-transformer.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; +import { dataTransformer } from '../../core/data-transformer'; import type { PageContext, PageData } from './types/page-types'; export class PageTransformer { diff --git a/packages/liquid-forge/services/fetchers/page/types/page-types.ts b/packages/liquid-forge/services/fetchers/page/types/page-types.ts index d5e0203f..797814c3 100644 --- a/packages/liquid-forge/services/fetchers/page/types/page-types.ts +++ b/packages/liquid-forge/services/fetchers/page/types/page-types.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { PageContext } from '@/liquid-forge/types'; +import type { PageContext } from '../../../../types'; export type { PageContext }; diff --git a/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts b/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts index c4eebb3d..6d0ade53 100644 --- a/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts +++ b/packages/liquid-forge/services/fetchers/product/product-cache-manager.ts @@ -18,7 +18,7 @@ import { getProductHandleMapCacheKey, getProductsCacheKey, getProductsPrefix, -} from '@/liquid-forge/services/core/cache'; +} from '../../core/cache'; import type { ProductContext, ProductsResponse } from './types/product-types'; export class ProductCacheManager { diff --git a/packages/liquid-forge/services/fetchers/product/product-fetcher.ts b/packages/liquid-forge/services/fetchers/product/product-fetcher.ts index c27c35cb..304ac92d 100644 --- a/packages/liquid-forge/services/fetchers/product/product-fetcher.ts +++ b/packages/liquid-forge/services/fetchers/product/product-fetcher.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { ProductContext, TemplateError } from '@/liquid-forge/types'; +import { logger } from '../../../lib/logger'; +import type { ProductContext, TemplateError } from '../../../types'; import { productCacheManager } from './product-cache-manager'; import { productHandleManager } from './product-handle-manager'; import { productQueryManager } from './product-query-manager'; diff --git a/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts b/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts index c5ad7356..0aaa80ba 100644 --- a/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts +++ b/packages/liquid-forge/services/fetchers/product/product-handle-manager.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; +import { dataTransformer } from '../../core/data-transformer'; import { productCacheManager } from './product-cache-manager'; import { productQueryManager } from './product-query-manager'; import type { ProductData, ProductHandleMap } from './types/product-types'; diff --git a/packages/liquid-forge/services/fetchers/product/product-transformer.ts b/packages/liquid-forge/services/fetchers/product/product-transformer.ts index 6154bc53..d41e890c 100644 --- a/packages/liquid-forge/services/fetchers/product/product-transformer.ts +++ b/packages/liquid-forge/services/fetchers/product/product-transformer.ts @@ -10,8 +10,8 @@ * limitations under the License. */ -import { dataTransformer } from '@/liquid-forge/services/core/data-transformer'; -import type { ProductAttribute, ProductContext } from '@/liquid-forge/types'; +import { dataTransformer } from '../../core/data-transformer'; +import type { ProductAttribute, ProductContext } from '../../../types'; import type { ProductData } from './types/product-types'; export class ProductTransformer { diff --git a/packages/liquid-forge/services/fetchers/product/types/product-types.ts b/packages/liquid-forge/services/fetchers/product/types/product-types.ts index 786fbf43..26a46f01 100644 --- a/packages/liquid-forge/services/fetchers/product/types/product-types.ts +++ b/packages/liquid-forge/services/fetchers/product/types/product-types.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { ProductContext } from '@/liquid-forge/types'; +import type { ProductContext } from '../../../../types'; export type { ProductContext }; diff --git a/packages/liquid-forge/services/notifications/email-notification-service.ts b/packages/liquid-forge/services/notifications/email-notification-service.ts index 2180a907..c1c81b3a 100644 --- a/packages/liquid-forge/services/notifications/email-notification-service.ts +++ b/packages/liquid-forge/services/notifications/email-notification-service.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../types'; import { getOrderStatus, getPaymentStatus } from './status-translations'; import { EmailFormattingUtils, type Address, type AddressInfo } from './email-formatting-utils'; import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda'; diff --git a/packages/liquid-forge/services/notifications/email-order-service.ts b/packages/liquid-forge/services/notifications/email-order-service.ts index 9246eee8..50f8dbe9 100644 --- a/packages/liquid-forge/services/notifications/email-order-service.ts +++ b/packages/liquid-forge/services/notifications/email-order-service.ts @@ -10,7 +10,7 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; import { EmailNotificationService, EmailFormattingUtils, type Address } from './index'; diff --git a/packages/liquid-forge/services/notifications/index.ts b/packages/liquid-forge/services/notifications/index.ts index c841e881..83142ae7 100644 --- a/packages/liquid-forge/services/notifications/index.ts +++ b/packages/liquid-forge/services/notifications/index.ts @@ -30,6 +30,7 @@ export type { OrderConfirmationEmailRequest, } from './email-notification-service'; -// Exportar servicios de notificación para el panel admin -export { NotificationCreator, notificationCreator } from './notification-creator'; +// NOTE: NotificationCreator y notificationCreator son server-only +// y se exportan desde './server.ts' para evitar que sean incluidos en bundles del cliente + export type { CreateAdminNotificationRequest } from './types/notification-types'; diff --git a/packages/liquid-forge/services/notifications/notification-creator.ts b/packages/liquid-forge/services/notifications/notification-creator.ts index e502535a..c88c495c 100644 --- a/packages/liquid-forge/services/notifications/notification-creator.ts +++ b/packages/liquid-forge/services/notifications/notification-creator.ts @@ -15,8 +15,8 @@ */ import { cookiesClient } from '@/utils/server/AmplifyServer'; -import type { Order } from '@/liquid-forge/types'; -import { logger } from '@/liquid-forge/lib/logger'; +import type { Order } from '../../types'; +import { logger } from '../../lib/logger'; import type { CreateAdminNotificationRequest } from './types/notification-types'; import { EmailFormattingUtils } from './email-formatting-utils'; diff --git a/packages/liquid-forge/services/notifications/server.ts b/packages/liquid-forge/services/notifications/server.ts new file mode 100644 index 00000000..2f5eb69e --- /dev/null +++ b/packages/liquid-forge/services/notifications/server.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Fasttify LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Server-only exports + * + * Este archivo exporta código que SOLO puede usarse en el servidor + * porque depende de APIs del servidor (next/headers, etc). + * + * NO importar estos desde componentes del cliente. + */ + +export { NotificationCreator, notificationCreator } from './notification-creator'; +export type { CreateAdminNotificationRequest } from './types/notification-types'; diff --git a/packages/liquid-forge/services/notifications/types/notification-types.ts b/packages/liquid-forge/services/notifications/types/notification-types.ts index 632c6512..b2fbd8ac 100644 --- a/packages/liquid-forge/services/notifications/types/notification-types.ts +++ b/packages/liquid-forge/services/notifications/types/notification-types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Order } from '@/liquid-forge/types'; +import type { Order } from '../../../types'; export interface CreateAdminNotificationRequest { order: Order; diff --git a/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts b/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts index 0f17f7f2..474b8fd2 100644 --- a/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/core/context-builder-helper.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Tipo para builders de contexto por página diff --git a/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts b/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts index e8fe765d..a7e727df 100644 --- a/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts +++ b/packages/liquid-forge/services/page/data-loader/core/core-data-loader.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { loadDataFromAnalysis } from '@/liquid-forge/services/page/data-loader'; -import { buildContextData } from '@/liquid-forge/services/page/data-loader/core/context-builder-helper'; -import { analyzeRequiredTemplates } from '@/liquid-forge/services/page/data-loader/core/template-analyzer-helper'; -import { buildPaginationObject } from '@/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper'; -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions, PaginationInfo } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import { loadDataFromAnalysis } from '..'; +import { buildContextData } from './context-builder-helper'; +import { analyzeRequiredTemplates } from './template-analyzer-helper'; +import { buildPaginationObject } from '../pagination/pagination-builder-helper'; +import type { TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import type { PageRenderOptions, PaginationInfo } from '../../../../types/template'; /** * Interfaz para los datos principales cargados diff --git a/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts b/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts index c96ab87c..a8d7abac 100644 --- a/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/core/template-analyzer-helper.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { extractPaginationLimitFromTemplate } from '@/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor'; -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import { templateAnalyzer } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import { extractPaginationLimitFromTemplate } from '../pagination/pagination-limit-extractor'; +import type { TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import { templateAnalyzer } from '../../../templates/analysis/template-analyzer'; +import { templateLoader } from '../../../templates/template-loader'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Tipo para cargadores de templates diff --git a/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts b/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts index c0a79e45..35b3494d 100644 --- a/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/data-fetcher-helper.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions, PaginationInfo } from '@/liquid-forge/types/template'; +import { logger } from '../../../lib/logger'; +import type { TemplateAnalysis } from '../../templates/analysis/template-analyzer'; +import type { PageRenderOptions, PaginationInfo } from '../../../types/template'; import { loadSpecificData } from './handlers/data-handlers'; import { responseProcessors } from './handlers/response-processors'; diff --git a/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts b/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts index c5183ba2..1e147c05 100644 --- a/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts +++ b/packages/liquid-forge/services/page/data-loader/handlers/data-handlers.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { checkoutFetcher } from '@/liquid-forge/services/fetchers/checkout'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import type { DataLoadOptions, DataRequirement } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import { checkoutFetcher } from '../../../fetchers/checkout'; +import { dataFetcher } from '../../../fetchers/data-fetcher'; +import type { DataLoadOptions, DataRequirement } from '../../../templates/analysis/template-analyzer'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Tipo para las funciones handlers de datos diff --git a/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts b/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts index 834d94df..b7d95484 100644 --- a/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts +++ b/packages/liquid-forge/services/page/data-loader/handlers/response-processors.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { DataRequirement } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PaginationInfo } from '@/liquid-forge/types/template'; +import type { DataRequirement } from '../../../templates/analysis/template-analyzer'; +import type { PaginationInfo } from '../../../../types/template'; /** * Tipo para procesadores de respuesta de datos diff --git a/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts b/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts index c28fb541..8f75439a 100644 --- a/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts +++ b/packages/liquid-forge/services/page/data-loader/pagination/pagination-builder-helper.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PaginationInfo } from '@/liquid-forge/types/template'; +import type { TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import type { PaginationInfo } from '../../../../types/template'; interface LoadedData { products?: any[]; diff --git a/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts b/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts index 783338bc..959c972a 100644 --- a/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts +++ b/packages/liquid-forge/services/page/data-loader/pagination/pagination-limit-extractor.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import type { DataRequirement, TemplateAnalysis } from '@/liquid-forge/services/templates/analysis/template-analyzer'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../../../lib/logger'; +import type { DataRequirement, TemplateAnalysis } from '../../../templates/analysis/template-analyzer'; +import type { PageRenderOptions } from '../../../../types/template'; /** * Mapeo declarativo de tipos de página a paths de templates. diff --git a/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts b/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts index b797057f..a0ef63e2 100644 --- a/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts +++ b/packages/liquid-forge/services/page/data-loader/search/search-data-loader.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { dataFetcher } from '@/liquid-forge/services/fetchers/data-fetcher'; -import { productTransformer } from '@/liquid-forge/services/fetchers/product'; -import { extractSearchLimitsFromSettings } from '@/liquid-forge/services/page/data-loader/search/search-limits-extractor'; -import type { ProductContext } from '@/liquid-forge/types'; +import { logger } from '../../../../lib/logger'; +import { dataFetcher } from '../../../fetchers/data-fetcher'; +import { productTransformer } from '../../../fetchers/product'; +import { extractSearchLimitsFromSettings } from './search-limits-extractor'; +import type { ProductContext } from '../../../../types'; import { cookiesClient } from '@/utils/server/AmplifyServer'; /** diff --git a/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts b/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts index dbd81fe6..dcee85ac 100644 --- a/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts +++ b/packages/liquid-forge/services/page/data-loader/search/search-limits-extractor.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; +import { logger } from '../../../../lib/logger'; /** * Extrae los límites de búsqueda del archivo settings_schema.json diff --git a/packages/liquid-forge/services/page/dynamic-data-loader.ts b/packages/liquid-forge/services/page/dynamic-data-loader.ts index 99f20b03..26807a72 100644 --- a/packages/liquid-forge/services/page/dynamic-data-loader.ts +++ b/packages/liquid-forge/services/page/dynamic-data-loader.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { coreDataLoader, searchDataLoader } from '@/liquid-forge/services/page/data-loader'; -import type { CoreData } from '@/liquid-forge/services/page/data-loader/core/core-data-loader'; -import type { SearchData } from '@/liquid-forge/services/page/data-loader/search/search-data-loader'; -import type { CartContext } from '@/liquid-forge/types'; -import type { PageRenderOptions, PaginationInfo } from '@/liquid-forge/types/template'; +import { logger } from '../../lib/logger'; +import { coreDataLoader, searchDataLoader } from './data-loader'; +import type { CoreData } from './data-loader/core/core-data-loader'; +import type { SearchData } from './data-loader/search/search-data-loader'; +import type { CartContext } from '../../types'; +import type { PageRenderOptions, PaginationInfo } from '../../types/template'; /** * Resultado de la carga dinámica de datos diff --git a/packages/liquid-forge/services/rendering/global-context.ts b/packages/liquid-forge/services/rendering/global-context.ts index e45628c6..e80fcc94 100644 --- a/packages/liquid-forge/services/rendering/global-context.ts +++ b/packages/liquid-forge/services/rendering/global-context.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { flexibleLinkListService, linkListService } from '@/liquid-forge/services/core/navigation-service'; -import type { CartContext, PageContext, RenderContext, ShopContext } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import { flexibleLinkListService, linkListService } from '../core/navigation-service'; +import type { CartContext, PageContext, RenderContext, ShopContext } from '../../types'; export class ContextBuilder { /** diff --git a/packages/liquid-forge/services/rendering/metadata-generator.ts b/packages/liquid-forge/services/rendering/metadata-generator.ts index 7c1afeab..564e665c 100644 --- a/packages/liquid-forge/services/rendering/metadata-generator.ts +++ b/packages/liquid-forge/services/rendering/metadata-generator.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { OpenGraphData, RenderResult, SchemaData, TwitterCardData } from '@/liquid-forge/types/template'; +import type { OpenGraphData, RenderResult, SchemaData, TwitterCardData } from '../../types/template'; export class MetadataGenerator { /** diff --git a/packages/liquid-forge/services/rendering/page-html-cache.ts b/packages/liquid-forge/services/rendering/page-html-cache.ts index 07ddf604..b1e0f868 100644 --- a/packages/liquid-forge/services/rendering/page-html-cache.ts +++ b/packages/liquid-forge/services/rendering/page-html-cache.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { getPageCacheKey } from '@/liquid-forge/services/core/cache/cache-keys'; -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { getPageCacheKey } from '../core/cache/cache-keys'; +import { cacheManager } from '../core/cache'; +import type { PageRenderOptions } from '../../types/template'; export interface CachedPageRender { html: string; diff --git a/packages/liquid-forge/services/rendering/render-page-content.ts b/packages/liquid-forge/services/rendering/render-page-content.ts index b59b9d29..c72e3633 100644 --- a/packages/liquid-forge/services/rendering/render-page-content.ts +++ b/packages/liquid-forge/services/rendering/render-page-content.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import { sectionRenderer } from '@/liquid-forge/services/rendering/section-renderer'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import type { PageRenderOptions } from '@/liquid-forge/types/template'; +import { logger } from '../../lib/logger'; +import { liquidEngine } from '../../liquid/engine'; +import { sectionRenderer } from './section-renderer'; +import { templateLoader } from '../templates/template-loader'; +import type { PageRenderOptions } from '../../types/template'; import type { Template } from 'liquidjs'; /** diff --git a/packages/liquid-forge/services/rendering/section-renderer.ts b/packages/liquid-forge/services/rendering/section-renderer.ts index 9a3037ae..a2dbfd1d 100644 --- a/packages/liquid-forge/services/rendering/section-renderer.ts +++ b/packages/liquid-forge/services/rendering/section-renderer.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { liquidEngine } from '@/liquid-forge/liquid/engine'; -import { schemaParser } from '@/liquid-forge/services/templates/parsing/schema-parser'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import type { RenderContext } from '@/liquid-forge/types'; +import { logger } from '../../lib/logger'; +import { liquidEngine } from '../../liquid/engine'; +import { schemaParser } from '../templates/parsing/schema-parser'; +import { templateLoader } from '../templates/template-loader'; +import type { RenderContext } from '../../types'; export class SectionRenderer { /** diff --git a/packages/liquid-forge/services/templates/analysis/template-analyzer.ts b/packages/liquid-forge/services/templates/analysis/template-analyzer.ts index 2bf79de2..321cbc08 100644 --- a/packages/liquid-forge/services/templates/analysis/template-analyzer.ts +++ b/packages/liquid-forge/services/templates/analysis/template-analyzer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { LiquidSyntaxDetector } from '@/liquid-forge/services/templates/parsing/liquid-syntax-detector'; +import { logger } from '../../../lib/logger'; +import { LiquidSyntaxDetector } from '../parsing/liquid-syntax-detector'; /** * Tipo de datos que pueden ser detectados en una plantilla @@ -116,7 +116,7 @@ export class TemplateAnalyzer { storeId: string, initialTemplates: { [path: string]: string } ): Promise { - const { templateLoader } = await import('@/liquid-forge/services/templates/template-loader'); + const { templateLoader } = await import('../template-loader'); const combinedAnalysis: TemplateAnalysis = { requiredData: new Map(), diff --git a/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts b/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts index 13e36999..529afaf7 100644 --- a/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts +++ b/packages/liquid-forge/services/templates/parsing/liquid-syntax-detector.ts @@ -14,11 +14,15 @@ * limitations under the License. */ -import type { - DataLoadOptions, - DataRequirement, - TemplateAnalysis, -} from '@/liquid-forge/services/templates/analysis/template-analyzer'; +import type { DataLoadOptions, DataRequirement, TemplateAnalysis } from '../analysis/template-analyzer'; +import { + LIQUID_OBJECT_PATTERNS, + LIQUID_COLLECTION_ACCESS_PATTERNS, + LIQUID_FILTER_PATTERNS, + LIQUID_TAG_PATTERNS, + LIQUID_PAGINATION_PATTERNS, + LIQUID_OPTION_EXTRACTOR_PATTERNS, +} from '../../../lib/regex-patterns'; /** * Tipo para detectores de objetos Liquid @@ -38,14 +42,16 @@ type PaginationHandler = (match: string, analysis: TemplateAnalysis) => void; */ const loadOptionsExtractors: Record DataLoadOptions> = { products: (content: string) => { - const limitMatch = content.match(/products[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.productsLimit); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : undefined, }; }, collection_products: (content: string) => { - const collectionMatch = content.match(/collections\.([a-zA-Z0-9_-]+)\.products[^}]*limit:\s*(\d+)/i); + const collectionMatch = content.match( + new RegExp(`collections\\.([a-zA-Z0-9_-]+)\\.products[^}]*limit:\\s*(\\d+)`, 'i') + ); if (collectionMatch) { return { collectionHandle: collectionMatch[1], @@ -53,12 +59,12 @@ const loadOptionsExtractors: Record DataLo }; } - const collectionOnlyMatch = content.match(/collections\.([a-zA-Z0-9_-]+)\.products/i); + const collectionOnlyMatch = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts); return collectionOnlyMatch ? { collectionHandle: collectionOnlyMatch[1], limit: 8 } : { limit: 8 }; }, collections: (content: string) => { - const limitMatch = content.match(/collections[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.collectionsLimit); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : undefined, }; @@ -68,20 +74,18 @@ const loadOptionsExtractors: Record DataLo specific_collection: (content: string) => { const handles: string[] = []; - // Detectar collections['handle'] y collections["handle"] - const bracketMatches = content.match(/collections\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.bracketNotation); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/collections\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.bracketNotation); if (handleMatch) handles.push(handleMatch[1]); }); } - // Detectar collections.handle (acceso directo por propiedad) - const dotMatches = content.match(/collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g); + const dotMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.dotNotation); if (dotMatches) { dotMatches.forEach((match) => { - const handleMatch = match.match(/collections\.([a-zA-Z0-9_-]+)/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractCollectionHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -94,11 +98,10 @@ const loadOptionsExtractors: Record DataLo specific_product: (content: string) => { const handles: string[] = []; - // Detectar products['handle'] y products["handle"] - const bracketMatches = content.match(/products\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.specificProduct); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/products\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.specificProduct); if (handleMatch) handles.push(handleMatch[1]); }); } @@ -108,14 +111,13 @@ const loadOptionsExtractors: Record DataLo products_by_collection: (content: string) => { const handles: string[] = []; - const limitMatch = content.match(/limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.generalLimit); const limit = limitMatch ? parseInt(limitMatch[1], 10) : 8; - // Extraer handles de colecciones mencionadas para cargar sus productos - const collectionRefs = content.match(/collections\.([a-zA-Z0-9_-]+)\.products/g); + const collectionRefs = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts); if (collectionRefs) { collectionRefs.forEach((ref) => { - const handleMatch = ref.match(/collections\.([a-zA-Z0-9_-]+)/); + const handleMatch = ref.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractCollectionHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -126,7 +128,7 @@ const loadOptionsExtractors: Record DataLo }, related_products: (content: string) => { - const limitMatch = content.match(/related_products[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_FILTER_PATTERNS.productRelated); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : 4, }; @@ -135,20 +137,18 @@ const loadOptionsExtractors: Record DataLo specific_page: (content: string) => { const handles: string[] = []; - // Detectar pages['handle'] y pages["handle"] - const bracketMatches = content.match(/pages\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesBracket); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/pages\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.pagesBracketExtract); if (handleMatch) handles.push(handleMatch[1]); }); } - // Detectar pages.handle (acceso directo por propiedad) - const dotMatches = content.match(/pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g); + const dotMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesDot); if (dotMatches) { dotMatches.forEach((match) => { - const handleMatch = match.match(/pages\.([a-zA-Z0-9_-]+)/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -159,7 +159,7 @@ const loadOptionsExtractors: Record DataLo }, pages: (content: string) => { - const limitMatch = content.match(/pages[^}]*limit:\s*(\d+)/i); + const limitMatch = content.match(LIQUID_PAGINATION_PATTERNS.pagesLimit); return { limit: limitMatch ? parseInt(limitMatch[1], 10) : 10, }; @@ -178,20 +178,18 @@ const loadOptionsExtractors: Record DataLo page: (content: string) => { const handles: string[] = []; - // Detectar pages['handle'] y pages["handle"] - const bracketMatches = content.match(/pages\[['"]([^'"]+)['"]\]/g); + const bracketMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesBracket); if (bracketMatches) { bracketMatches.forEach((match) => { - const handleMatch = match.match(/pages\[['"]([^'"]+)['"]\]/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.pagesBracketExtract); if (handleMatch) handles.push(handleMatch[1]); }); } - // Detectar pages.handle (acceso directo por propiedad) - const dotMatches = content.match(/pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g); + const dotMatches = content.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesDot); if (dotMatches) { dotMatches.forEach((match) => { - const handleMatch = match.match(/pages\.([a-zA-Z0-9_-]+)/); + const handleMatch = match.match(LIQUID_COLLECTION_ACCESS_PATTERNS.extractPagesHandle); if (handleMatch && !handles.includes(handleMatch[1])) { handles.push(handleMatch[1]); } @@ -211,92 +209,85 @@ const loadOptionsExtractors: Record DataLo */ const objectDetectors: Record = { products: { - pattern: /\{\{\s*products\s*[\|\}]/g, + pattern: LIQUID_OBJECT_PATTERNS.products, optionsExtractor: loadOptionsExtractors.products, }, collection_products: { - pattern: /collections\.([a-zA-Z0-9_-]+)\.products/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts, optionsExtractor: loadOptionsExtractors.collection_products, }, collections: { - pattern: /\{\{\s*collections\s*[\|\}]/g, + pattern: LIQUID_OBJECT_PATTERNS.collections, optionsExtractor: loadOptionsExtractors.collections, }, - - // Nuevos detectores para acceso específico specific_collection: { - pattern: /collections\[['"]([^'"]+)['"]\]|collections\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.specificAccess, optionsExtractor: loadOptionsExtractors.specific_collection, }, specific_product: { - pattern: /products\[['"]([^'"]+)['"]\]/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.specificProduct, optionsExtractor: loadOptionsExtractors.specific_product, }, products_by_collection: { - pattern: /collections\.([a-zA-Z0-9_-]+)\.products/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.collectionProducts, optionsExtractor: loadOptionsExtractors.products_by_collection, }, related_products: { - pattern: /related_products|product\s*\|\s*related/g, + pattern: LIQUID_OBJECT_PATTERNS.relatedProducts, optionsExtractor: loadOptionsExtractors.related_products, }, product: { - pattern: /\{\{\s*product\./g, + pattern: LIQUID_OBJECT_PATTERNS.product, optionsExtractor: loadOptionsExtractors.product, }, collection: { - pattern: /\{\{\s*collection\./g, + pattern: LIQUID_OBJECT_PATTERNS.collection, optionsExtractor: loadOptionsExtractors.collection, }, linklists: { - pattern: /\{\{\s*linklists\./g, + pattern: LIQUID_OBJECT_PATTERNS.linklists, optionsExtractor: loadOptionsExtractors.linklists, }, shop: { - pattern: /\{\{\s*shop\./g, + pattern: LIQUID_OBJECT_PATTERNS.shop, optionsExtractor: loadOptionsExtractors.shop, }, specific_page: { - pattern: /pages\[['"]([^'"]+)['"]\]|pages\.([a-zA-Z0-9_-]+)(?!\.\w)/g, + pattern: LIQUID_COLLECTION_ACCESS_PATTERNS.specificPage, optionsExtractor: loadOptionsExtractors.specific_page, }, pages: { - pattern: /\{\{\s*pages\s*[\|\}]/g, + pattern: LIQUID_OBJECT_PATTERNS.pages, optionsExtractor: loadOptionsExtractors.pages, }, policies: { - pattern: /for\s+item\s+in\s+policies|for\s+policy\s+in\s+policies|\{\{\s*policies\s*[\|\}]/g, + pattern: LIQUID_PAGINATION_PATTERNS.policies, optionsExtractor: loadOptionsExtractors.policies, }, page: { - pattern: /\{\{\s*page\./g, + pattern: LIQUID_OBJECT_PATTERNS.page, optionsExtractor: loadOptionsExtractors.page, }, blog: { - pattern: /\{\{\s*blog\./g, + pattern: LIQUID_OBJECT_PATTERNS.blog, optionsExtractor: loadOptionsExtractors.blog, }, pagination: { - pattern: /\{\%\s*paginate/g, + pattern: LIQUID_OBJECT_PATTERNS.pagination, optionsExtractor: loadOptionsExtractors.pagination, }, checkout: { - pattern: /\{\{\s*checkout\./g, + pattern: LIQUID_OBJECT_PATTERNS.checkout, optionsExtractor: loadOptionsExtractors.checkout, }, checkout_confirmation: { - pattern: /\{\{\s*checkout\./g, + pattern: LIQUID_OBJECT_PATTERNS.checkout, optionsExtractor: loadOptionsExtractors.checkout_confirmation, }, }; export class LiquidSyntaxDetector { - private static readonly TAG_PATTERNS = { - paginate: /\{\%\s*paginate\s+([^%]+)\%\}/g, - section: /\{\%\s*section\s+['"]([^'"]+)['"]\s*\%\}/g, - render: /\{\%\s*render\s+['"]([^'"]+)['"]/g, - include: /\{\%\s*include\s+['"]([^'"]+)['"]/g, - }; + private static readonly TAG_PATTERNS = LIQUID_TAG_PATTERNS; public static detectLiquidObjects(content: string, analysis: TemplateAnalysis): void { for (const [objectType, detector] of Object.entries(objectDetectors)) { @@ -310,14 +301,12 @@ export class LiquidSyntaxDetector { } public static detectPagination(content: string, analysis: TemplateAnalysis): void { - const paginatePattern = /\{%\s*paginate\s+(.+?)\s*%\}/; - const match = content.match(paginatePattern); + const match = content.match(LIQUID_PAGINATION_PATTERNS.paginate); if (match) { analysis.hasPagination = true; - const paginatedObject = match[1].split(/\s+by\s+/)[0]; + const paginatedObject = match[1].split(LIQUID_FILTER_PATTERNS.paginateBy)[0]; - // Determinar si se paginan productos o colecciones if (paginatedObject.includes('products')) { analysis.requiredData.set('products', {}); } else if (paginatedObject.includes('collections')) { @@ -328,20 +317,18 @@ export class LiquidSyntaxDetector { } public static detectDependencies(content: string, analysis: TemplateAnalysis): void { - // Detectar secciones this.extractMatches(content, this.TAG_PATTERNS.section, (match) => { - const sectionName = this.extractName(match, /section\s+['"]([^'"]+)['"]/i); + const sectionName = this.extractName(match, LIQUID_OPTION_EXTRACTOR_PATTERNS.sectionName); if (sectionName) { analysis.usedSections.push(sectionName); analysis.dependencies.push(`sections/${sectionName}.liquid`); } }); - // Detectar snippets (render e include) const snippetPatterns = [this.TAG_PATTERNS.render, this.TAG_PATTERNS.include]; for (const pattern of snippetPatterns) { this.extractMatches(content, pattern, (match) => { - const snippetName = this.extractName(match, /(?:render|include)\s+['"]([^'"]+)['"]/i); + const snippetName = this.extractName(match, LIQUID_OPTION_EXTRACTOR_PATTERNS.snippetName); if (snippetName) { analysis.dependencies.push(`snippets/${snippetName}.liquid`); } diff --git a/packages/liquid-forge/services/templates/parsing/schema-parser.ts b/packages/liquid-forge/services/templates/parsing/schema-parser.ts index 53ae79fd..0e437d3d 100644 --- a/packages/liquid-forge/services/templates/parsing/schema-parser.ts +++ b/packages/liquid-forge/services/templates/parsing/schema-parser.ts @@ -14,8 +14,10 @@ * limitations under the License. */ +import { JSON_PARSING_PATTERNS, MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; + export class SchemaParser { - private static readonly SCHEMA_REGEX = /{%\s*schema\s*%}([\s\S]*?){%\s*endschema\s*%}/i; + private static readonly SCHEMA_REGEX = JSON_PARSING_PATTERNS.liquidSchema; /** * Extrae y parsea el schema completo de un template (una sola vez) @@ -44,11 +46,11 @@ export class SchemaParser { */ private cleanJSON(content: string): string { return content - .replace(/\/\/.*$/gm, '') // Comentarios // - .replace(/\/\*[\s\S]*?\*\//g, '') // Comentarios /* */ - .replace(/,(\s*[}\]])/g, '$1') // Comas finales - .replace(/,,+/g, ',') // Comas dobles - .replace(/\s+/g, ' ') // Espacios extra + .replace(JSON_PARSING_PATTERNS.lineComment, '') + .replace(JSON_PARSING_PATTERNS.blockComment, '') + .replace(JSON_PARSING_PATTERNS.trailingComma, '$1') + .replace(JSON_PARSING_PATTERNS.multipleCommas, ',') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') .trim(); } diff --git a/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts b/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts index 3c463edf..25769277 100644 --- a/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts +++ b/packages/liquid-forge/services/templates/sync/template-dev-synchronizer.ts @@ -14,11 +14,12 @@ * limitations under the License. */ -import { logger } from '@/liquid-forge/lib/logger'; -import { cacheManager } from '@/liquid-forge/services/core/cache'; -import { cacheInvalidationService } from '@/liquid-forge/services/core/cache/cache-invalidation-service'; -import { templateLoader } from '@/liquid-forge/services/templates/template-loader'; -import { PostCSSProcessor } from '@/liquid-forge/services/themes/optimization/postcss-processor'; +import { logger } from '../../../lib/logger'; +import { cacheManager } from '../../core/cache'; +import { cacheInvalidationService } from '../../core/cache/cache-invalidation-service'; +import { PATH_PATTERNS } from '../../../lib/regex-patterns'; +import { templateLoader } from '../template-loader'; +import { PostCSSProcessor } from '../../themes/optimization/postcss-processor'; import { getContentType, isBinaryFile } from '@/lib/utils/file-utils'; import { DeleteObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { Upload } from '@aws-sdk/lib-storage'; @@ -190,17 +191,12 @@ export class TemplateDevSynchronizer { throw new Error(`Unsafe path: ${filePath}`); } const validatedPath = path.resolve(filePath); - const relativePath = path.relative(this.localDir, validatedPath).replace(/\\/g, '/'); + const relativePath = path.relative(this.localDir, validatedPath).replace(PATH_PATTERNS.backslashes, '/'); return { validatedPath, relativePath }; } - /** - * Construye la clave de S3 para un archivo relativo al directorio local. - * @param relativePath - Ruta relativa desde el directorio local - * @returns Clave S3 normalizada - */ private buildS3Key(relativePath: string): string { - return `templates/${this.storeId}/${relativePath}`.replace(/\\/g, '/'); + return `templates/${this.storeId}/${relativePath}`.replace(PATH_PATTERNS.backslashes, '/'); } /** @@ -240,7 +236,7 @@ export class TemplateDevSynchronizer { await this.deleteFileFromS3(s3Key); } - const templateName = relativePath.replace(/\\/g, '/'); + const templateName = relativePath.replace(PATH_PATTERNS.backslashes, '/'); templateLoader.invalidateTemplateCache(this.storeId, templateName); @@ -275,7 +271,7 @@ export class TemplateDevSynchronizer { const isBinary = isBinaryFile(validatedPath); - const relativePath = path.relative(this.localDir, validatedPath).replace(/\\/g, '/'); + const relativePath = path.relative(this.localDir, validatedPath).replace(PATH_PATTERNS.backslashes, '/'); let body; if (isBinary) { diff --git a/packages/liquid-forge/services/templates/template-loader.ts b/packages/liquid-forge/services/templates/template-loader.ts index 2c6595ec..4b79727d 100644 --- a/packages/liquid-forge/services/templates/template-loader.ts +++ b/packages/liquid-forge/services/templates/template-loader.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { liquidEngine } from '@/liquid-forge/liquid/engine'; +import { liquidEngine } from '../../liquid/engine'; import { cacheManager, getAssetCacheKey, @@ -22,8 +22,8 @@ import { getCompiledTemplatesPrefix, getTemplateCacheKey, getTemplatesPrefix, -} from '@/liquid-forge/services/core/cache'; -import type { TemplateCache, TemplateError } from '@/liquid-forge/types'; +} from '../core/cache'; +import type { TemplateCache, TemplateError } from '../../types'; import { getCdnUrlForKey } from '@/utils/server'; import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; import type { Template } from 'liquidjs'; diff --git a/packages/liquid-forge/services/themes/core/theme-processor.ts b/packages/liquid-forge/services/themes/core/theme-processor.ts index eef69795..0c0fc0f7 100644 --- a/packages/liquid-forge/services/themes/core/theme-processor.ts +++ b/packages/liquid-forge/services/themes/core/theme-processor.ts @@ -14,10 +14,11 @@ * limitations under the License. */ -import { TemplateAnalysis } from '@/liquid-forge/exports'; -import { RendererLogger } from '@/liquid-forge/lib/logger'; -import { SchemaParser } from '@/liquid-forge/services/templates/parsing/schema-parser'; +import { TemplateAnalysis } from '../../../exports'; +import { RendererLogger } from '../../../lib/logger'; +import { SchemaParser } from '../../templates/parsing/schema-parser'; import { PostCSSProcessor } from '../optimization/postcss-processor'; +import { SANITIZATION_PATTERNS } from '../../../lib/regex-patterns'; import JSZip from 'jszip'; import type { ProcessedTheme, @@ -407,7 +408,7 @@ export class ThemeProcessor { */ private generateThemeId(name: string, storeId: string): string { const timestamp = Date.now(); - const sanitizedName = name.toLowerCase().replace(/[^a-z0-9]/g, '-'); + const sanitizedName = name.toLowerCase().replace(SANITIZATION_PATTERNS.themeName, '-'); return `${sanitizedName}-${storeId}-${timestamp}`; } diff --git a/packages/liquid-forge/services/themes/optimization/postcss-processor.ts b/packages/liquid-forge/services/themes/optimization/postcss-processor.ts index a7113eff..aea7f913 100644 --- a/packages/liquid-forge/services/themes/optimization/postcss-processor.ts +++ b/packages/liquid-forge/services/themes/optimization/postcss-processor.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { MINIFICATION_PATTERNS, CSS_MINIFICATION_PATTERNS } from '../../../lib/regex-patterns'; + export interface PostCSSOptions { autoprefixer: boolean; removeComments: boolean; @@ -65,14 +67,13 @@ export class PostCSSProcessor { this.cssProcessor = null; } - // Para JS usamos minificación simple (como hacen los desarrolladores de Shopify) this.jsProcessor = { minify: (js: string): string => { return js - .replace(/\/\/.*$/gm, '') // Comentarios de línea - .replace(/\/\*[\s\S]*?\*\//g, '') // Comentarios de bloque - .replace(/\s+/g, ' ') // Espacios - .replace(/\s*([=+\-*/%&|^!~?:,;{}()\[\]<>])\s*/g, '$1') + .replace(MINIFICATION_PATTERNS.lineComment, '') + .replace(MINIFICATION_PATTERNS.blockComment, '') + .replace(MINIFICATION_PATTERNS.multipleSpaces, ' ') + .replace(MINIFICATION_PATTERNS.operatorSpaces, '$1') .trim(); }, }; diff --git a/packages/liquid-forge/services/themes/storage/s3-storage-service.ts b/packages/liquid-forge/services/themes/storage/s3-storage-service.ts index a951d839..140dc440 100644 --- a/packages/liquid-forge/services/themes/storage/s3-storage-service.ts +++ b/packages/liquid-forge/services/themes/storage/s3-storage-service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { RendererLogger } from '@/liquid-forge/lib/logger'; +import { RendererLogger } from '../../../lib/logger'; import { getCdnUrlForKey } from '@/utils/server'; import { getContentType } from '@/lib/utils'; import { diff --git a/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts b/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts index 6520e4a3..d9aee66c 100644 --- a/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts +++ b/packages/liquid-forge/services/themes/validation/rules/liquid-syntax-rules.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { LiquidEngine } from '@/liquid-forge/liquid/engine'; +import { LiquidEngine } from '../../../../liquid/engine'; import type { ThemeFile, ThemeValidationConfig, diff --git a/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts b/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts index f22d17dd..635e7962 100644 --- a/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts +++ b/packages/liquid-forge/services/themes/validation/rules/performance-rules.ts @@ -22,6 +22,7 @@ import type { ValidationWarning, } from '../../types'; import { filterAssetFiles, filterTextFiles } from '../utils/file-filters'; +import { SECURITY_PATTERNS } from '../../../../lib/regex-patterns'; export class PerformanceRules { /** @@ -139,10 +140,9 @@ export class PerformanceRules { for (const file of textFiles) { const content = file.content as string; - // Contar recursos externos - const externalScripts = (content.match(/]*src=["'](https?:\/\/[^"']+)["'][^>]*>/gi) || []).length; - const externalCss = (content.match(/]*href=["'](https?:\/\/[^"']+)["'][^>]*>/gi) || []).length; - const externalImages = (content.match(/src=["'](https?:\/\/[^"']+)["']/gi) || []).length; + const externalScripts = (content.match(SECURITY_PATTERNS.externalScript) || []).length; + const externalCss = (content.match(SECURITY_PATTERNS.externalCss) || []).length; + const externalImages = (content.match(SECURITY_PATTERNS.externalImage) || []).length; if (externalScripts > 5) { warnings.push({ diff --git a/packages/liquid-forge/services/themes/validation/rules/security-rules.ts b/packages/liquid-forge/services/themes/validation/rules/security-rules.ts index 701c6334..0625eefd 100644 --- a/packages/liquid-forge/services/themes/validation/rules/security-rules.ts +++ b/packages/liquid-forge/services/themes/validation/rules/security-rules.ts @@ -22,6 +22,11 @@ import type { ValidationWarning, } from '../../types'; import { filterTextFiles } from '../utils/file-filters'; +import { + SECURITY_PATTERNS, + DANGEROUS_FUNCTION_PATTERNS, + SENSITIVE_DATA_PATTERNS, +} from '../../../../lib/regex-patterns'; export class SecurityRules { /** @@ -49,8 +54,7 @@ export class SecurityRules { } } - // Verificar scripts externos - const externalScriptMatches = content.match(/]*src=["'](https?:\/\/[^"']+)["'][^>]*>/gi); + const externalScriptMatches = content.match(SECURITY_PATTERNS.externalScript); if (externalScriptMatches) { warnings.push({ type: 'security', @@ -60,8 +64,7 @@ export class SecurityRules { }); } - // Verificar CSS externo - const externalCssMatches = content.match(/]*href=["'](https?:\/\/[^"']+)["'][^>]*>/gi); + const externalCssMatches = content.match(SECURITY_PATTERNS.externalCss); if (externalCssMatches) { warnings.push({ type: 'security', @@ -83,14 +86,14 @@ export class SecurityRules { const warnings: ValidationWarning[] = []; const dangerousPatterns = [ - /eval\s*\(/, - /Function\s*\(/, - /setTimeout\s*\(/, - /setInterval\s*\(/, - /document\.write/, - /document\.writeln/, - /innerHTML\s*=/, - /outerHTML\s*=/, + DANGEROUS_FUNCTION_PATTERNS.eval, + DANGEROUS_FUNCTION_PATTERNS.functionConstructor, + DANGEROUS_FUNCTION_PATTERNS.setTimeout, + DANGEROUS_FUNCTION_PATTERNS.setInterval, + DANGEROUS_FUNCTION_PATTERNS.documentWrite, + DANGEROUS_FUNCTION_PATTERNS.documentWriteln, + DANGEROUS_FUNCTION_PATTERNS.innerHTML, + DANGEROUS_FUNCTION_PATTERNS.outerHTML, ]; // Filtrar solo archivos basados en texto @@ -127,8 +130,7 @@ export class SecurityRules { for (const file of textFiles) { const content = file.content as string; - // Buscar URLs externas - const urlMatches = content.match(/https?:\/\/[^\s"']+/gi); + const urlMatches = content.match(SECURITY_PATTERNS.externalUrl); if (urlMatches) { for (const url of urlMatches) { // Verificar si es un dominio sospechoso @@ -154,7 +156,14 @@ export class SecurityRules { const errors: ValidationError[] = []; const warnings: ValidationWarning[] = []; - const sensitivePatterns = [/api_key/, /secret/, /password/, /token/, /private_key/, /access_key/]; + const sensitivePatterns = [ + SENSITIVE_DATA_PATTERNS.apiKey, + SENSITIVE_DATA_PATTERNS.secret, + SENSITIVE_DATA_PATTERNS.password, + SENSITIVE_DATA_PATTERNS.token, + SENSITIVE_DATA_PATTERNS.privateKey, + SENSITIVE_DATA_PATTERNS.accessKey, + ]; // Filtrar solo archivos basados en texto const textFiles = filterTextFiles(files); diff --git a/packages/liquid-forge/services/themes/validation/validation-engine.ts b/packages/liquid-forge/services/themes/validation/validation-engine.ts index 3ea19d0a..4c8a4dd1 100644 --- a/packages/liquid-forge/services/themes/validation/validation-engine.ts +++ b/packages/liquid-forge/services/themes/validation/validation-engine.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { RendererLogger } from '@/liquid-forge/lib/logger'; +import { RendererLogger } from '../../../lib/logger'; import type { ThemeFile, ThemeValidationConfig, ValidationResult } from '../types'; import { FileStructureRules } from './rules/file-structure-rules'; import { LiquidSyntaxRules } from './rules/liquid-syntax-rules'; diff --git a/packages/liquid-forge/types/template.ts b/packages/liquid-forge/types/template.ts index 98e36bee..2f0afd54 100644 --- a/packages/liquid-forge/types/template.ts +++ b/packages/liquid-forge/types/template.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { AssetCollector } from '@/liquid-forge/services/rendering/asset-collector'; +import type { AssetCollector } from '../services/rendering/asset-collector'; export interface TemplateFile { path: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74bcba1b..25f4c2c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -413,6 +413,9 @@ importers: jest: specifier: ^30.0.4 version: 30.2.0(@types/node@24.6.2)(ts-node@10.9.2(@types/node@24.6.2)(typescript@5.9.3)) + tsx: + specifier: ^4.20.6 + version: 4.20.6 typescript: specifier: ^5.8.3 version: 5.9.3 @@ -10843,7 +10846,7 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.621.0) + '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.901.0) '@aws-sdk/client-sts': 3.621.0 '@aws-sdk/core': 3.621.0 '@aws-sdk/credential-provider-node': 3.621.0(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/client-sts@3.621.0) @@ -10981,7 +10984,7 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.901.0) + '@aws-sdk/client-sso-oidc': 3.621.0(@aws-sdk/client-sts@3.621.0) '@aws-sdk/client-sts': 3.621.0 '@aws-sdk/core': 3.621.0 '@aws-sdk/credential-provider-node': 3.621.0(@aws-sdk/client-sso-oidc@3.621.0(@aws-sdk/client-sts@3.621.0))(@aws-sdk/client-sts@3.621.0) diff --git a/scripts/upload-base-template.js b/scripts/upload-base-template.js index 74b59d2f..0cec2ecf 100644 --- a/scripts/upload-base-template.js +++ b/scripts/upload-base-template.js @@ -24,7 +24,7 @@ const CLOUDFRONT_DISTRIBUTION_ID = process.env.CLOUDFRONT_DISTRIBUTION_ID; const BASE_TEMPLATE_PREFIX = 'base-templates/default/'; const TEMPLATE_DIR = join(process.cwd(), 'packages/example-themes/fasttify/theme'); const FILTER_MODULES_DIR = join(process.cwd(), 'packages/liquid-forge/liquid/tags/filters/js'); -const FILTER_MODULES_PREFIX = 'assets/'; +const FILTER_MODULES_PREFIX = 'assets/global-static/'; const s3Client = new S3Client({ region: REGION, @@ -231,7 +231,7 @@ async function invalidateCloudFrontCache() { InvalidationBatch: { Paths: { Quantity: 1, - Items: ['/assets/*'], + Items: ['/assets/global-static/*'], }, CallerReference: `invalidation-${Date.now()}`, }, @@ -271,7 +271,7 @@ async function main() { console.log('\nSubida completada con éxito!'); console.log(`Se subieron ${templateResults.length} archivos de plantilla.`); console.log(`Se subieron ${moduleResults.length} módulos de filtros.`); - console.log(`\nLos módulos están disponibles en: https://cdn.fasttify.com/assets/`); + console.log(`\nLos módulos están disponibles en: https://cdn.fasttify.com/assets/global-static/`); console.log('\nInvalidando cache de CloudFront...'); await invalidateCloudFrontCache(); diff --git a/test/unit/cache/cache-invalidation-service.test.ts b/test/unit/cache/cache-invalidation-service.test.ts index b65bdefd..2e670e39 100644 --- a/test/unit/cache/cache-invalidation-service.test.ts +++ b/test/unit/cache/cache-invalidation-service.test.ts @@ -1,5 +1,5 @@ -jest.mock('@/packages/liquid-forge/services/core/cache/cache-manager', () => { - const original = jest.requireActual('@/packages/liquid-forge/services/core/cache/cache-manager'); +jest.mock('@fasttify/liquid-forge/services/core/cache/cache-manager', () => { + const original = jest.requireActual('@fasttify/liquid-forge/services/core/cache/cache-manager'); return { ...original, cacheManager: { @@ -11,8 +11,7 @@ jest.mock('@/packages/liquid-forge/services/core/cache/cache-manager', () => { }; }); -import { cacheInvalidationService } from '@/packages/liquid-forge/services/core/cache/cache-invalidation-service'; -import { cacheManager } from '@/packages/liquid-forge/services/core/cache'; +import { cacheInvalidationService, cacheManager } from '@fasttify/liquid-forge/services/core/cache'; describe('CacheInvalidationService', () => { beforeEach(() => { diff --git a/test/unit/cache/cache-manager.test.ts b/test/unit/cache/cache-manager.test.ts index 4466787e..c1ea61ae 100644 --- a/test/unit/cache/cache-manager.test.ts +++ b/test/unit/cache/cache-manager.test.ts @@ -1,4 +1,4 @@ -import { cacheManager } from '@/packages/liquid-forge/services/core/cache'; +import { cacheManager } from '@fasttify/liquid-forge/services/core/cache'; describe('CacheManager (node-cache integration)', () => { beforeEach(() => { diff --git a/test/unit/liquid-filters/ecommerce-filters.test.ts b/test/unit/liquid-filters/ecommerce-filters.test.ts index 702c5224..ce6c403e 100644 --- a/test/unit/liquid-filters/ecommerce-filters.test.ts +++ b/test/unit/liquid-filters/ecommerce-filters.test.ts @@ -1,5 +1,5 @@ import { Liquid } from 'liquidjs'; -import { ecommerceFilters } from '@/packages/liquid-forge/liquid/filters/ecommerce-filters'; +import { ecommerceFilters } from '@fasttify/liquid-forge/liquid/filters/ecommerce-filters'; describe('Ecommerce Filters - Image Optimization', () => { let liquid: Liquid; diff --git a/test/unit/liquid-tags/render-tag.test.ts b/test/unit/liquid-tags/render-tag.test.ts index c2d5ffa6..18984c88 100644 --- a/test/unit/liquid-tags/render-tag.test.ts +++ b/test/unit/liquid-tags/render-tag.test.ts @@ -1,12 +1,13 @@ -import { RenderTag } from '@/packages/liquid-forge/liquid/tags/core/render-tag'; +import { RenderTag } from '@fasttify/liquid-forge/liquid/tags/core/render-tag'; import { beforeEach, describe, expect, it, jest } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid, mockTemplateLoader } from './setup'; -// Mock del TemplateLoader -jest.mock('@/packages/liquid-forge/services/templates/template-loader', () => ({ +const mockGetInstance = jest.fn(() => mockTemplateLoader); + +jest.mock('@fasttify/liquid-forge/services/templates/template-loader', () => ({ TemplateLoader: { - getInstance: () => mockTemplateLoader, + getInstance: mockGetInstance, }, })); @@ -18,7 +19,9 @@ describe('RenderTag', () => { liquid.registerTag('render', RenderTag); // Reset mocks - jest.clearAllMocks(); + mockGetInstance.mockClear(); + mockTemplateLoader.loadTemplate.mockClear(); + mockTemplateLoader.loadTemplate.mockResolvedValue('
Mock Template
'); }); it('should render a basic snippet', async () => { diff --git a/test/unit/liquid-tags/schema-tag.test.ts b/test/unit/liquid-tags/schema-tag.test.ts index 457b01a0..283e7944 100644 --- a/test/unit/liquid-tags/schema-tag.test.ts +++ b/test/unit/liquid-tags/schema-tag.test.ts @@ -1,4 +1,4 @@ -import { SchemaTag } from '@/packages/liquid-forge/liquid/tags/data/schema-tag'; +import { SchemaTag } from '@fasttify/liquid-forge/liquid/tags/data/schema-tag'; import { beforeEach, describe, expect, it } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid } from './setup'; diff --git a/test/unit/liquid-tags/script-tag.test.ts b/test/unit/liquid-tags/script-tag.test.ts index 08c9872a..a79b5f6d 100644 --- a/test/unit/liquid-tags/script-tag.test.ts +++ b/test/unit/liquid-tags/script-tag.test.ts @@ -1,4 +1,4 @@ -import { ScriptTag } from '@/packages/liquid-forge/liquid/tags/styling/script-tag'; +import { ScriptTag } from '@fasttify/liquid-forge/liquid/tags/styling/script-tag'; import { beforeEach, describe, expect, it } from '@jest/globals'; import { Liquid } from 'liquidjs'; import { createTestContext, createTestLiquid } from './setup'; diff --git a/test/unit/liquid-tags/setup.ts b/test/unit/liquid-tags/setup.ts index 25580a27..3dd2f7d4 100644 --- a/test/unit/liquid-tags/setup.ts +++ b/test/unit/liquid-tags/setup.ts @@ -1,5 +1,5 @@ import { Liquid } from 'liquidjs'; -import type { RenderContext } from '@/packages/liquid-forge/types/template'; +import type { RenderContext } from '@fasttify/liquid-forge/types/template'; // Crear contexto de prueba simple export const createTestContext = (customData: Partial = {}): RenderContext => { diff --git a/test/unit/pipeline-steps/build-context-step.test.ts b/test/unit/pipeline-steps/build-context-step.test.ts index d007287a..eb5fa06d 100644 --- a/test/unit/pipeline-steps/build-context-step.test.ts +++ b/test/unit/pipeline-steps/build-context-step.test.ts @@ -1,11 +1,11 @@ -jest.mock('@/packages/liquid-forge/services/rendering/global-context', () => ({ +jest.mock('@fasttify/liquid-forge/services/rendering/global-context', () => ({ contextBuilder: { createRenderContext: jest.fn().mockResolvedValue({ context: 'ok' }), }, })); -import { buildContextStep } from '@/packages/liquid-forge/renderers/pipeline-steps/build-context-step'; -import { contextBuilder } from '@/packages/liquid-forge/services/rendering/global-context'; +import { buildContextStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/build-context-step'; +import { contextBuilder } from '@fasttify/liquid-forge/services/rendering/global-context'; describe('buildContextStep', () => { it('debe construir el contexto correctamente', async () => { diff --git a/test/unit/pipeline-steps/initialize-engine-step.test.ts b/test/unit/pipeline-steps/initialize-engine-step.test.ts index f37f8bb1..d0653a72 100644 --- a/test/unit/pipeline-steps/initialize-engine-step.test.ts +++ b/test/unit/pipeline-steps/initialize-engine-step.test.ts @@ -1,5 +1,5 @@ // Mock para liquidEngine y assetCollector -jest.mock('@/packages/liquid-forge/liquid/engine', () => ({ +jest.mock('@fasttify/liquid-forge/liquid/engine', () => ({ liquidEngine: { assetCollector: { clear: jest.fn(), @@ -7,8 +7,8 @@ jest.mock('@/packages/liquid-forge/liquid/engine', () => ({ }, })); -import { liquidEngine } from '@/packages/liquid-forge/liquid/engine'; -import { initializeEngineStep } from '@/packages/liquid-forge/renderers/pipeline-steps/initialize-engine-step'; +import { liquidEngine } from '@fasttify/liquid-forge/liquid/engine'; +import { initializeEngineStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/initialize-engine-step'; describe('initializeEngineStep', () => { it('debe limpiar el assetCollector y retornar los datos sin modificar', async () => { diff --git a/test/unit/pipeline-steps/load-data-step.test.ts b/test/unit/pipeline-steps/load-data-step.test.ts index 76c590eb..9ac1ce31 100644 --- a/test/unit/pipeline-steps/load-data-step.test.ts +++ b/test/unit/pipeline-steps/load-data-step.test.ts @@ -1,5 +1,5 @@ // Mocks para dependencias -jest.mock('@/packages/liquid-forge/services/templates/template-loader', () => ({ +jest.mock('@fasttify/liquid-forge/services/templates/template-loader', () => ({ templateLoader: { loadMainLayout: jest.fn().mockResolvedValue('layout-content'), loadMainLayoutCompiled: jest.fn().mockResolvedValue(['compiled-layout']), @@ -7,25 +7,25 @@ jest.mock('@/packages/liquid-forge/services/templates/template-loader', () => ({ loadCompiledTemplate: jest.fn().mockResolvedValue(['compiled-page-template']), }, })); -jest.mock('@/packages/liquid-forge/services/page/dynamic-data-loader', () => ({ +jest.mock('@fasttify/liquid-forge/services/page/dynamic-data-loader', () => ({ dynamicDataLoader: { loadDynamicData: jest .fn() .mockResolvedValue({ analysis: { requiredData: new Map(), liquidObjects: [], dependencies: [] } }), }, })); -jest.mock('@/packages/liquid-forge/services/fetchers/data-fetcher', () => ({ +jest.mock('@fasttify/liquid-forge/services/fetchers/data-fetcher', () => ({ dataFetcher: { getStoreNavigationMenus: jest.fn().mockResolvedValue('store-template'), }, })); -jest.mock('@/packages/liquid-forge/config/page-config', () => ({ +jest.mock('@fasttify/liquid-forge/config/page-config', () => ({ pageConfig: { getTemplatePath: jest.fn().mockReturnValue('page.liquid'), }, })); -import { loadDataStep } from '@/packages/liquid-forge/renderers/pipeline-steps/load-data-step'; +import { loadDataStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/load-data-step'; describe('loadDataStep', () => { it('debe cargar layout, compilados, datos dinámicos y template en paralelo', async () => { diff --git a/test/unit/pipeline-steps/render-content-step.test.ts b/test/unit/pipeline-steps/render-content-step.test.ts index b8bb686d..125a6d5e 100644 --- a/test/unit/pipeline-steps/render-content-step.test.ts +++ b/test/unit/pipeline-steps/render-content-step.test.ts @@ -1,9 +1,9 @@ -jest.mock('@/packages/liquid-forge/services/rendering/render-page-content', () => ({ +jest.mock('@fasttify/liquid-forge/services/rendering/render-page-content', () => ({ renderPageContent: jest.fn().mockResolvedValue('contenido-renderizado'), })); -import { renderContentStep } from '@/packages/liquid-forge/renderers/pipeline-steps/render-content-step'; -import { renderPageContent } from '@/packages/liquid-forge/services/rendering/render-page-content'; +import { renderContentStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/render-content-step'; +import { renderPageContent } from '@fasttify/liquid-forge/services/rendering/render-page-content'; describe('renderContentStep', () => { it('debe renderizar el contenido de la página y asignarlo a renderedContent', async () => { diff --git a/test/unit/pipeline-steps/resolve-store-step.test.ts b/test/unit/pipeline-steps/resolve-store-step.test.ts index e1461d7d..f121ad64 100644 --- a/test/unit/pipeline-steps/resolve-store-step.test.ts +++ b/test/unit/pipeline-steps/resolve-store-step.test.ts @@ -8,14 +8,14 @@ jest.mock('aws-amplify/auth/server', () => ({ getCurrentUser: jest.fn(), })); -jest.mock('@/packages/liquid-forge/services/core/domain-resolver', () => ({ +jest.mock('@fasttify/liquid-forge/services/core/domain-resolver', () => ({ domainResolver: { resolveStoreByDomain: jest.fn(), }, })); -import { resolveStoreStep } from '@/packages/liquid-forge/renderers/pipeline-steps/resolve-store-step'; -import { domainResolver } from '@/packages/liquid-forge/services/core/domain-resolver'; +import { resolveStoreStep } from '@fasttify/liquid-forge/renderers/pipeline-steps/resolve-store-step'; +import { domainResolver } from '@fasttify/liquid-forge/services/core/domain-resolver'; describe('resolveStoreStep', () => { it('debe resolver la tienda correctamente', async () => {