@clienwork/errors 1.0.0 → 1.0.2
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 +41 -28
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/dist/index.mjs +136 -10
- package/dist/react.d.mts +48 -3
- package/dist/react.d.ts +48 -3
- package/dist/react.js +5 -1
- package/dist/react.mjs +126 -8
- package/dist/upload-sourcemaps.d.mts +41 -0
- package/dist/upload-sourcemaps.d.ts +41 -0
- package/dist/upload-sourcemaps.js +227 -0
- package/dist/upload-sourcemaps.mjs +199 -0
- package/package.json +18 -4
- package/dist/chunk-HLDC7IDZ.mjs +0 -143
package/README.md
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# @clienwork/errors
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@clienwork/errors)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Error collection SDK for [Clienwork](https://clienwork.com)
|
|
7
|
+
|
|
8
|
+
Automatically collect and monitor errors from your frontend and backend applications. View error logs, analyze stack traces, and receive Slack notifications from the Clienwork dashboard.
|
|
9
|
+
|
|
10
|
+
**[Get Started](https://clienwork.com)** | **[Documentation](https://clienwork.com/docs)** | **[Dashboard](https://app.clienwork.com)**
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
6
13
|
|
|
7
14
|
```bash
|
|
8
15
|
npm install @clienwork/errors
|
|
@@ -12,44 +19,44 @@ yarn add @clienwork/errors
|
|
|
12
19
|
pnpm add @clienwork/errors
|
|
13
20
|
```
|
|
14
21
|
|
|
15
|
-
##
|
|
22
|
+
## Frontend Usage
|
|
16
23
|
|
|
17
|
-
###
|
|
24
|
+
### Initialization
|
|
18
25
|
|
|
19
26
|
```typescript
|
|
20
27
|
import { init } from '@clienwork/errors'
|
|
21
28
|
|
|
22
29
|
init({
|
|
23
|
-
apiToken: 'clw_err_xxx', //
|
|
24
|
-
source: 'frontend', //
|
|
30
|
+
apiToken: 'clw_err_xxx', // Token from your project settings
|
|
31
|
+
source: 'frontend', // Default
|
|
25
32
|
})
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
###
|
|
35
|
+
### Manual Error Reporting
|
|
29
36
|
|
|
30
37
|
```typescript
|
|
31
38
|
import { report } from '@clienwork/errors'
|
|
32
39
|
|
|
33
40
|
try {
|
|
34
|
-
//
|
|
41
|
+
// Risky operation
|
|
35
42
|
} catch (error) {
|
|
36
43
|
report(error, { context: 'checkout' })
|
|
37
44
|
}
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
###
|
|
47
|
+
### User Identification
|
|
41
48
|
|
|
42
49
|
```typescript
|
|
43
50
|
import { setUser, clearUser } from '@clienwork/errors'
|
|
44
51
|
|
|
45
|
-
//
|
|
52
|
+
// On login
|
|
46
53
|
setUser('user-123')
|
|
47
54
|
|
|
48
|
-
//
|
|
55
|
+
// On logout
|
|
49
56
|
clearUser()
|
|
50
57
|
```
|
|
51
58
|
|
|
52
|
-
## React
|
|
59
|
+
## React Usage
|
|
53
60
|
|
|
54
61
|
### Error Boundary
|
|
55
62
|
|
|
@@ -59,7 +66,7 @@ import { ClienworkErrorBoundary } from '@clienwork/errors/react'
|
|
|
59
66
|
function App() {
|
|
60
67
|
return (
|
|
61
68
|
<ClienworkErrorBoundary
|
|
62
|
-
fallback={<div
|
|
69
|
+
fallback={<div>Something went wrong.</div>}
|
|
63
70
|
>
|
|
64
71
|
<MyComponent />
|
|
65
72
|
</ClienworkErrorBoundary>
|
|
@@ -92,12 +99,12 @@ function MyComponent() {
|
|
|
92
99
|
```tsx
|
|
93
100
|
import { withErrorBoundary } from '@clienwork/errors/react'
|
|
94
101
|
|
|
95
|
-
const SafeComponent = withErrorBoundary(MyComponent, <div
|
|
102
|
+
const SafeComponent = withErrorBoundary(MyComponent, <div>Error occurred</div>)
|
|
96
103
|
```
|
|
97
104
|
|
|
98
|
-
##
|
|
105
|
+
## Backend Usage (Node.js)
|
|
99
106
|
|
|
100
|
-
###
|
|
107
|
+
### Basic Usage
|
|
101
108
|
|
|
102
109
|
```typescript
|
|
103
110
|
import { createBackendReporter } from '@clienwork/errors'
|
|
@@ -107,13 +114,13 @@ const errorReporter = createBackendReporter({
|
|
|
107
114
|
})
|
|
108
115
|
|
|
109
116
|
try {
|
|
110
|
-
//
|
|
117
|
+
// Risky operation
|
|
111
118
|
} catch (error) {
|
|
112
119
|
await errorReporter.report(error, { endpoint: '/api/users' })
|
|
113
120
|
}
|
|
114
121
|
```
|
|
115
122
|
|
|
116
|
-
### Express
|
|
123
|
+
### Express Middleware
|
|
117
124
|
|
|
118
125
|
```typescript
|
|
119
126
|
import { createBackendReporter, expressErrorHandler } from '@clienwork/errors'
|
|
@@ -144,22 +151,28 @@ export async function GET(request: Request) {
|
|
|
144
151
|
}
|
|
145
152
|
```
|
|
146
153
|
|
|
147
|
-
##
|
|
154
|
+
## Configuration Options
|
|
148
155
|
|
|
149
156
|
```typescript
|
|
150
157
|
init({
|
|
151
|
-
apiToken: 'clw_err_xxx', //
|
|
152
|
-
endpoint: 'https://...', //
|
|
153
|
-
source: 'frontend', //
|
|
154
|
-
enabled: true, //
|
|
155
|
-
defaultMetadata: {}, //
|
|
156
|
-
beforeSend: (error) => { //
|
|
157
|
-
// null
|
|
158
|
+
apiToken: 'clw_err_xxx', // Required: API token
|
|
159
|
+
endpoint: 'https://...', // Optional: Custom endpoint
|
|
160
|
+
source: 'frontend', // Optional: 'frontend' | 'backend'
|
|
161
|
+
enabled: true, // Optional: Enable/disable (useful for dev environment)
|
|
162
|
+
defaultMetadata: {}, // Optional: Default metadata included in all errors
|
|
163
|
+
beforeSend: (error) => { // Optional: Modify/filter errors before sending
|
|
164
|
+
// Return null to cancel sending
|
|
158
165
|
return error
|
|
159
166
|
},
|
|
160
167
|
})
|
|
161
168
|
```
|
|
162
169
|
|
|
163
|
-
##
|
|
170
|
+
## Support
|
|
171
|
+
|
|
172
|
+
- [Clienwork Homepage](https://clienwork.com)
|
|
173
|
+
- [Contact Us](mailto:support@clienwork.com)
|
|
174
|
+
- [GitHub Issues](https://github.com/clienwork/clienwork/issues)
|
|
175
|
+
|
|
176
|
+
## License
|
|
164
177
|
|
|
165
|
-
MIT
|
|
178
|
+
MIT - [Clienwork](https://clienwork.com)
|
package/dist/index.d.mts
CHANGED
|
@@ -10,6 +10,7 @@ interface ErrorReportOptions {
|
|
|
10
10
|
userId?: string;
|
|
11
11
|
metadata?: Record<string, unknown>;
|
|
12
12
|
fingerprint?: string;
|
|
13
|
+
release?: string;
|
|
13
14
|
}
|
|
14
15
|
interface ClienworkErrorConfig {
|
|
15
16
|
apiToken: string;
|
|
@@ -18,6 +19,7 @@ interface ClienworkErrorConfig {
|
|
|
18
19
|
defaultMetadata?: Record<string, unknown>;
|
|
19
20
|
enabled?: boolean;
|
|
20
21
|
beforeSend?: (error: ErrorReportOptions) => ErrorReportOptions | null;
|
|
22
|
+
release?: string;
|
|
21
23
|
}
|
|
22
24
|
interface ErrorReporterInstance {
|
|
23
25
|
report: (error: Error | ErrorReportOptions, metadata?: Record<string, unknown>) => Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ interface ErrorReportOptions {
|
|
|
10
10
|
userId?: string;
|
|
11
11
|
metadata?: Record<string, unknown>;
|
|
12
12
|
fingerprint?: string;
|
|
13
|
+
release?: string;
|
|
13
14
|
}
|
|
14
15
|
interface ClienworkErrorConfig {
|
|
15
16
|
apiToken: string;
|
|
@@ -18,6 +19,7 @@ interface ClienworkErrorConfig {
|
|
|
18
19
|
defaultMetadata?: Record<string, unknown>;
|
|
19
20
|
enabled?: boolean;
|
|
20
21
|
beforeSend?: (error: ErrorReportOptions) => ErrorReportOptions | null;
|
|
22
|
+
release?: string;
|
|
21
23
|
}
|
|
22
24
|
interface ErrorReporterInstance {
|
|
23
25
|
report: (error: Error | ErrorReportOptions, metadata?: Record<string, unknown>) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -43,7 +43,8 @@ var ErrorReporter = class {
|
|
|
43
43
|
source: config.source || "frontend",
|
|
44
44
|
defaultMetadata: config.defaultMetadata || {},
|
|
45
45
|
enabled: config.enabled ?? true,
|
|
46
|
-
beforeSend: config.beforeSend
|
|
46
|
+
beforeSend: config.beforeSend,
|
|
47
|
+
release: config.release
|
|
47
48
|
};
|
|
48
49
|
if (typeof window !== "undefined" && this.config.source === "frontend") {
|
|
49
50
|
this.setupGlobalHandlers();
|
|
@@ -110,6 +111,9 @@ var ErrorReporter = class {
|
|
|
110
111
|
reportData.url = reportData.url || window.location.href;
|
|
111
112
|
reportData.userAgent = reportData.userAgent || navigator.userAgent;
|
|
112
113
|
}
|
|
114
|
+
if (this.config.release) {
|
|
115
|
+
reportData.release = reportData.release || this.config.release;
|
|
116
|
+
}
|
|
113
117
|
if (this.config.beforeSend) {
|
|
114
118
|
const modified = this.config.beforeSend(reportData);
|
|
115
119
|
if (modified === null) return;
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,139 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
// src/reporter.ts
|
|
2
|
+
var DEFAULT_ENDPOINT = "https://app.clienwork.com/api/errors";
|
|
3
|
+
var ErrorReporter = class {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.userId = null;
|
|
6
|
+
this.metadata = {};
|
|
7
|
+
this.config = {
|
|
8
|
+
apiToken: config.apiToken,
|
|
9
|
+
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
10
|
+
source: config.source || "frontend",
|
|
11
|
+
defaultMetadata: config.defaultMetadata || {},
|
|
12
|
+
enabled: config.enabled ?? true,
|
|
13
|
+
beforeSend: config.beforeSend,
|
|
14
|
+
release: config.release
|
|
15
|
+
};
|
|
16
|
+
if (typeof window !== "undefined" && this.config.source === "frontend") {
|
|
17
|
+
this.setupGlobalHandlers();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
setupGlobalHandlers() {
|
|
21
|
+
window.addEventListener("error", (event) => {
|
|
22
|
+
this.report({
|
|
23
|
+
message: event.message,
|
|
24
|
+
stack: event.error?.stack,
|
|
25
|
+
url: event.filename,
|
|
26
|
+
metadata: {
|
|
27
|
+
lineno: event.lineno,
|
|
28
|
+
colno: event.colno
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
33
|
+
const error = event.reason;
|
|
34
|
+
if (error instanceof Error) {
|
|
35
|
+
this.report(error);
|
|
36
|
+
} else {
|
|
37
|
+
this.report({
|
|
38
|
+
message: String(error),
|
|
39
|
+
metadata: { type: "unhandledrejection" }
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
setUser(userId) {
|
|
45
|
+
this.userId = userId;
|
|
46
|
+
}
|
|
47
|
+
clearUser() {
|
|
48
|
+
this.userId = null;
|
|
49
|
+
}
|
|
50
|
+
setMetadata(metadata) {
|
|
51
|
+
this.metadata = { ...this.metadata, ...metadata };
|
|
52
|
+
}
|
|
53
|
+
async report(error, additionalMetadata) {
|
|
54
|
+
if (!this.config.enabled) return;
|
|
55
|
+
let reportData;
|
|
56
|
+
if (error instanceof Error) {
|
|
57
|
+
reportData = {
|
|
58
|
+
message: error.message,
|
|
59
|
+
stack: error.stack,
|
|
60
|
+
source: this.config.source
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
reportData = {
|
|
64
|
+
...error,
|
|
65
|
+
source: error.source || this.config.source
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
reportData.metadata = {
|
|
69
|
+
...this.config.defaultMetadata,
|
|
70
|
+
...this.metadata,
|
|
71
|
+
...reportData.metadata,
|
|
72
|
+
...additionalMetadata
|
|
73
|
+
};
|
|
74
|
+
if (this.userId) {
|
|
75
|
+
reportData.userId = this.userId;
|
|
76
|
+
}
|
|
77
|
+
if (typeof window !== "undefined") {
|
|
78
|
+
reportData.url = reportData.url || window.location.href;
|
|
79
|
+
reportData.userAgent = reportData.userAgent || navigator.userAgent;
|
|
80
|
+
}
|
|
81
|
+
if (this.config.release) {
|
|
82
|
+
reportData.release = reportData.release || this.config.release;
|
|
83
|
+
}
|
|
84
|
+
if (this.config.beforeSend) {
|
|
85
|
+
const modified = this.config.beforeSend(reportData);
|
|
86
|
+
if (modified === null) return;
|
|
87
|
+
reportData = modified;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await fetch(this.config.endpoint, {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: {
|
|
93
|
+
"Content-Type": "application/json",
|
|
94
|
+
Authorization: `Bearer ${this.config.apiToken}`
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify(reportData)
|
|
97
|
+
});
|
|
98
|
+
} catch (e) {
|
|
99
|
+
console.warn("[ClienworkErrors] Failed to report error:", e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var instance = null;
|
|
104
|
+
function init(config) {
|
|
105
|
+
instance = new ErrorReporter(config);
|
|
106
|
+
return instance;
|
|
107
|
+
}
|
|
108
|
+
function getReporter() {
|
|
109
|
+
return instance;
|
|
110
|
+
}
|
|
111
|
+
function report(error, metadata) {
|
|
112
|
+
if (!instance) {
|
|
113
|
+
console.warn("[ClienworkErrors] Not initialized. Call init() first.");
|
|
114
|
+
return Promise.resolve();
|
|
115
|
+
}
|
|
116
|
+
return instance.report(error, metadata);
|
|
117
|
+
}
|
|
118
|
+
function setUser(userId) {
|
|
119
|
+
instance?.setUser(userId);
|
|
120
|
+
}
|
|
121
|
+
function clearUser() {
|
|
122
|
+
instance?.clearUser();
|
|
123
|
+
}
|
|
124
|
+
function createBackendReporter(config) {
|
|
125
|
+
return new ErrorReporter({ ...config, source: "backend" });
|
|
126
|
+
}
|
|
127
|
+
function expressErrorHandler(reporter) {
|
|
128
|
+
return (err, req, res, next) => {
|
|
129
|
+
reporter.report(err, {
|
|
130
|
+
path: req.path,
|
|
131
|
+
method: req.method,
|
|
132
|
+
userAgent: req.headers?.["user-agent"]
|
|
133
|
+
});
|
|
134
|
+
next();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
11
137
|
export {
|
|
12
138
|
ErrorReporter,
|
|
13
139
|
clearUser,
|
package/dist/react.d.mts
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
type ErrorSource = 'frontend' | 'backend';
|
|
4
|
+
type ErrorLevel = 'error' | 'warning';
|
|
5
|
+
interface ErrorReportOptions {
|
|
6
|
+
message: string;
|
|
7
|
+
stack?: string;
|
|
8
|
+
level?: ErrorLevel;
|
|
9
|
+
source?: ErrorSource;
|
|
10
|
+
url?: string;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
userId?: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
fingerprint?: string;
|
|
15
|
+
release?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ClienworkErrorConfig {
|
|
18
|
+
apiToken: string;
|
|
19
|
+
endpoint?: string;
|
|
20
|
+
source?: ErrorSource;
|
|
21
|
+
defaultMetadata?: Record<string, unknown>;
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
beforeSend?: (error: ErrorReportOptions) => ErrorReportOptions | null;
|
|
24
|
+
release?: string;
|
|
25
|
+
}
|
|
26
|
+
interface ErrorReporterInstance {
|
|
27
|
+
report: (error: Error | ErrorReportOptions, metadata?: Record<string, unknown>) => Promise<void>;
|
|
28
|
+
setUser: (userId: string) => void;
|
|
29
|
+
clearUser: () => void;
|
|
30
|
+
setMetadata: (metadata: Record<string, unknown>) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare class ErrorReporter implements ErrorReporterInstance {
|
|
34
|
+
private config;
|
|
35
|
+
private userId;
|
|
36
|
+
private metadata;
|
|
37
|
+
constructor(config: ClienworkErrorConfig);
|
|
38
|
+
private setupGlobalHandlers;
|
|
39
|
+
setUser(userId: string): void;
|
|
40
|
+
clearUser(): void;
|
|
41
|
+
setMetadata(metadata: Record<string, unknown>): void;
|
|
42
|
+
report(error: Error | ErrorReportOptions, additionalMetadata?: Record<string, unknown>): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
declare function init(config: ClienworkErrorConfig): ErrorReporter;
|
|
45
|
+
declare function getReporter(): ErrorReporter | null;
|
|
46
|
+
declare function report(error: Error | ErrorReportOptions, metadata?: Record<string, unknown>): Promise<void>;
|
|
47
|
+
declare function setUser(userId: string): void;
|
|
48
|
+
declare function clearUser(): void;
|
|
4
49
|
|
|
5
50
|
interface ErrorBoundaryProps {
|
|
6
51
|
children: React.ReactNode;
|
|
@@ -22,4 +67,4 @@ declare function useErrorReporter(): {
|
|
|
22
67
|
};
|
|
23
68
|
declare function withErrorBoundary<P extends object>(Component: React.ComponentType<P>, fallback?: ErrorBoundaryProps['fallback']): React.FC<P>;
|
|
24
69
|
|
|
25
|
-
export { ClienworkErrorBoundary, ErrorReportOptions, useErrorReporter, withErrorBoundary };
|
|
70
|
+
export { ClienworkErrorBoundary, type ClienworkErrorConfig, type ErrorReportOptions, clearUser, getReporter, init, report, setUser, useErrorReporter, withErrorBoundary };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
type ErrorSource = 'frontend' | 'backend';
|
|
4
|
+
type ErrorLevel = 'error' | 'warning';
|
|
5
|
+
interface ErrorReportOptions {
|
|
6
|
+
message: string;
|
|
7
|
+
stack?: string;
|
|
8
|
+
level?: ErrorLevel;
|
|
9
|
+
source?: ErrorSource;
|
|
10
|
+
url?: string;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
userId?: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
fingerprint?: string;
|
|
15
|
+
release?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ClienworkErrorConfig {
|
|
18
|
+
apiToken: string;
|
|
19
|
+
endpoint?: string;
|
|
20
|
+
source?: ErrorSource;
|
|
21
|
+
defaultMetadata?: Record<string, unknown>;
|
|
22
|
+
enabled?: boolean;
|
|
23
|
+
beforeSend?: (error: ErrorReportOptions) => ErrorReportOptions | null;
|
|
24
|
+
release?: string;
|
|
25
|
+
}
|
|
26
|
+
interface ErrorReporterInstance {
|
|
27
|
+
report: (error: Error | ErrorReportOptions, metadata?: Record<string, unknown>) => Promise<void>;
|
|
28
|
+
setUser: (userId: string) => void;
|
|
29
|
+
clearUser: () => void;
|
|
30
|
+
setMetadata: (metadata: Record<string, unknown>) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare class ErrorReporter implements ErrorReporterInstance {
|
|
34
|
+
private config;
|
|
35
|
+
private userId;
|
|
36
|
+
private metadata;
|
|
37
|
+
constructor(config: ClienworkErrorConfig);
|
|
38
|
+
private setupGlobalHandlers;
|
|
39
|
+
setUser(userId: string): void;
|
|
40
|
+
clearUser(): void;
|
|
41
|
+
setMetadata(metadata: Record<string, unknown>): void;
|
|
42
|
+
report(error: Error | ErrorReportOptions, additionalMetadata?: Record<string, unknown>): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
declare function init(config: ClienworkErrorConfig): ErrorReporter;
|
|
45
|
+
declare function getReporter(): ErrorReporter | null;
|
|
46
|
+
declare function report(error: Error | ErrorReportOptions, metadata?: Record<string, unknown>): Promise<void>;
|
|
47
|
+
declare function setUser(userId: string): void;
|
|
48
|
+
declare function clearUser(): void;
|
|
4
49
|
|
|
5
50
|
interface ErrorBoundaryProps {
|
|
6
51
|
children: React.ReactNode;
|
|
@@ -22,4 +67,4 @@ declare function useErrorReporter(): {
|
|
|
22
67
|
};
|
|
23
68
|
declare function withErrorBoundary<P extends object>(Component: React.ComponentType<P>, fallback?: ErrorBoundaryProps['fallback']): React.FC<P>;
|
|
24
69
|
|
|
25
|
-
export { ClienworkErrorBoundary, ErrorReportOptions, useErrorReporter, withErrorBoundary };
|
|
70
|
+
export { ClienworkErrorBoundary, type ClienworkErrorConfig, type ErrorReportOptions, clearUser, getReporter, init, report, setUser, useErrorReporter, withErrorBoundary };
|
package/dist/react.js
CHANGED
|
@@ -54,7 +54,8 @@ var ErrorReporter = class {
|
|
|
54
54
|
source: config.source || "frontend",
|
|
55
55
|
defaultMetadata: config.defaultMetadata || {},
|
|
56
56
|
enabled: config.enabled ?? true,
|
|
57
|
-
beforeSend: config.beforeSend
|
|
57
|
+
beforeSend: config.beforeSend,
|
|
58
|
+
release: config.release
|
|
58
59
|
};
|
|
59
60
|
if (typeof window !== "undefined" && this.config.source === "frontend") {
|
|
60
61
|
this.setupGlobalHandlers();
|
|
@@ -121,6 +122,9 @@ var ErrorReporter = class {
|
|
|
121
122
|
reportData.url = reportData.url || window.location.href;
|
|
122
123
|
reportData.userAgent = reportData.userAgent || navigator.userAgent;
|
|
123
124
|
}
|
|
125
|
+
if (this.config.release) {
|
|
126
|
+
reportData.release = reportData.release || this.config.release;
|
|
127
|
+
}
|
|
124
128
|
if (this.config.beforeSend) {
|
|
125
129
|
const modified = this.config.beforeSend(reportData);
|
|
126
130
|
if (modified === null) return;
|
package/dist/react.mjs
CHANGED
|
@@ -1,13 +1,131 @@
|
|
|
1
|
-
import {
|
|
2
|
-
clearUser,
|
|
3
|
-
getReporter,
|
|
4
|
-
init,
|
|
5
|
-
report,
|
|
6
|
-
setUser
|
|
7
|
-
} from "./chunk-HLDC7IDZ.mjs";
|
|
8
|
-
|
|
9
1
|
// src/react.ts
|
|
10
2
|
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
// src/reporter.ts
|
|
5
|
+
var DEFAULT_ENDPOINT = "https://app.clienwork.com/api/errors";
|
|
6
|
+
var ErrorReporter = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.userId = null;
|
|
9
|
+
this.metadata = {};
|
|
10
|
+
this.config = {
|
|
11
|
+
apiToken: config.apiToken,
|
|
12
|
+
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
13
|
+
source: config.source || "frontend",
|
|
14
|
+
defaultMetadata: config.defaultMetadata || {},
|
|
15
|
+
enabled: config.enabled ?? true,
|
|
16
|
+
beforeSend: config.beforeSend,
|
|
17
|
+
release: config.release
|
|
18
|
+
};
|
|
19
|
+
if (typeof window !== "undefined" && this.config.source === "frontend") {
|
|
20
|
+
this.setupGlobalHandlers();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
setupGlobalHandlers() {
|
|
24
|
+
window.addEventListener("error", (event) => {
|
|
25
|
+
this.report({
|
|
26
|
+
message: event.message,
|
|
27
|
+
stack: event.error?.stack,
|
|
28
|
+
url: event.filename,
|
|
29
|
+
metadata: {
|
|
30
|
+
lineno: event.lineno,
|
|
31
|
+
colno: event.colno
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
36
|
+
const error = event.reason;
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
this.report(error);
|
|
39
|
+
} else {
|
|
40
|
+
this.report({
|
|
41
|
+
message: String(error),
|
|
42
|
+
metadata: { type: "unhandledrejection" }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
setUser(userId) {
|
|
48
|
+
this.userId = userId;
|
|
49
|
+
}
|
|
50
|
+
clearUser() {
|
|
51
|
+
this.userId = null;
|
|
52
|
+
}
|
|
53
|
+
setMetadata(metadata) {
|
|
54
|
+
this.metadata = { ...this.metadata, ...metadata };
|
|
55
|
+
}
|
|
56
|
+
async report(error, additionalMetadata) {
|
|
57
|
+
if (!this.config.enabled) return;
|
|
58
|
+
let reportData;
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
reportData = {
|
|
61
|
+
message: error.message,
|
|
62
|
+
stack: error.stack,
|
|
63
|
+
source: this.config.source
|
|
64
|
+
};
|
|
65
|
+
} else {
|
|
66
|
+
reportData = {
|
|
67
|
+
...error,
|
|
68
|
+
source: error.source || this.config.source
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
reportData.metadata = {
|
|
72
|
+
...this.config.defaultMetadata,
|
|
73
|
+
...this.metadata,
|
|
74
|
+
...reportData.metadata,
|
|
75
|
+
...additionalMetadata
|
|
76
|
+
};
|
|
77
|
+
if (this.userId) {
|
|
78
|
+
reportData.userId = this.userId;
|
|
79
|
+
}
|
|
80
|
+
if (typeof window !== "undefined") {
|
|
81
|
+
reportData.url = reportData.url || window.location.href;
|
|
82
|
+
reportData.userAgent = reportData.userAgent || navigator.userAgent;
|
|
83
|
+
}
|
|
84
|
+
if (this.config.release) {
|
|
85
|
+
reportData.release = reportData.release || this.config.release;
|
|
86
|
+
}
|
|
87
|
+
if (this.config.beforeSend) {
|
|
88
|
+
const modified = this.config.beforeSend(reportData);
|
|
89
|
+
if (modified === null) return;
|
|
90
|
+
reportData = modified;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
await fetch(this.config.endpoint, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
Authorization: `Bearer ${this.config.apiToken}`
|
|
98
|
+
},
|
|
99
|
+
body: JSON.stringify(reportData)
|
|
100
|
+
});
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.warn("[ClienworkErrors] Failed to report error:", e);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var instance = null;
|
|
107
|
+
function init(config) {
|
|
108
|
+
instance = new ErrorReporter(config);
|
|
109
|
+
return instance;
|
|
110
|
+
}
|
|
111
|
+
function getReporter() {
|
|
112
|
+
return instance;
|
|
113
|
+
}
|
|
114
|
+
function report(error, metadata) {
|
|
115
|
+
if (!instance) {
|
|
116
|
+
console.warn("[ClienworkErrors] Not initialized. Call init() first.");
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
}
|
|
119
|
+
return instance.report(error, metadata);
|
|
120
|
+
}
|
|
121
|
+
function setUser(userId) {
|
|
122
|
+
instance?.setUser(userId);
|
|
123
|
+
}
|
|
124
|
+
function clearUser() {
|
|
125
|
+
instance?.clearUser();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/react.ts
|
|
11
129
|
var ClienworkErrorBoundary = class extends React.Component {
|
|
12
130
|
constructor(props) {
|
|
13
131
|
super(props);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Clienwork Source Maps Upload CLI
|
|
4
|
+
*
|
|
5
|
+
* Upload source maps to Clienwork for stack trace parsing.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx clienwork-upload-sourcemaps --release <version> --include <glob>
|
|
9
|
+
*
|
|
10
|
+
* Environment:
|
|
11
|
+
* CLIENWORK_ERROR_TOKEN - API token for authentication
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* npx clienwork-upload-sourcemaps --release v1.0.0 --include ".next/**\/*.map"
|
|
15
|
+
* npx clienwork-upload-sourcemaps --release $VERCEL_GIT_COMMIT_SHA --include "dist/**\/*.map"
|
|
16
|
+
*/
|
|
17
|
+
interface UploadOptions {
|
|
18
|
+
apiToken: string;
|
|
19
|
+
release: string;
|
|
20
|
+
include: string[];
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface UploadResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
uploaded: Array<{
|
|
27
|
+
filename: string;
|
|
28
|
+
release: string;
|
|
29
|
+
size: number;
|
|
30
|
+
}>;
|
|
31
|
+
errors?: Array<{
|
|
32
|
+
filename: string;
|
|
33
|
+
error: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Upload source maps to Clienwork
|
|
38
|
+
*/
|
|
39
|
+
declare function uploadSourceMaps(options: UploadOptions): Promise<UploadResult>;
|
|
40
|
+
|
|
41
|
+
export { type UploadOptions, type UploadResult, uploadSourceMaps };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Clienwork Source Maps Upload CLI
|
|
4
|
+
*
|
|
5
|
+
* Upload source maps to Clienwork for stack trace parsing.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx clienwork-upload-sourcemaps --release <version> --include <glob>
|
|
9
|
+
*
|
|
10
|
+
* Environment:
|
|
11
|
+
* CLIENWORK_ERROR_TOKEN - API token for authentication
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* npx clienwork-upload-sourcemaps --release v1.0.0 --include ".next/**\/*.map"
|
|
15
|
+
* npx clienwork-upload-sourcemaps --release $VERCEL_GIT_COMMIT_SHA --include "dist/**\/*.map"
|
|
16
|
+
*/
|
|
17
|
+
interface UploadOptions {
|
|
18
|
+
apiToken: string;
|
|
19
|
+
release: string;
|
|
20
|
+
include: string[];
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface UploadResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
uploaded: Array<{
|
|
27
|
+
filename: string;
|
|
28
|
+
release: string;
|
|
29
|
+
size: number;
|
|
30
|
+
}>;
|
|
31
|
+
errors?: Array<{
|
|
32
|
+
filename: string;
|
|
33
|
+
error: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Upload source maps to Clienwork
|
|
38
|
+
*/
|
|
39
|
+
declare function uploadSourceMaps(options: UploadOptions): Promise<UploadResult>;
|
|
40
|
+
|
|
41
|
+
export { type UploadOptions, type UploadResult, uploadSourceMaps };
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/upload-sourcemaps.ts
|
|
32
|
+
var upload_sourcemaps_exports = {};
|
|
33
|
+
__export(upload_sourcemaps_exports, {
|
|
34
|
+
uploadSourceMaps: () => uploadSourceMaps
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(upload_sourcemaps_exports);
|
|
37
|
+
var fs = __toESM(require("fs"));
|
|
38
|
+
var path = __toESM(require("path"));
|
|
39
|
+
var DEFAULT_ENDPOINT = "https://app.clienwork.com/api/source-maps";
|
|
40
|
+
function matchGlob(pattern, filePath) {
|
|
41
|
+
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
42
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
43
|
+
return regex.test(filePath);
|
|
44
|
+
}
|
|
45
|
+
function findFiles(patterns, basePath = process.cwd()) {
|
|
46
|
+
const results = [];
|
|
47
|
+
function walk(dir, baseDir) {
|
|
48
|
+
try {
|
|
49
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const fullPath = path.join(dir, entry.name);
|
|
52
|
+
const relativePath = path.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
if (entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
55
|
+
walk(fullPath, baseDir);
|
|
56
|
+
}
|
|
57
|
+
} else if (entry.isFile() && entry.name.endsWith(".map")) {
|
|
58
|
+
for (const pattern of patterns) {
|
|
59
|
+
if (matchGlob(pattern, relativePath)) {
|
|
60
|
+
results.push(fullPath);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
walk(basePath, basePath);
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
async function uploadSourceMaps(options) {
|
|
73
|
+
const { apiToken, release, include, endpoint = DEFAULT_ENDPOINT, dryRun = false } = options;
|
|
74
|
+
console.log(`[Clienwork] Uploading source maps for release: ${release}`);
|
|
75
|
+
const files = findFiles(include);
|
|
76
|
+
if (files.length === 0) {
|
|
77
|
+
console.log("[Clienwork] No source map files found");
|
|
78
|
+
return { success: true, uploaded: [] };
|
|
79
|
+
}
|
|
80
|
+
console.log(`[Clienwork] Found ${files.length} source map files`);
|
|
81
|
+
if (dryRun) {
|
|
82
|
+
console.log("[Clienwork] Dry run mode - files would be uploaded:");
|
|
83
|
+
files.forEach((f) => console.log(` - ${path.relative(process.cwd(), f)}`));
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
uploaded: files.map((f) => ({
|
|
87
|
+
filename: path.basename(f),
|
|
88
|
+
release,
|
|
89
|
+
size: fs.statSync(f).size
|
|
90
|
+
}))
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const boundary = "----ClienworkBoundary" + Math.random().toString(36).substring(2);
|
|
94
|
+
const parts = [];
|
|
95
|
+
parts.push(
|
|
96
|
+
Buffer.from(
|
|
97
|
+
`--${boundary}\r
|
|
98
|
+
Content-Disposition: form-data; name="release"\r
|
|
99
|
+
\r
|
|
100
|
+
${release}\r
|
|
101
|
+
`
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
const filename = path.basename(file);
|
|
106
|
+
const content = fs.readFileSync(file);
|
|
107
|
+
parts.push(
|
|
108
|
+
Buffer.from(
|
|
109
|
+
`--${boundary}\r
|
|
110
|
+
Content-Disposition: form-data; name="files"; filename="${filename}"\r
|
|
111
|
+
Content-Type: application/json\r
|
|
112
|
+
\r
|
|
113
|
+
`
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
parts.push(content);
|
|
117
|
+
parts.push(Buffer.from("\r\n"));
|
|
118
|
+
}
|
|
119
|
+
parts.push(Buffer.from(`--${boundary}--\r
|
|
120
|
+
`));
|
|
121
|
+
const body = Buffer.concat(parts);
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetch(endpoint, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: {
|
|
126
|
+
Authorization: `Bearer ${apiToken}`,
|
|
127
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`
|
|
128
|
+
},
|
|
129
|
+
body
|
|
130
|
+
});
|
|
131
|
+
const result = await response.json();
|
|
132
|
+
if (result.success) {
|
|
133
|
+
console.log(`[Clienwork] Uploaded ${result.uploaded.length} source maps`);
|
|
134
|
+
result.uploaded.forEach((f) => console.log(` \u2713 ${f.filename} (${formatBytes(f.size)})`));
|
|
135
|
+
if (result.errors && result.errors.length > 0) {
|
|
136
|
+
console.warn("[Clienwork] Some files failed:");
|
|
137
|
+
result.errors.forEach((e) => console.warn(` \u2717 ${e.filename}: ${e.error}`));
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
} else {
|
|
141
|
+
throw new Error(result.error || "Upload failed");
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error("[Clienwork] Upload failed:", error);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function formatBytes(bytes) {
|
|
149
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
150
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
151
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
152
|
+
}
|
|
153
|
+
function parseArgs(args) {
|
|
154
|
+
const result = {
|
|
155
|
+
include: [],
|
|
156
|
+
dryRun: false
|
|
157
|
+
};
|
|
158
|
+
for (let i = 0; i < args.length; i++) {
|
|
159
|
+
const arg = args[i];
|
|
160
|
+
if (arg === "--release" || arg === "-r") {
|
|
161
|
+
result.release = args[++i];
|
|
162
|
+
} else if (arg === "--include" || arg === "-i") {
|
|
163
|
+
result.include.push(args[++i]);
|
|
164
|
+
} else if (arg === "--dry-run") {
|
|
165
|
+
result.dryRun = true;
|
|
166
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
167
|
+
console.log(`
|
|
168
|
+
Clienwork Source Maps Upload CLI
|
|
169
|
+
|
|
170
|
+
Usage:
|
|
171
|
+
clienwork-upload-sourcemaps --release <version> --include <glob>
|
|
172
|
+
|
|
173
|
+
Options:
|
|
174
|
+
-r, --release <version> Release version (required)
|
|
175
|
+
-i, --include <glob> Glob pattern for source map files (can be used multiple times)
|
|
176
|
+
--dry-run Show what would be uploaded without uploading
|
|
177
|
+
-h, --help Show this help message
|
|
178
|
+
|
|
179
|
+
Environment Variables:
|
|
180
|
+
CLIENWORK_ERROR_TOKEN API token for authentication (required)
|
|
181
|
+
CLIENWORK_RELEASE Default release version if --release not specified
|
|
182
|
+
CLIENWORK_ENDPOINT Custom API endpoint (optional)
|
|
183
|
+
|
|
184
|
+
Examples:
|
|
185
|
+
clienwork-upload-sourcemaps --release v1.0.0 --include ".next/**/*.map"
|
|
186
|
+
clienwork-upload-sourcemaps --release \${VERCEL_GIT_COMMIT_SHA} --include "dist/**/*.map"
|
|
187
|
+
`);
|
|
188
|
+
process.exit(0);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (result.include.length === 0) {
|
|
192
|
+
result.include = [".next/**/*.map", "dist/**/*.map", "build/**/*.map"];
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
async function main() {
|
|
197
|
+
const args = parseArgs(process.argv.slice(2));
|
|
198
|
+
const apiToken = process.env.CLIENWORK_ERROR_TOKEN;
|
|
199
|
+
if (!apiToken) {
|
|
200
|
+
console.error("[Clienwork] Error: CLIENWORK_ERROR_TOKEN environment variable is required");
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
const release = args.release || process.env.CLIENWORK_RELEASE;
|
|
204
|
+
if (!release) {
|
|
205
|
+
console.error("[Clienwork] Error: --release option or CLIENWORK_RELEASE environment variable is required");
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
await uploadSourceMaps({
|
|
210
|
+
apiToken,
|
|
211
|
+
release,
|
|
212
|
+
include: args.include,
|
|
213
|
+
endpoint: process.env.CLIENWORK_ENDPOINT,
|
|
214
|
+
dryRun: args.dryRun
|
|
215
|
+
});
|
|
216
|
+
process.exit(0);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (require.main === module) {
|
|
222
|
+
main();
|
|
223
|
+
}
|
|
224
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
225
|
+
0 && (module.exports = {
|
|
226
|
+
uploadSourceMaps
|
|
227
|
+
});
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// src/upload-sourcemaps.ts
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
var DEFAULT_ENDPOINT = "https://app.clienwork.com/api/source-maps";
|
|
13
|
+
function matchGlob(pattern, filePath) {
|
|
14
|
+
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
15
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
16
|
+
return regex.test(filePath);
|
|
17
|
+
}
|
|
18
|
+
function findFiles(patterns, basePath = process.cwd()) {
|
|
19
|
+
const results = [];
|
|
20
|
+
function walk(dir, baseDir) {
|
|
21
|
+
try {
|
|
22
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const fullPath = path.join(dir, entry.name);
|
|
25
|
+
const relativePath = path.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
26
|
+
if (entry.isDirectory()) {
|
|
27
|
+
if (entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
28
|
+
walk(fullPath, baseDir);
|
|
29
|
+
}
|
|
30
|
+
} else if (entry.isFile() && entry.name.endsWith(".map")) {
|
|
31
|
+
for (const pattern of patterns) {
|
|
32
|
+
if (matchGlob(pattern, relativePath)) {
|
|
33
|
+
results.push(fullPath);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
walk(basePath, basePath);
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
async function uploadSourceMaps(options) {
|
|
46
|
+
const { apiToken, release, include, endpoint = DEFAULT_ENDPOINT, dryRun = false } = options;
|
|
47
|
+
console.log(`[Clienwork] Uploading source maps for release: ${release}`);
|
|
48
|
+
const files = findFiles(include);
|
|
49
|
+
if (files.length === 0) {
|
|
50
|
+
console.log("[Clienwork] No source map files found");
|
|
51
|
+
return { success: true, uploaded: [] };
|
|
52
|
+
}
|
|
53
|
+
console.log(`[Clienwork] Found ${files.length} source map files`);
|
|
54
|
+
if (dryRun) {
|
|
55
|
+
console.log("[Clienwork] Dry run mode - files would be uploaded:");
|
|
56
|
+
files.forEach((f) => console.log(` - ${path.relative(process.cwd(), f)}`));
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
uploaded: files.map((f) => ({
|
|
60
|
+
filename: path.basename(f),
|
|
61
|
+
release,
|
|
62
|
+
size: fs.statSync(f).size
|
|
63
|
+
}))
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const boundary = "----ClienworkBoundary" + Math.random().toString(36).substring(2);
|
|
67
|
+
const parts = [];
|
|
68
|
+
parts.push(
|
|
69
|
+
Buffer.from(
|
|
70
|
+
`--${boundary}\r
|
|
71
|
+
Content-Disposition: form-data; name="release"\r
|
|
72
|
+
\r
|
|
73
|
+
${release}\r
|
|
74
|
+
`
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
const filename = path.basename(file);
|
|
79
|
+
const content = fs.readFileSync(file);
|
|
80
|
+
parts.push(
|
|
81
|
+
Buffer.from(
|
|
82
|
+
`--${boundary}\r
|
|
83
|
+
Content-Disposition: form-data; name="files"; filename="${filename}"\r
|
|
84
|
+
Content-Type: application/json\r
|
|
85
|
+
\r
|
|
86
|
+
`
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
parts.push(content);
|
|
90
|
+
parts.push(Buffer.from("\r\n"));
|
|
91
|
+
}
|
|
92
|
+
parts.push(Buffer.from(`--${boundary}--\r
|
|
93
|
+
`));
|
|
94
|
+
const body = Buffer.concat(parts);
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetch(endpoint, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
Authorization: `Bearer ${apiToken}`,
|
|
100
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`
|
|
101
|
+
},
|
|
102
|
+
body
|
|
103
|
+
});
|
|
104
|
+
const result = await response.json();
|
|
105
|
+
if (result.success) {
|
|
106
|
+
console.log(`[Clienwork] Uploaded ${result.uploaded.length} source maps`);
|
|
107
|
+
result.uploaded.forEach((f) => console.log(` \u2713 ${f.filename} (${formatBytes(f.size)})`));
|
|
108
|
+
if (result.errors && result.errors.length > 0) {
|
|
109
|
+
console.warn("[Clienwork] Some files failed:");
|
|
110
|
+
result.errors.forEach((e) => console.warn(` \u2717 ${e.filename}: ${e.error}`));
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
} else {
|
|
114
|
+
throw new Error(result.error || "Upload failed");
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error("[Clienwork] Upload failed:", error);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function formatBytes(bytes) {
|
|
122
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
123
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
124
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
125
|
+
}
|
|
126
|
+
function parseArgs(args) {
|
|
127
|
+
const result = {
|
|
128
|
+
include: [],
|
|
129
|
+
dryRun: false
|
|
130
|
+
};
|
|
131
|
+
for (let i = 0; i < args.length; i++) {
|
|
132
|
+
const arg = args[i];
|
|
133
|
+
if (arg === "--release" || arg === "-r") {
|
|
134
|
+
result.release = args[++i];
|
|
135
|
+
} else if (arg === "--include" || arg === "-i") {
|
|
136
|
+
result.include.push(args[++i]);
|
|
137
|
+
} else if (arg === "--dry-run") {
|
|
138
|
+
result.dryRun = true;
|
|
139
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
140
|
+
console.log(`
|
|
141
|
+
Clienwork Source Maps Upload CLI
|
|
142
|
+
|
|
143
|
+
Usage:
|
|
144
|
+
clienwork-upload-sourcemaps --release <version> --include <glob>
|
|
145
|
+
|
|
146
|
+
Options:
|
|
147
|
+
-r, --release <version> Release version (required)
|
|
148
|
+
-i, --include <glob> Glob pattern for source map files (can be used multiple times)
|
|
149
|
+
--dry-run Show what would be uploaded without uploading
|
|
150
|
+
-h, --help Show this help message
|
|
151
|
+
|
|
152
|
+
Environment Variables:
|
|
153
|
+
CLIENWORK_ERROR_TOKEN API token for authentication (required)
|
|
154
|
+
CLIENWORK_RELEASE Default release version if --release not specified
|
|
155
|
+
CLIENWORK_ENDPOINT Custom API endpoint (optional)
|
|
156
|
+
|
|
157
|
+
Examples:
|
|
158
|
+
clienwork-upload-sourcemaps --release v1.0.0 --include ".next/**/*.map"
|
|
159
|
+
clienwork-upload-sourcemaps --release \${VERCEL_GIT_COMMIT_SHA} --include "dist/**/*.map"
|
|
160
|
+
`);
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (result.include.length === 0) {
|
|
165
|
+
result.include = [".next/**/*.map", "dist/**/*.map", "build/**/*.map"];
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
async function main() {
|
|
170
|
+
const args = parseArgs(process.argv.slice(2));
|
|
171
|
+
const apiToken = process.env.CLIENWORK_ERROR_TOKEN;
|
|
172
|
+
if (!apiToken) {
|
|
173
|
+
console.error("[Clienwork] Error: CLIENWORK_ERROR_TOKEN environment variable is required");
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
const release = args.release || process.env.CLIENWORK_RELEASE;
|
|
177
|
+
if (!release) {
|
|
178
|
+
console.error("[Clienwork] Error: --release option or CLIENWORK_RELEASE environment variable is required");
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
await uploadSourceMaps({
|
|
183
|
+
apiToken,
|
|
184
|
+
release,
|
|
185
|
+
include: args.include,
|
|
186
|
+
endpoint: process.env.CLIENWORK_ENDPOINT,
|
|
187
|
+
dryRun: args.dryRun
|
|
188
|
+
});
|
|
189
|
+
process.exit(0);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (__require.main === module) {
|
|
195
|
+
main();
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
uploadSourceMaps
|
|
199
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clienwork/errors",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Error collection SDK for Clienwork",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"clienwork-upload-sourcemaps": "./dist/upload-sourcemaps.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
@@ -15,14 +18,19 @@
|
|
|
15
18
|
"types": "./dist/react.d.ts",
|
|
16
19
|
"import": "./dist/react.mjs",
|
|
17
20
|
"require": "./dist/react.js"
|
|
21
|
+
},
|
|
22
|
+
"./upload-sourcemaps": {
|
|
23
|
+
"types": "./dist/upload-sourcemaps.d.ts",
|
|
24
|
+
"import": "./dist/upload-sourcemaps.mjs",
|
|
25
|
+
"require": "./dist/upload-sourcemaps.js"
|
|
18
26
|
}
|
|
19
27
|
},
|
|
20
28
|
"files": [
|
|
21
29
|
"dist"
|
|
22
30
|
],
|
|
23
31
|
"scripts": {
|
|
24
|
-
"build": "tsup src/index.ts src/react.ts --format cjs,esm --dts --
|
|
25
|
-
"dev": "tsup src/index.ts src/react.ts --format cjs,esm --dts --watch",
|
|
32
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean && tsup src/react.ts --format cjs,esm --dts && tsup src/upload-sourcemaps.ts --format cjs,esm --dts",
|
|
33
|
+
"dev": "tsup src/index.ts src/react.ts src/upload-sourcemaps.ts --format cjs,esm --dts --watch",
|
|
26
34
|
"prepublishOnly": "npm run build"
|
|
27
35
|
},
|
|
28
36
|
"peerDependencies": {
|
|
@@ -34,6 +42,7 @@
|
|
|
34
42
|
}
|
|
35
43
|
},
|
|
36
44
|
"devDependencies": {
|
|
45
|
+
"@types/node": "^20.0.0",
|
|
37
46
|
"react": "^18.0.0",
|
|
38
47
|
"tsup": "^8.0.0",
|
|
39
48
|
"typescript": "^5.0.0"
|
|
@@ -45,8 +54,13 @@
|
|
|
45
54
|
"clienwork"
|
|
46
55
|
],
|
|
47
56
|
"license": "MIT",
|
|
57
|
+
"homepage": "https://clienwork.com",
|
|
48
58
|
"repository": {
|
|
49
59
|
"type": "git",
|
|
50
60
|
"url": "https://github.com/clienwork/clienwork"
|
|
51
|
-
}
|
|
61
|
+
},
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/clienwork/clienwork/issues"
|
|
64
|
+
},
|
|
65
|
+
"author": "Clienwork <support@clienwork.com>"
|
|
52
66
|
}
|
package/dist/chunk-HLDC7IDZ.mjs
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// src/reporter.ts
|
|
2
|
-
var DEFAULT_ENDPOINT = "https://app.clienwork.com/api/errors";
|
|
3
|
-
var ErrorReporter = class {
|
|
4
|
-
constructor(config) {
|
|
5
|
-
this.userId = null;
|
|
6
|
-
this.metadata = {};
|
|
7
|
-
this.config = {
|
|
8
|
-
apiToken: config.apiToken,
|
|
9
|
-
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
10
|
-
source: config.source || "frontend",
|
|
11
|
-
defaultMetadata: config.defaultMetadata || {},
|
|
12
|
-
enabled: config.enabled ?? true,
|
|
13
|
-
beforeSend: config.beforeSend
|
|
14
|
-
};
|
|
15
|
-
if (typeof window !== "undefined" && this.config.source === "frontend") {
|
|
16
|
-
this.setupGlobalHandlers();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
setupGlobalHandlers() {
|
|
20
|
-
window.addEventListener("error", (event) => {
|
|
21
|
-
this.report({
|
|
22
|
-
message: event.message,
|
|
23
|
-
stack: event.error?.stack,
|
|
24
|
-
url: event.filename,
|
|
25
|
-
metadata: {
|
|
26
|
-
lineno: event.lineno,
|
|
27
|
-
colno: event.colno
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
window.addEventListener("unhandledrejection", (event) => {
|
|
32
|
-
const error = event.reason;
|
|
33
|
-
if (error instanceof Error) {
|
|
34
|
-
this.report(error);
|
|
35
|
-
} else {
|
|
36
|
-
this.report({
|
|
37
|
-
message: String(error),
|
|
38
|
-
metadata: { type: "unhandledrejection" }
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
setUser(userId) {
|
|
44
|
-
this.userId = userId;
|
|
45
|
-
}
|
|
46
|
-
clearUser() {
|
|
47
|
-
this.userId = null;
|
|
48
|
-
}
|
|
49
|
-
setMetadata(metadata) {
|
|
50
|
-
this.metadata = { ...this.metadata, ...metadata };
|
|
51
|
-
}
|
|
52
|
-
async report(error, additionalMetadata) {
|
|
53
|
-
if (!this.config.enabled) return;
|
|
54
|
-
let reportData;
|
|
55
|
-
if (error instanceof Error) {
|
|
56
|
-
reportData = {
|
|
57
|
-
message: error.message,
|
|
58
|
-
stack: error.stack,
|
|
59
|
-
source: this.config.source
|
|
60
|
-
};
|
|
61
|
-
} else {
|
|
62
|
-
reportData = {
|
|
63
|
-
...error,
|
|
64
|
-
source: error.source || this.config.source
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
reportData.metadata = {
|
|
68
|
-
...this.config.defaultMetadata,
|
|
69
|
-
...this.metadata,
|
|
70
|
-
...reportData.metadata,
|
|
71
|
-
...additionalMetadata
|
|
72
|
-
};
|
|
73
|
-
if (this.userId) {
|
|
74
|
-
reportData.userId = this.userId;
|
|
75
|
-
}
|
|
76
|
-
if (typeof window !== "undefined") {
|
|
77
|
-
reportData.url = reportData.url || window.location.href;
|
|
78
|
-
reportData.userAgent = reportData.userAgent || navigator.userAgent;
|
|
79
|
-
}
|
|
80
|
-
if (this.config.beforeSend) {
|
|
81
|
-
const modified = this.config.beforeSend(reportData);
|
|
82
|
-
if (modified === null) return;
|
|
83
|
-
reportData = modified;
|
|
84
|
-
}
|
|
85
|
-
try {
|
|
86
|
-
await fetch(this.config.endpoint, {
|
|
87
|
-
method: "POST",
|
|
88
|
-
headers: {
|
|
89
|
-
"Content-Type": "application/json",
|
|
90
|
-
Authorization: `Bearer ${this.config.apiToken}`
|
|
91
|
-
},
|
|
92
|
-
body: JSON.stringify(reportData)
|
|
93
|
-
});
|
|
94
|
-
} catch (e) {
|
|
95
|
-
console.warn("[ClienworkErrors] Failed to report error:", e);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
var instance = null;
|
|
100
|
-
function init(config) {
|
|
101
|
-
instance = new ErrorReporter(config);
|
|
102
|
-
return instance;
|
|
103
|
-
}
|
|
104
|
-
function getReporter() {
|
|
105
|
-
return instance;
|
|
106
|
-
}
|
|
107
|
-
function report(error, metadata) {
|
|
108
|
-
if (!instance) {
|
|
109
|
-
console.warn("[ClienworkErrors] Not initialized. Call init() first.");
|
|
110
|
-
return Promise.resolve();
|
|
111
|
-
}
|
|
112
|
-
return instance.report(error, metadata);
|
|
113
|
-
}
|
|
114
|
-
function setUser(userId) {
|
|
115
|
-
instance?.setUser(userId);
|
|
116
|
-
}
|
|
117
|
-
function clearUser() {
|
|
118
|
-
instance?.clearUser();
|
|
119
|
-
}
|
|
120
|
-
function createBackendReporter(config) {
|
|
121
|
-
return new ErrorReporter({ ...config, source: "backend" });
|
|
122
|
-
}
|
|
123
|
-
function expressErrorHandler(reporter) {
|
|
124
|
-
return (err, req, res, next) => {
|
|
125
|
-
reporter.report(err, {
|
|
126
|
-
path: req.path,
|
|
127
|
-
method: req.method,
|
|
128
|
-
userAgent: req.headers?.["user-agent"]
|
|
129
|
-
});
|
|
130
|
-
next();
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export {
|
|
135
|
-
ErrorReporter,
|
|
136
|
-
init,
|
|
137
|
-
getReporter,
|
|
138
|
-
report,
|
|
139
|
-
setUser,
|
|
140
|
-
clearUser,
|
|
141
|
-
createBackendReporter,
|
|
142
|
-
expressErrorHandler
|
|
143
|
-
};
|