@magicfeedback/popup-sdk 1.1.2 → 1.1.4

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/dist/index.d.mts CHANGED
@@ -352,6 +352,109 @@ declare class ContactManager {
352
352
  setAttributes(attributes: ContactAttributes): Promise<boolean>;
353
353
  }
354
354
 
355
+ /**
356
+ * Crash & error reporting (Stability #14–17) — captura de errores GESTIONADOS.
357
+ *
358
+ * - `reportError()`: API pública para el host (app viva) → emite `deepdots_app_crash` ya.
359
+ * - handlers de `window` (errores no capturados): persisten a disco y se reenvían
360
+ * en el siguiente arranque (el proceso puede morir antes del flush normal).
361
+ *
362
+ * El contexto de device/sesión se captura EN EL MOMENTO del crash y se guarda en el
363
+ * record — no se lee del envelope en el replay (el replay puede ocurrir en otra
364
+ * app_version/OS). Las dims OS/device en Web suelen ir vacías (se derivan del UA en backend).
365
+ */
366
+
367
+ type CrashSeverity = 'fatal' | 'error' | 'warning';
368
+ interface CrashRecord {
369
+ crashedAt: number;
370
+ crashType: string;
371
+ message: string;
372
+ stack: string;
373
+ fatal: boolean;
374
+ handled: boolean;
375
+ severity: CrashSeverity;
376
+ sessionId: string | null;
377
+ appVersion?: string;
378
+ osVersion?: string;
379
+ deviceModel?: string;
380
+ context?: Record<string, string>;
381
+ }
382
+ interface ReportErrorOptions {
383
+ severity?: CrashSeverity;
384
+ handled?: boolean;
385
+ context?: Record<string, unknown>;
386
+ }
387
+ interface DeviceSnapshot {
388
+ appVersion?: string;
389
+ osVersion?: string;
390
+ deviceModel?: string;
391
+ }
392
+ /** Forma mínima de `global.ErrorUtils` de React Native. */
393
+ interface ReactNativeErrorUtils {
394
+ getGlobalHandler?(): ((error: unknown, isFatal?: boolean) => void) | undefined;
395
+ setGlobalHandler(handler: (error: unknown, isFatal?: boolean) => void): void;
396
+ }
397
+ interface CrashReporterOptions {
398
+ storage: KeyValueStorage;
399
+ /** Emite un evento deepdots_app_crash AHORA (app viva). */
400
+ emit: (params: Record<string, unknown>) => void;
401
+ /** Snapshot de device en el momento del crash. */
402
+ device: () => DeviceSnapshot;
403
+ /** session_id en el momento del crash. */
404
+ sessionId: () => string | null;
405
+ now?: () => number;
406
+ /** Kill-switch de consentimiento. */
407
+ enabled?: () => boolean;
408
+ }
409
+ /** Convierte un CrashRecord en los params del evento `deepdots_app_crash` (omite undefined). */
410
+ declare function crashRecordToParams(r: CrashRecord): Record<string, unknown>;
411
+ declare class CrashReporter {
412
+ private options;
413
+ private now;
414
+ constructor(options: CrashReporterOptions);
415
+ private isEnabled;
416
+ /** Construye un CrashRecord capturando contexto en el momento del crash. */
417
+ private buildRecord;
418
+ /** API pública del host: reporta un error (app viva) → emite el evento ya. */
419
+ reportError(error: unknown, options?: ReportErrorOptions): void;
420
+ /** Captura un error no manejado (fatal) → persiste a disco para replay en el siguiente arranque. */
421
+ captureUnhandled(error: unknown): void;
422
+ /** Persiste un crash no capturado a disco para reenviarlo en el siguiente arranque. */
423
+ private persist;
424
+ /** Lee y vacía la cola de crashes pendientes (llamado en init para el replay). */
425
+ drainPendingCrashes(): CrashRecord[];
426
+ /** Instala los handlers globales de errores no capturados (no-op sin `window`). */
427
+ install(): void;
428
+ /** Engancha `global.ErrorUtils` de RN (no hay `window`): persiste y encadena al handler previo. */
429
+ installReactNative(errorUtils: ReactNativeErrorUtils): void;
430
+ /** @internal Test seam: ejercita el camino de persistencia (window handlers no son testeables en vitest). */
431
+ _persistForTest(error: unknown): void;
432
+ }
433
+
434
+ /**
435
+ * Messaging (#18–22): tracking de notificaciones del host (push / in-app).
436
+ * Un único evento reservado `deepdots_message` con un campo `stage` discriminador;
437
+ * el funnel se correlaciona por `message_id` y se agrupa por `message_title`.
438
+ */
439
+ type MessageStage = 'delivered' | 'clicked' | 'converted';
440
+ interface TrackMessageOptions {
441
+ /** Identificador del mensaje/notificación — correlaciona el funnel delivered→clicked→converted. */
442
+ id: string;
443
+ /** Título del mensaje — dimensión de agrupación de #18–22. */
444
+ title: string;
445
+ /** Canal de entrega. */
446
+ channel: 'push' | 'in_app';
447
+ /** Nombre de la campaña (opcional). */
448
+ campaign?: string;
449
+ /** Valor de conversión (opcional, típico en `stage: 'converted'`). */
450
+ value?: number;
451
+ currency?: string;
452
+ /** Parámetros libres adicionales. */
453
+ params?: Record<string, unknown>;
454
+ }
455
+ /** Construye los params del evento `deepdots_message` (omite opcionales ausentes). */
456
+ declare function buildMessageParams(stage: MessageStage, o: TrackMessageOptions): Record<string, unknown>;
457
+
355
458
  /**
356
459
  * Main class for managing survey popups
357
460
  */
@@ -380,6 +483,8 @@ declare class DeepdotsPopups {
380
483
  private analyticsFeedbackSessionId;
381
484
  /** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
382
485
  private analyticsFlushTimer;
486
+ /** Crash & error reporting (#14–17). Null hasta init(). */
487
+ private crashReporter;
383
488
  /** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
384
489
  private navObserver;
385
490
  /** Tiempo activo (engagement time, #8). */
@@ -400,6 +505,10 @@ declare class DeepdotsPopups {
400
505
  track(name: string, params?: Record<string, unknown>): void;
401
506
  /** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
402
507
  setUserAttributes(attributes: Record<string, string | number | boolean>): void;
508
+ /** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
509
+ reportError(error: unknown, options?: ReportErrorOptions): void;
510
+ /** Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. No-op si tracking off. */
511
+ installReactNativeCrashHandler(errorUtils: ReactNativeErrorUtils): void;
403
512
  /**
404
513
  * Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
405
514
  * Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del
@@ -409,14 +518,16 @@ declare class DeepdotsPopups {
409
518
  setContactAttributes(attributes: ContactAttributes): Promise<boolean>;
410
519
  /** Marca el inicio de un mini-service; etiqueta los eventos siguientes. No-op si tracking off. */
411
520
  enterMiniService(name: string, entryPointType?: string): void;
412
- /** Cierra el mini-service activo (emite `mini_service_exit` con duración, #27). No-op si tracking off. */
413
- exitMiniService(): void;
521
+ /** Cierra el mini-service `name` (emite `mini_service_exit` con duración, #27). No-op si tracking off o si ese no está activo. */
522
+ exitMiniService(name: string): void;
414
523
  /** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */
415
524
  trackSearch(query: string, resultsCount: number, params?: Record<string, unknown>): void;
416
525
  /** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */
417
526
  trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
418
527
  /** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
419
528
  trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
529
+ /** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
530
+ trackMessage(stage: MessageStage, options: TrackMessageOptions): void;
420
531
  /**
421
532
  * Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
422
533
  * El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
@@ -610,6 +721,8 @@ interface ReactNativeSetupDeps {
610
721
  appState?: AppStateLike | null;
611
722
  platform?: 'web' | 'android' | 'ios';
612
723
  renderer?: PopupRenderer;
724
+ /** `global.ErrorUtils` de RN (para capturar errores JS no manejados). Default: `globalThis.ErrorUtils`. */
725
+ errorUtils?: ReactNativeErrorUtils | null;
613
726
  }
614
727
  /** Adaptador KeyValueStorage (síncrono) sobre una instancia de MMKV. */
615
728
  declare function mmkvStorage(mmkv: MmkvLike): KeyValueStorage;
@@ -622,4 +735,4 @@ declare function collectRnDevice(d: DeviceInfoLike): DeviceInfo;
622
735
  */
623
736
  declare function setupReactNative(sdk: DeepdotsPopups, config: DeepdotsInitParams, deps?: ReactNativeSetupDeps): () => void;
624
737
 
625
- export { type AnalyticsFeedbackBody, type AnalyticsKeys, type AppStateLike, BrowserPopupRenderer, type ContactAttributeValue, type ContactAttributes, type ContactBody, ContactManager, type DeepdotsConfig, type DeepdotsEvent, type DeepdotsEventType, DeepdotsPopups, type DeviceInfo, type DeviceInfoLike, type EventListener, type FeedbackKV, InMemoryStorage, type KeyValueStorage, type MmkvLike, NoopPopupRenderer, POPUP_TRIGGER_CONDITION_STATUSES, type PopupDefinition, type PopupRenderer, type PopupTrigger, type PopupTriggerCondition, type PopupTriggerConditionStatus, ReactNativePopupRenderer, type ReactNativeRendererOptions, type ReactNativeSetupDeps, type ReactNativeSurveyPayload, type TriggerConfig, buildAnalyticsFeedbackBody, buildSurveyHtml, collectRnDevice, createDefaultRenderer, createFeedbackSink, mmkvStorage, setupReactNative };
738
+ export { type AnalyticsFeedbackBody, type AnalyticsKeys, type AppStateLike, BrowserPopupRenderer, type ContactAttributeValue, type ContactAttributes, type ContactBody, ContactManager, type CrashRecord, CrashReporter, type CrashSeverity, type DeepdotsConfig, type DeepdotsEvent, type DeepdotsEventType, DeepdotsPopups, type DeviceInfo, type DeviceInfoLike, type DeviceSnapshot, type EventListener, type FeedbackKV, InMemoryStorage, type KeyValueStorage, type MessageStage, type MmkvLike, NoopPopupRenderer, POPUP_TRIGGER_CONDITION_STATUSES, type PopupDefinition, type PopupRenderer, type PopupTrigger, type PopupTriggerCondition, type PopupTriggerConditionStatus, type ReactNativeErrorUtils, ReactNativePopupRenderer, type ReactNativeRendererOptions, type ReactNativeSetupDeps, type ReactNativeSurveyPayload, type ReportErrorOptions, type TrackMessageOptions, type TriggerConfig, buildAnalyticsFeedbackBody, buildMessageParams, buildSurveyHtml, collectRnDevice, crashRecordToParams, createDefaultRenderer, createFeedbackSink, mmkvStorage, setupReactNative };
package/dist/index.d.ts CHANGED
@@ -352,6 +352,109 @@ declare class ContactManager {
352
352
  setAttributes(attributes: ContactAttributes): Promise<boolean>;
353
353
  }
354
354
 
355
+ /**
356
+ * Crash & error reporting (Stability #14–17) — captura de errores GESTIONADOS.
357
+ *
358
+ * - `reportError()`: API pública para el host (app viva) → emite `deepdots_app_crash` ya.
359
+ * - handlers de `window` (errores no capturados): persisten a disco y se reenvían
360
+ * en el siguiente arranque (el proceso puede morir antes del flush normal).
361
+ *
362
+ * El contexto de device/sesión se captura EN EL MOMENTO del crash y se guarda en el
363
+ * record — no se lee del envelope en el replay (el replay puede ocurrir en otra
364
+ * app_version/OS). Las dims OS/device en Web suelen ir vacías (se derivan del UA en backend).
365
+ */
366
+
367
+ type CrashSeverity = 'fatal' | 'error' | 'warning';
368
+ interface CrashRecord {
369
+ crashedAt: number;
370
+ crashType: string;
371
+ message: string;
372
+ stack: string;
373
+ fatal: boolean;
374
+ handled: boolean;
375
+ severity: CrashSeverity;
376
+ sessionId: string | null;
377
+ appVersion?: string;
378
+ osVersion?: string;
379
+ deviceModel?: string;
380
+ context?: Record<string, string>;
381
+ }
382
+ interface ReportErrorOptions {
383
+ severity?: CrashSeverity;
384
+ handled?: boolean;
385
+ context?: Record<string, unknown>;
386
+ }
387
+ interface DeviceSnapshot {
388
+ appVersion?: string;
389
+ osVersion?: string;
390
+ deviceModel?: string;
391
+ }
392
+ /** Forma mínima de `global.ErrorUtils` de React Native. */
393
+ interface ReactNativeErrorUtils {
394
+ getGlobalHandler?(): ((error: unknown, isFatal?: boolean) => void) | undefined;
395
+ setGlobalHandler(handler: (error: unknown, isFatal?: boolean) => void): void;
396
+ }
397
+ interface CrashReporterOptions {
398
+ storage: KeyValueStorage;
399
+ /** Emite un evento deepdots_app_crash AHORA (app viva). */
400
+ emit: (params: Record<string, unknown>) => void;
401
+ /** Snapshot de device en el momento del crash. */
402
+ device: () => DeviceSnapshot;
403
+ /** session_id en el momento del crash. */
404
+ sessionId: () => string | null;
405
+ now?: () => number;
406
+ /** Kill-switch de consentimiento. */
407
+ enabled?: () => boolean;
408
+ }
409
+ /** Convierte un CrashRecord en los params del evento `deepdots_app_crash` (omite undefined). */
410
+ declare function crashRecordToParams(r: CrashRecord): Record<string, unknown>;
411
+ declare class CrashReporter {
412
+ private options;
413
+ private now;
414
+ constructor(options: CrashReporterOptions);
415
+ private isEnabled;
416
+ /** Construye un CrashRecord capturando contexto en el momento del crash. */
417
+ private buildRecord;
418
+ /** API pública del host: reporta un error (app viva) → emite el evento ya. */
419
+ reportError(error: unknown, options?: ReportErrorOptions): void;
420
+ /** Captura un error no manejado (fatal) → persiste a disco para replay en el siguiente arranque. */
421
+ captureUnhandled(error: unknown): void;
422
+ /** Persiste un crash no capturado a disco para reenviarlo en el siguiente arranque. */
423
+ private persist;
424
+ /** Lee y vacía la cola de crashes pendientes (llamado en init para el replay). */
425
+ drainPendingCrashes(): CrashRecord[];
426
+ /** Instala los handlers globales de errores no capturados (no-op sin `window`). */
427
+ install(): void;
428
+ /** Engancha `global.ErrorUtils` de RN (no hay `window`): persiste y encadena al handler previo. */
429
+ installReactNative(errorUtils: ReactNativeErrorUtils): void;
430
+ /** @internal Test seam: ejercita el camino de persistencia (window handlers no son testeables en vitest). */
431
+ _persistForTest(error: unknown): void;
432
+ }
433
+
434
+ /**
435
+ * Messaging (#18–22): tracking de notificaciones del host (push / in-app).
436
+ * Un único evento reservado `deepdots_message` con un campo `stage` discriminador;
437
+ * el funnel se correlaciona por `message_id` y se agrupa por `message_title`.
438
+ */
439
+ type MessageStage = 'delivered' | 'clicked' | 'converted';
440
+ interface TrackMessageOptions {
441
+ /** Identificador del mensaje/notificación — correlaciona el funnel delivered→clicked→converted. */
442
+ id: string;
443
+ /** Título del mensaje — dimensión de agrupación de #18–22. */
444
+ title: string;
445
+ /** Canal de entrega. */
446
+ channel: 'push' | 'in_app';
447
+ /** Nombre de la campaña (opcional). */
448
+ campaign?: string;
449
+ /** Valor de conversión (opcional, típico en `stage: 'converted'`). */
450
+ value?: number;
451
+ currency?: string;
452
+ /** Parámetros libres adicionales. */
453
+ params?: Record<string, unknown>;
454
+ }
455
+ /** Construye los params del evento `deepdots_message` (omite opcionales ausentes). */
456
+ declare function buildMessageParams(stage: MessageStage, o: TrackMessageOptions): Record<string, unknown>;
457
+
355
458
  /**
356
459
  * Main class for managing survey popups
357
460
  */
@@ -380,6 +483,8 @@ declare class DeepdotsPopups {
380
483
  private analyticsFeedbackSessionId;
381
484
  /** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
382
485
  private analyticsFlushTimer;
486
+ /** Crash & error reporting (#14–17). Null hasta init(). */
487
+ private crashReporter;
383
488
  /** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
384
489
  private navObserver;
385
490
  /** Tiempo activo (engagement time, #8). */
@@ -400,6 +505,10 @@ declare class DeepdotsPopups {
400
505
  track(name: string, params?: Record<string, unknown>): void;
401
506
  /** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
402
507
  setUserAttributes(attributes: Record<string, string | number | boolean>): void;
508
+ /** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
509
+ reportError(error: unknown, options?: ReportErrorOptions): void;
510
+ /** Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. No-op si tracking off. */
511
+ installReactNativeCrashHandler(errorUtils: ReactNativeErrorUtils): void;
403
512
  /**
404
513
  * Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
405
514
  * Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del
@@ -409,14 +518,16 @@ declare class DeepdotsPopups {
409
518
  setContactAttributes(attributes: ContactAttributes): Promise<boolean>;
410
519
  /** Marca el inicio de un mini-service; etiqueta los eventos siguientes. No-op si tracking off. */
411
520
  enterMiniService(name: string, entryPointType?: string): void;
412
- /** Cierra el mini-service activo (emite `mini_service_exit` con duración, #27). No-op si tracking off. */
413
- exitMiniService(): void;
521
+ /** Cierra el mini-service `name` (emite `mini_service_exit` con duración, #27). No-op si tracking off o si ese no está activo. */
522
+ exitMiniService(name: string): void;
414
523
  /** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */
415
524
  trackSearch(query: string, resultsCount: number, params?: Record<string, unknown>): void;
416
525
  /** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */
417
526
  trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
418
527
  /** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
419
528
  trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
529
+ /** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
530
+ trackMessage(stage: MessageStage, options: TrackMessageOptions): void;
420
531
  /**
421
532
  * Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
422
533
  * El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
@@ -610,6 +721,8 @@ interface ReactNativeSetupDeps {
610
721
  appState?: AppStateLike | null;
611
722
  platform?: 'web' | 'android' | 'ios';
612
723
  renderer?: PopupRenderer;
724
+ /** `global.ErrorUtils` de RN (para capturar errores JS no manejados). Default: `globalThis.ErrorUtils`. */
725
+ errorUtils?: ReactNativeErrorUtils | null;
613
726
  }
614
727
  /** Adaptador KeyValueStorage (síncrono) sobre una instancia de MMKV. */
615
728
  declare function mmkvStorage(mmkv: MmkvLike): KeyValueStorage;
@@ -622,4 +735,4 @@ declare function collectRnDevice(d: DeviceInfoLike): DeviceInfo;
622
735
  */
623
736
  declare function setupReactNative(sdk: DeepdotsPopups, config: DeepdotsInitParams, deps?: ReactNativeSetupDeps): () => void;
624
737
 
625
- export { type AnalyticsFeedbackBody, type AnalyticsKeys, type AppStateLike, BrowserPopupRenderer, type ContactAttributeValue, type ContactAttributes, type ContactBody, ContactManager, type DeepdotsConfig, type DeepdotsEvent, type DeepdotsEventType, DeepdotsPopups, type DeviceInfo, type DeviceInfoLike, type EventListener, type FeedbackKV, InMemoryStorage, type KeyValueStorage, type MmkvLike, NoopPopupRenderer, POPUP_TRIGGER_CONDITION_STATUSES, type PopupDefinition, type PopupRenderer, type PopupTrigger, type PopupTriggerCondition, type PopupTriggerConditionStatus, ReactNativePopupRenderer, type ReactNativeRendererOptions, type ReactNativeSetupDeps, type ReactNativeSurveyPayload, type TriggerConfig, buildAnalyticsFeedbackBody, buildSurveyHtml, collectRnDevice, createDefaultRenderer, createFeedbackSink, mmkvStorage, setupReactNative };
738
+ export { type AnalyticsFeedbackBody, type AnalyticsKeys, type AppStateLike, BrowserPopupRenderer, type ContactAttributeValue, type ContactAttributes, type ContactBody, ContactManager, type CrashRecord, CrashReporter, type CrashSeverity, type DeepdotsConfig, type DeepdotsEvent, type DeepdotsEventType, DeepdotsPopups, type DeviceInfo, type DeviceInfoLike, type DeviceSnapshot, type EventListener, type FeedbackKV, InMemoryStorage, type KeyValueStorage, type MessageStage, type MmkvLike, NoopPopupRenderer, POPUP_TRIGGER_CONDITION_STATUSES, type PopupDefinition, type PopupRenderer, type PopupTrigger, type PopupTriggerCondition, type PopupTriggerConditionStatus, type ReactNativeErrorUtils, ReactNativePopupRenderer, type ReactNativeRendererOptions, type ReactNativeSetupDeps, type ReactNativeSurveyPayload, type ReportErrorOptions, type TrackMessageOptions, type TriggerConfig, buildAnalyticsFeedbackBody, buildMessageParams, buildSurveyHtml, collectRnDevice, crashRecordToParams, createDefaultRenderer, createFeedbackSink, mmkvStorage, setupReactNative };