@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 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
@@ -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 };
@@ -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
+ }