@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/react-native.mjs
CHANGED
|
@@ -392,8 +392,8 @@ var AnalyticsManager = class {
|
|
|
392
392
|
constructor(options = {}) {
|
|
393
393
|
this.events = [];
|
|
394
394
|
this.attributes = {};
|
|
395
|
-
|
|
396
|
-
this.
|
|
395
|
+
/** Mini-services activos: nombre → timestamp de entrada. El orden de inserción marca el "más reciente". */
|
|
396
|
+
this.activeMiniServices = /* @__PURE__ */ new Map();
|
|
397
397
|
this.sink = options.sink ?? dryRunSink;
|
|
398
398
|
this.now = options.now ?? (() => Date.now());
|
|
399
399
|
this.publicKey = options.publicKey;
|
|
@@ -414,24 +414,38 @@ var AnalyticsManager = class {
|
|
|
414
414
|
this.attributes[k] = String(v);
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
|
-
/**
|
|
417
|
+
/**
|
|
418
|
+
* Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`.
|
|
419
|
+
* Admite varios activos a la vez (concurrencia); el "actual" para etiquetar es el más reciente.
|
|
420
|
+
* Reentrar con un nombre ya activo refresca su orden y su tiempo de entrada.
|
|
421
|
+
*/
|
|
418
422
|
enterMiniService(name, entryPointType) {
|
|
419
|
-
this.
|
|
420
|
-
this.
|
|
421
|
-
this.track("
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
423
|
+
this.activeMiniServices.delete(name);
|
|
424
|
+
this.activeMiniServices.set(name, this.now());
|
|
425
|
+
this.track("deepdots_mini_service_enter", { entry_point_type: entryPointType ?? null });
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Cierra el mini-service `name` emitiendo `mini_service_exit` con su duración (#27).
|
|
429
|
+
* No-op si ese nombre no está activo. Cierre coherente con concurrencia.
|
|
430
|
+
*/
|
|
431
|
+
exitMiniService(name) {
|
|
432
|
+
const enteredAt = this.activeMiniServices.get(name);
|
|
433
|
+
if (enteredAt === void 0) return;
|
|
434
|
+
const durationSeconds = Math.max(0, Math.round((this.now() - enteredAt) / 1e3));
|
|
435
|
+
this.activeMiniServices.delete(name);
|
|
436
|
+
this.track("deepdots_mini_service_exit", { mini_service: name, duration_seconds: durationSeconds });
|
|
437
|
+
}
|
|
438
|
+
/** Cierra TODOS los mini-services activos (orden LIFO). Para el cierre por lifecycle (background/cierre). */
|
|
439
|
+
exitAllMiniServices() {
|
|
440
|
+
for (const name of Array.from(this.activeMiniServices.keys()).reverse()) {
|
|
441
|
+
this.exitMiniService(name);
|
|
442
|
+
}
|
|
430
443
|
}
|
|
431
444
|
/** Registra un evento de analítica (modelo GA: nombre + params). */
|
|
432
445
|
track(name, params) {
|
|
446
|
+
const current = this.getMiniService();
|
|
433
447
|
const merged = {
|
|
434
|
-
...
|
|
448
|
+
...current ? { mini_service: current } : {},
|
|
435
449
|
...params ?? {}
|
|
436
450
|
};
|
|
437
451
|
this.events.push({
|
|
@@ -443,9 +457,11 @@ var AnalyticsManager = class {
|
|
|
443
457
|
this.onFlushNeeded?.();
|
|
444
458
|
}
|
|
445
459
|
}
|
|
446
|
-
/** Mini-service activo
|
|
460
|
+
/** Mini-service actual (el más reciente aún activo) para etiquetar eventos + metadata del survey (#33). */
|
|
447
461
|
getMiniService() {
|
|
448
|
-
|
|
462
|
+
let last = null;
|
|
463
|
+
for (const name of this.activeMiniServices.keys()) last = name;
|
|
464
|
+
return last;
|
|
449
465
|
}
|
|
450
466
|
/** Nº de eventos pendientes de flush. */
|
|
451
467
|
pending() {
|
|
@@ -784,11 +800,153 @@ var ContactManager = class {
|
|
|
784
800
|
}
|
|
785
801
|
};
|
|
786
802
|
|
|
803
|
+
// src/analytics/crash-reporter.ts
|
|
804
|
+
var STACK_MAX = 8e3;
|
|
805
|
+
var QUEUE_KEY = "deepdots.crash.queue";
|
|
806
|
+
var MAX_QUEUED = 20;
|
|
807
|
+
function crashRecordToParams(r) {
|
|
808
|
+
const params = {
|
|
809
|
+
crashed_at: r.crashedAt,
|
|
810
|
+
crash_type: r.crashType,
|
|
811
|
+
message: r.message,
|
|
812
|
+
stack: r.stack,
|
|
813
|
+
fatal: r.fatal,
|
|
814
|
+
handled: r.handled,
|
|
815
|
+
severity: r.severity
|
|
816
|
+
};
|
|
817
|
+
if (r.sessionId) params.crashed_session_id = r.sessionId;
|
|
818
|
+
if (r.appVersion) params.crashed_app_version = r.appVersion;
|
|
819
|
+
if (r.osVersion) params.crashed_os_version = r.osVersion;
|
|
820
|
+
if (r.deviceModel) params.crashed_device_model = r.deviceModel;
|
|
821
|
+
if (r.context) {
|
|
822
|
+
for (const [k, v] of Object.entries(r.context)) params[`ctx_${k}`] = v;
|
|
823
|
+
}
|
|
824
|
+
return params;
|
|
825
|
+
}
|
|
826
|
+
var CrashReporter = class {
|
|
827
|
+
constructor(options) {
|
|
828
|
+
this.options = options;
|
|
829
|
+
this.now = options.now ?? (() => Date.now());
|
|
830
|
+
}
|
|
831
|
+
isEnabled() {
|
|
832
|
+
return this.options.enabled ? this.options.enabled() : true;
|
|
833
|
+
}
|
|
834
|
+
/** Construye un CrashRecord capturando contexto en el momento del crash. */
|
|
835
|
+
buildRecord(error, severity, handled, fatal, context) {
|
|
836
|
+
const err = error;
|
|
837
|
+
const isErr = error instanceof Error;
|
|
838
|
+
const dev = this.options.device();
|
|
839
|
+
const ctx = context ? Object.fromEntries(Object.entries(context).map(([k, v]) => [k, String(v)])) : void 0;
|
|
840
|
+
return {
|
|
841
|
+
crashedAt: this.now(),
|
|
842
|
+
crashType: isErr && err.name || "Error",
|
|
843
|
+
message: isErr ? String(err.message ?? "") : String(error),
|
|
844
|
+
stack: (isErr && typeof err.stack === "string" ? err.stack : "").slice(0, STACK_MAX),
|
|
845
|
+
fatal,
|
|
846
|
+
handled,
|
|
847
|
+
severity,
|
|
848
|
+
sessionId: this.options.sessionId(),
|
|
849
|
+
appVersion: dev.appVersion,
|
|
850
|
+
osVersion: dev.osVersion,
|
|
851
|
+
deviceModel: dev.deviceModel,
|
|
852
|
+
context: ctx
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
/** API pública del host: reporta un error (app viva) → emite el evento ya. */
|
|
856
|
+
reportError(error, options = {}) {
|
|
857
|
+
if (!this.isEnabled()) return;
|
|
858
|
+
const severity = options.severity ?? "error";
|
|
859
|
+
const handled = options.handled ?? true;
|
|
860
|
+
const fatal = severity === "fatal";
|
|
861
|
+
const record = this.buildRecord(error, severity, handled, fatal, options.context);
|
|
862
|
+
this.options.emit(crashRecordToParams(record));
|
|
863
|
+
}
|
|
864
|
+
/** Captura un error no manejado (fatal) → persiste a disco para replay en el siguiente arranque. */
|
|
865
|
+
captureUnhandled(error) {
|
|
866
|
+
this.persist(this.buildRecord(error, "fatal", false, true));
|
|
867
|
+
}
|
|
868
|
+
/** Persiste un crash no capturado a disco para reenviarlo en el siguiente arranque. */
|
|
869
|
+
persist(record) {
|
|
870
|
+
let queue = [];
|
|
871
|
+
try {
|
|
872
|
+
const raw = this.options.storage.getItem(QUEUE_KEY);
|
|
873
|
+
if (raw) queue = JSON.parse(raw);
|
|
874
|
+
if (!Array.isArray(queue)) queue = [];
|
|
875
|
+
} catch {
|
|
876
|
+
queue = [];
|
|
877
|
+
}
|
|
878
|
+
queue.push(record);
|
|
879
|
+
if (queue.length > MAX_QUEUED) queue = queue.slice(queue.length - MAX_QUEUED);
|
|
880
|
+
try {
|
|
881
|
+
this.options.storage.setItem(QUEUE_KEY, JSON.stringify(queue));
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
/** Lee y vacía la cola de crashes pendientes (llamado en init para el replay). */
|
|
886
|
+
drainPendingCrashes() {
|
|
887
|
+
let queue = [];
|
|
888
|
+
try {
|
|
889
|
+
const raw = this.options.storage.getItem(QUEUE_KEY);
|
|
890
|
+
if (raw) queue = JSON.parse(raw);
|
|
891
|
+
if (!Array.isArray(queue)) queue = [];
|
|
892
|
+
} catch {
|
|
893
|
+
queue = [];
|
|
894
|
+
}
|
|
895
|
+
if (queue.length) {
|
|
896
|
+
try {
|
|
897
|
+
this.options.storage.removeItem(QUEUE_KEY);
|
|
898
|
+
} catch {
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return queue;
|
|
902
|
+
}
|
|
903
|
+
/** Instala los handlers globales de errores no capturados (no-op sin `window`). */
|
|
904
|
+
install() {
|
|
905
|
+
if (typeof window === "undefined") return;
|
|
906
|
+
window.addEventListener("error", (e) => {
|
|
907
|
+
if (!this.isEnabled()) return;
|
|
908
|
+
this.captureUnhandled(e.error ?? e.message ?? "error");
|
|
909
|
+
});
|
|
910
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
911
|
+
if (!this.isEnabled()) return;
|
|
912
|
+
this.captureUnhandled(e.reason ?? "unhandledrejection");
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
/** Engancha `global.ErrorUtils` de RN (no hay `window`): persiste y encadena al handler previo. */
|
|
916
|
+
installReactNative(errorUtils) {
|
|
917
|
+
const previous = errorUtils.getGlobalHandler?.();
|
|
918
|
+
errorUtils.setGlobalHandler((error, isFatal) => {
|
|
919
|
+
if (this.isEnabled()) this.captureUnhandled(error);
|
|
920
|
+
previous?.(error, isFatal);
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
/** @internal Test seam: ejercita el camino de persistencia (window handlers no son testeables en vitest). */
|
|
924
|
+
_persistForTest(error) {
|
|
925
|
+
this.captureUnhandled(error);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
// src/analytics/messaging.ts
|
|
930
|
+
function buildMessageParams(stage, o) {
|
|
931
|
+
const p = {
|
|
932
|
+
stage,
|
|
933
|
+
message_id: o.id,
|
|
934
|
+
message_title: o.title,
|
|
935
|
+
channel: o.channel
|
|
936
|
+
};
|
|
937
|
+
if (o.campaign) p.campaign = o.campaign;
|
|
938
|
+
if (o.value !== void 0) p.value = o.value;
|
|
939
|
+
if (o.currency) p.currency = o.currency;
|
|
940
|
+
return { ...p, ...o.params ?? {} };
|
|
941
|
+
}
|
|
942
|
+
|
|
787
943
|
// src/core/deepdots-popups.ts
|
|
788
944
|
var EXIT_QUEUE_STORAGE_KEY = "__deepdots_exit_popup_queue__";
|
|
789
945
|
var DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
790
946
|
var ANALYTICS_MAX_BATCH_SIZE = 20;
|
|
791
947
|
var ANALYTICS_FLUSH_INTERVAL_MS = 3e4;
|
|
948
|
+
var RESERVED_EVENT_PREFIX = "deepdots_";
|
|
949
|
+
var CUSTOM_EVENT_PREFIX = "deepdots_event_";
|
|
792
950
|
var DeepdotsPopups = class {
|
|
793
951
|
constructor() {
|
|
794
952
|
this.config = null;
|
|
@@ -818,6 +976,8 @@ var DeepdotsPopups = class {
|
|
|
818
976
|
this.analyticsFeedbackSessionId = void 0;
|
|
819
977
|
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
820
978
|
this.analyticsFlushTimer = void 0;
|
|
979
|
+
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
980
|
+
this.crashReporter = null;
|
|
821
981
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
822
982
|
this.navObserver = null;
|
|
823
983
|
/** Tiempo activo (engagement time, #8). */
|
|
@@ -869,25 +1029,44 @@ var DeepdotsPopups = class {
|
|
|
869
1029
|
this.analyticsFeedbackSessionId = id;
|
|
870
1030
|
}
|
|
871
1031
|
}) : void 0;
|
|
1032
|
+
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
872
1033
|
this.analytics = new AnalyticsManager({
|
|
873
1034
|
sink: analyticsSink,
|
|
874
1035
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
875
1036
|
language: typeof navigator !== "undefined" ? navigator.language : void 0,
|
|
876
1037
|
platform: config.platform ?? "web",
|
|
877
|
-
device
|
|
1038
|
+
device,
|
|
878
1039
|
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
879
1040
|
onFlushNeeded: () => this.flushAnalytics()
|
|
880
1041
|
});
|
|
1042
|
+
this.crashReporter = new CrashReporter({
|
|
1043
|
+
storage,
|
|
1044
|
+
emit: (params) => this.track("deepdots_app_crash", params),
|
|
1045
|
+
device: () => ({ appVersion: device.app_version, osVersion: device.os_version, deviceModel: device.device_model }),
|
|
1046
|
+
sessionId: () => this.tracking?.getSessionId() ?? null,
|
|
1047
|
+
now: () => Date.now(),
|
|
1048
|
+
enabled: () => this.tracking?.isTrackingEnabled() ?? false
|
|
1049
|
+
});
|
|
1050
|
+
if (this.tracking?.isTrackingEnabled()) {
|
|
1051
|
+
this.crashReporter.install();
|
|
1052
|
+
}
|
|
881
1053
|
collectGeoInfo().then((geo) => {
|
|
882
1054
|
if (geo) this.analytics?.updateDevice(geo);
|
|
883
1055
|
}).catch(() => {
|
|
884
1056
|
});
|
|
885
1057
|
this.navObserver = new NavigationObserver();
|
|
886
|
-
this.navObserver.onVisit((v) => this.track("
|
|
1058
|
+
this.navObserver.onVisit((v) => this.track("deepdots_page_view", { screen: v.screen, duration_seconds: v.durationSeconds }));
|
|
887
1059
|
this.navObserver.install();
|
|
888
1060
|
this.engagement = new EngagementTracker();
|
|
889
1061
|
this.engagement.resume();
|
|
890
1062
|
this.setupAnalyticsFlush();
|
|
1063
|
+
this.track("deepdots_session_start", {});
|
|
1064
|
+
const pendingCrashes = this.crashReporter.drainPendingCrashes();
|
|
1065
|
+
if (this.tracking?.isTrackingEnabled()) {
|
|
1066
|
+
for (const rec of pendingCrashes) {
|
|
1067
|
+
this.track("deepdots_app_crash", crashRecordToParams(rec));
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
891
1070
|
this.initialized = true;
|
|
892
1071
|
this.log("SDK initialized", this.config);
|
|
893
1072
|
if (this.renderer.init) this.renderer.init();
|
|
@@ -920,14 +1099,25 @@ var DeepdotsPopups = class {
|
|
|
920
1099
|
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
|
921
1100
|
track(name, params) {
|
|
922
1101
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
923
|
-
|
|
924
|
-
this.
|
|
1102
|
+
const eventName = name.startsWith(RESERVED_EVENT_PREFIX) ? name : `${CUSTOM_EVENT_PREFIX}${name}`;
|
|
1103
|
+
this.analytics?.track(eventName, params);
|
|
1104
|
+
this.log("analytics \xB7 track:", eventName, params ?? {});
|
|
925
1105
|
}
|
|
926
1106
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
927
1107
|
setUserAttributes(attributes) {
|
|
928
1108
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
929
1109
|
this.analytics?.setUserAttributes(attributes);
|
|
930
1110
|
}
|
|
1111
|
+
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
1112
|
+
reportError(error, options) {
|
|
1113
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
1114
|
+
this.crashReporter?.reportError(error, options);
|
|
1115
|
+
}
|
|
1116
|
+
/** Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. No-op si tracking off. */
|
|
1117
|
+
installReactNativeCrashHandler(errorUtils) {
|
|
1118
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
1119
|
+
this.crashReporter?.installReactNative(errorUtils);
|
|
1120
|
+
}
|
|
931
1121
|
/**
|
|
932
1122
|
* Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
|
|
933
1123
|
* Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del
|
|
@@ -945,23 +1135,27 @@ var DeepdotsPopups = class {
|
|
|
945
1135
|
this.analytics?.enterMiniService(name, entryPointType);
|
|
946
1136
|
this.log("analytics \xB7 enterMiniService:", name, entryPointType ?? "");
|
|
947
1137
|
}
|
|
948
|
-
/** Cierra el mini-service
|
|
949
|
-
exitMiniService() {
|
|
1138
|
+
/** Cierra el mini-service `name` (emite `mini_service_exit` con duración, #27). No-op si tracking off o si ese no está activo. */
|
|
1139
|
+
exitMiniService(name) {
|
|
950
1140
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
951
|
-
this.analytics?.exitMiniService();
|
|
952
|
-
this.log("analytics \xB7 exitMiniService");
|
|
1141
|
+
this.analytics?.exitMiniService(name);
|
|
1142
|
+
this.log("analytics \xB7 exitMiniService:", name);
|
|
953
1143
|
}
|
|
954
1144
|
/** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */
|
|
955
1145
|
trackSearch(query, resultsCount, params) {
|
|
956
|
-
this.track("
|
|
1146
|
+
this.track("deepdots_search", { query, results_count: resultsCount, has_results: resultsCount > 0, ...params ?? {} });
|
|
957
1147
|
}
|
|
958
1148
|
/** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */
|
|
959
1149
|
trackFindabilityFriction(frictionTopic, params) {
|
|
960
|
-
this.track("
|
|
1150
|
+
this.track("deepdots_findability_friction", { friction_topic: frictionTopic, ...params ?? {} });
|
|
961
1151
|
}
|
|
962
1152
|
/** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
|
|
963
1153
|
trackFunnelStep(funnel, step, taskId, params) {
|
|
964
|
-
this.track("
|
|
1154
|
+
this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
|
|
1155
|
+
}
|
|
1156
|
+
/** Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app). No-op si tracking off. */
|
|
1157
|
+
trackMessage(stage, options) {
|
|
1158
|
+
this.track("deepdots_message", buildMessageParams(stage, options));
|
|
965
1159
|
}
|
|
966
1160
|
/**
|
|
967
1161
|
* Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).
|
|
@@ -987,7 +1181,7 @@ var DeepdotsPopups = class {
|
|
|
987
1181
|
onBackground() {
|
|
988
1182
|
this.navObserver?.stop();
|
|
989
1183
|
this.navStarted = false;
|
|
990
|
-
this.
|
|
1184
|
+
if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
|
|
991
1185
|
this.flushEngagement();
|
|
992
1186
|
this.engagement?.pause();
|
|
993
1187
|
this.flushAnalytics();
|
|
@@ -1010,7 +1204,7 @@ var DeepdotsPopups = class {
|
|
|
1010
1204
|
flushEngagement() {
|
|
1011
1205
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
1012
1206
|
const ms = this.engagement?.consume() ?? 0;
|
|
1013
|
-
if (ms > 0) this.track("
|
|
1207
|
+
if (ms > 0) this.track("deepdots_user_engagement", { engagement_time_msec: ms });
|
|
1014
1208
|
}
|
|
1015
1209
|
analyticsIdentity() {
|
|
1016
1210
|
return {
|
|
@@ -1025,7 +1219,7 @@ var DeepdotsPopups = class {
|
|
|
1025
1219
|
window.addEventListener("pagehide", () => {
|
|
1026
1220
|
clearInterval(this.analyticsFlushTimer);
|
|
1027
1221
|
this.navObserver?.stop();
|
|
1028
|
-
this.
|
|
1222
|
+
if (this.tracking?.isTrackingEnabled()) this.analytics?.exitAllMiniServices();
|
|
1029
1223
|
this.flushEngagement();
|
|
1030
1224
|
this.flushAnalytics();
|
|
1031
1225
|
});
|
|
@@ -1691,6 +1885,10 @@ function setupReactNative(sdk2, config, deps = {}) {
|
|
|
1691
1885
|
const device = deps.deviceInfo ? collectRnDevice(deps.deviceInfo) : config.device;
|
|
1692
1886
|
const platform = deps.platform ?? config.platform;
|
|
1693
1887
|
sdk2.init({ ...config, storage, device, platform });
|
|
1888
|
+
const errorUtils = deps.errorUtils ?? globalThis.ErrorUtils;
|
|
1889
|
+
if (errorUtils && typeof errorUtils.setGlobalHandler === "function") {
|
|
1890
|
+
sdk2.installReactNativeCrashHandler(errorUtils);
|
|
1891
|
+
}
|
|
1694
1892
|
let subscription;
|
|
1695
1893
|
if (deps.appState?.addEventListener) {
|
|
1696
1894
|
subscription = deps.appState.addEventListener("change", (state) => {
|