webs-sdk 0.17.3 → 0.17.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
- # WebsSDK
1
+ # webs-sdk
2
2
 
3
- SDK completo para aplicaciones web que proporciona funcionalidades esenciales para el desarrollo de aplicaciones modernas.
3
+ SDK interno npm para portales Nexum. Encapsula toda la lógica común: autenticación, sesiones, comunicación con la API, tracking, internacionalización y componentes React reutilizables.
4
+
5
+ **Versión actual:** 0.17.4
4
6
 
5
7
  ## Instalación
6
8
 
@@ -8,391 +10,442 @@ SDK completo para aplicaciones web que proporciona funcionalidades esenciales pa
8
10
  npm install webs-sdk
9
11
  ```
10
12
 
11
- ## Uso Básico
13
+ ## Uso básico
12
14
 
13
- ```javascript
15
+ ```typescript
14
16
  import WebsSDK from 'webs-sdk';
15
17
 
16
- // Inicializar el SDK
18
+ // Inicializar sesión
17
19
  await WebsSDK.Session.init();
18
20
 
19
- // Realizar una petición de red
20
- const response = await WebsSDK.Networking.request('https://api.example.com/data');
21
-
22
- // Almacenar datos
23
- await WebsSDK.Storage.storeData('user_preferences', { theme: 'dark' });
21
+ // Petición HTTP
22
+ const data = await WebsSDK.Networking.request('/api/endpoint', { param: 'value' });
24
23
 
25
- // Obtener datos
26
- const preferences = await WebsSDK.Storage.getData('user_preferences');
24
+ // Almacenamiento local
25
+ WebsSDK.Storage.set('key', 'value');
26
+ const value = WebsSDK.Storage.get('key');
27
27
  ```
28
28
 
29
- ## Librerías Disponibles
30
-
31
- ### 🌐 Networking
32
- Manejo avanzado de peticiones HTTP y comunicación con APIs.
29
+ ## Librerías disponibles
33
30
 
34
- ```javascript
35
- // Petición básica
36
- const data = await WebsSDK.Networking.request('/api/endpoint', { param: 'value' });
31
+ ### Networking
37
32
 
38
- // Inicialización del SDK
39
- await WebsSDK.Networking.executeInit();
33
+ ```typescript
34
+ // Petición HTTP (inyecta sessionData automáticamente)
35
+ await WebsSDK.Networking.request(url, data);
40
36
 
41
- // Envío de eventos
37
+ // Eventos de tracking
42
38
  await WebsSDK.Networking.sendEvent('action', 'button_click', { button_id: 'submit' });
43
39
 
44
- // Verificar conectividad
45
- const isOnline = await WebsSDK.Networking.checkConnection();
46
-
47
- // Gestión de suscripciones
48
- const subscription = await WebsSDK.Networking.createSubscription(subscriptionData);
49
- const isActive = await WebsSDK.Networking.checkSubscription();
40
+ // Suscripciones
41
+ await WebsSDK.Networking.createSubscription(subscriptionData);
42
+ const { success, subscription_active } = await WebsSDK.Networking.checkSubscription();
50
43
  ```
51
44
 
52
- ### 💾 Storage
53
- Almacenamiento local avanzado con soporte para datos complejos.
45
+ ### Storage
54
46
 
55
- ```javascript
56
- // Métodos básicos
47
+ ```typescript
57
48
  WebsSDK.Storage.set('key', 'value');
58
49
  const value = WebsSDK.Storage.get('key');
59
50
 
60
- // Almacenamiento de datos complejos
61
- await WebsSDK.Storage.storeData('complex_data', { user: 'john', age: 30 });
62
- const data = await WebsSDK.Storage.getData('complex_data');
51
+ // Compresión de imágenes (0.8 ratio, 1024px max)
52
+ const compressed = await WebsSDK.Storage.compressImage(imageFile, 2); // 2MB max
63
53
 
64
- // Manejo de creaciones (imágenes, archivos)
65
- await WebsSDK.Storage.handleDownloadImageToCreations(base64Image, 'creation_1', metadata);
54
+ // Creaciones (imágenes generadas por el usuario)
55
+ await WebsSDK.Storage.handleDownloadImageToCreations(base64, 'id', metadata);
66
56
  const creations = await WebsSDK.Storage.getCreations();
67
-
68
- // Compresión de imágenes
69
- const compressedImage = await WebsSDK.Storage.compressImage(imageUri, 2); // 2MB max
70
-
71
- // Descarga de archivos
72
- await WebsSDK.Storage.handleDownloadImage(base64, 'imagen');
73
-
74
- // Compartir archivos
75
- await WebsSDK.Storage.handleShareFile(fileUrl);
57
+ await WebsSDK.Storage.deleteCreation('id');
76
58
  ```
77
59
 
78
- ### 👤 Session
79
- Gestión completa de sesiones de usuario y datos del dispositivo.
60
+ ### Session
80
61
 
81
- ```javascript
82
- // Inicializar sesión
62
+ ```typescript
83
63
  await WebsSDK.Session.init();
84
64
 
85
- // Obtener datos de sesión
86
65
  const sessionData = WebsSDK.Session.getSessionData();
87
- const userID = WebsSDK.Session.getUserID();
88
- const sessionID = WebsSDK.Session.getSessionID();
89
-
90
- // Gestión de suscripciones
91
- await WebsSDK.Session.setIsSubscribed(true);
66
+ const userId = WebsSDK.Session.getUserID();
67
+ const lang = WebsSDK.Session.getDeviceLanguage();
92
68
  const isSubscribed = WebsSDK.Session.getIsSubscribed();
69
+ ```
70
+
71
+ ### Utils
72
+
73
+ ```typescript
74
+ const id = WebsSDK.Utils.generateId();
75
+ const isValidEmail = WebsSDK.Utils.isValidEmail('user@example.com');
76
+ const isMobile = WebsSDK.Utils.isMobile();
77
+ const deviceInfo = WebsSDK.Utils.getDeviceInfo();
78
+ const truncated = WebsSDK.Utils.truncateText('Long text...', 10);
79
+ ```
93
80
 
94
- // Información del dispositivo y usuario
95
- const language = WebsSDK.Session.getDeviceLanguage();
96
- const languageRegion = WebsSDK.Session.getDeviceLanguageAndRegion();
97
- const isFirstOpen = WebsSDK.Session.getIsFirstOpen();
81
+ ### MixPanel
98
82
 
99
- // Gestión de usuarios
100
- WebsSDK.Session.setUserID('user_123');
101
- await WebsSDK.Session.setIsDevUser(true);
83
+ ```typescript
84
+ await WebsSDK.MixPanel.initialize(token, false, false, false);
85
+ await WebsSDK.MixPanel.trackEvent('page_view', { page: 'home' });
86
+ await WebsSDK.MixPanel.identifyUser('user_123');
87
+ await WebsSDK.MixPanel.setUserProperties({ plan: 'premium' });
102
88
  ```
103
89
 
104
- ### 🔧 Utils
105
- Amplia colección de utilidades para desarrollo web.
90
+ ### AuthManager
106
91
 
107
- ```javascript
108
- // Utilidades de fecha y ID
109
- const id = WebsSDK.Utils.generateId();
110
- const formattedDate = WebsSDK.Utils.formatDate(new Date(), 'DD/MM/YYYY');
92
+ ```typescript
93
+ // Login estándar
94
+ await WebsSDK.AuthManager.authUser({ login, password, website_id });
111
95
 
112
- // Validaciones
113
- const isValidEmail = WebsSDK.Utils.isValidEmail('user@example.com');
114
- const isValidUrl = WebsSDK.Utils.isValidUrl('https://example.com');
96
+ // Login sin password (MSISDN)
97
+ await WebsSDK.AuthManager.authOnlyUser({ login, website_id });
115
98
 
116
- // Manipulación de strings
117
- const truncated = WebsSDK.Utils.truncateText('Long text...', 10);
118
- const capitalized = WebsSDK.Utils.capitalizeFirst('hello world');
99
+ // Login con OTP
100
+ await WebsSDK.AuthManager.authOnlyUserOTP({ login, website_id, pincode });
119
101
 
120
- // Funciones de tiempo
121
- await WebsSDK.Utils.sleep(1000); // Esperar 1 segundo
122
- const randomNum = WebsSDK.Utils.getRandomInt(1, 100);
102
+ // Registro
103
+ await WebsSDK.AuthManager.createUser({ login, password, website_id });
123
104
 
124
- // Manipulación de arrays y objetos
125
- const chunks = WebsSDK.Utils.arrayChunk([1,2,3,4,5], 2);
126
- const unique = WebsSDK.Utils.arrayUnique([1,1,2,3,3]);
127
- const picked = WebsSDK.Utils.objectPick(obj, ['key1', 'key2']);
105
+ // Auto-login post-pago (usado en el proxy de next_template)
106
+ await WebsSDK.AuthManager.auto_login(cfg_sessionid, websiteData, requestUrl);
128
107
 
129
- // Funciones específicas para web
130
- const deviceInfo = WebsSDK.Utils.getDeviceInfo();
131
- const isMobile = WebsSDK.Utils.isMobile();
132
- const isTablet = WebsSDK.Utils.isTablet();
133
- const isDesktop = WebsSDK.Utils.isDesktop();
134
- const browserInfo = WebsSDK.Utils.getBrowserInfo();
108
+ // Baja/cancelación
109
+ await WebsSDK.AuthManager.unsubscribeUser({ user_id, callback_url?, org? });
110
+
111
+ // Gestión de perfil
112
+ await WebsSDK.AuthManager.setUserMetadata(params);
113
+ await WebsSDK.AuthManager.uploadProfilePhoto(params); // JPG/PNG/WEBP, max 5MB
114
+ await WebsSDK.AuthManager.changePassword(params);
115
+ ```
135
116
 
136
- // Manejo de archivos y datos
137
- const isBase64 = WebsSDK.Utils.isBase64(string);
138
- const isBase64Image = WebsSDK.Utils.isBase64Image(dataUri);
139
- WebsSDK.Utils.downloadFile(data, 'filename.txt', 'text/plain');
140
- await WebsSDK.Utils.copyToClipboard('texto a copiar');
117
+ ### Andromeda Portales
141
118
 
142
- // Utilidades de formato
143
- const formattedBytes = WebsSDK.Utils.formatBytes(1024); // "1 KB"
144
- const queryParams = WebsSDK.Utils.parseQueryString('?a=1&b=2');
145
- const queryString = WebsSDK.Utils.buildQueryString({a: 1, b: 2});
119
+ ```typescript
120
+ // Metadata del portal (optimizado con cookie websiteDataFlag)
121
+ await WebsSDK.Andromeda.getWebsiteMetadatabyHostname('portal.com');
122
+
123
+ // Configuración del producto (langs, login_type, logo, favicon, theme)
124
+ await WebsSDK.Andromeda.getWebsiteConfig('portal-corp-web');
146
125
  ```
147
126
 
148
- ### 📊 MixPanel
149
- Sistema completo de análisis y tracking de eventos.
127
+ ### ContentManager
150
128
 
151
- ```javascript
152
- // Inicializar MixPanel
153
- await WebsSDK.MixPanel.initialize('your_token', false, false, false);
129
+ ```typescript
130
+ // Contenido por preset
131
+ await WebsSDK.ContentManager.getContentByPreset('homepage', { lang: 'es' });
154
132
 
155
- // Tracking de eventos
156
- await WebsSDK.MixPanel.trackEvent('page_view', { page: 'home' });
157
- await WebsSDK.MixPanel.trackEventIfExist('special_event', { data: 'value' });
133
+ // Colección filtrada por tags
134
+ await WebsSDK.ContentManager.getCollection(['tag1', 'tag2'], 'es', false);
135
+ ```
158
136
 
159
- // Gestión de usuarios
160
- await WebsSDK.MixPanel.identifyUser('user_123');
161
- await WebsSDK.MixPanel.setUserProperties({ plan: 'premium' });
162
- await WebsSDK.MixPanel.resetUserID();
137
+ ### User
138
+
139
+ ```typescript
140
+ await WebsSDK.User.checkSession(token); // Valida token (usado en middleware)
141
+ await WebsSDK.User.getUserData(user_id, website_id);
142
+ await WebsSDK.User.getCredits(user_id, website_id);
143
+ ```
163
144
 
164
- // Super propiedades
165
- await WebsSDK.MixPanel.superProperties({ app_version: '1.0.0' });
166
- await WebsSDK.MixPanel.superPropertiesAppend({ session_id: 'abc123' });
145
+ ### CorporateAuthManager
167
146
 
168
- // Control de tracking
169
- WebsSDK.MixPanel.disableTracking();
170
- WebsSDK.MixPanel.enableTracking();
171
- const isInitialized = WebsSDK.MixPanel.isMixpanelInitialized();
147
+ ```typescript
148
+ // Server-side (middleware Next.js)
149
+ const result = await WebsSDK.CorporateAuthManager.validateCorporateAuthServerSide(request);
150
+ if (!result.authenticated) return result.redirectResponse;
151
+
152
+ // Client-side
153
+ await WebsSDK.CorporateAuthManager.checkCorporateLogin();
154
+ await WebsSDK.CorporateAuthManager.validateUser(autoRedirect?);
155
+ await WebsSDK.CorporateAuthManager.login(returnTo?);
156
+ await WebsSDK.CorporateAuthManager.logout(redirectTo?);
157
+ await WebsSDK.CorporateAuthManager.isAdmin(userData?);
158
+ ```
159
+
160
+ ### Legal
161
+
162
+ ```typescript
163
+ await WebsSDK.Legal.getLegalText('tos' | 'privacy' | 'faq', web_id, lang);
164
+ ```
165
+
166
+ ### Namespace `pre` — Pre-producción
167
+
168
+ ```typescript
169
+ // Lazy-loaded, apunta a bc1742-pre.gways.org
170
+ await WebsSDK.pre.AuthManager.authUser(params);
171
+ await WebsSDK.pre.Andromeda.getWebsiteMetadatabyHostname(hostname);
172
172
  ```
173
173
 
174
- ### 🔐 AuthManager
175
- Gestión de autenticación (en desarrollo).
174
+ ---
175
+
176
+ ## Componentes React
177
+
178
+ ### CookieConsent — Banner GDPR
179
+
180
+ ```typescript
181
+ import { CookieConsent, getCookieConsentTexts } from 'webs-sdk';
182
+ import type { CookieConsentProps, CookieConsentTheme, CookieConsentPreferences } from 'webs-sdk';
183
+
184
+ <CookieConsent
185
+ locale="es" // en | es | fr | nl | ar | de | fi | ro | tr
186
+ privacyPolicyUrl="/privacy"
187
+ theme={{
188
+ backgroundColor: '#1a1a2e',
189
+ accentColor: '#6c63ff', // aplica a botón Accept, borde Reject, links y glow
190
+ acceptButtonBg: 'linear-gradient(90deg, #6c63ff, #a855f7)', // soporta gradients CSS
191
+ textColor: '#ffffff',
192
+ }}
193
+ onAccept={() => updateGAConsent('granted')}
194
+ onReject={() => updateGAConsent('denied')}
195
+ onSaveSettings={(prefs: CookieConsentPreferences) => handlePrefs(prefs)}
196
+ />
197
+ ```
198
+
199
+ **Storage:**
200
+ - Cookie `cookieConsent`: `accepted | rejected | custom` (1 año)
201
+ - `localStorage.TRACKING_PERMISSION`: `granted | denied`
202
+ - `localStorage.FUNCTIONAL_PERMISSION`: `granted | denied`
203
+
204
+ ### Google Analytics — GA4
176
205
 
177
- ### 🌍 I18nManager
178
- Sistema de internacionalización (en desarrollo).
206
+ ```typescript
207
+ import { initGoogleAnalytics, updateGAConsent, trackGAEvent, trackGAPageView, isGALoaded } from 'webs-sdk';
179
208
 
180
- ### 🚀 Andromeda
181
- Funcionalidades avanzadas (en desarrollo).
209
+ // Registra el GA ID. Carga el script SOLO si hay consentimiento previo.
210
+ await initGoogleAnalytics('G-XXXXXXXXXX', { sendPageView: true, debug: false });
182
211
 
183
- ### 📝 ContentManager
184
- Gestión y obtención de contenido dinámico.
212
+ // Llamar tras decisión del usuario en CookieConsent
213
+ updateGAConsent('granted'); // carga GA si había pendingTrackingId
214
+ updateGAConsent('denied'); // GA nunca se carga
185
215
 
186
- ```javascript
187
- const content = await WebsSDK.ContentManager.getContentByPreset('homepage', { lang: 'es' });
216
+ // Tracking
217
+ trackGAEvent('purchase', { value: 9.99, currency: 'EUR' });
218
+ trackGAPageView('/home', 'Inicio');
219
+ isGALoaded(); // boolean
188
220
  ```
189
221
 
190
- ## Configuración
222
+ > **GDPR estricto:** GA no usa Consent Mode v2. Si no hay consentimiento, el script `gtag.js` nunca se inyecta en el DOM.
191
223
 
192
- El SDK incluye configuración completa que puede ser personalizada según tus necesidades:
224
+ ### CDN Proxy
193
225
 
194
- ```javascript
195
- // Acceder a la configuración
196
- const config = WebsSDK.Config;
226
+ ```typescript
227
+ import { handleCDNRequest } from 'webs-sdk';
228
+ import type { CDNOptions } from 'webs-sdk';
197
229
 
198
- // Configurar endpoints de API
199
- config.endpoints.CONFIG = 'https://mi-api.com/config';
200
- config.endpoints.USER_CREATE_ID = 'https://mi-api.com/user/create';
230
+ // Usar en un route handler de Next.js (compatible con Edge Runtime)
231
+ async function handleCDNRequest(
232
+ pathname: string, // '/cdn/apariencias/33702/image.png'
233
+ options?: {
234
+ cdnBaseUrl?: string; // Default: 'https://dy822md8ge77v.cloudfront.net'
235
+ cacheSeconds?: number; // Default: 300 (5 min)
236
+ }
237
+ ): Promise<Response | null> // null si pathname no empieza por /cdn/
238
+ ```
239
+
240
+ ### Componentes "For You" — Creación de contenido AI
241
+
242
+ ```typescript
243
+ import {
244
+ AvatarAI, AvatarAIPage, AvatarAIForYouCard,
245
+ CreativeFaceSwap, CreativeFaceSwapPage, CreativeFaceSwapForYouCard,
246
+ MemeGenerator, MemeGeneratorPage, MemeGeneratorForYouCard,
247
+ WallpapersName, WallpapersNamePage, WallpapersNameForYouCard,
248
+ Wallpapers,
249
+ Ringtone,
250
+ } from 'webs-sdk';
251
+ ```
252
+
253
+ Todos aceptan `websiteId?: string` para tracking multi-portal.
254
+
255
+ ### ARFilters — Filtros AR con DeepAR
256
+
257
+ ```typescript
258
+ import { ARFilters, ARFiltersForYouCard } from 'webs-sdk';
259
+ import type { DeepARProps, DeepARFilter, DeepARTexts } from 'webs-sdk';
260
+
261
+ <ARFilters
262
+ licenseKey="deepar-license-key"
263
+ websiteId="portal-web-id"
264
+ apiBaseUrl="https://api.fluver-ai.com"
265
+ downloadProxyUrl="/cdn" // Proxy CORS para descarga de efectos
266
+ filterTag="ar-filters-tag" // Obtiene filtros del CMS por tag
267
+ // O bien: filters={[{ file: 'beauty', thumbUrl: '/thumb.jpg' }]}
268
+ locale="es"
269
+ texts={getARFiltersTexts('es')} // requerido
270
+ onPhotoTaken={(url) => share(url)}
271
+ onVideoRecorded={(url) => share(url)}
272
+ />
273
+ ```
201
274
 
202
- // Configurar MixPanel
203
- config.MIXPANEL.TOKEN = 'tu_token_mixpanel';
275
+ Soporta fullscreen en mobile (vendor-prefixed API para iOS Safari).
204
276
 
205
- // Configuración de desarrollo
206
- config.DEBUG_MODE = true;
277
+ ### SpinningWheel Ruleta
207
278
 
208
- // Configuración de eventos especiales
209
- config.SPECIAL_EVENTS = ['first_open', 'subscription_purchased'];
279
+ ```typescript
280
+ import { SpinningWheel, wheelThemes } from 'webs-sdk';
210
281
 
211
- // Configuración de compresión de imágenes
212
- config.IMAGE_COMPRESSION.COMPRESSION = 0.8;
213
- config.IMAGE_COMPRESSION.WIDTH = 1024;
214
- config.IMAGE_COMPRESSION.ACTIVE = true;
282
+ // Temas: vodacom | totalgym | womantoday | afristream | efc
283
+ <SpinningWheel
284
+ userId={userId}
285
+ websiteId={websiteId}
286
+ theme="vodacom"
287
+ onResult={(segment) => handleWin(segment)}
288
+ canSpin={true}
289
+ alreadySpunToday={false}
290
+ />
215
291
  ```
216
292
 
217
- ## Funcionalidades Avanzadas
293
+ ### Quiz y Stickers
218
294
 
219
- ### Gestión de Eventos
220
- ```javascript
221
- // Configurar y enviar eventos personalizados
222
- await WebsSDK.Networking.sendEvent('user_action', 'button_click', {
223
- button_id: 'cta_primary',
224
- page: 'landing',
225
- timestamp: Date.now()
226
- });
295
+ ```typescript
296
+ import { Quiz, Stickers } from 'webs-sdk';
297
+ ```
227
298
 
228
- // Eventos pendientes (para cuando no hay conexión)
229
- WebsSDK.Networking.addPendingEvent({
230
- eventType: 'action',
231
- eventKeyword: 'offline_action',
232
- eventData: { action: 'save_draft' }
233
- });
299
+ ### Esports Noticias, Vídeos y Partidas en Directo
234
300
 
235
- // Enviar eventos pendientes cuando se restaure la conexión
236
- await WebsSDK.Networking.sendPendingEvents();
301
+ ```typescript
302
+ import { EsportsNews, EsportsVideos, EsportsLive } from 'webs-sdk';
237
303
  ```
238
304
 
239
- ### Gestión de Archivos e Imágenes
240
- ```javascript
241
- // Comprimir y guardar imágenes
242
- const compressedImage = await WebsSDK.Storage.compressImage(imageFile, 2);
243
- await WebsSDK.Storage.handleDownloadImageToCreations(compressedImage, 'photo_1', {
244
- timestamp: Date.now(),
245
- location: 'homepage'
246
- });
305
+ #### EsportsNews
306
+
307
+ ```typescript
308
+ <EsportsNews
309
+ config={{
310
+ key: 'api-key',
311
+ contents: [{ preset: 'esports_news', apiProviderId: 'id', category: [], tags: [], limit: 10 }]
312
+ }}
313
+ language="es"
314
+ onNewsClick={(item) => router.push(`/news/${item.id}`)}
315
+ theme={theme} // CSS classes para container, cards, badges, etc.
316
+ categoryStyles={{ gaming: { badge: 'bg-blue-500', badgeText: 'text-white' } }}
317
+ // Modo detalle (renderiza EsportsNewsDetail):
318
+ newsId="article-id"
319
+ newsData={item} // opcional si ya tienes los datos
320
+ onBack={() => router.back()}
321
+ />
322
+ ```
247
323
 
248
- // Obtener todas las creaciones guardadas
249
- const allCreations = await WebsSDK.Storage.getCreations();
324
+ #### EsportsVideos
250
325
 
251
- // Eliminar creaciones específicas
252
- await WebsSDK.Storage.deleteCreation('photo_1');
326
+ ```typescript
327
+ <EsportsVideos
328
+ config={{ key: 'api-key', contents: [...] }}
329
+ language="es"
330
+ onVideoClick={(item) => openPlayer(item.video_url)}
331
+ theme={theme} // incluye clases para duration overlay
332
+ />
253
333
  ```
254
334
 
255
- ### Información del Dispositivo
256
- ```javascript
257
- // Obtener información completa del dispositivo
258
- const deviceInfo = WebsSDK.Utils.getDeviceInfo();
259
- console.log(deviceInfo);
260
- // {
261
- // userAgent: "Mozilla/5.0...",
262
- // platform: "MacIntel",
263
- // language: "es-ES",
264
- // screenWidth: 1920,
265
- // screenHeight: 1080,
266
- // timezone: "Europe/Madrid"
267
- // }
335
+ #### EsportsLive
336
+
337
+ ```typescript
338
+ <EsportsLive
339
+ language="es"
340
+ timezone="Europe/Madrid"
341
+ onLiveClick={(item) => openStream(item.streamUrl)}
342
+ onMatchClick={(match) => openMatchDetail(match)}
343
+ onGameClick={(gameId) => filterByGame(gameId)}
344
+ website_id="portal-web-id"
345
+ theme={theme}
346
+ // Modo detalle de partida:
347
+ matchId="serie-id"
348
+ matchData={calendarMatch}
349
+ onBack={() => setMatchId(null)}
350
+ />
351
+ ```
352
+
353
+ ---
268
354
 
269
- // Detectar tipo de dispositivo
270
- if (WebsSDK.Utils.isMobile()) {
271
- // Lógica específica para móviles
272
- } else if (WebsSDK.Utils.isTablet()) {
273
- // Lógica específica para tablets
274
- } else {
275
- // Lógica para desktop
276
- }
355
+ ## Utilidades de validación de vídeo
356
+
357
+ ```typescript
358
+ import { validateVideoUrl, validateVideoBatch, clearValidationCache } from 'webs-sdk';
359
+ import { useValidateVideoOnMount, useLazyVideoValidation } from 'webs-sdk';
360
+
361
+ // Función pura (sin React)
362
+ const state = await validateVideoUrl('https://stream.example.com/live.m3u8');
363
+ // 'valid' | 'invalid' | 'unknown'
364
+
365
+ // Batch con concurrencia 3
366
+ const results = await validateVideoBatch([url1, url2, url3]);
367
+
368
+ // React: validar al montar (evita renderizar streams rotos)
369
+ const { isValid, isInvalid, isValidating } = useValidateVideoOnMount(videoUrl);
370
+
371
+ // React: validación manual
372
+ const { validate, isValid, isInvalid } = useLazyVideoValidation();
373
+ await validate(videoUrl);
277
374
  ```
278
375
 
279
- ## Estructura del Proyecto
376
+ Soporta HLS, DASH, MP4. Cache en memoria con TTL de 5 minutos.
377
+
378
+ ---
379
+
380
+ ## Estructura del proyecto
280
381
 
281
382
  ```
282
383
  src/
283
- ├── config.ts # Configuración central del SDK
284
- ├── index.ts # Punto de entrada principal
384
+ ├── config.ts # Configuración central (URLs, endpoints, constantes)
385
+ ├── index.ts # Barrel export principal
285
386
  ├── libraries/
286
- │ ├── networking.ts # Peticiones HTTP y comunicación
287
- │ ├── storage.ts # Almacenamiento local avanzado
288
- │ ├── session.ts # Gestión de sesiones
387
+ │ ├── networking.ts # HTTP + events
388
+ │ ├── auth.ts # Autenticación, login, baja
389
+ │ ├── session.ts # Sesiones y dispositivo
390
+ │ ├── storage.ts # LocalStorage + compresión de imágenes
289
391
  │ ├── utils.ts # Utilidades generales
290
- │ ├── mixpanel.ts # Analytics y tracking
291
- │ ├── auth.ts # Autenticación
292
- │ ├── i18n.ts # Internacionalización
293
- │ ├── andromeda.ts # Funcionalidades avanzadas
294
- │ ├── content.ts # Gestión de contenido
295
- └── index.ts # Exports de librerías
296
- └── types/
297
- └── index.d.ts # Definiciones de tipos TypeScript
392
+ │ ├── andromeda.ts # Metadata y config de portales
393
+ │ ├── corp_auth.ts # Auth corporativa (JWT)
394
+ │ ├── user.ts # Datos de usuario
395
+ │ ├── mixpanel.ts # Analytics
396
+ │ ├── audio.ts # Text-to-speech
397
+ ├── calypso.ts # Event tracking interno
398
+ │ ├── legal.ts # Textos legales
399
+ │ ├── content.ts # Contenido dinámico + colecciones por tags
400
+ │ ├── cdn.ts # CDN Proxy (Edge Runtime compatible)
401
+ │ └── googleAnalytics.ts # GA4 con carga diferida por consentimiento
402
+ ├── utils/
403
+ │ ├── videoValidation.ts # Validación de URLs de streaming
404
+ │ ├── useVideoValidation.ts # React hooks para validación
405
+ │ └── timeUtils.ts # getTimeAgo — tiempo relativo i18n
406
+ └── components/
407
+ ├── AvatarAI.tsx
408
+ ├── CreativeFaceSwap.tsx
409
+ ├── Wallpapers.tsx
410
+ ├── WallpapersName.tsx
411
+ ├── Ringtone.tsx
412
+ ├── MemeGenerator.tsx
413
+ ├── Quiz.tsx
414
+ ├── Stickers.tsx
415
+ ├── VideoPlayer.tsx
416
+ ├── ARFilters/
417
+ ├── SpinningWheel/
418
+ ├── CookieConsent/
419
+ └── esports/
420
+ ├── news/
421
+ ├── videos/
422
+ └── live/
298
423
  ```
299
424
 
300
425
  ## Desarrollo
301
426
 
302
427
  ```bash
303
- # Instalar dependencias
304
428
  npm install
305
-
306
- # Compilar en modo desarrollo con watch
307
- npm run build:watch
308
-
309
- # Compilar para producción
310
- npm run build
311
-
312
- # Ejecutar linting
429
+ npm run build # tsc → dist/
430
+ npm run build:watch # watch mode
313
431
  npm run lint
314
-
315
- # Ejecutar servidor de desarrollo
316
- npm run dev
317
432
  ```
318
433
 
319
434
  ## Publicación
320
435
 
321
- El proyecto está configurado para ser publicado como módulo npm:
322
-
323
436
  ```bash
324
- # Compilar y preparar para publicación
325
- npm run prepublishOnly
326
-
327
- # Publicar en npm
328
- npm publish
437
+ # Incluir [ROAD-361723] en el commit para auto-publish en CI
438
+ git commit -m "feat: descripción [ROAD-361723]"
439
+ git push
329
440
  ```
330
441
 
331
- ## Compatibilidad
332
-
333
- - **Navegadores**: Chrome 60+, Firefox 55+, Safari 12+, Edge 79+
334
- - **Node.js**: 16+ (para desarrollo)
335
- - **TypeScript**: 5+
336
- - **Frameworks**: Compatible con React, Vue, Angular, Vanilla JS
337
-
338
- ## Ejemplos de Uso
339
-
340
- ### Aplicación Básica
341
- ```javascript
342
- import WebsSDK from 'webs-sdk';
442
+ Tras publicar, actualizar en los portales:
443
+ ```bash
444
+ npm install webs-sdk@<nueva-version>
445
+ ```
343
446
 
344
- async function initApp() {
345
- // Inicializar el SDK
346
- await WebsSDK.Session.init();
347
-
348
- // Configurar tracking
349
- await WebsSDK.MixPanel.initialize(process.env.MIXPANEL_TOKEN);
350
-
351
- // Obtener configuración del servidor
352
- const config = await WebsSDK.Networking.request('/api/config');
353
-
354
- // Guardar preferencias del usuario
355
- await WebsSDK.Storage.storeData('app_config', config);
356
-
357
- console.log('App inicializada correctamente');
358
- }
359
-
360
- initApp();
361
- ```
362
-
363
- ### E-commerce
364
- ```javascript
365
- // Tracking de eventos de e-commerce
366
- await WebsSDK.MixPanel.trackEvent('product_viewed', {
367
- product_id: 'ABC123',
368
- category: 'electronics',
369
- price: 299.99
370
- });
371
-
372
- // Gestión de carrito
373
- await WebsSDK.Storage.storeData('cart_items', cartItems);
374
- const savedCart = await WebsSDK.Storage.getData('cart_items');
375
- ```
376
-
377
- ### Aplicación con Suscripciones
378
- ```javascript
379
- // Verificar estado de suscripción
380
- const isSubscribed = await WebsSDK.Session.getIsSubscribed();
381
-
382
- if (!isSubscribed) {
383
- // Mostrar paywall
384
- await WebsSDK.MixPanel.trackEvent('paywall_shown', {
385
- source: 'premium_feature'
386
- });
387
- }
388
-
389
- // Crear nueva suscripción
390
- const subscription = await WebsSDK.Networking.createSubscription({
391
- plan: 'premium_monthly',
392
- user_id: WebsSDK.Session.getUserID()
393
- });
394
- ```
395
-
396
- ## Licencia
397
-
398
- MIT License
447
+ **Nota importante `index.js` raíz:** es el barrel CommonJS mantenido manualmente. Cada export nuevo en `src/index.ts` debe añadirse también en `index.js`:
448
+ ```js
449
+ const { nuevaFuncion } = require('./dist/index.js');
450
+ module.exports.nuevaFuncion = nuevaFuncion;
451
+ ```