@magicfeedback/popup-sdk 1.16.0 → 1.17.0
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 +66 -24
- package/dist/index.d.ts +66 -24
- package/dist/index.js +192 -82
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -82
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +21 -5
- package/dist/react-native.d.ts +21 -5
- package/dist/react-native.js +192 -82
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +192 -82
- package/dist/react-native.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7706,97 +7706,166 @@ async function safeBodyText(res) {
|
|
|
7706
7706
|
return "";
|
|
7707
7707
|
}
|
|
7708
7708
|
}
|
|
7709
|
+
var PENDING_MAX = 50;
|
|
7710
|
+
var PENDING_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
7711
|
+
var POST_TIMEOUT_MS = 2e4;
|
|
7712
|
+
function pendingKey(integration) {
|
|
7713
|
+
return `__deepdots_analytics_pending__:${integration}`;
|
|
7714
|
+
}
|
|
7715
|
+
function generateSessionId() {
|
|
7716
|
+
const c = globalThis.crypto;
|
|
7717
|
+
if (typeof c?.randomUUID === "function") return c.randomUUID();
|
|
7718
|
+
const b = new Uint8Array(16);
|
|
7719
|
+
if (typeof c?.getRandomValues === "function") c.getRandomValues(b);
|
|
7720
|
+
else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);
|
|
7721
|
+
b[6] = b[6] & 15 | 64;
|
|
7722
|
+
b[8] = b[8] & 63 | 128;
|
|
7723
|
+
const h = Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
7724
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
7725
|
+
}
|
|
7709
7726
|
function createFeedbackSink(options) {
|
|
7710
|
-
let feedbackSessionId = options.initialSessionId || void 0;
|
|
7711
|
-
let firstPostInFlight = null;
|
|
7712
|
-
let generation = 0;
|
|
7713
7727
|
const url = `${options.baseUrl}/sdk/feedback`;
|
|
7714
|
-
const
|
|
7715
|
-
|
|
7728
|
+
const newSessionId = options.newSessionId ?? generateSessionId;
|
|
7729
|
+
const now = options.now ?? (() => Date.now());
|
|
7730
|
+
const storageKey = pendingKey(options.keys.integration);
|
|
7731
|
+
let feedbackSessionId = options.initialSessionId || void 0;
|
|
7732
|
+
let announcedId = feedbackSessionId;
|
|
7733
|
+
const pending2 = loadPending();
|
|
7734
|
+
let draining = null;
|
|
7735
|
+
function loadPending() {
|
|
7736
|
+
if (!options.storage) return [];
|
|
7737
|
+
try {
|
|
7738
|
+
const raw = options.storage.getItem(storageKey);
|
|
7739
|
+
const list = raw ? JSON.parse(raw) : [];
|
|
7740
|
+
return Array.isArray(list) ? list.filter((p) => p?.body && now() - p.at <= PENDING_MAX_AGE_MS) : [];
|
|
7741
|
+
} catch {
|
|
7742
|
+
return [];
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
function savePending() {
|
|
7746
|
+
if (!options.storage) return;
|
|
7747
|
+
try {
|
|
7748
|
+
if (pending2.length) options.storage.setItem(storageKey, JSON.stringify(pending2));
|
|
7749
|
+
else options.storage.removeItem(storageKey);
|
|
7750
|
+
} catch {
|
|
7751
|
+
}
|
|
7752
|
+
}
|
|
7753
|
+
function addPending(batch) {
|
|
7754
|
+
pending2.push(batch);
|
|
7755
|
+
if (pending2.length > PENDING_MAX) pending2.splice(0, pending2.length - PENDING_MAX);
|
|
7756
|
+
savePending();
|
|
7757
|
+
}
|
|
7758
|
+
function removePending(batch) {
|
|
7759
|
+
const i = pending2.indexOf(batch);
|
|
7760
|
+
if (i >= 0) pending2.splice(i, 1);
|
|
7761
|
+
savePending();
|
|
7762
|
+
}
|
|
7763
|
+
async function post(body, final) {
|
|
7716
7764
|
const json = JSON.stringify(body);
|
|
7717
7765
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
7718
7766
|
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
7719
7767
|
const f = options.fetchImpl ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
7720
7768
|
if (!f) {
|
|
7721
7769
|
options.log?.("[DeepdotsAnalytics] no fetch available; payload not sent", body);
|
|
7722
|
-
return;
|
|
7770
|
+
return "drop";
|
|
7723
7771
|
}
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7772
|
+
let timer;
|
|
7773
|
+
try {
|
|
7774
|
+
const request = f(url, {
|
|
7775
|
+
method: "POST",
|
|
7776
|
+
headers: { "Content-Type": "application/json" },
|
|
7777
|
+
body: json,
|
|
7778
|
+
// La API autentica por `publicKey` en el body y responde `Allow-Origin: *`: una
|
|
7779
|
+
// petición con credenciales fallaría el CORS. Explícito para no depender del default.
|
|
7780
|
+
credentials: "omit",
|
|
7781
|
+
// keepalive: el POST sobrevive a la navegación mientras el body sea pequeño.
|
|
7782
|
+
...small ? { keepalive: true } : {}
|
|
7783
|
+
});
|
|
7784
|
+
const timeout = new Promise((_, reject) => {
|
|
7785
|
+
timer = setTimeout(() => reject(new Error("timeout")), POST_TIMEOUT_MS);
|
|
7786
|
+
});
|
|
7787
|
+
const res = await Promise.race([request, timeout]);
|
|
7788
|
+
if (res.ok) return "ok";
|
|
7735
7789
|
const detail = await safeBodyText(res);
|
|
7736
7790
|
if (isRetryableStatus(res.status)) {
|
|
7737
|
-
|
|
7791
|
+
options.log?.(`[DeepdotsAnalytics] POST /sdk/feedback ${res.status} (retryable): ${detail}`);
|
|
7792
|
+
return "retry";
|
|
7738
7793
|
}
|
|
7739
7794
|
options.log?.(
|
|
7740
7795
|
`[DeepdotsAnalytics] POST /sdk/feedback rejected with ${res.status}; batch DISCARDED:`,
|
|
7741
7796
|
detail
|
|
7742
7797
|
);
|
|
7743
|
-
return;
|
|
7744
|
-
}
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
closedId = data?.sessionId ?? closedId;
|
|
7750
|
-
} catch {
|
|
7751
|
-
}
|
|
7752
|
-
try {
|
|
7753
|
-
if (closedId) options.onSessionClosed?.(closedId);
|
|
7754
|
-
} catch {
|
|
7755
|
-
}
|
|
7756
|
-
return;
|
|
7798
|
+
return "drop";
|
|
7799
|
+
} catch (error) {
|
|
7800
|
+
options.log?.("[DeepdotsAnalytics] POST /sdk/feedback failed (will retry):", error);
|
|
7801
|
+
return "retry";
|
|
7802
|
+
} finally {
|
|
7803
|
+
clearTimeout(timer);
|
|
7757
7804
|
}
|
|
7805
|
+
}
|
|
7806
|
+
function delivered(body) {
|
|
7807
|
+
const id = body.sessionId;
|
|
7808
|
+
if (!id) return;
|
|
7758
7809
|
try {
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
feedbackSessionId = data.sessionId;
|
|
7766
|
-
options.log?.("[DeepdotsAnalytics] feedbackSessionId cached:", feedbackSessionId);
|
|
7767
|
-
options.onSessionId?.(feedbackSessionId);
|
|
7810
|
+
if (body.completed) {
|
|
7811
|
+
options.onSessionClosed?.(id);
|
|
7812
|
+
} else if (id === feedbackSessionId && id !== announcedId) {
|
|
7813
|
+
announcedId = id;
|
|
7814
|
+
options.log?.("[DeepdotsAnalytics] feedback session open:", id);
|
|
7815
|
+
options.onSessionId?.(id);
|
|
7768
7816
|
}
|
|
7769
7817
|
} catch {
|
|
7770
7818
|
}
|
|
7771
|
-
}
|
|
7819
|
+
}
|
|
7820
|
+
async function send(batch, final, alreadyPending) {
|
|
7821
|
+
const result = await post(batch.body, final);
|
|
7822
|
+
if (result === "retry") {
|
|
7823
|
+
if (!alreadyPending) addPending(batch);
|
|
7824
|
+
return;
|
|
7825
|
+
}
|
|
7826
|
+
if (alreadyPending) removePending(batch);
|
|
7827
|
+
if (result === "ok") delivered(batch.body);
|
|
7828
|
+
}
|
|
7829
|
+
function drain() {
|
|
7830
|
+
const run = async () => {
|
|
7831
|
+
for (let i = 0; i < pending2.length; ) {
|
|
7832
|
+
const batch = pending2[i];
|
|
7833
|
+
await send(batch, false, true);
|
|
7834
|
+
if (pending2[i] === batch) return;
|
|
7835
|
+
}
|
|
7836
|
+
};
|
|
7837
|
+
const next = draining ? draining.then(run) : run();
|
|
7838
|
+
draining = next;
|
|
7839
|
+
void next.finally(() => {
|
|
7840
|
+
if (draining === next) draining = null;
|
|
7841
|
+
});
|
|
7842
|
+
return next;
|
|
7843
|
+
}
|
|
7844
|
+
if (pending2.length) {
|
|
7845
|
+
savePending();
|
|
7846
|
+
setTimeout(() => void drain(), 0);
|
|
7847
|
+
}
|
|
7772
7848
|
return async (envelope, meta) => {
|
|
7773
7849
|
if (meta?.resetSession) {
|
|
7774
7850
|
feedbackSessionId = void 0;
|
|
7775
|
-
firstPostInFlight = null;
|
|
7776
|
-
generation++;
|
|
7777
7851
|
options.onSessionReset?.();
|
|
7778
7852
|
return;
|
|
7779
7853
|
}
|
|
7780
7854
|
const final = meta?.final === true;
|
|
7781
7855
|
const sessionEnd = meta?.sessionEnd === true;
|
|
7782
|
-
if (!feedbackSessionId
|
|
7783
|
-
await firstPostInFlight;
|
|
7784
|
-
}
|
|
7856
|
+
if (!feedbackSessionId) feedbackSessionId = newSessionId();
|
|
7785
7857
|
const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId, { sessionEnd });
|
|
7786
7858
|
options.log?.("[DeepdotsAnalytics] POST /sdk/feedback \u2192", body);
|
|
7787
|
-
const
|
|
7859
|
+
const batch = { body, at: now() };
|
|
7788
7860
|
if (sessionEnd) {
|
|
7789
7861
|
feedbackSessionId = void 0;
|
|
7790
|
-
firstPostInFlight = null;
|
|
7791
|
-
generation++;
|
|
7792
7862
|
options.onSessionReset?.();
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
() => void 0
|
|
7797
|
-
);
|
|
7863
|
+
addPending(batch);
|
|
7864
|
+
await drain();
|
|
7865
|
+
return;
|
|
7798
7866
|
}
|
|
7799
|
-
|
|
7867
|
+
const retries = pending2.length ? drain() : null;
|
|
7868
|
+
await Promise.all([retries, send(batch, final, false)]);
|
|
7800
7869
|
};
|
|
7801
7870
|
}
|
|
7802
7871
|
|
|
@@ -8154,12 +8223,12 @@ var EngagementTracker = class {
|
|
|
8154
8223
|
|
|
8155
8224
|
// src/contact/contact-manager.ts
|
|
8156
8225
|
init_tracking_manager();
|
|
8157
|
-
function stableSerialize(attrs) {
|
|
8226
|
+
function stableSerialize(userId, attrs) {
|
|
8158
8227
|
const sorted = {};
|
|
8159
8228
|
for (const key of Object.keys(attrs).sort()) {
|
|
8160
8229
|
sorted[key] = attrs[key];
|
|
8161
8230
|
}
|
|
8162
|
-
return JSON.stringify(sorted);
|
|
8231
|
+
return JSON.stringify({ userId, attributes: sorted });
|
|
8163
8232
|
}
|
|
8164
8233
|
var ContactManager = class {
|
|
8165
8234
|
constructor(options) {
|
|
@@ -8169,18 +8238,23 @@ var ContactManager = class {
|
|
|
8169
8238
|
this.post = options.post;
|
|
8170
8239
|
}
|
|
8171
8240
|
/**
|
|
8172
|
-
* Envía los atributos si cambiaron respecto a lo último enviado.
|
|
8173
|
-
*
|
|
8241
|
+
* Envía los atributos si cambiaron respecto a lo último enviado. La caché solo se guarda
|
|
8242
|
+
* cuando la API acepta el envío; si falla, la llamada siguiente reintenta. Nunca rechaza.
|
|
8243
|
+
* @returns `true` si se envió, `false` si no hubo cambios o el envío falló.
|
|
8174
8244
|
*/
|
|
8175
8245
|
async setAttributes(attributes) {
|
|
8176
|
-
const serialized = stableSerialize(attributes);
|
|
8246
|
+
const serialized = stableSerialize(this.userId, attributes);
|
|
8177
8247
|
const last = this.storage.getItem(STORAGE_KEYS.contactAttributes);
|
|
8178
8248
|
if (last === serialized) return false;
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8249
|
+
try {
|
|
8250
|
+
await this.post({
|
|
8251
|
+
publicKey: this.publicKey,
|
|
8252
|
+
userId: this.userId,
|
|
8253
|
+
userAttributes: attributes
|
|
8254
|
+
});
|
|
8255
|
+
} catch {
|
|
8256
|
+
return false;
|
|
8257
|
+
}
|
|
8184
8258
|
this.storage.setItem(STORAGE_KEYS.contactAttributes, serialized);
|
|
8185
8259
|
return true;
|
|
8186
8260
|
}
|
|
@@ -8211,6 +8285,10 @@ function crashRecordToParams(r) {
|
|
|
8211
8285
|
}
|
|
8212
8286
|
var CrashReporter = class {
|
|
8213
8287
|
constructor(options) {
|
|
8288
|
+
/** Los handlers de `window` ya están puestos: `install()` es idempotente. */
|
|
8289
|
+
this.windowInstalled = false;
|
|
8290
|
+
/** ErrorUtils ya enganchados: engancharlos dos veces capturaría cada error dos veces. */
|
|
8291
|
+
this.hookedErrorUtils = /* @__PURE__ */ new Set();
|
|
8214
8292
|
this.options = options;
|
|
8215
8293
|
this.now = options.now ?? (() => Date.now());
|
|
8216
8294
|
}
|
|
@@ -8286,9 +8364,11 @@ var CrashReporter = class {
|
|
|
8286
8364
|
}
|
|
8287
8365
|
return queue;
|
|
8288
8366
|
}
|
|
8289
|
-
/** Instala los handlers globales de errores no capturados (no-op sin `window`). */
|
|
8367
|
+
/** Instala los handlers globales de errores no capturados (no-op sin `window`). Idempotente. */
|
|
8290
8368
|
install() {
|
|
8369
|
+
if (this.windowInstalled) return;
|
|
8291
8370
|
if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
|
|
8371
|
+
this.windowInstalled = true;
|
|
8292
8372
|
window.addEventListener("error", (e) => {
|
|
8293
8373
|
if (!this.isEnabled()) return;
|
|
8294
8374
|
this.captureUnhandled(e.error ?? e.message ?? "error");
|
|
@@ -8298,8 +8378,10 @@ var CrashReporter = class {
|
|
|
8298
8378
|
this.captureUnhandled(e.reason ?? "unhandledrejection");
|
|
8299
8379
|
});
|
|
8300
8380
|
}
|
|
8301
|
-
/** Engancha `global.ErrorUtils` de RN (no hay `window`): persiste y encadena al handler previo. */
|
|
8381
|
+
/** Engancha `global.ErrorUtils` de RN (no hay `window`): persiste y encadena al handler previo. Idempotente por instancia de ErrorUtils. */
|
|
8302
8382
|
installReactNative(errorUtils) {
|
|
8383
|
+
if (this.hookedErrorUtils.has(errorUtils)) return;
|
|
8384
|
+
this.hookedErrorUtils.add(errorUtils);
|
|
8303
8385
|
const previous = errorUtils.getGlobalHandler?.();
|
|
8304
8386
|
errorUtils.setGlobalHandler((error, isFatal) => {
|
|
8305
8387
|
if (this.isEnabled()) this.captureUnhandled(error);
|
|
@@ -8515,6 +8597,8 @@ var DeepdotsPopups = class {
|
|
|
8515
8597
|
this.analyticsFlushTimer = void 0;
|
|
8516
8598
|
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
8517
8599
|
this.crashReporter = null;
|
|
8600
|
+
/** ErrorUtils de RN recibido del host (o del Provider); se engancha en cuanto haya consentimiento. */
|
|
8601
|
+
this.rnErrorUtils = null;
|
|
8518
8602
|
/** Protecciones del funnel de Messaging (#18–22). Vigencia de sesión: se reinicia en init(). */
|
|
8519
8603
|
this.messageGuard = new MessageGuard();
|
|
8520
8604
|
/** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */
|
|
@@ -8615,6 +8699,10 @@ var DeepdotsPopups = class {
|
|
|
8615
8699
|
keys: config.analytics,
|
|
8616
8700
|
log: (...a) => this.log(...a),
|
|
8617
8701
|
initialSessionId: resumable?.sessionId,
|
|
8702
|
+
// En móvil (RN) la app puede morir con el lote de cierre sin entregar: se
|
|
8703
|
+
// persiste y se reenvía en el arranque siguiente. En web el cierre sale con
|
|
8704
|
+
// `keepalive` y varias pestañas compartirían el mismo storage.
|
|
8705
|
+
storage: (config.platform ?? "web") !== "web" ? storage : void 0,
|
|
8618
8706
|
onSessionId: (id) => {
|
|
8619
8707
|
this.analyticsFeedbackSessionId = id;
|
|
8620
8708
|
this.notifyFeedbackSession(id, "open");
|
|
@@ -8650,9 +8738,7 @@ var DeepdotsPopups = class {
|
|
|
8650
8738
|
now: () => Date.now(),
|
|
8651
8739
|
enabled: () => this.tracking?.isTrackingEnabled() ?? false
|
|
8652
8740
|
});
|
|
8653
|
-
|
|
8654
|
-
this.crashReporter.install();
|
|
8655
|
-
}
|
|
8741
|
+
this.installCrashHandlers();
|
|
8656
8742
|
this.geoEnabled = config.geolocation ?? true;
|
|
8657
8743
|
this.geoWanted = this.geoEnabled && !!config.analytics;
|
|
8658
8744
|
this.resolveGeo();
|
|
@@ -8721,6 +8807,7 @@ var DeepdotsPopups = class {
|
|
|
8721
8807
|
if (!enabled) this.closeSession("tracking_disabled");
|
|
8722
8808
|
this.tracking?.setTrackingEnabled(enabled);
|
|
8723
8809
|
if (enabled) this.openSession("tracking_enabled");
|
|
8810
|
+
if (enabled) this.installCrashHandlers();
|
|
8724
8811
|
if (enabled) this.resolveGeo();
|
|
8725
8812
|
this.log("tracking \xB7 setTrackingEnabled:", enabled, "\xB7 session_id:", this.tracking?.getSessionId() ?? null);
|
|
8726
8813
|
}
|
|
@@ -8751,10 +8838,27 @@ var DeepdotsPopups = class {
|
|
|
8751
8838
|
if (!this.tracking?.isTrackingEnabled()) return;
|
|
8752
8839
|
this.crashReporter?.reportError(error, options);
|
|
8753
8840
|
}
|
|
8754
|
-
/**
|
|
8841
|
+
/**
|
|
8842
|
+
* Engancha `global.ErrorUtils` de RN para capturar errores JS no manejados. Con tracking off
|
|
8843
|
+
* se recuerda y se engancha cuando se conceda el consentimiento (`setTrackingEnabled(true)`).
|
|
8844
|
+
*/
|
|
8755
8845
|
installReactNativeCrashHandler(errorUtils) {
|
|
8756
|
-
|
|
8757
|
-
this.
|
|
8846
|
+
this.rnErrorUtils = errorUtils;
|
|
8847
|
+
this.installCrashHandlers();
|
|
8848
|
+
}
|
|
8849
|
+
/**
|
|
8850
|
+
* Pone los handlers de errores no capturados: `window` en web y `ErrorUtils` en RN (el que
|
|
8851
|
+
* pasó el host o, si no pasó ninguno, `globalThis.ErrorUtils`, para que un `init()` sin
|
|
8852
|
+
* `setupReactNative` también capture). Solo con consentimiento; idempotente, así que se
|
|
8853
|
+
* llama en `init()` y cada vez que se activa el tracking.
|
|
8854
|
+
*/
|
|
8855
|
+
installCrashHandlers() {
|
|
8856
|
+
if (!this.crashReporter || !this.tracking?.isTrackingEnabled()) return;
|
|
8857
|
+
this.crashReporter.install();
|
|
8858
|
+
const errorUtils = this.rnErrorUtils ?? globalThis.ErrorUtils;
|
|
8859
|
+
if (errorUtils && typeof errorUtils.setGlobalHandler === "function") {
|
|
8860
|
+
this.crashReporter.installReactNative(errorUtils);
|
|
8861
|
+
}
|
|
8758
8862
|
}
|
|
8759
8863
|
/**
|
|
8760
8864
|
* Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el
|
|
@@ -9577,25 +9681,31 @@ var DeepdotsPopups = class {
|
|
|
9577
9681
|
return null;
|
|
9578
9682
|
}
|
|
9579
9683
|
}
|
|
9580
|
-
/**
|
|
9684
|
+
/**
|
|
9685
|
+
* Envía los atributos de contact a la API (`POST /sdk/popups/contact`). Rechaza si no se
|
|
9686
|
+
* pudo enviar (sin red o respuesta no-2xx), para que `ContactManager` no guarde la caché
|
|
9687
|
+
* y la llamada siguiente reintente.
|
|
9688
|
+
*/
|
|
9581
9689
|
async postContact(body) {
|
|
9582
9690
|
const baseUrl = this.baseUrl;
|
|
9583
|
-
if (!baseUrl)
|
|
9691
|
+
if (!baseUrl) throw new Error("no base URL");
|
|
9584
9692
|
const endpoint = `${baseUrl}/sdk/popups/contact`;
|
|
9693
|
+
let response;
|
|
9585
9694
|
try {
|
|
9586
|
-
|
|
9695
|
+
response = await fetch(endpoint, {
|
|
9587
9696
|
method: "POST",
|
|
9588
9697
|
headers: { "Content-Type": "application/json" },
|
|
9589
9698
|
body: JSON.stringify(body)
|
|
9590
9699
|
});
|
|
9591
|
-
if (!response.ok) {
|
|
9592
|
-
this.log("Failed to post contact", response.status, response.statusText);
|
|
9593
|
-
return;
|
|
9594
|
-
}
|
|
9595
|
-
this.log("contact \xB7 sent attributes for user:", body.userId);
|
|
9596
9700
|
} catch (error) {
|
|
9597
9701
|
this.log("Error posting contact", error);
|
|
9702
|
+
throw error;
|
|
9703
|
+
}
|
|
9704
|
+
if (!response.ok) {
|
|
9705
|
+
this.log("Failed to post contact", response.status, response.statusText);
|
|
9706
|
+
throw new Error(`HTTP ${response.status}`);
|
|
9598
9707
|
}
|
|
9708
|
+
this.log("contact \xB7 sent attributes for user:", body.userId);
|
|
9599
9709
|
}
|
|
9600
9710
|
/** Notifica eventos de popup a la API */
|
|
9601
9711
|
async postPopupEvent(status, popupId, userId) {
|