@magicfeedback/popup-sdk 1.8.2 → 1.8.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/README.md +34 -2
- package/dist/index.d.mts +48 -2
- package/dist/index.d.ts +48 -2
- package/dist/index.js +132 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +133 -21
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +39 -1
- package/dist/react-native.d.ts +39 -1
- package/dist/react-native.js +130 -18
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +130 -18
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-ERUKQE4U.mjs → renderPopup-3CZ6WTXF.mjs} +3 -3
- package/dist/renderPopup-3CZ6WTXF.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/renderPopup-ERUKQE4U.mjs.map +0 -1
package/dist/react-native.mjs
CHANGED
|
@@ -968,10 +968,13 @@ function createDefaultRenderer() {
|
|
|
968
968
|
|
|
969
969
|
// src/analytics/analytics-manager.ts
|
|
970
970
|
function createDryRunSink(log = console.log) {
|
|
971
|
-
return (payload) =>
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
971
|
+
return (payload, meta) => {
|
|
972
|
+
if (meta?.resetSession) return;
|
|
973
|
+
log(
|
|
974
|
+
"[DeepdotsAnalytics] (dry-run \xB7 NOT sent \xB7 no init.analytics) POST /sdk/feedback \u2192",
|
|
975
|
+
JSON.stringify(payload, null, 2)
|
|
976
|
+
);
|
|
977
|
+
};
|
|
975
978
|
}
|
|
976
979
|
var dryRunSink = createDryRunSink();
|
|
977
980
|
var AnalyticsManager = class {
|
|
@@ -1038,6 +1041,19 @@ var AnalyticsManager = class {
|
|
|
1038
1041
|
this.exitMiniService(name);
|
|
1039
1042
|
}
|
|
1040
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Olvida la sesión en curso SIN emitir nada: vacía el buffer y los mini-services activos
|
|
1046
|
+
* (sin `mini_service_exit`). Para una sesión que el backend ya cerró por inactividad: lo
|
|
1047
|
+
* que quedara pendiente pertenecía a ella y no debe llegar con la sesión nueva.
|
|
1048
|
+
*/
|
|
1049
|
+
discardSession(identity) {
|
|
1050
|
+
this.events = [];
|
|
1051
|
+
this.activeMiniServices.clear();
|
|
1052
|
+
try {
|
|
1053
|
+
void this.sink({ ...this.buildPayload(identity), events: [] }, { resetSession: true });
|
|
1054
|
+
} catch {
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1041
1057
|
/**
|
|
1042
1058
|
* Olvida lo que pertenecía al usuario anterior (user attributes + métricas) al cambiar de
|
|
1043
1059
|
* usuario. NO toca el buffer de eventos: el cierre de sesión ya lo vació con la identidad
|
|
@@ -1247,6 +1263,12 @@ function createFeedbackSink(options) {
|
|
|
1247
1263
|
}
|
|
1248
1264
|
};
|
|
1249
1265
|
return async (envelope, meta) => {
|
|
1266
|
+
if (meta?.resetSession) {
|
|
1267
|
+
feedbackSessionId = void 0;
|
|
1268
|
+
firstPostInFlight = null;
|
|
1269
|
+
options.onSessionReset?.();
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1250
1272
|
const final = meta?.final === true;
|
|
1251
1273
|
const sessionEnd = meta?.sessionEnd === true;
|
|
1252
1274
|
if (!feedbackSessionId && firstPostInFlight && !final) {
|
|
@@ -1313,6 +1335,14 @@ var NavigationObserver = class {
|
|
|
1313
1335
|
this.closeCurrent();
|
|
1314
1336
|
this.currentScreen = null;
|
|
1315
1337
|
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Olvida la pantalla actual SIN emitir su visita. Para una sesión que el backend ya dio
|
|
1340
|
+
* por cerrada: su visita no tiene dónde ir, y mandarla con la sesión nueva la contaría
|
|
1341
|
+
* donde no ocurrió. Después, `restart()` vuelve a empezar en la pantalla actual.
|
|
1342
|
+
*/
|
|
1343
|
+
discard() {
|
|
1344
|
+
this.currentScreen = null;
|
|
1345
|
+
}
|
|
1316
1346
|
/**
|
|
1317
1347
|
* Retoma el seguimiento en la pantalla actual tras un `stop()`, cuando se abre una sesión
|
|
1318
1348
|
* nueva sin navegar (cambio de usuario, vuelta de la bfcache). No-op si nunca se instaló
|
|
@@ -1842,6 +1872,10 @@ var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
|
1842
1872
|
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
1843
1873
|
var ANALYTICS_MAX_BATCH_SIZE = 20;
|
|
1844
1874
|
var ANALYTICS_FLUSH_INTERVAL_MS = 3e4;
|
|
1875
|
+
var ANALYTICS_HEARTBEAT_MS = 5 * 6e4;
|
|
1876
|
+
var ANALYTICS_SESSION_TIMEOUT_MS = 30 * 6e4;
|
|
1877
|
+
var ANALYTICS_SERVER_IDLE_MS = 60 * 6e4;
|
|
1878
|
+
var ANALYTICS_SESSION_DISCARD_MS = ANALYTICS_SERVER_IDLE_MS - 10 * 6e4;
|
|
1845
1879
|
var RESERVED_EVENT_PREFIX = "deepdots_";
|
|
1846
1880
|
var CUSTOM_EVENT_PREFIX = "deepdots_event_";
|
|
1847
1881
|
var DeepdotsPopups = class {
|
|
@@ -1905,11 +1939,21 @@ var DeepdotsPopups = class {
|
|
|
1905
1939
|
* nueva al volver a foreground o al conceder el consentimiento más tarde.
|
|
1906
1940
|
*/
|
|
1907
1941
|
this.sessionOpen = false;
|
|
1942
|
+
/** Último `user_engagement` emitido (ms epoch): marca el latido. */
|
|
1943
|
+
this.lastEngagementAt = 0;
|
|
1944
|
+
/** Cuándo se ocultó la pestaña por última vez (ms epoch); null si está visible. */
|
|
1945
|
+
this.hiddenAt = null;
|
|
1908
1946
|
/**
|
|
1909
1947
|
* Idioma resuelto en init() (explícito > navigator.language > Intl). Única fuente de
|
|
1910
1948
|
* verdad para el context de analytics Y para la segmentación por idioma de los popups.
|
|
1911
1949
|
*/
|
|
1912
1950
|
this.language = void 0;
|
|
1951
|
+
/** `geolocation` del init: si es false no se usa ni la caché ni el lookup. */
|
|
1952
|
+
this.geoEnabled = true;
|
|
1953
|
+
/** Solo se consulta a terceros si el país/ciudad se va a enviar (analytics configurado). */
|
|
1954
|
+
this.geoWanted = false;
|
|
1955
|
+
/** Evita más de un lookup por instancia (el consentimiento puede ir y venir). */
|
|
1956
|
+
this.geoLookupStarted = false;
|
|
1913
1957
|
}
|
|
1914
1958
|
/** Initialize the SDK with configuration */
|
|
1915
1959
|
init(config) {
|
|
@@ -1967,7 +2011,9 @@ var DeepdotsPopups = class {
|
|
|
1967
2011
|
onSessionReset: () => {
|
|
1968
2012
|
this.analyticsFeedbackSessionId = void 0;
|
|
1969
2013
|
}
|
|
1970
|
-
}) : createDryRunSink((...a) =>
|
|
2014
|
+
}) : createDryRunSink((...a) => {
|
|
2015
|
+
if (this.config?.debug) this.logger.log(...a);
|
|
2016
|
+
});
|
|
1971
2017
|
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
1972
2018
|
this.language = resolveLanguage({
|
|
1973
2019
|
explicit: config.language,
|
|
@@ -1995,15 +2041,9 @@ var DeepdotsPopups = class {
|
|
|
1995
2041
|
if (this.tracking?.isTrackingEnabled()) {
|
|
1996
2042
|
this.crashReporter.install();
|
|
1997
2043
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
if (geo) {
|
|
2002
|
-
this.analytics?.updateDevice(geo);
|
|
2003
|
-
writeCachedGeo(storage, geo, Date.now());
|
|
2004
|
-
}
|
|
2005
|
-
}).catch(() => {
|
|
2006
|
-
});
|
|
2044
|
+
this.geoEnabled = config.geolocation ?? true;
|
|
2045
|
+
this.geoWanted = this.geoEnabled && !!config.analytics;
|
|
2046
|
+
this.resolveGeo();
|
|
2007
2047
|
this.navObserver = new NavigationObserver();
|
|
2008
2048
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
2009
2049
|
this.navObserver.install();
|
|
@@ -2045,6 +2085,7 @@ var DeepdotsPopups = class {
|
|
|
2045
2085
|
if (!enabled) this.closeSession("tracking_disabled");
|
|
2046
2086
|
this.tracking?.setTrackingEnabled(enabled);
|
|
2047
2087
|
if (enabled) this.openSession();
|
|
2088
|
+
if (enabled) this.resolveGeo();
|
|
2048
2089
|
this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
|
|
2049
2090
|
}
|
|
2050
2091
|
// ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────
|
|
@@ -2241,6 +2282,7 @@ var DeepdotsPopups = class {
|
|
|
2241
2282
|
if (this.sessionOpen) return;
|
|
2242
2283
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2243
2284
|
this.sessionOpen = true;
|
|
2285
|
+
this.lastEngagementAt = Date.now();
|
|
2244
2286
|
this.track("deepdots_session_start", {});
|
|
2245
2287
|
this.navObserver?.restart();
|
|
2246
2288
|
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
@@ -2271,7 +2313,76 @@ var DeepdotsPopups = class {
|
|
|
2271
2313
|
flushEngagement() {
|
|
2272
2314
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2273
2315
|
const ms = this.engagement?.consume() ?? 0;
|
|
2274
|
-
if (ms > 0)
|
|
2316
|
+
if (ms > 0) {
|
|
2317
|
+
this.track("deepdots_user_engagement", { engagement_time_msec: ms });
|
|
2318
|
+
this.lastEngagementAt = Date.now();
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
/** Tick del flush periódico: latido de engagement si toca, y envío de lo acumulado. */
|
|
2322
|
+
onAnalyticsFlushTick() {
|
|
2323
|
+
if (this.sessionOpen && this.isDocumentVisible() && Date.now() - this.lastEngagementAt >= ANALYTICS_HEARTBEAT_MS) {
|
|
2324
|
+
this.flushEngagement();
|
|
2325
|
+
}
|
|
2326
|
+
this.flushAnalytics();
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* La pestaña vuelve a verse. Tras más de ANALYTICS_SESSION_TIMEOUT_MS oculta, la sesión
|
|
2330
|
+
* anterior termina y empieza otra: seguir con el mismo sessionId mezclaba dos visitas y,
|
|
2331
|
+
* si el backend ya la había cerrado por inactividad, la partía en dos feedbacks.
|
|
2332
|
+
* - Oculta menos de ANALYTICS_SESSION_DISCARD_MS: el backend aún la tiene abierta → se
|
|
2333
|
+
* cierra con normalidad (page_view de la pantalla, session_end `idle_timeout`, completed).
|
|
2334
|
+
* - Más: ya la cerró el backend → se descarta sin enviar nada.
|
|
2335
|
+
*/
|
|
2336
|
+
onDocumentVisible() {
|
|
2337
|
+
const hiddenFor = this.hiddenAt === null ? 0 : Date.now() - this.hiddenAt;
|
|
2338
|
+
this.hiddenAt = null;
|
|
2339
|
+
if (!this.sessionOpen) return;
|
|
2340
|
+
if (hiddenFor < ANALYTICS_SESSION_TIMEOUT_MS) {
|
|
2341
|
+
this.engagement?.resume();
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
if (hiddenFor < ANALYTICS_SESSION_DISCARD_MS) {
|
|
2345
|
+
this.closeSession("idle_timeout");
|
|
2346
|
+
} else {
|
|
2347
|
+
this.discardSession();
|
|
2348
|
+
}
|
|
2349
|
+
this.openSession();
|
|
2350
|
+
}
|
|
2351
|
+
/** Olvida la sesión abierta sin enviar nada (ver onDocumentVisible). */
|
|
2352
|
+
discardSession() {
|
|
2353
|
+
this.sessionOpen = false;
|
|
2354
|
+
this.navObserver?.discard();
|
|
2355
|
+
this.analytics?.discardSession(this.analyticsIdentity());
|
|
2356
|
+
this.engagement?.consume();
|
|
2357
|
+
this.engagement?.pause();
|
|
2358
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
2359
|
+
this.tracking?.setSessionId(null);
|
|
2360
|
+
this.log("tracking \xB7 session discarded after", ANALYTICS_SESSION_DISCARD_MS / 6e4, "min hidden");
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* Geolocalización por IP (país/ciudad para analytics). La caché persistente es local y se
|
|
2364
|
+
* aplica siempre que `geolocation` no esté desactivado. El lookup a terceros (ipapi.co →
|
|
2365
|
+
* ipwho.is → ipinfo.io, fallback con timeout) solo se hace si el dato se va a ENVIAR:
|
|
2366
|
+
* analytics configurado + tracking activo + caché ausente o caducada. Una vez por instancia.
|
|
2367
|
+
*/
|
|
2368
|
+
resolveGeo() {
|
|
2369
|
+
const storage = this.storage;
|
|
2370
|
+
if (!this.geoEnabled || !storage || !this.analytics) return;
|
|
2371
|
+
const cachedGeo = readCachedGeo(storage, Date.now());
|
|
2372
|
+
if (cachedGeo) {
|
|
2373
|
+
this.analytics.updateDevice(cachedGeo);
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
if (!this.geoWanted || this.geoLookupStarted) return;
|
|
2377
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2378
|
+
this.geoLookupStarted = true;
|
|
2379
|
+
fetchGeo().then((geo) => {
|
|
2380
|
+
if (geo) {
|
|
2381
|
+
this.analytics?.updateDevice(geo);
|
|
2382
|
+
writeCachedGeo(storage, geo, Date.now());
|
|
2383
|
+
}
|
|
2384
|
+
}).catch(() => {
|
|
2385
|
+
});
|
|
2275
2386
|
}
|
|
2276
2387
|
analyticsIdentity() {
|
|
2277
2388
|
return {
|
|
@@ -2281,8 +2392,8 @@ var DeepdotsPopups = class {
|
|
|
2281
2392
|
}
|
|
2282
2393
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2283
2394
|
setupAnalyticsFlush() {
|
|
2284
|
-
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2285
2395
|
this.startAnalyticsFlushTimer();
|
|
2396
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2286
2397
|
window.addEventListener("pagehide", () => {
|
|
2287
2398
|
clearInterval(this.analyticsFlushTimer);
|
|
2288
2399
|
this.closeSession("page_hide");
|
|
@@ -2297,14 +2408,15 @@ var DeepdotsPopups = class {
|
|
|
2297
2408
|
this.flushEngagement();
|
|
2298
2409
|
this.engagement?.pause();
|
|
2299
2410
|
this.flushAnalytics();
|
|
2411
|
+
this.hiddenAt = Date.now();
|
|
2300
2412
|
} else if (document.visibilityState === "visible") {
|
|
2301
|
-
|
|
2413
|
+
this.onDocumentVisible();
|
|
2302
2414
|
}
|
|
2303
2415
|
});
|
|
2304
2416
|
}
|
|
2305
2417
|
startAnalyticsFlushTimer() {
|
|
2306
2418
|
clearInterval(this.analyticsFlushTimer);
|
|
2307
|
-
this.analyticsFlushTimer = setInterval(() => this.
|
|
2419
|
+
this.analyticsFlushTimer = setInterval(() => this.onAnalyticsFlushTick(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
2308
2420
|
}
|
|
2309
2421
|
/** Enable auto-launch functionality with configured triggers */
|
|
2310
2422
|
autoLaunch() {
|