@vigneshreddy/cms-sdk 1.0.14 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,20 @@
1
1
  # @vigneshreddy/cms-sdk
2
2
 
3
- Official TypeScript/JavaScript SDK for the CutMeShort CMS API.
3
+ [![npm version](https://img.shields.io/npm/v/@vigneshreddy/cms-sdk)](https://www.npmjs.com/package/@vigneshreddy/cms-sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Node.js 18+](https://img.shields.io/badge/Node.js-18%2B-green)](https://nodejs.org/)
4
6
 
5
- Use this package to send:
6
- - lead tracking events (`cms.trackLead`)
7
- - sale tracking events (`cms.trackSale`)
7
+ Official TypeScript/JavaScript SDK for the CutMeShort CMS API. Track leads and sales with reliable retry logic, comprehensive validation, and security best practices.
8
+
9
+ **Key Features:**
10
+ - 🚀 Full TypeScript support with strict types
11
+ - ✅ Input validation with Zod
12
+ - 🔄 Automatic retry with exponential backoff
13
+ - 🛡️ Security hardened (API key validation, sensitive data redaction)
14
+ - ⚡ Sub-second tracking events
15
+ - 📊 Comprehensive error handling with specific error types
16
+
17
+ ---
8
18
 
9
19
  ## Install
10
20
 
@@ -41,7 +51,7 @@ const cms = new CMS({
41
51
  const response = await cms.trackLead({
42
52
  clickId: "id_123",
43
53
  eventName: "signup_started",
44
- customerId: "user_42",
54
+ customerExternalId: "user_42",
45
55
  });
46
56
 
47
57
  console.log(response);
@@ -59,16 +69,15 @@ const cms = new CMS({ apiKey: "sk_live_xxx" });
59
69
  await cms.trackLead({
60
70
  clickId: "id_123",
61
71
  eventName: "signup_started",
62
- customerId: "user_42",
72
+ customerExternalId: "user_42",
63
73
  });
64
74
  ```
65
75
 
66
76
  Lead payload fields:
67
77
  - `clickId: string` (optional in **deferred** follow-up calls)
68
78
  - `eventName: string`
69
- - `customerId: string`
79
+ - `customerExternalId: string`
70
80
  - `timestamp?: string` (ISO 8601, e.g. `new Date().toISOString()`)
71
- - `customerExternalId?: string`
72
81
  - `customerName?: string`
73
82
  - `customerEmail?: string`
74
83
  - `customerAvatar?: string`
@@ -82,18 +91,18 @@ import { CMS } from "@vigneshreddy/cms-sdk";
82
91
 
83
92
  const cms = new CMS({ apiKey: "sk_live_xxx" });
84
93
 
85
- // Step 1: store the clickId <-> customerId association
94
+ // Step 1: store the clickId <-> customerExternalId association
86
95
  await cms.trackLead({
87
96
  clickId: "id_123",
88
97
  eventName: "signup_started",
89
- customerId: "user_42",
98
+ customerExternalId: "user_42",
90
99
  mode: "deferred",
91
100
  });
92
101
 
93
- // Step 2: later, track using just customerId (no clickId)
102
+ // Step 2: later, track using just customerExternalId (no clickId)
94
103
  await cms.trackLead({
95
104
  eventName: "email_verified",
96
- customerId: "user_42",
105
+ customerExternalId: "user_42",
97
106
  mode: "deferred",
98
107
  });
99
108
  ```
@@ -103,10 +112,11 @@ await cms.trackLead({
103
112
  ```ts
104
113
  import { CMS } from "@vigneshreddy/cms-sdk";
105
114
 
106
- const cms = new CMS({ apiKey: "sk_live_xxx" });
115
+ const cms = new CMS({ apiKey: "xxx" });
107
116
 
108
117
  await cms.trackSale({
109
118
  clickId: "id_123",
119
+ customerExternalId: "user_123",
110
120
  eventName: "purchase_completed",
111
121
  invoiceId: "inv_987",
112
122
  amount: 4999,
@@ -118,7 +128,7 @@ Sale payload fields:
118
128
  - `clickId: string`
119
129
  - `eventName: string`
120
130
  - `timestamp?: string` (ISO 8601, e.g. `new Date().toISOString()`)
121
- - `customerExternalId?: string`
131
+ - `customerExternalId: string`
122
132
  - `customerName?: string`
123
133
  - `customerEmail?: string`
124
134
  - `customerAvatar?: string`
@@ -144,7 +154,7 @@ type CMSConfig = {
144
154
  ```
145
155
 
146
156
  Defaults:
147
- - `baseUrl`: `https://www.cutmeshort.com/sdk`
157
+ - `baseUrl`: `https://www.cutmeshort.com`
148
158
  - `timeout`: `10000`
149
159
  - `maxRetries`: `2`
150
160
  - `retryDelayMs`: `500`
@@ -165,7 +175,7 @@ await cms.trackLead(
165
175
  {
166
176
  clickId: "id_123",
167
177
  eventName: "signup_started",
168
- customerId: "user_42",
178
+ customerExternalId: "user_42",
169
179
  },
170
180
  {
171
181
  timeout: 5000,
@@ -177,42 +187,117 @@ await cms.trackLead(
177
187
 
178
188
  ## Error Handling
179
189
 
180
- Class-based methods (`cms.trackLead`, `cms.trackSale`) throw `CMSAPIError` on failure.
190
+ The SDK provides specific error classes for different failure scenarios. Catch and handle them appropriately:
181
191
 
182
192
  ```ts
183
- import { CMS, CMSAPIError } from "@vigneshreddy/cms-sdk";
193
+ import {
194
+ CMS,
195
+ CMSAPIError,
196
+ UnauthorizedError,
197
+ RateLimitError,
198
+ ValidationError
199
+ } from "@vigneshreddy/cms-sdk";
184
200
 
185
- const cms = new CMS({ apiKey: "sk_live_xxx" });
201
+ const cms = new CMS({ apiKey: process.env.CMS_API_KEY });
186
202
 
187
203
  try {
188
- await cms.trackSale({
204
+ await cms.trackLead({
189
205
  clickId: "id_123",
190
- eventName: "purchase_completed",
191
- invoiceId: "inv_987",
192
- amount: 4999,
193
- currency: "USD",
206
+ eventName: "signup_started",
207
+ customerExternalId: "user_42",
194
208
  });
195
209
  } catch (error) {
196
- if (error instanceof CMSAPIError) {
197
- console.error("CMS API error", {
198
- statusCode: error.statusCode,
199
- type: error.type,
200
- message: error.message,
201
- });
210
+ if (error instanceof UnauthorizedError) {
211
+ console.error("Invalid API key - check your credentials");
212
+ } else if (error instanceof RateLimitError) {
213
+ console.error("Rate limited - wait before retrying");
214
+ } else if (error instanceof CMSAPIError) {
215
+ console.error(`CMS API error [${error.statusCode}]:`, error.message);
216
+ console.debug("Type:", error.type);
202
217
  } else {
203
- console.error("Unexpected error", error);
218
+ console.error("Unexpected error:", error);
204
219
  }
205
220
  }
206
221
  ```
207
222
 
223
+ ### Available Error Types
224
+
225
+ | Error Type | HTTP Code | When It Happens |
226
+ |---|---|---|
227
+ | `BadRequestError` | 400 | Invalid payload format |
228
+ | `UnauthorizedError` | 401 | Invalid/missing API key |
229
+ | `ForbiddenError` | 403 | Insufficient permissions |
230
+ | `NotFoundError` | 404 | Resource not found |
231
+ | `ConflictError` | 409 | Resource already exists |
232
+ | `UnprocessableEntityError` | 422 | Validation failed |
233
+ | `RateLimitError` | 429 | Too many requests |
234
+ | `InternalServerError` | 500 | Server error |
235
+ | `BadGatewayError` | 502 | Gateway error (retried) |
236
+ | `ServiceUnavailableError` | 503 | Service down (retried) |
237
+ | `GatewayTimeoutError` | 504 | Timeout (retried) |
238
+
239
+ **Note:** Errors with HTTP codes 429, 500, 502, 503, 504 are automatically retried (configurable).
240
+
241
+ ### Retry Behavior
242
+
243
+ The SDK automatically retries transient failures with exponential backoff:
244
+
245
+ ```ts
246
+ const cms = new CMS({
247
+ apiKey: process.env.CMS_API_KEY,
248
+ maxRetries: 2, // default: 2
249
+ retryDelayMs: 500, // initial delay: 500ms
250
+ retryMaxDelayMs: 10000, // max delay cap: 10s
251
+ retryOnNetworkError: true,
252
+ });
253
+
254
+ // Override for a specific request
255
+ await cms.trackLead(
256
+ { clickId: "id_123", eventName: "event", customerExternalId: "user_1" },
257
+ { maxRetries: 5, retryDelayMs: 1000 }
258
+ );
259
+ ```
260
+ ## TypeScript Usage
261
+
262
+ The SDK is fully typed. Utilize TypeScript for better DX:
263
+
264
+ ```ts
265
+ import {
266
+ CMS,
267
+ type CMSConfig,
268
+ type LeadPayload,
269
+ type SalePayload,
270
+ type RequestOptions
271
+ } from "@vigneshreddy/cms-sdk";
272
+
273
+ // Config is validated at initialization
274
+ const config: CMSConfig = {
275
+ apiKey: process.env.CMS_API_KEY!,
276
+ timeout: 5000,
277
+ maxRetries: 3,
278
+ };
279
+
280
+ const cms = new CMS(config);
281
+
282
+ // Payloads have full type hints
283
+ const leadPayload: LeadPayload = {
284
+ clickId: "id_123",
285
+ eventName: "signup_started",
286
+ customerExteranlId: "user_42",
287
+ customerEmail: "user@example.com",
288
+ };
289
+
290
+ // RequestOptions for per-call configuration
291
+ const options: RequestOptions = {
292
+ timeout: 3000,
293
+ maxRetries: 1,
294
+ };
295
+
296
+ await cms.trackLead(leadPayload, options);
297
+ ```
298
+
208
299
  ## Public API
209
300
 
210
301
  This package intentionally exposes only:
211
302
  - `CMS` (use `cms.trackLead` and `cms.trackSale`)
212
303
  - `CMSAPIError` (for `instanceof` checks)
213
-
214
- Deep imports (example: `@vigneshreddy/cms-sdk/client`) are intentionally blocked.
215
-
216
- ## Security Best Practice
217
-
218
- Do not expose private API keys in public frontend code. Use this SDK from a trusted backend/server environment when using secret keys.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Circuit breaker to prevent cascading failures
3
+ * Fails fast when backend is unhealthy
4
+ */
5
+ export declare enum CircuitBreakerState {
6
+ CLOSED = "CLOSED",// Normal operation
7
+ OPEN = "OPEN",// Failing fast
8
+ HALF_OPEN = "HALF_OPEN"
9
+ }
10
+ export interface CircuitBreakerOptions {
11
+ /** Number of failures before opening circuit */
12
+ failureThreshold: number;
13
+ /** Time window to count failures (milliseconds) */
14
+ failureWindowMs: number;
15
+ /** Time to wait before attempting to recover (milliseconds) */
16
+ resetTimeoutMs: number;
17
+ /** Callback when state changes */
18
+ onStateChange?: (newState: CircuitBreakerState, reason?: string) => void;
19
+ }
20
+ export declare class CircuitBreaker {
21
+ private state;
22
+ private failureCount;
23
+ private failureTimestamps;
24
+ private lastFailureTime;
25
+ private nextAttemptTime;
26
+ private failureThreshold;
27
+ private failureWindowMs;
28
+ private resetTimeoutMs;
29
+ private onStateChange?;
30
+ constructor(options: CircuitBreakerOptions);
31
+ /**
32
+ * Check if a request should be allowed
33
+ */
34
+ canAttempt(): boolean;
35
+ /**
36
+ * Record a successful request
37
+ */
38
+ recordSuccess(): void;
39
+ /**
40
+ * Record a failed request
41
+ */
42
+ recordFailure(): void;
43
+ /**
44
+ * Get current state
45
+ */
46
+ getState(): CircuitBreakerState;
47
+ /**
48
+ * Get number of recent failures
49
+ */
50
+ getFailureCount(): number;
51
+ /**
52
+ * Get milliseconds until circuit attempts to recover
53
+ */
54
+ getRecoveryTime(): number;
55
+ /**
56
+ * Manually reset the circuit breaker
57
+ */
58
+ reset(): void;
59
+ private setState;
60
+ }
61
+ /**
62
+ * Pre-configured circuit breaker settings for common scenarios
63
+ */
64
+ export declare const CIRCUIT_BREAKER_PRESETS: {
65
+ /**
66
+ * Strict: Open after 2 failures in 10 seconds, wait 30 seconds before retry
67
+ */
68
+ readonly STRICT: {
69
+ readonly failureThreshold: 2;
70
+ readonly failureWindowMs: 10000;
71
+ readonly resetTimeoutMs: 30000;
72
+ };
73
+ /**
74
+ * Standard: Open after 5 failures in 30 seconds, wait 60 seconds before retry
75
+ */
76
+ readonly STANDARD: {
77
+ readonly failureThreshold: 5;
78
+ readonly failureWindowMs: 30000;
79
+ readonly resetTimeoutMs: 60000;
80
+ };
81
+ /**
82
+ * Lenient: Open after 10 failures in 60 seconds, wait 120 seconds before retry
83
+ */
84
+ readonly LENIENT: {
85
+ readonly failureThreshold: 10;
86
+ readonly failureWindowMs: 60000;
87
+ readonly resetTimeoutMs: 120000;
88
+ };
89
+ /**
90
+ * Disabled: Circuit breaker always allows attempts
91
+ */
92
+ readonly DISABLED: {
93
+ readonly failureThreshold: number;
94
+ readonly failureWindowMs: number;
95
+ readonly resetTimeoutMs: 0;
96
+ };
97
+ };
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * Circuit breaker to prevent cascading failures
4
+ * Fails fast when backend is unhealthy
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CIRCUIT_BREAKER_PRESETS = exports.CircuitBreaker = exports.CircuitBreakerState = void 0;
8
+ var CircuitBreakerState;
9
+ (function (CircuitBreakerState) {
10
+ CircuitBreakerState["CLOSED"] = "CLOSED";
11
+ CircuitBreakerState["OPEN"] = "OPEN";
12
+ CircuitBreakerState["HALF_OPEN"] = "HALF_OPEN";
13
+ })(CircuitBreakerState || (exports.CircuitBreakerState = CircuitBreakerState = {}));
14
+ class CircuitBreaker {
15
+ constructor(options) {
16
+ this.state = CircuitBreakerState.CLOSED;
17
+ this.failureCount = 0;
18
+ this.failureTimestamps = [];
19
+ this.lastFailureTime = 0;
20
+ this.nextAttemptTime = 0;
21
+ this.failureThreshold = options.failureThreshold;
22
+ this.failureWindowMs = options.failureWindowMs;
23
+ this.resetTimeoutMs = options.resetTimeoutMs;
24
+ this.onStateChange = options.onStateChange;
25
+ }
26
+ /**
27
+ * Check if a request should be allowed
28
+ */
29
+ canAttempt() {
30
+ if (this.state === CircuitBreakerState.CLOSED) {
31
+ return true;
32
+ }
33
+ if (this.state === CircuitBreakerState.OPEN) {
34
+ const now = Date.now();
35
+ if (now >= this.nextAttemptTime) {
36
+ // Try to recover
37
+ this.setState(CircuitBreakerState.HALF_OPEN, "Attempting recovery");
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ // HALF_OPEN - allow attempt
43
+ return true;
44
+ }
45
+ /**
46
+ * Record a successful request
47
+ */
48
+ recordSuccess() {
49
+ if (this.state === CircuitBreakerState.HALF_OPEN) {
50
+ this.setState(CircuitBreakerState.CLOSED, "Recovered successfully");
51
+ }
52
+ // Clear failure history on success
53
+ if (this.state === CircuitBreakerState.CLOSED) {
54
+ this.failureCount = 0;
55
+ this.failureTimestamps = [];
56
+ }
57
+ }
58
+ /**
59
+ * Record a failed request
60
+ */
61
+ recordFailure() {
62
+ const now = Date.now();
63
+ this.lastFailureTime = now;
64
+ // Remove old failures outside the window
65
+ this.failureTimestamps = this.failureTimestamps.filter((timestamp) => now - timestamp < this.failureWindowMs);
66
+ // Add new failure
67
+ this.failureTimestamps.push(now);
68
+ this.failureCount = this.failureTimestamps.length;
69
+ if (this.state === CircuitBreakerState.HALF_OPEN) {
70
+ // Failed during recovery attempt
71
+ this.setState(CircuitBreakerState.OPEN, "Recovery attempt failed");
72
+ this.nextAttemptTime = now + this.resetTimeoutMs;
73
+ }
74
+ else if (this.state === CircuitBreakerState.CLOSED) {
75
+ // Check if threshold exceeded
76
+ if (this.failureCount >= this.failureThreshold) {
77
+ this.setState(CircuitBreakerState.OPEN, `${this.failureCount} failures in ${this.failureWindowMs}ms`);
78
+ this.nextAttemptTime = now + this.resetTimeoutMs;
79
+ }
80
+ }
81
+ }
82
+ /**
83
+ * Get current state
84
+ */
85
+ getState() {
86
+ return this.state;
87
+ }
88
+ /**
89
+ * Get number of recent failures
90
+ */
91
+ getFailureCount() {
92
+ // Clean up old failures
93
+ const now = Date.now();
94
+ this.failureTimestamps = this.failureTimestamps.filter((timestamp) => now - timestamp < this.failureWindowMs);
95
+ return this.failureTimestamps.length;
96
+ }
97
+ /**
98
+ * Get milliseconds until circuit attempts to recover
99
+ */
100
+ getRecoveryTime() {
101
+ if (this.state !== CircuitBreakerState.OPEN) {
102
+ return 0;
103
+ }
104
+ return Math.max(0, this.nextAttemptTime - Date.now());
105
+ }
106
+ /**
107
+ * Manually reset the circuit breaker
108
+ */
109
+ reset() {
110
+ this.setState(CircuitBreakerState.CLOSED, "Manual reset");
111
+ this.failureCount = 0;
112
+ this.failureTimestamps = [];
113
+ this.lastFailureTime = 0;
114
+ this.nextAttemptTime = 0;
115
+ }
116
+ setState(newState, reason) {
117
+ if (this.state !== newState) {
118
+ this.state = newState;
119
+ if (this.onStateChange) {
120
+ this.onStateChange(newState, reason);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ exports.CircuitBreaker = CircuitBreaker;
126
+ /**
127
+ * Pre-configured circuit breaker settings for common scenarios
128
+ */
129
+ exports.CIRCUIT_BREAKER_PRESETS = {
130
+ /**
131
+ * Strict: Open after 2 failures in 10 seconds, wait 30 seconds before retry
132
+ */
133
+ STRICT: {
134
+ failureThreshold: 2,
135
+ failureWindowMs: 10000,
136
+ resetTimeoutMs: 30000,
137
+ },
138
+ /**
139
+ * Standard: Open after 5 failures in 30 seconds, wait 60 seconds before retry
140
+ */
141
+ STANDARD: {
142
+ failureThreshold: 5,
143
+ failureWindowMs: 30000,
144
+ resetTimeoutMs: 60000,
145
+ },
146
+ /**
147
+ * Lenient: Open after 10 failures in 60 seconds, wait 120 seconds before retry
148
+ */
149
+ LENIENT: {
150
+ failureThreshold: 10,
151
+ failureWindowMs: 60000,
152
+ resetTimeoutMs: 120000,
153
+ },
154
+ /**
155
+ * Disabled: Circuit breaker always allows attempts
156
+ */
157
+ DISABLED: {
158
+ failureThreshold: Infinity,
159
+ failureWindowMs: Infinity,
160
+ resetTimeoutMs: 0,
161
+ },
162
+ };
@@ -1,4 +1,4 @@
1
- import { EventsApi, TrackLeadRequest, TrackSaleRequest } from "./generated";
1
+ import { EventsApi, TrackLeadRequest, TrackSaleRequest } from "./funcs";
2
2
  export type LeadPayload = TrackLeadRequest;
3
3
  export type SalePayload = TrackSaleRequest;
4
4
  export interface CMSConfig {
@@ -57,6 +57,6 @@ export declare class CMS {
57
57
  private toGeneratedRequestOptions;
58
58
  private withRetry;
59
59
  private isRetryableErrorWithOptions;
60
- trackLead(leadData: LeadPayload, options?: RequestOptions): Promise<import("./generated").TrackResponse>;
61
- trackSale(saleData: SalePayload, options?: RequestOptions): Promise<import("./generated").TrackResponse>;
60
+ trackLead(leadData: LeadPayload, options?: RequestOptions): Promise<import("./funcs").TrackResponse>;
61
+ trackSale(saleData: SalePayload, options?: RequestOptions): Promise<import("./funcs").TrackResponse>;
62
62
  }
@@ -2,19 +2,26 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CMS = void 0;
4
4
  // src/client.ts
5
- const generated_1 = require("./generated");
5
+ const funcs_1 = require("./funcs");
6
6
  const errors_1 = require("./errors");
7
- const DEFAULT_BASE_URL = "https://www.cutmeshort.com";
8
- const DEFAULT_TIMEOUT_MS = 10000;
9
- const DEFAULT_MAX_RETRIES = 2;
10
- const DEFAULT_RETRY_DELAY_MS = 500;
11
- const DEFAULT_RETRY_MAX_DELAY_MS = 10000;
12
- const DEFAULT_RETRY_STATUSES = [429, 500, 502, 503, 504];
7
+ const validation_1 = require("./validations/validation");
8
+ const constants_1 = require("./constants/constants");
9
+ const DEFAULT_BASE_URL = constants_1.API_CONFIG.BASE_URL;
10
+ const DEFAULT_TIMEOUT_MS = constants_1.API_CONFIG.TIMEOUT_MS;
11
+ const DEFAULT_MAX_RETRIES = constants_1.RETRY_CONFIG.MAX_RETRIES;
12
+ const DEFAULT_RETRY_DELAY_MS = constants_1.RETRY_CONFIG.DELAY_MS;
13
+ const DEFAULT_RETRY_MAX_DELAY_MS = constants_1.RETRY_CONFIG.MAX_DELAY_MS;
14
+ const DEFAULT_RETRY_STATUSES = [...constants_1.RETRY_CONFIG.ON_STATUSES];
13
15
  class CMS {
14
16
  constructor(config) {
15
17
  var _a, _b, _c, _d;
16
- if (!config.apiKey) {
17
- throw new Error("CMS SDK: apiKey is required.");
18
+ // Validate configuration with strict schema
19
+ try {
20
+ (0, validation_1.validateCMSConfig)(config);
21
+ }
22
+ catch (error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ throw new Error(`CMS SDK: Invalid configuration - ${message}`);
18
25
  }
19
26
  const basePath = this.normalizeBaseUrl((_a = config.baseUrl) !== null && _a !== void 0 ? _a : DEFAULT_BASE_URL);
20
27
  // Retry configuration with sensible defaults
@@ -24,20 +31,22 @@ class CMS {
24
31
  this.retryOnStatuses = this.resolveRetryStatuses((_b = config.retryOnStatuses) !== null && _b !== void 0 ? _b : DEFAULT_RETRY_STATUSES);
25
32
  this.retryOnNetworkError = (_c = config.retryOnNetworkError) !== null && _c !== void 0 ? _c : true;
26
33
  // Create the Configuration object
27
- const apiConfig = new generated_1.Configuration({
34
+ const apiConfig = new funcs_1.Configuration({
28
35
  basePath,
29
36
  accessToken: config.apiKey,
30
37
  baseOptions: {
31
38
  // Ensure generated client uses the same timeout & headers
32
39
  timeout: (_d = config.timeout) !== null && _d !== void 0 ? _d : DEFAULT_TIMEOUT_MS,
33
40
  headers: {
34
- 'Content-Type': 'application/json'
41
+ 'Content-Type': 'application/json',
42
+ 'X-CMS-SDK-Version': constants_1.SDK_VERSION,
43
+ 'X-CMS-SDK-Runtime': typeof globalThis !== 'undefined' ? constants_1.HTTP_HEADERS.RUNTIME_NODEJS : constants_1.HTTP_HEADERS.RUNTIME_BROWSER,
35
44
  },
36
45
  },
37
46
  });
38
47
  // Instantiate the EventsApi using configuration and basePath.
39
48
  // The underlying implementation uses the global `fetch` API.
40
- this.events = new generated_1.EventsApi(apiConfig, basePath);
49
+ this.events = new funcs_1.EventsApi(apiConfig, basePath);
41
50
  }
42
51
  async sleep(ms) {
43
52
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -182,12 +191,28 @@ class CMS {
182
191
  return false;
183
192
  }
184
193
  async trackLead(leadData, options) {
194
+ // Validate lead data
195
+ try {
196
+ (0, validation_1.validateLeadPayload)(leadData);
197
+ }
198
+ catch (error) {
199
+ const message = error instanceof Error ? error.message : String(error);
200
+ throw new Error(`CMS SDK: Invalid lead data - ${message}`);
201
+ }
185
202
  const requestOptions = this.toGeneratedRequestOptions(options);
186
203
  return this.withRetry(async () => {
187
204
  return this.events.trackLead(leadData, requestOptions);
188
205
  }, options);
189
206
  }
190
207
  async trackSale(saleData, options) {
208
+ // Validate sale data
209
+ try {
210
+ (0, validation_1.validateSalePayload)(saleData);
211
+ }
212
+ catch (error) {
213
+ const message = error instanceof Error ? error.message : String(error);
214
+ throw new Error(`CMS SDK: Invalid sale data - ${message}`);
215
+ }
191
216
  const requestOptions = this.toGeneratedRequestOptions(options);
192
217
  return this.withRetry(async () => {
193
218
  return this.events.trackSale(saleData, requestOptions);