@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/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) {
|
|
@@ -181,6 +181,27 @@ var init_font = __esm({
|
|
|
181
181
|
}
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
// src/util/logger.ts
|
|
185
|
+
function setLogger(logger) {
|
|
186
|
+
active = logger ?? console;
|
|
187
|
+
}
|
|
188
|
+
function sdkLog(...args) {
|
|
189
|
+
active.log(...args);
|
|
190
|
+
}
|
|
191
|
+
function sdkWarn(...args) {
|
|
192
|
+
(active.warn ?? active.log)(...args);
|
|
193
|
+
}
|
|
194
|
+
function sdkError(...args) {
|
|
195
|
+
(active.error ?? active.log)(...args);
|
|
196
|
+
}
|
|
197
|
+
var active;
|
|
198
|
+
var init_logger = __esm({
|
|
199
|
+
"src/util/logger.ts"() {
|
|
200
|
+
"use strict";
|
|
201
|
+
active = console;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
184
205
|
// node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js
|
|
185
206
|
var require_magicfeedback_sdk_browser = __commonJS({
|
|
186
207
|
"node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"(exports, module2) {
|
|
@@ -3359,7 +3380,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3359
3380
|
let formInstance = null;
|
|
3360
3381
|
try {
|
|
3361
3382
|
if (!import_native.default || typeof import_native.default.form !== "function") {
|
|
3362
|
-
|
|
3383
|
+
sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
|
|
3363
3384
|
setLoading(false);
|
|
3364
3385
|
return;
|
|
3365
3386
|
}
|
|
@@ -3446,7 +3467,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3446
3467
|
}
|
|
3447
3468
|
}
|
|
3448
3469
|
if (s.startMessage && s.startMessage !== "") {
|
|
3449
|
-
|
|
3470
|
+
sdkLog(s.startMessage);
|
|
3450
3471
|
updateButtons("start");
|
|
3451
3472
|
} else {
|
|
3452
3473
|
updateButtons("in_progress_first");
|
|
@@ -3501,11 +3522,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3501
3522
|
}
|
|
3502
3523
|
};
|
|
3503
3524
|
formInstance.generate(formDivId, generateOptions).catch((err) => {
|
|
3504
|
-
|
|
3525
|
+
sdkError("[MagicFeedback] Error generating form:", err);
|
|
3505
3526
|
setLoading(false);
|
|
3506
3527
|
});
|
|
3507
3528
|
} catch (e) {
|
|
3508
|
-
|
|
3529
|
+
sdkError("[MagicFeedback] Exception initializing form:", e);
|
|
3509
3530
|
setLoading(false);
|
|
3510
3531
|
}
|
|
3511
3532
|
}
|
|
@@ -3515,6 +3536,7 @@ var init_renderPopup = __esm({
|
|
|
3515
3536
|
"use strict";
|
|
3516
3537
|
init_tracking_manager();
|
|
3517
3538
|
init_font();
|
|
3539
|
+
init_logger();
|
|
3518
3540
|
import_native = __toESM(require_magicfeedback_sdk_browser());
|
|
3519
3541
|
init_style();
|
|
3520
3542
|
}
|
|
@@ -3786,6 +3808,7 @@ function buildSurveyHtml(opts) {
|
|
|
3786
3808
|
}
|
|
3787
3809
|
|
|
3788
3810
|
// src/platform/react-native-renderer.ts
|
|
3811
|
+
init_logger();
|
|
3789
3812
|
var ReactNativePopupRenderer = class {
|
|
3790
3813
|
constructor(options = {}) {
|
|
3791
3814
|
this.options = options;
|
|
@@ -3806,7 +3829,7 @@ var ReactNativePopupRenderer = class {
|
|
|
3806
3829
|
if (this.options.onShow) {
|
|
3807
3830
|
this.options.onShow({ surveyId, productId, html });
|
|
3808
3831
|
} else {
|
|
3809
|
-
|
|
3832
|
+
sdkWarn(
|
|
3810
3833
|
"[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey."
|
|
3811
3834
|
);
|
|
3812
3835
|
}
|
|
@@ -3923,16 +3946,19 @@ function createDefaultRenderer() {
|
|
|
3923
3946
|
init_tracking_manager();
|
|
3924
3947
|
|
|
3925
3948
|
// src/analytics/analytics-manager.ts
|
|
3926
|
-
|
|
3927
|
-
|
|
3949
|
+
function createDryRunSink(log = console.log) {
|
|
3950
|
+
return (payload) => log(
|
|
3928
3951
|
"[DeepdotsAnalytics] (dry-run \xB7 NO enviado \xB7 sin init.analytics) POST /sdk/feedback \u2192",
|
|
3929
3952
|
JSON.stringify(payload, null, 2)
|
|
3930
3953
|
);
|
|
3931
|
-
}
|
|
3954
|
+
}
|
|
3955
|
+
var dryRunSink = createDryRunSink();
|
|
3932
3956
|
var AnalyticsManager = class {
|
|
3933
3957
|
constructor(options = {}) {
|
|
3934
3958
|
this.events = [];
|
|
3935
3959
|
this.attributes = {};
|
|
3960
|
+
/** Métricas del host (valores medibles persistentes). Sobrescriben por key; se reenvían en cada flush. */
|
|
3961
|
+
this.metrics = {};
|
|
3936
3962
|
/** Mini-services activos: nombre → timestamp de entrada. El orden de inserción marca el "más reciente". */
|
|
3937
3963
|
this.activeMiniServices = /* @__PURE__ */ new Map();
|
|
3938
3964
|
this.sink = options.sink ?? dryRunSink;
|
|
@@ -3955,6 +3981,14 @@ var AnalyticsManager = class {
|
|
|
3955
3981
|
this.attributes[k] = String(v);
|
|
3956
3982
|
}
|
|
3957
3983
|
}
|
|
3984
|
+
/**
|
|
3985
|
+
* Registra/actualiza una métrica (valor medible). Persistente: se reenvía en cada flush.
|
|
3986
|
+
* Repetir la misma key SOBRESCRIBE; el valor se coerciona a string; ignora key vacía.
|
|
3987
|
+
*/
|
|
3988
|
+
setMetric(key, value) {
|
|
3989
|
+
if (!key) return;
|
|
3990
|
+
this.metrics[key] = String(value);
|
|
3991
|
+
}
|
|
3958
3992
|
/**
|
|
3959
3993
|
* Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`.
|
|
3960
3994
|
* Admite varios activos a la vez (concurrencia); el "actual" para etiquetar es el más reciente.
|
|
@@ -4018,7 +4052,8 @@ var AnalyticsManager = class {
|
|
|
4018
4052
|
platform: this.platform,
|
|
4019
4053
|
language: this.language,
|
|
4020
4054
|
device: this.device,
|
|
4021
|
-
attributes: { ...this.attributes }
|
|
4055
|
+
attributes: { ...this.attributes },
|
|
4056
|
+
metrics: { ...this.metrics }
|
|
4022
4057
|
},
|
|
4023
4058
|
events: [...this.events]
|
|
4024
4059
|
};
|
|
@@ -4074,10 +4109,15 @@ function buildAnalyticsFeedbackBody(envelope, keys, feedbackSessionId) {
|
|
|
4074
4109
|
value: [JSON.stringify({ timestamp: e.timestamp, ...e.params ?? {} })]
|
|
4075
4110
|
});
|
|
4076
4111
|
}
|
|
4112
|
+
const metrics = [];
|
|
4113
|
+
for (const [k, v] of Object.entries(context.metrics ?? {})) {
|
|
4114
|
+
pushKV(metrics, k, v);
|
|
4115
|
+
}
|
|
4077
4116
|
return {
|
|
4078
4117
|
feedback: {
|
|
4079
4118
|
text: "",
|
|
4080
4119
|
answers: [],
|
|
4120
|
+
metrics,
|
|
4081
4121
|
metadata,
|
|
4082
4122
|
profile,
|
|
4083
4123
|
finished: false
|
|
@@ -4258,20 +4298,95 @@ function collectDeviceInfo(appVersion) {
|
|
|
4258
4298
|
}
|
|
4259
4299
|
|
|
4260
4300
|
// src/analytics/geo-info.ts
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4301
|
+
var GEO_STORAGE_KEY = "deepdots.geo";
|
|
4302
|
+
var GEO_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4303
|
+
var GEO_TIMEOUT_MS = 3e3;
|
|
4304
|
+
function readCachedGeo(storage, now) {
|
|
4264
4305
|
try {
|
|
4265
|
-
const
|
|
4306
|
+
const raw = storage.getItem(GEO_STORAGE_KEY);
|
|
4307
|
+
if (!raw) return null;
|
|
4308
|
+
const cached = JSON.parse(raw);
|
|
4309
|
+
if (typeof cached.ts !== "number" || now - cached.ts > GEO_TTL_MS) return null;
|
|
4310
|
+
const geo = {};
|
|
4311
|
+
if (cached.country) geo.country = cached.country;
|
|
4312
|
+
if (cached.city) geo.city = cached.city;
|
|
4313
|
+
return geo.country || geo.city ? geo : null;
|
|
4314
|
+
} catch {
|
|
4315
|
+
return null;
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
function writeCachedGeo(storage, geo, now) {
|
|
4319
|
+
try {
|
|
4320
|
+
const payload = { ...geo, ts: now };
|
|
4321
|
+
storage.setItem(GEO_STORAGE_KEY, JSON.stringify(payload));
|
|
4322
|
+
} catch {
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
var DEFAULT_GEO_PROVIDERS = [
|
|
4326
|
+
{
|
|
4327
|
+
// https://ipapi.co/json/
|
|
4328
|
+
url: "https://ipapi.co/json/",
|
|
4329
|
+
parse: (d) => geoFrom(d.country_code, d.city)
|
|
4330
|
+
},
|
|
4331
|
+
{
|
|
4332
|
+
// https://ipwho.is/ (devuelve success:false con 200 en errores)
|
|
4333
|
+
url: "https://ipwho.is/",
|
|
4334
|
+
parse: (d) => d.success === false ? null : geoFrom(d.country_code, d.city)
|
|
4335
|
+
},
|
|
4336
|
+
{
|
|
4337
|
+
// https://ipinfo.io/json (country = código ISO)
|
|
4338
|
+
url: "https://ipinfo.io/json",
|
|
4339
|
+
parse: (d) => geoFrom(d.country, d.city)
|
|
4340
|
+
}
|
|
4341
|
+
];
|
|
4342
|
+
function geoFrom(country, city) {
|
|
4343
|
+
const c = typeof country === "string" && country ? country : void 0;
|
|
4344
|
+
const t = typeof city === "string" && city ? city : void 0;
|
|
4345
|
+
if (!c && !t) return null;
|
|
4346
|
+
return { country: c, city: t };
|
|
4347
|
+
}
|
|
4348
|
+
async function fetchOne(provider, f, timeoutMs) {
|
|
4349
|
+
const controller = new AbortController();
|
|
4350
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
4351
|
+
try {
|
|
4352
|
+
const res = await f(provider.url, { signal: controller.signal });
|
|
4266
4353
|
if (!res.ok) return null;
|
|
4267
4354
|
const data = await res.json();
|
|
4268
|
-
|
|
4269
|
-
const city = data.city || void 0;
|
|
4270
|
-
if (!country && !city) return null;
|
|
4271
|
-
return { country, city };
|
|
4355
|
+
return provider.parse(data);
|
|
4272
4356
|
} catch {
|
|
4273
4357
|
return null;
|
|
4358
|
+
} finally {
|
|
4359
|
+
clearTimeout(timer);
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
async function fetchGeo(options = {}) {
|
|
4363
|
+
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
4364
|
+
if (!f) return null;
|
|
4365
|
+
const providers = options.providers ?? DEFAULT_GEO_PROVIDERS;
|
|
4366
|
+
const timeoutMs = options.timeoutMs ?? GEO_TIMEOUT_MS;
|
|
4367
|
+
for (const provider of providers) {
|
|
4368
|
+
const geo = await fetchOne(provider, f, timeoutMs);
|
|
4369
|
+
if (geo) return geo;
|
|
4370
|
+
}
|
|
4371
|
+
return null;
|
|
4372
|
+
}
|
|
4373
|
+
|
|
4374
|
+
// src/analytics/language.ts
|
|
4375
|
+
function clean(value) {
|
|
4376
|
+
const trimmed = value?.trim();
|
|
4377
|
+
return trimmed ? trimmed : void 0;
|
|
4378
|
+
}
|
|
4379
|
+
function resolveLanguage(sources) {
|
|
4380
|
+
const explicit = clean(sources.explicit);
|
|
4381
|
+
if (explicit) return explicit;
|
|
4382
|
+
const navLang = clean(sources.navigator?.language);
|
|
4383
|
+
if (navLang) return navLang;
|
|
4384
|
+
try {
|
|
4385
|
+
const locale = clean(sources.intl?.DateTimeFormat().resolvedOptions().locale);
|
|
4386
|
+
if (locale) return locale;
|
|
4387
|
+
} catch {
|
|
4274
4388
|
}
|
|
4389
|
+
return void 0;
|
|
4275
4390
|
}
|
|
4276
4391
|
|
|
4277
4392
|
// src/analytics/engagement-tracker.ts
|
|
@@ -4444,7 +4559,7 @@ var CrashReporter = class {
|
|
|
4444
4559
|
}
|
|
4445
4560
|
/** Instala los handlers globales de errores no capturados (no-op sin `window`). */
|
|
4446
4561
|
install() {
|
|
4447
|
-
if (typeof window === "undefined") return;
|
|
4562
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
|
|
4448
4563
|
window.addEventListener("error", (e) => {
|
|
4449
4564
|
if (!this.isEnabled()) return;
|
|
4450
4565
|
this.captureUnhandled(e.error ?? e.message ?? "error");
|
|
@@ -4468,6 +4583,9 @@ var CrashReporter = class {
|
|
|
4468
4583
|
}
|
|
4469
4584
|
};
|
|
4470
4585
|
|
|
4586
|
+
// src/core/deepdots-popups.ts
|
|
4587
|
+
init_logger();
|
|
4588
|
+
|
|
4471
4589
|
// src/analytics/messaging.ts
|
|
4472
4590
|
function buildMessageParams(stage, o) {
|
|
4473
4591
|
const p = {
|
|
@@ -4496,6 +4614,8 @@ var DeepdotsPopups = class {
|
|
|
4496
4614
|
this.triggers = [];
|
|
4497
4615
|
this.initialized = false;
|
|
4498
4616
|
this.renderer = createDefaultRenderer();
|
|
4617
|
+
/** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
|
|
4618
|
+
this.logger = console;
|
|
4499
4619
|
this.popupContainer = null;
|
|
4500
4620
|
// deprecated: mantenido para compatibilidad interna
|
|
4501
4621
|
this.popupDefinitions = [];
|
|
@@ -4531,6 +4651,8 @@ var DeepdotsPopups = class {
|
|
|
4531
4651
|
}
|
|
4532
4652
|
/** Initialize the SDK with configuration */
|
|
4533
4653
|
init(config) {
|
|
4654
|
+
this.logger = config.logger ?? console;
|
|
4655
|
+
setLogger(this.logger);
|
|
4534
4656
|
if (this.initialized) {
|
|
4535
4657
|
this.log("SDK already initialized");
|
|
4536
4658
|
return;
|
|
@@ -4570,12 +4692,16 @@ var DeepdotsPopups = class {
|
|
|
4570
4692
|
onSessionId: (id) => {
|
|
4571
4693
|
this.analyticsFeedbackSessionId = id;
|
|
4572
4694
|
}
|
|
4573
|
-
}) :
|
|
4695
|
+
}) : createDryRunSink((...a) => this.logger.log(...a));
|
|
4574
4696
|
const device = config.device ?? collectDeviceInfo(config.appVersion);
|
|
4575
4697
|
this.analytics = new AnalyticsManager({
|
|
4576
4698
|
sink: analyticsSink,
|
|
4577
4699
|
publicKey: config.analytics?.publicKey ?? this.config.apiKey,
|
|
4578
|
-
language:
|
|
4700
|
+
language: resolveLanguage({
|
|
4701
|
+
explicit: config.language,
|
|
4702
|
+
navigator: typeof navigator !== "undefined" ? navigator : void 0,
|
|
4703
|
+
intl: typeof Intl !== "undefined" ? Intl : void 0
|
|
4704
|
+
}),
|
|
4579
4705
|
platform: config.platform ?? "web",
|
|
4580
4706
|
device,
|
|
4581
4707
|
maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,
|
|
@@ -4592,8 +4718,13 @@ var DeepdotsPopups = class {
|
|
|
4592
4718
|
if (this.tracking?.isTrackingEnabled()) {
|
|
4593
4719
|
this.crashReporter.install();
|
|
4594
4720
|
}
|
|
4595
|
-
|
|
4596
|
-
|
|
4721
|
+
const cachedGeo = readCachedGeo(storage, Date.now());
|
|
4722
|
+
if (cachedGeo) this.analytics.updateDevice(cachedGeo);
|
|
4723
|
+
fetchGeo().then((geo) => {
|
|
4724
|
+
if (geo) {
|
|
4725
|
+
this.analytics?.updateDevice(geo);
|
|
4726
|
+
writeCachedGeo(storage, geo, Date.now());
|
|
4727
|
+
}
|
|
4597
4728
|
}).catch(() => {
|
|
4598
4729
|
});
|
|
4599
4730
|
this.navObserver = new NavigationObserver();
|
|
@@ -4650,6 +4781,15 @@ var DeepdotsPopups = class {
|
|
|
4650
4781
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4651
4782
|
this.analytics?.setUserAttributes(attributes);
|
|
4652
4783
|
}
|
|
4784
|
+
/**
|
|
4785
|
+
* Registra/actualiza una métrica (valor medible) → `feedback.metrics` del body.
|
|
4786
|
+
* Persistente y sobrescribe por key. No-op si tracking off.
|
|
4787
|
+
*/
|
|
4788
|
+
setMetric(key, value) {
|
|
4789
|
+
if (!this.tracking?.isTrackingEnabled()) return;
|
|
4790
|
+
this.analytics?.setMetric(key, value);
|
|
4791
|
+
this.log("analytics \xB7 setMetric:", key, value);
|
|
4792
|
+
}
|
|
4653
4793
|
/** Reporta un error del host (manejado o no) → evento `deepdots_app_crash`. No-op si tracking off. */
|
|
4654
4794
|
reportError(error, options) {
|
|
4655
4795
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
@@ -5279,7 +5419,8 @@ var DeepdotsPopups = class {
|
|
|
5279
5419
|
try {
|
|
5280
5420
|
listener(event);
|
|
5281
5421
|
} catch (error) {
|
|
5282
|
-
|
|
5422
|
+
if (this.logger.error) this.logger.error("Error in event listener:", error);
|
|
5423
|
+
else this.logger.log("Error in event listener:", error);
|
|
5283
5424
|
}
|
|
5284
5425
|
});
|
|
5285
5426
|
}
|
|
@@ -5287,7 +5428,7 @@ var DeepdotsPopups = class {
|
|
|
5287
5428
|
/** Log debug messages */
|
|
5288
5429
|
log(...args) {
|
|
5289
5430
|
if (this.config?.debug) {
|
|
5290
|
-
|
|
5431
|
+
this.logger.log("[DeepdotsPopups]", ...args);
|
|
5291
5432
|
}
|
|
5292
5433
|
}
|
|
5293
5434
|
/** External debug method for triggers */
|