UNPKG

datadog-ux-utils

Version:

Datadog RUM focused UX & performance toolkit: API guards (retry, breaker, rate), React telemetry (error boundary, profiler, Suspense), web vitals & resource observers, offline queues.

210 lines (209 loc) 5.88 kB
import { a as y } from "./datadog-irY4zUa4.js"; import { datadogRum as d } from "@datadog/browser-rum"; import { g as h } from "./config-D7lUKPO2.js"; const _ = { failureThreshold: 5, cooldownMs: 1e4, halfOpenMax: 2 }, g = /* @__PURE__ */ new Map(); async function B(e, r, a = {}) { const t = N(e, a), o = Date.now(); if (t.state === "open") { if (o < t.nextTry) throw M("open", e, t.nextTry); t.state = "half", t.halfOpenInFlight = 0; } if (t.state === "half" && t.halfOpenInFlight >= t.cfg.halfOpenMax) throw M("half-saturated", e, t.nextTry); t.state === "half" && t.halfOpenInFlight++; try { const n = await r(); return S(t, e), n; } catch (n) { throw A(t), n; } finally { t.state === "half" && t.halfOpenInFlight--; } } function N(e, r) { let a = g.get(e); return a || (a = { state: "closed", failures: 0, nextTry: 0, halfOpenInFlight: 0, cfg: { ..._, ...r } }, g.set(e, a)), a; } function S(e, r) { e.failures = 0, e.state !== "closed" && (e.state = "closed"); } function A(e, r) { e.failures++, (e.state === "half" || e.failures >= e.cfg.failureThreshold) && (e.state = "open", e.nextTry = Date.now() + e.cfg.cooldownMs, y("api_circuit_open", { cooldown_ms: e.cfg.cooldownMs })); } function M(e, r, a) { const t = new Error( `Circuit ${e} for ${r} until ${new Date(a).toISOString()}` ); return t.name = "ApiCircuitOpenError", t; } const L = async (e, r) => { const a = h(), t = performance.now(); let o; try { o = await fetch(e, r); const n = performance.now() - t; return n >= a.apiSlowMs && b( "api_slow", { url: String( typeof e == "string" ? e : e.toString() ), method: r?.method ?? "GET", duration_ms: Math.round(n), status: o.status }, a.actionSampleRate ), o; } catch (n) { throw x(n, { where: "ddFetch", url: String( typeof e == "string" ? e : e.toString() ) }), n; } }, $ = async (e, r, a) => { const t = h(), o = performance.now(); try { const n = await r, s = performance.now() - o; return s >= t.apiSlowMs && b( "promise_slow", { label: e, duration_ms: Math.round(s), ...a }, t.actionSampleRate ), n; } catch (n) { throw x(n, { label: e, ...a }), n; } }, b = (e, r, a) => { Math.random() * 100 < a && d.addAction(e, r); }, x = (e, r) => { d.addError(e, r); }, l = /* @__PURE__ */ new Map(); function v(e, r, a) { const { ttlMs: t, report: o } = F(a), n = Date.now(), s = l.get(e); if (s && (s.state === "pending" || s.expireAt > n)) return T(o, s.state, e, { age_ms: n - s.startedAt, ttl_ms: s.expireAt ? s.expireAt - n : 0 }), s.promise; const m = n, i = { promise: Promise.resolve().then(r), // ensure async boundary expireAt: n + (t > 0 ? t : 0), state: "pending", startedAt: m }; return l.set(e, i), i.promise = i.promise.then((c) => (t > 0 ? (i.state = "cached", i.expireAt = Date.now() + t, i.promise = Promise.resolve(c), setTimeout(() => { const u = l.get(e); u && u.state === "cached" && u.expireAt <= Date.now() && l.delete(e); }, t + 25)) : l.delete(e), c)).catch((c) => { throw l.delete(e), c; }), i.promise; } function K(e) { e ? l.delete(e) : l.clear(); } function U() { return l.size; } function F(e) { if (typeof e == "number") return { ttlMs: e, report: { enabled: !1, sampleRate: 10, actionName: "api_dedupe_hit" } }; const r = e?.ttlMs ?? 0; let a = !1, t = 10, o = "api_dedupe_hit"; return e?.report === !0 ? a = !0 : typeof e?.report == "object" && (a = !0, typeof e.report.sampleRate == "number" && (t = O(e.report.sampleRate)), e.report.actionName && (o = e.report.actionName)), { ttlMs: r, report: { enabled: a, sampleRate: t, actionName: o } }; } function T(e, r, a, t) { if (e.enabled && !(Math.random() * 100 >= e.sampleRate)) try { y(e.actionName, { key: a, kind: r, ...t }, e.sampleRate); } catch { } } function O(e) { return Number.isNaN(e) ? 0 : Math.max(0, Math.min(100, Math.round(e))); } const G = async (e, r) => { const a = h(); if (!a.captureResponseSize) return e; try { const t = e.headers.get("content-length"); let o = t ? parseInt(t, 10) : NaN; Number.isFinite(o) || (o = (await e.clone().arrayBuffer()).byteLength); const n = Math.round(o / 1024); n >= a.apiLargeKb && d.addAction("api_large_payload", { url: r, size_kb: n, threshold_kb: a.apiLargeKb, status: e.status }); } catch { } return e; }, D = { retries: 3, baseMs: 200, maxMs: 5e3, factor: 2, jitter: !0, shouldRetry: () => !0, report: !1 }; async function W(e, r, a) { const { retries: t, baseMs: o, maxMs: n, factor: s, jitter: m, shouldRetry: i, report: c } = { ...D, ...a }, u = typeof c == "object" ? { enabled: !0, sampleRate: c.sampleRate ?? 10, actionName: c.actionName ?? "api_retry" } : c === !0 ? { enabled: !0, sampleRate: 10, actionName: "api_retry" } : { enabled: !1, sampleRate: 0, actionName: "" }; let f = 0, w; for (; f <= t; ) try { return f > 0 && u.enabled && I(u, e, f), await r(); } catch (p) { if (w = p, f >= t || !i(p, f)) throw p; const R = E(o, s, f, n, m); await z(R), f++; } throw w; } function E(e, r, a, t, o) { let n = Math.min(e * Math.pow(r, a), t); if (o) { const s = Math.floor(Math.random() * e); n += Math.random() < 0.5 ? -s : s, n = Math.max(0, n); } return n; } function z(e) { return new Promise((r) => setTimeout(r, e)); } function I(e, r, a) { e.enabled && Math.random() * 100 >= e.sampleRate; } export { x as a, v as b, K as c, L as d, U as e, G as f, b as m, W as r, $ as t, B as w }; //# sourceMappingURL=retry-vuLskHMp.js.map