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
JavaScript
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