@bugwatch/fastify 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.
- package/dist/index.d.mts +170 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.js +275 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +245 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyInstance, FastifyPluginAsync } from 'fastify';
|
|
2
|
+
import { BugwatchClient, 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 Fastify plugin
|
|
7
|
+
*/
|
|
8
|
+
interface BugwatchFastifyOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Extract user context from the request.
|
|
11
|
+
* Return null to skip user context extraction.
|
|
12
|
+
*/
|
|
13
|
+
extractUser?: (request: FastifyRequest) => 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 automatically capture unhandled errors.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
captureErrors?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to flush events before sending error response.
|
|
42
|
+
* Useful for serverless environments.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
flushOnError?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Bugwatch decorator interface for Fastify
|
|
49
|
+
*/
|
|
50
|
+
interface BugwatchDecorator {
|
|
51
|
+
/**
|
|
52
|
+
* Capture an error with request context
|
|
53
|
+
*/
|
|
54
|
+
captureError: (error: Error) => string;
|
|
55
|
+
/**
|
|
56
|
+
* Capture a message with request context
|
|
57
|
+
*/
|
|
58
|
+
captureMessage: (message: string, level?: "debug" | "info" | "warning" | "error") => string;
|
|
59
|
+
/**
|
|
60
|
+
* Get the current event ID if an error was captured
|
|
61
|
+
*/
|
|
62
|
+
getEventId: () => string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Access the underlying Bugwatch client
|
|
65
|
+
*/
|
|
66
|
+
client: BugwatchClient | null;
|
|
67
|
+
}
|
|
68
|
+
declare module "fastify" {
|
|
69
|
+
interface FastifyRequest {
|
|
70
|
+
bugwatch: BugwatchDecorator;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* One-liner setup for Bugwatch Fastify integration.
|
|
76
|
+
*
|
|
77
|
+
* This module provides a simplified setup function that handles all
|
|
78
|
+
* initialization and plugin registration in a single call.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Combined options for Fastify setup.
|
|
83
|
+
* Includes both core SDK options and Fastify-specific plugin options.
|
|
84
|
+
*/
|
|
85
|
+
interface BugwatchFastifySetupOptions extends BugwatchFastifyOptions, Partial<BugwatchOptions> {
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Set up Bugwatch for a Fastify application with a single call.
|
|
89
|
+
*
|
|
90
|
+
* This function:
|
|
91
|
+
* 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)
|
|
92
|
+
* 2. Registers the Bugwatch plugin with the Fastify instance
|
|
93
|
+
*
|
|
94
|
+
* @param fastify - The Fastify instance
|
|
95
|
+
* @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)
|
|
96
|
+
* @returns Promise that resolves when the plugin is registered
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import Fastify from "fastify";
|
|
101
|
+
* import { setup } from "@bugwatch/fastify";
|
|
102
|
+
*
|
|
103
|
+
* const fastify = Fastify();
|
|
104
|
+
*
|
|
105
|
+
* // Minimal setup (uses BUGWATCH_API_KEY env var)
|
|
106
|
+
* await setup(fastify);
|
|
107
|
+
*
|
|
108
|
+
* // Or with explicit options
|
|
109
|
+
* await setup(fastify, {
|
|
110
|
+
* apiKey: "bw_live_xxxxx",
|
|
111
|
+
* environment: "production",
|
|
112
|
+
* extractUser: (request) => ({ id: request.user?.id }),
|
|
113
|
+
* });
|
|
114
|
+
*
|
|
115
|
+
* fastify.get("/", async () => ({ hello: "world" }));
|
|
116
|
+
*
|
|
117
|
+
* await fastify.listen({ port: 3000 });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function setup(fastify: FastifyInstance, options?: BugwatchFastifySetupOptions): Promise<void>;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Bugwatch Fastify plugin.
|
|
124
|
+
*
|
|
125
|
+
* This plugin automatically:
|
|
126
|
+
* - Captures request context for errors
|
|
127
|
+
* - Adds HTTP request breadcrumbs
|
|
128
|
+
* - Reports unhandled errors to Bugwatch
|
|
129
|
+
* - Provides `request.bugwatch` decorator for manual error capture
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* import Fastify from "fastify";
|
|
134
|
+
* import { init } from "@bugwatch/core";
|
|
135
|
+
* import { bugwatchPlugin } from "@bugwatch/fastify";
|
|
136
|
+
*
|
|
137
|
+
* // Initialize Bugwatch
|
|
138
|
+
* init({ apiKey: "your-api-key" });
|
|
139
|
+
*
|
|
140
|
+
* const fastify = Fastify();
|
|
141
|
+
*
|
|
142
|
+
* // Register the plugin
|
|
143
|
+
* await fastify.register(bugwatchPlugin, {
|
|
144
|
+
* extractUser: (request) => {
|
|
145
|
+
* // Extract user from your auth system
|
|
146
|
+
* return request.user ? { id: request.user.id } : null;
|
|
147
|
+
* },
|
|
148
|
+
* });
|
|
149
|
+
*
|
|
150
|
+
* // Your routes
|
|
151
|
+
* fastify.get("/", async (request, reply) => {
|
|
152
|
+
* return { hello: "world" };
|
|
153
|
+
* });
|
|
154
|
+
*
|
|
155
|
+
* // Manual error capture
|
|
156
|
+
* fastify.get("/risky", async (request, reply) => {
|
|
157
|
+
* try {
|
|
158
|
+
* await riskyOperation();
|
|
159
|
+
* } catch (err) {
|
|
160
|
+
* request.bugwatch.captureError(err);
|
|
161
|
+
* return { error: "Something went wrong" };
|
|
162
|
+
* }
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* await fastify.listen({ port: 3000 });
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare const bugwatchPlugin: FastifyPluginAsync<BugwatchFastifyOptions>;
|
|
169
|
+
|
|
170
|
+
export { type BugwatchDecorator, type BugwatchFastifyOptions, type BugwatchFastifySetupOptions, bugwatchPlugin, setup };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyInstance, FastifyPluginAsync } from 'fastify';
|
|
2
|
+
import { BugwatchClient, 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 Fastify plugin
|
|
7
|
+
*/
|
|
8
|
+
interface BugwatchFastifyOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Extract user context from the request.
|
|
11
|
+
* Return null to skip user context extraction.
|
|
12
|
+
*/
|
|
13
|
+
extractUser?: (request: FastifyRequest) => 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 automatically capture unhandled errors.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
captureErrors?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to flush events before sending error response.
|
|
42
|
+
* Useful for serverless environments.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
flushOnError?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Bugwatch decorator interface for Fastify
|
|
49
|
+
*/
|
|
50
|
+
interface BugwatchDecorator {
|
|
51
|
+
/**
|
|
52
|
+
* Capture an error with request context
|
|
53
|
+
*/
|
|
54
|
+
captureError: (error: Error) => string;
|
|
55
|
+
/**
|
|
56
|
+
* Capture a message with request context
|
|
57
|
+
*/
|
|
58
|
+
captureMessage: (message: string, level?: "debug" | "info" | "warning" | "error") => string;
|
|
59
|
+
/**
|
|
60
|
+
* Get the current event ID if an error was captured
|
|
61
|
+
*/
|
|
62
|
+
getEventId: () => string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Access the underlying Bugwatch client
|
|
65
|
+
*/
|
|
66
|
+
client: BugwatchClient | null;
|
|
67
|
+
}
|
|
68
|
+
declare module "fastify" {
|
|
69
|
+
interface FastifyRequest {
|
|
70
|
+
bugwatch: BugwatchDecorator;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* One-liner setup for Bugwatch Fastify integration.
|
|
76
|
+
*
|
|
77
|
+
* This module provides a simplified setup function that handles all
|
|
78
|
+
* initialization and plugin registration in a single call.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Combined options for Fastify setup.
|
|
83
|
+
* Includes both core SDK options and Fastify-specific plugin options.
|
|
84
|
+
*/
|
|
85
|
+
interface BugwatchFastifySetupOptions extends BugwatchFastifyOptions, Partial<BugwatchOptions> {
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Set up Bugwatch for a Fastify application with a single call.
|
|
89
|
+
*
|
|
90
|
+
* This function:
|
|
91
|
+
* 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)
|
|
92
|
+
* 2. Registers the Bugwatch plugin with the Fastify instance
|
|
93
|
+
*
|
|
94
|
+
* @param fastify - The Fastify instance
|
|
95
|
+
* @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)
|
|
96
|
+
* @returns Promise that resolves when the plugin is registered
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import Fastify from "fastify";
|
|
101
|
+
* import { setup } from "@bugwatch/fastify";
|
|
102
|
+
*
|
|
103
|
+
* const fastify = Fastify();
|
|
104
|
+
*
|
|
105
|
+
* // Minimal setup (uses BUGWATCH_API_KEY env var)
|
|
106
|
+
* await setup(fastify);
|
|
107
|
+
*
|
|
108
|
+
* // Or with explicit options
|
|
109
|
+
* await setup(fastify, {
|
|
110
|
+
* apiKey: "bw_live_xxxxx",
|
|
111
|
+
* environment: "production",
|
|
112
|
+
* extractUser: (request) => ({ id: request.user?.id }),
|
|
113
|
+
* });
|
|
114
|
+
*
|
|
115
|
+
* fastify.get("/", async () => ({ hello: "world" }));
|
|
116
|
+
*
|
|
117
|
+
* await fastify.listen({ port: 3000 });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function setup(fastify: FastifyInstance, options?: BugwatchFastifySetupOptions): Promise<void>;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Bugwatch Fastify plugin.
|
|
124
|
+
*
|
|
125
|
+
* This plugin automatically:
|
|
126
|
+
* - Captures request context for errors
|
|
127
|
+
* - Adds HTTP request breadcrumbs
|
|
128
|
+
* - Reports unhandled errors to Bugwatch
|
|
129
|
+
* - Provides `request.bugwatch` decorator for manual error capture
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* import Fastify from "fastify";
|
|
134
|
+
* import { init } from "@bugwatch/core";
|
|
135
|
+
* import { bugwatchPlugin } from "@bugwatch/fastify";
|
|
136
|
+
*
|
|
137
|
+
* // Initialize Bugwatch
|
|
138
|
+
* init({ apiKey: "your-api-key" });
|
|
139
|
+
*
|
|
140
|
+
* const fastify = Fastify();
|
|
141
|
+
*
|
|
142
|
+
* // Register the plugin
|
|
143
|
+
* await fastify.register(bugwatchPlugin, {
|
|
144
|
+
* extractUser: (request) => {
|
|
145
|
+
* // Extract user from your auth system
|
|
146
|
+
* return request.user ? { id: request.user.id } : null;
|
|
147
|
+
* },
|
|
148
|
+
* });
|
|
149
|
+
*
|
|
150
|
+
* // Your routes
|
|
151
|
+
* fastify.get("/", async (request, reply) => {
|
|
152
|
+
* return { hello: "world" };
|
|
153
|
+
* });
|
|
154
|
+
*
|
|
155
|
+
* // Manual error capture
|
|
156
|
+
* fastify.get("/risky", async (request, reply) => {
|
|
157
|
+
* try {
|
|
158
|
+
* await riskyOperation();
|
|
159
|
+
* } catch (err) {
|
|
160
|
+
* request.bugwatch.captureError(err);
|
|
161
|
+
* return { error: "Something went wrong" };
|
|
162
|
+
* }
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* await fastify.listen({ port: 3000 });
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare const bugwatchPlugin: FastifyPluginAsync<BugwatchFastifyOptions>;
|
|
169
|
+
|
|
170
|
+
export { type BugwatchDecorator, type BugwatchFastifyOptions, type BugwatchFastifySetupOptions, bugwatchPlugin, setup };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
addBreadcrumb: () => import_core3.addBreadcrumb,
|
|
34
|
+
bugwatchPlugin: () => bugwatchPlugin,
|
|
35
|
+
captureException: () => import_core3.captureException,
|
|
36
|
+
captureMessage: () => import_core3.captureMessage,
|
|
37
|
+
close: () => import_core3.close,
|
|
38
|
+
flush: () => import_core3.flush,
|
|
39
|
+
getClient: () => import_core3.getClient,
|
|
40
|
+
init: () => import_core3.init,
|
|
41
|
+
setExtra: () => import_core3.setExtra,
|
|
42
|
+
setTag: () => import_core3.setTag,
|
|
43
|
+
setUser: () => import_core3.setUser,
|
|
44
|
+
setup: () => setup
|
|
45
|
+
});
|
|
46
|
+
module.exports = __toCommonJS(index_exports);
|
|
47
|
+
|
|
48
|
+
// src/setup.ts
|
|
49
|
+
var import_core2 = require("@bugwatch/core");
|
|
50
|
+
|
|
51
|
+
// src/plugin.ts
|
|
52
|
+
var import_fastify_plugin = __toESM(require("fastify-plugin"));
|
|
53
|
+
var import_core = require("@bugwatch/core");
|
|
54
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
55
|
+
"authorization",
|
|
56
|
+
"cookie",
|
|
57
|
+
"set-cookie",
|
|
58
|
+
"x-api-key",
|
|
59
|
+
"x-auth-token",
|
|
60
|
+
"x-csrf-token",
|
|
61
|
+
"x-xsrf-token",
|
|
62
|
+
"proxy-authorization"
|
|
63
|
+
]);
|
|
64
|
+
var SENSITIVE_BODY_FIELDS = /* @__PURE__ */ new Set([
|
|
65
|
+
"password",
|
|
66
|
+
"secret",
|
|
67
|
+
"token",
|
|
68
|
+
"api_key",
|
|
69
|
+
"apiKey",
|
|
70
|
+
"credit_card",
|
|
71
|
+
"creditCard",
|
|
72
|
+
"ssn",
|
|
73
|
+
"social_security"
|
|
74
|
+
]);
|
|
75
|
+
function defaultHeaderFilter(name) {
|
|
76
|
+
return !SENSITIVE_HEADERS.has(name.toLowerCase());
|
|
77
|
+
}
|
|
78
|
+
function defaultBodyFilter(key) {
|
|
79
|
+
return !SENSITIVE_BODY_FIELDS.has(key.toLowerCase());
|
|
80
|
+
}
|
|
81
|
+
function filterObject(obj, filter) {
|
|
82
|
+
const result = {};
|
|
83
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
84
|
+
if (filter(key, value)) {
|
|
85
|
+
result[key] = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
function extractRequestContext(request, options) {
|
|
91
|
+
const headerFilter = options.filterHeaders || defaultHeaderFilter;
|
|
92
|
+
const bodyFilter = options.filterBody || defaultBodyFilter;
|
|
93
|
+
const headers = {};
|
|
94
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
95
|
+
if (typeof value === "string" && headerFilter(name, value)) {
|
|
96
|
+
headers[name] = value;
|
|
97
|
+
} else if (Array.isArray(value)) {
|
|
98
|
+
const filtered = value.filter((v) => headerFilter(name, v));
|
|
99
|
+
if (filtered.length > 0) {
|
|
100
|
+
headers[name] = filtered.join(", ");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const context = {
|
|
105
|
+
url: request.url,
|
|
106
|
+
method: request.method,
|
|
107
|
+
headers,
|
|
108
|
+
query_string: request.url.includes("?") ? request.url.split("?")[1] : void 0
|
|
109
|
+
};
|
|
110
|
+
if (options.includeBody && request.body && typeof request.body === "object") {
|
|
111
|
+
context.data = filterObject(request.body, bodyFilter);
|
|
112
|
+
}
|
|
113
|
+
return context;
|
|
114
|
+
}
|
|
115
|
+
function extractClientIp(request) {
|
|
116
|
+
const forwarded = request.headers["x-forwarded-for"];
|
|
117
|
+
if (typeof forwarded === "string") {
|
|
118
|
+
const firstIp = forwarded.split(",")[0]?.trim();
|
|
119
|
+
if (firstIp) return firstIp;
|
|
120
|
+
}
|
|
121
|
+
const realIp = request.headers["x-real-ip"];
|
|
122
|
+
if (typeof realIp === "string") return realIp;
|
|
123
|
+
const cfIp = request.headers["cf-connecting-ip"];
|
|
124
|
+
if (typeof cfIp === "string") return cfIp;
|
|
125
|
+
return request.ip;
|
|
126
|
+
}
|
|
127
|
+
function createBugwatchDecorator(request, options) {
|
|
128
|
+
let eventId;
|
|
129
|
+
const client = (0, import_core.getClient)();
|
|
130
|
+
return {
|
|
131
|
+
captureError(error) {
|
|
132
|
+
if (!client) return "";
|
|
133
|
+
const requestContext = extractRequestContext(request, options);
|
|
134
|
+
const clientIp = extractClientIp(request);
|
|
135
|
+
eventId = (0, import_core.captureException)(error, {
|
|
136
|
+
request: requestContext,
|
|
137
|
+
extra: {
|
|
138
|
+
request: requestContext,
|
|
139
|
+
...clientIp && { client_ip: clientIp }
|
|
140
|
+
},
|
|
141
|
+
tags: {
|
|
142
|
+
"http.method": request.method,
|
|
143
|
+
"http.url": request.url
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return eventId;
|
|
147
|
+
},
|
|
148
|
+
captureMessage(message, level = "info") {
|
|
149
|
+
if (!client) return "";
|
|
150
|
+
eventId = (0, import_core.captureMessage)(message, level);
|
|
151
|
+
return eventId;
|
|
152
|
+
},
|
|
153
|
+
getEventId() {
|
|
154
|
+
return eventId;
|
|
155
|
+
},
|
|
156
|
+
get client() {
|
|
157
|
+
return client;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
var bugwatchPluginImpl = async (fastify, options) => {
|
|
162
|
+
const opts = {
|
|
163
|
+
addBreadcrumbs: true,
|
|
164
|
+
captureErrors: true,
|
|
165
|
+
includeBody: false,
|
|
166
|
+
flushOnError: false,
|
|
167
|
+
...options
|
|
168
|
+
};
|
|
169
|
+
const requestStartTimes = /* @__PURE__ */ new WeakMap();
|
|
170
|
+
const requestContexts = /* @__PURE__ */ new WeakMap();
|
|
171
|
+
fastify.decorateRequest("bugwatch", null);
|
|
172
|
+
fastify.addHook("onRequest", async (request) => {
|
|
173
|
+
const client = (0, import_core.getClient)();
|
|
174
|
+
if (!client) return;
|
|
175
|
+
requestStartTimes.set(request, Date.now());
|
|
176
|
+
const scopedContext = (0, import_core.createScopedContext)();
|
|
177
|
+
requestContexts.set(request, scopedContext);
|
|
178
|
+
request.bugwatch = createBugwatchDecorator(request, opts);
|
|
179
|
+
if (opts.extractUser) {
|
|
180
|
+
const user = opts.extractUser(request);
|
|
181
|
+
if (user) {
|
|
182
|
+
scopedContext.user = user;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (opts.addBreadcrumbs) {
|
|
186
|
+
(0, import_core.addBreadcrumb)({
|
|
187
|
+
category: "http",
|
|
188
|
+
message: `${request.method} ${request.url}`,
|
|
189
|
+
level: "info",
|
|
190
|
+
data: {
|
|
191
|
+
method: request.method,
|
|
192
|
+
url: request.url
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
fastify.addHook("onResponse", async (request, reply) => {
|
|
198
|
+
if (!opts.addBreadcrumbs) return;
|
|
199
|
+
const client = (0, import_core.getClient)();
|
|
200
|
+
if (!client) return;
|
|
201
|
+
const startTime = requestStartTimes.get(request);
|
|
202
|
+
const duration = startTime ? Date.now() - startTime : void 0;
|
|
203
|
+
(0, import_core.addBreadcrumb)({
|
|
204
|
+
category: "http",
|
|
205
|
+
message: `${request.method} ${request.url} -> ${reply.statusCode}`,
|
|
206
|
+
level: reply.statusCode >= 500 ? "error" : reply.statusCode >= 400 ? "warning" : "info",
|
|
207
|
+
data: {
|
|
208
|
+
method: request.method,
|
|
209
|
+
url: request.url,
|
|
210
|
+
status_code: reply.statusCode,
|
|
211
|
+
...duration !== void 0 && { duration_ms: duration }
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
fastify.addHook("onError", async (request, reply, error) => {
|
|
216
|
+
if (!opts.captureErrors) return;
|
|
217
|
+
const client = (0, import_core.getClient)();
|
|
218
|
+
if (!client) return;
|
|
219
|
+
request.bugwatch?.captureError(error);
|
|
220
|
+
if (opts.flushOnError) {
|
|
221
|
+
await (0, import_core.flush)();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
var bugwatchPlugin = (0, import_fastify_plugin.default)(bugwatchPluginImpl, {
|
|
226
|
+
fastify: "4.x || 5.x",
|
|
227
|
+
name: "@bugwatch/fastify"
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// src/setup.ts
|
|
231
|
+
async function setup(fastify, options) {
|
|
232
|
+
if (!(0, import_core2.getClient)()) {
|
|
233
|
+
const coreOptions = {};
|
|
234
|
+
if (options?.apiKey) coreOptions.apiKey = options.apiKey;
|
|
235
|
+
if (options?.endpoint) coreOptions.endpoint = options.endpoint;
|
|
236
|
+
if (options?.environment) coreOptions.environment = options.environment;
|
|
237
|
+
if (options?.release) coreOptions.release = options.release;
|
|
238
|
+
if (options?.debug !== void 0) coreOptions.debug = options.debug;
|
|
239
|
+
if (options?.sampleRate !== void 0) coreOptions.sampleRate = options.sampleRate;
|
|
240
|
+
if (options?.maxBreadcrumbs !== void 0) coreOptions.maxBreadcrumbs = options.maxBreadcrumbs;
|
|
241
|
+
if (options?.tags) coreOptions.tags = options.tags;
|
|
242
|
+
if (options?.user) coreOptions.user = options.user;
|
|
243
|
+
if (options?.beforeSend) coreOptions.beforeSend = options.beforeSend;
|
|
244
|
+
if (options?.ignoreErrors) coreOptions.ignoreErrors = options.ignoreErrors;
|
|
245
|
+
(0, import_core2.init)(coreOptions);
|
|
246
|
+
}
|
|
247
|
+
const pluginOptions = {};
|
|
248
|
+
if (options?.extractUser) pluginOptions.extractUser = options.extractUser;
|
|
249
|
+
if (options?.filterHeaders) pluginOptions.filterHeaders = options.filterHeaders;
|
|
250
|
+
if (options?.filterBody) pluginOptions.filterBody = options.filterBody;
|
|
251
|
+
if (options?.includeBody !== void 0) pluginOptions.includeBody = options.includeBody;
|
|
252
|
+
if (options?.addBreadcrumbs !== void 0) pluginOptions.addBreadcrumbs = options.addBreadcrumbs;
|
|
253
|
+
if (options?.captureErrors !== void 0) pluginOptions.captureErrors = options.captureErrors;
|
|
254
|
+
if (options?.flushOnError !== void 0) pluginOptions.flushOnError = options.flushOnError;
|
|
255
|
+
await fastify.register(bugwatchPlugin, pluginOptions);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/index.ts
|
|
259
|
+
var import_core3 = require("@bugwatch/core");
|
|
260
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
261
|
+
0 && (module.exports = {
|
|
262
|
+
addBreadcrumb,
|
|
263
|
+
bugwatchPlugin,
|
|
264
|
+
captureException,
|
|
265
|
+
captureMessage,
|
|
266
|
+
close,
|
|
267
|
+
flush,
|
|
268
|
+
getClient,
|
|
269
|
+
init,
|
|
270
|
+
setExtra,
|
|
271
|
+
setTag,
|
|
272
|
+
setUser,
|
|
273
|
+
setup
|
|
274
|
+
});
|
|
275
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/setup.ts","../src/plugin.ts"],"sourcesContent":["/**\n * @bugwatch/fastify - Fastify integration for Bugwatch\n *\n * This package provides a plugin for automatically capturing errors\n * and request context in Fastify applications.\n *\n * @example One-liner setup (recommended)\n * ```typescript\n * import Fastify from \"fastify\";\n * import { setup } from \"@bugwatch/fastify\";\n *\n * const fastify = Fastify();\n *\n * // Single call handles everything (uses BUGWATCH_API_KEY env var)\n * await setup(fastify);\n *\n * // Or with explicit options\n * await setup(fastify, {\n * apiKey: \"bw_live_xxxxx\",\n * extractUser: (request) => ({ id: request.user?.id }),\n * });\n *\n * fastify.get(\"/\", async () => ({ hello: \"world\" }));\n *\n * await fastify.listen({ port: 3000 });\n * ```\n *\n * @example Manual setup (for more control)\n * ```typescript\n * import Fastify from \"fastify\";\n * import { init } from \"@bugwatch/core\";\n * import { bugwatchPlugin } from \"@bugwatch/fastify\";\n *\n * init({ apiKey: \"your-api-key\" });\n *\n * const fastify = Fastify();\n * await fastify.register(bugwatchPlugin);\n *\n * // request.bugwatch is available for manual capture\n * fastify.get(\"/risky\", async (request) => {\n * try {\n * await riskyOperation();\n * } catch (err) {\n * request.bugwatch.captureError(err);\n * return { error: \"Something went wrong\" };\n * }\n * });\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\n\n// Export one-liner setup\nexport { setup } from \"./setup\";\nexport type { BugwatchFastifySetupOptions } from \"./setup\";\n\n// Export plugin\nexport { bugwatchPlugin } from \"./plugin\";\n\n// Export types\nexport type {\n BugwatchFastifyOptions,\n BugwatchDecorator,\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 Fastify integration.\n *\n * This module provides a simplified setup function that handles all\n * initialization and plugin registration in a single call.\n */\n\nimport type { FastifyInstance } from \"fastify\";\nimport { init, getClient, type BugwatchOptions } from \"@bugwatch/core\";\nimport { bugwatchPlugin } from \"./plugin\";\nimport type { BugwatchFastifyOptions } from \"./types\";\n\n/**\n * Combined options for Fastify setup.\n * Includes both core SDK options and Fastify-specific plugin options.\n */\nexport interface BugwatchFastifySetupOptions\n extends BugwatchFastifyOptions,\n Partial<BugwatchOptions> {}\n\n/**\n * Set up Bugwatch for a Fastify application with a single call.\n *\n * This function:\n * 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)\n * 2. Registers the Bugwatch plugin with the Fastify instance\n *\n * @param fastify - The Fastify instance\n * @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)\n * @returns Promise that resolves when the plugin is registered\n *\n * @example\n * ```typescript\n * import Fastify from \"fastify\";\n * import { setup } from \"@bugwatch/fastify\";\n *\n * const fastify = Fastify();\n *\n * // Minimal setup (uses BUGWATCH_API_KEY env var)\n * await setup(fastify);\n *\n * // Or with explicit options\n * await setup(fastify, {\n * apiKey: \"bw_live_xxxxx\",\n * environment: \"production\",\n * extractUser: (request) => ({ id: request.user?.id }),\n * });\n *\n * fastify.get(\"/\", async () => ({ hello: \"world\" }));\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\nexport async function setup(\n fastify: FastifyInstance,\n options?: BugwatchFastifySetupOptions\n): Promise<void> {\n // Initialize core SDK if not already initialized\n if (!getClient()) {\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 }\n\n // Extract plugin-specific options\n const pluginOptions: BugwatchFastifyOptions = {};\n if (options?.extractUser) pluginOptions.extractUser = options.extractUser;\n if (options?.filterHeaders) pluginOptions.filterHeaders = options.filterHeaders;\n if (options?.filterBody) pluginOptions.filterBody = options.filterBody;\n if (options?.includeBody !== undefined) pluginOptions.includeBody = options.includeBody;\n if (options?.addBreadcrumbs !== undefined) pluginOptions.addBreadcrumbs = options.addBreadcrumbs;\n if (options?.captureErrors !== undefined) pluginOptions.captureErrors = options.captureErrors;\n if (options?.flushOnError !== undefined) pluginOptions.flushOnError = options.flushOnError;\n\n // Register the plugin\n await fastify.register(bugwatchPlugin, pluginOptions);\n}\n","import type { FastifyPluginAsync, FastifyRequest, FastifyReply } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport {\n getClient,\n captureException,\n captureMessage as coreCaptureMessage,\n addBreadcrumb,\n setUser,\n flush,\n runWithContextAsync,\n createScopedContext,\n type RequestContext,\n type BugwatchClient,\n type ScopedContext,\n} from \"@bugwatch/core\";\nimport type { BugwatchFastifyOptions, BugwatchDecorator } 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 a Fastify request.\n */\nfunction extractRequestContext(\n request: FastifyRequest,\n options: BugwatchFastifyOptions\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(request.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: request.url,\n method: request.method,\n headers,\n query_string: request.url.includes(\"?\") ? request.url.split(\"?\")[1] : undefined,\n };\n\n // Include body if requested and available\n if (options.includeBody && request.body && typeof request.body === \"object\") {\n context.data = filterObject(request.body as Record<string, unknown>, bodyFilter);\n }\n\n return context;\n}\n\n/**\n * Extract client IP from request.\n */\nfunction extractClientIp(request: FastifyRequest): string | undefined {\n // Check common proxy headers\n const forwarded = request.headers[\"x-forwarded-for\"];\n if (typeof forwarded === \"string\") {\n const firstIp = forwarded.split(\",\")[0]?.trim();\n if (firstIp) return firstIp;\n }\n\n const realIp = request.headers[\"x-real-ip\"];\n if (typeof realIp === \"string\") return realIp;\n\n const cfIp = request.headers[\"cf-connecting-ip\"];\n if (typeof cfIp === \"string\") return cfIp;\n\n // Fall back to connection address\n return request.ip;\n}\n\n/**\n * Create Bugwatch decorator for a request.\n */\nfunction createBugwatchDecorator(\n request: FastifyRequest,\n options: BugwatchFastifyOptions\n): BugwatchDecorator {\n let eventId: string | undefined;\n const client = getClient();\n\n return {\n captureError(error: Error): string {\n if (!client) return \"\";\n\n const requestContext = extractRequestContext(request, options);\n const clientIp = extractClientIp(request);\n\n eventId = captureException(error, {\n request: requestContext,\n extra: {\n request: requestContext,\n ...(clientIp && { client_ip: clientIp }),\n },\n tags: {\n \"http.method\": request.method,\n \"http.url\": request.url,\n },\n });\n\n return eventId;\n },\n\n captureMessage(message: string, level: \"debug\" | \"info\" | \"warning\" | \"error\" = \"info\"): string {\n if (!client) return \"\";\n\n eventId = coreCaptureMessage(message, level);\n return eventId;\n },\n\n getEventId(): string | undefined {\n return eventId;\n },\n\n get client(): BugwatchClient | null {\n return client;\n },\n };\n}\n\n/**\n * Bugwatch plugin implementation.\n */\nconst bugwatchPluginImpl: FastifyPluginAsync<BugwatchFastifyOptions> = async (\n fastify,\n options\n) => {\n const opts: BugwatchFastifyOptions = {\n addBreadcrumbs: true,\n captureErrors: true,\n includeBody: false,\n flushOnError: false,\n ...options,\n };\n\n // Store request start times and scoped contexts\n const requestStartTimes = new WeakMap<FastifyRequest, number>();\n const requestContexts = new WeakMap<FastifyRequest, ScopedContext>();\n\n // Decorate requests with bugwatch\n fastify.decorateRequest(\"bugwatch\", null);\n\n // onRequest hook - set up context\n fastify.addHook(\"onRequest\", async (request) => {\n const client = getClient();\n if (!client) return;\n\n // Store start time\n requestStartTimes.set(request, Date.now());\n\n // Create a request-scoped context for isolation\n const scopedContext: ScopedContext = createScopedContext();\n requestContexts.set(request, scopedContext);\n\n // Create and attach decorator\n request.bugwatch = createBugwatchDecorator(request, opts);\n\n // Extract and set user context in the scoped context (not globally!)\n if (opts.extractUser) {\n const user = opts.extractUser(request);\n if (user) {\n scopedContext.user = user;\n }\n }\n\n // Add request breadcrumb to scoped context\n if (opts.addBreadcrumbs) {\n addBreadcrumb({\n category: \"http\",\n message: `${request.method} ${request.url}`,\n level: \"info\",\n data: {\n method: request.method,\n url: request.url,\n },\n });\n }\n });\n\n // onResponse hook - add completion breadcrumb\n fastify.addHook(\"onResponse\", async (request, reply) => {\n if (!opts.addBreadcrumbs) return;\n\n const client = getClient();\n if (!client) return;\n\n const startTime = requestStartTimes.get(request);\n const duration = startTime ? Date.now() - startTime : undefined;\n\n addBreadcrumb({\n category: \"http\",\n message: `${request.method} ${request.url} -> ${reply.statusCode}`,\n level: reply.statusCode >= 500 ? \"error\" : reply.statusCode >= 400 ? \"warning\" : \"info\",\n data: {\n method: request.method,\n url: request.url,\n status_code: reply.statusCode,\n ...(duration !== undefined && { duration_ms: duration }),\n },\n });\n });\n\n // onError hook - capture errors\n fastify.addHook(\"onError\", async (request, reply, error) => {\n if (!opts.captureErrors) return;\n\n const client = getClient();\n if (!client) return;\n\n // Capture the error\n request.bugwatch?.captureError(error);\n\n // Flush if requested\n if (opts.flushOnError) {\n await flush();\n }\n });\n};\n\n/**\n * Bugwatch Fastify plugin.\n *\n * This plugin automatically:\n * - Captures request context for errors\n * - Adds HTTP request breadcrumbs\n * - Reports unhandled errors to Bugwatch\n * - Provides `request.bugwatch` decorator for manual error capture\n *\n * @example\n * ```typescript\n * import Fastify from \"fastify\";\n * import { init } from \"@bugwatch/core\";\n * import { bugwatchPlugin } from \"@bugwatch/fastify\";\n *\n * // Initialize Bugwatch\n * init({ apiKey: \"your-api-key\" });\n *\n * const fastify = Fastify();\n *\n * // Register the plugin\n * await fastify.register(bugwatchPlugin, {\n * extractUser: (request) => {\n * // Extract user from your auth system\n * return request.user ? { id: request.user.id } : null;\n * },\n * });\n *\n * // Your routes\n * fastify.get(\"/\", async (request, reply) => {\n * return { hello: \"world\" };\n * });\n *\n * // Manual error capture\n * fastify.get(\"/risky\", async (request, reply) => {\n * try {\n * await riskyOperation();\n * } catch (err) {\n * request.bugwatch.captureError(err);\n * return { error: \"Something went wrong\" };\n * }\n * });\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\nexport const bugwatchPlugin = fp(bugwatchPluginImpl, {\n fastify: \"4.x || 5.x\",\n name: \"@bugwatch/fastify\",\n});\n"],"mappings":"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","names":["import_core","coreCaptureMessage","fp","import_core"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// src/setup.ts
|
|
2
|
+
import { init, getClient as getClient2 } from "@bugwatch/core";
|
|
3
|
+
|
|
4
|
+
// src/plugin.ts
|
|
5
|
+
import fp from "fastify-plugin";
|
|
6
|
+
import {
|
|
7
|
+
getClient,
|
|
8
|
+
captureException,
|
|
9
|
+
captureMessage as coreCaptureMessage,
|
|
10
|
+
addBreadcrumb,
|
|
11
|
+
flush,
|
|
12
|
+
createScopedContext
|
|
13
|
+
} from "@bugwatch/core";
|
|
14
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
15
|
+
"authorization",
|
|
16
|
+
"cookie",
|
|
17
|
+
"set-cookie",
|
|
18
|
+
"x-api-key",
|
|
19
|
+
"x-auth-token",
|
|
20
|
+
"x-csrf-token",
|
|
21
|
+
"x-xsrf-token",
|
|
22
|
+
"proxy-authorization"
|
|
23
|
+
]);
|
|
24
|
+
var SENSITIVE_BODY_FIELDS = /* @__PURE__ */ new Set([
|
|
25
|
+
"password",
|
|
26
|
+
"secret",
|
|
27
|
+
"token",
|
|
28
|
+
"api_key",
|
|
29
|
+
"apiKey",
|
|
30
|
+
"credit_card",
|
|
31
|
+
"creditCard",
|
|
32
|
+
"ssn",
|
|
33
|
+
"social_security"
|
|
34
|
+
]);
|
|
35
|
+
function defaultHeaderFilter(name) {
|
|
36
|
+
return !SENSITIVE_HEADERS.has(name.toLowerCase());
|
|
37
|
+
}
|
|
38
|
+
function defaultBodyFilter(key) {
|
|
39
|
+
return !SENSITIVE_BODY_FIELDS.has(key.toLowerCase());
|
|
40
|
+
}
|
|
41
|
+
function filterObject(obj, filter) {
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
44
|
+
if (filter(key, value)) {
|
|
45
|
+
result[key] = value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function extractRequestContext(request, options) {
|
|
51
|
+
const headerFilter = options.filterHeaders || defaultHeaderFilter;
|
|
52
|
+
const bodyFilter = options.filterBody || defaultBodyFilter;
|
|
53
|
+
const headers = {};
|
|
54
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
55
|
+
if (typeof value === "string" && headerFilter(name, value)) {
|
|
56
|
+
headers[name] = value;
|
|
57
|
+
} else if (Array.isArray(value)) {
|
|
58
|
+
const filtered = value.filter((v) => headerFilter(name, v));
|
|
59
|
+
if (filtered.length > 0) {
|
|
60
|
+
headers[name] = filtered.join(", ");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const context = {
|
|
65
|
+
url: request.url,
|
|
66
|
+
method: request.method,
|
|
67
|
+
headers,
|
|
68
|
+
query_string: request.url.includes("?") ? request.url.split("?")[1] : void 0
|
|
69
|
+
};
|
|
70
|
+
if (options.includeBody && request.body && typeof request.body === "object") {
|
|
71
|
+
context.data = filterObject(request.body, bodyFilter);
|
|
72
|
+
}
|
|
73
|
+
return context;
|
|
74
|
+
}
|
|
75
|
+
function extractClientIp(request) {
|
|
76
|
+
const forwarded = request.headers["x-forwarded-for"];
|
|
77
|
+
if (typeof forwarded === "string") {
|
|
78
|
+
const firstIp = forwarded.split(",")[0]?.trim();
|
|
79
|
+
if (firstIp) return firstIp;
|
|
80
|
+
}
|
|
81
|
+
const realIp = request.headers["x-real-ip"];
|
|
82
|
+
if (typeof realIp === "string") return realIp;
|
|
83
|
+
const cfIp = request.headers["cf-connecting-ip"];
|
|
84
|
+
if (typeof cfIp === "string") return cfIp;
|
|
85
|
+
return request.ip;
|
|
86
|
+
}
|
|
87
|
+
function createBugwatchDecorator(request, options) {
|
|
88
|
+
let eventId;
|
|
89
|
+
const client = getClient();
|
|
90
|
+
return {
|
|
91
|
+
captureError(error) {
|
|
92
|
+
if (!client) return "";
|
|
93
|
+
const requestContext = extractRequestContext(request, options);
|
|
94
|
+
const clientIp = extractClientIp(request);
|
|
95
|
+
eventId = captureException(error, {
|
|
96
|
+
request: requestContext,
|
|
97
|
+
extra: {
|
|
98
|
+
request: requestContext,
|
|
99
|
+
...clientIp && { client_ip: clientIp }
|
|
100
|
+
},
|
|
101
|
+
tags: {
|
|
102
|
+
"http.method": request.method,
|
|
103
|
+
"http.url": request.url
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return eventId;
|
|
107
|
+
},
|
|
108
|
+
captureMessage(message, level = "info") {
|
|
109
|
+
if (!client) return "";
|
|
110
|
+
eventId = coreCaptureMessage(message, level);
|
|
111
|
+
return eventId;
|
|
112
|
+
},
|
|
113
|
+
getEventId() {
|
|
114
|
+
return eventId;
|
|
115
|
+
},
|
|
116
|
+
get client() {
|
|
117
|
+
return client;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
var bugwatchPluginImpl = async (fastify, options) => {
|
|
122
|
+
const opts = {
|
|
123
|
+
addBreadcrumbs: true,
|
|
124
|
+
captureErrors: true,
|
|
125
|
+
includeBody: false,
|
|
126
|
+
flushOnError: false,
|
|
127
|
+
...options
|
|
128
|
+
};
|
|
129
|
+
const requestStartTimes = /* @__PURE__ */ new WeakMap();
|
|
130
|
+
const requestContexts = /* @__PURE__ */ new WeakMap();
|
|
131
|
+
fastify.decorateRequest("bugwatch", null);
|
|
132
|
+
fastify.addHook("onRequest", async (request) => {
|
|
133
|
+
const client = getClient();
|
|
134
|
+
if (!client) return;
|
|
135
|
+
requestStartTimes.set(request, Date.now());
|
|
136
|
+
const scopedContext = createScopedContext();
|
|
137
|
+
requestContexts.set(request, scopedContext);
|
|
138
|
+
request.bugwatch = createBugwatchDecorator(request, opts);
|
|
139
|
+
if (opts.extractUser) {
|
|
140
|
+
const user = opts.extractUser(request);
|
|
141
|
+
if (user) {
|
|
142
|
+
scopedContext.user = user;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (opts.addBreadcrumbs) {
|
|
146
|
+
addBreadcrumb({
|
|
147
|
+
category: "http",
|
|
148
|
+
message: `${request.method} ${request.url}`,
|
|
149
|
+
level: "info",
|
|
150
|
+
data: {
|
|
151
|
+
method: request.method,
|
|
152
|
+
url: request.url
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
fastify.addHook("onResponse", async (request, reply) => {
|
|
158
|
+
if (!opts.addBreadcrumbs) return;
|
|
159
|
+
const client = getClient();
|
|
160
|
+
if (!client) return;
|
|
161
|
+
const startTime = requestStartTimes.get(request);
|
|
162
|
+
const duration = startTime ? Date.now() - startTime : void 0;
|
|
163
|
+
addBreadcrumb({
|
|
164
|
+
category: "http",
|
|
165
|
+
message: `${request.method} ${request.url} -> ${reply.statusCode}`,
|
|
166
|
+
level: reply.statusCode >= 500 ? "error" : reply.statusCode >= 400 ? "warning" : "info",
|
|
167
|
+
data: {
|
|
168
|
+
method: request.method,
|
|
169
|
+
url: request.url,
|
|
170
|
+
status_code: reply.statusCode,
|
|
171
|
+
...duration !== void 0 && { duration_ms: duration }
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
fastify.addHook("onError", async (request, reply, error) => {
|
|
176
|
+
if (!opts.captureErrors) return;
|
|
177
|
+
const client = getClient();
|
|
178
|
+
if (!client) return;
|
|
179
|
+
request.bugwatch?.captureError(error);
|
|
180
|
+
if (opts.flushOnError) {
|
|
181
|
+
await flush();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
var bugwatchPlugin = fp(bugwatchPluginImpl, {
|
|
186
|
+
fastify: "4.x || 5.x",
|
|
187
|
+
name: "@bugwatch/fastify"
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// src/setup.ts
|
|
191
|
+
async function setup(fastify, options) {
|
|
192
|
+
if (!getClient2()) {
|
|
193
|
+
const coreOptions = {};
|
|
194
|
+
if (options?.apiKey) coreOptions.apiKey = options.apiKey;
|
|
195
|
+
if (options?.endpoint) coreOptions.endpoint = options.endpoint;
|
|
196
|
+
if (options?.environment) coreOptions.environment = options.environment;
|
|
197
|
+
if (options?.release) coreOptions.release = options.release;
|
|
198
|
+
if (options?.debug !== void 0) coreOptions.debug = options.debug;
|
|
199
|
+
if (options?.sampleRate !== void 0) coreOptions.sampleRate = options.sampleRate;
|
|
200
|
+
if (options?.maxBreadcrumbs !== void 0) coreOptions.maxBreadcrumbs = options.maxBreadcrumbs;
|
|
201
|
+
if (options?.tags) coreOptions.tags = options.tags;
|
|
202
|
+
if (options?.user) coreOptions.user = options.user;
|
|
203
|
+
if (options?.beforeSend) coreOptions.beforeSend = options.beforeSend;
|
|
204
|
+
if (options?.ignoreErrors) coreOptions.ignoreErrors = options.ignoreErrors;
|
|
205
|
+
init(coreOptions);
|
|
206
|
+
}
|
|
207
|
+
const pluginOptions = {};
|
|
208
|
+
if (options?.extractUser) pluginOptions.extractUser = options.extractUser;
|
|
209
|
+
if (options?.filterHeaders) pluginOptions.filterHeaders = options.filterHeaders;
|
|
210
|
+
if (options?.filterBody) pluginOptions.filterBody = options.filterBody;
|
|
211
|
+
if (options?.includeBody !== void 0) pluginOptions.includeBody = options.includeBody;
|
|
212
|
+
if (options?.addBreadcrumbs !== void 0) pluginOptions.addBreadcrumbs = options.addBreadcrumbs;
|
|
213
|
+
if (options?.captureErrors !== void 0) pluginOptions.captureErrors = options.captureErrors;
|
|
214
|
+
if (options?.flushOnError !== void 0) pluginOptions.flushOnError = options.flushOnError;
|
|
215
|
+
await fastify.register(bugwatchPlugin, pluginOptions);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/index.ts
|
|
219
|
+
import {
|
|
220
|
+
init as init2,
|
|
221
|
+
getClient as getClient3,
|
|
222
|
+
captureException as captureException2,
|
|
223
|
+
captureMessage,
|
|
224
|
+
addBreadcrumb as addBreadcrumb2,
|
|
225
|
+
setUser as setUser2,
|
|
226
|
+
setTag,
|
|
227
|
+
setExtra,
|
|
228
|
+
flush as flush2,
|
|
229
|
+
close
|
|
230
|
+
} from "@bugwatch/core";
|
|
231
|
+
export {
|
|
232
|
+
addBreadcrumb2 as addBreadcrumb,
|
|
233
|
+
bugwatchPlugin,
|
|
234
|
+
captureException2 as captureException,
|
|
235
|
+
captureMessage,
|
|
236
|
+
close,
|
|
237
|
+
flush2 as flush,
|
|
238
|
+
getClient3 as getClient,
|
|
239
|
+
init2 as init,
|
|
240
|
+
setExtra,
|
|
241
|
+
setTag,
|
|
242
|
+
setUser2 as setUser,
|
|
243
|
+
setup
|
|
244
|
+
};
|
|
245
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/setup.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["/**\n * One-liner setup for Bugwatch Fastify integration.\n *\n * This module provides a simplified setup function that handles all\n * initialization and plugin registration in a single call.\n */\n\nimport type { FastifyInstance } from \"fastify\";\nimport { init, getClient, type BugwatchOptions } from \"@bugwatch/core\";\nimport { bugwatchPlugin } from \"./plugin\";\nimport type { BugwatchFastifyOptions } from \"./types\";\n\n/**\n * Combined options for Fastify setup.\n * Includes both core SDK options and Fastify-specific plugin options.\n */\nexport interface BugwatchFastifySetupOptions\n extends BugwatchFastifyOptions,\n Partial<BugwatchOptions> {}\n\n/**\n * Set up Bugwatch for a Fastify application with a single call.\n *\n * This function:\n * 1. Initializes the Bugwatch SDK (using env vars if no apiKey provided)\n * 2. Registers the Bugwatch plugin with the Fastify instance\n *\n * @param fastify - The Fastify instance\n * @param options - Configuration options (optional if BUGWATCH_API_KEY env var is set)\n * @returns Promise that resolves when the plugin is registered\n *\n * @example\n * ```typescript\n * import Fastify from \"fastify\";\n * import { setup } from \"@bugwatch/fastify\";\n *\n * const fastify = Fastify();\n *\n * // Minimal setup (uses BUGWATCH_API_KEY env var)\n * await setup(fastify);\n *\n * // Or with explicit options\n * await setup(fastify, {\n * apiKey: \"bw_live_xxxxx\",\n * environment: \"production\",\n * extractUser: (request) => ({ id: request.user?.id }),\n * });\n *\n * fastify.get(\"/\", async () => ({ hello: \"world\" }));\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\nexport async function setup(\n fastify: FastifyInstance,\n options?: BugwatchFastifySetupOptions\n): Promise<void> {\n // Initialize core SDK if not already initialized\n if (!getClient()) {\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 }\n\n // Extract plugin-specific options\n const pluginOptions: BugwatchFastifyOptions = {};\n if (options?.extractUser) pluginOptions.extractUser = options.extractUser;\n if (options?.filterHeaders) pluginOptions.filterHeaders = options.filterHeaders;\n if (options?.filterBody) pluginOptions.filterBody = options.filterBody;\n if (options?.includeBody !== undefined) pluginOptions.includeBody = options.includeBody;\n if (options?.addBreadcrumbs !== undefined) pluginOptions.addBreadcrumbs = options.addBreadcrumbs;\n if (options?.captureErrors !== undefined) pluginOptions.captureErrors = options.captureErrors;\n if (options?.flushOnError !== undefined) pluginOptions.flushOnError = options.flushOnError;\n\n // Register the plugin\n await fastify.register(bugwatchPlugin, pluginOptions);\n}\n","import type { FastifyPluginAsync, FastifyRequest, FastifyReply } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport {\n getClient,\n captureException,\n captureMessage as coreCaptureMessage,\n addBreadcrumb,\n setUser,\n flush,\n runWithContextAsync,\n createScopedContext,\n type RequestContext,\n type BugwatchClient,\n type ScopedContext,\n} from \"@bugwatch/core\";\nimport type { BugwatchFastifyOptions, BugwatchDecorator } 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 a Fastify request.\n */\nfunction extractRequestContext(\n request: FastifyRequest,\n options: BugwatchFastifyOptions\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(request.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: request.url,\n method: request.method,\n headers,\n query_string: request.url.includes(\"?\") ? request.url.split(\"?\")[1] : undefined,\n };\n\n // Include body if requested and available\n if (options.includeBody && request.body && typeof request.body === \"object\") {\n context.data = filterObject(request.body as Record<string, unknown>, bodyFilter);\n }\n\n return context;\n}\n\n/**\n * Extract client IP from request.\n */\nfunction extractClientIp(request: FastifyRequest): string | undefined {\n // Check common proxy headers\n const forwarded = request.headers[\"x-forwarded-for\"];\n if (typeof forwarded === \"string\") {\n const firstIp = forwarded.split(\",\")[0]?.trim();\n if (firstIp) return firstIp;\n }\n\n const realIp = request.headers[\"x-real-ip\"];\n if (typeof realIp === \"string\") return realIp;\n\n const cfIp = request.headers[\"cf-connecting-ip\"];\n if (typeof cfIp === \"string\") return cfIp;\n\n // Fall back to connection address\n return request.ip;\n}\n\n/**\n * Create Bugwatch decorator for a request.\n */\nfunction createBugwatchDecorator(\n request: FastifyRequest,\n options: BugwatchFastifyOptions\n): BugwatchDecorator {\n let eventId: string | undefined;\n const client = getClient();\n\n return {\n captureError(error: Error): string {\n if (!client) return \"\";\n\n const requestContext = extractRequestContext(request, options);\n const clientIp = extractClientIp(request);\n\n eventId = captureException(error, {\n request: requestContext,\n extra: {\n request: requestContext,\n ...(clientIp && { client_ip: clientIp }),\n },\n tags: {\n \"http.method\": request.method,\n \"http.url\": request.url,\n },\n });\n\n return eventId;\n },\n\n captureMessage(message: string, level: \"debug\" | \"info\" | \"warning\" | \"error\" = \"info\"): string {\n if (!client) return \"\";\n\n eventId = coreCaptureMessage(message, level);\n return eventId;\n },\n\n getEventId(): string | undefined {\n return eventId;\n },\n\n get client(): BugwatchClient | null {\n return client;\n },\n };\n}\n\n/**\n * Bugwatch plugin implementation.\n */\nconst bugwatchPluginImpl: FastifyPluginAsync<BugwatchFastifyOptions> = async (\n fastify,\n options\n) => {\n const opts: BugwatchFastifyOptions = {\n addBreadcrumbs: true,\n captureErrors: true,\n includeBody: false,\n flushOnError: false,\n ...options,\n };\n\n // Store request start times and scoped contexts\n const requestStartTimes = new WeakMap<FastifyRequest, number>();\n const requestContexts = new WeakMap<FastifyRequest, ScopedContext>();\n\n // Decorate requests with bugwatch\n fastify.decorateRequest(\"bugwatch\", null);\n\n // onRequest hook - set up context\n fastify.addHook(\"onRequest\", async (request) => {\n const client = getClient();\n if (!client) return;\n\n // Store start time\n requestStartTimes.set(request, Date.now());\n\n // Create a request-scoped context for isolation\n const scopedContext: ScopedContext = createScopedContext();\n requestContexts.set(request, scopedContext);\n\n // Create and attach decorator\n request.bugwatch = createBugwatchDecorator(request, opts);\n\n // Extract and set user context in the scoped context (not globally!)\n if (opts.extractUser) {\n const user = opts.extractUser(request);\n if (user) {\n scopedContext.user = user;\n }\n }\n\n // Add request breadcrumb to scoped context\n if (opts.addBreadcrumbs) {\n addBreadcrumb({\n category: \"http\",\n message: `${request.method} ${request.url}`,\n level: \"info\",\n data: {\n method: request.method,\n url: request.url,\n },\n });\n }\n });\n\n // onResponse hook - add completion breadcrumb\n fastify.addHook(\"onResponse\", async (request, reply) => {\n if (!opts.addBreadcrumbs) return;\n\n const client = getClient();\n if (!client) return;\n\n const startTime = requestStartTimes.get(request);\n const duration = startTime ? Date.now() - startTime : undefined;\n\n addBreadcrumb({\n category: \"http\",\n message: `${request.method} ${request.url} -> ${reply.statusCode}`,\n level: reply.statusCode >= 500 ? \"error\" : reply.statusCode >= 400 ? \"warning\" : \"info\",\n data: {\n method: request.method,\n url: request.url,\n status_code: reply.statusCode,\n ...(duration !== undefined && { duration_ms: duration }),\n },\n });\n });\n\n // onError hook - capture errors\n fastify.addHook(\"onError\", async (request, reply, error) => {\n if (!opts.captureErrors) return;\n\n const client = getClient();\n if (!client) return;\n\n // Capture the error\n request.bugwatch?.captureError(error);\n\n // Flush if requested\n if (opts.flushOnError) {\n await flush();\n }\n });\n};\n\n/**\n * Bugwatch Fastify plugin.\n *\n * This plugin automatically:\n * - Captures request context for errors\n * - Adds HTTP request breadcrumbs\n * - Reports unhandled errors to Bugwatch\n * - Provides `request.bugwatch` decorator for manual error capture\n *\n * @example\n * ```typescript\n * import Fastify from \"fastify\";\n * import { init } from \"@bugwatch/core\";\n * import { bugwatchPlugin } from \"@bugwatch/fastify\";\n *\n * // Initialize Bugwatch\n * init({ apiKey: \"your-api-key\" });\n *\n * const fastify = Fastify();\n *\n * // Register the plugin\n * await fastify.register(bugwatchPlugin, {\n * extractUser: (request) => {\n * // Extract user from your auth system\n * return request.user ? { id: request.user.id } : null;\n * },\n * });\n *\n * // Your routes\n * fastify.get(\"/\", async (request, reply) => {\n * return { hello: \"world\" };\n * });\n *\n * // Manual error capture\n * fastify.get(\"/risky\", async (request, reply) => {\n * try {\n * await riskyOperation();\n * } catch (err) {\n * request.bugwatch.captureError(err);\n * return { error: \"Something went wrong\" };\n * }\n * });\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\nexport const bugwatchPlugin = fp(bugwatchPluginImpl, {\n fastify: \"4.x || 5.x\",\n name: \"@bugwatch/fastify\",\n});\n","/**\n * @bugwatch/fastify - Fastify integration for Bugwatch\n *\n * This package provides a plugin for automatically capturing errors\n * and request context in Fastify applications.\n *\n * @example One-liner setup (recommended)\n * ```typescript\n * import Fastify from \"fastify\";\n * import { setup } from \"@bugwatch/fastify\";\n *\n * const fastify = Fastify();\n *\n * // Single call handles everything (uses BUGWATCH_API_KEY env var)\n * await setup(fastify);\n *\n * // Or with explicit options\n * await setup(fastify, {\n * apiKey: \"bw_live_xxxxx\",\n * extractUser: (request) => ({ id: request.user?.id }),\n * });\n *\n * fastify.get(\"/\", async () => ({ hello: \"world\" }));\n *\n * await fastify.listen({ port: 3000 });\n * ```\n *\n * @example Manual setup (for more control)\n * ```typescript\n * import Fastify from \"fastify\";\n * import { init } from \"@bugwatch/core\";\n * import { bugwatchPlugin } from \"@bugwatch/fastify\";\n *\n * init({ apiKey: \"your-api-key\" });\n *\n * const fastify = Fastify();\n * await fastify.register(bugwatchPlugin);\n *\n * // request.bugwatch is available for manual capture\n * fastify.get(\"/risky\", async (request) => {\n * try {\n * await riskyOperation();\n * } catch (err) {\n * request.bugwatch.captureError(err);\n * return { error: \"Something went wrong\" };\n * }\n * });\n *\n * await fastify.listen({ port: 3000 });\n * ```\n */\n\n// Export one-liner setup\nexport { setup } from \"./setup\";\nexport type { BugwatchFastifySetupOptions } from \"./setup\";\n\n// Export plugin\nexport { bugwatchPlugin } from \"./plugin\";\n\n// Export types\nexport type {\n BugwatchFastifyOptions,\n BugwatchDecorator,\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":";AAQA,SAAS,MAAM,aAAAA,kBAAuC;;;ACPtD,OAAO,QAAQ;AACf;AAAA,EACE;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AAAA,EAEA;AAAA,EAEA;AAAA,OAIK;AAMP,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKD,IAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKD,SAAS,oBAAoB,MAAuB;AAClD,SAAO,CAAC,kBAAkB,IAAI,KAAK,YAAY,CAAC;AAClD;AAKA,SAAS,kBAAkB,KAAsB;AAC/C,SAAO,CAAC,sBAAsB,IAAI,IAAI,YAAY,CAAC;AACrD;AAKA,SAAS,aACP,KACA,QACY;AACZ,QAAM,SAAqB,CAAC;AAC5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,OAAO,KAAK,KAAK,GAAG;AACtB,MAAC,OAAmC,GAAG,IAAI;AAAA,IAC7C;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,sBACP,SACA,SACgB;AAChB,QAAM,eAAe,QAAQ,iBAAiB;AAC9C,QAAM,aAAa,QAAQ,cAAc;AAGzC,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC3D,QAAI,OAAO,UAAU,YAAY,aAAa,MAAM,KAAK,GAAG;AAC1D,cAAQ,IAAI,IAAI;AAAA,IAClB,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,YAAM,WAAW,MAAM,OAAO,CAAC,MAAM,aAAa,MAAM,CAAC,CAAC;AAC1D,UAAI,SAAS,SAAS,GAAG;AACvB,gBAAQ,IAAI,IAAI,SAAS,KAAK,IAAI;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAA0B;AAAA,IAC9B,KAAK,QAAQ;AAAA,IACb,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,cAAc,QAAQ,IAAI,SAAS,GAAG,IAAI,QAAQ,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI;AAAA,EACxE;AAGA,MAAI,QAAQ,eAAe,QAAQ,QAAQ,OAAO,QAAQ,SAAS,UAAU;AAC3E,YAAQ,OAAO,aAAa,QAAQ,MAAiC,UAAU;AAAA,EACjF;AAEA,SAAO;AACT;AAKA,SAAS,gBAAgB,SAA6C;AAEpE,QAAM,YAAY,QAAQ,QAAQ,iBAAiB;AACnD,MAAI,OAAO,cAAc,UAAU;AACjC,UAAM,UAAU,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK;AAC9C,QAAI,QAAS,QAAO;AAAA,EACtB;AAEA,QAAM,SAAS,QAAQ,QAAQ,WAAW;AAC1C,MAAI,OAAO,WAAW,SAAU,QAAO;AAEvC,QAAM,OAAO,QAAQ,QAAQ,kBAAkB;AAC/C,MAAI,OAAO,SAAS,SAAU,QAAO;AAGrC,SAAO,QAAQ;AACjB;AAKA,SAAS,wBACP,SACA,SACmB;AACnB,MAAI;AACJ,QAAM,SAAS,UAAU;AAEzB,SAAO;AAAA,IACL,aAAa,OAAsB;AACjC,UAAI,CAAC,OAAQ,QAAO;AAEpB,YAAM,iBAAiB,sBAAsB,SAAS,OAAO;AAC7D,YAAM,WAAW,gBAAgB,OAAO;AAExC,gBAAU,iBAAiB,OAAO;AAAA,QAChC,SAAS;AAAA,QACT,OAAO;AAAA,UACL,SAAS;AAAA,UACT,GAAI,YAAY,EAAE,WAAW,SAAS;AAAA,QACxC;AAAA,QACA,MAAM;AAAA,UACJ,eAAe,QAAQ;AAAA,UACvB,YAAY,QAAQ;AAAA,QACtB;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAAA,IAEA,eAAe,SAAiB,QAAgD,QAAgB;AAC9F,UAAI,CAAC,OAAQ,QAAO;AAEpB,gBAAU,mBAAmB,SAAS,KAAK;AAC3C,aAAO;AAAA,IACT;AAAA,IAEA,aAAiC;AAC/B,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,SAAgC;AAClC,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAKA,IAAM,qBAAiE,OACrE,SACA,YACG;AACH,QAAM,OAA+B;AAAA,IACnC,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,cAAc;AAAA,IACd,GAAG;AAAA,EACL;AAGA,QAAM,oBAAoB,oBAAI,QAAgC;AAC9D,QAAM,kBAAkB,oBAAI,QAAuC;AAGnE,UAAQ,gBAAgB,YAAY,IAAI;AAGxC,UAAQ,QAAQ,aAAa,OAAO,YAAY;AAC9C,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AAGb,sBAAkB,IAAI,SAAS,KAAK,IAAI,CAAC;AAGzC,UAAM,gBAA+B,oBAAoB;AACzD,oBAAgB,IAAI,SAAS,aAAa;AAG1C,YAAQ,WAAW,wBAAwB,SAAS,IAAI;AAGxD,QAAI,KAAK,aAAa;AACpB,YAAM,OAAO,KAAK,YAAY,OAAO;AACrC,UAAI,MAAM;AACR,sBAAc,OAAO;AAAA,MACvB;AAAA,IACF;AAGA,QAAI,KAAK,gBAAgB;AACvB,oBAAc;AAAA,QACZ,UAAU;AAAA,QACV,SAAS,GAAG,QAAQ,MAAM,IAAI,QAAQ,GAAG;AAAA,QACzC,OAAO;AAAA,QACP,MAAM;AAAA,UACJ,QAAQ,QAAQ;AAAA,UAChB,KAAK,QAAQ;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAGD,UAAQ,QAAQ,cAAc,OAAO,SAAS,UAAU;AACtD,QAAI,CAAC,KAAK,eAAgB;AAE1B,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AAEb,UAAM,YAAY,kBAAkB,IAAI,OAAO;AAC/C,UAAM,WAAW,YAAY,KAAK,IAAI,IAAI,YAAY;AAEtD,kBAAc;AAAA,MACZ,UAAU;AAAA,MACV,SAAS,GAAG,QAAQ,MAAM,IAAI,QAAQ,GAAG,OAAO,MAAM,UAAU;AAAA,MAChE,OAAO,MAAM,cAAc,MAAM,UAAU,MAAM,cAAc,MAAM,YAAY;AAAA,MACjF,MAAM;AAAA,QACJ,QAAQ,QAAQ;AAAA,QAChB,KAAK,QAAQ;AAAA,QACb,aAAa,MAAM;AAAA,QACnB,GAAI,aAAa,UAAa,EAAE,aAAa,SAAS;AAAA,MACxD;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,UAAQ,QAAQ,WAAW,OAAO,SAAS,OAAO,UAAU;AAC1D,QAAI,CAAC,KAAK,cAAe;AAEzB,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AAGb,YAAQ,UAAU,aAAa,KAAK;AAGpC,QAAI,KAAK,cAAc;AACrB,YAAM,MAAM;AAAA,IACd;AAAA,EACF,CAAC;AACH;AAgDO,IAAM,iBAAiB,GAAG,oBAAoB;AAAA,EACnD,SAAS;AAAA,EACT,MAAM;AACR,CAAC;;;ADvRD,eAAsB,MACpB,SACA,SACe;AAEf,MAAI,CAACC,WAAU,GAAG;AAEhB,UAAM,cAAwC,CAAC;AAC/C,QAAI,SAAS,OAAQ,aAAY,SAAS,QAAQ;AAClD,QAAI,SAAS,SAAU,aAAY,WAAW,QAAQ;AACtD,QAAI,SAAS,YAAa,aAAY,cAAc,QAAQ;AAC5D,QAAI,SAAS,QAAS,aAAY,UAAU,QAAQ;AACpD,QAAI,SAAS,UAAU,OAAW,aAAY,QAAQ,QAAQ;AAC9D,QAAI,SAAS,eAAe,OAAW,aAAY,aAAa,QAAQ;AACxE,QAAI,SAAS,mBAAmB,OAAW,aAAY,iBAAiB,QAAQ;AAChF,QAAI,SAAS,KAAM,aAAY,OAAO,QAAQ;AAC9C,QAAI,SAAS,KAAM,aAAY,OAAO,QAAQ;AAC9C,QAAI,SAAS,WAAY,aAAY,aAAa,QAAQ;AAC1D,QAAI,SAAS,aAAc,aAAY,eAAe,QAAQ;AAE9D,SAAK,WAAW;AAAA,EAClB;AAGA,QAAM,gBAAwC,CAAC;AAC/C,MAAI,SAAS,YAAa,eAAc,cAAc,QAAQ;AAC9D,MAAI,SAAS,cAAe,eAAc,gBAAgB,QAAQ;AAClE,MAAI,SAAS,WAAY,eAAc,aAAa,QAAQ;AAC5D,MAAI,SAAS,gBAAgB,OAAW,eAAc,cAAc,QAAQ;AAC5E,MAAI,SAAS,mBAAmB,OAAW,eAAc,iBAAiB,QAAQ;AAClF,MAAI,SAAS,kBAAkB,OAAW,eAAc,gBAAgB,QAAQ;AAChF,MAAI,SAAS,iBAAiB,OAAW,eAAc,eAAe,QAAQ;AAG9E,QAAM,QAAQ,SAAS,gBAAgB,aAAa;AACtD;;;AEtBA;AAAA,EACE,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,oBAAAC;AAAA,EACA;AAAA,EACA,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAAC;AAAA,EACA;AAAA,OACK;","names":["getClient","getClient","init","getClient","captureException","addBreadcrumb","setUser","flush"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bugwatch/fastify",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bugwatch SDK integration for Fastify",
|
|
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
|
+
"fastify-plugin": "^4.5.1"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"fastify": "^4.0.0 || ^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"fastify": "^4.28.0",
|
|
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/fastify"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"keywords": [
|
|
48
|
+
"bugwatch",
|
|
49
|
+
"error-tracking",
|
|
50
|
+
"fastify",
|
|
51
|
+
"plugin",
|
|
52
|
+
"monitoring"
|
|
53
|
+
]
|
|
54
|
+
}
|