@magicfeedback/popup-sdk 1.1.8 → 1.3.0
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 +113 -70
- package/dist/assets/assets/style.css +5 -1
- package/dist/chunk-R4B3K2ZW.mjs +176 -0
- package/dist/chunk-R4B3K2ZW.mjs.map +1 -0
- package/dist/chunk-XFA2Y6CK.mjs +197 -0
- package/dist/chunk-XFA2Y6CK.mjs.map +1 -0
- package/dist/index.d.mts +130 -14
- package/dist/index.d.ts +130 -14
- package/dist/index.js +426 -910
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +416 -44
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +60 -8
- package/dist/react-native.d.ts +60 -8
- package/dist/react-native.js +425 -911
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +415 -45
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-EZKDMLVZ.mjs → renderPopup-NJK7Z46E.mjs} +10 -870
- package/dist/renderPopup-NJK7Z46E.mjs.map +1 -0
- package/dist/{renderPopup-PS3NJDUR.mjs → renderPopup-ZYXOUBCP.mjs} +10 -870
- package/dist/renderPopup-ZYXOUBCP.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-4UXOEIAB.mjs +0 -172
- package/dist/chunk-4UXOEIAB.mjs.map +0 -1
- package/dist/chunk-XOTM5CHX.mjs +0 -193
- package/dist/chunk-XOTM5CHX.mjs.map +0 -1
- package/dist/renderPopup-EZKDMLVZ.mjs.map +0 -1
- package/dist/renderPopup-PS3NJDUR.mjs.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -98,6 +98,12 @@ interface AnalyticsFlushMeta {
|
|
|
98
98
|
* un transporte que sobreviva al unload (sendBeacon) y no puede esperar respuestas.
|
|
99
99
|
*/
|
|
100
100
|
final?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* `true` cuando este es el ÚLTIMO lote de la sesión (cierre de página, app a background,
|
|
103
|
+
* cambio de usuario, tracking desactivado). El sink lo envía con `completed:true` y olvida
|
|
104
|
+
* el `sessionId`, para que el lote siguiente abra un registro nuevo.
|
|
105
|
+
*/
|
|
106
|
+
sessionEnd?: boolean;
|
|
101
107
|
}
|
|
102
108
|
/**
|
|
103
109
|
* Envío del lote. Si devuelve una promesa que **rechaza**, el manager re-encola los
|
|
@@ -110,8 +116,11 @@ type AnalyticsSink = (payload: AnalyticsEnvelope, meta?: AnalyticsFlushMeta) =>
|
|
|
110
116
|
* Mapeo del envelope de analytics → body de `POST /sdk/feedback`.
|
|
111
117
|
*
|
|
112
118
|
* La analítica se envía como un Feedback del modelo del Surveys SDK, agrupado por una
|
|
113
|
-
* INTEGRACIÓN creada en la plataforma. Se manda en streaming con `completed:false
|
|
114
|
-
*
|
|
119
|
+
* INTEGRACIÓN creada en la plataforma. Se manda en streaming con `completed:false`; el
|
|
120
|
+
* backend cose por `sessionId` + `user_id`. El ÚLTIMO lote de una sesión (cierre de página,
|
|
121
|
+
* app a background, cambio de usuario) va con `completed:true`: cierra el registro y el
|
|
122
|
+
* siguiente lote omite el `sessionId` viejo para que el backend abra uno nuevo.
|
|
123
|
+
* `feedback.finished` se queda siempre en `false` (no es la señal de cierre acordada).
|
|
115
124
|
*
|
|
116
125
|
* Encoding:
|
|
117
126
|
* - todo va en `feedback.metadata`: contexto (user_id, session_id, platform…) + eventos
|
|
@@ -147,7 +156,15 @@ interface AnalyticsFeedbackBody {
|
|
|
147
156
|
/** sessionId devuelto por el primer POST; permite que el backend agrupe todos los eventos en un solo registro. */
|
|
148
157
|
sessionId?: string;
|
|
149
158
|
}
|
|
150
|
-
|
|
159
|
+
interface BuildBodyOptions {
|
|
160
|
+
/**
|
|
161
|
+
* `true` en el último lote de la sesión → `completed:true` (cierra el registro en backend).
|
|
162
|
+
* El body SÍ lleva el `sessionId` actual (es el registro que se cierra); es el lote
|
|
163
|
+
* SIGUIENTE el que lo omite.
|
|
164
|
+
*/
|
|
165
|
+
sessionEnd?: boolean;
|
|
166
|
+
}
|
|
167
|
+
declare function buildAnalyticsFeedbackBody(envelope: AnalyticsEnvelope, keys: AnalyticsKeys, feedbackSessionId?: string, options?: BuildBodyOptions): AnalyticsFeedbackBody;
|
|
151
168
|
interface FeedbackSinkOptions {
|
|
152
169
|
/** Base URL del backend (api(.|-dev.)deepdots.com) ya resuelta por el entorno. */
|
|
153
170
|
baseUrl: string;
|
|
@@ -164,6 +181,8 @@ interface FeedbackSinkOptions {
|
|
|
164
181
|
sendBeaconImpl?: (url: string, body: Blob | string) => boolean;
|
|
165
182
|
/** Callback invocado cuando el backend devuelve un nuevo sessionId (primer POST). */
|
|
166
183
|
onSessionId?: (id: string) => void;
|
|
184
|
+
/** Callback invocado al cerrar la sesión (`completed:true`): el sessionId cacheado se olvida. */
|
|
185
|
+
onSessionReset?: () => void;
|
|
167
186
|
}
|
|
168
187
|
/**
|
|
169
188
|
* Sink real: transforma el envelope y hace `POST {baseUrl}/sdk/feedback`.
|
|
@@ -276,6 +295,16 @@ interface DeepdotsEvent {
|
|
|
276
295
|
* Event listener callback
|
|
277
296
|
*/
|
|
278
297
|
type EventListener = (event: DeepdotsEvent) => void;
|
|
298
|
+
/**
|
|
299
|
+
* Motivo del `deepdots_session_end` (último lote de la sesión, enviado con `completed:true`):
|
|
300
|
+
* - `page_hide`: la página web se cierra o se navega fuera (`pagehide`).
|
|
301
|
+
* - `background`: la app pasa a background (RN/nativo). Única señal disponible en móvil:
|
|
302
|
+
* el kill de la app por el usuario o el SO NO produce ningún callback.
|
|
303
|
+
* - `user_change`: `setUserId()` o un `init()` con otro `userId` (login/logout del host).
|
|
304
|
+
* - `tracking_disabled`: `setTrackingEnabled(false)` (consentimiento revocado).
|
|
305
|
+
* - `manual`: `endSession()` llamado por el host.
|
|
306
|
+
*/
|
|
307
|
+
type SessionEndReason = 'page_hide' | 'background' | 'user_change' | 'tracking_disabled' | 'manual';
|
|
279
308
|
/** Trigger type used by remote popup definitions */
|
|
280
309
|
type PopupTriggerType = 'time_on_page' | 'scroll' | 'exit' | 'click' | 'event';
|
|
281
310
|
type PopupTriggerConditionStatus = 'SHOWED' | 'PARTIAL' | 'COMPLETED';
|
|
@@ -498,13 +527,16 @@ declare class CrashReporter {
|
|
|
498
527
|
* el funnel se correlaciona por `message_id` y se agrupa por `message_title`.
|
|
499
528
|
*/
|
|
500
529
|
type MessageStage = 'delivered' | 'clicked' | 'converted';
|
|
530
|
+
/** Canales válidos. Un mensaje tiene UN canal: se valida en runtime porque el host puede no ser TS. */
|
|
531
|
+
declare const MESSAGE_CHANNELS: readonly ["push", "in_app"];
|
|
532
|
+
type MessageChannel = (typeof MESSAGE_CHANNELS)[number];
|
|
501
533
|
interface TrackMessageOptions {
|
|
502
534
|
/** Identificador del mensaje/notificación — correlaciona el funnel delivered→clicked→converted. */
|
|
503
535
|
id: string;
|
|
504
536
|
/** Título del mensaje — dimensión de agrupación de #18–22. */
|
|
505
537
|
title: string;
|
|
506
538
|
/** Canal de entrega. */
|
|
507
|
-
channel:
|
|
539
|
+
channel: MessageChannel;
|
|
508
540
|
/** Nombre de la campaña (opcional). */
|
|
509
541
|
campaign?: string;
|
|
510
542
|
/** Valor de conversión (opcional, típico en `stage: 'converted'`). */
|
|
@@ -515,6 +547,38 @@ interface TrackMessageOptions {
|
|
|
515
547
|
}
|
|
516
548
|
/** Construye los params del evento `deepdots_message` (omite opcionales ausentes). */
|
|
517
549
|
declare function buildMessageParams(stage: MessageStage, o: TrackMessageOptions): Record<string, unknown>;
|
|
550
|
+
/** Techo de `message_id` vigilados por sesión; los más antiguos se evictan (memoria acotada). */
|
|
551
|
+
declare const MAX_TRACKED_MESSAGES = 500;
|
|
552
|
+
type MessageRejectionReason = 'invalid_channel' | 'duplicate_stage' | 'channel_conflict';
|
|
553
|
+
type MessageGuardVerdict = {
|
|
554
|
+
emit: true;
|
|
555
|
+
} | {
|
|
556
|
+
emit: false;
|
|
557
|
+
reason: MessageRejectionReason;
|
|
558
|
+
detail: string;
|
|
559
|
+
};
|
|
560
|
+
/**
|
|
561
|
+
* Protege el funnel de Messaging de las formas imposibles que el host puede producir por error
|
|
562
|
+
* (visto en producción: CTR > 100% por falta de `delivered` + el mismo `message_id` reportado en
|
|
563
|
+
* dos canales). Tres reglas, todas dentro de la sesión (se reinicia en cada `init()`):
|
|
564
|
+
*
|
|
565
|
+
* 1. `channel` fuera de `MESSAGE_CHANNELS` → se descarta el evento.
|
|
566
|
+
* 2. Un `(message_id, stage)` ya emitido → se emite una sola vez.
|
|
567
|
+
* 3. Un `message_id` ya visto en un canal → se descartan los eventos de otro canal.
|
|
568
|
+
*
|
|
569
|
+
* Los eventos rechazados NO mutan el estado: un rechazo nunca "consume" el stage bueno.
|
|
570
|
+
* Lo que el SDK no puede arreglar es la ausencia de `delivered`: eso lo instrumenta el host.
|
|
571
|
+
*/
|
|
572
|
+
declare class MessageGuard {
|
|
573
|
+
private maxTrackedMessages;
|
|
574
|
+
/** message_id → canal fijado + stages ya emitidos. El orden de inserción marca la eviction. */
|
|
575
|
+
private seen;
|
|
576
|
+
constructor(maxTrackedMessages?: number);
|
|
577
|
+
evaluate(stage: MessageStage, options: TrackMessageOptions): MessageGuardVerdict;
|
|
578
|
+
/** Olvida todo lo vigilado (nueva sesión). */
|
|
579
|
+
reset(): void;
|
|
580
|
+
private remember;
|
|
581
|
+
}
|
|
518
582
|
|
|
519
583
|
/**
|
|
520
584
|
* Main class for managing survey popups
|
|
@@ -548,14 +612,24 @@ declare class DeepdotsPopups {
|
|
|
548
612
|
private analyticsFlushTimer;
|
|
549
613
|
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
550
614
|
private crashReporter;
|
|
615
|
+
/** Protecciones del funnel de Messaging (#18–22). Vigencia de sesión: se reinicia en init(). */
|
|
616
|
+
private messageGuard;
|
|
551
617
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
552
618
|
private navObserver;
|
|
553
619
|
/** Tiempo activo (engagement time, #8). */
|
|
554
620
|
private engagement;
|
|
555
621
|
/** Atributos internos del usuario identificado → POST /sdk/popups/contact. Null si no hay userId. */
|
|
556
622
|
private contact;
|
|
623
|
+
/** Storage resuelto en init() (inyectado por el host o localStorage). Reusado al cambiar de usuario. */
|
|
624
|
+
private storage;
|
|
557
625
|
/** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */
|
|
558
626
|
private navStarted;
|
|
627
|
+
/**
|
|
628
|
+
* `true` entre `session_start` y `session_end`. Hace idempotente tanto la apertura como
|
|
629
|
+
* el cierre (p. ej. `visibilitychange` + `pagehide` seguidos) y permite abrir una sesión
|
|
630
|
+
* nueva al volver a foreground o al conceder el consentimiento más tarde.
|
|
631
|
+
*/
|
|
632
|
+
private sessionOpen;
|
|
559
633
|
/**
|
|
560
634
|
* Idioma resuelto en init() (explícito > navigator.language > Intl). Única fuente de
|
|
561
635
|
* verdad para el context de analytics Y para la segmentación por idioma de los popups.
|
|
@@ -599,20 +673,55 @@ declare class DeepdotsPopups {
|
|
|
599
673
|
trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
|
|
600
674
|
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
601
675
|
trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
|
|
602
|
-
/**
|
|
676
|
+
/**
|
|
677
|
+
* Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
|
|
678
|
+
* No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
|
|
679
|
+
* si ese `(message_id, stage)` ya se emitió en la sesión, o si el `message_id` ya se reportó
|
|
680
|
+
* en otro canal — ver `MessageGuard`.
|
|
681
|
+
*/
|
|
603
682
|
trackMessage(stage: MessageStage, options: TrackMessageOptions): void;
|
|
604
683
|
/**
|
|
605
684
|
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
606
685
|
* El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.
|
|
607
686
|
*/
|
|
608
687
|
setScreen(name: string): void;
|
|
609
|
-
/** App a foreground (RN: AppState 'active'): reanuda el engagement time. */
|
|
688
|
+
/** App a foreground (RN: AppState 'active'): reanuda el engagement time y abre sesión nueva. */
|
|
610
689
|
onForeground(): void;
|
|
611
690
|
/**
|
|
612
|
-
* App a background (RN: AppState 'background'):
|
|
613
|
-
* mini-
|
|
691
|
+
* App a background (RN: AppState 'background'): FIN DE SESIÓN. Cierra pantalla actual
|
|
692
|
+
* (page_view), mini-services (mini_service_exit), emite engagement + `session_end` y
|
|
693
|
+
* envía el último lote con `completed:true`.
|
|
694
|
+
*
|
|
695
|
+
* Es la única señal disponible en móvil: el kill de la app (swipe) NO da callback.
|
|
614
696
|
*/
|
|
615
697
|
onBackground(): void;
|
|
698
|
+
/**
|
|
699
|
+
* Cierre EXPLÍCITO de la sesión por parte del host (logout, fin de flujo…): emite
|
|
700
|
+
* `session_end` y envía el último lote con `completed:true`.
|
|
701
|
+
*/
|
|
702
|
+
endSession(): void;
|
|
703
|
+
/**
|
|
704
|
+
* Cambio de usuario (login / logout / cambio de cuenta). Cierra la sesión del usuario
|
|
705
|
+
* anterior (`session_end` con `reason: 'user_change'` + `completed:true`), cambia la
|
|
706
|
+
* identidad y abre una sesión nueva. Sin `userId` vuelve al id anónimo del SDK.
|
|
707
|
+
*
|
|
708
|
+
* Equivale a llamar a `init()` otra vez con otro `userId`.
|
|
709
|
+
*/
|
|
710
|
+
setUserId(userId?: string): void;
|
|
711
|
+
/**
|
|
712
|
+
* Cierra la sesión actual: vuelca todo lo que quedaba abierto y envía el último lote
|
|
713
|
+
* marcado con `completed:true`, para que el backend cierre el registro. El `sessionId`
|
|
714
|
+
* cacheado se olvida → el lote siguiente abre un registro nuevo.
|
|
715
|
+
*
|
|
716
|
+
* Idempotente: dos cierres seguidos (p. ej. `visibilitychange` + `pagehide`) no duplican
|
|
717
|
+
* el `session_end`.
|
|
718
|
+
*/
|
|
719
|
+
private closeSession;
|
|
720
|
+
/**
|
|
721
|
+
* Abre sesión (`session_start`) si no hay una abierta y el tracking está activo. Idempotente:
|
|
722
|
+
* se llama en init(), al volver a foreground y al conceder el consentimiento.
|
|
723
|
+
*/
|
|
724
|
+
private openSession;
|
|
616
725
|
/** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */
|
|
617
726
|
previewAnalytics(): AnalyticsEnvelope;
|
|
618
727
|
/**
|
|
@@ -622,6 +731,7 @@ declare class DeepdotsPopups {
|
|
|
622
731
|
*/
|
|
623
732
|
flushAnalytics(options?: {
|
|
624
733
|
final?: boolean;
|
|
734
|
+
sessionEnd?: boolean;
|
|
625
735
|
}): void;
|
|
626
736
|
/** Emite un evento `user_engagement` con el tiempo activo acumulado (#8). */
|
|
627
737
|
private flushEngagement;
|
|
@@ -737,7 +847,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
|
|
|
737
847
|
private partialEmitted;
|
|
738
848
|
constructor(options?: ReactNativeRendererOptions);
|
|
739
849
|
init(): void;
|
|
740
|
-
show(surveyId: string, productId: string,
|
|
850
|
+
show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
|
|
741
851
|
hide(): void;
|
|
742
852
|
/**
|
|
743
853
|
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
|
@@ -748,10 +858,13 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
|
|
|
748
858
|
}
|
|
749
859
|
|
|
750
860
|
/**
|
|
751
|
-
* Construye un HTML autocontenido que renderiza el survey de
|
|
752
|
-
* dentro de un WebView (React Native).
|
|
753
|
-
*
|
|
754
|
-
*
|
|
861
|
+
* Construye un HTML autocontenido que renderiza el popup (chrome + survey de
|
|
862
|
+
* `@magicfeedback/native`) dentro de un WebView (React Native). Es el equivalente RN de
|
|
863
|
+
* `renderPopup.ts` (DOM real, solo navegador): mismo header con botón cerrar, spinner,
|
|
864
|
+
* footer con back/start/send, gate "Start survey" y colores/logo desde `formData.style`.
|
|
865
|
+
* Carga el bundle del survey desde CDN, crea el form con la identidad inyectada
|
|
866
|
+
* (`profile`/`metadata`) y reenvía los eventos al host vía el puente de
|
|
867
|
+
* react-native-webview: `window.ReactNativeWebView.postMessage`.
|
|
755
868
|
*
|
|
756
869
|
* Mensajes emitidos (JSON `{name, payload}`): `loaded`, `before_submit`, `after_submit`,
|
|
757
870
|
* `survey_completed`, `back`, `popup_close`.
|
|
@@ -764,6 +877,9 @@ interface BuildSurveyHtmlOptions {
|
|
|
764
877
|
metadata?: IdentityAnswer[];
|
|
765
878
|
version?: string;
|
|
766
879
|
font?: PopupFont;
|
|
880
|
+
theme?: 'light' | 'dark';
|
|
881
|
+
position?: 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';
|
|
882
|
+
actions?: PopupActions;
|
|
767
883
|
}
|
|
768
884
|
declare function buildSurveyHtml(opts: BuildSurveyHtmlOptions): string;
|
|
769
885
|
|
|
@@ -815,4 +931,4 @@ declare function collectRnDevice(d: DeviceInfoLike): DeviceInfo;
|
|
|
815
931
|
*/
|
|
816
932
|
declare function setupReactNative(sdk: DeepdotsPopups, config: DeepdotsInitParams, deps?: ReactNativeSetupDeps): () => void;
|
|
817
933
|
|
|
818
|
-
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, type DeepdotsInitParams, type DeepdotsLogger, 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 };
|
|
934
|
+
export { type AnalyticsFeedbackBody, type AnalyticsKeys, type AppStateLike, BrowserPopupRenderer, type BuildBodyOptions, type ContactAttributeValue, type ContactAttributes, type ContactBody, ContactManager, type CrashRecord, CrashReporter, type CrashSeverity, type DeepdotsConfig, type DeepdotsEvent, type DeepdotsEventType, type DeepdotsInitParams, type DeepdotsLogger, DeepdotsPopups, type DeviceInfo, type DeviceInfoLike, type DeviceSnapshot, type EventListener, type FeedbackKV, InMemoryStorage, type KeyValueStorage, MAX_TRACKED_MESSAGES, MESSAGE_CHANNELS, type MessageChannel, MessageGuard, type MessageGuardVerdict, type MessageRejectionReason, 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 SessionEndReason, type TrackMessageOptions, type TriggerConfig, buildAnalyticsFeedbackBody, buildMessageParams, buildSurveyHtml, collectRnDevice, crashRecordToParams, createDefaultRenderer, createFeedbackSink, mmkvStorage, setupReactNative };
|