webs-sdk 0.18.40 → 0.18.41

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.
Files changed (47) hide show
  1. package/README.md +451 -451
  2. package/dist/components/ARFilters/ARFilters.js +54 -54
  3. package/dist/components/AvatarAI.js +4 -4
  4. package/dist/components/CookieConsent/constants.js +31 -31
  5. package/dist/components/CreativeFaceSwap.js +12 -12
  6. package/dist/components/MemeGenerator.js +12 -12
  7. package/dist/components/PasswordReset/PasswordResetForm.d.ts +3 -0
  8. package/dist/components/PasswordReset/PasswordResetForm.d.ts.map +1 -0
  9. package/dist/components/PasswordReset/PasswordResetForm.js +130 -0
  10. package/dist/components/PasswordReset/PasswordResetForm.js.map +1 -0
  11. package/dist/components/PasswordReset/PasswordResetRequest.d.ts +3 -0
  12. package/dist/components/PasswordReset/PasswordResetRequest.d.ts.map +1 -0
  13. package/dist/components/PasswordReset/PasswordResetRequest.js +102 -0
  14. package/dist/components/PasswordReset/PasswordResetRequest.js.map +1 -0
  15. package/dist/components/PasswordReset/constants.d.ts +6 -0
  16. package/dist/components/PasswordReset/constants.d.ts.map +1 -0
  17. package/dist/components/PasswordReset/constants.js +55 -0
  18. package/dist/components/PasswordReset/constants.js.map +1 -0
  19. package/dist/components/PasswordReset/index.d.ts +5 -0
  20. package/dist/components/PasswordReset/index.d.ts.map +1 -0
  21. package/dist/components/PasswordReset/index.js +10 -0
  22. package/dist/components/PasswordReset/index.js.map +1 -0
  23. package/dist/components/PasswordReset/translations.d.ts +3 -0
  24. package/dist/components/PasswordReset/translations.d.ts.map +1 -0
  25. package/dist/components/PasswordReset/translations.js +72 -0
  26. package/dist/components/PasswordReset/translations.js.map +1 -0
  27. package/dist/components/PasswordReset/types.d.ts +89 -0
  28. package/dist/components/PasswordReset/types.d.ts.map +1 -0
  29. package/dist/components/PasswordReset/types.js +3 -0
  30. package/dist/components/PasswordReset/types.js.map +1 -0
  31. package/dist/components/SpinningWheel/SpinningWheel.js +7 -7
  32. package/dist/utils/videoValidation.d.ts.map +1 -1
  33. package/dist/utils/videoValidation.js +59 -4
  34. package/dist/utils/videoValidation.js.map +1 -1
  35. package/package.json +78 -78
  36. package/public/spin2win/assets/border-afristream.svg +26 -26
  37. package/public/spin2win/assets/border-efc.svg +23 -23
  38. package/public/spin2win/assets/border-totalgym.svg +27 -27
  39. package/public/spin2win/assets/border-womantoday.svg +26 -26
  40. package/public/spin2win/assets/indicator-afristream.svg +28 -28
  41. package/public/spin2win/assets/indicator-efc.svg +29 -29
  42. package/public/spin2win/assets/indicator-totalgym.svg +29 -29
  43. package/public/spin2win/fonts/switzer/README.md +22 -22
  44. package/dist/libraries/globals.d.ts +0 -17
  45. package/dist/libraries/globals.d.ts.map +0 -1
  46. package/dist/libraries/globals.js +0 -38
  47. package/dist/libraries/globals.js.map +0 -1
package/README.md CHANGED
@@ -1,451 +1,451 @@
1
- # webs-sdk
2
-
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.18.9
6
-
7
- ## Instalación
8
-
9
- ```bash
10
- npm install webs-sdk
11
- ```
12
-
13
- ## Uso básico
14
-
15
- ```typescript
16
- import WebsSDK from 'webs-sdk';
17
-
18
- // Inicializar sesión
19
- await WebsSDK.Session.init();
20
-
21
- // Petición HTTP
22
- const data = await WebsSDK.Networking.request('/api/endpoint', { param: 'value' });
23
-
24
- // Almacenamiento local
25
- WebsSDK.Storage.set('key', 'value');
26
- const value = WebsSDK.Storage.get('key');
27
- ```
28
-
29
- ## Librerías disponibles
30
-
31
- ### Networking
32
-
33
- ```typescript
34
- // Petición HTTP (inyecta sessionData automáticamente)
35
- await WebsSDK.Networking.request(url, data);
36
-
37
- // Eventos de tracking
38
- await WebsSDK.Networking.sendEvent('action', 'button_click', { button_id: 'submit' });
39
-
40
- // Suscripciones
41
- await WebsSDK.Networking.createSubscription(subscriptionData);
42
- const { success, subscription_active } = await WebsSDK.Networking.checkSubscription();
43
- ```
44
-
45
- ### Storage
46
-
47
- ```typescript
48
- WebsSDK.Storage.set('key', 'value');
49
- const value = WebsSDK.Storage.get('key');
50
-
51
- // Compresión de imágenes (0.8 ratio, 1024px max)
52
- const compressed = await WebsSDK.Storage.compressImage(imageFile, 2); // 2MB max
53
-
54
- // Creaciones (imágenes generadas por el usuario)
55
- await WebsSDK.Storage.handleDownloadImageToCreations(base64, 'id', metadata);
56
- const creations = await WebsSDK.Storage.getCreations();
57
- await WebsSDK.Storage.deleteCreation('id');
58
- ```
59
-
60
- ### Session
61
-
62
- ```typescript
63
- await WebsSDK.Session.init();
64
-
65
- const sessionData = WebsSDK.Session.getSessionData();
66
- const userId = WebsSDK.Session.getUserID();
67
- const lang = WebsSDK.Session.getDeviceLanguage();
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
- ```
80
-
81
- ### MixPanel
82
-
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' });
88
- ```
89
-
90
- ### AuthManager
91
-
92
- ```typescript
93
- // Login estándar
94
- await WebsSDK.AuthManager.authUser({ login, password, website_id });
95
-
96
- // Login sin password (MSISDN)
97
- await WebsSDK.AuthManager.authOnlyUser({ login, website_id });
98
-
99
- // Login con OTP
100
- await WebsSDK.AuthManager.authOnlyUserOTP({ login, website_id, pincode });
101
-
102
- // Registro
103
- await WebsSDK.AuthManager.createUser({ login, password, website_id });
104
-
105
- // Auto-login post-pago (usado en el proxy de next_template)
106
- await WebsSDK.AuthManager.auto_login(cfg_sessionid, websiteData, requestUrl);
107
-
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
- ```
116
-
117
- ### Andromeda — Portales
118
-
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');
125
- ```
126
-
127
- ### ContentManager
128
-
129
- ```typescript
130
- // Contenido por preset
131
- await WebsSDK.ContentManager.getContentByPreset('homepage', { lang: 'es' });
132
-
133
- // Colección filtrada por tags
134
- await WebsSDK.ContentManager.getCollection(['tag1', 'tag2'], 'es', false);
135
- ```
136
-
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
- ```
144
-
145
- ### CorporateAuthManager
146
-
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
- ```
173
-
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
205
-
206
- ```typescript
207
- import { initGoogleAnalytics, updateGAConsent, trackGAEvent, trackGAPageView, isGALoaded } from 'webs-sdk';
208
-
209
- // Registra el GA ID. Carga el script SOLO si hay consentimiento previo.
210
- await initGoogleAnalytics('G-XXXXXXXXXX', { sendPageView: true, debug: false });
211
-
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
215
-
216
- // Tracking
217
- trackGAEvent('purchase', { value: 9.99, currency: 'EUR' });
218
- trackGAPageView('/home', 'Inicio');
219
- isGALoaded(); // boolean
220
- ```
221
-
222
- > **GDPR estricto:** GA no usa Consent Mode v2. Si no hay consentimiento, el script `gtag.js` nunca se inyecta en el DOM.
223
-
224
- ### CDN Proxy
225
-
226
- ```typescript
227
- import { handleCDNRequest } from 'webs-sdk';
228
- import type { CDNOptions } from 'webs-sdk';
229
-
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
- ```
274
-
275
- Soporta fullscreen en mobile (vendor-prefixed API para iOS Safari).
276
-
277
- ### SpinningWheel — Ruleta
278
-
279
- ```typescript
280
- import { SpinningWheel, wheelThemes } from 'webs-sdk';
281
-
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
- />
291
- ```
292
-
293
- ### Quiz y Stickers
294
-
295
- ```typescript
296
- import { Quiz, Stickers } from 'webs-sdk';
297
- ```
298
-
299
- ### Esports — Noticias, Vídeos y Partidas en Directo
300
-
301
- ```typescript
302
- import { EsportsNews, EsportsVideos, EsportsLive } from 'webs-sdk';
303
- ```
304
-
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
- ```
323
-
324
- #### EsportsVideos
325
-
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
- />
333
- ```
334
-
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
- ---
354
-
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);
374
- ```
375
-
376
- Soporta HLS, DASH, MP4. Cache en memoria con TTL de 5 minutos.
377
-
378
- ---
379
-
380
- ## Estructura del proyecto
381
-
382
- ```
383
- src/
384
- ├── config.ts # Configuración central (URLs, endpoints, constantes)
385
- ├── index.ts # Barrel export principal
386
- ├── libraries/
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
391
- │ ├── utils.ts # Utilidades generales
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/
423
- ```
424
-
425
- ## Desarrollo
426
-
427
- ```bash
428
- npm install
429
- npm run build # tsc → dist/
430
- npm run build:watch # watch mode
431
- npm run lint
432
- ```
433
-
434
- ## Publicación
435
-
436
- ```bash
437
- # Incluir [ROAD-361723] en el commit para auto-publish en CI
438
- git commit -m "feat: descripción [ROAD-361723]"
439
- git push
440
- ```
441
-
442
- Tras publicar, actualizar en los portales:
443
- ```bash
444
- npm install webs-sdk@<nueva-version>
445
- ```
446
-
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
- ```
1
+ # webs-sdk
2
+
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.18.9
6
+
7
+ ## Instalación
8
+
9
+ ```bash
10
+ npm install webs-sdk
11
+ ```
12
+
13
+ ## Uso básico
14
+
15
+ ```typescript
16
+ import WebsSDK from 'webs-sdk';
17
+
18
+ // Inicializar sesión
19
+ await WebsSDK.Session.init();
20
+
21
+ // Petición HTTP
22
+ const data = await WebsSDK.Networking.request('/api/endpoint', { param: 'value' });
23
+
24
+ // Almacenamiento local
25
+ WebsSDK.Storage.set('key', 'value');
26
+ const value = WebsSDK.Storage.get('key');
27
+ ```
28
+
29
+ ## Librerías disponibles
30
+
31
+ ### Networking
32
+
33
+ ```typescript
34
+ // Petición HTTP (inyecta sessionData automáticamente)
35
+ await WebsSDK.Networking.request(url, data);
36
+
37
+ // Eventos de tracking
38
+ await WebsSDK.Networking.sendEvent('action', 'button_click', { button_id: 'submit' });
39
+
40
+ // Suscripciones
41
+ await WebsSDK.Networking.createSubscription(subscriptionData);
42
+ const { success, subscription_active } = await WebsSDK.Networking.checkSubscription();
43
+ ```
44
+
45
+ ### Storage
46
+
47
+ ```typescript
48
+ WebsSDK.Storage.set('key', 'value');
49
+ const value = WebsSDK.Storage.get('key');
50
+
51
+ // Compresión de imágenes (0.8 ratio, 1024px max)
52
+ const compressed = await WebsSDK.Storage.compressImage(imageFile, 2); // 2MB max
53
+
54
+ // Creaciones (imágenes generadas por el usuario)
55
+ await WebsSDK.Storage.handleDownloadImageToCreations(base64, 'id', metadata);
56
+ const creations = await WebsSDK.Storage.getCreations();
57
+ await WebsSDK.Storage.deleteCreation('id');
58
+ ```
59
+
60
+ ### Session
61
+
62
+ ```typescript
63
+ await WebsSDK.Session.init();
64
+
65
+ const sessionData = WebsSDK.Session.getSessionData();
66
+ const userId = WebsSDK.Session.getUserID();
67
+ const lang = WebsSDK.Session.getDeviceLanguage();
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
+ ```
80
+
81
+ ### MixPanel
82
+
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' });
88
+ ```
89
+
90
+ ### AuthManager
91
+
92
+ ```typescript
93
+ // Login estándar
94
+ await WebsSDK.AuthManager.authUser({ login, password, website_id });
95
+
96
+ // Login sin password (MSISDN)
97
+ await WebsSDK.AuthManager.authOnlyUser({ login, website_id });
98
+
99
+ // Login con OTP
100
+ await WebsSDK.AuthManager.authOnlyUserOTP({ login, website_id, pincode });
101
+
102
+ // Registro
103
+ await WebsSDK.AuthManager.createUser({ login, password, website_id });
104
+
105
+ // Auto-login post-pago (usado en el proxy de next_template)
106
+ await WebsSDK.AuthManager.auto_login(cfg_sessionid, websiteData, requestUrl);
107
+
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
+ ```
116
+
117
+ ### Andromeda — Portales
118
+
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');
125
+ ```
126
+
127
+ ### ContentManager
128
+
129
+ ```typescript
130
+ // Contenido por preset
131
+ await WebsSDK.ContentManager.getContentByPreset('homepage', { lang: 'es' });
132
+
133
+ // Colección filtrada por tags
134
+ await WebsSDK.ContentManager.getCollection(['tag1', 'tag2'], 'es', false);
135
+ ```
136
+
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
+ ```
144
+
145
+ ### CorporateAuthManager
146
+
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
+ ```
173
+
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
205
+
206
+ ```typescript
207
+ import { initGoogleAnalytics, updateGAConsent, trackGAEvent, trackGAPageView, isGALoaded } from 'webs-sdk';
208
+
209
+ // Registra el GA ID. Carga el script SOLO si hay consentimiento previo.
210
+ await initGoogleAnalytics('G-XXXXXXXXXX', { sendPageView: true, debug: false });
211
+
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
215
+
216
+ // Tracking
217
+ trackGAEvent('purchase', { value: 9.99, currency: 'EUR' });
218
+ trackGAPageView('/home', 'Inicio');
219
+ isGALoaded(); // boolean
220
+ ```
221
+
222
+ > **GDPR estricto:** GA no usa Consent Mode v2. Si no hay consentimiento, el script `gtag.js` nunca se inyecta en el DOM.
223
+
224
+ ### CDN Proxy
225
+
226
+ ```typescript
227
+ import { handleCDNRequest } from 'webs-sdk';
228
+ import type { CDNOptions } from 'webs-sdk';
229
+
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
+ ```
274
+
275
+ Soporta fullscreen en mobile (vendor-prefixed API para iOS Safari).
276
+
277
+ ### SpinningWheel — Ruleta
278
+
279
+ ```typescript
280
+ import { SpinningWheel, wheelThemes } from 'webs-sdk';
281
+
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
+ />
291
+ ```
292
+
293
+ ### Quiz y Stickers
294
+
295
+ ```typescript
296
+ import { Quiz, Stickers } from 'webs-sdk';
297
+ ```
298
+
299
+ ### Esports — Noticias, Vídeos y Partidas en Directo
300
+
301
+ ```typescript
302
+ import { EsportsNews, EsportsVideos, EsportsLive } from 'webs-sdk';
303
+ ```
304
+
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
+ ```
323
+
324
+ #### EsportsVideos
325
+
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
+ />
333
+ ```
334
+
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
+ ---
354
+
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);
374
+ ```
375
+
376
+ Soporta HLS, DASH, MP4. Cache en memoria con TTL de 5 minutos.
377
+
378
+ ---
379
+
380
+ ## Estructura del proyecto
381
+
382
+ ```
383
+ src/
384
+ ├── config.ts # Configuración central (URLs, endpoints, constantes)
385
+ ├── index.ts # Barrel export principal
386
+ ├── libraries/
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
391
+ │ ├── utils.ts # Utilidades generales
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/
423
+ ```
424
+
425
+ ## Desarrollo
426
+
427
+ ```bash
428
+ npm install
429
+ npm run build # tsc → dist/
430
+ npm run build:watch # watch mode
431
+ npm run lint
432
+ ```
433
+
434
+ ## Publicación
435
+
436
+ ```bash
437
+ # Incluir [ROAD-361723] en el commit para auto-publish en CI
438
+ git commit -m "feat: descripción [ROAD-361723]"
439
+ git push
440
+ ```
441
+
442
+ Tras publicar, actualizar en los portales:
443
+ ```bash
444
+ npm install webs-sdk@<nueva-version>
445
+ ```
446
+
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
+ ```