@magicfeedback/popup-sdk 1.1.5 → 1.1.7
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/{chunk-LSTKGFU7.mjs → chunk-4UXOEIAB.mjs} +21 -2
- package/dist/chunk-4UXOEIAB.mjs.map +1 -0
- package/dist/{chunk-7TOLYAFL.mjs → chunk-XOTM5CHX.mjs} +21 -2
- package/dist/chunk-XOTM5CHX.mjs.map +1 -0
- package/dist/index.d.mts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +167 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +141 -24
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +30 -1
- package/dist/react-native.d.ts +30 -1
- package/dist/react-native.js +167 -26
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +141 -24
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-MES7IINW.mjs → renderPopup-EZKDMLVZ.mjs} +10 -7
- package/dist/renderPopup-EZKDMLVZ.mjs.map +1 -0
- package/dist/{renderPopup-YNH2TD3B.mjs → renderPopup-PS3NJDUR.mjs} +10 -7
- package/dist/renderPopup-PS3NJDUR.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-7TOLYAFL.mjs.map +0 -1
- package/dist/chunk-LSTKGFU7.mjs.map +0 -1
- package/dist/renderPopup-MES7IINW.mjs.map +0 -1
- package/dist/renderPopup-YNH2TD3B.mjs.map +0 -1
package/dist/react-native.d.mts
CHANGED
|
@@ -76,6 +76,8 @@ interface AnalyticsEnvelope {
|
|
|
76
76
|
device?: DeviceInfo;
|
|
77
77
|
/** User attributes del cliente, usados para breakdowns (registration_status, pass_type, …). */
|
|
78
78
|
attributes: Record<string, string>;
|
|
79
|
+
/** Métricas del host: valores medibles persistentes → van en `feedback.metrics` del body. */
|
|
80
|
+
metrics?: Record<string, string>;
|
|
79
81
|
};
|
|
80
82
|
events: AnalyticsEvent[];
|
|
81
83
|
}
|
|
@@ -92,7 +94,8 @@ interface AnalyticsEnvelope {
|
|
|
92
94
|
* - cada entrada usa `value: string[]` (array de un elemento)
|
|
93
95
|
* - eventos: {key: nombre_evento, value: [JSON(timestamp + params)]}
|
|
94
96
|
* - identidad (user_id) → `profile` como `external-user-id`
|
|
95
|
-
* -
|
|
97
|
+
* - métricas del host (setMetric) → `feedback.metrics` (mismo shape que metadata, sin prefijo)
|
|
98
|
+
* - `answers` vacío
|
|
96
99
|
* - `text` vacío
|
|
97
100
|
*/
|
|
98
101
|
|
|
@@ -105,6 +108,17 @@ interface AnalyticsKeys {
|
|
|
105
108
|
* Configuration options for initializing the DeepdotsPopups SDK
|
|
106
109
|
*/
|
|
107
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Custom logger to receive the SDK's debug output instead of the console.
|
|
113
|
+
* Only `log` is required; `warn`/`error`/`info` fall back to `log` when omitted.
|
|
114
|
+
* Lets the host pipe debug output to a file, Firebase, etc. `console` satisfies this shape.
|
|
115
|
+
*/
|
|
116
|
+
interface DeepdotsLogger {
|
|
117
|
+
log: (...args: unknown[]) => void;
|
|
118
|
+
warn?: (...args: unknown[]) => void;
|
|
119
|
+
error?: (...args: unknown[]) => void;
|
|
120
|
+
info?: (...args: unknown[]) => void;
|
|
121
|
+
}
|
|
108
122
|
interface DeepdotsInitParams {
|
|
109
123
|
/** API key for authentication */
|
|
110
124
|
apiKey?: string;
|
|
@@ -112,6 +126,8 @@ interface DeepdotsInitParams {
|
|
|
112
126
|
nodeEnv?: 'development' | 'production';
|
|
113
127
|
/** Enable debug logging */
|
|
114
128
|
debug?: boolean;
|
|
129
|
+
/** Custom logger for debug output. When set (and `debug` is true), the SDK routes its logs here instead of `console`. Default: `console`. */
|
|
130
|
+
logger?: DeepdotsLogger;
|
|
115
131
|
/** Optional user id to send with popup events */
|
|
116
132
|
userId?: string;
|
|
117
133
|
/** Starts tracking enabled (default) or disabled (e.g. until consent is granted). Equivalent to calling `setTrackingEnabled` after init. */
|
|
@@ -122,6 +138,12 @@ interface DeepdotsInitParams {
|
|
|
122
138
|
storage?: KeyValueStorage;
|
|
123
139
|
/** Platform used in the analytics envelope. Default is 'web'; in RN pass 'android'/'ios'. */
|
|
124
140
|
platform?: 'web' | 'android' | 'ios';
|
|
141
|
+
/**
|
|
142
|
+
* BCP-47 language tag for the analytics context (e.g. 'es-ES'). If omitted, the SDK
|
|
143
|
+
* auto-detects: `navigator.language` on web, or the `Intl` locale as a fallback (works in
|
|
144
|
+
* React Native with Hermes, where `navigator.language` is absent).
|
|
145
|
+
*/
|
|
146
|
+
language?: string;
|
|
125
147
|
/** Injectable device info (RN: from react-native-device-info). If missing, it is derived from the browser. */
|
|
126
148
|
device?: DeviceInfo;
|
|
127
149
|
/**
|
|
@@ -280,6 +302,8 @@ declare class DeepdotsPopups {
|
|
|
280
302
|
private triggers;
|
|
281
303
|
private initialized;
|
|
282
304
|
private renderer;
|
|
305
|
+
/** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
|
|
306
|
+
private logger;
|
|
283
307
|
private popupContainer;
|
|
284
308
|
private popupDefinitions;
|
|
285
309
|
private popupsLoaded;
|
|
@@ -321,6 +345,11 @@ declare class DeepdotsPopups {
|
|
|
321
345
|
track(name: string, params?: Record<string, unknown>): void;
|
|
322
346
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
323
347
|
setUserAttributes(attributes: Record<string, string | number | boolean>): void;
|
|
348
|
+
/**
|
|
349
|
+
* Registra/actualiza una métrica (valor medible) → `feedback.metrics` del body.
|
|
350
|
+
* Persistente y sobrescribe por key. No-op si tracking off.
|
|
351
|
+
*/
|
|
352
|
+
setMetric(key: string, value: string | number | boolean): void;
|
|
324
353
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
325
354
|
reportError(error: unknown, options?: ReportErrorOptions): void;
|
|
326
355
|
/** Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. No-op si tracking off. */
|
package/dist/react-native.d.ts
CHANGED
|
@@ -76,6 +76,8 @@ interface AnalyticsEnvelope {
|
|
|
76
76
|
device?: DeviceInfo;
|
|
77
77
|
/** User attributes del cliente, usados para breakdowns (registration_status, pass_type, …). */
|
|
78
78
|
attributes: Record<string, string>;
|
|
79
|
+
/** Métricas del host: valores medibles persistentes → van en `feedback.metrics` del body. */
|
|
80
|
+
metrics?: Record<string, string>;
|
|
79
81
|
};
|
|
80
82
|
events: AnalyticsEvent[];
|
|
81
83
|
}
|
|
@@ -92,7 +94,8 @@ interface AnalyticsEnvelope {
|
|
|
92
94
|
* - cada entrada usa `value: string[]` (array de un elemento)
|
|
93
95
|
* - eventos: {key: nombre_evento, value: [JSON(timestamp + params)]}
|
|
94
96
|
* - identidad (user_id) → `profile` como `external-user-id`
|
|
95
|
-
* -
|
|
97
|
+
* - métricas del host (setMetric) → `feedback.metrics` (mismo shape que metadata, sin prefijo)
|
|
98
|
+
* - `answers` vacío
|
|
96
99
|
* - `text` vacío
|
|
97
100
|
*/
|
|
98
101
|
|
|
@@ -105,6 +108,17 @@ interface AnalyticsKeys {
|
|
|
105
108
|
* Configuration options for initializing the DeepdotsPopups SDK
|
|
106
109
|
*/
|
|
107
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Custom logger to receive the SDK's debug output instead of the console.
|
|
113
|
+
* Only `log` is required; `warn`/`error`/`info` fall back to `log` when omitted.
|
|
114
|
+
* Lets the host pipe debug output to a file, Firebase, etc. `console` satisfies this shape.
|
|
115
|
+
*/
|
|
116
|
+
interface DeepdotsLogger {
|
|
117
|
+
log: (...args: unknown[]) => void;
|
|
118
|
+
warn?: (...args: unknown[]) => void;
|
|
119
|
+
error?: (...args: unknown[]) => void;
|
|
120
|
+
info?: (...args: unknown[]) => void;
|
|
121
|
+
}
|
|
108
122
|
interface DeepdotsInitParams {
|
|
109
123
|
/** API key for authentication */
|
|
110
124
|
apiKey?: string;
|
|
@@ -112,6 +126,8 @@ interface DeepdotsInitParams {
|
|
|
112
126
|
nodeEnv?: 'development' | 'production';
|
|
113
127
|
/** Enable debug logging */
|
|
114
128
|
debug?: boolean;
|
|
129
|
+
/** Custom logger for debug output. When set (and `debug` is true), the SDK routes its logs here instead of `console`. Default: `console`. */
|
|
130
|
+
logger?: DeepdotsLogger;
|
|
115
131
|
/** Optional user id to send with popup events */
|
|
116
132
|
userId?: string;
|
|
117
133
|
/** Starts tracking enabled (default) or disabled (e.g. until consent is granted). Equivalent to calling `setTrackingEnabled` after init. */
|
|
@@ -122,6 +138,12 @@ interface DeepdotsInitParams {
|
|
|
122
138
|
storage?: KeyValueStorage;
|
|
123
139
|
/** Platform used in the analytics envelope. Default is 'web'; in RN pass 'android'/'ios'. */
|
|
124
140
|
platform?: 'web' | 'android' | 'ios';
|
|
141
|
+
/**
|
|
142
|
+
* BCP-47 language tag for the analytics context (e.g. 'es-ES'). If omitted, the SDK
|
|
143
|
+
* auto-detects: `navigator.language` on web, or the `Intl` locale as a fallback (works in
|
|
144
|
+
* React Native with Hermes, where `navigator.language` is absent).
|
|
145
|
+
*/
|
|
146
|
+
language?: string;
|
|
125
147
|
/** Injectable device info (RN: from react-native-device-info). If missing, it is derived from the browser. */
|
|
126
148
|
device?: DeviceInfo;
|
|
127
149
|
/**
|
|
@@ -280,6 +302,8 @@ declare class DeepdotsPopups {
|
|
|
280
302
|
private triggers;
|
|
281
303
|
private initialized;
|
|
282
304
|
private renderer;
|
|
305
|
+
/** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
|
|
306
|
+
private logger;
|
|
283
307
|
private popupContainer;
|
|
284
308
|
private popupDefinitions;
|
|
285
309
|
private popupsLoaded;
|
|
@@ -321,6 +345,11 @@ declare class DeepdotsPopups {
|
|
|
321
345
|
track(name: string, params?: Record<string, unknown>): void;
|
|
322
346
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
323
347
|
setUserAttributes(attributes: Record<string, string | number | boolean>): void;
|
|
348
|
+
/**
|
|
349
|
+
* Registra/actualiza una métrica (valor medible) → `feedback.metrics` del body.
|
|
350
|
+
* Persistente y sobrescribe por key. No-op si tracking off.
|
|
351
|
+
*/
|
|
352
|
+
setMetric(key: string, value: string | number | boolean): void;
|
|
324
353
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
325
354
|
reportError(error: unknown, options?: ReportErrorOptions): void;
|
|
326
355
|
/** Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. No-op si tracking off. */
|
package/dist/react-native.js
CHANGED
|
@@ -143,8 +143,8 @@ var init_tracking_manager = __esm({
|
|
|
143
143
|
|
|
144
144
|
// src/ui/font.ts
|
|
145
145
|
function fontFormatFromUrl(url) {
|
|
146
|
-
const
|
|
147
|
-
const ext =
|
|
146
|
+
const clean2 = url.split("?")[0].split("#")[0];
|
|
147
|
+
const ext = clean2.slice(clean2.lastIndexOf(".") + 1).toLowerCase();
|
|
148
148
|
return FORMAT_BY_EXT[ext];
|
|
149
149
|
}
|
|
150
150
|
function sanitizeFamily(family) {
|
|
@@ -178,6 +178,27 @@ var init_font = __esm({
|
|
|
178
178
|
}
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
+
// src/util/logger.ts
|
|
182
|
+
function setLogger(logger) {
|
|
183
|
+
active = logger ?? console;
|
|
184
|
+
}
|
|
185
|
+
function sdkLog(...args) {
|
|
186
|
+
active.log(...args);
|
|
187
|
+
}
|
|
188
|
+
function sdkWarn(...args) {
|
|
189
|
+
(active.warn ?? active.log)(...args);
|
|
190
|
+
}
|
|
191
|
+
function sdkError(...args) {
|
|
192
|
+
(active.error ?? active.log)(...args);
|
|
193
|
+
}
|
|
194
|
+
var active;
|
|
195
|
+
var init_logger = __esm({
|
|
196
|
+
"src/util/logger.ts"() {
|
|
197
|
+
"use strict";
|
|
198
|
+
active = console;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
181
202
|
// src/assets/style.css
|
|
182
203
|
var style_default;
|
|
183
204
|
var init_style = __esm({
|
|
@@ -1431,7 +1452,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
1431
1452
|
let formInstance = null;
|
|
1432
1453
|
try {
|
|
1433
1454
|
if (!import_native.default || typeof import_native.default.form !== "function") {
|
|
1434
|
-
|
|
1455
|
+
sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
|
|
1435
1456
|
setLoading(false);
|
|
1436
1457
|
return;
|
|
1437
1458
|
}
|
|
@@ -1518,7 +1539,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
1518
1539
|
}
|
|
1519
1540
|
}
|
|
1520
1541
|
if (s.startMessage && s.startMessage !== "") {
|
|
1521
|
-
|
|
1542
|
+
sdkLog(s.startMessage);
|
|
1522
1543
|
updateButtons("start");
|
|
1523
1544
|
} else {
|
|
1524
1545
|
updateButtons("in_progress_first");
|
|
@@ -1573,11 +1594,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
1573
1594
|
}
|
|
1574
1595
|
};
|
|
1575
1596
|
formInstance.generate(formDivId, generateOptions).catch((err) => {
|
|
1576
|
-
|
|
1597
|
+
sdkError("[MagicFeedback] Error generating form:", err);
|
|
1577
1598
|
setLoading(false);
|
|
1578
1599
|
});
|
|
1579
1600
|
} catch (e) {
|
|
1580
|
-
|
|
1601
|
+
sdkError("[MagicFeedback] Exception initializing form:", e);
|
|
1581
1602
|
setLoading(false);
|
|
1582
1603
|
}
|
|
1583
1604
|
}
|
|
@@ -1587,6 +1608,7 @@ var init_renderPopup = __esm({
|
|
|
1587
1608
|
"use strict";
|
|
1588
1609
|
init_tracking_manager();
|
|
1589
1610
|
init_font();
|
|
1611
|
+
init_logger();
|
|
1590
1612
|
import_native = __toESM(require("@magicfeedback/native"));
|
|
1591
1613
|
init_style();
|
|
1592
1614
|
}
|
|
@@ -1848,6 +1870,7 @@ function buildSurveyHtml(opts) {
|
|
|
1848
1870
|
}
|
|
1849
1871
|
|
|
1850
1872
|
// src/platform/react-native-renderer.ts
|
|
1873
|
+
init_logger();
|
|
1851
1874
|
var ReactNativePopupRenderer = class {
|
|
1852
1875
|
constructor(options = {}) {
|
|
1853
1876
|
this.options = options;
|
|
@@ -1868,7 +1891,7 @@ var ReactNativePopupRenderer = class {
|
|
|
1868
1891
|
if (this.options.onShow) {
|
|
1869
1892
|
this.options.onShow({ surveyId, productId, html });
|
|
1870
1893
|
} else {
|
|
1871
|
-
|
|
1894
|
+
sdkWarn(
|
|
1872
1895
|
"[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey."
|
|
1873
1896
|
);
|
|
1874
1897
|
}
|
|
@@ -1985,16 +2008,19 @@ function createDefaultRenderer() {
|
|
|
1985
2008
|
init_tracking_manager();
|
|
1986
2009
|
|
|
1987
2010
|
// src/analytics/analytics-manager.ts
|
|
1988
|
-
|
|
1989
|
-
|
|
2011
|
+
function createDryRunSink(log = console.log) {
|
|
2012
|
+
return (payload) => log(
|
|
1990
2013
|
"[DeepdotsAnalytics] (dry-run \xB7 NO enviado \xB7 sin init.analytics) POST /sdk/feedback \u2192",
|
|
1991
2014
|
JSON.stringify(payload, null, 2)
|
|
1992
2015
|
);
|
|
1993
|
-
}
|
|
2016
|
+
}
|
|
2017
|
+
var dryRunSink = createDryRunSink();
|
|
1994
2018
|
var AnalyticsManager = class {
|
|
1995
2019
|
constructor(options = {}) {
|
|
1996
2020
|
this.events = [];
|
|
1997
2021
|
this.attributes = {};
|
|
2022
|
+
/** Métricas del host (valores medibles persistentes). Sobrescriben por key; se reenvían en cada flush. */
|
|
2023
|
+
this.metrics = {};
|
|
1998
2024
|
/** Mini-services activos: nombre → timestamp de entrada. El orden de inserción marca el "más reciente". */
|
|
1999
2025
|
this.activeMiniServices = /* @__PURE__ */ new Map();
|
|
2000
2026
|
this.sink = options.sink ?? dryRunSink;
|
|
@@ -2017,6 +2043,14 @@ var AnalyticsManager = class {
|
|
|
2017
2043
|
this.attributes[k] = String(v);
|
|
2018
2044
|
}
|
|
2019
2045
|
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Registra/actualiza una métrica (valor medible). Persistente: se reenvía en cada flush.
|
|
2048
|
+
* Repetir la misma key SOBRESCRIBE; el valor se coerciona a string; ignora key vacía.
|
|
2049
|
+
*/
|
|
2050
|
+
setMetric(key, value) {
|
|
2051
|
+
if (!key) return;
|
|
2052
|
+
this.metrics[key] = String(value);
|
|
2053
|
+
}
|
|
2020
2054
|
/**
|
|
2021
2055
|
* Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`.
|
|
2022
2056
|
* Admite varios activos a la vez (concurrencia); el "actual" para etiquetar es el más reciente.
|
|
@@ -2080,7 +2114,8 @@ var AnalyticsManager = class {
|
|
|
2080
2114
|
platform: this.platform,
|
|
2081
2115
|
language: this.language,
|
|
2082
2116
|
device: this.device,
|
|
2083
|
-
attributes: { ...this.attributes }
|
|
2117
|
+
attributes: { ...this.attributes },
|
|
2118
|
+
metrics: { ...this.metrics }
|
|
2084
2119
|
},
|
|
2085
2120
|
events: [...this.events]
|
|
2086
2121
|
};
|
|
@@ -2136,10 +2171,15 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
|
2136
2171
|
value: [JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })]
|
|
2137
2172
|
});
|
|
2138
2173
|
}
|
|
2174
|
+
const metrics = [];
|
|
2175
|
+
for (const [k, v] of Object.entries(context.metrics ?? {})) {
|
|
2176
|
+
pushKV(metrics, k, v);
|
|
2177
|
+
}
|
|
2139
2178
|
return {
|
|
2140
2179
|
feedback: {
|
|
2141
2180
|
text: "",
|
|
2142
2181
|
answers: [],
|
|
2182
|
+
metrics,
|
|
2143
2183
|
metadata,
|
|
2144
2184
|
profile,
|
|
2145
2185
|
finished: false
|
|
@@ -2320,20 +2360,95 @@ function collectDeviceInfo(appVersion) {
|
|
|
2320
2360
|
}
|
|
2321
2361
|
|
|
2322
2362
|
// src/analytics/geo-info.ts
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2363
|
+
var GEO_STORAGE_KEY = "deepdots.geo";
|
|
2364
|
+
var GEO_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
2365
|
+
var GEO_TIMEOUT_MS = 3e3;
|
|
2366
|
+
function readCachedGeo(storage, now) {
|
|
2326
2367
|
try {
|
|
2327
|
-
const
|
|
2368
|
+
const raw = storage.getItem(GEO_STORAGE_KEY);
|
|
2369
|
+
if (!raw) return null;
|
|
2370
|
+
const cached = JSON.parse(raw);
|
|
2371
|
+
if (typeof cached.ts !== "number" || now - cached.ts > GEO_TTL_MS) return null;
|
|
2372
|
+
const geo = {};
|
|
2373
|
+
if (cached.country) geo.country = cached.country;
|
|
2374
|
+
if (cached.city) geo.city = cached.city;
|
|
2375
|
+
return geo.country || geo.city ? geo : null;
|
|
2376
|
+
} catch {
|
|
2377
|
+
return null;
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
function writeCachedGeo(storage, geo, now) {
|
|
2381
|
+
try {
|
|
2382
|
+
const payload = { ...geo, ts: now };
|
|
2383
|
+
storage.setItem(GEO_STORAGE_KEY, JSON.stringify(payload));
|
|
2384
|
+
} catch {
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
var DEFAULT_GEO_PROVIDERS = [
|
|
2388
|
+
{
|
|
2389
|
+
// https://ipapi.co/json/
|
|
2390
|
+
url: "https://ipapi.co/json/",
|
|
2391
|
+
parse: (d) => geoFrom(d.country_code, d.city)
|
|
2392
|
+
},
|
|
2393
|
+
{
|
|
2394
|
+
// https://ipwho.is/ (devuelve success:false con 200 en errores)
|
|
2395
|
+
url: "https://ipwho.is/",
|
|
2396
|
+
parse: (d) => d.success === false ? null : geoFrom(d.country_code, d.city)
|
|
2397
|
+
},
|
|
2398
|
+
{
|
|
2399
|
+
// https://ipinfo.io/json (country = código ISO)
|
|
2400
|
+
url: "https://ipinfo.io/json",
|
|
2401
|
+
parse: (d) => geoFrom(d.country, d.city)
|
|
2402
|
+
}
|
|
2403
|
+
];
|
|
2404
|
+
function geoFrom(country, city) {
|
|
2405
|
+
const c = typeof country === "string" && country ? country : void 0;
|
|
2406
|
+
const t = typeof city === "string" && city ? city : void 0;
|
|
2407
|
+
if (!c && !t) return null;
|
|
2408
|
+
return { country: c, city: t };
|
|
2409
|
+
}
|
|
2410
|
+
async function fetchOne(provider, f, timeoutMs) {
|
|
2411
|
+
const controller = new AbortController();
|
|
2412
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2413
|
+
try {
|
|
2414
|
+
const res = await f(provider.url, { signal: controller.signal });
|
|
2328
2415
|
if (!res.ok) return null;
|
|
2329
2416
|
const data = await res.json();
|
|
2330
|
-
|
|
2331
|
-
const city = data.city || void 0;
|
|
2332
|
-
if (!country && !city) return null;
|
|
2333
|
-
return { country, city };
|
|
2417
|
+
return provider.parse(data);
|
|
2334
2418
|
} catch {
|
|
2335
2419
|
return null;
|
|
2420
|
+
} finally {
|
|
2421
|
+
clearTimeout(timer);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
async function fetchGeo(options = {}) {
|
|
2425
|
+
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
2426
|
+
if (!f) return null;
|
|
2427
|
+
const providers = options.providers ?? DEFAULT_GEO_PROVIDERS;
|
|
2428
|
+
const timeoutMs = options.timeoutMs ?? GEO_TIMEOUT_MS;
|
|
2429
|
+
for (const provider of providers) {
|
|
2430
|
+
const geo = await fetchOne(provider, f, timeoutMs);
|
|
2431
|
+
if (geo) return geo;
|
|
2432
|
+
}
|
|
2433
|
+
return null;
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
// src/analytics/language.ts
|
|
2437
|
+
function clean(value) {
|
|
2438
|
+
const trimmed = value?.trim();
|
|
2439
|
+
return trimmed ? trimmed : void 0;
|
|
2440
|
+
}
|
|
2441
|
+
function resolveLanguage(sources) {
|
|
2442
|
+
const explicit = clean(sources.explicit);
|
|
2443
|
+
if (explicit) return explicit;
|
|
2444
|
+
const navLang = clean(sources.navigator?.language);
|
|
2445
|
+
if (navLang) return navLang;
|
|
2446
|
+
try {
|
|
2447
|
+
const locale = clean(sources.intl?.DateTimeFormat().resolvedOptions().locale);
|
|
2448
|
+
if (locale) return locale;
|
|
2449
|
+
} catch {
|
|
2336
2450
|
}
|
|
2451
|
+
return void 0;
|
|
2337
2452
|
}
|
|
2338
2453
|
|
|
2339
2454
|
// src/analytics/engagement-tracker.ts
|
|
@@ -2506,7 +2621,7 @@ var CrashReporter = class {
|
|
|
2506
2621
|
}
|
|
2507
2622
|
/** Instala los handlers globales de errores no capturados (no-op sin `window`). */
|
|
2508
2623
|
install() {
|
|
2509
|
-
if (typeof window === "undefined") return;
|
|
2624
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
|
|
2510
2625
|
window.addEventListener("error", (e) => {
|
|
2511
2626
|
if (!this.isEnabled()) return;
|
|
2512
2627
|
this.captureUnhandled(e.error ?? e.message ?? "error");
|
|
@@ -2530,6 +2645,9 @@ var CrashReporter = class {
|
|
|
2530
2645
|
}
|
|
2531
2646
|
};
|
|
2532
2647
|
|
|
2648
|
+
// src/core/deepdots-popups.ts
|
|
2649
|
+
init_logger();
|
|
2650
|
+
|
|
2533
2651
|
// src/analytics/messaging.ts
|
|
2534
2652
|
function buildMessageParams(stage, o) {
|
|
2535
2653
|
const p = {
|
|
@@ -2558,6 +2676,8 @@ var DeepdotsPopups = class {
|
|
|
2558
2676
|
this.triggers = [];
|
|
2559
2677
|
this.initialized = false;
|
|
2560
2678
|
this.renderer = createDefaultRenderer();
|
|
2679
|
+
/** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
|
|
2680
|
+
this.logger = console;
|
|
2561
2681
|
this.popupContainer = null;
|
|
2562
2682
|
// deprecated: mantenido para compatibilidad interna
|
|
2563
2683
|
this.popupDefinitions = [];
|
|
@@ -2593,6 +2713,8 @@ var DeepdotsPopups = class {
|
|
|
2593
2713
|
}
|
|
2594
2714
|
/** Initialize the SDK with configuration */
|
|
2595
2715
|
init(config) {
|
|
2716
|
+
this.logger = config.logger ?? console;
|
|
2717
|
+
setLogger(this.logger);
|
|
2596
2718
|
if (this.initialized) {
|
|
2597
2719
|
this.log("SDK already initialized");
|
|
2598
2720
|
return;
|
|
@@ -2632,12 +2754,16 @@ var DeepdotsPopups = class {
|
|
|
2632
2754
|
onSessionId: (id) => {
|
|
2633
2755
|
this.analyticsFeedbackSessionId = id;
|
|
2634
2756
|
}
|
|
2635
|
-
}) :
|
|
2757
|
+
}) : createDryRunSink((...a) => this.logger.log(...a));
|
|
2636
2758
|
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
2637
2759
|
this.analytics = new AnalyticsManager({
|
|
2638
2760
|
sink: analyticsSink,
|
|
2639
2761
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
2640
|
-
language:
|
|
2762
|
+
language: resolveLanguage({
|
|
2763
|
+
explicit: config.language,
|
|
2764
|
+
navigator: typeof navigator !== "undefined" ? navigator : void 0,
|
|
2765
|
+
intl: typeof Intl !== "undefined" ? Intl : void 0
|
|
2766
|
+
}),
|
|
2641
2767
|
platform: config.platform ?? "web",
|
|
2642
2768
|
device,
|
|
2643
2769
|
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
@@ -2654,8 +2780,13 @@ var DeepdotsPopups = class {
|
|
|
2654
2780
|
if (this.tracking?.isTrackingEnabled()) {
|
|
2655
2781
|
this.crashReporter.install();
|
|
2656
2782
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2783
|
+
const cachedGeo = readCachedGeo(storage, Date.now());
|
|
2784
|
+
if (cachedGeo) this.analytics.updateDevice(cachedGeo);
|
|
2785
|
+
fetchGeo().then((geo) => {
|
|
2786
|
+
if (geo) {
|
|
2787
|
+
this.analytics?.updateDevice(geo);
|
|
2788
|
+
writeCachedGeo(storage, geo, Date.now());
|
|
2789
|
+
}
|
|
2659
2790
|
}).catch(() => {
|
|
2660
2791
|
});
|
|
2661
2792
|
this.navObserver = new NavigationObserver();
|
|
@@ -2712,6 +2843,15 @@ var DeepdotsPopups = class {
|
|
|
2712
2843
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2713
2844
|
this.analytics?.setUserAttributes(attributes);
|
|
2714
2845
|
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Registra/actualiza una métrica (valor medible) → `feedback.metrics` del body.
|
|
2848
|
+
* Persistente y sobrescribe por key. No-op si tracking off.
|
|
2849
|
+
*/
|
|
2850
|
+
setMetric(key, value) {
|
|
2851
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2852
|
+
this.analytics?.setMetric(key, value);
|
|
2853
|
+
this.log("analytics \xB7 setMetric:", key, value);
|
|
2854
|
+
}
|
|
2715
2855
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
2716
2856
|
reportError(error, options) {
|
|
2717
2857
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -3341,7 +3481,8 @@ var DeepdotsPopups = class {
|
|
|
3341
3481
|
try {
|
|
3342
3482
|
listener(event);
|
|
3343
3483
|
} catch (error) {
|
|
3344
|
-
|
|
3484
|
+
if (this.logger.error) this.logger.error("Error in event listener:", error);
|
|
3485
|
+
else this.logger.log("Error in event listener:", error);
|
|
3345
3486
|
}
|
|
3346
3487
|
});
|
|
3347
3488
|
}
|
|
@@ -3349,7 +3490,7 @@ var DeepdotsPopups = class {
|
|
|
3349
3490
|
/** Log debug messages */
|
|
3350
3491
|
log(...args) {
|
|
3351
3492
|
if (this.config?.debug) {
|
|
3352
|
-
|
|
3493
|
+
this.logger.log("[DeepdotsPopups]", ...args);
|
|
3353
3494
|
}
|
|
3354
3495
|
}
|
|
3355
3496
|
/** External debug method for triggers */
|