@djangocfg/monitor 2.1.230 → 2.1.232

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/README.md CHANGED
@@ -76,6 +76,7 @@ const res = await monitoredFetch('/api/orders', { method: 'POST', body: JSON.str
76
76
  |--------|------|---------|-------------|
77
77
  | `project` | `string` | `''` | Project name sent with every event |
78
78
  | `environment` | `string` | `''` | `production` / `staging` / `development` |
79
+ | `buildId` | `string` | `sdk:<version>` | Build identifier (e.g. Next.js `BUILD_ID` or git SHA). Stamped on every event as `build_id`. Auto-filled with SDK version if not set |
79
80
  | `baseUrl` | `string` | same origin | Base URL of the django-cfg backend |
80
81
  | `flushInterval` | `number` | `5000` | Buffer flush interval (ms) |
81
82
  | `maxBufferSize` | `number` | `20` | Max events before immediate flush |
@@ -137,7 +138,21 @@ window.monitor.flush()
137
138
 
138
139
  // Inspect current state
139
140
  window.monitor.status()
140
- // → logs config, buffer size, session_id
141
+ // → logs sdk version, build_id, config, buffer size, session_id
142
+ ```
143
+
144
+ ## Debug Mode
145
+
146
+ `useDebugMode` hook controls whether the debug panel is unlocked:
147
+
148
+ - **development** — always `true`, no localStorage needed
149
+ - **production** — `?debug=1` in URL → persists to `localStorage.__debug_mode__`, panel unlocks
150
+ - **production** — `?debug=0` in URL → clears `localStorage.__debug_mode__`, panel locks
151
+
152
+ ```typescript
153
+ import { useDebugMode } from '@djangocfg/monitor/client'
154
+
155
+ const isDebug = useDebugMode()
141
156
  ```
142
157
 
143
158
  ## Debug Panel Integration
package/dist/client.cjs CHANGED
@@ -266,6 +266,7 @@ __export(client_exports, {
266
266
  EventLevel: () => FrontendEventIngestRequestLevel,
267
267
  EventType: () => FrontendEventIngestRequestEventType,
268
268
  FrontendMonitor: () => FrontendMonitor,
269
+ MONITOR_VERSION: () => MONITOR_VERSION,
269
270
  MonitorProvider: () => MonitorProvider,
270
271
  getSessionId: () => getSessionId,
271
272
  initWindowMonitor: () => initWindowMonitor,
@@ -994,43 +995,47 @@ var _APIClient = class _APIClient {
994
995
  if (error instanceof APIError) {
995
996
  throw error;
996
997
  }
997
- const isCORSError = error instanceof TypeError && (error.message.toLowerCase().includes("cors") || error.message.toLowerCase().includes("failed to fetch") || error.message.toLowerCase().includes("network request failed"));
998
+ let possiblyCors = false;
999
+ if (error instanceof TypeError && typeof window !== "undefined") {
1000
+ try {
1001
+ const isCrossOrigin = (() => {
1002
+ try {
1003
+ return new URL(url).origin !== window.location.origin;
1004
+ } catch {
1005
+ return false;
1006
+ }
1007
+ })();
1008
+ if (isCrossOrigin) {
1009
+ const entries = performance.getEntriesByName(url, "resource");
1010
+ if (entries.length > 0) {
1011
+ const last = entries[entries.length - 1];
1012
+ possiblyCors = "responseStatus" in last && last.responseStatus === 0;
1013
+ }
1014
+ }
1015
+ } catch {
1016
+ }
1017
+ }
998
1018
  if (this.logger) {
999
- if (isCORSError) {
1000
- this.logger.error(`\u{1F6AB} CORS Error: ${method} ${url}`);
1001
- this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
1002
- this.logger.error(` \u2192 Configure security_domains parameter on the server`);
1003
- } else {
1004
- this.logger.error(`\u26A0\uFE0F Network Error: ${method} ${url}`);
1005
- this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
1019
+ this.logger.error(`\u26A0\uFE0F Network Error: ${method} ${url}`);
1020
+ this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
1021
+ if (possiblyCors) {
1022
+ this.logger.error(` \u2192 Possibly blocked by CORS policy (configure CORS on the server)`);
1006
1023
  }
1007
1024
  }
1008
1025
  if (typeof window !== "undefined") {
1009
1026
  try {
1010
- if (isCORSError) {
1011
- window.dispatchEvent(new CustomEvent("cors-error", {
1012
- detail: {
1013
- url,
1014
- method,
1015
- error: error instanceof Error ? error.message : String(error),
1016
- timestamp: /* @__PURE__ */ new Date()
1017
- },
1018
- bubbles: true,
1019
- cancelable: false
1020
- }));
1021
- } else {
1022
- window.dispatchEvent(new CustomEvent("network-error", {
1023
- detail: {
1024
- url,
1025
- method,
1026
- error: error instanceof Error ? error.message : String(error),
1027
- timestamp: /* @__PURE__ */ new Date()
1028
- },
1029
- bubbles: true,
1030
- cancelable: false
1031
- }));
1032
- }
1033
- } catch (eventError) {
1027
+ window.dispatchEvent(new CustomEvent("network-error", {
1028
+ detail: {
1029
+ url,
1030
+ method,
1031
+ error: error instanceof Error ? error.message : String(error),
1032
+ possibly_cors: possiblyCors,
1033
+ timestamp: /* @__PURE__ */ new Date()
1034
+ },
1035
+ bubbles: true,
1036
+ cancelable: false
1037
+ }));
1038
+ } catch {
1034
1039
  }
1035
1040
  }
1036
1041
  const networkError = error instanceof Error ? new NetworkError(error.message, url, error) : new NetworkError("Unknown error", url);
@@ -1356,6 +1361,11 @@ async function sendBatch(batch, useBeacon = false) {
1356
1361
  }
1357
1362
  __name(sendBatch, "sendBatch");
1358
1363
 
1364
+ // src/client/utils/env.ts
1365
+ var isDevelopment = true;
1366
+ var isProduction = false;
1367
+ var MONITOR_VERSION = "2.1.232";
1368
+
1359
1369
  // src/client/store/index.ts
1360
1370
  var CIRCUIT_BREAKER_THRESHOLD = 3;
1361
1371
  var CIRCUIT_BREAKER_COOLDOWN_MS = 6e4;
@@ -1369,6 +1379,7 @@ var monitorStore = (0, import_vanilla.createStore)((set, get) => ({
1369
1379
  const { config, buffer } = get();
1370
1380
  const maxSize = config.maxBufferSize ?? 20;
1371
1381
  const sanitized = {
1382
+ build_id: event.build_id ?? config.buildId ?? `sdk:${MONITOR_VERSION}`,
1372
1383
  ...event,
1373
1384
  // Enforce field size limits before buffering (last-resort backstop)
1374
1385
  message: event.message && event.message.length > 4997 ? event.message.slice(0, 4997) + "..." : event.message,
@@ -1730,6 +1741,8 @@ function initWindowMonitor() {
1730
1741
  status() {
1731
1742
  const state = monitorStore.getState();
1732
1743
  console.group("[monitor] status");
1744
+ console.log("sdk version:", MONITOR_VERSION);
1745
+ console.log("build_id:", state.config.buildId ?? `sdk:${MONITOR_VERSION}`);
1733
1746
  console.log("config:", state.config);
1734
1747
  console.log("buffer size:", state.buffer.length);
1735
1748
  console.log("initialized:", state.initialized);
@@ -1754,12 +1767,6 @@ __name(MonitorProvider, "MonitorProvider");
1754
1767
 
1755
1768
  // src/client/hooks/useDebugMode.ts
1756
1769
  var import_react2 = require("react");
1757
-
1758
- // src/client/utils/env.ts
1759
- var isDevelopment = true;
1760
- var isProduction = false;
1761
-
1762
- // src/client/hooks/useDebugMode.ts
1763
1770
  var LS_KEY = "__debug_mode__";
1764
1771
  function readFromStorage() {
1765
1772
  try {
@@ -1776,16 +1783,27 @@ function persistToStorage() {
1776
1783
  }
1777
1784
  }
1778
1785
  __name(persistToStorage, "persistToStorage");
1786
+ function clearFromStorage() {
1787
+ try {
1788
+ localStorage.removeItem(LS_KEY);
1789
+ } catch {
1790
+ }
1791
+ }
1792
+ __name(clearFromStorage, "clearFromStorage");
1779
1793
  function consumeDebugParam() {
1780
- if (typeof window === "undefined") return false;
1794
+ if (typeof window === "undefined") return null;
1781
1795
  const params = new URLSearchParams(window.location.search);
1782
1796
  const value = params.get("debug");
1783
- if (!value) return false;
1784
- persistToStorage();
1797
+ if (value === null) return null;
1798
+ if (value === "1") {
1799
+ persistToStorage();
1800
+ } else {
1801
+ clearFromStorage();
1802
+ }
1785
1803
  params.delete("debug");
1786
1804
  const newUrl = `${window.location.pathname}${params.toString() ? `?${params.toString()}` : ""}${window.location.hash}`;
1787
1805
  window.history.replaceState(null, "", newUrl);
1788
- return true;
1806
+ return value === "1";
1789
1807
  }
1790
1808
  __name(consumeDebugParam, "consumeDebugParam");
1791
1809
  function useDebugMode() {
@@ -1793,8 +1811,8 @@ function useDebugMode() {
1793
1811
  const [isDebug, setIsDebug] = (0, import_react2.useState)(false);
1794
1812
  (0, import_react2.useEffect)(() => {
1795
1813
  const fromUrl = consumeDebugParam();
1796
- if (fromUrl) {
1797
- setIsDebug(true);
1814
+ if (fromUrl !== null) {
1815
+ setIsDebug(fromUrl);
1798
1816
  return;
1799
1817
  }
1800
1818
  setIsDebug(readFromStorage());