@watchforge/browser 0.1.3 → 0.1.5

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.
@@ -2,10 +2,51 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The WatchForge JavaScript SDK automatically captures errors and events in:
5
+ The WatchForge JavaScript SDK is one npm package with typed framework entry points for:
6
+
7
+ - **Browser JavaScript** applications
8
+ - **Next.js** App Router applications
9
+ - **React** frontend applications
6
10
  - **Node.js** applications
7
11
  - **Express.js** web servers
8
- - **React** frontend applications
12
+
13
+ This package intentionally stays unified instead of splitting into separate `watchforge-next-sdk`, `watchforge-node-sdk`, `watchforge-react-sdk`, and `watchforge-express-sdk` packages. The shared package keeps DSN handling, event transport, breadcrumbs, stack traces, source context, and session replay consistent across JavaScript runtimes.
14
+
15
+ ## Supported Imports
16
+
17
+ ```ts
18
+ // Browser JavaScript, React client, and shared APIs
19
+ import { register, captureException, captureMessage } from "@watchforge/browser";
20
+
21
+ // Next.js App Router client provider
22
+ import { WatchForgeProvider } from "@watchforge/browser/next";
23
+
24
+ // Next.js server route handlers
25
+ import { withWatchForgeRouteHandler } from "@watchforge/browser/next/server";
26
+
27
+ // React error boundary
28
+ import { ErrorBoundary } from "@watchforge/browser/react";
29
+
30
+ // Express middleware
31
+ import {
32
+ expressRequestMiddleware,
33
+ expressMiddleware,
34
+ } from "@watchforge/browser/express";
35
+
36
+ // Explicit Node.js import
37
+ import { register as registerNode } from "@watchforge/browser/node";
38
+ ```
39
+
40
+ ## Support Matrix
41
+
42
+ | Runtime / Framework | Entry point | Recommended setup |
43
+ | --- | --- | --- |
44
+ | Browser JavaScript | `@watchforge/browser` | Call `register()` once in the main browser bundle |
45
+ | React | `@watchforge/browser` + `@watchforge/browser/react` | Call `register()` once and wrap the app with `ErrorBoundary` |
46
+ | Next.js App Router client | `@watchforge/browser/next` | Use the wizard or render `WatchForgeProvider` from a client component |
47
+ | Next.js App Router server | `@watchforge/browser/next/server` | Wrap route handlers with `withWatchForgeRouteHandler()` |
48
+ | Node.js | `@watchforge/browser/node` | Call `register()` once during process startup |
49
+ | Express.js | `@watchforge/browser/express` | Add request middleware before routes and error middleware after routes |
9
50
 
10
51
  ## Installation
11
52
 
@@ -64,8 +105,39 @@ npm install ../watchforge-javascript-sdk
64
105
  ```
65
106
 
66
107
  After installation, use it the same way:
67
- ```javascript
68
- import { register, ErrorBoundary } from '@watchforge/browser';
108
+ ```ts
109
+ import { register } from "@watchforge/browser";
110
+ import { ErrorBoundary } from "@watchforge/browser/react";
111
+ ```
112
+
113
+ ### Next.js Wizard Setup
114
+
115
+ From your Next.js project root:
116
+
117
+ ```bash
118
+ npx @watchforge/browser -i nextjs --dsn "https://PUBLIC_KEY@dev.watchforges.com/PROJECT_ID" --app-env production
119
+ ```
120
+
121
+ With error-triggered Session Replay:
122
+
123
+ ```bash
124
+ npx @watchforge/browser -i nextjs \
125
+ --dsn "https://PUBLIC_KEY@dev.watchforges.com/PROJECT_ID" \
126
+ --app-env production \
127
+ --replays-on-error 1
128
+ ```
129
+
130
+ The wizard:
131
+
132
+ 1. Installs `@watchforge/browser`
133
+ 2. Creates `watchforge.config.ts`
134
+ 3. Creates `app/watchforge-init.tsx` (or `src/app/.../watchforge-init.tsx`)
135
+ 4. Patches `app/layout.tsx` to render `<WatchForgeInit />`
136
+
137
+ If you only want file generation and no package install:
138
+
139
+ ```bash
140
+ npx @watchforge/browser -i nextjs --dsn "..." --skip-install
69
141
  ```
70
142
 
71
143
  ## Quick Testing
@@ -158,11 +230,92 @@ See the React setup section below for complete examples.
158
230
 
159
231
  ## Quick Start
160
232
 
233
+ ### Browser JavaScript
234
+
235
+ ```ts
236
+ import { register } from "@watchforge/browser";
237
+
238
+ register({
239
+ dsn: "https://PUBLIC_KEY@watchforge.io/PROJECT_ID",
240
+ app_env: "production",
241
+ });
242
+ ```
243
+
244
+ This captures uncaught browser errors, unhandled promise rejections, breadcrumbs, page context, browser/device/OS details, and performance context.
245
+
246
+ ### Next.js App Router
247
+
248
+ Create `watchforge.config.ts` in your project root:
249
+
250
+ ```ts
251
+ export const watchforgeConfig = {
252
+ dsn: "https://PUBLIC_KEY@watchforge.io/PROJECT_ID",
253
+ app_env: "production",
254
+ replaysOnErrorSampleRate: 1,
255
+ maskAllInputs: true,
256
+ };
257
+ ```
258
+
259
+ Create a client init component beside your frontend layout, for example `src/app/watchforge-init.tsx` or `src/app/(frontend)/watchforge-init.tsx`:
260
+
261
+ ```tsx
262
+ "use client";
263
+
264
+ import { WatchForgeProvider } from "@watchforge/browser/next";
265
+ import { watchforgeConfig } from "../watchforge.config";
266
+
267
+ export default function WatchForgeInit() {
268
+ return <WatchForgeProvider options={watchforgeConfig} />;
269
+ }
270
+ ```
271
+
272
+ Render it once in the matching `layout.tsx`:
273
+
274
+ ```tsx
275
+ import WatchForgeInit from "./watchforge-init";
276
+
277
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
278
+ return (
279
+ <html lang="en">
280
+ <body>
281
+ <WatchForgeInit />
282
+ {children}
283
+ </body>
284
+ </html>
285
+ );
286
+ }
287
+ ```
288
+
289
+ For route-group projects like `src/app/(frontend)/layout.tsx`, put `watchforge-init.tsx` in that same route group and adjust the config import path if needed.
290
+
291
+ For Next.js route handlers, use the server entry point:
292
+
293
+ ```ts
294
+ // app/api/example/route.ts
295
+ import {
296
+ register,
297
+ withWatchForgeRouteHandler,
298
+ } from "@watchforge/browser/next/server";
299
+
300
+ register({
301
+ dsn: "https://PUBLIC_KEY@watchforge.io/PROJECT_ID",
302
+ app_env: "production",
303
+ });
304
+
305
+ export const GET = withWatchForgeRouteHandler(async () => {
306
+ throw new Error("Next.js API route test error");
307
+ });
308
+ ```
309
+
161
310
  ### 1. Node.js Application
162
311
 
163
- ```javascript
312
+ ```ts
164
313
  // app.js
165
- const { register, captureException, captureMessage } = require('@watchforge/browser');
314
+ import {
315
+ register,
316
+ captureException,
317
+ captureMessage,
318
+ } from "@watchforge/browser/node";
166
319
 
167
320
  // Initialize SDK
168
321
  // API URL is automatically derived from DSN host
@@ -184,10 +337,14 @@ captureMessage("User logged in", "info");
184
337
 
185
338
  ### 2. Express.js Application
186
339
 
187
- ```javascript
340
+ ```ts
188
341
  // server.js
189
- const express = require('express');
190
- const { register, expressMiddleware } = require('@watchforge/browser');
342
+ import express from "express";
343
+ import { register } from "@watchforge/browser/node";
344
+ import {
345
+ expressRequestMiddleware,
346
+ expressMiddleware,
347
+ } from "@watchforge/browser/express";
191
348
 
192
349
  const app = express();
193
350
 
@@ -710,7 +867,11 @@ try {
710
867
  ```javascript
711
868
  // server.js
712
869
  const express = require('express');
713
- const { register, expressMiddleware } = require('@watchforge/browser');
870
+ const {
871
+ register,
872
+ expressRequestMiddleware,
873
+ expressMiddleware,
874
+ } = require('@watchforge/browser');
714
875
 
715
876
  const app = express();
716
877
 
@@ -719,6 +880,12 @@ register({
719
880
  app_env: process.env.NODE_ENV,
720
881
  });
721
882
 
883
+ app.use(express.json());
884
+ app.use(expressRequestMiddleware());
885
+ app.use(expressRequestMiddleware());
886
+
887
+ // routes here
888
+
722
889
  // Add error handler (must be last middleware)
723
890
  app.use(expressMiddleware());
724
891
 
@@ -735,6 +902,11 @@ import App from './App';
735
902
  register({
736
903
  dsn: process.env.REACT_APP_WATCHFORGE_DSN,
737
904
  app_env: process.env.NODE_ENV,
905
+ // Optional: upload the last 60s of masked browser replay events when an error occurs.
906
+ replaysOnErrorSampleRate: 1.0,
907
+ // Optional: continuously sample full sessions. Keep 0 in production unless you need it.
908
+ replaysSessionSampleRate: 0,
909
+ maskAllInputs: true,
738
910
  });
739
911
 
740
912
  ReactDOM.render(
@@ -745,6 +917,95 @@ ReactDOM.render(
745
917
  );
746
918
  ```
747
919
 
920
+ ## Stack Trace Source Context
921
+
922
+ The SDK enriches stack frames with Python-style source context when possible:
923
+
924
+ | Field | Description |
925
+ |-------|-------------|
926
+ | `context_line` | The exact line where the error occurred |
927
+ | `pre_context` | Up to 5 lines before the error |
928
+ | `post_context` | Up to 5 lines after the error |
929
+
930
+ ### Node.js / Express
931
+
932
+ For local project files, the SDK reads source from disk and attaches context lines automatically. No extra configuration is required.
933
+
934
+ ### Browser (development)
935
+
936
+ In dev builds (Next.js, Vite, etc.), the SDK attempts to fetch same-origin script URLs from the stack trace and slice source lines around the reported line number.
937
+
938
+ Works when stack frames point to readable URLs such as:
939
+
940
+ ```txt
941
+ http://localhost:3000/_next/static/chunks/...
942
+ http://localhost:3000/src/App.tsx
943
+ ```
944
+
945
+ ### Browser (production / minified bundles)
946
+
947
+ Production bundles often report locations like:
948
+
949
+ ```txt
950
+ https://your-app.com/_next/static/chunks/app-abc123.js:1:98432
951
+ ```
952
+
953
+ The SDK still sends filename, line, and column, but **cannot** show original TypeScript/JSX source without **source maps**.
954
+
955
+ **Current status:** source map upload and symbolication are not yet supported (planned for a future release).
956
+
957
+ **Workarounds today:**
958
+
959
+ 1. Test with `npm run dev` / unminified builds to see source lines in WatchForge.
960
+ 2. Use `release` in `register()` so issues are grouped by deploy version.
961
+ 3. Rely on breadcrumbs and the Browser Event Summary in the dashboard to understand user actions before the error.
962
+
963
+ When source map support ships, you will upload maps during deploy and WatchForge will resolve minified frames back to original files.
964
+
965
+ ## Session Replay
966
+
967
+ The browser SDK can record a Sentry-style DOM replay using `rrweb`. Replays are **not videos**; they are masked DOM snapshots and incremental browser events that WatchForge can play back in the Issue Detail page.
968
+
969
+ Enable replay capture when registering the SDK:
970
+
971
+ ```javascript
972
+ register({
973
+ dsn: "https://PUBLIC_KEY@dev.watchforges.com/PROJECT_ID",
974
+ app_env: "production",
975
+ replaysOnErrorSampleRate: 1.0,
976
+ replaysSessionSampleRate: 0,
977
+ maskAllInputs: true,
978
+ });
979
+ ```
980
+
981
+ Replay behavior:
982
+
983
+ - `replaysOnErrorSampleRate`: records in buffer mode and uploads the last 60 seconds when an error is captured.
984
+ - `replaysSessionSampleRate`: continuously samples full browser sessions.
985
+ - Text/input privacy is handled in the browser before upload.
986
+ - Password, email, tel and text inputs are masked when `maskAllInputs` is true.
987
+ - Elements with `.rr-block` are blocked.
988
+ - Elements with `.rr-ignore` ignore input events.
989
+ - Elements with `.rr-mask` mask text.
990
+
991
+ When an error is captured, the SDK attaches:
992
+
993
+ ```json
994
+ {
995
+ "replay_id": "...",
996
+ "session_id": "...",
997
+ "contexts": {
998
+ "replay": {
999
+ "replay_id": "...",
1000
+ "session_id": "...",
1001
+ "sampled": true
1002
+ }
1003
+ }
1004
+ }
1005
+ ```
1006
+
1007
+ The dashboard shows the linked replay in the Issue Detail **Session Replay** tab.
1008
+
748
1009
  ## Test Scripts
749
1010
 
750
1011
  Test scripts are included in the SDK directory:
package/README.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # WatchForge JavaScript SDK (`@watchforge/browser`)
2
2
 
3
- Browser and Node SDK for WatchForge. **One call to `register()`** turns on automatic error reporting—no manual `try/catch` required for typical crashes.
3
+ Browser, Next.js, React, Node.js, and Express SDK for WatchForge. **One call to `register()`** turns on automatic error reporting for typical crashes, and framework entry points add richer context where needed.
4
+
5
+ ## Runtime Support
6
+
7
+ | Runtime / Framework | Import | What it captures |
8
+ | --- | --- | --- |
9
+ | Browser JavaScript | `@watchforge/browser` | Uncaught errors, unhandled promise rejections, breadcrumbs, browser/device/page/performance context |
10
+ | React | `@watchforge/browser` + `@watchforge/browser/react` | Browser errors plus React `ErrorBoundary` component stack context |
11
+ | Next.js App Router | `@watchforge/browser/next` | Client-side Next.js errors through a client provider; wizard patches the app layout |
12
+ | Node.js | `@watchforge/browser` or `@watchforge/browser/node` | `uncaughtException`, `unhandledRejection`, Node runtime/server context |
13
+ | Express.js | `@watchforge/browser/express` | Express request errors, request URL/method/headers/body/query, user/IP, route, duration |
14
+
15
+ The package is intentionally shipped as **one npm package** with typed subpath exports instead of separate SDK packages. This keeps DSN setup, replay, stack traces, breadcrumbs, and event transport consistent across JavaScript runtimes.
4
16
 
5
17
  ## What this package does *not* require
6
18
 
@@ -18,6 +30,150 @@ Browser and Node SDK for WatchForge. **One call to `register()`** turns on autom
18
30
  npm install @watchforge/browser
19
31
  ```
20
32
 
33
+ ## Next.js one-line setup
34
+
35
+ For Next.js apps, run the setup wizard from your app root:
36
+
37
+ ```bash
38
+ npx @watchforge/browser -i nextjs --dsn "https://PUBLIC_KEY@your-host/PROJECT_ID" --app-env production
39
+ ```
40
+
41
+ With Session Replay on errors:
42
+
43
+ ```bash
44
+ npx @watchforge/browser -i nextjs \
45
+ --dsn "https://PUBLIC_KEY@your-host/PROJECT_ID" \
46
+ --app-env production \
47
+ --replays-on-error 1
48
+ ```
49
+
50
+ The wizard installs `@watchforge/browser`, writes `watchforge.config.ts`, creates a client init component, and patches `app/layout.tsx` or `pages/_app.tsx`.
51
+
52
+ ## Framework Imports
53
+
54
+ WatchForge ships as one JavaScript SDK package with typed framework entry points:
55
+
56
+ ```ts
57
+ // Browser / React client / Node global handlers
58
+ import { register } from "@watchforge/browser";
59
+
60
+ // Next.js App Router client provider
61
+ import { WatchForgeProvider } from "@watchforge/browser/next";
62
+
63
+ // Next.js App Router server route helper
64
+ import { withWatchForgeRouteHandler } from "@watchforge/browser/next/server";
65
+
66
+ // React error boundary
67
+ import { ErrorBoundary } from "@watchforge/browser/react";
68
+
69
+ // Express middleware
70
+ import {
71
+ expressRequestMiddleware,
72
+ expressMiddleware,
73
+ } from "@watchforge/browser/express";
74
+
75
+ // Optional explicit Node import
76
+ import { captureException } from "@watchforge/browser/node";
77
+ ```
78
+
79
+ ## Next.js Manual Setup
80
+
81
+ Use the wizard when possible. For manual setup in App Router projects, create a client init component for browser-side errors:
82
+
83
+ ```tsx
84
+ "use client";
85
+
86
+ import { WatchForgeProvider } from "@watchforge/browser/next";
87
+ import { watchforgeConfig } from "../watchforge.config";
88
+
89
+ export default function WatchForgeInit() {
90
+ return <WatchForgeProvider options={watchforgeConfig} />;
91
+ }
92
+ ```
93
+
94
+ Then render it once in your frontend `layout.tsx`:
95
+
96
+ ```tsx
97
+ import WatchForgeInit from "./watchforge-init";
98
+
99
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
100
+ return (
101
+ <html lang="en">
102
+ <body>
103
+ <WatchForgeInit />
104
+ {children}
105
+ </body>
106
+ </html>
107
+ );
108
+ }
109
+ ```
110
+
111
+ For route groups, place the init component beside the frontend layout, for example `src/app/(frontend)/watchforge-init.tsx`, and import `watchforge.config.ts` using the correct relative path.
112
+
113
+ For server route handlers, initialize once in server code and wrap handlers:
114
+
115
+ ```ts
116
+ import {
117
+ register,
118
+ withWatchForgeRouteHandler,
119
+ } from "@watchforge/browser/next/server";
120
+
121
+ register({
122
+ dsn: "https://PUBLIC_KEY@your-host/PROJECT_ID",
123
+ app_env: "production",
124
+ });
125
+
126
+ export const GET = withWatchForgeRouteHandler(async () => {
127
+ throw new Error("Next.js route handler test error");
128
+ });
129
+ ```
130
+
131
+ ## Express Setup
132
+
133
+ ```ts
134
+ import express from "express";
135
+ import { register } from "@watchforge/browser/node";
136
+ import {
137
+ expressRequestMiddleware,
138
+ expressMiddleware,
139
+ } from "@watchforge/browser/express";
140
+
141
+ register({
142
+ dsn: "https://PUBLIC_KEY@your-host/PROJECT_ID",
143
+ app_env: "production",
144
+ });
145
+
146
+ const app = express();
147
+ app.use(express.json());
148
+ app.use(expressRequestMiddleware());
149
+
150
+ app.get("/error", () => {
151
+ throw new Error("Express test error");
152
+ });
153
+
154
+ app.use(expressMiddleware());
155
+ ```
156
+
157
+ ## React Setup
158
+
159
+ ```tsx
160
+ import { register } from "@watchforge/browser";
161
+ import { ErrorBoundary } from "@watchforge/browser/react";
162
+
163
+ register({
164
+ dsn: "https://PUBLIC_KEY@your-host/PROJECT_ID",
165
+ app_env: "production",
166
+ });
167
+
168
+ export function AppRoot() {
169
+ return (
170
+ <ErrorBoundary fallback={<div>Something went wrong.</div>}>
171
+ <App />
172
+ </ErrorBoundary>
173
+ );
174
+ }
175
+ ```
176
+
21
177
  ## Quick start (all most apps need)
22
178
 
23
179
  Call **`register()` once** as early as possible (e.g. app entry / main bundle), with your **DSN** from the WatchForge project settings.