@magicfeedback/popup-sdk 1.1.6 → 1.1.8
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 +5 -0
- package/dist/index.d.mts +63 -8
- package/dist/index.d.ts +63 -8
- package/dist/index.js +244 -43
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +242 -41
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +30 -3
- package/dist/react-native.d.ts +30 -3
- package/dist/react-native.js +244 -43
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +242 -41
- package/dist/react-native.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -146,8 +146,8 @@ var init_tracking_manager = __esm({
|
|
|
146
146
|
|
|
147
147
|
// src/ui/font.ts
|
|
148
148
|
function fontFormatFromUrl(url) {
|
|
149
|
-
const
|
|
150
|
-
const ext =
|
|
149
|
+
const clean2 = url.split("?")[0].split("#")[0];
|
|
150
|
+
const ext = clean2.slice(clean2.lastIndexOf(".") + 1).toLowerCase();
|
|
151
151
|
return FORMAT_BY_EXT[ext];
|
|
152
152
|
}
|
|
153
153
|
function sanitizeFamily(family) {
|
|
@@ -3957,6 +3957,8 @@ var AnalyticsManager = class {
|
|
|
3957
3957
|
constructor(options = {}) {
|
|
3958
3958
|
this.events = [];
|
|
3959
3959
|
this.attributes = {};
|
|
3960
|
+
/** Métricas del host (valores medibles persistentes). Sobrescriben por key; se reenvían en cada flush. */
|
|
3961
|
+
this.metrics = {};
|
|
3960
3962
|
/** Mini-services activos: nombre → timestamp de entrada. El orden de inserción marca el "más reciente". */
|
|
3961
3963
|
this.activeMiniServices = /* @__PURE__ */ new Map();
|
|
3962
3964
|
this.sink = options.sink ?? dryRunSink;
|
|
@@ -3966,6 +3968,7 @@ var AnalyticsManager = class {
|
|
|
3966
3968
|
this.device = options.device;
|
|
3967
3969
|
this.platform = options.platform ?? "web";
|
|
3968
3970
|
this.maxBatchSize = options.maxBatchSize ?? 20;
|
|
3971
|
+
this.maxBufferedEvents = options.maxBufferedEvents ?? 200;
|
|
3969
3972
|
this.onFlushNeeded = options.onFlushNeeded;
|
|
3970
3973
|
}
|
|
3971
3974
|
/** Actualiza campos de device en runtime (p.ej. country/city tras resolver geo). */
|
|
@@ -3979,6 +3982,14 @@ var AnalyticsManager = class {
|
|
|
3979
3982
|
this.attributes[k] = String(v);
|
|
3980
3983
|
}
|
|
3981
3984
|
}
|
|
3985
|
+
/**
|
|
3986
|
+
* Registra/actualiza una métrica (valor medible). Persistente: se reenvía en cada flush.
|
|
3987
|
+
* Repetir la misma key SOBRESCRIBE; el valor se coerciona a string; ignora key vacía.
|
|
3988
|
+
*/
|
|
3989
|
+
setMetric(key, value) {
|
|
3990
|
+
if (!key) return;
|
|
3991
|
+
this.metrics[key] = String(value);
|
|
3992
|
+
}
|
|
3982
3993
|
/**
|
|
3983
3994
|
* Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`.
|
|
3984
3995
|
* Admite varios activos a la vez (concurrencia); el "actual" para etiquetar es el más reciente.
|
|
@@ -4018,6 +4029,7 @@ var AnalyticsManager = class {
|
|
|
4018
4029
|
timestamp: this.now(),
|
|
4019
4030
|
params: Object.keys(merged).length ? merged : void 0
|
|
4020
4031
|
});
|
|
4032
|
+
this.enforceBufferCap();
|
|
4021
4033
|
if (this.events.length >= this.maxBatchSize) {
|
|
4022
4034
|
this.onFlushNeeded?.();
|
|
4023
4035
|
}
|
|
@@ -4042,19 +4054,49 @@ var AnalyticsManager = class {
|
|
|
4042
4054
|
platform: this.platform,
|
|
4043
4055
|
language: this.language,
|
|
4044
4056
|
device: this.device,
|
|
4045
|
-
attributes: { ...this.attributes }
|
|
4057
|
+
attributes: { ...this.attributes },
|
|
4058
|
+
metrics: { ...this.metrics }
|
|
4046
4059
|
},
|
|
4047
4060
|
events: [...this.events]
|
|
4048
4061
|
};
|
|
4049
4062
|
}
|
|
4050
|
-
/**
|
|
4051
|
-
|
|
4063
|
+
/**
|
|
4064
|
+
* Envía (vía sink) el lote acumulado y vacía el buffer. No-op si no hay eventos.
|
|
4065
|
+
*
|
|
4066
|
+
* Si el sink falla de forma transitoria (promesa rechazada o excepción síncrona) el lote
|
|
4067
|
+
* se **re-encola** al principio del buffer y se reintenta en el siguiente flush: un error
|
|
4068
|
+
* de red o un 5xx no debe hacer desaparecer los eventos.
|
|
4069
|
+
*/
|
|
4070
|
+
flush(identity, meta) {
|
|
4052
4071
|
if (this.events.length === 0) return null;
|
|
4072
|
+
const batch = this.events;
|
|
4053
4073
|
const payload = this.buildPayload(identity);
|
|
4054
|
-
this.sink(payload);
|
|
4055
4074
|
this.events = [];
|
|
4075
|
+
let result;
|
|
4076
|
+
try {
|
|
4077
|
+
result = meta ? this.sink(payload, meta) : this.sink(payload);
|
|
4078
|
+
} catch {
|
|
4079
|
+
this.requeue(batch);
|
|
4080
|
+
return payload;
|
|
4081
|
+
}
|
|
4082
|
+
if (result && typeof result.then === "function") {
|
|
4083
|
+
result.catch(() => this.requeue(batch));
|
|
4084
|
+
}
|
|
4056
4085
|
return payload;
|
|
4057
4086
|
}
|
|
4087
|
+
/**
|
|
4088
|
+
* Devuelve un lote fallido al buffer, delante de lo que haya llegado mientras se enviaba
|
|
4089
|
+
* (orden cronológico). Con el buffer lleno se descartan los eventos más antiguos.
|
|
4090
|
+
*/
|
|
4091
|
+
requeue(batch) {
|
|
4092
|
+
this.events = [...batch, ...this.events];
|
|
4093
|
+
this.enforceBufferCap();
|
|
4094
|
+
}
|
|
4095
|
+
/** Techo del buffer: si el envío lleva rato fallando, se sacrifica lo más antiguo. */
|
|
4096
|
+
enforceBufferCap() {
|
|
4097
|
+
const overflow = this.events.length - this.maxBufferedEvents;
|
|
4098
|
+
if (overflow > 0) this.events.splice(0, overflow);
|
|
4099
|
+
}
|
|
4058
4100
|
};
|
|
4059
4101
|
|
|
4060
4102
|
// src/analytics/feedback-payload.ts
|
|
@@ -4098,10 +4140,15 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
|
4098
4140
|
value: [JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })]
|
|
4099
4141
|
});
|
|
4100
4142
|
}
|
|
4143
|
+
const metrics = [];
|
|
4144
|
+
for (const [k, v] of Object.entries(context.metrics ?? {})) {
|
|
4145
|
+
pushKV(metrics, k, v);
|
|
4146
|
+
}
|
|
4101
4147
|
return {
|
|
4102
4148
|
feedback: {
|
|
4103
4149
|
text: "",
|
|
4104
4150
|
answers: [],
|
|
4151
|
+
metrics,
|
|
4105
4152
|
metadata,
|
|
4106
4153
|
profile,
|
|
4107
4154
|
finished: false
|
|
@@ -4112,33 +4159,80 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
|
4112
4159
|
...feedbackSessionId ? { sessionId: feedbackSessionId } : {}
|
|
4113
4160
|
};
|
|
4114
4161
|
}
|
|
4162
|
+
var KEEPALIVE_MAX_BYTES = 6e4;
|
|
4163
|
+
function isRetryableStatus(status) {
|
|
4164
|
+
return status >= 500 || status === 408 || status === 429;
|
|
4165
|
+
}
|
|
4166
|
+
async function safeBodyText(res) {
|
|
4167
|
+
try {
|
|
4168
|
+
return (await res.text()).slice(0, 500);
|
|
4169
|
+
} catch {
|
|
4170
|
+
return "";
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4115
4173
|
function createFeedbackSink(options) {
|
|
4116
4174
|
let feedbackSessionId;
|
|
4117
|
-
|
|
4118
|
-
|
|
4175
|
+
let firstPostInFlight = null;
|
|
4176
|
+
const url = `${options.baseUrl}/sdk/feedback`;
|
|
4177
|
+
const post = async (body, final) => {
|
|
4178
|
+
const json = JSON.stringify(body);
|
|
4179
|
+
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
4180
|
+
if (final && options.sendBeaconImpl && small) {
|
|
4181
|
+
const payload = typeof Blob !== "undefined" ? new Blob([json], { type: "application/json" }) : json;
|
|
4182
|
+
if (options.sendBeaconImpl(url, payload)) {
|
|
4183
|
+
options.log?.("[DeepdotsAnalytics] flush final v\xEDa sendBeacon");
|
|
4184
|
+
return;
|
|
4185
|
+
}
|
|
4186
|
+
options.log?.("[DeepdotsAnalytics] sendBeacon rechaz\xF3 el lote; fallback a fetch");
|
|
4187
|
+
}
|
|
4119
4188
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
4120
4189
|
if (!f) {
|
|
4121
4190
|
options.log?.("[DeepdotsAnalytics] no fetch disponible; payload no enviado", body);
|
|
4122
4191
|
return;
|
|
4123
4192
|
}
|
|
4124
|
-
|
|
4125
|
-
f(`${options.baseUrl}/sdk/feedback`, {
|
|
4193
|
+
const res = await f(url, {
|
|
4126
4194
|
method: "POST",
|
|
4127
4195
|
headers: { "Content-Type": "application/json" },
|
|
4128
|
-
body:
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
options.onSessionId?.(feedbackSessionId);
|
|
4137
|
-
}
|
|
4138
|
-
} catch {
|
|
4139
|
-
}
|
|
4196
|
+
body: json,
|
|
4197
|
+
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
4198
|
+
...small ? { keepalive: true } : {}
|
|
4199
|
+
});
|
|
4200
|
+
if (!res.ok) {
|
|
4201
|
+
const detail = await safeBodyText(res);
|
|
4202
|
+
if (isRetryableStatus(res.status)) {
|
|
4203
|
+
throw new Error(`POST /sdk/feedback ${res.status} (reintentable): ${detail}`);
|
|
4140
4204
|
}
|
|
4141
|
-
|
|
4205
|
+
options.log?.(
|
|
4206
|
+
`[DeepdotsAnalytics] POST /sdk/feedback rechazado con ${res.status}; lote DESCARTADO:`,
|
|
4207
|
+
detail
|
|
4208
|
+
);
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
try {
|
|
4212
|
+
const data = await res.json();
|
|
4213
|
+
if (data?.sessionId && data.sessionId !== feedbackSessionId) {
|
|
4214
|
+
feedbackSessionId = data.sessionId;
|
|
4215
|
+
options.log?.("[DeepdotsAnalytics] feedbackSessionId cacheado:", feedbackSessionId);
|
|
4216
|
+
options.onSessionId?.(feedbackSessionId);
|
|
4217
|
+
}
|
|
4218
|
+
} catch {
|
|
4219
|
+
}
|
|
4220
|
+
};
|
|
4221
|
+
return async (envelope, meta) => {
|
|
4222
|
+
const final = meta?.final === true;
|
|
4223
|
+
if (!feedbackSessionId && firstPostInFlight && !final) {
|
|
4224
|
+
await firstPostInFlight;
|
|
4225
|
+
}
|
|
4226
|
+
const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId);
|
|
4227
|
+
options.log?.("[DeepdotsAnalytics] POST /sdk/feedback \u2192", body);
|
|
4228
|
+
const sent = post(body, final);
|
|
4229
|
+
if (!feedbackSessionId) {
|
|
4230
|
+
firstPostInFlight = sent.then(
|
|
4231
|
+
() => void 0,
|
|
4232
|
+
() => void 0
|
|
4233
|
+
);
|
|
4234
|
+
}
|
|
4235
|
+
await sent;
|
|
4142
4236
|
};
|
|
4143
4237
|
}
|
|
4144
4238
|
|
|
@@ -4282,21 +4376,96 @@ function collectDeviceInfo(appVersion) {
|
|
|
4282
4376
|
}
|
|
4283
4377
|
|
|
4284
4378
|
// src/analytics/geo-info.ts
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4379
|
+
var GEO_STORAGE_KEY = "deepdots.geo";
|
|
4380
|
+
var GEO_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4381
|
+
var GEO_TIMEOUT_MS = 3e3;
|
|
4382
|
+
function readCachedGeo(storage, now) {
|
|
4383
|
+
try {
|
|
4384
|
+
const raw = storage.getItem(GEO_STORAGE_KEY);
|
|
4385
|
+
if (!raw) return null;
|
|
4386
|
+
const cached = JSON.parse(raw);
|
|
4387
|
+
if (typeof cached.ts !== "number" || now - cached.ts > GEO_TTL_MS) return null;
|
|
4388
|
+
const geo = {};
|
|
4389
|
+
if (cached.country) geo.country = cached.country;
|
|
4390
|
+
if (cached.city) geo.city = cached.city;
|
|
4391
|
+
return geo.country || geo.city ? geo : null;
|
|
4392
|
+
} catch {
|
|
4393
|
+
return null;
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
function writeCachedGeo(storage, geo, now) {
|
|
4397
|
+
try {
|
|
4398
|
+
const payload = { ...geo, ts: now };
|
|
4399
|
+
storage.setItem(GEO_STORAGE_KEY, JSON.stringify(payload));
|
|
4400
|
+
} catch {
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
var DEFAULT_GEO_PROVIDERS = [
|
|
4404
|
+
{
|
|
4405
|
+
// https://ipapi.co/json/
|
|
4406
|
+
url: "https://ipapi.co/json/",
|
|
4407
|
+
parse: (d) => geoFrom(d.country_code, d.city)
|
|
4408
|
+
},
|
|
4409
|
+
{
|
|
4410
|
+
// https://ipwho.is/ (devuelve success:false con 200 en errores)
|
|
4411
|
+
url: "https://ipwho.is/",
|
|
4412
|
+
parse: (d) => d.success === false ? null : geoFrom(d.country_code, d.city)
|
|
4413
|
+
},
|
|
4414
|
+
{
|
|
4415
|
+
// https://ipinfo.io/json (country = código ISO)
|
|
4416
|
+
url: "https://ipinfo.io/json",
|
|
4417
|
+
parse: (d) => geoFrom(d.country, d.city)
|
|
4418
|
+
}
|
|
4419
|
+
];
|
|
4420
|
+
function geoFrom(country, city) {
|
|
4421
|
+
const c = typeof country === "string" && country ? country : void 0;
|
|
4422
|
+
const t = typeof city === "string" && city ? city : void 0;
|
|
4423
|
+
if (!c && !t) return null;
|
|
4424
|
+
return { country: c, city: t };
|
|
4425
|
+
}
|
|
4426
|
+
async function fetchOne(provider, f, timeoutMs) {
|
|
4427
|
+
const controller = new AbortController();
|
|
4428
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
4288
4429
|
try {
|
|
4289
|
-
const res = await f(
|
|
4430
|
+
const res = await f(provider.url, { signal: controller.signal });
|
|
4290
4431
|
if (!res.ok) return null;
|
|
4291
4432
|
const data = await res.json();
|
|
4292
|
-
|
|
4293
|
-
const city = data.city || void 0;
|
|
4294
|
-
if (!country && !city) return null;
|
|
4295
|
-
return { country, city };
|
|
4433
|
+
return provider.parse(data);
|
|
4296
4434
|
} catch {
|
|
4297
4435
|
return null;
|
|
4436
|
+
} finally {
|
|
4437
|
+
clearTimeout(timer);
|
|
4298
4438
|
}
|
|
4299
4439
|
}
|
|
4440
|
+
async function fetchGeo(options = {}) {
|
|
4441
|
+
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
4442
|
+
if (!f) return null;
|
|
4443
|
+
const providers = options.providers ?? DEFAULT_GEO_PROVIDERS;
|
|
4444
|
+
const timeoutMs = options.timeoutMs ?? GEO_TIMEOUT_MS;
|
|
4445
|
+
for (const provider of providers) {
|
|
4446
|
+
const geo = await fetchOne(provider, f, timeoutMs);
|
|
4447
|
+
if (geo) return geo;
|
|
4448
|
+
}
|
|
4449
|
+
return null;
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4452
|
+
// src/analytics/language.ts
|
|
4453
|
+
function clean(value) {
|
|
4454
|
+
const trimmed = value?.trim();
|
|
4455
|
+
return trimmed ? trimmed : void 0;
|
|
4456
|
+
}
|
|
4457
|
+
function resolveLanguage(sources) {
|
|
4458
|
+
const explicit = clean(sources.explicit);
|
|
4459
|
+
if (explicit) return explicit;
|
|
4460
|
+
const navLang = clean(sources.navigator?.language);
|
|
4461
|
+
if (navLang) return navLang;
|
|
4462
|
+
try {
|
|
4463
|
+
const locale = clean(sources.intl?.DateTimeFormat().resolvedOptions().locale);
|
|
4464
|
+
if (locale) return locale;
|
|
4465
|
+
} catch {
|
|
4466
|
+
}
|
|
4467
|
+
return void 0;
|
|
4468
|
+
}
|
|
4300
4469
|
|
|
4301
4470
|
// src/analytics/engagement-tracker.ts
|
|
4302
4471
|
var EngagementTracker = class {
|
|
@@ -4557,6 +4726,11 @@ var DeepdotsPopups = class {
|
|
|
4557
4726
|
this.contact = null;
|
|
4558
4727
|
/** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */
|
|
4559
4728
|
this.navStarted = false;
|
|
4729
|
+
/**
|
|
4730
|
+
* Idioma resuelto en init() (explícito > navigator.language > Intl). Única fuente de
|
|
4731
|
+
* verdad para el context de analytics Y para la segmentación por idioma de los popups.
|
|
4732
|
+
*/
|
|
4733
|
+
this.language = void 0;
|
|
4560
4734
|
}
|
|
4561
4735
|
/** Initialize the SDK with configuration */
|
|
4562
4736
|
init(config) {
|
|
@@ -4598,15 +4772,22 @@ var DeepdotsPopups = class {
|
|
|
4598
4772
|
baseUrl: this.baseUrl,
|
|
4599
4773
|
keys: config.analytics,
|
|
4600
4774
|
log: (...a) => this.log(...a),
|
|
4775
|
+
// sendBeacon: único transporte que sobrevive al cierre de la página.
|
|
4776
|
+
sendBeaconImpl: typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function" ? (url, body) => navigator.sendBeacon(url, body) : void 0,
|
|
4601
4777
|
onSessionId: (id) => {
|
|
4602
4778
|
this.analyticsFeedbackSessionId = id;
|
|
4603
4779
|
}
|
|
4604
4780
|
}) : createDryRunSink((...a) => this.logger.log(...a));
|
|
4605
4781
|
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
4782
|
+
this.language = resolveLanguage({
|
|
4783
|
+
explicit: config.language,
|
|
4784
|
+
navigator: typeof navigator !== "undefined" ? navigator : void 0,
|
|
4785
|
+
intl: typeof Intl !== "undefined" ? Intl : void 0
|
|
4786
|
+
});
|
|
4606
4787
|
this.analytics = new AnalyticsManager({
|
|
4607
4788
|
sink: analyticsSink,
|
|
4608
4789
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
4609
|
-
language:
|
|
4790
|
+
language: this.language,
|
|
4610
4791
|
platform: config.platform ?? "web",
|
|
4611
4792
|
device,
|
|
4612
4793
|
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
@@ -4623,8 +4804,13 @@ var DeepdotsPopups = class {
|
|
|
4623
4804
|
if (this.tracking?.isTrackingEnabled()) {
|
|
4624
4805
|
this.crashReporter.install();
|
|
4625
4806
|
}
|
|
4626
|
-
|
|
4627
|
-
|
|
4807
|
+
const cachedGeo = readCachedGeo(storage, Date.now());
|
|
4808
|
+
if (cachedGeo) this.analytics.updateDevice(cachedGeo);
|
|
4809
|
+
fetchGeo().then((geo) => {
|
|
4810
|
+
if (geo) {
|
|
4811
|
+
this.analytics?.updateDevice(geo);
|
|
4812
|
+
writeCachedGeo(storage, geo, Date.now());
|
|
4813
|
+
}
|
|
4628
4814
|
}).catch(() => {
|
|
4629
4815
|
});
|
|
4630
4816
|
this.navObserver = new NavigationObserver();
|
|
@@ -4681,6 +4867,15 @@ var DeepdotsPopups = class {
|
|
|
4681
4867
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4682
4868
|
this.analytics?.setUserAttributes(attributes);
|
|
4683
4869
|
}
|
|
4870
|
+
/**
|
|
4871
|
+
* Registra/actualiza una métrica (valor medible) → `feedback.metrics` del body.
|
|
4872
|
+
* Persistente y sobrescribe por key. No-op si tracking off.
|
|
4873
|
+
*/
|
|
4874
|
+
setMetric(key, value) {
|
|
4875
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4876
|
+
this.analytics?.setMetric(key, value);
|
|
4877
|
+
this.log("analytics \xB7 setMetric:", key, value);
|
|
4878
|
+
}
|
|
4684
4879
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
4685
4880
|
reportError(error, options) {
|
|
4686
4881
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -4768,10 +4963,14 @@ var DeepdotsPopups = class {
|
|
|
4768
4963
|
events: []
|
|
4769
4964
|
};
|
|
4770
4965
|
}
|
|
4771
|
-
/**
|
|
4772
|
-
|
|
4966
|
+
/**
|
|
4967
|
+
* Envía el lote acumulado de analytics y vacía el buffer.
|
|
4968
|
+
* `final: true` (cierre de página/app) cambia el transporte a `sendBeacon`, que sobrevive
|
|
4969
|
+
* al unload; un lote que falle por red o 5xx se re-encola para el siguiente flush.
|
|
4970
|
+
*/
|
|
4971
|
+
flushAnalytics(options) {
|
|
4773
4972
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4774
|
-
this.analytics?.flush(this.analyticsIdentity());
|
|
4973
|
+
this.analytics?.flush(this.analyticsIdentity(), options);
|
|
4775
4974
|
}
|
|
4776
4975
|
/** Emite un evento `user_engagement` con el tiempo activo acumulado (#8). */
|
|
4777
4976
|
flushEngagement() {
|
|
@@ -4794,7 +4993,7 @@ var DeepdotsPopups = class {
|
|
|
4794
4993
|
this.navObserver?.stop();
|
|
4795
4994
|
if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
|
|
4796
4995
|
this.flushEngagement();
|
|
4797
|
-
this.flushAnalytics();
|
|
4996
|
+
this.flushAnalytics({ final: true });
|
|
4798
4997
|
});
|
|
4799
4998
|
document.addEventListener("visibilitychange", () => {
|
|
4800
4999
|
if (document.visibilityState === "hidden") {
|
|
@@ -4915,14 +5114,16 @@ var DeepdotsPopups = class {
|
|
|
4915
5114
|
matchesSegmentsLang(def) {
|
|
4916
5115
|
const langs = def.segments?.lang;
|
|
4917
5116
|
if (!langs || langs.length === 0) return true;
|
|
4918
|
-
const
|
|
4919
|
-
if (!
|
|
4920
|
-
this.debug("No
|
|
5117
|
+
const currentLang = (this.language ?? "").toLowerCase();
|
|
5118
|
+
if (!currentLang) {
|
|
5119
|
+
this.debug("No language available for lang comparison", { popupId: def.id, langs });
|
|
4921
5120
|
return true;
|
|
4922
5121
|
}
|
|
4923
|
-
const matches = langs.some(
|
|
5122
|
+
const matches = langs.some(
|
|
5123
|
+
(lang) => typeof lang === "string" && currentLang.startsWith(lang.toLowerCase())
|
|
5124
|
+
);
|
|
4924
5125
|
if (!matches) {
|
|
4925
|
-
this.debug("Lang mismatch for popup", { popupId: def.id, langs,
|
|
5126
|
+
this.debug("Lang mismatch for popup", { popupId: def.id, langs, currentLang });
|
|
4926
5127
|
}
|
|
4927
5128
|
return matches;
|
|
4928
5129
|
}
|