@djangocfg/monitor 2.1.216 → 2.1.217

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
@@ -1,8 +1,6 @@
1
1
  # @djangocfg/monitor
2
2
 
3
- Browser error and event monitoring SDK for django-cfg backends.
4
-
5
- **Part of [DjangoCFG](https://djangocfg.com)** — modern Django framework for production-ready SaaS applications.
3
+ Browser + server error monitoring SDK for [DjangoCFG](https://djangocfg.com) backends.
6
4
 
7
5
  ## Install
8
6
 
@@ -10,112 +8,27 @@ Browser error and event monitoring SDK for django-cfg backends.
10
8
  pnpm add @djangocfg/monitor
11
9
  ```
12
10
 
13
- ## What's Inside
14
-
15
- - **JS Error Capture** — `window.onerror` + `unhandledrejection` with SHA-256 fingerprinting
16
- - **Console Capture** — intercepts `console.warn` / `console.error` (consola reporter or native patch)
17
- - **Network Monitoring** — `monitoredFetch` wrapper reports non-2xx responses
18
- - **Zod Validation Errors** — listens to `zod-validation-error` custom events (auto-integrates with `@djangocfg/api`)
19
- - **Session Tracking** — anonymous `fm_session_id` UUID in localStorage + cookie
20
- - **Server-side Capture** — Node.js / Edge Runtime safe, no browser APIs
21
- - **Auto-flush** — batched delivery via generated API client; `KeepAliveFetchAdapter` survives page unload
22
- - **Type-safe** — types generated from Django OpenAPI schema via `make gen`
23
-
24
11
  ## Entry Points
25
12
 
26
- | Entry | Import | Description |
27
- |-------|--------|-------------|
28
- | **Main** | `@djangocfg/monitor` | Types only, server-safe |
29
- | **Client** | `@djangocfg/monitor/client` | Browser SDK (`"use client"`) |
30
- | **Server** | `@djangocfg/monitor/server` | Node.js / Edge Runtime |
31
-
32
- ## Package Structure
33
-
34
- ```
35
- src/
36
- ├── _api/
37
- │ ├── BaseClient.ts # monitorApi singleton (MemoryStorageAdapter)
38
- │ ├── index.ts # Re-exports types, enums, monitorApi
39
- │ └── generated/
40
- │ └── cfg_monitor/ # Auto-generated — do not edit
41
- │ ├── http.ts # FetchAdapter, KeepAliveFetchAdapter
42
- │ └── ...
43
- ├── types/
44
- │ ├── events.ts # EventType, EventLevel, MonitorEvent
45
- │ └── config.ts # MonitorConfig, ServerMonitorConfig
46
- ├── client/
47
- │ ├── index.ts # FrontendMonitor, monitoredFetch, getSessionId
48
- │ ├── capture/
49
- │ │ ├── js-errors.ts # window.onerror + unhandledrejection
50
- │ │ ├── console.ts # consola reporter / console.* patch
51
- │ │ ├── network.ts # monitoredFetch wrapper
52
- │ │ ├── validation.ts # zod-validation-error CustomEvent listener
53
- │ │ ├── session.ts # UUID session management
54
- │ │ └── fingerprint.ts # SHA-256 deduplication fingerprint
55
- │ ├── store/
56
- │ │ └── index.ts # zustand vanilla event buffer
57
- │ └── transport/
58
- │ └── ingest.ts # sendBatch via monitorApi / KeepAliveFetchAdapter
59
- ├── server/
60
- │ └── index.ts # serverMonitor singleton
61
- └── index.ts # Types-only re-export
62
- ```
63
-
64
- ## How It Works
65
-
66
- All transport uses the **generated API client** (`monitorApi`) — no hardcoded URLs or raw `fetch` calls.
67
-
68
- - **Normal flush** → `monitorApi.monitor.ingestCreate(batch)` (standard `FetchAdapter`)
69
- - **Page unload flush** → separate `monitorApiBeacon` instance with `KeepAliveFetchAdapter` — sets `keepalive: true` on every request, so the browser completes delivery even after navigation
70
-
71
- **Automatic error collection** (no extra setup after `FrontendMonitor.init()`):
72
-
73
- | Source | Mechanism |
74
- |--------|-----------|
75
- | JS exceptions | `window.onerror` + `unhandledrejection` |
76
- | Console warnings/errors | consola reporter or `console.*` patch |
77
- | `@djangocfg/api` Zod failures | `zod-validation-error` CustomEvent |
78
- | Network errors | `monitoredFetch` wrapper |
79
- | Server exceptions | `serverMonitor.captureError()` |
80
-
81
- ## Client Usage (React / Next.js)
13
+ | Entry | Use |
14
+ |-------|-----|
15
+ | `@djangocfg/monitor` | Types only server-safe |
16
+ | `@djangocfg/monitor/client` | Browser SDK (`'use client'`) |
17
+ | `@djangocfg/monitor/server` | Node.js / Edge Runtime |
82
18
 
83
- ### MonitorProvider
19
+ ## Client (React / Next.js)
84
20
 
85
- ```tsx
86
- // components/MonitorProvider.tsx
87
- 'use client'
88
-
89
- import { useEffect } from 'react'
90
- import { FrontendMonitor } from '@djangocfg/monitor/client'
91
-
92
- export function MonitorProvider() {
93
- useEffect(() => {
94
- FrontendMonitor.init({
95
- project: process.env.NEXT_PUBLIC_PROJECT_NAME ?? 'my-app',
96
- environment: process.env.NODE_ENV,
97
- // baseUrl: 'https://api.myapp.com', // default: same origin
98
- // flushInterval: 5000, // default: 5s
99
- // captureJsErrors: true, // default: true
100
- // captureConsole: true, // default: true
101
- // debug: false,
102
- })
103
- return () => FrontendMonitor.destroy()
104
- }, [])
105
-
106
- return null
107
- }
108
- ```
21
+ Drop `MonitorProvider` into your root layout — that's all:
109
22
 
110
23
  ```tsx
111
24
  // app/layout.tsx
112
- import { MonitorProvider } from '@/components/MonitorProvider'
25
+ import { MonitorProvider } from '@djangocfg/monitor/client'
113
26
 
114
27
  export default function RootLayout({ children }) {
115
28
  return (
116
- <html lang="en">
29
+ <html>
117
30
  <body>
118
- <MonitorProvider />
31
+ <MonitorProvider project="my-app" environment={process.env.NODE_ENV} />
119
32
  {children}
120
33
  </body>
121
34
  </html>
@@ -123,18 +36,18 @@ export default function RootLayout({ children }) {
123
36
  }
124
37
  ```
125
38
 
126
- ### Network Monitoring
39
+ `MonitorProvider` initialises `FrontendMonitor` on mount and tears it down on unmount.
127
40
 
128
- ```typescript
129
- import { monitoredFetch } from '@djangocfg/monitor/client'
41
+ **What gets captured automatically** (no extra setup):
130
42
 
131
- const response = await monitoredFetch('/api/orders', {
132
- method: 'POST',
133
- body: JSON.stringify(order),
134
- })
135
- ```
43
+ | Source | Mechanism |
44
+ |--------|-----------|
45
+ | JS exceptions | `window.onerror` + `unhandledrejection` |
46
+ | Console warnings/errors | consola reporter or `console.*` patch |
47
+ | `@djangocfg/api` Zod failures | `zod-validation-error` CustomEvent |
48
+ | Network errors | `monitoredFetch` wrapper |
136
49
 
137
- ### Manual Event Capture
50
+ ### Manual capture
138
51
 
139
52
  ```typescript
140
53
  import { FrontendMonitor } from '@djangocfg/monitor/client'
@@ -149,40 +62,41 @@ FrontendMonitor.capture({
149
62
  })
150
63
  ```
151
64
 
152
- ### Session ID
65
+ ### Network wrapper
153
66
 
154
67
  ```typescript
155
- import { getSessionId } from '@djangocfg/monitor/client'
68
+ import { monitoredFetch } from '@djangocfg/monitor/client'
156
69
 
157
- const sessionId = getSessionId() // UUID stored in localStorage + cookie
70
+ const res = await monitoredFetch('/api/orders', { method: 'POST', body: JSON.stringify(order) })
158
71
  ```
159
72
 
160
- ## Server Usage (Next.js Route Handlers / Server Components)
73
+ ### Config options
74
+
75
+ | Option | Type | Default | Description |
76
+ |--------|------|---------|-------------|
77
+ | `project` | `string` | `''` | Project name sent with every event |
78
+ | `environment` | `string` | `''` | `production` / `staging` / `development` |
79
+ | `baseUrl` | `string` | same origin | Base URL of the django-cfg backend |
80
+ | `flushInterval` | `number` | `5000` | Buffer flush interval (ms) |
81
+ | `maxBufferSize` | `number` | `20` | Max events before immediate flush |
82
+ | `captureJsErrors` | `boolean` | `true` | Capture `window.onerror` + unhandled rejections |
83
+ | `captureConsole` | `boolean` | `true` | Intercept `console.warn` / `console.error` |
84
+ | `debug` | `boolean` | `false` | Log init info to console |
161
85
 
162
- ### Configure once
86
+ ## Server (Next.js Route Handlers)
163
87
 
164
88
  ```typescript
165
89
  // lib/monitor.ts
166
90
  import { serverMonitor } from '@djangocfg/monitor/server'
167
-
168
- serverMonitor.configure({
169
- project: process.env.PROJECT_NAME ?? 'my-app',
170
- environment: process.env.NODE_ENV,
171
- // baseUrl: 'https://api.myapp.com', // required if backend is on different origin
172
- })
173
-
91
+ serverMonitor.configure({ project: 'my-app', environment: process.env.NODE_ENV })
174
92
  export { serverMonitor }
175
- ```
176
-
177
- ### Route Handler
178
93
 
179
- ```typescript
180
94
  // app/api/orders/route.ts
181
95
  import { serverMonitor } from '@/lib/monitor'
182
96
 
183
97
  export async function POST(req: Request) {
184
98
  try {
185
- // ... handle request
99
+ // ...
186
100
  } catch (err) {
187
101
  await serverMonitor.captureError(err, { url: req.url })
188
102
  return new Response('Internal Server Error', { status: 500 })
@@ -190,152 +104,30 @@ export async function POST(req: Request) {
190
104
  }
191
105
  ```
192
106
 
193
- ### Network Error
107
+ Or use the `withMonitor` HOC from `@djangocfg/nextjs/monitor`:
194
108
 
195
109
  ```typescript
196
- const response = await fetch(upstreamUrl)
197
- if (!response.ok) {
198
- await serverMonitor.captureNetworkError(
199
- response.status,
200
- 'GET',
201
- upstreamUrl,
202
- { pageUrl: req.url },
203
- )
204
- }
205
- ```
206
-
207
- ### Arbitrary Event
208
-
209
- ```typescript
210
- import { EventType, EventLevel } from '@djangocfg/monitor/server'
211
-
212
- await serverMonitor.capture({
213
- event_type: EventType.WARNING,
214
- level: EventLevel.WARN,
215
- message: 'Rate limit approaching',
216
- url: req.url,
217
- extra: { remaining: 5 },
218
- })
219
- ```
220
-
221
- ## Types
222
-
223
- ```typescript
224
- import type { MonitorConfig, MonitorEvent } from '@djangocfg/monitor'
225
- import { EventType, EventLevel } from '@djangocfg/monitor'
226
-
227
- EventType.JS_ERROR // 'js_error'
228
- EventType.NETWORK_ERROR // 'network_error'
229
- EventType.WARNING // 'warning'
230
- EventType.ERROR // 'error'
231
- EventType.INFO // 'info'
232
-
233
- EventLevel.ERROR // 'error'
234
- EventLevel.WARN // 'warn'
235
- EventLevel.INFO // 'info'
236
- ```
237
-
238
- ### MonitorConfig
239
-
240
- | Option | Type | Default | Description |
241
- |--------|------|---------|-------------|
242
- | `project` | `string` | `''` | Project name sent with every event |
243
- | `environment` | `string` | `''` | `production` / `staging` / `development` |
244
- | `baseUrl` | `string` | `''` | Base URL of the django-cfg backend (same origin by default) |
245
- | `flushInterval` | `number` | `5000` | Buffer flush interval (ms) |
246
- | `maxBufferSize` | `number` | `20` | Max events before immediate flush |
247
- | `captureJsErrors` | `boolean` | `true` | Capture `window.onerror` + unhandled rejections |
248
- | `captureConsole` | `boolean` | `true` | Intercept `console.warn` / `console.error` |
249
- | `debug` | `boolean` | `false` | Log init info to console |
110
+ import { withMonitor } from '@djangocfg/nextjs/monitor'
250
111
 
251
- ### ServerMonitorConfig
252
-
253
- | Option | Type | Default | Description |
254
- |--------|------|---------|-------------|
255
- | `project` | `string` | `''` | Project name |
256
- | `environment` | `string` | `''` | Environment name |
257
- | `baseUrl` | `string` | `''` | Base URL of the django-cfg backend (absolute required on server) |
258
-
259
- ## Zod Validation Integration
260
-
261
- `@djangocfg/api` automatically dispatches a `zod-validation-error` CustomEvent whenever a response fails schema validation. `@djangocfg/monitor/client` listens and forwards it as a `WARNING` — zero setup needed.
262
-
263
- ```
264
- @djangocfg/api fetcher validates response with Zod
265
- → dispatches window CustomEvent('zod-validation-error', { detail })
266
- → @djangocfg/monitor/client capture/validation.ts catches it
267
- → pushed to store → flushed to backend
268
- ```
269
-
270
- ## Custom HTTP Adapter
271
-
272
- The generated `KeepAliveFetchAdapter` is also available if you need it elsewhere:
273
-
274
- ```typescript
275
- import { KeepAliveFetchAdapter } from '@djangocfg/monitor/client'
276
- // or directly from generated:
277
- import { API, KeepAliveFetchAdapter } from '@djangocfg/monitor/client'
278
-
279
- const api = new API('https://api.myapp.com', {
280
- httpClient: new KeepAliveFetchAdapter(),
112
+ export const POST = withMonitor(async (req) => {
113
+ // errors are captured automatically
281
114
  })
282
115
  ```
283
116
 
284
117
  ## Django Backend
285
118
 
286
- Enable the `cfg_monitor` extension:
287
-
288
119
  ```python
289
120
  # cfg.py
290
- from django_cfg import DjangoConfig
291
-
292
121
  class Config(DjangoConfig):
293
- installed_apps = [
294
- ...
295
- 'django_cfg.apps.system.monitor',
296
- ]
122
+ installed_apps = [..., 'django_cfg.apps.system.monitor']
297
123
  ```
298
124
 
299
- | | |
300
- |---|---|
301
- | Endpoint | `POST /cfg/monitor/ingest/` |
302
- | Auth | not required |
303
- | Rate limit | 100 req/min per IP |
304
- | Batch size | up to 50 events |
125
+ Ingest endpoint: `POST /cfg/monitor/ingest/` — no auth required, 100 req/min per IP.
305
126
 
306
- ## Development
307
-
308
- ```bash
309
- # Regenerate API client from Django OpenAPI schema
310
- make generate
127
+ ## Safety
311
128
 
312
- # Build package
313
- make build
314
-
315
- # Watch mode
316
- pnpm dev
317
-
318
- # Type check
319
- pnpm check
320
- ```
321
-
322
- ## Peer Dependencies
323
-
324
- | Package | Required | Description |
325
- |---------|----------|-------------|
326
- | `consola` | optional | Uses consola reporter instead of patching `console.*` |
327
- | `zustand` | optional | Required for the client entry point event buffer |
328
-
329
- ## Requirements
330
-
331
- - Next.js >= 15 (or any React >= 19 app)
332
- - Django with `django_cfg >= 1.7` and `monitor` extension
129
+ 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.
333
130
 
334
131
  ## License
335
132
 
336
133
  MIT
337
-
338
- ## Links
339
-
340
- - [DjangoCFG Documentation](https://djangocfg.com)
341
- - [GitHub](https://github.com/markolofsen/django-cfg)
package/dist/client.cjs CHANGED
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var client_exports = {};
33
33
  __export(client_exports, {
34
34
  FrontendMonitor: () => FrontendMonitor,
35
+ MonitorProvider: () => MonitorProvider,
35
36
  getSessionId: () => getSessionId,
36
37
  monitoredFetch: () => monitoredFetch
37
38
  });
@@ -1234,6 +1235,17 @@ async function monitoredFetch(input, init) {
1234
1235
  }
1235
1236
  __name(monitoredFetch, "monitoredFetch");
1236
1237
 
1238
+ // src/client/MonitorProvider.tsx
1239
+ var import_react = require("react");
1240
+ function MonitorProvider({ children, ...config }) {
1241
+ (0, import_react.useEffect)(() => {
1242
+ FrontendMonitor.init(config);
1243
+ return () => FrontendMonitor.destroy();
1244
+ }, []);
1245
+ return children ?? null;
1246
+ }
1247
+ __name(MonitorProvider, "MonitorProvider");
1248
+
1237
1249
  // src/client/index.ts
1238
1250
  var flushInterval = null;
1239
1251
  var cleanupFns = [];