@magicfeedback/popup-sdk 1.1.2 → 1.1.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/dist/index.d.mts +116 -3
- package/dist/index.d.ts +116 -3
- package/dist/index.js +232 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -31
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +58 -2
- package/dist/react-native.d.ts +58 -2
- package/dist/react-native.js +229 -31
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +229 -31
- package/dist/react-native.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3458,14 +3458,17 @@ var index_exports = {};
|
|
|
3458
3458
|
__export(index_exports, {
|
|
3459
3459
|
BrowserPopupRenderer: () => BrowserPopupRenderer,
|
|
3460
3460
|
ContactManager: () => ContactManager,
|
|
3461
|
+
CrashReporter: () => CrashReporter,
|
|
3461
3462
|
DeepdotsPopups: () => DeepdotsPopups,
|
|
3462
3463
|
InMemoryStorage: () => InMemoryStorage,
|
|
3463
3464
|
NoopPopupRenderer: () => NoopPopupRenderer,
|
|
3464
3465
|
POPUP_TRIGGER_CONDITION_STATUSES: () => POPUP_TRIGGER_CONDITION_STATUSES,
|
|
3465
3466
|
ReactNativePopupRenderer: () => ReactNativePopupRenderer,
|
|
3466
3467
|
buildAnalyticsFeedbackBody: () => buildAnalyticsFeedbackBody,
|
|
3468
|
+
buildMessageParams: () => buildMessageParams,
|
|
3467
3469
|
buildSurveyHtml: () => buildSurveyHtml,
|
|
3468
3470
|
collectRnDevice: () => collectRnDevice,
|
|
3471
|
+
crashRecordToParams: () => crashRecordToParams,
|
|
3469
3472
|
createDefaultRenderer: () => createDefaultRenderer,
|
|
3470
3473
|
createFeedbackSink: () => createFeedbackSink,
|
|
3471
3474
|
mmkvStorage: () => mmkvStorage,
|
|
@@ -3860,8 +3863,8 @@ var AnalyticsManager = class {
|
|
|
3860
3863
|
constructor(options = {}) {
|
|
3861
3864
|
this.events = [];
|
|
3862
3865
|
this.attributes = {};
|
|
3863
|
-
|
|
3864
|
-
this.
|
|
3866
|
+
/** Mini-services activos: nombre → timestamp de entrada. El orden de inserción marca el "más reciente". */
|
|
3867
|
+
this.activeMiniServices = /* @__PURE__ */ new Map();
|
|
3865
3868
|
this.sink = options.sink ?? dryRunSink;
|
|
3866
3869
|
this.now = options.now ?? (() => Date.now());
|
|
3867
3870
|
this.publicKey = options.publicKey;
|
|
@@ -3882,24 +3885,38 @@ var AnalyticsManager = class {
|
|
|
3882
3885
|
this.attributes[k] = String(v);
|
|
3883
3886
|
}
|
|
3884
3887
|
}
|
|
3885
|
-
/**
|
|
3888
|
+
/**
|
|
3889
|
+
* Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`.
|
|
3890
|
+
* Admite varios activos a la vez (concurrencia); el "actual" para etiquetar es el más reciente.
|
|
3891
|
+
* Reentrar con un nombre ya activo refresca su orden y su tiempo de entrada.
|
|
3892
|
+
*/
|
|
3886
3893
|
enterMiniService(name, entryPointType) {
|
|
3887
|
-
this.
|
|
3888
|
-
this.
|
|
3889
|
-
this.track("
|
|
3890
|
-
}
|
|
3891
|
-
/**
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3894
|
+
this.activeMiniServices.delete(name);
|
|
3895
|
+
this.activeMiniServices.set(name, this.now());
|
|
3896
|
+
this.track("deepdots_mini_service_enter", { entry_point_type: entryPointType ?? null });
|
|
3897
|
+
}
|
|
3898
|
+
/**
|
|
3899
|
+
* Cierra el mini-service `name` emitiendo `mini_service_exit` con su duración (#27).
|
|
3900
|
+
* No-op si ese nombre no está activo. Cierre coherente con concurrencia.
|
|
3901
|
+
*/
|
|
3902
|
+
exitMiniService(name) {
|
|
3903
|
+
const enteredAt = this.activeMiniServices.get(name);
|
|
3904
|
+
if (enteredAt === void 0) return;
|
|
3905
|
+
const durationSeconds = Math.max(0, Math.round((this.now() - enteredAt) / 1e3));
|
|
3906
|
+
this.activeMiniServices.delete(name);
|
|
3907
|
+
this.track("deepdots_mini_service_exit", { mini_service: name, duration_seconds: durationSeconds });
|
|
3908
|
+
}
|
|
3909
|
+
/** Cierra TODOS los mini-services activos (orden LIFO). Para el cierre por lifecycle (background/cierre). */
|
|
3910
|
+
exitAllMiniServices() {
|
|
3911
|
+
for (const name of Array.from(this.activeMiniServices.keys()).reverse()) {
|
|
3912
|
+
this.exitMiniService(name);
|
|
3913
|
+
}
|
|
3898
3914
|
}
|
|
3899
3915
|
/** Registra un evento de analítica (modelo GA: nombre + params). */
|
|
3900
3916
|
track(name, params) {
|
|
3917
|
+
const current = this.getMiniService();
|
|
3901
3918
|
const merged = {
|
|
3902
|
-
...
|
|
3919
|
+
...current ? { mini_service: current } : {},
|
|
3903
3920
|
...params ?? {}
|
|
3904
3921
|
};
|
|
3905
3922
|
this.events.push({
|
|
@@ -3911,9 +3928,11 @@ var AnalyticsManager = class {
|
|
|
3911
3928
|
this.onFlushNeeded?.();
|
|
3912
3929
|
}
|
|
3913
3930
|
}
|
|
3914
|
-
/** Mini-service activo
|
|
3931
|
+
/** Mini-service actual (el más reciente aún activo) para etiquetar eventos + metadata del survey (#33). */
|
|
3915
3932
|
getMiniService() {
|
|
3916
|
-
|
|
3933
|
+
let last = null;
|
|
3934
|
+
for (const name of this.activeMiniServices.keys()) last = name;
|
|
3935
|
+
return last;
|
|
3917
3936
|
}
|
|
3918
3937
|
/** Nº de eventos pendientes de flush. */
|
|
3919
3938
|
pending() {
|
|
@@ -4253,11 +4272,153 @@ var ContactManager = class {
|
|
|
4253
4272
|
}
|
|
4254
4273
|
};
|
|
4255
4274
|
|
|
4275
|
+
// src/analytics/crash-reporter.ts
|
|
4276
|
+
var STACK_MAX = 8e3;
|
|
4277
|
+
var QUEUE_KEY = "deepdots.crash.queue";
|
|
4278
|
+
var MAX_QUEUED = 20;
|
|
4279
|
+
function crashRecordToParams(r) {
|
|
4280
|
+
const params = {
|
|
4281
|
+
crashed_at: r.crashedAt,
|
|
4282
|
+
crash_type: r.crashType,
|
|
4283
|
+
message: r.message,
|
|
4284
|
+
stack: r.stack,
|
|
4285
|
+
fatal: r.fatal,
|
|
4286
|
+
handled: r.handled,
|
|
4287
|
+
severity: r.severity
|
|
4288
|
+
};
|
|
4289
|
+
if (r.sessionId) params.crashed_session_id = r.sessionId;
|
|
4290
|
+
if (r.appVersion) params.crashed_app_version = r.appVersion;
|
|
4291
|
+
if (r.osVersion) params.crashed_os_version = r.osVersion;
|
|
4292
|
+
if (r.deviceModel) params.crashed_device_model = r.deviceModel;
|
|
4293
|
+
if (r.context) {
|
|
4294
|
+
for (const [k, v] of Object.entries(r.context)) params[`ctx_${k}`] = v;
|
|
4295
|
+
}
|
|
4296
|
+
return params;
|
|
4297
|
+
}
|
|
4298
|
+
var CrashReporter = class {
|
|
4299
|
+
constructor(options) {
|
|
4300
|
+
this.options = options;
|
|
4301
|
+
this.now = options.now ?? (() => Date.now());
|
|
4302
|
+
}
|
|
4303
|
+
isEnabled() {
|
|
4304
|
+
return this.options.enabled ? this.options.enabled() : true;
|
|
4305
|
+
}
|
|
4306
|
+
/** Construye un CrashRecord capturando contexto en el momento del crash. */
|
|
4307
|
+
buildRecord(error, severity, handled, fatal, context) {
|
|
4308
|
+
const err = error;
|
|
4309
|
+
const isErr = error instanceof Error;
|
|
4310
|
+
const dev = this.options.device();
|
|
4311
|
+
const ctx = context ? Object.fromEntries(Object.entries(context).map(([k, v]) => [k, String(v)])) : void 0;
|
|
4312
|
+
return {
|
|
4313
|
+
crashedAt: this.now(),
|
|
4314
|
+
crashType: isErr && err.name || "Error",
|
|
4315
|
+
message: isErr ? String(err.message ?? "") : String(error),
|
|
4316
|
+
stack: (isErr && typeof err.stack === "string" ? err.stack : "").slice(0, STACK_MAX),
|
|
4317
|
+
fatal,
|
|
4318
|
+
handled,
|
|
4319
|
+
severity,
|
|
4320
|
+
sessionId: this.options.sessionId(),
|
|
4321
|
+
appVersion: dev.appVersion,
|
|
4322
|
+
osVersion: dev.osVersion,
|
|
4323
|
+
deviceModel: dev.deviceModel,
|
|
4324
|
+
context: ctx
|
|
4325
|
+
};
|
|
4326
|
+
}
|
|
4327
|
+
/** API pública del host: reporta un error (app viva) → emite el evento ya. */
|
|
4328
|
+
reportError(error, options = {}) {
|
|
4329
|
+
if (!this.isEnabled()) return;
|
|
4330
|
+
const severity = options.severity ?? "error";
|
|
4331
|
+
const handled = options.handled ?? true;
|
|
4332
|
+
const fatal = severity === "fatal";
|
|
4333
|
+
const record = this.buildRecord(error, severity, handled, fatal, options.context);
|
|
4334
|
+
this.options.emit(crashRecordToParams(record));
|
|
4335
|
+
}
|
|
4336
|
+
/** Captura un error no manejado (fatal) → persiste a disco para replay en el siguiente arranque. */
|
|
4337
|
+
captureUnhandled(error) {
|
|
4338
|
+
this.persist(this.buildRecord(error, "fatal", false, true));
|
|
4339
|
+
}
|
|
4340
|
+
/** Persiste un crash no capturado a disco para reenviarlo en el siguiente arranque. */
|
|
4341
|
+
persist(record) {
|
|
4342
|
+
let queue = [];
|
|
4343
|
+
try {
|
|
4344
|
+
const raw = this.options.storage.getItem(QUEUE_KEY);
|
|
4345
|
+
if (raw) queue = JSON.parse(raw);
|
|
4346
|
+
if (!Array.isArray(queue)) queue = [];
|
|
4347
|
+
} catch {
|
|
4348
|
+
queue = [];
|
|
4349
|
+
}
|
|
4350
|
+
queue.push(record);
|
|
4351
|
+
if (queue.length > MAX_QUEUED) queue = queue.slice(queue.length - MAX_QUEUED);
|
|
4352
|
+
try {
|
|
4353
|
+
this.options.storage.setItem(QUEUE_KEY, JSON.stringify(queue));
|
|
4354
|
+
} catch {
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
/** Lee y vacía la cola de crashes pendientes (llamado en init para el replay). */
|
|
4358
|
+
drainPendingCrashes() {
|
|
4359
|
+
let queue = [];
|
|
4360
|
+
try {
|
|
4361
|
+
const raw = this.options.storage.getItem(QUEUE_KEY);
|
|
4362
|
+
if (raw) queue = JSON.parse(raw);
|
|
4363
|
+
if (!Array.isArray(queue)) queue = [];
|
|
4364
|
+
} catch {
|
|
4365
|
+
queue = [];
|
|
4366
|
+
}
|
|
4367
|
+
if (queue.length) {
|
|
4368
|
+
try {
|
|
4369
|
+
this.options.storage.removeItem(QUEUE_KEY);
|
|
4370
|
+
} catch {
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
return queue;
|
|
4374
|
+
}
|
|
4375
|
+
/** Instala los handlers globales de errores no capturados (no-op sin `window`). */
|
|
4376
|
+
install() {
|
|
4377
|
+
if (typeof window === "undefined") return;
|
|
4378
|
+
window.addEventListener("error", (e) => {
|
|
4379
|
+
if (!this.isEnabled()) return;
|
|
4380
|
+
this.captureUnhandled(e.error ?? e.message ?? "error");
|
|
4381
|
+
});
|
|
4382
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
4383
|
+
if (!this.isEnabled()) return;
|
|
4384
|
+
this.captureUnhandled(e.reason ?? "unhandledrejection");
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
/** Engancha `global.ErrorUtils` de RN (no hay `window`): persiste y encadena al handler previo. */
|
|
4388
|
+
installReactNative(errorUtils) {
|
|
4389
|
+
const previous = errorUtils.getGlobalHandler?.();
|
|
4390
|
+
errorUtils.setGlobalHandler((error, isFatal) => {
|
|
4391
|
+
if (this.isEnabled()) this.captureUnhandled(error);
|
|
4392
|
+
previous?.(error, isFatal);
|
|
4393
|
+
});
|
|
4394
|
+
}
|
|
4395
|
+
/** @internal Test seam: ejercita el camino de persistencia (window handlers no son testeables en vitest). */
|
|
4396
|
+
_persistForTest(error) {
|
|
4397
|
+
this.captureUnhandled(error);
|
|
4398
|
+
}
|
|
4399
|
+
};
|
|
4400
|
+
|
|
4401
|
+
// src/analytics/messaging.ts
|
|
4402
|
+
function buildMessageParams(stage, o) {
|
|
4403
|
+
const p = {
|
|
4404
|
+
stage,
|
|
4405
|
+
message_id: o.id,
|
|
4406
|
+
message_title: o.title,
|
|
4407
|
+
channel: o.channel
|
|
4408
|
+
};
|
|
4409
|
+
if (o.campaign) p.campaign = o.campaign;
|
|
4410
|
+
if (o.value !== void 0) p.value = o.value;
|
|
4411
|
+
if (o.currency) p.currency = o.currency;
|
|
4412
|
+
return { ...p, ...o.params ?? {} };
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4256
4415
|
// src/core/deepdots-popups.ts
|
|
4257
4416
|
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
4258
4417
|
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
4259
4418
|
var ANALYTICS_MAX_BATCH_SIZE = 20;
|
|
4260
4419
|
var ANALYTICS_FLUSH_INTERVAL_MS = 3e4;
|
|
4420
|
+
var RESERVED_EVENT_PREFIX = "deepdots_";
|
|
4421
|
+
var CUSTOM_EVENT_PREFIX = "deepdots_event_";
|
|
4261
4422
|
var DeepdotsPopups = class {
|
|
4262
4423
|
constructor() {
|
|
4263
4424
|
this.config = null;
|
|
@@ -4287,6 +4448,8 @@ var DeepdotsPopups = class {
|
|
|
4287
4448
|
this.analyticsFeedbackSessionId = void 0;
|
|
4288
4449
|
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
4289
4450
|
this.analyticsFlushTimer = void 0;
|
|
4451
|
+
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
4452
|
+
this.crashReporter = null;
|
|
4290
4453
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
4291
4454
|
this.navObserver = null;
|
|
4292
4455
|
/** Tiempo activo (engagement time, #8). */
|
|
@@ -4338,25 +4501,44 @@ var DeepdotsPopups = class {
|
|
|
4338
4501
|
this.analyticsFeedbackSessionId = id;
|
|
4339
4502
|
}
|
|
4340
4503
|
}) : void 0;
|
|
4504
|
+
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
4341
4505
|
this.analytics = new AnalyticsManager({
|
|
4342
4506
|
sink: analyticsSink,
|
|
4343
4507
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
4344
4508
|
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
4345
4509
|
platform: config.platform ?? "web",
|
|
4346
|
-
device
|
|
4510
|
+
device,
|
|
4347
4511
|
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
4348
4512
|
onFlushNeeded: () => this.flushAnalytics()
|
|
4349
4513
|
});
|
|
4514
|
+
this.crashReporter = new CrashReporter({
|
|
4515
|
+
storage,
|
|
4516
|
+
emit: (params) => this.track("deepdots_app_crash", params),
|
|
4517
|
+
device: () => ({ appVersion: device.app_version, osVersion: device.os_version, deviceModel: device.device_model }),
|
|
4518
|
+
sessionId: () => this.tracking?.getSessionId() ?? null,
|
|
4519
|
+
now: () => Date.now(),
|
|
4520
|
+
enabled: () => this.tracking?.isTrackingEnabled() ?? false
|
|
4521
|
+
});
|
|
4522
|
+
if (this.tracking?.isTrackingEnabled()) {
|
|
4523
|
+
this.crashReporter.install();
|
|
4524
|
+
}
|
|
4350
4525
|
collectGeoInfo().then((geo) => {
|
|
4351
4526
|
if (geo) this.analytics?.updateDevice(geo);
|
|
4352
4527
|
}).catch(() => {
|
|
4353
4528
|
});
|
|
4354
4529
|
this.navObserver = new NavigationObserver();
|
|
4355
|
-
this.navObserver.onVisit((v) => this.track("
|
|
4530
|
+
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
4356
4531
|
this.navObserver.install();
|
|
4357
4532
|
this.engagement = new EngagementTracker();
|
|
4358
4533
|
this.engagement.resume();
|
|
4359
4534
|
this.setupAnalyticsFlush();
|
|
4535
|
+
this.track("deepdots_session_start", {});
|
|
4536
|
+
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
4537
|
+
if (this.tracking?.isTrackingEnabled()) {
|
|
4538
|
+
for (const rec of pendingCrashes) {
|
|
4539
|
+
this.track("deepdots_app_crash", crashRecordToParams(rec));
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4360
4542
|
this.initialized = true;
|
|
4361
4543
|
this.log("SDK initialized", this.config);
|
|
4362
4544
|
if (this.renderer.init) this.renderer.init();
|
|
@@ -4389,14 +4571,25 @@ var DeepdotsPopups = class {
|
|
|
4389
4571
|
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
|
4390
4572
|
track(name, params) {
|
|
4391
4573
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4392
|
-
|
|
4393
|
-
this.
|
|
4574
|
+
const eventName = name.startsWith(RESERVED_EVENT_PREFIX) ? name : `${CUSTOM_EVENT_PREFIX}${name}`;
|
|
4575
|
+
this.analytics?.track(eventName, params);
|
|
4576
|
+
this.log("analytics \xB7 track:", eventName, params ?? {});
|
|
4394
4577
|
}
|
|
4395
4578
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
4396
4579
|
setUserAttributes(attributes) {
|
|
4397
4580
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4398
4581
|
this.analytics?.setUserAttributes(attributes);
|
|
4399
4582
|
}
|
|
4583
|
+
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
4584
|
+
reportError(error, options) {
|
|
4585
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4586
|
+
this.crashReporter?.reportError(error, options);
|
|
4587
|
+
}
|
|
4588
|
+
/** Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. No-op si tracking off. */
|
|
4589
|
+
installReactNativeCrashHandler(errorUtils) {
|
|
4590
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4591
|
+
this.crashReporter?.installReactNative(errorUtils);
|
|
4592
|
+
}
|
|
4400
4593
|
/**
|
|
4401
4594
|
* Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
|
|
4402
4595
|
* Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del
|
|
@@ -4414,23 +4607,27 @@ var DeepdotsPopups = class {
|
|
|
4414
4607
|
this.analytics?.enterMiniService(name, entryPointType);
|
|
4415
4608
|
this.log("analytics \xB7 enterMiniService:", name, entryPointType ?? "");
|
|
4416
4609
|
}
|
|
4417
|
-
/** Cierra el mini-service
|
|
4418
|
-
exitMiniService() {
|
|
4610
|
+
/** Cierra el mini-service `name` (emite `mini_service_exit` con duración, #27). No-op si tracking off o si ese no está activo. */
|
|
4611
|
+
exitMiniService(name) {
|
|
4419
4612
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4420
|
-
this.analytics?.exitMiniService();
|
|
4421
|
-
this.log("analytics \xB7 exitMiniService");
|
|
4613
|
+
this.analytics?.exitMiniService(name);
|
|
4614
|
+
this.log("analytics \xB7 exitMiniService:", name);
|
|
4422
4615
|
}
|
|
4423
4616
|
/** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */
|
|
4424
4617
|
trackSearch(query, resultsCount, params) {
|
|
4425
|
-
this.track("
|
|
4618
|
+
this.track("deepdots_search", { query, results_count: resultsCount, has_results: resultsCount > 0, ...params ?? {} });
|
|
4426
4619
|
}
|
|
4427
4620
|
/** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */
|
|
4428
4621
|
trackFindabilityFriction(frictionTopic, params) {
|
|
4429
|
-
this.track("
|
|
4622
|
+
this.track("deepdots_findability_friction", { friction_topic: frictionTopic, ...params ?? {} });
|
|
4430
4623
|
}
|
|
4431
4624
|
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
4432
4625
|
trackFunnelStep(funnel, step, taskId, params) {
|
|
4433
|
-
this.track("
|
|
4626
|
+
this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
|
|
4627
|
+
}
|
|
4628
|
+
/** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
|
|
4629
|
+
trackMessage(stage, options) {
|
|
4630
|
+
this.track("deepdots_message", buildMessageParams(stage, options));
|
|
4434
4631
|
}
|
|
4435
4632
|
/**
|
|
4436
4633
|
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
@@ -4456,7 +4653,7 @@ var DeepdotsPopups = class {
|
|
|
4456
4653
|
onBackground() {
|
|
4457
4654
|
this.navObserver?.stop();
|
|
4458
4655
|
this.navStarted = false;
|
|
4459
|
-
this.
|
|
4656
|
+
if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
|
|
4460
4657
|
this.flushEngagement();
|
|
4461
4658
|
this.engagement?.pause();
|
|
4462
4659
|
this.flushAnalytics();
|
|
@@ -4479,7 +4676,7 @@ var DeepdotsPopups = class {
|
|
|
4479
4676
|
flushEngagement() {
|
|
4480
4677
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4481
4678
|
const ms = this.engagement?.consume() ?? 0;
|
|
4482
|
-
if (ms > 0) this.track("
|
|
4679
|
+
if (ms > 0) this.track("deepdots_user_engagement", { engagement_time_msec: ms });
|
|
4483
4680
|
}
|
|
4484
4681
|
analyticsIdentity() {
|
|
4485
4682
|
return {
|
|
@@ -4494,7 +4691,7 @@ var DeepdotsPopups = class {
|
|
|
4494
4691
|
window.addEventListener("pagehide", () => {
|
|
4495
4692
|
clearInterval(this.analyticsFlushTimer);
|
|
4496
4693
|
this.navObserver?.stop();
|
|
4497
|
-
this.
|
|
4694
|
+
if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
|
|
4498
4695
|
this.flushEngagement();
|
|
4499
4696
|
this.flushAnalytics();
|
|
4500
4697
|
});
|
|
@@ -5163,6 +5360,10 @@ function setupReactNative(sdk, config, deps = {}) {
|
|
|
5163
5360
|
const device = deps.deviceInfo ? collectRnDevice(deps.deviceInfo) : config.device;
|
|
5164
5361
|
const platform = deps.platform ?? config.platform;
|
|
5165
5362
|
sdk.init({ ...config, storage, device, platform });
|
|
5363
|
+
const errorUtils = deps.errorUtils ?? globalThis.ErrorUtils;
|
|
5364
|
+
if (errorUtils && typeof errorUtils.setGlobalHandler === "function") {
|
|
5365
|
+
sdk.installReactNativeCrashHandler(errorUtils);
|
|
5366
|
+
}
|
|
5166
5367
|
let subscription;
|
|
5167
5368
|
if (deps.appState?.addEventListener) {
|
|
5168
5369
|
subscription = deps.appState.addEventListener("change", (state) => {
|