usetraceforge 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,35 +1,36 @@
1
- # traceforge
1
+ # usetraceforge
2
2
 
3
3
  TraceForge JavaScript SDK for sending errors to your TraceForge backend.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install traceforge
8
+ npm install usetraceforge
9
9
  ```
10
10
 
11
11
  ## Local pack test
12
12
 
13
13
  ```bash
14
14
  cd packages/sdk
15
+ npm run build
15
16
  npm pack
16
17
  ```
17
18
 
18
19
  Then in any local app:
19
20
 
20
21
  ```bash
21
- npm install /path/to/traceforge-0.1.0.tgz
22
+ npm install /path/to/usetraceforge-0.1.4.tgz
22
23
  ```
23
24
 
24
- ## Usage
25
+ ## Frontend setup
25
26
  ```ts
26
- import TraceForge from "traceforge";
27
+ import TraceForge from "usetraceforge";
27
28
 
28
29
  TraceForge.init({
29
30
  apiKey: "YOUR_PROJECT_API_KEY",
30
31
  endpoint: "http://localhost:3001/ingest",
31
32
  autoCapture: true,
32
- environment: "development",
33
+ environment: "production",
33
34
  release: "web@1.0.0"
34
35
  });
35
36
 
@@ -42,6 +43,37 @@ try {
42
43
  }
43
44
  ```
44
45
 
46
+ ## Backend setup
47
+ ```ts
48
+ import express from "express";
49
+ import TraceForge from "usetraceforge";
50
+
51
+ TraceForge.init({
52
+ apiKey: process.env.TRACEFORGE_API_KEY!,
53
+ endpoint: process.env.TRACEFORGE_INGEST_URL,
54
+ environment: process.env.TRACEFORGE_ENV || "production",
55
+ release: process.env.TRACEFORGE_RELEASE || "api@1.0.0"
56
+ });
57
+
58
+ const app = express();
59
+
60
+ app.use((error: unknown, _req, res, _next) => {
61
+ TraceForge.captureException(error, {
62
+ payload: { route: "express-error-handler" }
63
+ }).catch(() => undefined);
64
+
65
+ res.status(500).json({ error: "Internal server error" });
66
+ });
67
+ ```
68
+
69
+ ## Environment variables
70
+ ```env
71
+ TRACEFORGE_INGEST_URL=http://localhost:3001/ingest
72
+ TRACEFORGE_API_KEY=YOUR_PROJECT_API_KEY
73
+ TRACEFORGE_ENV=production
74
+ TRACEFORGE_RELEASE=web@1.0.0
75
+ ```
76
+
45
77
  ## Options
46
78
  - `autoCapture`: Listen to `window.onerror` and `window.onunhandledrejection`.
47
79
  - `ignoreErrors`: Array of strings or regex to skip noisy errors.
@@ -61,7 +93,5 @@ npm publish --access public
61
93
  After publish, consumers can install it with:
62
94
 
63
95
  ```bash
64
- npm install traceforge
96
+ npm install usetraceforge
65
97
  ```
66
-
67
- If the unscoped npm name is unavailable, publish under a scoped package name and update the install line to match.
@@ -0,0 +1,6 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ /**
3
+ * Express middleware to catch unhandled errors and report them to TraceForge.
4
+ * Ensure this is added AFTER all your routes and controllers, but BEFORE your custom error handler.
5
+ */
6
+ export declare const TraceForgeErrorHandler: (error: Error, req: Request, res: Response, next: NextFunction) => void;
@@ -0,0 +1,27 @@
1
+ import TraceForge from "./index.js";
2
+ /**
3
+ * Express middleware to catch unhandled errors and report them to TraceForge.
4
+ * Ensure this is added AFTER all your routes and controllers, but BEFORE your custom error handler.
5
+ */
6
+ export const TraceForgeErrorHandler = (error, req, res, next) => {
7
+ TraceForge.captureException(error, {
8
+ environment: "node",
9
+ tags: {
10
+ method: req.method,
11
+ url: req.url,
12
+ path: req.path,
13
+ ip: req.ip || "Unknown"
14
+ },
15
+ payload: {
16
+ query: req.query,
17
+ body: req.body,
18
+ headers: {
19
+ ...req.headers,
20
+ authorization: req.headers.authorization ? "[REDACTED]" : undefined,
21
+ cookie: req.headers.cookie ? "[REDACTED]" : undefined
22
+ }
23
+ }
24
+ }).catch(() => undefined);
25
+ // Pass the error to the next error handler
26
+ next(error);
27
+ };
package/dist/index.js CHANGED
@@ -1,6 +1,16 @@
1
1
  let config = null;
2
2
  let autoCaptureInitialized = false;
3
+ let setupHandshakeSent = false;
3
4
  const defaultEndpoint = "http://localhost:3001/ingest";
5
+ const getIngestEndpoint = () => config?.endpoint || defaultEndpoint;
6
+ const getSetupEndpoint = () => {
7
+ const endpoint = getIngestEndpoint();
8
+ const normalized = endpoint.replace(/\/+$/, "");
9
+ if (normalized.endsWith("/setup")) {
10
+ return normalized;
11
+ }
12
+ return `${normalized}/setup`;
13
+ };
4
14
  const ensureConfig = () => {
5
15
  if (!config) {
6
16
  throw new Error("TraceForge not initialized. Call TraceForge.init({ apiKey }) first.");
@@ -52,7 +62,7 @@ const sendEvent = async (event) => {
52
62
  if (!processed) {
53
63
  return;
54
64
  }
55
- await fetch(config.endpoint || defaultEndpoint, {
65
+ await fetch(getIngestEndpoint(), {
56
66
  method: "POST",
57
67
  headers: {
58
68
  "Content-Type": "application/json",
@@ -66,6 +76,29 @@ const capture = async (error, extras) => {
66
76
  const event = buildEvent(error, extras);
67
77
  await sendEvent(event);
68
78
  };
79
+ const sendSetupHandshake = async () => {
80
+ if (!config?.apiKey || setupHandshakeSent) {
81
+ return;
82
+ }
83
+ setupHandshakeSent = true;
84
+ try {
85
+ await fetch(getSetupEndpoint(), {
86
+ method: "POST",
87
+ headers: {
88
+ "Content-Type": "application/json",
89
+ "X-Traceforge-Key": config.apiKey
90
+ },
91
+ body: JSON.stringify({
92
+ environment: config.environment,
93
+ release: config.release,
94
+ tags: config.tags
95
+ })
96
+ });
97
+ }
98
+ catch {
99
+ setupHandshakeSent = false;
100
+ }
101
+ };
69
102
  const setupAutoCapture = () => {
70
103
  if (autoCaptureInitialized)
71
104
  return;
@@ -88,11 +121,18 @@ const setupAutoCapture = () => {
88
121
  };
89
122
  const TraceForge = {
90
123
  init: (options) => {
124
+ const previousApiKey = config?.apiKey ?? null;
125
+ const previousEndpoint = config?.endpoint ?? defaultEndpoint;
91
126
  config = {
92
127
  endpoint: defaultEndpoint,
93
128
  autoCapture: false,
94
129
  ...options
95
130
  };
131
+ const currentEndpoint = config.endpoint || defaultEndpoint;
132
+ if (previousApiKey !== config.apiKey || previousEndpoint !== currentEndpoint) {
133
+ setupHandshakeSent = false;
134
+ }
135
+ void sendSetupHandshake();
96
136
  if (config.autoCapture) {
97
137
  setupAutoCapture();
98
138
  }
package/dist/next.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function withTraceForge(handler: Function): (...args: any[]) => Promise<any>;
package/dist/next.js ADDED
@@ -0,0 +1,20 @@
1
+ import TraceForge from "./index.js";
2
+ // A wrapper for Next.js API Routes (App Router or Pages Router)
3
+ export function withTraceForge(handler) {
4
+ return async function (...args) {
5
+ try {
6
+ return await handler(...args);
7
+ }
8
+ catch (error) {
9
+ // Catch Next.js serverless API errors
10
+ await TraceForge.captureException(error, {
11
+ environment: "node",
12
+ tags: {
13
+ runtime: "nextjs-api"
14
+ }
15
+ }).catch(() => undefined);
16
+ // Re-throw so Next.js can handle the 500 response natively
17
+ throw error;
18
+ }
19
+ };
20
+ }
@@ -0,0 +1,18 @@
1
+ import React, { Component, ErrorInfo, ReactNode } from "react";
2
+ interface Props {
3
+ children?: ReactNode;
4
+ fallback?: ReactNode | ((error: Error) => ReactNode);
5
+ environment?: string;
6
+ release?: string;
7
+ }
8
+ interface State {
9
+ hasError: boolean;
10
+ error: Error | null;
11
+ }
12
+ export declare class TraceForgeErrorBoundary extends Component<Props, State> {
13
+ state: State;
14
+ static getDerivedStateFromError(error: Error): State;
15
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
16
+ render(): React.ReactNode;
17
+ }
18
+ export {};
package/dist/react.js ADDED
@@ -0,0 +1,33 @@
1
+ import { Component } from "react";
2
+ import TraceForge from "./index.js";
3
+ export class TraceForgeErrorBoundary extends Component {
4
+ state = {
5
+ hasError: false,
6
+ error: null
7
+ };
8
+ static getDerivedStateFromError(error) {
9
+ return { hasError: true, error };
10
+ }
11
+ componentDidCatch(error, errorInfo) {
12
+ TraceForge.captureException(error, {
13
+ environment: this.props.environment || "browser",
14
+ release: this.props.release,
15
+ tags: {
16
+ componentStack: errorInfo.componentStack || "Unknown"
17
+ }
18
+ }).catch(() => undefined);
19
+ }
20
+ render() {
21
+ if (this.state.hasError && this.state.error) {
22
+ if (typeof this.props.fallback === "function") {
23
+ return this.props.fallback(this.state.error);
24
+ }
25
+ if (this.props.fallback) {
26
+ return this.props.fallback;
27
+ }
28
+ // Default fallback if nothing is provided
29
+ return null;
30
+ }
31
+ return this.props.children;
32
+ }
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usetraceforge",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "description": "TraceForge JavaScript SDK for sending errors to a TraceForge ingest endpoint.",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  "types": "dist/index.d.ts",
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "https://github.com/khushalp2004/TraceForge.git",
12
+ "url": "git+https://github.com/khushalp2004/TraceForge.git",
13
13
  "directory": "packages/sdk"
14
14
  },
15
15
  "homepage": "https://usetraceforge.com/docs",
@@ -28,6 +28,18 @@
28
28
  ".": {
29
29
  "types": "./dist/index.d.ts",
30
30
  "import": "./dist/index.js"
31
+ },
32
+ "./react": {
33
+ "types": "./dist/react.d.ts",
34
+ "import": "./dist/react.js"
35
+ },
36
+ "./express": {
37
+ "types": "./dist/express.d.ts",
38
+ "import": "./dist/express.js"
39
+ },
40
+ "./next": {
41
+ "types": "./dist/next.d.ts",
42
+ "import": "./dist/next.js"
31
43
  }
32
44
  },
33
45
  "files": [
@@ -47,6 +59,16 @@
47
59
  "dependencies": {},
48
60
  "devDependencies": {
49
61
  "typescript": "^5.5.4",
50
- "tsx": "^4.16.2"
62
+ "tsx": "^4.16.2",
63
+ "@types/react": "^18.2.0",
64
+ "@types/express": "^4.17.21"
65
+ },
66
+ "peerDependencies": {
67
+ "react": ">=16.8.0",
68
+ "express": ">=4.0.0"
69
+ },
70
+ "peerDependenciesMeta": {
71
+ "react": { "optional": true },
72
+ "express": { "optional": true }
51
73
  }
52
74
  }