@alertsamurai/sdk-js 0.0.1
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 +191 -0
- package/dist/index.d.mts +194 -0
- package/dist/index.d.ts +194 -0
- package/dist/index.js +279 -0
- package/dist/index.mjs +248 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# @alertsamurai/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for AlertSamurai metrics and alerting.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @alertsamurai/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { AlertSamuraiClient } from "@alertsamurai/sdk";
|
|
15
|
+
|
|
16
|
+
const client = new AlertSamuraiClient({
|
|
17
|
+
dsn: "your-project-dsn",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Send a metric
|
|
21
|
+
await client.sendMetric({
|
|
22
|
+
metricType: "photo_upload_speed",
|
|
23
|
+
value: 2.5,
|
|
24
|
+
unit: "MB/s",
|
|
25
|
+
environment: "production",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Send an alert
|
|
29
|
+
await client.sendAlert({
|
|
30
|
+
message: "Database connection failed",
|
|
31
|
+
priority: "critical",
|
|
32
|
+
environment: "production",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Convenience methods
|
|
36
|
+
await client.critical("Server down!");
|
|
37
|
+
await client.error("Failed to process payment");
|
|
38
|
+
await client.warning("High memory usage");
|
|
39
|
+
await client.info("User signed in");
|
|
40
|
+
await client.debug("Cache miss for key: xyz");
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const client = new AlertSamuraiClient({
|
|
47
|
+
dsn: "your-dsn", // Required - found in project settings
|
|
48
|
+
baseUrl: "https://...", // Optional, default: api.alertsamurai.com
|
|
49
|
+
timeout: 30000, // Optional, default: 30s
|
|
50
|
+
retries: 3, // Optional, default: 3
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Metrics API
|
|
55
|
+
|
|
56
|
+
Send custom metrics for tracking and threshold-based alerting.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
await client.sendMetric({
|
|
60
|
+
metricType: "photo_upload_speed", // Required - metric name
|
|
61
|
+
value: 2.5, // Required - numeric value
|
|
62
|
+
unit: "MB/s", // Optional - unit of measurement
|
|
63
|
+
environment: "production", // Optional - environment name
|
|
64
|
+
metadata: {
|
|
65
|
+
// Optional - additional context
|
|
66
|
+
userId: "123",
|
|
67
|
+
endpoint: "/api/upload",
|
|
68
|
+
},
|
|
69
|
+
timestamp: new Date(), // Optional - defaults to now
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Threshold-Based Alerts
|
|
74
|
+
|
|
75
|
+
Configure metric alerts in your AlertSamurai dashboard. When a metric exceeds a threshold (e.g., `photo_upload_speed < 1 MB/s`), you'll receive a notification via Telegram.
|
|
76
|
+
|
|
77
|
+
## Alerts API
|
|
78
|
+
|
|
79
|
+
Send application alerts with priority levels.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
await client.sendAlert({
|
|
83
|
+
message: "Database connection failed", // Required - alert message
|
|
84
|
+
priority: "critical", // Required - critical|error|warning|info|debug
|
|
85
|
+
environment: "production", // Optional - environment name
|
|
86
|
+
data: {
|
|
87
|
+
// Optional - additional context
|
|
88
|
+
errorCode: "ECONNREFUSED",
|
|
89
|
+
retryCount: 3,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Convenience Methods
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// These methods send alerts with the specified priority
|
|
98
|
+
await client.critical("Server down!"); // priority: "critical"
|
|
99
|
+
await client.error("Failed to process payment"); // priority: "error"
|
|
100
|
+
await client.warning("High memory usage"); // priority: "warning"
|
|
101
|
+
await client.info("User signed in"); // priority: "info"
|
|
102
|
+
await client.debug("Cache miss"); // priority: "debug"
|
|
103
|
+
|
|
104
|
+
// With additional data
|
|
105
|
+
await client.error("Payment failed", {
|
|
106
|
+
orderId: "12345",
|
|
107
|
+
errorCode: "INSUFFICIENT_FUNDS",
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Error Handling
|
|
112
|
+
|
|
113
|
+
The SDK provides typed errors for different failure scenarios:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import {
|
|
117
|
+
AlertSamuraiClient,
|
|
118
|
+
AlertSamuraiError,
|
|
119
|
+
AuthenticationError,
|
|
120
|
+
ValidationError,
|
|
121
|
+
NetworkError,
|
|
122
|
+
} from "@alertsamurai/sdk";
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await client.sendMetric({ metricType: "test", value: 123 });
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error instanceof AuthenticationError) {
|
|
128
|
+
// Invalid or missing DSN (401)
|
|
129
|
+
console.error("Authentication failed:", error.message);
|
|
130
|
+
} else if (error instanceof ValidationError) {
|
|
131
|
+
// Invalid request data (400)
|
|
132
|
+
console.error("Validation failed:", error.message);
|
|
133
|
+
} else if (error instanceof NetworkError) {
|
|
134
|
+
// Network failure, timeout, etc.
|
|
135
|
+
console.error("Network error:", error.message);
|
|
136
|
+
} else if (error instanceof AlertSamuraiError) {
|
|
137
|
+
// Other API errors
|
|
138
|
+
console.error("API error:", error.message, error.statusCode);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Retry Logic
|
|
144
|
+
|
|
145
|
+
The SDK automatically retries failed requests with exponential backoff:
|
|
146
|
+
|
|
147
|
+
- Default: 3 retries
|
|
148
|
+
- Backoff: 1s, 2s, 4s (capped at 10s)
|
|
149
|
+
- **Does not retry** on authentication (401) or validation (400) errors
|
|
150
|
+
|
|
151
|
+
Configure retries:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const client = new AlertSamuraiClient({
|
|
155
|
+
dsn: "your-dsn",
|
|
156
|
+
retries: 5, // More retries
|
|
157
|
+
timeout: 60000, // Longer timeout
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## TypeScript Support
|
|
162
|
+
|
|
163
|
+
The SDK is written in TypeScript and includes full type definitions:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import type {
|
|
167
|
+
AlertSamuraiConfig,
|
|
168
|
+
SendMetricOptions,
|
|
169
|
+
SendMetricResponse,
|
|
170
|
+
SendAlertOptions,
|
|
171
|
+
SendAlertResponse,
|
|
172
|
+
AlertPriority,
|
|
173
|
+
} from "@alertsamurai/sdk";
|
|
174
|
+
|
|
175
|
+
const options: SendMetricOptions = {
|
|
176
|
+
metricType: "api_latency",
|
|
177
|
+
value: 150,
|
|
178
|
+
unit: "ms",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const priority: AlertPriority = "warning";
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Requirements
|
|
185
|
+
|
|
186
|
+
- Node.js 18+ (for native `fetch` support) or browser environment
|
|
187
|
+
- Or any environment with a global `fetch` implementation
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client configuration options
|
|
3
|
+
*/
|
|
4
|
+
interface AlertSamuraiConfig {
|
|
5
|
+
/** Project DSN (required) - found in your project settings */
|
|
6
|
+
dsn: string;
|
|
7
|
+
/** API base URL (optional) - defaults to https://api.alertsamurai.com */
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
/** Request timeout in milliseconds (optional) - defaults to 30000 */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
/** Number of retries on network failure (optional) - defaults to 3 */
|
|
12
|
+
retries?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for sending a metric
|
|
16
|
+
*/
|
|
17
|
+
interface SendMetricOptions {
|
|
18
|
+
/** Metric type/name (e.g., "photo_upload_speed", "api_latency") */
|
|
19
|
+
metricType: string;
|
|
20
|
+
/** Numeric value of the metric */
|
|
21
|
+
value: number;
|
|
22
|
+
/** Unit of measurement (e.g., "MB/s", "ms") */
|
|
23
|
+
unit?: string;
|
|
24
|
+
/** Environment name (e.g., "production", "staging") */
|
|
25
|
+
environment?: string;
|
|
26
|
+
/** Additional metadata as key-value pairs */
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
/** Timestamp - accepts Date, milliseconds, or ISO string. Defaults to now */
|
|
29
|
+
timestamp?: Date | number | string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Response from sending a metric
|
|
33
|
+
*/
|
|
34
|
+
interface SendMetricResponse {
|
|
35
|
+
/** ID of the created metric record */
|
|
36
|
+
id: string;
|
|
37
|
+
/** Whether the metric was successfully processed */
|
|
38
|
+
success: boolean;
|
|
39
|
+
/** Whether any threshold-based alerts were triggered */
|
|
40
|
+
alertTriggered?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Alert priority levels
|
|
44
|
+
*/
|
|
45
|
+
type AlertPriority = "critical" | "error" | "warning" | "info" | "debug";
|
|
46
|
+
/**
|
|
47
|
+
* Options for sending an alert
|
|
48
|
+
*/
|
|
49
|
+
interface SendAlertOptions {
|
|
50
|
+
/** Alert message/description */
|
|
51
|
+
message: string;
|
|
52
|
+
/** Priority level of the alert */
|
|
53
|
+
priority: AlertPriority;
|
|
54
|
+
/** Environment name (e.g., "production", "staging") */
|
|
55
|
+
environment?: string;
|
|
56
|
+
/** Additional context data as key-value pairs */
|
|
57
|
+
data?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Response from sending an alert
|
|
61
|
+
*/
|
|
62
|
+
interface SendAlertResponse {
|
|
63
|
+
/** ID of the created alert record */
|
|
64
|
+
id: string;
|
|
65
|
+
/** Whether the alert was successfully processed */
|
|
66
|
+
success: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* AlertSamurai SDK client for sending metrics and alerts
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const client = new AlertSamuraiClient({
|
|
75
|
+
* dsn: "your-project-dsn",
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* // Send a metric
|
|
79
|
+
* await client.sendMetric({
|
|
80
|
+
* metricType: "photo_upload_speed",
|
|
81
|
+
* value: 2.5,
|
|
82
|
+
* unit: "MB/s",
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* // Send an alert
|
|
86
|
+
* await client.critical("Database connection failed");
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare class AlertSamuraiClient {
|
|
90
|
+
private dsn;
|
|
91
|
+
private baseUrl;
|
|
92
|
+
private timeout;
|
|
93
|
+
private retries;
|
|
94
|
+
constructor(config: AlertSamuraiConfig);
|
|
95
|
+
/**
|
|
96
|
+
* Send a custom metric for tracking and threshold-based alerting
|
|
97
|
+
*
|
|
98
|
+
* @param options - Metric options
|
|
99
|
+
* @returns Response with metric ID and alert trigger status
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* await client.sendMetric({
|
|
104
|
+
* metricType: "photo_upload_speed",
|
|
105
|
+
* value: 2.5,
|
|
106
|
+
* unit: "MB/s",
|
|
107
|
+
* environment: "production",
|
|
108
|
+
* metadata: { userId: "123" },
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
sendMetric(options: SendMetricOptions): Promise<SendMetricResponse>;
|
|
113
|
+
/**
|
|
114
|
+
* Send an application alert with priority level
|
|
115
|
+
*
|
|
116
|
+
* @param options - Alert options
|
|
117
|
+
* @returns Response with alert ID
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* await client.sendAlert({
|
|
122
|
+
* message: "Database connection failed",
|
|
123
|
+
* priority: "critical",
|
|
124
|
+
* environment: "production",
|
|
125
|
+
* data: { errorCode: "ECONNREFUSED" },
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
sendAlert(options: SendAlertOptions): Promise<SendAlertResponse>;
|
|
130
|
+
/**
|
|
131
|
+
* Send a critical priority alert
|
|
132
|
+
* @param message - Alert message
|
|
133
|
+
* @param data - Optional additional context data
|
|
134
|
+
*/
|
|
135
|
+
critical(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
136
|
+
/**
|
|
137
|
+
* Send an error priority alert
|
|
138
|
+
* @param message - Alert message
|
|
139
|
+
* @param data - Optional additional context data
|
|
140
|
+
*/
|
|
141
|
+
error(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
142
|
+
/**
|
|
143
|
+
* Send a warning priority alert
|
|
144
|
+
* @param message - Alert message
|
|
145
|
+
* @param data - Optional additional context data
|
|
146
|
+
*/
|
|
147
|
+
warning(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
148
|
+
/**
|
|
149
|
+
* Send an info priority alert
|
|
150
|
+
* @param message - Alert message
|
|
151
|
+
* @param data - Optional additional context data
|
|
152
|
+
*/
|
|
153
|
+
info(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
154
|
+
/**
|
|
155
|
+
* Send a debug priority alert
|
|
156
|
+
* @param message - Alert message
|
|
157
|
+
* @param data - Optional additional context data
|
|
158
|
+
*/
|
|
159
|
+
debug(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
160
|
+
private request;
|
|
161
|
+
private doRequest;
|
|
162
|
+
private validateMetric;
|
|
163
|
+
private validateAlert;
|
|
164
|
+
private normalizeTimestamp;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Base error class for all AlertSamurai SDK errors
|
|
169
|
+
*/
|
|
170
|
+
declare class AlertSamuraiError extends Error {
|
|
171
|
+
statusCode?: number | undefined;
|
|
172
|
+
response?: unknown | undefined;
|
|
173
|
+
constructor(message: string, statusCode?: number | undefined, response?: unknown | undefined);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Thrown when authentication fails (invalid or missing DSN)
|
|
177
|
+
*/
|
|
178
|
+
declare class AuthenticationError extends AlertSamuraiError {
|
|
179
|
+
constructor(message?: string);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Thrown when request validation fails
|
|
183
|
+
*/
|
|
184
|
+
declare class ValidationError extends AlertSamuraiError {
|
|
185
|
+
constructor(message: string);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Thrown when network request fails (timeout, connection error, etc.)
|
|
189
|
+
*/
|
|
190
|
+
declare class NetworkError extends AlertSamuraiError {
|
|
191
|
+
constructor(message?: string);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { type AlertPriority, AlertSamuraiClient, type AlertSamuraiConfig, AlertSamuraiError, AuthenticationError, NetworkError, type SendAlertOptions, type SendAlertResponse, type SendMetricOptions, type SendMetricResponse, ValidationError };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client configuration options
|
|
3
|
+
*/
|
|
4
|
+
interface AlertSamuraiConfig {
|
|
5
|
+
/** Project DSN (required) - found in your project settings */
|
|
6
|
+
dsn: string;
|
|
7
|
+
/** API base URL (optional) - defaults to https://api.alertsamurai.com */
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
/** Request timeout in milliseconds (optional) - defaults to 30000 */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
/** Number of retries on network failure (optional) - defaults to 3 */
|
|
12
|
+
retries?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for sending a metric
|
|
16
|
+
*/
|
|
17
|
+
interface SendMetricOptions {
|
|
18
|
+
/** Metric type/name (e.g., "photo_upload_speed", "api_latency") */
|
|
19
|
+
metricType: string;
|
|
20
|
+
/** Numeric value of the metric */
|
|
21
|
+
value: number;
|
|
22
|
+
/** Unit of measurement (e.g., "MB/s", "ms") */
|
|
23
|
+
unit?: string;
|
|
24
|
+
/** Environment name (e.g., "production", "staging") */
|
|
25
|
+
environment?: string;
|
|
26
|
+
/** Additional metadata as key-value pairs */
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
/** Timestamp - accepts Date, milliseconds, or ISO string. Defaults to now */
|
|
29
|
+
timestamp?: Date | number | string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Response from sending a metric
|
|
33
|
+
*/
|
|
34
|
+
interface SendMetricResponse {
|
|
35
|
+
/** ID of the created metric record */
|
|
36
|
+
id: string;
|
|
37
|
+
/** Whether the metric was successfully processed */
|
|
38
|
+
success: boolean;
|
|
39
|
+
/** Whether any threshold-based alerts were triggered */
|
|
40
|
+
alertTriggered?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Alert priority levels
|
|
44
|
+
*/
|
|
45
|
+
type AlertPriority = "critical" | "error" | "warning" | "info" | "debug";
|
|
46
|
+
/**
|
|
47
|
+
* Options for sending an alert
|
|
48
|
+
*/
|
|
49
|
+
interface SendAlertOptions {
|
|
50
|
+
/** Alert message/description */
|
|
51
|
+
message: string;
|
|
52
|
+
/** Priority level of the alert */
|
|
53
|
+
priority: AlertPriority;
|
|
54
|
+
/** Environment name (e.g., "production", "staging") */
|
|
55
|
+
environment?: string;
|
|
56
|
+
/** Additional context data as key-value pairs */
|
|
57
|
+
data?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Response from sending an alert
|
|
61
|
+
*/
|
|
62
|
+
interface SendAlertResponse {
|
|
63
|
+
/** ID of the created alert record */
|
|
64
|
+
id: string;
|
|
65
|
+
/** Whether the alert was successfully processed */
|
|
66
|
+
success: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* AlertSamurai SDK client for sending metrics and alerts
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const client = new AlertSamuraiClient({
|
|
75
|
+
* dsn: "your-project-dsn",
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* // Send a metric
|
|
79
|
+
* await client.sendMetric({
|
|
80
|
+
* metricType: "photo_upload_speed",
|
|
81
|
+
* value: 2.5,
|
|
82
|
+
* unit: "MB/s",
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* // Send an alert
|
|
86
|
+
* await client.critical("Database connection failed");
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare class AlertSamuraiClient {
|
|
90
|
+
private dsn;
|
|
91
|
+
private baseUrl;
|
|
92
|
+
private timeout;
|
|
93
|
+
private retries;
|
|
94
|
+
constructor(config: AlertSamuraiConfig);
|
|
95
|
+
/**
|
|
96
|
+
* Send a custom metric for tracking and threshold-based alerting
|
|
97
|
+
*
|
|
98
|
+
* @param options - Metric options
|
|
99
|
+
* @returns Response with metric ID and alert trigger status
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* await client.sendMetric({
|
|
104
|
+
* metricType: "photo_upload_speed",
|
|
105
|
+
* value: 2.5,
|
|
106
|
+
* unit: "MB/s",
|
|
107
|
+
* environment: "production",
|
|
108
|
+
* metadata: { userId: "123" },
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
sendMetric(options: SendMetricOptions): Promise<SendMetricResponse>;
|
|
113
|
+
/**
|
|
114
|
+
* Send an application alert with priority level
|
|
115
|
+
*
|
|
116
|
+
* @param options - Alert options
|
|
117
|
+
* @returns Response with alert ID
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* await client.sendAlert({
|
|
122
|
+
* message: "Database connection failed",
|
|
123
|
+
* priority: "critical",
|
|
124
|
+
* environment: "production",
|
|
125
|
+
* data: { errorCode: "ECONNREFUSED" },
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
sendAlert(options: SendAlertOptions): Promise<SendAlertResponse>;
|
|
130
|
+
/**
|
|
131
|
+
* Send a critical priority alert
|
|
132
|
+
* @param message - Alert message
|
|
133
|
+
* @param data - Optional additional context data
|
|
134
|
+
*/
|
|
135
|
+
critical(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
136
|
+
/**
|
|
137
|
+
* Send an error priority alert
|
|
138
|
+
* @param message - Alert message
|
|
139
|
+
* @param data - Optional additional context data
|
|
140
|
+
*/
|
|
141
|
+
error(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
142
|
+
/**
|
|
143
|
+
* Send a warning priority alert
|
|
144
|
+
* @param message - Alert message
|
|
145
|
+
* @param data - Optional additional context data
|
|
146
|
+
*/
|
|
147
|
+
warning(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
148
|
+
/**
|
|
149
|
+
* Send an info priority alert
|
|
150
|
+
* @param message - Alert message
|
|
151
|
+
* @param data - Optional additional context data
|
|
152
|
+
*/
|
|
153
|
+
info(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
154
|
+
/**
|
|
155
|
+
* Send a debug priority alert
|
|
156
|
+
* @param message - Alert message
|
|
157
|
+
* @param data - Optional additional context data
|
|
158
|
+
*/
|
|
159
|
+
debug(message: string, data?: Record<string, unknown>): Promise<SendAlertResponse>;
|
|
160
|
+
private request;
|
|
161
|
+
private doRequest;
|
|
162
|
+
private validateMetric;
|
|
163
|
+
private validateAlert;
|
|
164
|
+
private normalizeTimestamp;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Base error class for all AlertSamurai SDK errors
|
|
169
|
+
*/
|
|
170
|
+
declare class AlertSamuraiError extends Error {
|
|
171
|
+
statusCode?: number | undefined;
|
|
172
|
+
response?: unknown | undefined;
|
|
173
|
+
constructor(message: string, statusCode?: number | undefined, response?: unknown | undefined);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Thrown when authentication fails (invalid or missing DSN)
|
|
177
|
+
*/
|
|
178
|
+
declare class AuthenticationError extends AlertSamuraiError {
|
|
179
|
+
constructor(message?: string);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Thrown when request validation fails
|
|
183
|
+
*/
|
|
184
|
+
declare class ValidationError extends AlertSamuraiError {
|
|
185
|
+
constructor(message: string);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Thrown when network request fails (timeout, connection error, etc.)
|
|
189
|
+
*/
|
|
190
|
+
declare class NetworkError extends AlertSamuraiError {
|
|
191
|
+
constructor(message?: string);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { type AlertPriority, AlertSamuraiClient, type AlertSamuraiConfig, AlertSamuraiError, AuthenticationError, NetworkError, type SendAlertOptions, type SendAlertResponse, type SendMetricOptions, type SendMetricResponse, ValidationError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AlertSamuraiClient: () => AlertSamuraiClient,
|
|
24
|
+
AlertSamuraiError: () => AlertSamuraiError,
|
|
25
|
+
AuthenticationError: () => AuthenticationError,
|
|
26
|
+
NetworkError: () => NetworkError,
|
|
27
|
+
ValidationError: () => ValidationError
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/errors.ts
|
|
32
|
+
var AlertSamuraiError = class extends Error {
|
|
33
|
+
constructor(message, statusCode, response) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.statusCode = statusCode;
|
|
36
|
+
this.response = response;
|
|
37
|
+
this.name = "AlertSamuraiError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var AuthenticationError = class extends AlertSamuraiError {
|
|
41
|
+
constructor(message = "Invalid or missing DSN") {
|
|
42
|
+
super(message, 401);
|
|
43
|
+
this.name = "AuthenticationError";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var ValidationError = class extends AlertSamuraiError {
|
|
47
|
+
constructor(message) {
|
|
48
|
+
super(message, 400);
|
|
49
|
+
this.name = "ValidationError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var NetworkError = class extends AlertSamuraiError {
|
|
53
|
+
constructor(message = "Network request failed") {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "NetworkError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/utils/retry.ts
|
|
60
|
+
async function withRetry(fn, options) {
|
|
61
|
+
let lastError;
|
|
62
|
+
for (let attempt = 0; attempt <= options.retries; attempt++) {
|
|
63
|
+
try {
|
|
64
|
+
return await fn();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
lastError = error;
|
|
67
|
+
if (error instanceof Error && (error.name === "AuthenticationError" || error.name === "ValidationError")) {
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
if (attempt < options.retries) {
|
|
71
|
+
const delay = Math.min(
|
|
72
|
+
options.baseDelay * Math.pow(2, attempt),
|
|
73
|
+
options.maxDelay
|
|
74
|
+
);
|
|
75
|
+
await sleep(delay);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw lastError;
|
|
80
|
+
}
|
|
81
|
+
function sleep(ms) {
|
|
82
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/client.ts
|
|
86
|
+
var DEFAULT_BASE_URL = "https://api.alertsamurai.com";
|
|
87
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
88
|
+
var DEFAULT_RETRIES = 3;
|
|
89
|
+
var AlertSamuraiClient = class {
|
|
90
|
+
constructor(config) {
|
|
91
|
+
if (!config.dsn) {
|
|
92
|
+
throw new ValidationError("DSN is required");
|
|
93
|
+
}
|
|
94
|
+
this.dsn = config.dsn;
|
|
95
|
+
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
96
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
97
|
+
this.retries = config.retries ?? DEFAULT_RETRIES;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Send a custom metric for tracking and threshold-based alerting
|
|
101
|
+
*
|
|
102
|
+
* @param options - Metric options
|
|
103
|
+
* @returns Response with metric ID and alert trigger status
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* await client.sendMetric({
|
|
108
|
+
* metricType: "photo_upload_speed",
|
|
109
|
+
* value: 2.5,
|
|
110
|
+
* unit: "MB/s",
|
|
111
|
+
* environment: "production",
|
|
112
|
+
* metadata: { userId: "123" },
|
|
113
|
+
* });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
async sendMetric(options) {
|
|
117
|
+
this.validateMetric(options);
|
|
118
|
+
const body = {
|
|
119
|
+
metricType: options.metricType,
|
|
120
|
+
value: options.value,
|
|
121
|
+
unit: options.unit,
|
|
122
|
+
environment: options.environment,
|
|
123
|
+
metadata: options.metadata,
|
|
124
|
+
timestamp: this.normalizeTimestamp(options.timestamp)
|
|
125
|
+
};
|
|
126
|
+
return this.request("/api/metrics", body);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Send an application alert with priority level
|
|
130
|
+
*
|
|
131
|
+
* @param options - Alert options
|
|
132
|
+
* @returns Response with alert ID
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* await client.sendAlert({
|
|
137
|
+
* message: "Database connection failed",
|
|
138
|
+
* priority: "critical",
|
|
139
|
+
* environment: "production",
|
|
140
|
+
* data: { errorCode: "ECONNREFUSED" },
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
async sendAlert(options) {
|
|
145
|
+
this.validateAlert(options);
|
|
146
|
+
return this.request("/api/alerts", options);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Send a critical priority alert
|
|
150
|
+
* @param message - Alert message
|
|
151
|
+
* @param data - Optional additional context data
|
|
152
|
+
*/
|
|
153
|
+
async critical(message, data) {
|
|
154
|
+
return this.sendAlert({ message, priority: "critical", data });
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Send an error priority alert
|
|
158
|
+
* @param message - Alert message
|
|
159
|
+
* @param data - Optional additional context data
|
|
160
|
+
*/
|
|
161
|
+
async error(message, data) {
|
|
162
|
+
return this.sendAlert({ message, priority: "error", data });
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Send a warning priority alert
|
|
166
|
+
* @param message - Alert message
|
|
167
|
+
* @param data - Optional additional context data
|
|
168
|
+
*/
|
|
169
|
+
async warning(message, data) {
|
|
170
|
+
return this.sendAlert({ message, priority: "warning", data });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Send an info priority alert
|
|
174
|
+
* @param message - Alert message
|
|
175
|
+
* @param data - Optional additional context data
|
|
176
|
+
*/
|
|
177
|
+
async info(message, data) {
|
|
178
|
+
return this.sendAlert({ message, priority: "info", data });
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Send a debug priority alert
|
|
182
|
+
* @param message - Alert message
|
|
183
|
+
* @param data - Optional additional context data
|
|
184
|
+
*/
|
|
185
|
+
async debug(message, data) {
|
|
186
|
+
return this.sendAlert({ message, priority: "debug", data });
|
|
187
|
+
}
|
|
188
|
+
async request(path, body) {
|
|
189
|
+
return withRetry(() => this.doRequest(path, body), {
|
|
190
|
+
retries: this.retries,
|
|
191
|
+
baseDelay: 1e3,
|
|
192
|
+
maxDelay: 1e4
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async doRequest(path, body) {
|
|
196
|
+
const controller = new AbortController();
|
|
197
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
198
|
+
try {
|
|
199
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
200
|
+
method: "POST",
|
|
201
|
+
headers: {
|
|
202
|
+
"Content-Type": "application/json",
|
|
203
|
+
Authorization: `Bearer ${this.dsn}`
|
|
204
|
+
},
|
|
205
|
+
body: JSON.stringify(body),
|
|
206
|
+
signal: controller.signal
|
|
207
|
+
});
|
|
208
|
+
clearTimeout(timeoutId);
|
|
209
|
+
if (response.status === 401) {
|
|
210
|
+
throw new AuthenticationError();
|
|
211
|
+
}
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
214
|
+
throw new AlertSamuraiError(
|
|
215
|
+
errorBody.message || `Request failed with status ${response.status}`,
|
|
216
|
+
response.status,
|
|
217
|
+
errorBody
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return response.json();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
clearTimeout(timeoutId);
|
|
223
|
+
if (error instanceof AlertSamuraiError) {
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
227
|
+
throw new NetworkError("Request timed out");
|
|
228
|
+
}
|
|
229
|
+
throw new NetworkError(
|
|
230
|
+
error instanceof Error ? error.message : "Unknown network error"
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
validateMetric(options) {
|
|
235
|
+
if (!options.metricType || typeof options.metricType !== "string") {
|
|
236
|
+
throw new ValidationError("metricType is required and must be a string");
|
|
237
|
+
}
|
|
238
|
+
if (options.metricType.length > 100) {
|
|
239
|
+
throw new ValidationError("metricType must be 100 characters or less");
|
|
240
|
+
}
|
|
241
|
+
if (typeof options.value !== "number" || isNaN(options.value)) {
|
|
242
|
+
throw new ValidationError("value is required and must be a number");
|
|
243
|
+
}
|
|
244
|
+
if (options.unit !== void 0 && typeof options.unit !== "string") {
|
|
245
|
+
throw new ValidationError("unit must be a string");
|
|
246
|
+
}
|
|
247
|
+
if (options.unit && options.unit.length > 50) {
|
|
248
|
+
throw new ValidationError("unit must be 50 characters or less");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
validateAlert(options) {
|
|
252
|
+
if (!options.message || typeof options.message !== "string") {
|
|
253
|
+
throw new ValidationError("message is required and must be a string");
|
|
254
|
+
}
|
|
255
|
+
if (options.message.length > 4e3) {
|
|
256
|
+
throw new ValidationError("message must be 4000 characters or less");
|
|
257
|
+
}
|
|
258
|
+
const validPriorities = ["critical", "error", "warning", "info", "debug"];
|
|
259
|
+
if (!validPriorities.includes(options.priority)) {
|
|
260
|
+
throw new ValidationError(
|
|
261
|
+
`priority must be one of: ${validPriorities.join(", ")}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
normalizeTimestamp(timestamp) {
|
|
266
|
+
if (!timestamp) return void 0;
|
|
267
|
+
if (timestamp instanceof Date) return timestamp.toISOString();
|
|
268
|
+
if (typeof timestamp === "number") return new Date(timestamp).toISOString();
|
|
269
|
+
return timestamp;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
273
|
+
0 && (module.exports = {
|
|
274
|
+
AlertSamuraiClient,
|
|
275
|
+
AlertSamuraiError,
|
|
276
|
+
AuthenticationError,
|
|
277
|
+
NetworkError,
|
|
278
|
+
ValidationError
|
|
279
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var AlertSamuraiError = class extends Error {
|
|
3
|
+
constructor(message, statusCode, response) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.statusCode = statusCode;
|
|
6
|
+
this.response = response;
|
|
7
|
+
this.name = "AlertSamuraiError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var AuthenticationError = class extends AlertSamuraiError {
|
|
11
|
+
constructor(message = "Invalid or missing DSN") {
|
|
12
|
+
super(message, 401);
|
|
13
|
+
this.name = "AuthenticationError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var ValidationError = class extends AlertSamuraiError {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message, 400);
|
|
19
|
+
this.name = "ValidationError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var NetworkError = class extends AlertSamuraiError {
|
|
23
|
+
constructor(message = "Network request failed") {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "NetworkError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/utils/retry.ts
|
|
30
|
+
async function withRetry(fn, options) {
|
|
31
|
+
let lastError;
|
|
32
|
+
for (let attempt = 0; attempt <= options.retries; attempt++) {
|
|
33
|
+
try {
|
|
34
|
+
return await fn();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
lastError = error;
|
|
37
|
+
if (error instanceof Error && (error.name === "AuthenticationError" || error.name === "ValidationError")) {
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
if (attempt < options.retries) {
|
|
41
|
+
const delay = Math.min(
|
|
42
|
+
options.baseDelay * Math.pow(2, attempt),
|
|
43
|
+
options.maxDelay
|
|
44
|
+
);
|
|
45
|
+
await sleep(delay);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw lastError;
|
|
50
|
+
}
|
|
51
|
+
function sleep(ms) {
|
|
52
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/client.ts
|
|
56
|
+
var DEFAULT_BASE_URL = "https://api.alertsamurai.com";
|
|
57
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
58
|
+
var DEFAULT_RETRIES = 3;
|
|
59
|
+
var AlertSamuraiClient = class {
|
|
60
|
+
constructor(config) {
|
|
61
|
+
if (!config.dsn) {
|
|
62
|
+
throw new ValidationError("DSN is required");
|
|
63
|
+
}
|
|
64
|
+
this.dsn = config.dsn;
|
|
65
|
+
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
66
|
+
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
|
67
|
+
this.retries = config.retries ?? DEFAULT_RETRIES;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Send a custom metric for tracking and threshold-based alerting
|
|
71
|
+
*
|
|
72
|
+
* @param options - Metric options
|
|
73
|
+
* @returns Response with metric ID and alert trigger status
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* await client.sendMetric({
|
|
78
|
+
* metricType: "photo_upload_speed",
|
|
79
|
+
* value: 2.5,
|
|
80
|
+
* unit: "MB/s",
|
|
81
|
+
* environment: "production",
|
|
82
|
+
* metadata: { userId: "123" },
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
async sendMetric(options) {
|
|
87
|
+
this.validateMetric(options);
|
|
88
|
+
const body = {
|
|
89
|
+
metricType: options.metricType,
|
|
90
|
+
value: options.value,
|
|
91
|
+
unit: options.unit,
|
|
92
|
+
environment: options.environment,
|
|
93
|
+
metadata: options.metadata,
|
|
94
|
+
timestamp: this.normalizeTimestamp(options.timestamp)
|
|
95
|
+
};
|
|
96
|
+
return this.request("/api/metrics", body);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Send an application alert with priority level
|
|
100
|
+
*
|
|
101
|
+
* @param options - Alert options
|
|
102
|
+
* @returns Response with alert ID
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* await client.sendAlert({
|
|
107
|
+
* message: "Database connection failed",
|
|
108
|
+
* priority: "critical",
|
|
109
|
+
* environment: "production",
|
|
110
|
+
* data: { errorCode: "ECONNREFUSED" },
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async sendAlert(options) {
|
|
115
|
+
this.validateAlert(options);
|
|
116
|
+
return this.request("/api/alerts", options);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Send a critical priority alert
|
|
120
|
+
* @param message - Alert message
|
|
121
|
+
* @param data - Optional additional context data
|
|
122
|
+
*/
|
|
123
|
+
async critical(message, data) {
|
|
124
|
+
return this.sendAlert({ message, priority: "critical", data });
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Send an error priority alert
|
|
128
|
+
* @param message - Alert message
|
|
129
|
+
* @param data - Optional additional context data
|
|
130
|
+
*/
|
|
131
|
+
async error(message, data) {
|
|
132
|
+
return this.sendAlert({ message, priority: "error", data });
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Send a warning priority alert
|
|
136
|
+
* @param message - Alert message
|
|
137
|
+
* @param data - Optional additional context data
|
|
138
|
+
*/
|
|
139
|
+
async warning(message, data) {
|
|
140
|
+
return this.sendAlert({ message, priority: "warning", data });
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Send an info priority alert
|
|
144
|
+
* @param message - Alert message
|
|
145
|
+
* @param data - Optional additional context data
|
|
146
|
+
*/
|
|
147
|
+
async info(message, data) {
|
|
148
|
+
return this.sendAlert({ message, priority: "info", data });
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Send a debug priority alert
|
|
152
|
+
* @param message - Alert message
|
|
153
|
+
* @param data - Optional additional context data
|
|
154
|
+
*/
|
|
155
|
+
async debug(message, data) {
|
|
156
|
+
return this.sendAlert({ message, priority: "debug", data });
|
|
157
|
+
}
|
|
158
|
+
async request(path, body) {
|
|
159
|
+
return withRetry(() => this.doRequest(path, body), {
|
|
160
|
+
retries: this.retries,
|
|
161
|
+
baseDelay: 1e3,
|
|
162
|
+
maxDelay: 1e4
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async doRequest(path, body) {
|
|
166
|
+
const controller = new AbortController();
|
|
167
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
168
|
+
try {
|
|
169
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: {
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
Authorization: `Bearer ${this.dsn}`
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify(body),
|
|
176
|
+
signal: controller.signal
|
|
177
|
+
});
|
|
178
|
+
clearTimeout(timeoutId);
|
|
179
|
+
if (response.status === 401) {
|
|
180
|
+
throw new AuthenticationError();
|
|
181
|
+
}
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
184
|
+
throw new AlertSamuraiError(
|
|
185
|
+
errorBody.message || `Request failed with status ${response.status}`,
|
|
186
|
+
response.status,
|
|
187
|
+
errorBody
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return response.json();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
clearTimeout(timeoutId);
|
|
193
|
+
if (error instanceof AlertSamuraiError) {
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
197
|
+
throw new NetworkError("Request timed out");
|
|
198
|
+
}
|
|
199
|
+
throw new NetworkError(
|
|
200
|
+
error instanceof Error ? error.message : "Unknown network error"
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
validateMetric(options) {
|
|
205
|
+
if (!options.metricType || typeof options.metricType !== "string") {
|
|
206
|
+
throw new ValidationError("metricType is required and must be a string");
|
|
207
|
+
}
|
|
208
|
+
if (options.metricType.length > 100) {
|
|
209
|
+
throw new ValidationError("metricType must be 100 characters or less");
|
|
210
|
+
}
|
|
211
|
+
if (typeof options.value !== "number" || isNaN(options.value)) {
|
|
212
|
+
throw new ValidationError("value is required and must be a number");
|
|
213
|
+
}
|
|
214
|
+
if (options.unit !== void 0 && typeof options.unit !== "string") {
|
|
215
|
+
throw new ValidationError("unit must be a string");
|
|
216
|
+
}
|
|
217
|
+
if (options.unit && options.unit.length > 50) {
|
|
218
|
+
throw new ValidationError("unit must be 50 characters or less");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
validateAlert(options) {
|
|
222
|
+
if (!options.message || typeof options.message !== "string") {
|
|
223
|
+
throw new ValidationError("message is required and must be a string");
|
|
224
|
+
}
|
|
225
|
+
if (options.message.length > 4e3) {
|
|
226
|
+
throw new ValidationError("message must be 4000 characters or less");
|
|
227
|
+
}
|
|
228
|
+
const validPriorities = ["critical", "error", "warning", "info", "debug"];
|
|
229
|
+
if (!validPriorities.includes(options.priority)) {
|
|
230
|
+
throw new ValidationError(
|
|
231
|
+
`priority must be one of: ${validPriorities.join(", ")}`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
normalizeTimestamp(timestamp) {
|
|
236
|
+
if (!timestamp) return void 0;
|
|
237
|
+
if (timestamp instanceof Date) return timestamp.toISOString();
|
|
238
|
+
if (typeof timestamp === "number") return new Date(timestamp).toISOString();
|
|
239
|
+
return timestamp;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
export {
|
|
243
|
+
AlertSamuraiClient,
|
|
244
|
+
AlertSamuraiError,
|
|
245
|
+
AuthenticationError,
|
|
246
|
+
NetworkError,
|
|
247
|
+
ValidationError
|
|
248
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alertsamurai/sdk-js",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "TypeScript SDK for AlertSamurai metrics and alerting",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"lint": "eslint src --ext .ts",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"alertsamurai",
|
|
27
|
+
"monitoring",
|
|
28
|
+
"metrics",
|
|
29
|
+
"alerting",
|
|
30
|
+
"observability"
|
|
31
|
+
],
|
|
32
|
+
"author": "",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"tsup": "^8.0.0",
|
|
36
|
+
"typescript": "^5.0.0",
|
|
37
|
+
"vitest": "^1.0.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|