@magicfeedback/popup-sdk 1.1.1 → 1.1.2
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/{chunk-5VVCJBND.mjs → chunk-6WTOQGET.mjs} +3 -2
- package/dist/chunk-6WTOQGET.mjs.map +1 -0
- package/dist/{chunk-M6PEFLLQ.mjs → chunk-IOAZQPZE.mjs} +3 -2
- package/dist/chunk-IOAZQPZE.mjs.map +1 -0
- package/dist/index.d.mts +32 -15
- package/dist/index.d.ts +32 -15
- package/dist/index.js +113 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +112 -29
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +26 -10
- package/dist/react-native.d.ts +26 -10
- package/dist/react-native.js +113 -29
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +112 -29
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-7GGKR5BY.mjs → renderPopup-KBDWWSUU.mjs} +4 -4
- package/dist/renderPopup-KBDWWSUU.mjs.map +1 -0
- package/dist/{renderPopup-Y4WG3575.mjs → renderPopup-YLFRGPQN.mjs} +4 -4
- package/dist/renderPopup-YLFRGPQN.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-5VVCJBND.mjs.map +0 -1
- package/dist/chunk-M6PEFLLQ.mjs.map +0 -1
- package/dist/renderPopup-7GGKR5BY.mjs.map +0 -1
- package/dist/renderPopup-Y4WG3575.mjs.map +0 -1
package/dist/react-native.d.mts
CHANGED
|
@@ -21,10 +21,11 @@ interface KeyValueStorage {
|
|
|
21
21
|
/**
|
|
22
22
|
* Device info para el context de analytics (Technology #11–13).
|
|
23
23
|
*
|
|
24
|
-
* En Web enviamos `device_type`
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* En Web enviamos `device_type` + `user_agent` crudo y dejamos que el backend
|
|
25
|
+
* derive OS/navegador del UA (como hace GA). Los campos adicionales (timezone,
|
|
26
|
+
* referrer, viewport, etc.) se recogen de las APIs del navegador sin permisos.
|
|
27
|
+
* `country`/`city` se rellenan de forma asíncrona (ver geo-info.ts).
|
|
28
|
+
* En KMP las APIs nativas dan os_version/device_model reales.
|
|
28
29
|
*/
|
|
29
30
|
interface DeviceInfo {
|
|
30
31
|
device_type: 'mobile' | 'tablet' | 'desktop';
|
|
@@ -32,6 +33,16 @@ interface DeviceInfo {
|
|
|
32
33
|
device_model?: string;
|
|
33
34
|
app_version?: string;
|
|
34
35
|
user_agent?: string;
|
|
36
|
+
timezone?: string;
|
|
37
|
+
referrer?: string;
|
|
38
|
+
viewport_size?: string;
|
|
39
|
+
screen_resolution?: string;
|
|
40
|
+
pixel_ratio?: number;
|
|
41
|
+
entry_type?: string;
|
|
42
|
+
page_load_ms?: number;
|
|
43
|
+
connection_type?: string;
|
|
44
|
+
country?: string;
|
|
45
|
+
city?: string;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
/**
|
|
@@ -76,11 +87,12 @@ interface AnalyticsEnvelope {
|
|
|
76
87
|
* INTEGRACIÓN creada en la plataforma. Se manda en streaming con `completed:false` y
|
|
77
88
|
* `finished:false` (nunca se "cierra"); el backend cose por `sessionId` + `user_id`.
|
|
78
89
|
*
|
|
79
|
-
* Encoding
|
|
80
|
-
* -
|
|
90
|
+
* Encoding:
|
|
91
|
+
* - todo va en `feedback.metadata`: contexto (user_id, session_id, platform…) + eventos
|
|
92
|
+
* - cada entrada usa `value: string[]` (array de un elemento)
|
|
93
|
+
* - eventos: {key: nombre_evento, value: [JSON(timestamp + params)]}
|
|
81
94
|
* - identidad (user_id) → `profile` como `external-user-id`
|
|
82
|
-
* -
|
|
83
|
-
* - `answers` vacío
|
|
95
|
+
* - `answers` vacío, `metrics` no se usa
|
|
84
96
|
* - `text` vacío
|
|
85
97
|
*/
|
|
86
98
|
|
|
@@ -195,7 +207,7 @@ interface PopupRenderer {
|
|
|
195
207
|
/** Preparar recursos si aplica */
|
|
196
208
|
init?(): void;
|
|
197
209
|
/** Mostrar popup */
|
|
198
|
-
show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string): void;
|
|
210
|
+
show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
|
|
199
211
|
/** Ocultar popup */
|
|
200
212
|
hide(): void;
|
|
201
213
|
}
|
|
@@ -228,6 +240,10 @@ declare class DeepdotsPopups {
|
|
|
228
240
|
private tracking;
|
|
229
241
|
/** Capa de analytics (canal separado del feedback). Null hasta init(). */
|
|
230
242
|
private analytics;
|
|
243
|
+
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
244
|
+
private analyticsFeedbackSessionId;
|
|
245
|
+
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
246
|
+
private analyticsFlushTimer;
|
|
231
247
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
232
248
|
private navObserver;
|
|
233
249
|
/** Tiempo activo (engagement time, #8). */
|
|
@@ -437,7 +453,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
|
|
|
437
453
|
private partialEmitted;
|
|
438
454
|
constructor(options?: ReactNativeRendererOptions);
|
|
439
455
|
init(): void;
|
|
440
|
-
show(surveyId: string, productId: string, _actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, _style?: PopupStyle, sessionId?: string, miniService?: string): void;
|
|
456
|
+
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;
|
|
441
457
|
hide(): void;
|
|
442
458
|
/**
|
|
443
459
|
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
package/dist/react-native.d.ts
CHANGED
|
@@ -21,10 +21,11 @@ interface KeyValueStorage {
|
|
|
21
21
|
/**
|
|
22
22
|
* Device info para el context de analytics (Technology #11–13).
|
|
23
23
|
*
|
|
24
|
-
* En Web enviamos `device_type`
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* En Web enviamos `device_type` + `user_agent` crudo y dejamos que el backend
|
|
25
|
+
* derive OS/navegador del UA (como hace GA). Los campos adicionales (timezone,
|
|
26
|
+
* referrer, viewport, etc.) se recogen de las APIs del navegador sin permisos.
|
|
27
|
+
* `country`/`city` se rellenan de forma asíncrona (ver geo-info.ts).
|
|
28
|
+
* En KMP las APIs nativas dan os_version/device_model reales.
|
|
28
29
|
*/
|
|
29
30
|
interface DeviceInfo {
|
|
30
31
|
device_type: 'mobile' | 'tablet' | 'desktop';
|
|
@@ -32,6 +33,16 @@ interface DeviceInfo {
|
|
|
32
33
|
device_model?: string;
|
|
33
34
|
app_version?: string;
|
|
34
35
|
user_agent?: string;
|
|
36
|
+
timezone?: string;
|
|
37
|
+
referrer?: string;
|
|
38
|
+
viewport_size?: string;
|
|
39
|
+
screen_resolution?: string;
|
|
40
|
+
pixel_ratio?: number;
|
|
41
|
+
entry_type?: string;
|
|
42
|
+
page_load_ms?: number;
|
|
43
|
+
connection_type?: string;
|
|
44
|
+
country?: string;
|
|
45
|
+
city?: string;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
/**
|
|
@@ -76,11 +87,12 @@ interface AnalyticsEnvelope {
|
|
|
76
87
|
* INTEGRACIÓN creada en la plataforma. Se manda en streaming con `completed:false` y
|
|
77
88
|
* `finished:false` (nunca se "cierra"); el backend cose por `sessionId` + `user_id`.
|
|
78
89
|
*
|
|
79
|
-
* Encoding
|
|
80
|
-
* -
|
|
90
|
+
* Encoding:
|
|
91
|
+
* - todo va en `feedback.metadata`: contexto (user_id, session_id, platform…) + eventos
|
|
92
|
+
* - cada entrada usa `value: string[]` (array de un elemento)
|
|
93
|
+
* - eventos: {key: nombre_evento, value: [JSON(timestamp + params)]}
|
|
81
94
|
* - identidad (user_id) → `profile` como `external-user-id`
|
|
82
|
-
* -
|
|
83
|
-
* - `answers` vacío
|
|
95
|
+
* - `answers` vacío, `metrics` no se usa
|
|
84
96
|
* - `text` vacío
|
|
85
97
|
*/
|
|
86
98
|
|
|
@@ -195,7 +207,7 @@ interface PopupRenderer {
|
|
|
195
207
|
/** Preparar recursos si aplica */
|
|
196
208
|
init?(): void;
|
|
197
209
|
/** Mostrar popup */
|
|
198
|
-
show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string): void;
|
|
210
|
+
show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
|
|
199
211
|
/** Ocultar popup */
|
|
200
212
|
hide(): void;
|
|
201
213
|
}
|
|
@@ -228,6 +240,10 @@ declare class DeepdotsPopups {
|
|
|
228
240
|
private tracking;
|
|
229
241
|
/** Capa de analytics (canal separado del feedback). Null hasta init(). */
|
|
230
242
|
private analytics;
|
|
243
|
+
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
244
|
+
private analyticsFeedbackSessionId;
|
|
245
|
+
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
246
|
+
private analyticsFlushTimer;
|
|
231
247
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
232
248
|
private navObserver;
|
|
233
249
|
/** Tiempo activo (engagement time, #8). */
|
|
@@ -437,7 +453,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
|
|
|
437
453
|
private partialEmitted;
|
|
438
454
|
constructor(options?: ReactNativeRendererOptions);
|
|
439
455
|
init(): void;
|
|
440
|
-
show(surveyId: string, productId: string, _actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, _style?: PopupStyle, sessionId?: string, miniService?: string): void;
|
|
456
|
+
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;
|
|
441
457
|
hide(): void;
|
|
442
458
|
/**
|
|
443
459
|
* El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
|
package/dist/react-native.js
CHANGED
|
@@ -48,12 +48,13 @@ function createDefaultStorage() {
|
|
|
48
48
|
}
|
|
49
49
|
return new InMemoryStorage();
|
|
50
50
|
}
|
|
51
|
-
function buildSurveyIdentity(userId, sessionId, miniService = null) {
|
|
51
|
+
function buildSurveyIdentity(userId, sessionId, miniService = null, analyticsFeedbackSessionId = null) {
|
|
52
52
|
const profile = userId ? [{ key: "external-user-id", value: [userId] }] : [];
|
|
53
53
|
const metadata = [];
|
|
54
54
|
if (sessionId) metadata.push({ key: "session_id", value: [sessionId] });
|
|
55
55
|
if (userId) metadata.push({ key: "user_id", value: [userId] });
|
|
56
56
|
if (miniService) metadata.push({ key: "mini_service", value: [miniService] });
|
|
57
|
+
if (analyticsFeedbackSessionId) metadata.push({ key: "deepdots_analytics_feedback_session_id", value: [analyticsFeedbackSessionId] });
|
|
57
58
|
return { profile, metadata };
|
|
58
59
|
}
|
|
59
60
|
function defaultUuid() {
|
|
@@ -1063,7 +1064,7 @@ function ensureResponsiveStyles(_popup) {
|
|
|
1063
1064
|
`;
|
|
1064
1065
|
document.head.appendChild(style);
|
|
1065
1066
|
}
|
|
1066
|
-
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService) {
|
|
1067
|
+
async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
1067
1068
|
let surveyCompletedEmitted = false;
|
|
1068
1069
|
let stylesInjected = false;
|
|
1069
1070
|
const isDark = style?.theme === "dark";
|
|
@@ -1374,7 +1375,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
1374
1375
|
env: env === "production" ? "prod" : "dev"
|
|
1375
1376
|
}
|
|
1376
1377
|
);
|
|
1377
|
-
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null);
|
|
1378
|
+
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
|
|
1378
1379
|
formInstance = import_native.default.form(surveyId, productId, profile, metadata);
|
|
1379
1380
|
const generateOptions = {
|
|
1380
1381
|
addButton: false,
|
|
@@ -1787,12 +1788,12 @@ var ReactNativePopupRenderer = class {
|
|
|
1787
1788
|
}
|
|
1788
1789
|
init() {
|
|
1789
1790
|
}
|
|
1790
|
-
show(surveyId, productId, _actions, emit, onClose, env = "production", userId, _style, sessionId, miniService) {
|
|
1791
|
+
show(surveyId, productId, _actions, emit, onClose, env = "production", userId, _style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
1791
1792
|
this.emitFn = emit;
|
|
1792
1793
|
this.onCloseFn = onClose;
|
|
1793
1794
|
this.currentSurveyId = surveyId;
|
|
1794
1795
|
this.partialEmitted = false;
|
|
1795
|
-
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null);
|
|
1796
|
+
const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
|
|
1796
1797
|
const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata });
|
|
1797
1798
|
if (this.options.onShow) {
|
|
1798
1799
|
this.options.onShow({ surveyId, productId, html });
|
|
@@ -1883,14 +1884,14 @@ var BrowserPopupRenderer = class {
|
|
|
1883
1884
|
document.body.appendChild(this.container);
|
|
1884
1885
|
}
|
|
1885
1886
|
}
|
|
1886
|
-
show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService) {
|
|
1887
|
+
show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
|
|
1887
1888
|
if (this.visible) return;
|
|
1888
1889
|
if (!this.container || !document.body.contains(this.container)) this.init();
|
|
1889
1890
|
if (!this.container) return;
|
|
1890
1891
|
this.visible = true;
|
|
1891
1892
|
const container = this.container;
|
|
1892
1893
|
void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
|
|
1893
|
-
renderPopup2(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService);
|
|
1894
|
+
renderPopup2(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId);
|
|
1894
1895
|
});
|
|
1895
1896
|
}
|
|
1896
1897
|
hide() {
|
|
@@ -1932,6 +1933,12 @@ var AnalyticsManager = class {
|
|
|
1932
1933
|
this.language = options.language;
|
|
1933
1934
|
this.device = options.device;
|
|
1934
1935
|
this.platform = options.platform ?? "web";
|
|
1936
|
+
this.maxBatchSize = options.maxBatchSize ?? 20;
|
|
1937
|
+
this.onFlushNeeded = options.onFlushNeeded;
|
|
1938
|
+
}
|
|
1939
|
+
/** Actualiza campos de device en runtime (p.ej. country/city tras resolver geo). */
|
|
1940
|
+
updateDevice(partial) {
|
|
1941
|
+
this.device = { ...this.device, ...partial };
|
|
1935
1942
|
}
|
|
1936
1943
|
/** Mezcla user attributes (se coercionan a string). Mutable en runtime. */
|
|
1937
1944
|
setUserAttributes(attrs) {
|
|
@@ -1965,6 +1972,9 @@ var AnalyticsManager = class {
|
|
|
1965
1972
|
timestamp: this.now(),
|
|
1966
1973
|
params: Object.keys(merged).length ? merged : void 0
|
|
1967
1974
|
});
|
|
1975
|
+
if (this.events.length >= this.maxBatchSize) {
|
|
1976
|
+
this.onFlushNeeded?.();
|
|
1977
|
+
}
|
|
1968
1978
|
}
|
|
1969
1979
|
/** Mini-service activo (para inyectarlo en la metadata del survey, #33). */
|
|
1970
1980
|
getMiniService() {
|
|
@@ -2002,44 +2012,55 @@ var AnalyticsManager = class {
|
|
|
2002
2012
|
// src/analytics/feedback-payload.ts
|
|
2003
2013
|
function pushKV(list, key, value) {
|
|
2004
2014
|
if (value === void 0 || value === null || value === "") return;
|
|
2005
|
-
list.push({ key, value: String(value) });
|
|
2015
|
+
list.push({ key, value: [String(value)] });
|
|
2006
2016
|
}
|
|
2007
2017
|
function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
2008
2018
|
const { context } = envelope;
|
|
2009
|
-
const metrics = envelope.events.map((e) => ({
|
|
2010
|
-
key: e.name,
|
|
2011
|
-
value: JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })
|
|
2012
|
-
}));
|
|
2013
2019
|
const profile = [];
|
|
2014
2020
|
pushKV(profile, "external-user-id", envelope.userId);
|
|
2015
2021
|
const metadata = [];
|
|
2016
|
-
pushKV(metadata, "
|
|
2017
|
-
pushKV(metadata, "
|
|
2018
|
-
pushKV(metadata, "
|
|
2019
|
-
pushKV(metadata, "
|
|
2022
|
+
pushKV(metadata, "deepdots_user_id", envelope.userId);
|
|
2023
|
+
pushKV(metadata, "deepdots_session_id", envelope.sessionId);
|
|
2024
|
+
pushKV(metadata, "deepdots_platform", context.platform);
|
|
2025
|
+
pushKV(metadata, "deepdots_language", context.language);
|
|
2020
2026
|
if (context.device) {
|
|
2021
|
-
|
|
2022
|
-
pushKV(metadata, "
|
|
2023
|
-
pushKV(metadata, "
|
|
2024
|
-
pushKV(metadata, "
|
|
2025
|
-
pushKV(metadata, "
|
|
2027
|
+
const d = context.device;
|
|
2028
|
+
pushKV(metadata, "deepdots_device_type", d.device_type);
|
|
2029
|
+
pushKV(metadata, "deepdots_os_version", d.os_version);
|
|
2030
|
+
pushKV(metadata, "deepdots_device_model", d.device_model);
|
|
2031
|
+
pushKV(metadata, "deepdots_app_version", d.app_version);
|
|
2032
|
+
pushKV(metadata, "deepdots_user_agent", d.user_agent);
|
|
2033
|
+
pushKV(metadata, "deepdots_timezone", d.timezone);
|
|
2034
|
+
pushKV(metadata, "deepdots_referrer", d.referrer);
|
|
2035
|
+
pushKV(metadata, "deepdots_viewport_size", d.viewport_size);
|
|
2036
|
+
pushKV(metadata, "deepdots_screen_resolution", d.screen_resolution);
|
|
2037
|
+
pushKV(metadata, "deepdots_pixel_ratio", d.pixel_ratio);
|
|
2038
|
+
pushKV(metadata, "deepdots_entry_type", d.entry_type);
|
|
2039
|
+
pushKV(metadata, "deepdots_page_load_ms", d.page_load_ms);
|
|
2040
|
+
pushKV(metadata, "deepdots_connection_type", d.connection_type);
|
|
2041
|
+
pushKV(metadata, "deepdots_country", d.country);
|
|
2042
|
+
pushKV(metadata, "deepdots_city", d.city);
|
|
2026
2043
|
}
|
|
2027
2044
|
for (const [k, v] of Object.entries(context.attributes ?? {})) {
|
|
2028
2045
|
pushKV(metadata, k, v);
|
|
2029
2046
|
}
|
|
2047
|
+
for (const e of envelope.events) {
|
|
2048
|
+
metadata.push({
|
|
2049
|
+
key: e.name,
|
|
2050
|
+
value: [JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })]
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2030
2053
|
return {
|
|
2031
2054
|
feedback: {
|
|
2032
2055
|
text: "",
|
|
2033
2056
|
answers: [],
|
|
2034
2057
|
metadata,
|
|
2035
|
-
metrics,
|
|
2036
2058
|
profile,
|
|
2037
2059
|
finished: false
|
|
2038
2060
|
},
|
|
2039
2061
|
publicKey: keys.publicKey,
|
|
2040
2062
|
integration: keys.integration,
|
|
2041
2063
|
completed: false,
|
|
2042
|
-
// finished: false,
|
|
2043
2064
|
...feedbackSessionId ? { sessionId: feedbackSessionId } : {}
|
|
2044
2065
|
};
|
|
2045
2066
|
}
|
|
@@ -2064,6 +2085,7 @@ function createFeedbackSink(options) {
|
|
|
2064
2085
|
if (data?.sessionId && data.sessionId !== feedbackSessionId) {
|
|
2065
2086
|
feedbackSessionId = data.sessionId;
|
|
2066
2087
|
options.log?.("[DeepdotsAnalytics] feedbackSessionId cacheado:", feedbackSessionId);
|
|
2088
|
+
options.onSessionId?.(feedbackSessionId);
|
|
2067
2089
|
}
|
|
2068
2090
|
} catch {
|
|
2069
2091
|
}
|
|
@@ -2177,15 +2199,57 @@ function parseDeviceType(ua) {
|
|
|
2177
2199
|
}
|
|
2178
2200
|
return "desktop";
|
|
2179
2201
|
}
|
|
2202
|
+
function getEntryType() {
|
|
2203
|
+
if (typeof performance === "undefined") return void 0;
|
|
2204
|
+
const entries = performance.getEntriesByType("navigation");
|
|
2205
|
+
if (!entries.length) return void 0;
|
|
2206
|
+
return entries[0].type;
|
|
2207
|
+
}
|
|
2208
|
+
function getPageLoadMs() {
|
|
2209
|
+
if (typeof performance === "undefined") return void 0;
|
|
2210
|
+
const entries = performance.getEntriesByType("navigation");
|
|
2211
|
+
if (!entries.length) return void 0;
|
|
2212
|
+
const load = entries[0].loadEventEnd;
|
|
2213
|
+
return load > 0 ? Math.round(load) : void 0;
|
|
2214
|
+
}
|
|
2215
|
+
function getConnectionType() {
|
|
2216
|
+
if (typeof navigator === "undefined") return void 0;
|
|
2217
|
+
return navigator.connection?.effectiveType ?? void 0;
|
|
2218
|
+
}
|
|
2180
2219
|
function collectDeviceInfo(appVersion) {
|
|
2181
2220
|
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
2182
2221
|
return {
|
|
2183
2222
|
device_type: parseDeviceType(ua),
|
|
2184
2223
|
user_agent: ua || void 0,
|
|
2185
|
-
app_version: appVersion
|
|
2224
|
+
app_version: appVersion,
|
|
2225
|
+
timezone: typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : void 0,
|
|
2226
|
+
referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0,
|
|
2227
|
+
viewport_size: typeof window !== "undefined" ? `${window.innerWidth}x${window.innerHeight}` : void 0,
|
|
2228
|
+
screen_resolution: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0,
|
|
2229
|
+
pixel_ratio: typeof window !== "undefined" ? window.devicePixelRatio : void 0,
|
|
2230
|
+
entry_type: getEntryType(),
|
|
2231
|
+
page_load_ms: getPageLoadMs(),
|
|
2232
|
+
connection_type: getConnectionType()
|
|
2186
2233
|
};
|
|
2187
2234
|
}
|
|
2188
2235
|
|
|
2236
|
+
// src/analytics/geo-info.ts
|
|
2237
|
+
async function collectGeoInfo(fetchImpl) {
|
|
2238
|
+
const f = fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
2239
|
+
if (!f) return null;
|
|
2240
|
+
try {
|
|
2241
|
+
const res = await f("https://ipapi.co/json/");
|
|
2242
|
+
if (!res.ok) return null;
|
|
2243
|
+
const data = await res.json();
|
|
2244
|
+
const country = data.country_code || void 0;
|
|
2245
|
+
const city = data.city || void 0;
|
|
2246
|
+
if (!country && !city) return null;
|
|
2247
|
+
return { country, city };
|
|
2248
|
+
} catch {
|
|
2249
|
+
return null;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2189
2253
|
// src/analytics/engagement-tracker.ts
|
|
2190
2254
|
var EngagementTracker = class {
|
|
2191
2255
|
constructor(now = () => Date.now()) {
|
|
@@ -2257,6 +2321,8 @@ var ContactManager = class {
|
|
|
2257
2321
|
// src/core/deepdots-popups.ts
|
|
2258
2322
|
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
2259
2323
|
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
2324
|
+
var ANALYTICS_MAX_BATCH_SIZE = 20;
|
|
2325
|
+
var ANALYTICS_FLUSH_INTERVAL_MS = 3e4;
|
|
2260
2326
|
var DeepdotsPopups = class {
|
|
2261
2327
|
constructor() {
|
|
2262
2328
|
this.config = null;
|
|
@@ -2282,6 +2348,10 @@ var DeepdotsPopups = class {
|
|
|
2282
2348
|
this.tracking = null;
|
|
2283
2349
|
/** Capa de analytics (canal separado del feedback). Null hasta init(). */
|
|
2284
2350
|
this.analytics = null;
|
|
2351
|
+
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
2352
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
2353
|
+
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
2354
|
+
this.analyticsFlushTimer = void 0;
|
|
2285
2355
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
2286
2356
|
this.navObserver = null;
|
|
2287
2357
|
/** Tiempo activo (engagement time, #8). */
|
|
@@ -2328,14 +2398,23 @@ var DeepdotsPopups = class {
|
|
|
2328
2398
|
const analyticsSink = config.analytics ? createFeedbackSink({
|
|
2329
2399
|
baseUrl: this.baseUrl,
|
|
2330
2400
|
keys: config.analytics,
|
|
2331
|
-
log: (...a) => this.log(...a)
|
|
2401
|
+
log: (...a) => this.log(...a),
|
|
2402
|
+
onSessionId: (id) => {
|
|
2403
|
+
this.analyticsFeedbackSessionId = id;
|
|
2404
|
+
}
|
|
2332
2405
|
}) : void 0;
|
|
2333
2406
|
this.analytics = new AnalyticsManager({
|
|
2334
2407
|
sink: analyticsSink,
|
|
2335
2408
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
2336
2409
|
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
2337
2410
|
platform: config.platform ?? "web",
|
|
2338
|
-
device: config.device ?? collectDeviceInfo(config.appVersion)
|
|
2411
|
+
device: config.device ?? collectDeviceInfo(config.appVersion),
|
|
2412
|
+
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
2413
|
+
onFlushNeeded: () => this.flushAnalytics()
|
|
2414
|
+
});
|
|
2415
|
+
collectGeoInfo().then((geo) => {
|
|
2416
|
+
if (geo) this.analytics?.updateDevice(geo);
|
|
2417
|
+
}).catch(() => {
|
|
2339
2418
|
});
|
|
2340
2419
|
this.navObserver = new NavigationObserver();
|
|
2341
2420
|
this.navObserver.onVisit((v) => this.track("page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
@@ -2476,7 +2555,9 @@ var DeepdotsPopups = class {
|
|
|
2476
2555
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2477
2556
|
setupAnalyticsFlush() {
|
|
2478
2557
|
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2558
|
+
this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
2479
2559
|
window.addEventListener("pagehide", () => {
|
|
2560
|
+
clearInterval(this.analyticsFlushTimer);
|
|
2480
2561
|
this.navObserver?.stop();
|
|
2481
2562
|
this.exitMiniService();
|
|
2482
2563
|
this.flushEngagement();
|
|
@@ -2920,7 +3001,8 @@ var DeepdotsPopups = class {
|
|
|
2920
3001
|
const userId = this.tracking?.getUserId() ?? this.config?.userId;
|
|
2921
3002
|
const sessionId = this.tracking?.getSessionId() ?? void 0;
|
|
2922
3003
|
const miniService = this.analytics?.getMiniService() ?? void 0;
|
|
2923
|
-
|
|
3004
|
+
const analyticsFeedbackSessionId = this.analyticsFeedbackSessionId;
|
|
3005
|
+
this.log("tracking \xB7 survey identity \u2192", { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null, analyticsFeedbackSessionId: analyticsFeedbackSessionId ?? null });
|
|
2924
3006
|
if (this.renderer) {
|
|
2925
3007
|
this.renderer.show(
|
|
2926
3008
|
surveyId,
|
|
@@ -2932,7 +3014,8 @@ var DeepdotsPopups = class {
|
|
|
2932
3014
|
userId,
|
|
2933
3015
|
style,
|
|
2934
3016
|
sessionId,
|
|
2935
|
-
miniService
|
|
3017
|
+
miniService,
|
|
3018
|
+
analyticsFeedbackSessionId
|
|
2936
3019
|
);
|
|
2937
3020
|
return;
|
|
2938
3021
|
}
|
|
@@ -2950,7 +3033,8 @@ var DeepdotsPopups = class {
|
|
|
2950
3033
|
userId,
|
|
2951
3034
|
style,
|
|
2952
3035
|
sessionId,
|
|
2953
|
-
miniService
|
|
3036
|
+
miniService,
|
|
3037
|
+
analyticsFeedbackSessionId
|
|
2954
3038
|
);
|
|
2955
3039
|
});
|
|
2956
3040
|
}
|