@magicfeedback/popup-sdk 1.1.6 → 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/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +123 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +121 -12
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +15 -1
- package/dist/react-native.d.ts +15 -1
- package/dist/react-native.js +123 -14
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +121 -12
- package/dist/react-native.mjs.map +1 -1
- package/package.json +1 -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
|
|
|
@@ -135,6 +138,12 @@ interface DeepdotsInitParams {
|
|
|
135
138
|
storage?: KeyValueStorage;
|
|
136
139
|
/** Platform used in the analytics envelope. Default is 'web'; in RN pass 'android'/'ios'. */
|
|
137
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;
|
|
138
147
|
/** Injectable device info (RN: from react-native-device-info). If missing, it is derived from the browser. */
|
|
139
148
|
device?: DeviceInfo;
|
|
140
149
|
/**
|
|
@@ -336,6 +345,11 @@ declare class DeepdotsPopups {
|
|
|
336
345
|
track(name: string, params?: Record<string, unknown>): void;
|
|
337
346
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
338
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;
|
|
339
353
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
340
354
|
reportError(error: unknown, options?: ReportErrorOptions): void;
|
|
341
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
|
|
|
@@ -135,6 +138,12 @@ interface DeepdotsInitParams {
|
|
|
135
138
|
storage?: KeyValueStorage;
|
|
136
139
|
/** Platform used in the analytics envelope. Default is 'web'; in RN pass 'android'/'ios'. */
|
|
137
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;
|
|
138
147
|
/** Injectable device info (RN: from react-native-device-info). If missing, it is derived from the browser. */
|
|
139
148
|
device?: DeviceInfo;
|
|
140
149
|
/**
|
|
@@ -336,6 +345,11 @@ declare class DeepdotsPopups {
|
|
|
336
345
|
track(name: string, params?: Record<string, unknown>): void;
|
|
337
346
|
/** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */
|
|
338
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;
|
|
339
353
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
340
354
|
reportError(error: unknown, options?: ReportErrorOptions): void;
|
|
341
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) {
|
|
@@ -2019,6 +2019,8 @@ var AnalyticsManager = class {
|
|
|
2019
2019
|
constructor(options = {}) {
|
|
2020
2020
|
this.events = [];
|
|
2021
2021
|
this.attributes = {};
|
|
2022
|
+
/** Métricas del host (valores medibles persistentes). Sobrescriben por key; se reenvían en cada flush. */
|
|
2023
|
+
this.metrics = {};
|
|
2022
2024
|
/** Mini-services activos: nombre → timestamp de entrada. El orden de inserción marca el "más reciente". */
|
|
2023
2025
|
this.activeMiniServices = /* @__PURE__ */ new Map();
|
|
2024
2026
|
this.sink = options.sink ?? dryRunSink;
|
|
@@ -2041,6 +2043,14 @@ var AnalyticsManager = class {
|
|
|
2041
2043
|
this.attributes[k] = String(v);
|
|
2042
2044
|
}
|
|
2043
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
|
+
}
|
|
2044
2054
|
/**
|
|
2045
2055
|
* Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`.
|
|
2046
2056
|
* Admite varios activos a la vez (concurrencia); el "actual" para etiquetar es el más reciente.
|
|
@@ -2104,7 +2114,8 @@ var AnalyticsManager = class {
|
|
|
2104
2114
|
platform: this.platform,
|
|
2105
2115
|
language: this.language,
|
|
2106
2116
|
device: this.device,
|
|
2107
|
-
attributes: { ...this.attributes }
|
|
2117
|
+
attributes: { ...this.attributes },
|
|
2118
|
+
metrics: { ...this.metrics }
|
|
2108
2119
|
},
|
|
2109
2120
|
events: [...this.events]
|
|
2110
2121
|
};
|
|
@@ -2160,10 +2171,15 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
|
2160
2171
|
value: [JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })]
|
|
2161
2172
|
});
|
|
2162
2173
|
}
|
|
2174
|
+
const metrics = [];
|
|
2175
|
+
for (const [k, v] of Object.entries(context.metrics ?? {})) {
|
|
2176
|
+
pushKV(metrics, k, v);
|
|
2177
|
+
}
|
|
2163
2178
|
return {
|
|
2164
2179
|
feedback: {
|
|
2165
2180
|
text: "",
|
|
2166
2181
|
answers: [],
|
|
2182
|
+
metrics,
|
|
2167
2183
|
metadata,
|
|
2168
2184
|
profile,
|
|
2169
2185
|
finished: false
|
|
@@ -2344,20 +2360,95 @@ function collectDeviceInfo(appVersion) {
|
|
|
2344
2360
|
}
|
|
2345
2361
|
|
|
2346
2362
|
// src/analytics/geo-info.ts
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
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) {
|
|
2367
|
+
try {
|
|
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);
|
|
2350
2413
|
try {
|
|
2351
|
-
const res = await f(
|
|
2414
|
+
const res = await f(provider.url, { signal: controller.signal });
|
|
2352
2415
|
if (!res.ok) return null;
|
|
2353
2416
|
const data = await res.json();
|
|
2354
|
-
|
|
2355
|
-
const city = data.city || void 0;
|
|
2356
|
-
if (!country && !city) return null;
|
|
2357
|
-
return { country, city };
|
|
2417
|
+
return provider.parse(data);
|
|
2358
2418
|
} catch {
|
|
2359
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 {
|
|
2360
2450
|
}
|
|
2451
|
+
return void 0;
|
|
2361
2452
|
}
|
|
2362
2453
|
|
|
2363
2454
|
// src/analytics/engagement-tracker.ts
|
|
@@ -2668,7 +2759,11 @@ var DeepdotsPopups = class {
|
|
|
2668
2759
|
this.analytics = new AnalyticsManager({
|
|
2669
2760
|
sink: analyticsSink,
|
|
2670
2761
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
2671
|
-
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
|
+
}),
|
|
2672
2767
|
platform: config.platform ?? "web",
|
|
2673
2768
|
device,
|
|
2674
2769
|
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
@@ -2685,8 +2780,13 @@ var DeepdotsPopups = class {
|
|
|
2685
2780
|
if (this.tracking?.isTrackingEnabled()) {
|
|
2686
2781
|
this.crashReporter.install();
|
|
2687
2782
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
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
|
+
}
|
|
2690
2790
|
}).catch(() => {
|
|
2691
2791
|
});
|
|
2692
2792
|
this.navObserver = new NavigationObserver();
|
|
@@ -2743,6 +2843,15 @@ var DeepdotsPopups = class {
|
|
|
2743
2843
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
2744
2844
|
this.analytics?.setUserAttributes(attributes);
|
|
2745
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
|
+
}
|
|
2746
2855
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
2747
2856
|
reportError(error, options) {
|
|
2748
2857
|
if (!this.tracking?.isTrackingEnabled()) return;
|