@magicfeedback/popup-sdk 1.8.2 → 1.8.3
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 +32 -1
- package/dist/index.d.mts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +129 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +130 -20
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +37 -0
- package/dist/react-native.d.ts +37 -0
- package/dist/react-native.js +127 -17
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +127 -17
- 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) {
|
|
@@ -1995,15 +2039,9 @@ var DeepdotsPopups = class {
|
|
|
1995
2039
|
if (this.tracking?.isTrackingEnabled()) {
|
|
1996
2040
|
this.crashReporter.install();
|
|
1997
2041
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
if (geo) {
|
|
2002
|
-
this.analytics?.updateDevice(geo);
|
|
2003
|
-
writeCachedGeo(storage, geo, Date.now());
|
|
2004
|
-
}
|
|
2005
|
-
}).catch(() => {
|
|
2006
|
-
});
|
|
2042
|
+
this.geoEnabled = config.geolocation ?? true;
|
|
2043
|
+
this.geoWanted = this.geoEnabled && !!config.analytics;
|
|
2044
|
+
this.resolveGeo();
|
|
2007
2045
|
this.navObserver = new NavigationObserver();
|
|
2008
2046
|
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
2009
2047
|
this.navObserver.install();
|
|
@@ -2045,6 +2083,7 @@ var DeepdotsPopups = class {
|
|
|
2045
2083
|
if (!enabled) this.closeSession("tracking_disabled");
|
|
2046
2084
|
this.tracking?.setTrackingEnabled(enabled);
|
|
2047
2085
|
if (enabled) this.openSession();
|
|
2086
|
+
if (enabled) this.resolveGeo();
|
|
2048
2087
|
this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
|
|
2049
2088
|
}
|
|
2050
2089
|
// ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────
|
|
@@ -2241,6 +2280,7 @@ var DeepdotsPopups = class {
|
|
|
2241
2280
|
if (this.sessionOpen) return;
|
|
2242
2281
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2243
2282
|
this.sessionOpen = true;
|
|
2283
|
+
this.lastEngagementAt = Date.now();
|
|
2244
2284
|
this.track("deepdots_session_start", {});
|
|
2245
2285
|
this.navObserver?.restart();
|
|
2246
2286
|
if (this.isDocumentVisible()) this.engagement?.resume();
|
|
@@ -2271,7 +2311,76 @@ var DeepdotsPopups = class {
|
|
|
2271
2311
|
flushEngagement() {
|
|
2272
2312
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2273
2313
|
const ms = this.engagement?.consume() ?? 0;
|
|
2274
|
-
if (ms > 0)
|
|
2314
|
+
if (ms > 0) {
|
|
2315
|
+
this.track("deepdots_user_engagement", { engagement_time_msec: ms });
|
|
2316
|
+
this.lastEngagementAt = Date.now();
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
/** Tick del flush periódico: latido de engagement si toca, y envío de lo acumulado. */
|
|
2320
|
+
onAnalyticsFlushTick() {
|
|
2321
|
+
if (this.sessionOpen && this.isDocumentVisible() && Date.now() - this.lastEngagementAt >= ANALYTICS_HEARTBEAT_MS) {
|
|
2322
|
+
this.flushEngagement();
|
|
2323
|
+
}
|
|
2324
|
+
this.flushAnalytics();
|
|
2325
|
+
}
|
|
2326
|
+
/**
|
|
2327
|
+
* La pestaña vuelve a verse. Tras más de ANALYTICS_SESSION_TIMEOUT_MS oculta, la sesión
|
|
2328
|
+
* anterior termina y empieza otra: seguir con el mismo sessionId mezclaba dos visitas y,
|
|
2329
|
+
* si el backend ya la había cerrado por inactividad, la partía en dos feedbacks.
|
|
2330
|
+
* - Oculta menos de ANALYTICS_SESSION_DISCARD_MS: el backend aún la tiene abierta → se
|
|
2331
|
+
* cierra con normalidad (page_view de la pantalla, session_end `idle_timeout`, completed).
|
|
2332
|
+
* - Más: ya la cerró el backend → se descarta sin enviar nada.
|
|
2333
|
+
*/
|
|
2334
|
+
onDocumentVisible() {
|
|
2335
|
+
const hiddenFor = this.hiddenAt === null ? 0 : Date.now() - this.hiddenAt;
|
|
2336
|
+
this.hiddenAt = null;
|
|
2337
|
+
if (!this.sessionOpen) return;
|
|
2338
|
+
if (hiddenFor < ANALYTICS_SESSION_TIMEOUT_MS) {
|
|
2339
|
+
this.engagement?.resume();
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
if (hiddenFor < ANALYTICS_SESSION_DISCARD_MS) {
|
|
2343
|
+
this.closeSession("idle_timeout");
|
|
2344
|
+
} else {
|
|
2345
|
+
this.discardSession();
|
|
2346
|
+
}
|
|
2347
|
+
this.openSession();
|
|
2348
|
+
}
|
|
2349
|
+
/** Olvida la sesión abierta sin enviar nada (ver onDocumentVisible). */
|
|
2350
|
+
discardSession() {
|
|
2351
|
+
this.sessionOpen = false;
|
|
2352
|
+
this.navObserver?.discard();
|
|
2353
|
+
this.analytics?.discardSession(this.analyticsIdentity());
|
|
2354
|
+
this.engagement?.consume();
|
|
2355
|
+
this.engagement?.pause();
|
|
2356
|
+
this.analyticsFeedbackSessionId = void 0;
|
|
2357
|
+
this.tracking?.setSessionId(null);
|
|
2358
|
+
this.log("tracking \xB7 session discarded after", ANALYTICS_SESSION_DISCARD_MS / 6e4, "min hidden");
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* Geolocalización por IP (país/ciudad para analytics). La caché persistente es local y se
|
|
2362
|
+
* aplica siempre que `geolocation` no esté desactivado. El lookup a terceros (ipapi.co →
|
|
2363
|
+
* ipwho.is → ipinfo.io, fallback con timeout) solo se hace si el dato se va a ENVIAR:
|
|
2364
|
+
* analytics configurado + tracking activo + caché ausente o caducada. Una vez por instancia.
|
|
2365
|
+
*/
|
|
2366
|
+
resolveGeo() {
|
|
2367
|
+
const storage = this.storage;
|
|
2368
|
+
if (!this.geoEnabled || !storage || !this.analytics) return;
|
|
2369
|
+
const cachedGeo = readCachedGeo(storage, Date.now());
|
|
2370
|
+
if (cachedGeo) {
|
|
2371
|
+
this.analytics.updateDevice(cachedGeo);
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
if (!this.geoWanted || this.geoLookupStarted) return;
|
|
2375
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2376
|
+
this.geoLookupStarted = true;
|
|
2377
|
+
fetchGeo().then((geo) => {
|
|
2378
|
+
if (geo) {
|
|
2379
|
+
this.analytics?.updateDevice(geo);
|
|
2380
|
+
writeCachedGeo(storage, geo, Date.now());
|
|
2381
|
+
}
|
|
2382
|
+
}).catch(() => {
|
|
2383
|
+
});
|
|
2275
2384
|
}
|
|
2276
2385
|
analyticsIdentity() {
|
|
2277
2386
|
return {
|
|
@@ -2281,8 +2390,8 @@ var DeepdotsPopups = class {
|
|
|
2281
2390
|
}
|
|
2282
2391
|
/** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */
|
|
2283
2392
|
setupAnalyticsFlush() {
|
|
2284
|
-
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2285
2393
|
this.startAnalyticsFlushTimer();
|
|
2394
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
2286
2395
|
window.addEventListener("pagehide", () => {
|
|
2287
2396
|
clearInterval(this.analyticsFlushTimer);
|
|
2288
2397
|
this.closeSession("page_hide");
|
|
@@ -2297,14 +2406,15 @@ var DeepdotsPopups = class {
|
|
|
2297
2406
|
this.flushEngagement();
|
|
2298
2407
|
this.engagement?.pause();
|
|
2299
2408
|
this.flushAnalytics();
|
|
2409
|
+
this.hiddenAt = Date.now();
|
|
2300
2410
|
} else if (document.visibilityState === "visible") {
|
|
2301
|
-
|
|
2411
|
+
this.onDocumentVisible();
|
|
2302
2412
|
}
|
|
2303
2413
|
});
|
|
2304
2414
|
}
|
|
2305
2415
|
startAnalyticsFlushTimer() {
|
|
2306
2416
|
clearInterval(this.analyticsFlushTimer);
|
|
2307
|
-
this.analyticsFlushTimer = setInterval(() => this.
|
|
2417
|
+
this.analyticsFlushTimer = setInterval(() => this.onAnalyticsFlushTick(), ANALYTICS_FLUSH_INTERVAL_MS);
|
|
2308
2418
|
}
|
|
2309
2419
|
/** Enable auto-launch functionality with configured triggers */
|
|
2310
2420
|
autoLaunch() {
|