sitepong 0.0.5 → 0.0.7
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/breadcrumbs-DwXK5gNn.d.mts +119 -0
- package/dist/breadcrumbs-DwXK5gNn.d.ts +119 -0
- package/dist/express/index.d.mts +47 -0
- package/dist/express/index.d.ts +47 -0
- package/dist/express/index.js +128 -0
- package/dist/express/index.js.map +1 -0
- package/dist/express/index.mjs +124 -0
- package/dist/express/index.mjs.map +1 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs/index.d.mts +109 -0
- package/dist/nextjs/index.d.ts +109 -0
- package/dist/nextjs/index.js +440 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/nextjs/index.mjs +431 -0
- package/dist/nextjs/index.mjs.map +1 -0
- package/dist/react/index.d.mts +1 -110
- package/dist/react/index.d.ts +1 -110
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs.map +1 -1
- package/dist/server/index.d.mts +46 -0
- package/dist/server/index.d.ts +46 -0
- package/dist/server/index.js +360 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +352 -0
- package/dist/server/index.mjs.map +1 -0
- package/package.json +35 -6
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
import { S as SitePongConfig } from '../breadcrumbs-DwXK5gNn.mjs';
|
|
3
|
+
export { a as addBreadcrumb } from '../breadcrumbs-DwXK5gNn.mjs';
|
|
4
|
+
export { captureServerException, initServer } from '../server/index.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Next.js integration for SitePong
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // next.config.js
|
|
12
|
+
* const { withSitePong } = require('@sitepong/react/nextjs');
|
|
13
|
+
*
|
|
14
|
+
* module.exports = withSitePong({
|
|
15
|
+
* // your Next.js config
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // app/layout.tsx
|
|
22
|
+
* import { SitePongProvider } from '@sitepong/react';
|
|
23
|
+
*
|
|
24
|
+
* export default function RootLayout({ children }) {
|
|
25
|
+
* return (
|
|
26
|
+
* <html>
|
|
27
|
+
* <body>
|
|
28
|
+
* <SitePongProvider dsn={process.env.NEXT_PUBLIC_SITEPONG_DSN}>
|
|
29
|
+
* {children}
|
|
30
|
+
* </SitePongProvider>
|
|
31
|
+
* </body>
|
|
32
|
+
* </html>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Initialize SitePong for Next.js
|
|
40
|
+
* Call this in your instrumentation.ts file
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // instrumentation.ts
|
|
45
|
+
* import { initNextJs } from '@sitepong/react/nextjs';
|
|
46
|
+
*
|
|
47
|
+
* export function register() {
|
|
48
|
+
* initNextJs({
|
|
49
|
+
* dsn: process.env.SITEPONG_DSN,
|
|
50
|
+
* });
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function initNextJs(config: SitePongConfig): void;
|
|
55
|
+
/**
|
|
56
|
+
* Wrap a Next.js API route handler to capture errors
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* // app/api/users/route.ts
|
|
61
|
+
* import { wrapApiHandler } from '@sitepong/react/nextjs';
|
|
62
|
+
*
|
|
63
|
+
* export const GET = wrapApiHandler(async (request) => {
|
|
64
|
+
* const users = await getUsers();
|
|
65
|
+
* return Response.json(users);
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function wrapApiHandler<T extends (request: NextRequest, context?: unknown) => Promise<Response>>(handler: T): T;
|
|
70
|
+
/**
|
|
71
|
+
* Wrap a Next.js Server Action to capture errors
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // app/actions.ts
|
|
76
|
+
* 'use server';
|
|
77
|
+
* import { wrapServerAction } from '@sitepong/react/nextjs';
|
|
78
|
+
*
|
|
79
|
+
* export const createUser = wrapServerAction(async (formData: FormData) => {
|
|
80
|
+
* // Your server action logic
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
declare function wrapServerAction<T extends (...args: unknown[]) => Promise<unknown>>(action: T, actionName?: string): T;
|
|
85
|
+
/**
|
|
86
|
+
* Global error handler for Next.js App Router
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* // app/error.tsx
|
|
91
|
+
* 'use client';
|
|
92
|
+
* import { NextErrorComponent } from '@sitepong/react/nextjs';
|
|
93
|
+
*
|
|
94
|
+
* export default NextErrorComponent;
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare function NextErrorComponent({ error, reset, }: {
|
|
98
|
+
error: Error & {
|
|
99
|
+
digest?: string;
|
|
100
|
+
};
|
|
101
|
+
reset: () => void;
|
|
102
|
+
}): any;
|
|
103
|
+
/**
|
|
104
|
+
* Next.js config wrapper (optional)
|
|
105
|
+
* Adds source map upload and other enhancements
|
|
106
|
+
*/
|
|
107
|
+
declare function withSitePong(nextConfig?: Record<string, unknown>): Record<string, unknown>;
|
|
108
|
+
|
|
109
|
+
export { NextErrorComponent, initNextJs, withSitePong, wrapApiHandler, wrapServerAction };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
import { S as SitePongConfig } from '../breadcrumbs-DwXK5gNn.js';
|
|
3
|
+
export { a as addBreadcrumb } from '../breadcrumbs-DwXK5gNn.js';
|
|
4
|
+
export { captureServerException, initServer } from '../server/index.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Next.js integration for SitePong
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // next.config.js
|
|
12
|
+
* const { withSitePong } = require('@sitepong/react/nextjs');
|
|
13
|
+
*
|
|
14
|
+
* module.exports = withSitePong({
|
|
15
|
+
* // your Next.js config
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // app/layout.tsx
|
|
22
|
+
* import { SitePongProvider } from '@sitepong/react';
|
|
23
|
+
*
|
|
24
|
+
* export default function RootLayout({ children }) {
|
|
25
|
+
* return (
|
|
26
|
+
* <html>
|
|
27
|
+
* <body>
|
|
28
|
+
* <SitePongProvider dsn={process.env.NEXT_PUBLIC_SITEPONG_DSN}>
|
|
29
|
+
* {children}
|
|
30
|
+
* </SitePongProvider>
|
|
31
|
+
* </body>
|
|
32
|
+
* </html>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Initialize SitePong for Next.js
|
|
40
|
+
* Call this in your instrumentation.ts file
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // instrumentation.ts
|
|
45
|
+
* import { initNextJs } from '@sitepong/react/nextjs';
|
|
46
|
+
*
|
|
47
|
+
* export function register() {
|
|
48
|
+
* initNextJs({
|
|
49
|
+
* dsn: process.env.SITEPONG_DSN,
|
|
50
|
+
* });
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function initNextJs(config: SitePongConfig): void;
|
|
55
|
+
/**
|
|
56
|
+
* Wrap a Next.js API route handler to capture errors
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* // app/api/users/route.ts
|
|
61
|
+
* import { wrapApiHandler } from '@sitepong/react/nextjs';
|
|
62
|
+
*
|
|
63
|
+
* export const GET = wrapApiHandler(async (request) => {
|
|
64
|
+
* const users = await getUsers();
|
|
65
|
+
* return Response.json(users);
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function wrapApiHandler<T extends (request: NextRequest, context?: unknown) => Promise<Response>>(handler: T): T;
|
|
70
|
+
/**
|
|
71
|
+
* Wrap a Next.js Server Action to capture errors
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // app/actions.ts
|
|
76
|
+
* 'use server';
|
|
77
|
+
* import { wrapServerAction } from '@sitepong/react/nextjs';
|
|
78
|
+
*
|
|
79
|
+
* export const createUser = wrapServerAction(async (formData: FormData) => {
|
|
80
|
+
* // Your server action logic
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
declare function wrapServerAction<T extends (...args: unknown[]) => Promise<unknown>>(action: T, actionName?: string): T;
|
|
85
|
+
/**
|
|
86
|
+
* Global error handler for Next.js App Router
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* // app/error.tsx
|
|
91
|
+
* 'use client';
|
|
92
|
+
* import { NextErrorComponent } from '@sitepong/react/nextjs';
|
|
93
|
+
*
|
|
94
|
+
* export default NextErrorComponent;
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare function NextErrorComponent({ error, reset, }: {
|
|
98
|
+
error: Error & {
|
|
99
|
+
digest?: string;
|
|
100
|
+
};
|
|
101
|
+
reset: () => void;
|
|
102
|
+
}): any;
|
|
103
|
+
/**
|
|
104
|
+
* Next.js config wrapper (optional)
|
|
105
|
+
* Adds source map upload and other enhancements
|
|
106
|
+
*/
|
|
107
|
+
declare function withSitePong(nextConfig?: Record<string, unknown>): Record<string, unknown>;
|
|
108
|
+
|
|
109
|
+
export { NextErrorComponent, initNextJs, withSitePong, wrapApiHandler, wrapServerAction };
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// src/core/dsn.ts
|
|
11
|
+
function parseDSN(dsn) {
|
|
12
|
+
const match = dsn.match(
|
|
13
|
+
/^(https?):\/\/([^@]+)@([^/]+)\/(.+)$/
|
|
14
|
+
);
|
|
15
|
+
if (!match) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`Invalid DSN: ${dsn}. Expected format: https://<public_key>@<host>/<project_id>`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const [, protocol, publicKey, host, projectId] = match;
|
|
21
|
+
return {
|
|
22
|
+
protocol,
|
|
23
|
+
publicKey,
|
|
24
|
+
host,
|
|
25
|
+
projectId
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function getApiEndpoint(config) {
|
|
29
|
+
if (config.apiEndpoint) {
|
|
30
|
+
return config.apiEndpoint;
|
|
31
|
+
}
|
|
32
|
+
if (config.dsn) {
|
|
33
|
+
const parsed = parseDSN(config.dsn);
|
|
34
|
+
return `${parsed.protocol}://${parsed.host}`;
|
|
35
|
+
}
|
|
36
|
+
return "https://api.sitepong.com";
|
|
37
|
+
}
|
|
38
|
+
function getAuthHeaders(config) {
|
|
39
|
+
if (config.dsn) {
|
|
40
|
+
const parsed = parseDSN(config.dsn);
|
|
41
|
+
return {
|
|
42
|
+
"X-SitePong-Key": parsed.publicKey,
|
|
43
|
+
"X-SitePong-Project": parsed.projectId
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (config.apiKey) {
|
|
47
|
+
return {
|
|
48
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
function validateConfig(config) {
|
|
54
|
+
if (!config.dsn && !config.apiKey) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
"SitePong: You must provide either a DSN or an API key"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (config.dsn && config.apiKey) {
|
|
60
|
+
console.warn(
|
|
61
|
+
"SitePong: Both DSN and API key provided. DSN will be used."
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (config.sampleRate !== void 0) {
|
|
65
|
+
if (config.sampleRate < 0 || config.sampleRate > 1) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"SitePong: sampleRate must be between 0 and 1"
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/utils/platform.ts
|
|
74
|
+
function detectPlatform() {
|
|
75
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
76
|
+
return "react-native";
|
|
77
|
+
}
|
|
78
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
79
|
+
return "node";
|
|
80
|
+
}
|
|
81
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
82
|
+
return "browser";
|
|
83
|
+
}
|
|
84
|
+
return "unknown";
|
|
85
|
+
}
|
|
86
|
+
var currentPlatform = detectPlatform();
|
|
87
|
+
function isReactNative() {
|
|
88
|
+
return currentPlatform === "react-native";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/utils/stacktrace.ts
|
|
92
|
+
function parseStackTrace(error) {
|
|
93
|
+
if (!error.stack) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const lines = error.stack.split("\n");
|
|
97
|
+
const frames = [];
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const frame = parseStackLine(line);
|
|
100
|
+
if (frame) {
|
|
101
|
+
frames.push(frame);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return frames;
|
|
105
|
+
}
|
|
106
|
+
function parseStackLine(line) {
|
|
107
|
+
const chromeMatch = line.match(
|
|
108
|
+
/^\s*at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+))\)?$/
|
|
109
|
+
);
|
|
110
|
+
if (chromeMatch) {
|
|
111
|
+
return {
|
|
112
|
+
function: chromeMatch[1] || "anonymous",
|
|
113
|
+
filename: chromeMatch[2],
|
|
114
|
+
lineno: parseInt(chromeMatch[3], 10),
|
|
115
|
+
colno: parseInt(chromeMatch[4], 10),
|
|
116
|
+
in_app: !chromeMatch[2]?.includes("node_modules")
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const firefoxMatch = line.match(/^(.+?)@(.+?):(\d+):(\d+)$/);
|
|
120
|
+
if (firefoxMatch) {
|
|
121
|
+
return {
|
|
122
|
+
function: firefoxMatch[1] || "anonymous",
|
|
123
|
+
filename: firefoxMatch[2],
|
|
124
|
+
lineno: parseInt(firefoxMatch[3], 10),
|
|
125
|
+
colno: parseInt(firefoxMatch[4], 10),
|
|
126
|
+
in_app: !firefoxMatch[2]?.includes("node_modules")
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
function generateEventId() {
|
|
132
|
+
const hex = "0123456789abcdef";
|
|
133
|
+
let id = "";
|
|
134
|
+
for (let i = 0; i < 32; i++) {
|
|
135
|
+
id += hex[Math.floor(Math.random() * 16)];
|
|
136
|
+
}
|
|
137
|
+
return id;
|
|
138
|
+
}
|
|
139
|
+
function getBrowserContext() {
|
|
140
|
+
if (isReactNative()) {
|
|
141
|
+
return {
|
|
142
|
+
browser: { name: "React Native", version: "unknown" },
|
|
143
|
+
os: { name: "unknown", version: "unknown" },
|
|
144
|
+
runtime: { name: "React Native", version: "unknown" }
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
148
|
+
return {
|
|
149
|
+
browser: { name: "Node.js", version: process.versions.node },
|
|
150
|
+
os: {
|
|
151
|
+
name: process.platform || "unknown",
|
|
152
|
+
version: process.version || "unknown"
|
|
153
|
+
},
|
|
154
|
+
runtime: { name: "Node.js", version: process.versions.node }
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (typeof navigator === "undefined") {
|
|
158
|
+
return {
|
|
159
|
+
browser: { name: "unknown", version: "unknown" },
|
|
160
|
+
os: { name: "unknown", version: "unknown" }
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const ua = navigator.userAgent;
|
|
164
|
+
let browserName = "unknown";
|
|
165
|
+
let browserVersion = "unknown";
|
|
166
|
+
if (ua.includes("Firefox/")) {
|
|
167
|
+
browserName = "Firefox";
|
|
168
|
+
browserVersion = ua.match(/Firefox\/(\d+)/)?.[1] || "unknown";
|
|
169
|
+
} else if (ua.includes("Edg/")) {
|
|
170
|
+
browserName = "Edge";
|
|
171
|
+
browserVersion = ua.match(/Edg\/(\d+)/)?.[1] || "unknown";
|
|
172
|
+
} else if (ua.includes("Chrome/")) {
|
|
173
|
+
browserName = "Chrome";
|
|
174
|
+
browserVersion = ua.match(/Chrome\/(\d+)/)?.[1] || "unknown";
|
|
175
|
+
} else if (ua.includes("Safari/") && !ua.includes("Chrome")) {
|
|
176
|
+
browserName = "Safari";
|
|
177
|
+
browserVersion = ua.match(/Version\/(\d+)/)?.[1] || "unknown";
|
|
178
|
+
}
|
|
179
|
+
let osName = "unknown";
|
|
180
|
+
let osVersion = "unknown";
|
|
181
|
+
if (ua.includes("Windows")) {
|
|
182
|
+
osName = "Windows";
|
|
183
|
+
osVersion = ua.match(/Windows NT (\d+\.\d+)/)?.[1] || "unknown";
|
|
184
|
+
} else if (ua.includes("Mac OS X")) {
|
|
185
|
+
osName = "macOS";
|
|
186
|
+
osVersion = ua.match(/Mac OS X (\d+[._]\d+)/)?.[1]?.replace("_", ".") || "unknown";
|
|
187
|
+
} else if (ua.includes("Linux")) {
|
|
188
|
+
osName = "Linux";
|
|
189
|
+
} else if (ua.includes("Android")) {
|
|
190
|
+
osName = "Android";
|
|
191
|
+
osVersion = ua.match(/Android (\d+)/)?.[1] || "unknown";
|
|
192
|
+
} else if (ua.includes("iOS") || ua.includes("iPhone") || ua.includes("iPad")) {
|
|
193
|
+
osName = "iOS";
|
|
194
|
+
osVersion = ua.match(/OS (\d+)/)?.[1] || "unknown";
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
browser: { name: browserName, version: browserVersion },
|
|
198
|
+
os: { name: osName, version: osVersion }
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/core/breadcrumbs.ts
|
|
203
|
+
var MAX_BREADCRUMBS = 100;
|
|
204
|
+
var BreadcrumbManager = class {
|
|
205
|
+
constructor() {
|
|
206
|
+
this.breadcrumbs = [];
|
|
207
|
+
this.maxBreadcrumbs = MAX_BREADCRUMBS;
|
|
208
|
+
}
|
|
209
|
+
setMaxBreadcrumbs(max) {
|
|
210
|
+
this.maxBreadcrumbs = max;
|
|
211
|
+
this.trim();
|
|
212
|
+
}
|
|
213
|
+
add(breadcrumb) {
|
|
214
|
+
const entry = {
|
|
215
|
+
...breadcrumb,
|
|
216
|
+
timestamp: Date.now()
|
|
217
|
+
};
|
|
218
|
+
this.breadcrumbs.push(entry);
|
|
219
|
+
this.trim();
|
|
220
|
+
}
|
|
221
|
+
getAll() {
|
|
222
|
+
return [...this.breadcrumbs];
|
|
223
|
+
}
|
|
224
|
+
clear() {
|
|
225
|
+
this.breadcrumbs = [];
|
|
226
|
+
}
|
|
227
|
+
trim() {
|
|
228
|
+
if (this.breadcrumbs.length > this.maxBreadcrumbs) {
|
|
229
|
+
this.breadcrumbs = this.breadcrumbs.slice(-this.maxBreadcrumbs);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
var breadcrumbs = new BreadcrumbManager();
|
|
234
|
+
function addBreadcrumb(breadcrumb) {
|
|
235
|
+
breadcrumbs.add(breadcrumb);
|
|
236
|
+
}
|
|
237
|
+
function getBreadcrumbs() {
|
|
238
|
+
return breadcrumbs.getAll();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/server/index.ts
|
|
242
|
+
var serverConfig = null;
|
|
243
|
+
function initServer(config) {
|
|
244
|
+
validateConfig(config);
|
|
245
|
+
serverConfig = {
|
|
246
|
+
enabled: true,
|
|
247
|
+
debug: false,
|
|
248
|
+
sampleRate: 1,
|
|
249
|
+
...config
|
|
250
|
+
};
|
|
251
|
+
if (serverConfig.debug) {
|
|
252
|
+
console.log("[SitePong] Server SDK initialized");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function captureServerException(error, context) {
|
|
256
|
+
if (!serverConfig?.enabled) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
260
|
+
const event = createServerErrorEvent(err, context);
|
|
261
|
+
await sendServerEvent(event);
|
|
262
|
+
return event.event_id;
|
|
263
|
+
}
|
|
264
|
+
function createServerErrorEvent(error, context) {
|
|
265
|
+
const browserContext = getBrowserContext();
|
|
266
|
+
return {
|
|
267
|
+
event_id: generateEventId(),
|
|
268
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
269
|
+
level: context?.level || "error",
|
|
270
|
+
message: error.message,
|
|
271
|
+
exception: {
|
|
272
|
+
type: error.name,
|
|
273
|
+
value: error.message,
|
|
274
|
+
stacktrace: parseStackTrace(error)
|
|
275
|
+
},
|
|
276
|
+
tags: { ...context?.tags, runtime: "server" },
|
|
277
|
+
extra: { ...context?.extra },
|
|
278
|
+
user: context?.user,
|
|
279
|
+
breadcrumbs: getBreadcrumbs(),
|
|
280
|
+
context: {
|
|
281
|
+
runtime: browserContext.runtime || { name: "Node.js", version: process.version }
|
|
282
|
+
},
|
|
283
|
+
environment: serverConfig?.environment,
|
|
284
|
+
release: serverConfig?.release,
|
|
285
|
+
request: context?.request
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
async function sendServerEvent(event) {
|
|
289
|
+
if (!serverConfig) return;
|
|
290
|
+
const endpoint = getApiEndpoint(serverConfig);
|
|
291
|
+
const headers = getAuthHeaders(serverConfig);
|
|
292
|
+
try {
|
|
293
|
+
const response = await fetch(`${endpoint}/api/errors`, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: {
|
|
296
|
+
"Content-Type": "application/json",
|
|
297
|
+
...headers
|
|
298
|
+
},
|
|
299
|
+
body: JSON.stringify({
|
|
300
|
+
title: event.exception?.type || "Error",
|
|
301
|
+
message: event.message,
|
|
302
|
+
stack: event.exception?.stacktrace?.map(
|
|
303
|
+
(frame) => ` at ${frame.function || "anonymous"} (${frame.filename}:${frame.lineno}:${frame.colno})`
|
|
304
|
+
).join("\n"),
|
|
305
|
+
level: event.level,
|
|
306
|
+
timestamp: event.timestamp,
|
|
307
|
+
environment: event.environment,
|
|
308
|
+
release: event.release,
|
|
309
|
+
tags: event.tags,
|
|
310
|
+
extra: event.extra,
|
|
311
|
+
user: event.user,
|
|
312
|
+
breadcrumbs: event.breadcrumbs,
|
|
313
|
+
context: event.context,
|
|
314
|
+
request: event.request
|
|
315
|
+
})
|
|
316
|
+
});
|
|
317
|
+
if (serverConfig.debug) {
|
|
318
|
+
if (response.ok) {
|
|
319
|
+
console.log("[SitePong] Server event sent:", event.event_id);
|
|
320
|
+
} else {
|
|
321
|
+
console.error("[SitePong] Failed to send server event:", response.status);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
} catch (err) {
|
|
325
|
+
if (serverConfig.debug) {
|
|
326
|
+
console.error("[SitePong] Error sending server event:", err);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/integrations/nextjs.ts
|
|
332
|
+
function initNextJs(config) {
|
|
333
|
+
initServer(config);
|
|
334
|
+
}
|
|
335
|
+
function wrapApiHandler(handler) {
|
|
336
|
+
return (async (request, context) => {
|
|
337
|
+
addBreadcrumb({
|
|
338
|
+
type: "http",
|
|
339
|
+
category: "http.request",
|
|
340
|
+
message: `${request.method} ${request.url}`,
|
|
341
|
+
level: "info",
|
|
342
|
+
data: {
|
|
343
|
+
method: request.method,
|
|
344
|
+
url: request.url
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
return await handler(request, context);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
await captureServerException(error, {
|
|
351
|
+
request: {
|
|
352
|
+
url: request.url,
|
|
353
|
+
method: request.method,
|
|
354
|
+
headers: Object.fromEntries(request.headers.entries())
|
|
355
|
+
},
|
|
356
|
+
tags: {
|
|
357
|
+
"next.runtime": "edge"
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function wrapServerAction(action, actionName) {
|
|
365
|
+
return (async (...args) => {
|
|
366
|
+
addBreadcrumb({
|
|
367
|
+
type: "default",
|
|
368
|
+
category: "server-action",
|
|
369
|
+
message: `Server Action: ${actionName || action.name || "anonymous"}`,
|
|
370
|
+
level: "info"
|
|
371
|
+
});
|
|
372
|
+
try {
|
|
373
|
+
return await action(...args);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
await captureServerException(error, {
|
|
376
|
+
tags: {
|
|
377
|
+
"next.server_action": actionName || action.name || "anonymous"
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
function NextErrorComponent({
|
|
385
|
+
error,
|
|
386
|
+
reset
|
|
387
|
+
}) {
|
|
388
|
+
const React = __require("react");
|
|
389
|
+
React.useEffect(() => {
|
|
390
|
+
console.error("[SitePong] Page error:", error);
|
|
391
|
+
}, [error]);
|
|
392
|
+
return React.createElement(
|
|
393
|
+
"div",
|
|
394
|
+
{
|
|
395
|
+
style: {
|
|
396
|
+
padding: "40px",
|
|
397
|
+
textAlign: "center",
|
|
398
|
+
fontFamily: "system-ui, sans-serif"
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
React.createElement("h2", null, "Something went wrong!"),
|
|
402
|
+
React.createElement(
|
|
403
|
+
"p",
|
|
404
|
+
{ style: { color: "#666", marginBottom: "20px" } },
|
|
405
|
+
error.message || "An unexpected error occurred"
|
|
406
|
+
),
|
|
407
|
+
React.createElement(
|
|
408
|
+
"button",
|
|
409
|
+
{
|
|
410
|
+
onClick: reset,
|
|
411
|
+
style: {
|
|
412
|
+
padding: "10px 20px",
|
|
413
|
+
cursor: "pointer",
|
|
414
|
+
backgroundColor: "#6366f1",
|
|
415
|
+
color: "white",
|
|
416
|
+
border: "none",
|
|
417
|
+
borderRadius: "6px"
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
"Try again"
|
|
421
|
+
)
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
function withSitePong(nextConfig = {}) {
|
|
425
|
+
return {
|
|
426
|
+
...nextConfig
|
|
427
|
+
// Future: Add source map upload, etc.
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
exports.NextErrorComponent = NextErrorComponent;
|
|
432
|
+
exports.addBreadcrumb = addBreadcrumb;
|
|
433
|
+
exports.captureServerException = captureServerException;
|
|
434
|
+
exports.initNextJs = initNextJs;
|
|
435
|
+
exports.initServer = initServer;
|
|
436
|
+
exports.withSitePong = withSitePong;
|
|
437
|
+
exports.wrapApiHandler = wrapApiHandler;
|
|
438
|
+
exports.wrapServerAction = wrapServerAction;
|
|
439
|
+
//# sourceMappingURL=index.js.map
|
|
440
|
+
//# sourceMappingURL=index.js.map
|