@bugwatch/express 0.1.0

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.
@@ -0,0 +1,220 @@
1
+ import { Request, Response, NextFunction, Express, Application, RequestHandler, ErrorRequestHandler } from 'express';
2
+ import { UserContext, BugwatchOptions } from '@bugwatch/core';
3
+ export { addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, setExtra, setTag, setUser } from '@bugwatch/core';
4
+
5
+ /**
6
+ * Options for the Bugwatch Express middleware
7
+ */
8
+ interface BugwatchExpressOptions {
9
+ /**
10
+ * Extract user context from the request.
11
+ * Return null to skip user context extraction.
12
+ */
13
+ extractUser?: (req: Request) => UserContext | null;
14
+ /**
15
+ * Filter headers before sending to Bugwatch.
16
+ * Return true to include the header, false to exclude.
17
+ * By default, sensitive headers are excluded.
18
+ */
19
+ filterHeaders?: (name: string, value: string) => boolean;
20
+ /**
21
+ * Filter body fields before sending to Bugwatch.
22
+ * Return true to include the field, false to exclude.
23
+ */
24
+ filterBody?: (key: string, value: unknown) => boolean;
25
+ /**
26
+ * Whether to include request body in error context.
27
+ * @default false
28
+ */
29
+ includeBody?: boolean;
30
+ /**
31
+ * Whether to add breadcrumbs for requests.
32
+ * @default true
33
+ */
34
+ addBreadcrumbs?: boolean;
35
+ /**
36
+ * Whether to flush events before sending error response.
37
+ * Useful for serverless environments.
38
+ * @default false
39
+ */
40
+ flushOnError?: boolean;
41
+ }
42
+ /**
43
+ * Extended Express Request with Bugwatch context
44
+ */
45
+ interface BugwatchRequest extends Request {
46
+ bugwatch?: {
47
+ eventId?: string;
48
+ startTime: number;
49
+ };
50
+ }
51
+ /**
52
+ * Async request handler type
53
+ */
54
+ type AsyncRequestHandler = (req: Request, res: Response, next: NextFunction) => Promise<void>;
55
+
56
+ /**
57
+ * One-liner setup for Bugwatch Express integration.
58
+ *
59
+ * This module provides a simplified setup function that handles all
60
+ * initialization and middleware registration in a single call.
61
+ */
62
+
63
+ /**
64
+ * Combined options for Express setup.
65
+ * Includes both core SDK options and Express-specific middleware options.
66
+ */
67
+ interface BugwatchExpressSetupOptions extends BugwatchExpressOptions, Partial<BugwatchOptions> {
68
+ }
69
+ /**
70
+ * Result object returned by setup() for advanced use cases.
71
+ */
72
+ interface BugwatchSetupResult {
73
+ /**
74
+ * Manually add the error handler middleware.
75
+ * Call this if you're not using app.listen() directly (e.g., http.createServer).
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const { addErrorHandler } = setup(app);
80
+ *
81
+ * // Define your routes...
82
+ *
83
+ * // If not using app.listen(), manually add error handler after routes:
84
+ * addErrorHandler();
85
+ *
86
+ * http.createServer(app).listen(3000);
87
+ * ```
88
+ */
89
+ addErrorHandler: () => void;
90
+ }
91
+ /**
92
+ * Set up Bugwatch for an Express application with a single call.
93
+ *
94
+ * This function:
95
+ * 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)
96
+ * 2. Adds the request handler middleware immediately
97
+ * 3. Hooks into app.listen() to add the error handler before the server starts
98
+ *
99
+ * **Important**: Call `setup()` early, before defining your routes, so the
100
+ * request handler can capture context for all requests.
101
+ *
102
+ * @param app - The Express application instance
103
+ * @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)
104
+ * @returns Object with `addErrorHandler()` for manual error handler registration
105
+ *
106
+ * @example Basic usage (recommended)
107
+ * ```typescript
108
+ * import express from "express";
109
+ * import { setup } from "@bugwatch/express";
110
+ *
111
+ * const app = express();
112
+ *
113
+ * // Call setup BEFORE defining routes
114
+ * setup(app);
115
+ *
116
+ * app.get("/", (req, res) => res.send("Hello!"));
117
+ *
118
+ * // Error handler is automatically added when listen() is called
119
+ * app.listen(3000);
120
+ * ```
121
+ *
122
+ * @example With explicit options
123
+ * ```typescript
124
+ * setup(app, {
125
+ * apiKey: "bw_live_xxxxx",
126
+ * environment: "production",
127
+ * extractUser: (req) => ({ id: req.user?.id }),
128
+ * });
129
+ * ```
130
+ *
131
+ * @example With http.createServer (manual error handler)
132
+ * ```typescript
133
+ * import http from "http";
134
+ * import express from "express";
135
+ * import { setup } from "@bugwatch/express";
136
+ *
137
+ * const app = express();
138
+ * const { addErrorHandler } = setup(app);
139
+ *
140
+ * app.get("/", (req, res) => res.send("Hello!"));
141
+ *
142
+ * // Manually add error handler AFTER routes
143
+ * addErrorHandler();
144
+ *
145
+ * http.createServer(app).listen(3000);
146
+ * ```
147
+ */
148
+ declare function setup(app: Express | Application, options?: BugwatchExpressSetupOptions): BugwatchSetupResult;
149
+
150
+ /**
151
+ * Request handler middleware that adds request context and breadcrumbs.
152
+ *
153
+ * This middleware should be added early in your middleware chain.
154
+ * Uses AsyncLocalStorage for request-scoped context isolation to prevent
155
+ * user context leakage between concurrent requests.
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * import express from "express";
160
+ * import { requestHandler } from "@bugwatch/express";
161
+ *
162
+ * const app = express();
163
+ * app.use(requestHandler());
164
+ * ```
165
+ */
166
+ declare function requestHandler(options?: BugwatchExpressOptions): RequestHandler;
167
+ /**
168
+ * Error handler middleware that captures errors to Bugwatch.
169
+ *
170
+ * This middleware should be added after all your routes.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * import express from "express";
175
+ * import { requestHandler, errorHandler } from "@bugwatch/express";
176
+ *
177
+ * const app = express();
178
+ * app.use(requestHandler());
179
+ *
180
+ * // ... your routes ...
181
+ *
182
+ * app.use(errorHandler());
183
+ * ```
184
+ */
185
+ declare function errorHandler(options?: BugwatchExpressOptions): ErrorRequestHandler;
186
+ /**
187
+ * Wrap an async request handler to automatically capture errors.
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * import { wrapHandler } from "@bugwatch/express";
192
+ *
193
+ * app.get("/users/:id", wrapHandler(async (req, res) => {
194
+ * const user = await getUserById(req.params.id);
195
+ * res.json(user);
196
+ * }));
197
+ * ```
198
+ */
199
+ declare function wrapHandler(handler: AsyncRequestHandler): RequestHandler;
200
+ /**
201
+ * Manually capture an error with Express request context.
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * import { captureError } from "@bugwatch/express";
206
+ *
207
+ * app.get("/users/:id", (req, res) => {
208
+ * try {
209
+ * const user = getUserById(req.params.id);
210
+ * res.json(user);
211
+ * } catch (err) {
212
+ * captureError(req, err);
213
+ * res.status(500).json({ error: "Internal server error" });
214
+ * }
215
+ * });
216
+ * ```
217
+ */
218
+ declare function captureError(req: Request, error: Error, options?: BugwatchExpressOptions): string;
219
+
220
+ export { type AsyncRequestHandler, type BugwatchExpressOptions, type BugwatchExpressSetupOptions, type BugwatchRequest, type BugwatchSetupResult, captureError, errorHandler, requestHandler, setup, wrapHandler };
@@ -0,0 +1,220 @@
1
+ import { Request, Response, NextFunction, Express, Application, RequestHandler, ErrorRequestHandler } from 'express';
2
+ import { UserContext, BugwatchOptions } from '@bugwatch/core';
3
+ export { addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, setExtra, setTag, setUser } from '@bugwatch/core';
4
+
5
+ /**
6
+ * Options for the Bugwatch Express middleware
7
+ */
8
+ interface BugwatchExpressOptions {
9
+ /**
10
+ * Extract user context from the request.
11
+ * Return null to skip user context extraction.
12
+ */
13
+ extractUser?: (req: Request) => UserContext | null;
14
+ /**
15
+ * Filter headers before sending to Bugwatch.
16
+ * Return true to include the header, false to exclude.
17
+ * By default, sensitive headers are excluded.
18
+ */
19
+ filterHeaders?: (name: string, value: string) => boolean;
20
+ /**
21
+ * Filter body fields before sending to Bugwatch.
22
+ * Return true to include the field, false to exclude.
23
+ */
24
+ filterBody?: (key: string, value: unknown) => boolean;
25
+ /**
26
+ * Whether to include request body in error context.
27
+ * @default false
28
+ */
29
+ includeBody?: boolean;
30
+ /**
31
+ * Whether to add breadcrumbs for requests.
32
+ * @default true
33
+ */
34
+ addBreadcrumbs?: boolean;
35
+ /**
36
+ * Whether to flush events before sending error response.
37
+ * Useful for serverless environments.
38
+ * @default false
39
+ */
40
+ flushOnError?: boolean;
41
+ }
42
+ /**
43
+ * Extended Express Request with Bugwatch context
44
+ */
45
+ interface BugwatchRequest extends Request {
46
+ bugwatch?: {
47
+ eventId?: string;
48
+ startTime: number;
49
+ };
50
+ }
51
+ /**
52
+ * Async request handler type
53
+ */
54
+ type AsyncRequestHandler = (req: Request, res: Response, next: NextFunction) => Promise<void>;
55
+
56
+ /**
57
+ * One-liner setup for Bugwatch Express integration.
58
+ *
59
+ * This module provides a simplified setup function that handles all
60
+ * initialization and middleware registration in a single call.
61
+ */
62
+
63
+ /**
64
+ * Combined options for Express setup.
65
+ * Includes both core SDK options and Express-specific middleware options.
66
+ */
67
+ interface BugwatchExpressSetupOptions extends BugwatchExpressOptions, Partial<BugwatchOptions> {
68
+ }
69
+ /**
70
+ * Result object returned by setup() for advanced use cases.
71
+ */
72
+ interface BugwatchSetupResult {
73
+ /**
74
+ * Manually add the error handler middleware.
75
+ * Call this if you're not using app.listen() directly (e.g., http.createServer).
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const { addErrorHandler } = setup(app);
80
+ *
81
+ * // Define your routes...
82
+ *
83
+ * // If not using app.listen(), manually add error handler after routes:
84
+ * addErrorHandler();
85
+ *
86
+ * http.createServer(app).listen(3000);
87
+ * ```
88
+ */
89
+ addErrorHandler: () => void;
90
+ }
91
+ /**
92
+ * Set up Bugwatch for an Express application with a single call.
93
+ *
94
+ * This function:
95
+ * 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)
96
+ * 2. Adds the request handler middleware immediately
97
+ * 3. Hooks into app.listen() to add the error handler before the server starts
98
+ *
99
+ * **Important**: Call `setup()` early, before defining your routes, so the
100
+ * request handler can capture context for all requests.
101
+ *
102
+ * @param app - The Express application instance
103
+ * @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)
104
+ * @returns Object with `addErrorHandler()` for manual error handler registration
105
+ *
106
+ * @example Basic usage (recommended)
107
+ * ```typescript
108
+ * import express from "express";
109
+ * import { setup } from "@bugwatch/express";
110
+ *
111
+ * const app = express();
112
+ *
113
+ * // Call setup BEFORE defining routes
114
+ * setup(app);
115
+ *
116
+ * app.get("/", (req, res) => res.send("Hello!"));
117
+ *
118
+ * // Error handler is automatically added when listen() is called
119
+ * app.listen(3000);
120
+ * ```
121
+ *
122
+ * @example With explicit options
123
+ * ```typescript
124
+ * setup(app, {
125
+ * apiKey: "bw_live_xxxxx",
126
+ * environment: "production",
127
+ * extractUser: (req) => ({ id: req.user?.id }),
128
+ * });
129
+ * ```
130
+ *
131
+ * @example With http.createServer (manual error handler)
132
+ * ```typescript
133
+ * import http from "http";
134
+ * import express from "express";
135
+ * import { setup } from "@bugwatch/express";
136
+ *
137
+ * const app = express();
138
+ * const { addErrorHandler } = setup(app);
139
+ *
140
+ * app.get("/", (req, res) => res.send("Hello!"));
141
+ *
142
+ * // Manually add error handler AFTER routes
143
+ * addErrorHandler();
144
+ *
145
+ * http.createServer(app).listen(3000);
146
+ * ```
147
+ */
148
+ declare function setup(app: Express | Application, options?: BugwatchExpressSetupOptions): BugwatchSetupResult;
149
+
150
+ /**
151
+ * Request handler middleware that adds request context and breadcrumbs.
152
+ *
153
+ * This middleware should be added early in your middleware chain.
154
+ * Uses AsyncLocalStorage for request-scoped context isolation to prevent
155
+ * user context leakage between concurrent requests.
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * import express from "express";
160
+ * import { requestHandler } from "@bugwatch/express";
161
+ *
162
+ * const app = express();
163
+ * app.use(requestHandler());
164
+ * ```
165
+ */
166
+ declare function requestHandler(options?: BugwatchExpressOptions): RequestHandler;
167
+ /**
168
+ * Error handler middleware that captures errors to Bugwatch.
169
+ *
170
+ * This middleware should be added after all your routes.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * import express from "express";
175
+ * import { requestHandler, errorHandler } from "@bugwatch/express";
176
+ *
177
+ * const app = express();
178
+ * app.use(requestHandler());
179
+ *
180
+ * // ... your routes ...
181
+ *
182
+ * app.use(errorHandler());
183
+ * ```
184
+ */
185
+ declare function errorHandler(options?: BugwatchExpressOptions): ErrorRequestHandler;
186
+ /**
187
+ * Wrap an async request handler to automatically capture errors.
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * import { wrapHandler } from "@bugwatch/express";
192
+ *
193
+ * app.get("/users/:id", wrapHandler(async (req, res) => {
194
+ * const user = await getUserById(req.params.id);
195
+ * res.json(user);
196
+ * }));
197
+ * ```
198
+ */
199
+ declare function wrapHandler(handler: AsyncRequestHandler): RequestHandler;
200
+ /**
201
+ * Manually capture an error with Express request context.
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * import { captureError } from "@bugwatch/express";
206
+ *
207
+ * app.get("/users/:id", (req, res) => {
208
+ * try {
209
+ * const user = getUserById(req.params.id);
210
+ * res.json(user);
211
+ * } catch (err) {
212
+ * captureError(req, err);
213
+ * res.status(500).json({ error: "Internal server error" });
214
+ * }
215
+ * });
216
+ * ```
217
+ */
218
+ declare function captureError(req: Request, error: Error, options?: BugwatchExpressOptions): string;
219
+
220
+ export { type AsyncRequestHandler, type BugwatchExpressOptions, type BugwatchExpressSetupOptions, type BugwatchRequest, type BugwatchSetupResult, captureError, errorHandler, requestHandler, setup, wrapHandler };
package/dist/index.js ADDED
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ addBreadcrumb: () => import_core3.addBreadcrumb,
24
+ captureError: () => captureError,
25
+ captureException: () => import_core3.captureException,
26
+ captureMessage: () => import_core3.captureMessage,
27
+ close: () => import_core3.close,
28
+ errorHandler: () => errorHandler,
29
+ flush: () => import_core3.flush,
30
+ getClient: () => import_core3.getClient,
31
+ init: () => import_core3.init,
32
+ requestHandler: () => requestHandler,
33
+ setExtra: () => import_core3.setExtra,
34
+ setTag: () => import_core3.setTag,
35
+ setUser: () => import_core3.setUser,
36
+ setup: () => setup,
37
+ wrapHandler: () => wrapHandler
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+
41
+ // src/setup.ts
42
+ var import_core2 = require("@bugwatch/core");
43
+
44
+ // src/middleware.ts
45
+ var import_core = require("@bugwatch/core");
46
+ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
47
+ "authorization",
48
+ "cookie",
49
+ "set-cookie",
50
+ "x-api-key",
51
+ "x-auth-token",
52
+ "x-csrf-token",
53
+ "x-xsrf-token",
54
+ "proxy-authorization"
55
+ ]);
56
+ var SENSITIVE_BODY_FIELDS = /* @__PURE__ */ new Set([
57
+ "password",
58
+ "secret",
59
+ "token",
60
+ "api_key",
61
+ "apiKey",
62
+ "credit_card",
63
+ "creditCard",
64
+ "ssn",
65
+ "social_security"
66
+ ]);
67
+ function defaultHeaderFilter(name) {
68
+ return !SENSITIVE_HEADERS.has(name.toLowerCase());
69
+ }
70
+ function defaultBodyFilter(key) {
71
+ return !SENSITIVE_BODY_FIELDS.has(key.toLowerCase());
72
+ }
73
+ function filterObject(obj, filter) {
74
+ const result = {};
75
+ for (const [key, value] of Object.entries(obj)) {
76
+ if (filter(key, value)) {
77
+ result[key] = value;
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ function extractRequestContext(req, options) {
83
+ const headerFilter = options.filterHeaders || defaultHeaderFilter;
84
+ const bodyFilter = options.filterBody || defaultBodyFilter;
85
+ const headers = {};
86
+ for (const [name, value] of Object.entries(req.headers)) {
87
+ if (typeof value === "string" && headerFilter(name, value)) {
88
+ headers[name] = value;
89
+ } else if (Array.isArray(value)) {
90
+ const filtered = value.filter((v) => headerFilter(name, v));
91
+ if (filtered.length > 0) {
92
+ headers[name] = filtered.join(", ");
93
+ }
94
+ }
95
+ }
96
+ const context = {
97
+ url: `${req.protocol}://${req.get("host")}${req.originalUrl}`,
98
+ method: req.method,
99
+ headers,
100
+ query_string: req.url.includes("?") ? req.url.split("?")[1] : void 0
101
+ };
102
+ if (options.includeBody && req.body && typeof req.body === "object") {
103
+ context.data = filterObject(req.body, bodyFilter);
104
+ }
105
+ return context;
106
+ }
107
+ function extractClientIp(req) {
108
+ const forwarded = req.get("x-forwarded-for");
109
+ if (forwarded) {
110
+ const firstIp = forwarded.split(",")[0]?.trim();
111
+ if (firstIp) return firstIp;
112
+ }
113
+ const realIp = req.get("x-real-ip");
114
+ if (realIp) return realIp;
115
+ const cfIp = req.get("cf-connecting-ip");
116
+ if (cfIp) return cfIp;
117
+ return req.socket.remoteAddress;
118
+ }
119
+ function requestHandler(options = {}) {
120
+ return (req, res, next) => {
121
+ const client = (0, import_core.getClient)();
122
+ if (!client) {
123
+ return next();
124
+ }
125
+ const scopedContext = (0, import_core.createScopedContext)();
126
+ req.bugwatch = { startTime: Date.now() };
127
+ if (options.extractUser) {
128
+ const user = options.extractUser(req);
129
+ if (user) {
130
+ scopedContext.user = user;
131
+ }
132
+ }
133
+ (0, import_core.runWithContext)(scopedContext, () => {
134
+ if (options.addBreadcrumbs !== false) {
135
+ (0, import_core.addBreadcrumb)({
136
+ category: "http",
137
+ message: `${req.method} ${req.path}`,
138
+ level: "info",
139
+ data: {
140
+ method: req.method,
141
+ url: req.originalUrl
142
+ }
143
+ });
144
+ }
145
+ const originalEnd = res.end.bind(res);
146
+ res.end = function(...args) {
147
+ if (options.addBreadcrumbs !== false) {
148
+ const duration = Date.now() - (req.bugwatch?.startTime || Date.now());
149
+ (0, import_core.addBreadcrumb)({
150
+ category: "http",
151
+ message: `${req.method} ${req.path} -> ${res.statusCode}`,
152
+ level: res.statusCode >= 500 ? "error" : res.statusCode >= 400 ? "warning" : "info",
153
+ data: {
154
+ method: req.method,
155
+ url: req.originalUrl,
156
+ status_code: res.statusCode,
157
+ duration_ms: duration
158
+ }
159
+ });
160
+ }
161
+ return originalEnd(...args);
162
+ };
163
+ next();
164
+ });
165
+ };
166
+ }
167
+ function errorHandler(options = {}) {
168
+ return async (err, req, res, next) => {
169
+ const client = (0, import_core.getClient)();
170
+ if (!client) {
171
+ return next(err);
172
+ }
173
+ const requestContext = extractRequestContext(req, options);
174
+ const extra = {
175
+ request: requestContext
176
+ };
177
+ const clientIp = extractClientIp(req);
178
+ if (clientIp) {
179
+ extra.client_ip = clientIp;
180
+ }
181
+ const eventId = (0, import_core.captureException)(err, {
182
+ request: requestContext,
183
+ extra,
184
+ tags: {
185
+ "http.method": req.method,
186
+ "http.url": req.originalUrl
187
+ }
188
+ });
189
+ if (req.bugwatch) {
190
+ req.bugwatch.eventId = eventId;
191
+ }
192
+ if (options.flushOnError) {
193
+ await (0, import_core.flush)();
194
+ }
195
+ next(err);
196
+ };
197
+ }
198
+ function wrapHandler(handler) {
199
+ return (req, res, next) => {
200
+ Promise.resolve(handler(req, res, next)).catch(next);
201
+ };
202
+ }
203
+ function captureError(req, error, options = {}) {
204
+ const client = (0, import_core.getClient)();
205
+ if (!client) {
206
+ return "";
207
+ }
208
+ const requestContext = extractRequestContext(req, options);
209
+ const clientIp = extractClientIp(req);
210
+ return (0, import_core.captureException)(error, {
211
+ request: requestContext,
212
+ extra: {
213
+ request: requestContext,
214
+ ...clientIp && { client_ip: clientIp }
215
+ },
216
+ tags: {
217
+ "http.method": req.method,
218
+ "http.url": req.originalUrl
219
+ }
220
+ });
221
+ }
222
+
223
+ // src/setup.ts
224
+ var setupApps = /* @__PURE__ */ new WeakSet();
225
+ var initializationInProgress = false;
226
+ var initializationPromise = null;
227
+ async function ensureInitialized(options) {
228
+ if ((0, import_core2.getClient)()) {
229
+ return;
230
+ }
231
+ if (initializationInProgress && initializationPromise) {
232
+ await initializationPromise;
233
+ return;
234
+ }
235
+ initializationInProgress = true;
236
+ initializationPromise = (async () => {
237
+ try {
238
+ if ((0, import_core2.getClient)()) {
239
+ return;
240
+ }
241
+ const coreOptions = {};
242
+ if (options?.apiKey) coreOptions.apiKey = options.apiKey;
243
+ if (options?.endpoint) coreOptions.endpoint = options.endpoint;
244
+ if (options?.environment) coreOptions.environment = options.environment;
245
+ if (options?.release) coreOptions.release = options.release;
246
+ if (options?.debug !== void 0) coreOptions.debug = options.debug;
247
+ if (options?.sampleRate !== void 0) coreOptions.sampleRate = options.sampleRate;
248
+ if (options?.maxBreadcrumbs !== void 0) coreOptions.maxBreadcrumbs = options.maxBreadcrumbs;
249
+ if (options?.tags) coreOptions.tags = options.tags;
250
+ if (options?.user) coreOptions.user = options.user;
251
+ if (options?.beforeSend) coreOptions.beforeSend = options.beforeSend;
252
+ if (options?.ignoreErrors) coreOptions.ignoreErrors = options.ignoreErrors;
253
+ (0, import_core2.init)(coreOptions);
254
+ } finally {
255
+ initializationInProgress = false;
256
+ }
257
+ })();
258
+ await initializationPromise;
259
+ }
260
+ function setup(app, options) {
261
+ if (setupApps.has(app)) {
262
+ console.warn("[Bugwatch] setup() already called for this app. Skipping.");
263
+ return {
264
+ addErrorHandler: () => {
265
+ }
266
+ };
267
+ }
268
+ setupApps.add(app);
269
+ ensureInitialized(options).catch((err) => {
270
+ if (options?.debug) {
271
+ console.error("[Bugwatch] Initialization failed:", err);
272
+ }
273
+ });
274
+ const middlewareOptions = {};
275
+ if (options?.extractUser) middlewareOptions.extractUser = options.extractUser;
276
+ if (options?.filterHeaders) middlewareOptions.filterHeaders = options.filterHeaders;
277
+ if (options?.filterBody) middlewareOptions.filterBody = options.filterBody;
278
+ if (options?.includeBody !== void 0) middlewareOptions.includeBody = options.includeBody;
279
+ if (options?.addBreadcrumbs !== void 0) middlewareOptions.addBreadcrumbs = options.addBreadcrumbs;
280
+ if (options?.flushOnError !== void 0) middlewareOptions.flushOnError = options.flushOnError;
281
+ app.use(requestHandler(middlewareOptions));
282
+ let errorHandlerAdded = false;
283
+ const addErrorHandler = () => {
284
+ if (errorHandlerAdded) {
285
+ return;
286
+ }
287
+ errorHandlerAdded = true;
288
+ app.use(errorHandler(middlewareOptions));
289
+ };
290
+ const originalListen = app.listen.bind(app);
291
+ app.listen = function(...args) {
292
+ addErrorHandler();
293
+ app.listen = originalListen;
294
+ return originalListen(...args);
295
+ };
296
+ return { addErrorHandler };
297
+ }
298
+
299
+ // src/index.ts
300
+ var import_core3 = require("@bugwatch/core");
301
+ // Annotate the CommonJS export names for ESM import in node:
302
+ 0 && (module.exports = {
303
+ addBreadcrumb,
304
+ captureError,
305
+ captureException,
306
+ captureMessage,
307
+ close,
308
+ errorHandler,
309
+ flush,
310
+ getClient,
311
+ init,
312
+ requestHandler,
313
+ setExtra,
314
+ setTag,
315
+ setUser,
316
+ setup,
317
+ wrapHandler
318
+ });
319
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/setup.ts","../src/middleware.ts"],"sourcesContent":["/**\n * @bugwatch/express - Express.js integration for Bugwatch\n *\n * This package provides middleware for automatically capturing errors\n * and request context in Express.js applications.\n *\n * @example One-liner setup (recommended)\n * ```typescript\n * import express from \"express\";\n * import { setup } from \"@bugwatch/express\";\n *\n * const app = express();\n *\n * // Single call handles everything (uses BUGWATCH_API_KEY env var)\n * setup(app);\n *\n * // Or with explicit options\n * setup(app, {\n * apiKey: \"bw_live_xxxxx\",\n * extractUser: (req) => ({ id: req.user?.id }),\n * });\n *\n * app.get(\"/\", (req, res) => res.send(\"Hello World!\"));\n *\n * app.listen(3000);\n * ```\n *\n * @example Manual setup (for more control)\n * ```typescript\n * import express from \"express\";\n * import { init } from \"@bugwatch/core\";\n * import { requestHandler, errorHandler } from \"@bugwatch/express\";\n *\n * init({ apiKey: \"your-api-key\" });\n *\n * const app = express();\n * app.use(requestHandler());\n *\n * // Your routes...\n *\n * app.use(errorHandler());\n * app.listen(3000);\n * ```\n */\n\n// Export one-liner setup\nexport { setup } from \"./setup\";\nexport type { BugwatchExpressSetupOptions, BugwatchSetupResult } from \"./setup\";\n\n// Export middleware functions\nexport {\n requestHandler,\n errorHandler,\n wrapHandler,\n captureError,\n} from \"./middleware\";\n\n// Export types\nexport type {\n BugwatchExpressOptions,\n BugwatchRequest,\n AsyncRequestHandler,\n} from \"./types\";\n\n// Re-export core functions for convenience\nexport {\n init,\n getClient,\n captureException,\n captureMessage,\n addBreadcrumb,\n setUser,\n setTag,\n setExtra,\n flush,\n close,\n} from \"@bugwatch/core\";\n","/**\n * One-liner setup for Bugwatch Express integration.\n *\n * This module provides a simplified setup function that handles all\n * initialization and middleware registration in a single call.\n */\n\nimport type { Express, Application } from \"express\";\nimport { init, getClient, type BugwatchOptions } from \"@bugwatch/core\";\nimport { requestHandler, errorHandler } from \"./middleware\";\nimport type { BugwatchExpressOptions } from \"./types\";\n\n/**\n * Combined options for Express setup.\n * Includes both core SDK options and Express-specific middleware options.\n */\nexport interface BugwatchExpressSetupOptions\n extends BugwatchExpressOptions,\n Partial<BugwatchOptions> {}\n\n/**\n * Result object returned by setup() for advanced use cases.\n */\nexport interface BugwatchSetupResult {\n /**\n * Manually add the error handler middleware.\n * Call this if you're not using app.listen() directly (e.g., http.createServer).\n *\n * @example\n * ```typescript\n * const { addErrorHandler } = setup(app);\n *\n * // Define your routes...\n *\n * // If not using app.listen(), manually add error handler after routes:\n * addErrorHandler();\n *\n * http.createServer(app).listen(3000);\n * ```\n */\n addErrorHandler: () => void;\n}\n\n// Track which apps have been set up to prevent double-setup\nconst setupApps = new WeakSet<Express | Application>();\n\n// Mutex for thread-safe initialization\nlet initializationInProgress = false;\nlet initializationPromise: Promise<void> | null = null;\n\n/**\n * Thread-safe SDK initialization\n */\nasync function ensureInitialized(options?: Partial<BugwatchOptions>): Promise<void> {\n // Fast path: already initialized\n if (getClient()) {\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationInProgress && initializationPromise) {\n await initializationPromise;\n return;\n }\n\n // Start initialization\n initializationInProgress = true;\n initializationPromise = (async () => {\n try {\n // Double-check after acquiring \"lock\"\n if (getClient()) {\n return;\n }\n\n // Extract core options\n const coreOptions: Partial<BugwatchOptions> = {};\n if (options?.apiKey) coreOptions.apiKey = options.apiKey;\n if (options?.endpoint) coreOptions.endpoint = options.endpoint;\n if (options?.environment) coreOptions.environment = options.environment;\n if (options?.release) coreOptions.release = options.release;\n if (options?.debug !== undefined) coreOptions.debug = options.debug;\n if (options?.sampleRate !== undefined) coreOptions.sampleRate = options.sampleRate;\n if (options?.maxBreadcrumbs !== undefined) coreOptions.maxBreadcrumbs = options.maxBreadcrumbs;\n if (options?.tags) coreOptions.tags = options.tags;\n if (options?.user) coreOptions.user = options.user;\n if (options?.beforeSend) coreOptions.beforeSend = options.beforeSend;\n if (options?.ignoreErrors) coreOptions.ignoreErrors = options.ignoreErrors;\n\n init(coreOptions);\n } finally {\n initializationInProgress = false;\n }\n })();\n\n await initializationPromise;\n}\n\n/**\n * Set up Bugwatch for an Express application with a single call.\n *\n * This function:\n * 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)\n * 2. Adds the request handler middleware immediately\n * 3. Hooks into app.listen() to add the error handler before the server starts\n *\n * **Important**: Call `setup()` early, before defining your routes, so the\n * request handler can capture context for all requests.\n *\n * @param app - The Express application instance\n * @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)\n * @returns Object with `addErrorHandler()` for manual error handler registration\n *\n * @example Basic usage (recommended)\n * ```typescript\n * import express from \"express\";\n * import { setup } from \"@bugwatch/express\";\n *\n * const app = express();\n *\n * // Call setup BEFORE defining routes\n * setup(app);\n *\n * app.get(\"/\", (req, res) => res.send(\"Hello!\"));\n *\n * // Error handler is automatically added when listen() is called\n * app.listen(3000);\n * ```\n *\n * @example With explicit options\n * ```typescript\n * setup(app, {\n * apiKey: \"bw_live_xxxxx\",\n * environment: \"production\",\n * extractUser: (req) => ({ id: req.user?.id }),\n * });\n * ```\n *\n * @example With http.createServer (manual error handler)\n * ```typescript\n * import http from \"http\";\n * import express from \"express\";\n * import { setup } from \"@bugwatch/express\";\n *\n * const app = express();\n * const { addErrorHandler } = setup(app);\n *\n * app.get(\"/\", (req, res) => res.send(\"Hello!\"));\n *\n * // Manually add error handler AFTER routes\n * addErrorHandler();\n *\n * http.createServer(app).listen(3000);\n * ```\n */\nexport function setup(\n app: Express | Application,\n options?: BugwatchExpressSetupOptions\n): BugwatchSetupResult {\n // Prevent double-setup\n if (setupApps.has(app)) {\n console.warn(\"[Bugwatch] setup() already called for this app. Skipping.\");\n return {\n addErrorHandler: () => {\n // No-op for already-setup apps\n },\n };\n }\n setupApps.add(app);\n\n // Initialize core SDK with race condition protection\n // Note: This is async but we don't await it here to avoid breaking the sync API\n // The initialization will complete before any requests are handled\n ensureInitialized(options).catch((err) => {\n if (options?.debug) {\n console.error(\"[Bugwatch] Initialization failed:\", err);\n }\n });\n\n // Extract middleware-specific options\n const middlewareOptions: BugwatchExpressOptions = {};\n if (options?.extractUser) middlewareOptions.extractUser = options.extractUser;\n if (options?.filterHeaders) middlewareOptions.filterHeaders = options.filterHeaders;\n if (options?.filterBody) middlewareOptions.filterBody = options.filterBody;\n if (options?.includeBody !== undefined) middlewareOptions.includeBody = options.includeBody;\n if (options?.addBreadcrumbs !== undefined) middlewareOptions.addBreadcrumbs = options.addBreadcrumbs;\n if (options?.flushOnError !== undefined) middlewareOptions.flushOnError = options.flushOnError;\n\n // Add request handler middleware early\n app.use(requestHandler(middlewareOptions));\n\n // Track whether error handler has been added\n let errorHandlerAdded = false;\n\n /**\n * Add the error handler middleware.\n * This should be called AFTER all routes are defined.\n */\n const addErrorHandler = (): void => {\n if (errorHandlerAdded) {\n return; // Already added, skip\n }\n errorHandlerAdded = true;\n app.use(errorHandler(middlewareOptions));\n };\n\n // Hook into app.listen to add error handler automatically\n const originalListen = app.listen.bind(app);\n\n (app as any).listen = function (\n ...args: Parameters<typeof originalListen>\n ): ReturnType<typeof originalListen> {\n // Add error handler before calling original listen\n addErrorHandler();\n\n // Restore original listen for any future calls\n (app as any).listen = originalListen;\n\n return originalListen(...args);\n };\n\n return { addErrorHandler };\n}\n","import type { Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from \"express\";\nimport {\n getClient,\n captureException,\n addBreadcrumb,\n setUser,\n flush,\n runWithContext,\n createScopedContext,\n type RequestContext,\n type ErrorEvent,\n type ScopedContext,\n} from \"@bugwatch/core\";\nimport type { BugwatchExpressOptions, BugwatchRequest, AsyncRequestHandler } from \"./types\";\n\n/**\n * Headers that should be filtered out by default for security.\n */\nconst SENSITIVE_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"set-cookie\",\n \"x-api-key\",\n \"x-auth-token\",\n \"x-csrf-token\",\n \"x-xsrf-token\",\n \"proxy-authorization\",\n]);\n\n/**\n * Body fields that should be filtered out by default for security.\n */\nconst SENSITIVE_BODY_FIELDS = new Set([\n \"password\",\n \"secret\",\n \"token\",\n \"api_key\",\n \"apiKey\",\n \"credit_card\",\n \"creditCard\",\n \"ssn\",\n \"social_security\",\n]);\n\n/**\n * Default header filter function.\n */\nfunction defaultHeaderFilter(name: string): boolean {\n return !SENSITIVE_HEADERS.has(name.toLowerCase());\n}\n\n/**\n * Default body field filter function.\n */\nfunction defaultBodyFilter(key: string): boolean {\n return !SENSITIVE_BODY_FIELDS.has(key.toLowerCase());\n}\n\n/**\n * Filter an object's keys based on a filter function.\n */\nfunction filterObject<T extends Record<string, unknown>>(\n obj: T,\n filter: (key: string, value: unknown) => boolean\n): Partial<T> {\n const result: Partial<T> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (filter(key, value)) {\n (result as Record<string, unknown>)[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Extract request context from an Express request.\n */\nfunction extractRequestContext(\n req: Request,\n options: BugwatchExpressOptions\n): RequestContext {\n const headerFilter = options.filterHeaders || defaultHeaderFilter;\n const bodyFilter = options.filterBody || defaultBodyFilter;\n\n // Filter headers\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\" && headerFilter(name, value)) {\n headers[name] = value;\n } else if (Array.isArray(value)) {\n const filtered = value.filter((v) => headerFilter(name, v));\n if (filtered.length > 0) {\n headers[name] = filtered.join(\", \");\n }\n }\n }\n\n const context: RequestContext = {\n url: `${req.protocol}://${req.get(\"host\")}${req.originalUrl}`,\n method: req.method,\n headers,\n query_string: req.url.includes(\"?\") ? req.url.split(\"?\")[1] : undefined,\n };\n\n // Include body if requested and available\n if (options.includeBody && req.body && typeof req.body === \"object\") {\n context.data = filterObject(req.body, bodyFilter);\n }\n\n return context;\n}\n\n/**\n * Extract client IP from request.\n */\nfunction extractClientIp(req: Request): string | undefined {\n // Check common proxy headers\n const forwarded = req.get(\"x-forwarded-for\");\n if (forwarded) {\n const firstIp = forwarded.split(\",\")[0]?.trim();\n if (firstIp) return firstIp;\n }\n\n const realIp = req.get(\"x-real-ip\");\n if (realIp) return realIp;\n\n const cfIp = req.get(\"cf-connecting-ip\");\n if (cfIp) return cfIp;\n\n // Fall back to socket address\n return req.socket.remoteAddress;\n}\n\n/**\n * Request handler middleware that adds request context and breadcrumbs.\n *\n * This middleware should be added early in your middleware chain.\n * Uses AsyncLocalStorage for request-scoped context isolation to prevent\n * user context leakage between concurrent requests.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { requestHandler } from \"@bugwatch/express\";\n *\n * const app = express();\n * app.use(requestHandler());\n * ```\n */\nexport function requestHandler(\n options: BugwatchExpressOptions = {}\n): RequestHandler {\n return (req: BugwatchRequest, res: Response, next: NextFunction) => {\n const client = getClient();\n if (!client) {\n return next();\n }\n\n // Create a request-scoped context for isolation\n const scopedContext: ScopedContext = createScopedContext();\n\n // Store start time for breadcrumb\n req.bugwatch = { startTime: Date.now() };\n\n // Extract and set user context in the scoped context\n if (options.extractUser) {\n const user = options.extractUser(req);\n if (user) {\n scopedContext.user = user;\n }\n }\n\n // Run the rest of the request handling within the isolated context\n runWithContext(scopedContext, () => {\n // Add request start breadcrumb\n if (options.addBreadcrumbs !== false) {\n addBreadcrumb({\n category: \"http\",\n message: `${req.method} ${req.path}`,\n level: \"info\",\n data: {\n method: req.method,\n url: req.originalUrl,\n },\n });\n }\n\n // Track response for completion breadcrumb\n const originalEnd = res.end.bind(res);\n res.end = function (...args: Parameters<Response[\"end\"]>) {\n if (options.addBreadcrumbs !== false) {\n const duration = Date.now() - (req.bugwatch?.startTime || Date.now());\n addBreadcrumb({\n category: \"http\",\n message: `${req.method} ${req.path} -> ${res.statusCode}`,\n level: res.statusCode >= 500 ? \"error\" : res.statusCode >= 400 ? \"warning\" : \"info\",\n data: {\n method: req.method,\n url: req.originalUrl,\n status_code: res.statusCode,\n duration_ms: duration,\n },\n });\n }\n return originalEnd(...args);\n } as Response[\"end\"];\n\n next();\n });\n };\n}\n\n/**\n * Error handler middleware that captures errors to Bugwatch.\n *\n * This middleware should be added after all your routes.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { requestHandler, errorHandler } from \"@bugwatch/express\";\n *\n * const app = express();\n * app.use(requestHandler());\n *\n * // ... your routes ...\n *\n * app.use(errorHandler());\n * ```\n */\nexport function errorHandler(\n options: BugwatchExpressOptions = {}\n): ErrorRequestHandler {\n return async (\n err: Error,\n req: BugwatchRequest,\n res: Response,\n next: NextFunction\n ) => {\n const client = getClient();\n if (!client) {\n return next(err);\n }\n\n // Build request context\n const requestContext = extractRequestContext(req, options);\n\n // Build extra context\n const extra: Record<string, unknown> = {\n request: requestContext,\n };\n\n const clientIp = extractClientIp(req);\n if (clientIp) {\n extra.client_ip = clientIp;\n }\n\n // Capture the error\n const eventId = captureException(err, {\n request: requestContext,\n extra,\n tags: {\n \"http.method\": req.method,\n \"http.url\": req.originalUrl,\n },\n });\n\n // Store event ID for reference\n if (req.bugwatch) {\n req.bugwatch.eventId = eventId;\n }\n\n // Flush if requested (useful for serverless)\n if (options.flushOnError) {\n await flush();\n }\n\n // Pass to next error handler\n next(err);\n };\n}\n\n/**\n * Wrap an async request handler to automatically capture errors.\n *\n * @example\n * ```typescript\n * import { wrapHandler } from \"@bugwatch/express\";\n *\n * app.get(\"/users/:id\", wrapHandler(async (req, res) => {\n * const user = await getUserById(req.params.id);\n * res.json(user);\n * }));\n * ```\n */\nexport function wrapHandler(handler: AsyncRequestHandler): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n Promise.resolve(handler(req, res, next)).catch(next);\n };\n}\n\n/**\n * Manually capture an error with Express request context.\n *\n * @example\n * ```typescript\n * import { captureError } from \"@bugwatch/express\";\n *\n * app.get(\"/users/:id\", (req, res) => {\n * try {\n * const user = getUserById(req.params.id);\n * res.json(user);\n * } catch (err) {\n * captureError(req, err);\n * res.status(500).json({ error: \"Internal server error\" });\n * }\n * });\n * ```\n */\nexport function captureError(\n req: Request,\n error: Error,\n options: BugwatchExpressOptions = {}\n): string {\n const client = getClient();\n if (!client) {\n return \"\";\n }\n\n const requestContext = extractRequestContext(req, options);\n const clientIp = extractClientIp(req);\n\n return captureException(error, {\n request: requestContext,\n extra: {\n request: requestContext,\n ...(clientIp && { client_ip: clientIp }),\n },\n tags: {\n \"http.method\": req.method,\n \"http.url\": req.originalUrl,\n },\n });\n}\n"],"mappings":"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","names":["import_core","import_core"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,296 @@
1
+ // src/setup.ts
2
+ import { init, getClient as getClient2 } from "@bugwatch/core";
3
+
4
+ // src/middleware.ts
5
+ import {
6
+ getClient,
7
+ captureException,
8
+ addBreadcrumb,
9
+ flush,
10
+ runWithContext,
11
+ createScopedContext
12
+ } from "@bugwatch/core";
13
+ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
14
+ "authorization",
15
+ "cookie",
16
+ "set-cookie",
17
+ "x-api-key",
18
+ "x-auth-token",
19
+ "x-csrf-token",
20
+ "x-xsrf-token",
21
+ "proxy-authorization"
22
+ ]);
23
+ var SENSITIVE_BODY_FIELDS = /* @__PURE__ */ new Set([
24
+ "password",
25
+ "secret",
26
+ "token",
27
+ "api_key",
28
+ "apiKey",
29
+ "credit_card",
30
+ "creditCard",
31
+ "ssn",
32
+ "social_security"
33
+ ]);
34
+ function defaultHeaderFilter(name) {
35
+ return !SENSITIVE_HEADERS.has(name.toLowerCase());
36
+ }
37
+ function defaultBodyFilter(key) {
38
+ return !SENSITIVE_BODY_FIELDS.has(key.toLowerCase());
39
+ }
40
+ function filterObject(obj, filter) {
41
+ const result = {};
42
+ for (const [key, value] of Object.entries(obj)) {
43
+ if (filter(key, value)) {
44
+ result[key] = value;
45
+ }
46
+ }
47
+ return result;
48
+ }
49
+ function extractRequestContext(req, options) {
50
+ const headerFilter = options.filterHeaders || defaultHeaderFilter;
51
+ const bodyFilter = options.filterBody || defaultBodyFilter;
52
+ const headers = {};
53
+ for (const [name, value] of Object.entries(req.headers)) {
54
+ if (typeof value === "string" && headerFilter(name, value)) {
55
+ headers[name] = value;
56
+ } else if (Array.isArray(value)) {
57
+ const filtered = value.filter((v) => headerFilter(name, v));
58
+ if (filtered.length > 0) {
59
+ headers[name] = filtered.join(", ");
60
+ }
61
+ }
62
+ }
63
+ const context = {
64
+ url: `${req.protocol}://${req.get("host")}${req.originalUrl}`,
65
+ method: req.method,
66
+ headers,
67
+ query_string: req.url.includes("?") ? req.url.split("?")[1] : void 0
68
+ };
69
+ if (options.includeBody && req.body && typeof req.body === "object") {
70
+ context.data = filterObject(req.body, bodyFilter);
71
+ }
72
+ return context;
73
+ }
74
+ function extractClientIp(req) {
75
+ const forwarded = req.get("x-forwarded-for");
76
+ if (forwarded) {
77
+ const firstIp = forwarded.split(",")[0]?.trim();
78
+ if (firstIp) return firstIp;
79
+ }
80
+ const realIp = req.get("x-real-ip");
81
+ if (realIp) return realIp;
82
+ const cfIp = req.get("cf-connecting-ip");
83
+ if (cfIp) return cfIp;
84
+ return req.socket.remoteAddress;
85
+ }
86
+ function requestHandler(options = {}) {
87
+ return (req, res, next) => {
88
+ const client = getClient();
89
+ if (!client) {
90
+ return next();
91
+ }
92
+ const scopedContext = createScopedContext();
93
+ req.bugwatch = { startTime: Date.now() };
94
+ if (options.extractUser) {
95
+ const user = options.extractUser(req);
96
+ if (user) {
97
+ scopedContext.user = user;
98
+ }
99
+ }
100
+ runWithContext(scopedContext, () => {
101
+ if (options.addBreadcrumbs !== false) {
102
+ addBreadcrumb({
103
+ category: "http",
104
+ message: `${req.method} ${req.path}`,
105
+ level: "info",
106
+ data: {
107
+ method: req.method,
108
+ url: req.originalUrl
109
+ }
110
+ });
111
+ }
112
+ const originalEnd = res.end.bind(res);
113
+ res.end = function(...args) {
114
+ if (options.addBreadcrumbs !== false) {
115
+ const duration = Date.now() - (req.bugwatch?.startTime || Date.now());
116
+ addBreadcrumb({
117
+ category: "http",
118
+ message: `${req.method} ${req.path} -> ${res.statusCode}`,
119
+ level: res.statusCode >= 500 ? "error" : res.statusCode >= 400 ? "warning" : "info",
120
+ data: {
121
+ method: req.method,
122
+ url: req.originalUrl,
123
+ status_code: res.statusCode,
124
+ duration_ms: duration
125
+ }
126
+ });
127
+ }
128
+ return originalEnd(...args);
129
+ };
130
+ next();
131
+ });
132
+ };
133
+ }
134
+ function errorHandler(options = {}) {
135
+ return async (err, req, res, next) => {
136
+ const client = getClient();
137
+ if (!client) {
138
+ return next(err);
139
+ }
140
+ const requestContext = extractRequestContext(req, options);
141
+ const extra = {
142
+ request: requestContext
143
+ };
144
+ const clientIp = extractClientIp(req);
145
+ if (clientIp) {
146
+ extra.client_ip = clientIp;
147
+ }
148
+ const eventId = captureException(err, {
149
+ request: requestContext,
150
+ extra,
151
+ tags: {
152
+ "http.method": req.method,
153
+ "http.url": req.originalUrl
154
+ }
155
+ });
156
+ if (req.bugwatch) {
157
+ req.bugwatch.eventId = eventId;
158
+ }
159
+ if (options.flushOnError) {
160
+ await flush();
161
+ }
162
+ next(err);
163
+ };
164
+ }
165
+ function wrapHandler(handler) {
166
+ return (req, res, next) => {
167
+ Promise.resolve(handler(req, res, next)).catch(next);
168
+ };
169
+ }
170
+ function captureError(req, error, options = {}) {
171
+ const client = getClient();
172
+ if (!client) {
173
+ return "";
174
+ }
175
+ const requestContext = extractRequestContext(req, options);
176
+ const clientIp = extractClientIp(req);
177
+ return captureException(error, {
178
+ request: requestContext,
179
+ extra: {
180
+ request: requestContext,
181
+ ...clientIp && { client_ip: clientIp }
182
+ },
183
+ tags: {
184
+ "http.method": req.method,
185
+ "http.url": req.originalUrl
186
+ }
187
+ });
188
+ }
189
+
190
+ // src/setup.ts
191
+ var setupApps = /* @__PURE__ */ new WeakSet();
192
+ var initializationInProgress = false;
193
+ var initializationPromise = null;
194
+ async function ensureInitialized(options) {
195
+ if (getClient2()) {
196
+ return;
197
+ }
198
+ if (initializationInProgress && initializationPromise) {
199
+ await initializationPromise;
200
+ return;
201
+ }
202
+ initializationInProgress = true;
203
+ initializationPromise = (async () => {
204
+ try {
205
+ if (getClient2()) {
206
+ return;
207
+ }
208
+ const coreOptions = {};
209
+ if (options?.apiKey) coreOptions.apiKey = options.apiKey;
210
+ if (options?.endpoint) coreOptions.endpoint = options.endpoint;
211
+ if (options?.environment) coreOptions.environment = options.environment;
212
+ if (options?.release) coreOptions.release = options.release;
213
+ if (options?.debug !== void 0) coreOptions.debug = options.debug;
214
+ if (options?.sampleRate !== void 0) coreOptions.sampleRate = options.sampleRate;
215
+ if (options?.maxBreadcrumbs !== void 0) coreOptions.maxBreadcrumbs = options.maxBreadcrumbs;
216
+ if (options?.tags) coreOptions.tags = options.tags;
217
+ if (options?.user) coreOptions.user = options.user;
218
+ if (options?.beforeSend) coreOptions.beforeSend = options.beforeSend;
219
+ if (options?.ignoreErrors) coreOptions.ignoreErrors = options.ignoreErrors;
220
+ init(coreOptions);
221
+ } finally {
222
+ initializationInProgress = false;
223
+ }
224
+ })();
225
+ await initializationPromise;
226
+ }
227
+ function setup(app, options) {
228
+ if (setupApps.has(app)) {
229
+ console.warn("[Bugwatch] setup() already called for this app. Skipping.");
230
+ return {
231
+ addErrorHandler: () => {
232
+ }
233
+ };
234
+ }
235
+ setupApps.add(app);
236
+ ensureInitialized(options).catch((err) => {
237
+ if (options?.debug) {
238
+ console.error("[Bugwatch] Initialization failed:", err);
239
+ }
240
+ });
241
+ const middlewareOptions = {};
242
+ if (options?.extractUser) middlewareOptions.extractUser = options.extractUser;
243
+ if (options?.filterHeaders) middlewareOptions.filterHeaders = options.filterHeaders;
244
+ if (options?.filterBody) middlewareOptions.filterBody = options.filterBody;
245
+ if (options?.includeBody !== void 0) middlewareOptions.includeBody = options.includeBody;
246
+ if (options?.addBreadcrumbs !== void 0) middlewareOptions.addBreadcrumbs = options.addBreadcrumbs;
247
+ if (options?.flushOnError !== void 0) middlewareOptions.flushOnError = options.flushOnError;
248
+ app.use(requestHandler(middlewareOptions));
249
+ let errorHandlerAdded = false;
250
+ const addErrorHandler = () => {
251
+ if (errorHandlerAdded) {
252
+ return;
253
+ }
254
+ errorHandlerAdded = true;
255
+ app.use(errorHandler(middlewareOptions));
256
+ };
257
+ const originalListen = app.listen.bind(app);
258
+ app.listen = function(...args) {
259
+ addErrorHandler();
260
+ app.listen = originalListen;
261
+ return originalListen(...args);
262
+ };
263
+ return { addErrorHandler };
264
+ }
265
+
266
+ // src/index.ts
267
+ import {
268
+ init as init2,
269
+ getClient as getClient3,
270
+ captureException as captureException2,
271
+ captureMessage,
272
+ addBreadcrumb as addBreadcrumb2,
273
+ setUser as setUser2,
274
+ setTag,
275
+ setExtra,
276
+ flush as flush2,
277
+ close
278
+ } from "@bugwatch/core";
279
+ export {
280
+ addBreadcrumb2 as addBreadcrumb,
281
+ captureError,
282
+ captureException2 as captureException,
283
+ captureMessage,
284
+ close,
285
+ errorHandler,
286
+ flush2 as flush,
287
+ getClient3 as getClient,
288
+ init2 as init,
289
+ requestHandler,
290
+ setExtra,
291
+ setTag,
292
+ setUser2 as setUser,
293
+ setup,
294
+ wrapHandler
295
+ };
296
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/setup.ts","../src/middleware.ts","../src/index.ts"],"sourcesContent":["/**\n * One-liner setup for Bugwatch Express integration.\n *\n * This module provides a simplified setup function that handles all\n * initialization and middleware registration in a single call.\n */\n\nimport type { Express, Application } from \"express\";\nimport { init, getClient, type BugwatchOptions } from \"@bugwatch/core\";\nimport { requestHandler, errorHandler } from \"./middleware\";\nimport type { BugwatchExpressOptions } from \"./types\";\n\n/**\n * Combined options for Express setup.\n * Includes both core SDK options and Express-specific middleware options.\n */\nexport interface BugwatchExpressSetupOptions\n extends BugwatchExpressOptions,\n Partial<BugwatchOptions> {}\n\n/**\n * Result object returned by setup() for advanced use cases.\n */\nexport interface BugwatchSetupResult {\n /**\n * Manually add the error handler middleware.\n * Call this if you're not using app.listen() directly (e.g., http.createServer).\n *\n * @example\n * ```typescript\n * const { addErrorHandler } = setup(app);\n *\n * // Define your routes...\n *\n * // If not using app.listen(), manually add error handler after routes:\n * addErrorHandler();\n *\n * http.createServer(app).listen(3000);\n * ```\n */\n addErrorHandler: () => void;\n}\n\n// Track which apps have been set up to prevent double-setup\nconst setupApps = new WeakSet<Express | Application>();\n\n// Mutex for thread-safe initialization\nlet initializationInProgress = false;\nlet initializationPromise: Promise<void> | null = null;\n\n/**\n * Thread-safe SDK initialization\n */\nasync function ensureInitialized(options?: Partial<BugwatchOptions>): Promise<void> {\n // Fast path: already initialized\n if (getClient()) {\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationInProgress && initializationPromise) {\n await initializationPromise;\n return;\n }\n\n // Start initialization\n initializationInProgress = true;\n initializationPromise = (async () => {\n try {\n // Double-check after acquiring \"lock\"\n if (getClient()) {\n return;\n }\n\n // Extract core options\n const coreOptions: Partial<BugwatchOptions> = {};\n if (options?.apiKey) coreOptions.apiKey = options.apiKey;\n if (options?.endpoint) coreOptions.endpoint = options.endpoint;\n if (options?.environment) coreOptions.environment = options.environment;\n if (options?.release) coreOptions.release = options.release;\n if (options?.debug !== undefined) coreOptions.debug = options.debug;\n if (options?.sampleRate !== undefined) coreOptions.sampleRate = options.sampleRate;\n if (options?.maxBreadcrumbs !== undefined) coreOptions.maxBreadcrumbs = options.maxBreadcrumbs;\n if (options?.tags) coreOptions.tags = options.tags;\n if (options?.user) coreOptions.user = options.user;\n if (options?.beforeSend) coreOptions.beforeSend = options.beforeSend;\n if (options?.ignoreErrors) coreOptions.ignoreErrors = options.ignoreErrors;\n\n init(coreOptions);\n } finally {\n initializationInProgress = false;\n }\n })();\n\n await initializationPromise;\n}\n\n/**\n * Set up Bugwatch for an Express application with a single call.\n *\n * This function:\n * 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)\n * 2. Adds the request handler middleware immediately\n * 3. Hooks into app.listen() to add the error handler before the server starts\n *\n * **Important**: Call `setup()` early, before defining your routes, so the\n * request handler can capture context for all requests.\n *\n * @param app - The Express application instance\n * @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)\n * @returns Object with `addErrorHandler()` for manual error handler registration\n *\n * @example Basic usage (recommended)\n * ```typescript\n * import express from \"express\";\n * import { setup } from \"@bugwatch/express\";\n *\n * const app = express();\n *\n * // Call setup BEFORE defining routes\n * setup(app);\n *\n * app.get(\"/\", (req, res) => res.send(\"Hello!\"));\n *\n * // Error handler is automatically added when listen() is called\n * app.listen(3000);\n * ```\n *\n * @example With explicit options\n * ```typescript\n * setup(app, {\n * apiKey: \"bw_live_xxxxx\",\n * environment: \"production\",\n * extractUser: (req) => ({ id: req.user?.id }),\n * });\n * ```\n *\n * @example With http.createServer (manual error handler)\n * ```typescript\n * import http from \"http\";\n * import express from \"express\";\n * import { setup } from \"@bugwatch/express\";\n *\n * const app = express();\n * const { addErrorHandler } = setup(app);\n *\n * app.get(\"/\", (req, res) => res.send(\"Hello!\"));\n *\n * // Manually add error handler AFTER routes\n * addErrorHandler();\n *\n * http.createServer(app).listen(3000);\n * ```\n */\nexport function setup(\n app: Express | Application,\n options?: BugwatchExpressSetupOptions\n): BugwatchSetupResult {\n // Prevent double-setup\n if (setupApps.has(app)) {\n console.warn(\"[Bugwatch] setup() already called for this app. Skipping.\");\n return {\n addErrorHandler: () => {\n // No-op for already-setup apps\n },\n };\n }\n setupApps.add(app);\n\n // Initialize core SDK with race condition protection\n // Note: This is async but we don't await it here to avoid breaking the sync API\n // The initialization will complete before any requests are handled\n ensureInitialized(options).catch((err) => {\n if (options?.debug) {\n console.error(\"[Bugwatch] Initialization failed:\", err);\n }\n });\n\n // Extract middleware-specific options\n const middlewareOptions: BugwatchExpressOptions = {};\n if (options?.extractUser) middlewareOptions.extractUser = options.extractUser;\n if (options?.filterHeaders) middlewareOptions.filterHeaders = options.filterHeaders;\n if (options?.filterBody) middlewareOptions.filterBody = options.filterBody;\n if (options?.includeBody !== undefined) middlewareOptions.includeBody = options.includeBody;\n if (options?.addBreadcrumbs !== undefined) middlewareOptions.addBreadcrumbs = options.addBreadcrumbs;\n if (options?.flushOnError !== undefined) middlewareOptions.flushOnError = options.flushOnError;\n\n // Add request handler middleware early\n app.use(requestHandler(middlewareOptions));\n\n // Track whether error handler has been added\n let errorHandlerAdded = false;\n\n /**\n * Add the error handler middleware.\n * This should be called AFTER all routes are defined.\n */\n const addErrorHandler = (): void => {\n if (errorHandlerAdded) {\n return; // Already added, skip\n }\n errorHandlerAdded = true;\n app.use(errorHandler(middlewareOptions));\n };\n\n // Hook into app.listen to add error handler automatically\n const originalListen = app.listen.bind(app);\n\n (app as any).listen = function (\n ...args: Parameters<typeof originalListen>\n ): ReturnType<typeof originalListen> {\n // Add error handler before calling original listen\n addErrorHandler();\n\n // Restore original listen for any future calls\n (app as any).listen = originalListen;\n\n return originalListen(...args);\n };\n\n return { addErrorHandler };\n}\n","import type { Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from \"express\";\nimport {\n getClient,\n captureException,\n addBreadcrumb,\n setUser,\n flush,\n runWithContext,\n createScopedContext,\n type RequestContext,\n type ErrorEvent,\n type ScopedContext,\n} from \"@bugwatch/core\";\nimport type { BugwatchExpressOptions, BugwatchRequest, AsyncRequestHandler } from \"./types\";\n\n/**\n * Headers that should be filtered out by default for security.\n */\nconst SENSITIVE_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"set-cookie\",\n \"x-api-key\",\n \"x-auth-token\",\n \"x-csrf-token\",\n \"x-xsrf-token\",\n \"proxy-authorization\",\n]);\n\n/**\n * Body fields that should be filtered out by default for security.\n */\nconst SENSITIVE_BODY_FIELDS = new Set([\n \"password\",\n \"secret\",\n \"token\",\n \"api_key\",\n \"apiKey\",\n \"credit_card\",\n \"creditCard\",\n \"ssn\",\n \"social_security\",\n]);\n\n/**\n * Default header filter function.\n */\nfunction defaultHeaderFilter(name: string): boolean {\n return !SENSITIVE_HEADERS.has(name.toLowerCase());\n}\n\n/**\n * Default body field filter function.\n */\nfunction defaultBodyFilter(key: string): boolean {\n return !SENSITIVE_BODY_FIELDS.has(key.toLowerCase());\n}\n\n/**\n * Filter an object's keys based on a filter function.\n */\nfunction filterObject<T extends Record<string, unknown>>(\n obj: T,\n filter: (key: string, value: unknown) => boolean\n): Partial<T> {\n const result: Partial<T> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (filter(key, value)) {\n (result as Record<string, unknown>)[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Extract request context from an Express request.\n */\nfunction extractRequestContext(\n req: Request,\n options: BugwatchExpressOptions\n): RequestContext {\n const headerFilter = options.filterHeaders || defaultHeaderFilter;\n const bodyFilter = options.filterBody || defaultBodyFilter;\n\n // Filter headers\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\" && headerFilter(name, value)) {\n headers[name] = value;\n } else if (Array.isArray(value)) {\n const filtered = value.filter((v) => headerFilter(name, v));\n if (filtered.length > 0) {\n headers[name] = filtered.join(\", \");\n }\n }\n }\n\n const context: RequestContext = {\n url: `${req.protocol}://${req.get(\"host\")}${req.originalUrl}`,\n method: req.method,\n headers,\n query_string: req.url.includes(\"?\") ? req.url.split(\"?\")[1] : undefined,\n };\n\n // Include body if requested and available\n if (options.includeBody && req.body && typeof req.body === \"object\") {\n context.data = filterObject(req.body, bodyFilter);\n }\n\n return context;\n}\n\n/**\n * Extract client IP from request.\n */\nfunction extractClientIp(req: Request): string | undefined {\n // Check common proxy headers\n const forwarded = req.get(\"x-forwarded-for\");\n if (forwarded) {\n const firstIp = forwarded.split(\",\")[0]?.trim();\n if (firstIp) return firstIp;\n }\n\n const realIp = req.get(\"x-real-ip\");\n if (realIp) return realIp;\n\n const cfIp = req.get(\"cf-connecting-ip\");\n if (cfIp) return cfIp;\n\n // Fall back to socket address\n return req.socket.remoteAddress;\n}\n\n/**\n * Request handler middleware that adds request context and breadcrumbs.\n *\n * This middleware should be added early in your middleware chain.\n * Uses AsyncLocalStorage for request-scoped context isolation to prevent\n * user context leakage between concurrent requests.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { requestHandler } from \"@bugwatch/express\";\n *\n * const app = express();\n * app.use(requestHandler());\n * ```\n */\nexport function requestHandler(\n options: BugwatchExpressOptions = {}\n): RequestHandler {\n return (req: BugwatchRequest, res: Response, next: NextFunction) => {\n const client = getClient();\n if (!client) {\n return next();\n }\n\n // Create a request-scoped context for isolation\n const scopedContext: ScopedContext = createScopedContext();\n\n // Store start time for breadcrumb\n req.bugwatch = { startTime: Date.now() };\n\n // Extract and set user context in the scoped context\n if (options.extractUser) {\n const user = options.extractUser(req);\n if (user) {\n scopedContext.user = user;\n }\n }\n\n // Run the rest of the request handling within the isolated context\n runWithContext(scopedContext, () => {\n // Add request start breadcrumb\n if (options.addBreadcrumbs !== false) {\n addBreadcrumb({\n category: \"http\",\n message: `${req.method} ${req.path}`,\n level: \"info\",\n data: {\n method: req.method,\n url: req.originalUrl,\n },\n });\n }\n\n // Track response for completion breadcrumb\n const originalEnd = res.end.bind(res);\n res.end = function (...args: Parameters<Response[\"end\"]>) {\n if (options.addBreadcrumbs !== false) {\n const duration = Date.now() - (req.bugwatch?.startTime || Date.now());\n addBreadcrumb({\n category: \"http\",\n message: `${req.method} ${req.path} -> ${res.statusCode}`,\n level: res.statusCode >= 500 ? \"error\" : res.statusCode >= 400 ? \"warning\" : \"info\",\n data: {\n method: req.method,\n url: req.originalUrl,\n status_code: res.statusCode,\n duration_ms: duration,\n },\n });\n }\n return originalEnd(...args);\n } as Response[\"end\"];\n\n next();\n });\n };\n}\n\n/**\n * Error handler middleware that captures errors to Bugwatch.\n *\n * This middleware should be added after all your routes.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { requestHandler, errorHandler } from \"@bugwatch/express\";\n *\n * const app = express();\n * app.use(requestHandler());\n *\n * // ... your routes ...\n *\n * app.use(errorHandler());\n * ```\n */\nexport function errorHandler(\n options: BugwatchExpressOptions = {}\n): ErrorRequestHandler {\n return async (\n err: Error,\n req: BugwatchRequest,\n res: Response,\n next: NextFunction\n ) => {\n const client = getClient();\n if (!client) {\n return next(err);\n }\n\n // Build request context\n const requestContext = extractRequestContext(req, options);\n\n // Build extra context\n const extra: Record<string, unknown> = {\n request: requestContext,\n };\n\n const clientIp = extractClientIp(req);\n if (clientIp) {\n extra.client_ip = clientIp;\n }\n\n // Capture the error\n const eventId = captureException(err, {\n request: requestContext,\n extra,\n tags: {\n \"http.method\": req.method,\n \"http.url\": req.originalUrl,\n },\n });\n\n // Store event ID for reference\n if (req.bugwatch) {\n req.bugwatch.eventId = eventId;\n }\n\n // Flush if requested (useful for serverless)\n if (options.flushOnError) {\n await flush();\n }\n\n // Pass to next error handler\n next(err);\n };\n}\n\n/**\n * Wrap an async request handler to automatically capture errors.\n *\n * @example\n * ```typescript\n * import { wrapHandler } from \"@bugwatch/express\";\n *\n * app.get(\"/users/:id\", wrapHandler(async (req, res) => {\n * const user = await getUserById(req.params.id);\n * res.json(user);\n * }));\n * ```\n */\nexport function wrapHandler(handler: AsyncRequestHandler): RequestHandler {\n return (req: Request, res: Response, next: NextFunction) => {\n Promise.resolve(handler(req, res, next)).catch(next);\n };\n}\n\n/**\n * Manually capture an error with Express request context.\n *\n * @example\n * ```typescript\n * import { captureError } from \"@bugwatch/express\";\n *\n * app.get(\"/users/:id\", (req, res) => {\n * try {\n * const user = getUserById(req.params.id);\n * res.json(user);\n * } catch (err) {\n * captureError(req, err);\n * res.status(500).json({ error: \"Internal server error\" });\n * }\n * });\n * ```\n */\nexport function captureError(\n req: Request,\n error: Error,\n options: BugwatchExpressOptions = {}\n): string {\n const client = getClient();\n if (!client) {\n return \"\";\n }\n\n const requestContext = extractRequestContext(req, options);\n const clientIp = extractClientIp(req);\n\n return captureException(error, {\n request: requestContext,\n extra: {\n request: requestContext,\n ...(clientIp && { client_ip: clientIp }),\n },\n tags: {\n \"http.method\": req.method,\n \"http.url\": req.originalUrl,\n },\n });\n}\n","/**\n * @bugwatch/express - Express.js integration for Bugwatch\n *\n * This package provides middleware for automatically capturing errors\n * and request context in Express.js applications.\n *\n * @example One-liner setup (recommended)\n * ```typescript\n * import express from \"express\";\n * import { setup } from \"@bugwatch/express\";\n *\n * const app = express();\n *\n * // Single call handles everything (uses BUGWATCH_API_KEY env var)\n * setup(app);\n *\n * // Or with explicit options\n * setup(app, {\n * apiKey: \"bw_live_xxxxx\",\n * extractUser: (req) => ({ id: req.user?.id }),\n * });\n *\n * app.get(\"/\", (req, res) => res.send(\"Hello World!\"));\n *\n * app.listen(3000);\n * ```\n *\n * @example Manual setup (for more control)\n * ```typescript\n * import express from \"express\";\n * import { init } from \"@bugwatch/core\";\n * import { requestHandler, errorHandler } from \"@bugwatch/express\";\n *\n * init({ apiKey: \"your-api-key\" });\n *\n * const app = express();\n * app.use(requestHandler());\n *\n * // Your routes...\n *\n * app.use(errorHandler());\n * app.listen(3000);\n * ```\n */\n\n// Export one-liner setup\nexport { setup } from \"./setup\";\nexport type { BugwatchExpressSetupOptions, BugwatchSetupResult } from \"./setup\";\n\n// Export middleware functions\nexport {\n requestHandler,\n errorHandler,\n wrapHandler,\n captureError,\n} from \"./middleware\";\n\n// Export types\nexport type {\n BugwatchExpressOptions,\n BugwatchRequest,\n AsyncRequestHandler,\n} from \"./types\";\n\n// Re-export core functions for convenience\nexport {\n init,\n getClient,\n captureException,\n captureMessage,\n addBreadcrumb,\n setUser,\n setTag,\n setExtra,\n flush,\n close,\n} from \"@bugwatch/core\";\n"],"mappings":"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","names":["getClient","getClient","init","getClient","captureException","addBreadcrumb","setUser","flush"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@bugwatch/express",
3
+ "version": "0.1.0",
4
+ "description": "Bugwatch SDK integration for Express.js",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "typecheck": "tsc --noEmit",
22
+ "clean": "rm -rf dist",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest"
25
+ },
26
+ "dependencies": {
27
+ "@bugwatch/core": "*"
28
+ },
29
+ "peerDependencies": {
30
+ "express": "^4.0.0 || ^5.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/express": "^4.17.21",
34
+ "express": "^4.18.2",
35
+ "tsup": "^8.0.0",
36
+ "typescript": "^5.3.0"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/bugwatch/bugwatch.git",
44
+ "directory": "packages/sdk/express"
45
+ },
46
+ "license": "MIT",
47
+ "keywords": [
48
+ "bugwatch",
49
+ "error-tracking",
50
+ "express",
51
+ "middleware",
52
+ "monitoring"
53
+ ]
54
+ }