@djangocfg/monitor 2.1.237 → 2.1.239

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/client.cjs CHANGED
@@ -1364,11 +1364,36 @@ __name(sendBatch, "sendBatch");
1364
1364
  // src/client/utils/env.ts
1365
1365
  var isDevelopment = process.env.NODE_ENV === "development";
1366
1366
  var isProduction = !isDevelopment;
1367
- var MONITOR_VERSION = "2.1.237";
1367
+ var MONITOR_VERSION = "2.1.239";
1368
+
1369
+ // src/client/constants.ts
1370
+ var MONITOR_INGEST_PATTERN = /cfg\/monitor\/ingest/;
1371
+ var DEFAULT_DEDUPE_TTL = 5e3;
1372
+ var DEFAULT_DEDUPE_STORE_TTL = 3e4;
1368
1373
 
1369
1374
  // src/client/store/index.ts
1370
1375
  var CIRCUIT_BREAKER_THRESHOLD = 3;
1371
1376
  var CIRCUIT_BREAKER_COOLDOWN_MS = 6e4;
1377
+ var STORE_DEDUP_MAX = 200;
1378
+ var _recentPushKeys = /* @__PURE__ */ new Map();
1379
+ function _pushDedupeKey(event) {
1380
+ const msg = (event.message ?? "").slice(0, 100);
1381
+ return `${event.event_type}:${event.level}:${msg}:${event.http_url ?? event.url ?? ""}`;
1382
+ }
1383
+ __name(_pushDedupeKey, "_pushDedupeKey");
1384
+ function _isRecentPush(key, ttl) {
1385
+ const now = Date.now();
1386
+ const last = _recentPushKeys.get(key);
1387
+ if (last !== void 0 && now - last < ttl) return true;
1388
+ _recentPushKeys.set(key, now);
1389
+ if (_recentPushKeys.size > STORE_DEDUP_MAX) {
1390
+ for (const [k, ts] of _recentPushKeys) {
1391
+ if (now - ts > ttl) _recentPushKeys.delete(k);
1392
+ }
1393
+ }
1394
+ return false;
1395
+ }
1396
+ __name(_isRecentPush, "_isRecentPush");
1372
1397
  var monitorStore = (0, import_vanilla.createStore)((set, get) => ({
1373
1398
  config: {},
1374
1399
  buffer: [],
@@ -1377,6 +1402,9 @@ var monitorStore = (0, import_vanilla.createStore)((set, get) => ({
1377
1402
  _pausedUntil: 0,
1378
1403
  push(event) {
1379
1404
  const { config, buffer } = get();
1405
+ const storeTtl = config.dedupeStoreTtl ?? DEFAULT_DEDUPE_STORE_TTL;
1406
+ const dedupeKey = _pushDedupeKey(event);
1407
+ if (_isRecentPush(dedupeKey, storeTtl)) return;
1380
1408
  const maxSize = config.maxBufferSize ?? 20;
1381
1409
  const sanitized = {
1382
1410
  build_id: event.build_id ?? config.buildId ?? `sdk:${MONITOR_VERSION}`,
@@ -1422,9 +1450,6 @@ var monitorStore = (0, import_vanilla.createStore)((set, get) => ({
1422
1450
  }
1423
1451
  }));
1424
1452
 
1425
- // src/client/constants.ts
1426
- var MONITOR_INGEST_PATTERN = /cfg\/monitor\/ingest/;
1427
-
1428
1453
  // src/client/capture/js-errors.ts
1429
1454
  var MSG_MAX = 2e3;
1430
1455
  function truncate(s, max = MSG_MAX) {
@@ -1447,12 +1472,12 @@ function isHydrationNoise(msg) {
1447
1472
  return HYDRATION_NOISE.some((p) => p.test(msg));
1448
1473
  }
1449
1474
  __name(isHydrationNoise, "isHydrationNoise");
1450
- var CLIENT_DEDUP_TTL = 5 * 60 * 1e3;
1451
1475
  var recentFingerprints = /* @__PURE__ */ new Map();
1452
1476
  function isRecentlySent(fingerprint) {
1477
+ const ttl = monitorStore.getState().config.dedupeTtl ?? DEFAULT_DEDUPE_TTL;
1453
1478
  const now = Date.now();
1454
1479
  const last = recentFingerprints.get(fingerprint);
1455
- if (last !== void 0 && now - last < CLIENT_DEDUP_TTL) return true;
1480
+ if (last !== void 0 && now - last < ttl) return true;
1456
1481
  recentFingerprints.set(fingerprint, now);
1457
1482
  if (recentFingerprints.size > 100) {
1458
1483
  const oldest = [...recentFingerprints.entries()].sort((a, b) => a[1] - b[1])[0];
@@ -1537,6 +1562,20 @@ var typeMap = {
1537
1562
  error: "ERROR" /* ERROR */
1538
1563
  };
1539
1564
  var ARG_MAX = 500;
1565
+ var recentConsoleFingerprints = /* @__PURE__ */ new Map();
1566
+ function isRecentConsole(fingerprint) {
1567
+ const ttl = monitorStore.getState().config.dedupeTtl ?? DEFAULT_DEDUPE_TTL;
1568
+ const now = Date.now();
1569
+ const last = recentConsoleFingerprints.get(fingerprint);
1570
+ if (last !== void 0 && now - last < ttl) return true;
1571
+ recentConsoleFingerprints.set(fingerprint, now);
1572
+ if (recentConsoleFingerprints.size > 100) {
1573
+ const oldest = [...recentConsoleFingerprints.entries()].sort((a, b) => a[1] - b[1])[0];
1574
+ recentConsoleFingerprints.delete(oldest[0]);
1575
+ }
1576
+ return false;
1577
+ }
1578
+ __name(isRecentConsole, "isRecentConsole");
1540
1579
  function stringifyArg(a) {
1541
1580
  let s;
1542
1581
  if (typeof a === "string") s = a;
@@ -1561,6 +1600,7 @@ async function captureConsoleEvent(level, args) {
1561
1600
  if (MONITOR_INGEST_PATTERN.test(message)) return;
1562
1601
  const url = typeof window !== "undefined" ? window.location.href : "";
1563
1602
  const fingerprint = await computeFingerprint(message, "", url);
1603
+ if (isRecentConsole(fingerprint)) return;
1564
1604
  const { config } = monitorStore.getState();
1565
1605
  monitorStore.getState().push({
1566
1606
  event_type: typeMap[level],
@@ -1613,6 +1653,21 @@ function installConsoleCapture() {
1613
1653
  __name(installConsoleCapture, "installConsoleCapture");
1614
1654
 
1615
1655
  // src/client/capture/validation.ts
1656
+ var recentValidations = /* @__PURE__ */ new Map();
1657
+ function isRecentValidation(key) {
1658
+ const ttl = monitorStore.getState().config.dedupeTtl ?? DEFAULT_DEDUPE_TTL;
1659
+ const now = Date.now();
1660
+ const last = recentValidations.get(key);
1661
+ if (last !== void 0 && now - last < ttl) return true;
1662
+ recentValidations.set(key, now);
1663
+ if (recentValidations.size > 50) {
1664
+ for (const [k, ts] of recentValidations) {
1665
+ if (now - ts > ttl) recentValidations.delete(k);
1666
+ }
1667
+ }
1668
+ return false;
1669
+ }
1670
+ __name(isRecentValidation, "isRecentValidation");
1616
1671
  function installValidationCapture() {
1617
1672
  if (typeof window === "undefined") return () => {
1618
1673
  };
@@ -1620,6 +1675,8 @@ function installValidationCapture() {
1620
1675
  if (!(event instanceof CustomEvent)) return;
1621
1676
  try {
1622
1677
  const detail = event.detail;
1678
+ const dedupeKey = `${detail.operation}:${detail.path}:${detail.method}`;
1679
+ if (isRecentValidation(dedupeKey)) return;
1623
1680
  const { config } = monitorStore.getState();
1624
1681
  const rawMsg = `Zod validation error in ${detail.operation}: ${detail.error?.message ?? "unknown"}`;
1625
1682
  monitorStore.getState().push({
@@ -1644,12 +1701,29 @@ function installValidationCapture() {
1644
1701
  __name(installValidationCapture, "installValidationCapture");
1645
1702
 
1646
1703
  // src/client/capture/network.ts
1704
+ var recentNetworkErrors = /* @__PURE__ */ new Map();
1705
+ function isRecentNetwork(key) {
1706
+ const ttl = monitorStore.getState().config.dedupeTtl ?? DEFAULT_DEDUPE_TTL;
1707
+ const now = Date.now();
1708
+ const last = recentNetworkErrors.get(key);
1709
+ if (last !== void 0 && now - last < ttl) return true;
1710
+ recentNetworkErrors.set(key, now);
1711
+ if (recentNetworkErrors.size > 100) {
1712
+ for (const [k, ts] of recentNetworkErrors) {
1713
+ if (now - ts > ttl) recentNetworkErrors.delete(k);
1714
+ }
1715
+ }
1716
+ return false;
1717
+ }
1718
+ __name(isRecentNetwork, "isRecentNetwork");
1647
1719
  async function monitoredFetch(input, init) {
1648
1720
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1649
1721
  const method = (init?.method ?? "GET").toUpperCase();
1650
1722
  try {
1651
1723
  const response = await fetch(input, init);
1652
1724
  if (!response.ok) {
1725
+ const netKey = `${response.status}:${method}:${url}`;
1726
+ if (isRecentNetwork(netKey)) return response;
1653
1727
  const { config } = monitorStore.getState();
1654
1728
  monitorStore.getState().push({
1655
1729
  event_type: "NETWORK_ERROR" /* NETWORK_ERROR */,
@@ -1667,19 +1741,23 @@ async function monitoredFetch(input, init) {
1667
1741
  }
1668
1742
  return response;
1669
1743
  } catch (err) {
1670
- const { config } = monitorStore.getState();
1671
- monitorStore.getState().push({
1672
- event_type: "NETWORK_ERROR" /* NETWORK_ERROR */,
1673
- level: "error" /* ERROR */,
1674
- message: err instanceof Error ? err.message : `Network error \u2014 ${method} ${url}`,
1675
- url: typeof window !== "undefined" ? window.location.href : "",
1676
- http_method: method,
1677
- http_url: url,
1678
- session_id: getSessionId(),
1679
- user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "",
1680
- project_name: config.project,
1681
- environment: config.environment
1682
- });
1744
+ const errMsg = err instanceof Error ? err.message : "network-error";
1745
+ const netKey = `err:${errMsg.slice(0, 50)}:${method}:${url}`;
1746
+ if (!isRecentNetwork(netKey)) {
1747
+ const { config } = monitorStore.getState();
1748
+ monitorStore.getState().push({
1749
+ event_type: "NETWORK_ERROR" /* NETWORK_ERROR */,
1750
+ level: "error" /* ERROR */,
1751
+ message: err instanceof Error ? err.message : `Network error \u2014 ${method} ${url}`,
1752
+ url: typeof window !== "undefined" ? window.location.href : "",
1753
+ http_method: method,
1754
+ http_url: url,
1755
+ session_id: getSessionId(),
1756
+ user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "",
1757
+ project_name: config.project,
1758
+ environment: config.environment
1759
+ });
1760
+ }
1683
1761
  throw err;
1684
1762
  }
1685
1763
  }