@djangocfg/monitor 2.1.217 → 2.1.219

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
@@ -124,6 +124,36 @@ class Config(DjangoConfig):
124
124
 
125
125
  Ingest endpoint: `POST /cfg/monitor/ingest/` — no auth required, 100 req/min per IP.
126
126
 
127
+ ## Browser Console Testing
128
+
129
+ After `MonitorProvider` mounts, `window.monitor` is available in DevTools for manual testing:
130
+
131
+ ```javascript
132
+ // Fire events from DevTools console
133
+ window.monitor.error('Payment failed', { orderId: '123' })
134
+ window.monitor.warn('Slow response', { ms: 2400 })
135
+ window.monitor.info('User action', { action: 'checkout' })
136
+ window.monitor.network(404, 'GET', '/api/users/')
137
+ window.monitor.network(502, 'POST', '/api/orders/', { retries: 3 })
138
+
139
+ // Force-send buffered events immediately
140
+ window.monitor.flush()
141
+
142
+ // Inspect current state
143
+ window.monitor.status()
144
+ // → logs config, buffer size, session_id
145
+ ```
146
+
147
+ ## Debug Panel Integration
148
+
149
+ Install `@djangocfg/debuger` alongside this package — it auto-bridges the monitor event store into the debug panel's Logs tab. No extra setup needed.
150
+
151
+ ```bash
152
+ pnpm add @djangocfg/debuger
153
+ ```
154
+
155
+ Events captured by monitor (JS errors, console, network) appear in the panel as `monitor:JS_ERROR`, `monitor:NETWORK_ERROR`, etc.
156
+
127
157
  ## Safety
128
158
 
129
159
  Transport errors are **always swallowed** — monitor never crashes your app and never sends its own errors to itself. If the backend is unreachable, events are silently dropped.
package/dist/client.cjs CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
9
12
  var __export = (target, all) => {
10
13
  for (var name in all)
11
14
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -28,12 +31,243 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
31
  ));
29
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
33
 
34
+ // ../../node_modules/.pnpm/retry@0.13.1/node_modules/retry/lib/retry_operation.js
35
+ var require_retry_operation = __commonJS({
36
+ "../../node_modules/.pnpm/retry@0.13.1/node_modules/retry/lib/retry_operation.js"(exports, module2) {
37
+ function RetryOperation(timeouts, options) {
38
+ if (typeof options === "boolean") {
39
+ options = { forever: options };
40
+ }
41
+ this._originalTimeouts = JSON.parse(JSON.stringify(timeouts));
42
+ this._timeouts = timeouts;
43
+ this._options = options || {};
44
+ this._maxRetryTime = options && options.maxRetryTime || Infinity;
45
+ this._fn = null;
46
+ this._errors = [];
47
+ this._attempts = 1;
48
+ this._operationTimeout = null;
49
+ this._operationTimeoutCb = null;
50
+ this._timeout = null;
51
+ this._operationStart = null;
52
+ this._timer = null;
53
+ if (this._options.forever) {
54
+ this._cachedTimeouts = this._timeouts.slice(0);
55
+ }
56
+ }
57
+ __name(RetryOperation, "RetryOperation");
58
+ module2.exports = RetryOperation;
59
+ RetryOperation.prototype.reset = function() {
60
+ this._attempts = 1;
61
+ this._timeouts = this._originalTimeouts.slice(0);
62
+ };
63
+ RetryOperation.prototype.stop = function() {
64
+ if (this._timeout) {
65
+ clearTimeout(this._timeout);
66
+ }
67
+ if (this._timer) {
68
+ clearTimeout(this._timer);
69
+ }
70
+ this._timeouts = [];
71
+ this._cachedTimeouts = null;
72
+ };
73
+ RetryOperation.prototype.retry = function(err) {
74
+ if (this._timeout) {
75
+ clearTimeout(this._timeout);
76
+ }
77
+ if (!err) {
78
+ return false;
79
+ }
80
+ var currentTime = (/* @__PURE__ */ new Date()).getTime();
81
+ if (err && currentTime - this._operationStart >= this._maxRetryTime) {
82
+ this._errors.push(err);
83
+ this._errors.unshift(new Error("RetryOperation timeout occurred"));
84
+ return false;
85
+ }
86
+ this._errors.push(err);
87
+ var timeout = this._timeouts.shift();
88
+ if (timeout === void 0) {
89
+ if (this._cachedTimeouts) {
90
+ this._errors.splice(0, this._errors.length - 1);
91
+ timeout = this._cachedTimeouts.slice(-1);
92
+ } else {
93
+ return false;
94
+ }
95
+ }
96
+ var self = this;
97
+ this._timer = setTimeout(function() {
98
+ self._attempts++;
99
+ if (self._operationTimeoutCb) {
100
+ self._timeout = setTimeout(function() {
101
+ self._operationTimeoutCb(self._attempts);
102
+ }, self._operationTimeout);
103
+ if (self._options.unref) {
104
+ self._timeout.unref();
105
+ }
106
+ }
107
+ self._fn(self._attempts);
108
+ }, timeout);
109
+ if (this._options.unref) {
110
+ this._timer.unref();
111
+ }
112
+ return true;
113
+ };
114
+ RetryOperation.prototype.attempt = function(fn, timeoutOps) {
115
+ this._fn = fn;
116
+ if (timeoutOps) {
117
+ if (timeoutOps.timeout) {
118
+ this._operationTimeout = timeoutOps.timeout;
119
+ }
120
+ if (timeoutOps.cb) {
121
+ this._operationTimeoutCb = timeoutOps.cb;
122
+ }
123
+ }
124
+ var self = this;
125
+ if (this._operationTimeoutCb) {
126
+ this._timeout = setTimeout(function() {
127
+ self._operationTimeoutCb();
128
+ }, self._operationTimeout);
129
+ }
130
+ this._operationStart = (/* @__PURE__ */ new Date()).getTime();
131
+ this._fn(this._attempts);
132
+ };
133
+ RetryOperation.prototype.try = function(fn) {
134
+ console.log("Using RetryOperation.try() is deprecated");
135
+ this.attempt(fn);
136
+ };
137
+ RetryOperation.prototype.start = function(fn) {
138
+ console.log("Using RetryOperation.start() is deprecated");
139
+ this.attempt(fn);
140
+ };
141
+ RetryOperation.prototype.start = RetryOperation.prototype.try;
142
+ RetryOperation.prototype.errors = function() {
143
+ return this._errors;
144
+ };
145
+ RetryOperation.prototype.attempts = function() {
146
+ return this._attempts;
147
+ };
148
+ RetryOperation.prototype.mainError = function() {
149
+ if (this._errors.length === 0) {
150
+ return null;
151
+ }
152
+ var counts = {};
153
+ var mainError = null;
154
+ var mainErrorCount = 0;
155
+ for (var i = 0; i < this._errors.length; i++) {
156
+ var error = this._errors[i];
157
+ var message = error.message;
158
+ var count = (counts[message] || 0) + 1;
159
+ counts[message] = count;
160
+ if (count >= mainErrorCount) {
161
+ mainError = error;
162
+ mainErrorCount = count;
163
+ }
164
+ }
165
+ return mainError;
166
+ };
167
+ }
168
+ });
169
+
170
+ // ../../node_modules/.pnpm/retry@0.13.1/node_modules/retry/lib/retry.js
171
+ var require_retry = __commonJS({
172
+ "../../node_modules/.pnpm/retry@0.13.1/node_modules/retry/lib/retry.js"(exports) {
173
+ var RetryOperation = require_retry_operation();
174
+ exports.operation = function(options) {
175
+ var timeouts = exports.timeouts(options);
176
+ return new RetryOperation(timeouts, {
177
+ forever: options && (options.forever || options.retries === Infinity),
178
+ unref: options && options.unref,
179
+ maxRetryTime: options && options.maxRetryTime
180
+ });
181
+ };
182
+ exports.timeouts = function(options) {
183
+ if (options instanceof Array) {
184
+ return [].concat(options);
185
+ }
186
+ var opts = {
187
+ retries: 10,
188
+ factor: 2,
189
+ minTimeout: 1 * 1e3,
190
+ maxTimeout: Infinity,
191
+ randomize: false
192
+ };
193
+ for (var key in options) {
194
+ opts[key] = options[key];
195
+ }
196
+ if (opts.minTimeout > opts.maxTimeout) {
197
+ throw new Error("minTimeout is greater than maxTimeout");
198
+ }
199
+ var timeouts = [];
200
+ for (var i = 0; i < opts.retries; i++) {
201
+ timeouts.push(this.createTimeout(i, opts));
202
+ }
203
+ if (options && options.forever && !timeouts.length) {
204
+ timeouts.push(this.createTimeout(i, opts));
205
+ }
206
+ timeouts.sort(function(a, b) {
207
+ return a - b;
208
+ });
209
+ return timeouts;
210
+ };
211
+ exports.createTimeout = function(attempt, opts) {
212
+ var random = opts.randomize ? Math.random() + 1 : 1;
213
+ var timeout = Math.round(random * Math.max(opts.minTimeout, 1) * Math.pow(opts.factor, attempt));
214
+ timeout = Math.min(timeout, opts.maxTimeout);
215
+ return timeout;
216
+ };
217
+ exports.wrap = function(obj, options, methods) {
218
+ if (options instanceof Array) {
219
+ methods = options;
220
+ options = null;
221
+ }
222
+ if (!methods) {
223
+ methods = [];
224
+ for (var key in obj) {
225
+ if (typeof obj[key] === "function") {
226
+ methods.push(key);
227
+ }
228
+ }
229
+ }
230
+ for (var i = 0; i < methods.length; i++) {
231
+ var method = methods[i];
232
+ var original = obj[method];
233
+ obj[method] = (/* @__PURE__ */ __name(function retryWrapper(original2) {
234
+ var op = exports.operation(options);
235
+ var args = Array.prototype.slice.call(arguments, 1);
236
+ var callback = args.pop();
237
+ args.push(function(err) {
238
+ if (op.retry(err)) {
239
+ return;
240
+ }
241
+ if (err) {
242
+ arguments[0] = op.mainError();
243
+ }
244
+ callback.apply(this, arguments);
245
+ });
246
+ op.attempt(function() {
247
+ original2.apply(obj, args);
248
+ });
249
+ }, "retryWrapper")).bind(obj, original);
250
+ obj[method].options = options;
251
+ }
252
+ };
253
+ }
254
+ });
255
+
256
+ // ../../node_modules/.pnpm/retry@0.13.1/node_modules/retry/index.js
257
+ var require_retry2 = __commonJS({
258
+ "../../node_modules/.pnpm/retry@0.13.1/node_modules/retry/index.js"(exports, module2) {
259
+ module2.exports = require_retry();
260
+ }
261
+ });
262
+
31
263
  // src/client/index.ts
32
264
  var client_exports = {};
33
265
  __export(client_exports, {
34
266
  FrontendMonitor: () => FrontendMonitor,
35
267
  MonitorProvider: () => MonitorProvider,
36
268
  getSessionId: () => getSessionId,
269
+ initWindowMonitor: () => initWindowMonitor,
270
+ monitorStore: () => monitorStore,
37
271
  monitoredFetch: () => monitoredFetch
38
272
  });
39
273
  module.exports = __toCommonJS(client_exports);
@@ -428,8 +662,127 @@ __name(_APILogger, "APILogger");
428
662
  var APILogger = _APILogger;
429
663
  var defaultLogger = new APILogger();
430
664
 
665
+ // ../../node_modules/.pnpm/p-retry@6.2.1/node_modules/p-retry/index.js
666
+ var import_retry = __toESM(require_retry2(), 1);
667
+
668
+ // ../../node_modules/.pnpm/is-network-error@1.3.0/node_modules/is-network-error/index.js
669
+ var objectToString = Object.prototype.toString;
670
+ var isError = /* @__PURE__ */ __name((value) => objectToString.call(value) === "[object Error]", "isError");
671
+ var errorMessages = /* @__PURE__ */ new Set([
672
+ "network error",
673
+ // Chrome
674
+ "Failed to fetch",
675
+ // Chrome
676
+ "NetworkError when attempting to fetch resource.",
677
+ // Firefox
678
+ "The Internet connection appears to be offline.",
679
+ // Safari 16
680
+ "Network request failed",
681
+ // `cross-fetch`
682
+ "fetch failed",
683
+ // Undici (Node.js)
684
+ "terminated",
685
+ // Undici (Node.js)
686
+ " A network error occurred.",
687
+ // Bun (WebKit)
688
+ "Network connection lost"
689
+ // Cloudflare Workers (fetch)
690
+ ]);
691
+ function isNetworkError(error) {
692
+ const isValid = error && isError(error) && error.name === "TypeError" && typeof error.message === "string";
693
+ if (!isValid) {
694
+ return false;
695
+ }
696
+ const { message, stack } = error;
697
+ if (message === "Load failed") {
698
+ return stack === void 0 || "__sentry_captured__" in error;
699
+ }
700
+ if (message.startsWith("error sending request for url")) {
701
+ return true;
702
+ }
703
+ return errorMessages.has(message);
704
+ }
705
+ __name(isNetworkError, "isNetworkError");
706
+
707
+ // ../../node_modules/.pnpm/p-retry@6.2.1/node_modules/p-retry/index.js
708
+ var _AbortError = class _AbortError extends Error {
709
+ constructor(message) {
710
+ super();
711
+ if (message instanceof Error) {
712
+ this.originalError = message;
713
+ ({ message } = message);
714
+ } else {
715
+ this.originalError = new Error(message);
716
+ this.originalError.stack = this.stack;
717
+ }
718
+ this.name = "AbortError";
719
+ this.message = message;
720
+ }
721
+ };
722
+ __name(_AbortError, "AbortError");
723
+ var AbortError = _AbortError;
724
+ var decorateErrorWithCounts = /* @__PURE__ */ __name((error, attemptNumber, options) => {
725
+ const retriesLeft = options.retries - (attemptNumber - 1);
726
+ error.attemptNumber = attemptNumber;
727
+ error.retriesLeft = retriesLeft;
728
+ return error;
729
+ }, "decorateErrorWithCounts");
730
+ async function pRetry(input, options) {
731
+ return new Promise((resolve, reject) => {
732
+ options = { ...options };
733
+ options.onFailedAttempt ?? (options.onFailedAttempt = () => {
734
+ });
735
+ options.shouldRetry ?? (options.shouldRetry = () => true);
736
+ options.retries ?? (options.retries = 10);
737
+ const operation = import_retry.default.operation(options);
738
+ const abortHandler = /* @__PURE__ */ __name(() => {
739
+ operation.stop();
740
+ reject(options.signal?.reason);
741
+ }, "abortHandler");
742
+ if (options.signal && !options.signal.aborted) {
743
+ options.signal.addEventListener("abort", abortHandler, { once: true });
744
+ }
745
+ const cleanUp = /* @__PURE__ */ __name(() => {
746
+ options.signal?.removeEventListener("abort", abortHandler);
747
+ operation.stop();
748
+ }, "cleanUp");
749
+ operation.attempt(async (attemptNumber) => {
750
+ try {
751
+ const result = await input(attemptNumber);
752
+ cleanUp();
753
+ resolve(result);
754
+ } catch (error) {
755
+ try {
756
+ if (!(error instanceof Error)) {
757
+ throw new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`);
758
+ }
759
+ if (error instanceof AbortError) {
760
+ throw error.originalError;
761
+ }
762
+ if (error instanceof TypeError && !isNetworkError(error)) {
763
+ throw error;
764
+ }
765
+ decorateErrorWithCounts(error, attemptNumber, options);
766
+ if (!await options.shouldRetry(error)) {
767
+ operation.stop();
768
+ reject(error);
769
+ }
770
+ await options.onFailedAttempt(error);
771
+ if (!operation.retry(error)) {
772
+ throw operation.mainError();
773
+ }
774
+ } catch (finalError) {
775
+ decorateErrorWithCounts(finalError, attemptNumber, options);
776
+ cleanUp();
777
+ reject(finalError);
778
+ }
779
+ }
780
+ });
781
+ });
782
+ }
783
+ __name(pRetry, "pRetry");
784
+
431
785
  // src/_api/generated/cfg_monitor/retry.ts
432
- var import_p_retry = __toESM(require("p-retry"), 1);
433
786
  var DEFAULT_RETRY_CONFIG = {
434
787
  retries: 3,
435
788
  factor: 2,
@@ -458,13 +811,13 @@ function shouldRetry(error) {
458
811
  __name(shouldRetry, "shouldRetry");
459
812
  async function withRetry(fn, config) {
460
813
  const finalConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
461
- return (0, import_p_retry.default)(
814
+ return pRetry(
462
815
  async () => {
463
816
  try {
464
817
  return await fn();
465
818
  } catch (error) {
466
819
  if (!shouldRetry(error)) {
467
- throw new import_p_retry.AbortError(error);
820
+ throw new AbortError(error);
468
821
  }
469
822
  throw error;
470
823
  }
@@ -1237,9 +1590,76 @@ __name(monitoredFetch, "monitoredFetch");
1237
1590
 
1238
1591
  // src/client/MonitorProvider.tsx
1239
1592
  var import_react = require("react");
1593
+
1594
+ // src/client/window.ts
1595
+ function makeEvent(type, level, message, extra) {
1596
+ const state = monitorStore.getState();
1597
+ const cfg = state.config;
1598
+ return {
1599
+ event_type: type,
1600
+ level,
1601
+ message,
1602
+ session_id: getSessionId(),
1603
+ url: window.location.href,
1604
+ user_agent: navigator.userAgent,
1605
+ project_name: cfg.project ?? "",
1606
+ environment: cfg.environment ?? "",
1607
+ ...extra ? { extra } : {}
1608
+ };
1609
+ }
1610
+ __name(makeEvent, "makeEvent");
1611
+ function initWindowMonitor() {
1612
+ if (typeof window === "undefined") return;
1613
+ const api = {
1614
+ error(message, extra) {
1615
+ monitorStore.getState().push(makeEvent("JS_ERROR" /* JS_ERROR */, "error" /* ERROR */, message, extra));
1616
+ console.log("[monitor] error captured \u2192", message);
1617
+ },
1618
+ warn(message, extra) {
1619
+ monitorStore.getState().push(makeEvent("WARNING" /* WARNING */, "warn" /* WARN */, message, extra));
1620
+ console.log("[monitor] warn captured \u2192", message);
1621
+ },
1622
+ info(message, extra) {
1623
+ monitorStore.getState().push(makeEvent("INFO" /* INFO */, "info" /* INFO */, message, extra));
1624
+ console.log("[monitor] info captured \u2192", message);
1625
+ },
1626
+ network(status, method, url, extra) {
1627
+ const event = makeEvent(
1628
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1629
+ status >= 500 ? "error" /* ERROR */ : "warn" /* WARN */,
1630
+ `${method} ${url} \u2192 ${status}`,
1631
+ extra
1632
+ );
1633
+ monitorStore.getState().push({ ...event, http_status: status, http_method: method, http_url: url });
1634
+ console.log("[monitor] network captured \u2192", method, url, status);
1635
+ },
1636
+ capture(event) {
1637
+ monitorStore.getState().push(event);
1638
+ console.log("[monitor] event captured \u2192", event);
1639
+ },
1640
+ flush() {
1641
+ monitorStore.getState().flush();
1642
+ console.log("[monitor] flushed");
1643
+ },
1644
+ status() {
1645
+ const state = monitorStore.getState();
1646
+ console.group("[monitor] status");
1647
+ console.log("config:", state.config);
1648
+ console.log("buffer size:", state.buffer.length);
1649
+ console.log("initialized:", state.initialized);
1650
+ console.log("session_id:", getSessionId());
1651
+ console.groupEnd();
1652
+ }
1653
+ };
1654
+ window.monitor = api;
1655
+ }
1656
+ __name(initWindowMonitor, "initWindowMonitor");
1657
+
1658
+ // src/client/MonitorProvider.tsx
1240
1659
  function MonitorProvider({ children, ...config }) {
1241
1660
  (0, import_react.useEffect)(() => {
1242
1661
  FrontendMonitor.init(config);
1662
+ initWindowMonitor();
1243
1663
  return () => FrontendMonitor.destroy();
1244
1664
  }, []);
1245
1665
  return children ?? null;
@@ -1252,7 +1672,8 @@ var cleanupFns = [];
1252
1672
  var FrontendMonitor = {
1253
1673
  init(config = {}) {
1254
1674
  if (typeof window === "undefined") return;
1255
- if (config.baseUrl) configureMonitorApi(config.baseUrl);
1675
+ const baseUrl = config.baseUrl ?? process.env.NEXT_PUBLIC_API_URL ?? "";
1676
+ if (baseUrl) configureMonitorApi(baseUrl);
1256
1677
  monitorStore.getState().setConfig(config);
1257
1678
  ensureSessionCookie();
1258
1679
  if (config.captureJsErrors !== false) cleanupFns.push(installJsErrorCapture());