@codematic/opencdp 5.0.13
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 +279 -0
- package/dist/cjs/client.d.ts +87 -0
- package/dist/cjs/client.js +890 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +7 -0
- package/dist/cjs/types.d.ts +122 -0
- package/dist/cjs/types.js +40 -0
- package/dist/esm/client.d.ts +87 -0
- package/dist/esm/client.js +883 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/types.d.ts +122 -0
- package/dist/esm/types.js +36 -0
- package/package.json +40 -0
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CDPClient = void 0;
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
const customerio_node_1 = require("customerio-node");
|
|
18
|
+
const p_limit_1 = __importDefault(require("p-limit"));
|
|
19
|
+
/**
|
|
20
|
+
* Validates that the identifier is not empty
|
|
21
|
+
*/
|
|
22
|
+
function validateIdentifier(identifier) {
|
|
23
|
+
if (identifier === null ||
|
|
24
|
+
identifier === undefined ||
|
|
25
|
+
identifier === "" ||
|
|
26
|
+
(typeof identifier === "string" && identifier.trim() === "")) {
|
|
27
|
+
throw new Error("Identifier cannot be empty");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validates that the event name is not empty
|
|
32
|
+
*/
|
|
33
|
+
function validateEventName(eventName) {
|
|
34
|
+
if (!eventName || eventName.trim() === "") {
|
|
35
|
+
throw new Error("Event name cannot be empty");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validates that properties is a valid object
|
|
40
|
+
*/
|
|
41
|
+
function validateProperties(properties) {
|
|
42
|
+
if (properties === null || properties === undefined) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
// if (typeof properties !== 'object' || Array.isArray(properties)) {
|
|
46
|
+
// throw new Error('Properties must be a valid object');
|
|
47
|
+
// }
|
|
48
|
+
return properties;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validates that the email address is not empty
|
|
52
|
+
*/
|
|
53
|
+
function validateEmail(email) {
|
|
54
|
+
if (!email || email.trim() === "") {
|
|
55
|
+
throw new Error("Email address cannot be empty");
|
|
56
|
+
}
|
|
57
|
+
// Basic email validation
|
|
58
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
59
|
+
if (!emailRegex.test(email)) {
|
|
60
|
+
throw new Error("Invalid email address format");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validates that the send email request has required fields
|
|
65
|
+
*/
|
|
66
|
+
function validateSendEmailRequest(request) {
|
|
67
|
+
const message = request.message;
|
|
68
|
+
// Validate required fields
|
|
69
|
+
if (!message.to) {
|
|
70
|
+
throw new Error("to is required");
|
|
71
|
+
}
|
|
72
|
+
validateEmail(message.to);
|
|
73
|
+
// Validate identifiers - must contain exactly one of: id or email
|
|
74
|
+
if (!message.identifiers) {
|
|
75
|
+
throw new Error("identifiers is required");
|
|
76
|
+
}
|
|
77
|
+
const hasId = "id" in message.identifiers &&
|
|
78
|
+
message.identifiers.id !== undefined &&
|
|
79
|
+
message.identifiers.id !== null &&
|
|
80
|
+
message.identifiers.id !== "";
|
|
81
|
+
const hasEmail = "email" in message.identifiers &&
|
|
82
|
+
message.identifiers.email !== undefined &&
|
|
83
|
+
message.identifiers.email !== null &&
|
|
84
|
+
message.identifiers.email !== "";
|
|
85
|
+
if (!hasId && !hasEmail) {
|
|
86
|
+
throw new Error("identifiers must contain exactly one of: id, email, or cdp_id");
|
|
87
|
+
}
|
|
88
|
+
if (hasId && hasEmail) {
|
|
89
|
+
throw new Error("identifiers must contain exactly one of: id, email, or cdp_id");
|
|
90
|
+
}
|
|
91
|
+
// Validate email fields
|
|
92
|
+
if ("from" in message && message.from) {
|
|
93
|
+
validateEmail(message.from);
|
|
94
|
+
}
|
|
95
|
+
if (message.bcc && message.bcc.length > 0) {
|
|
96
|
+
if (Array.isArray(message.bcc)) {
|
|
97
|
+
message.bcc.forEach(email => validateEmail(email));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
validateEmail(message.bcc);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (message.cc && message.cc.length > 0) {
|
|
104
|
+
if (Array.isArray(message.cc)) {
|
|
105
|
+
message.cc.forEach(email => validateEmail(email));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
validateEmail(message.cc);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (message.reply_to) {
|
|
112
|
+
validateEmail(message.reply_to);
|
|
113
|
+
}
|
|
114
|
+
// Validate send_at if provided
|
|
115
|
+
if (message.send_at !== undefined) {
|
|
116
|
+
if (!Number.isInteger(message.send_at) || message.send_at < 0) {
|
|
117
|
+
throw new Error("send_at must be a positive integer");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Validate body fields
|
|
121
|
+
if ("body" in message &&
|
|
122
|
+
message.body !== undefined &&
|
|
123
|
+
message.body !== null &&
|
|
124
|
+
message.body.trim() === "") {
|
|
125
|
+
throw new Error("body cannot be empty if provided");
|
|
126
|
+
}
|
|
127
|
+
if (message.amp_body !== undefined &&
|
|
128
|
+
message.amp_body !== null &&
|
|
129
|
+
message.amp_body.trim() === "") {
|
|
130
|
+
throw new Error("amp_body cannot be empty if provided");
|
|
131
|
+
}
|
|
132
|
+
if (message.plaintext_body !== undefined &&
|
|
133
|
+
message.plaintext_body !== null &&
|
|
134
|
+
message.plaintext_body.trim() === "") {
|
|
135
|
+
throw new Error("plaintext_body cannot be empty if provided");
|
|
136
|
+
}
|
|
137
|
+
// Validate headers if provided
|
|
138
|
+
if (message.headers !== undefined) {
|
|
139
|
+
if (typeof message.headers !== "object" || Array.isArray(message.headers)) {
|
|
140
|
+
throw new Error("headers must be an object");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Type guard to check if it's a template-based request
|
|
144
|
+
const isTemplateRequest = message.transactional_message_id !== undefined;
|
|
145
|
+
if (!isTemplateRequest) {
|
|
146
|
+
// Raw email - body, subject, and from are required
|
|
147
|
+
const errors = [];
|
|
148
|
+
if (!("body" in message) || !message.body) {
|
|
149
|
+
errors.push("body is required when not using a template");
|
|
150
|
+
}
|
|
151
|
+
if (!("subject" in message) || !message.subject) {
|
|
152
|
+
errors.push("subject is required when not using a template");
|
|
153
|
+
}
|
|
154
|
+
if (!("from" in message) || !message.from) {
|
|
155
|
+
errors.push("from is required when not using a template");
|
|
156
|
+
}
|
|
157
|
+
if (errors.length > 0) {
|
|
158
|
+
throw new Error(`When not using a template: ${errors.join(", ")}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validates that the send push request has required fields
|
|
164
|
+
*/
|
|
165
|
+
function validateSendPushRequest(request) {
|
|
166
|
+
// Validate required fields
|
|
167
|
+
if (!request.identifiers) {
|
|
168
|
+
throw new Error("identifiers is required");
|
|
169
|
+
}
|
|
170
|
+
const hasId = "id" in request.identifiers &&
|
|
171
|
+
request.identifiers.id !== undefined &&
|
|
172
|
+
request.identifiers.id !== null &&
|
|
173
|
+
request.identifiers.id !== "";
|
|
174
|
+
const hasEmail = "email" in request.identifiers &&
|
|
175
|
+
request.identifiers.email !== undefined &&
|
|
176
|
+
request.identifiers.email !== null &&
|
|
177
|
+
request.identifiers.email !== "";
|
|
178
|
+
const hasCdpId = "cdp_id" in request.identifiers &&
|
|
179
|
+
request.identifiers.cdp_id !== undefined &&
|
|
180
|
+
request.identifiers.cdp_id !== null &&
|
|
181
|
+
request.identifiers.cdp_id !== "";
|
|
182
|
+
if (!hasId && !hasEmail && !hasCdpId) {
|
|
183
|
+
throw new Error("identifiers must contain exactly one of: id, email, or cdp_id");
|
|
184
|
+
}
|
|
185
|
+
if ((hasId ? 1 : 0) + (hasEmail ? 1 : 0) + (hasCdpId ? 1 : 0) > 1) {
|
|
186
|
+
throw new Error("identifiers must contain exactly one of: id, email, or cdp_id");
|
|
187
|
+
}
|
|
188
|
+
if (!request.transactional_message_id) {
|
|
189
|
+
throw new Error("transactional_message_id is required");
|
|
190
|
+
}
|
|
191
|
+
// Validate body field
|
|
192
|
+
if (request.body !== undefined &&
|
|
193
|
+
request.body !== null &&
|
|
194
|
+
request.body.trim() === "") {
|
|
195
|
+
throw new Error("body cannot be empty if provided");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Validates phone number format (E.164 format)
|
|
200
|
+
*/
|
|
201
|
+
function validatePhoneNumber(phone) {
|
|
202
|
+
if (!phone || phone.trim() === "") {
|
|
203
|
+
throw new Error("Phone number cannot be empty");
|
|
204
|
+
}
|
|
205
|
+
// E.164 format: ^\+?[1-9]\d{1,14}$
|
|
206
|
+
const phoneRegex = /^\+?[1-9]\d{1,14}$/;
|
|
207
|
+
if (!phoneRegex.test(phone)) {
|
|
208
|
+
throw new Error("Phone number must be in international format (e.g., +1234567890)");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Validates that the send SMS request has required fields
|
|
213
|
+
*/
|
|
214
|
+
function validateSendSmsRequest(request) {
|
|
215
|
+
// Validate required fields
|
|
216
|
+
if (!request.identifiers) {
|
|
217
|
+
throw new Error("identifiers is required");
|
|
218
|
+
}
|
|
219
|
+
const hasId = "id" in request.identifiers &&
|
|
220
|
+
request.identifiers.id !== undefined &&
|
|
221
|
+
request.identifiers.id !== null &&
|
|
222
|
+
request.identifiers.id !== "";
|
|
223
|
+
const hasEmail = "email" in request.identifiers &&
|
|
224
|
+
request.identifiers.email !== undefined &&
|
|
225
|
+
request.identifiers.email !== null &&
|
|
226
|
+
request.identifiers.email !== "";
|
|
227
|
+
const hasCdpId = "cdp_id" in request.identifiers &&
|
|
228
|
+
request.identifiers.cdp_id !== undefined &&
|
|
229
|
+
request.identifiers.cdp_id !== null &&
|
|
230
|
+
request.identifiers.cdp_id !== "";
|
|
231
|
+
if (!hasId && !hasEmail && !hasCdpId) {
|
|
232
|
+
throw new Error("identifiers must contain exactly one of: id, email, or cdp_id");
|
|
233
|
+
}
|
|
234
|
+
if ((hasId ? 1 : 0) + (hasEmail ? 1 : 0) + (hasCdpId ? 1 : 0) > 1) {
|
|
235
|
+
throw new Error("identifiers must contain exactly one of: id, email, or cdp_id");
|
|
236
|
+
}
|
|
237
|
+
// Validate conditional requirement: body is required if no transactional_message_id
|
|
238
|
+
const hasTemplateId = request.transactional_message_id !== undefined &&
|
|
239
|
+
request.transactional_message_id !== null &&
|
|
240
|
+
request.transactional_message_id !== "";
|
|
241
|
+
if (!hasTemplateId && !request.body) {
|
|
242
|
+
throw new Error("body is required when not using a template");
|
|
243
|
+
}
|
|
244
|
+
// Validate phone number format if to is provided
|
|
245
|
+
if (request.to) {
|
|
246
|
+
validatePhoneNumber(request.to);
|
|
247
|
+
}
|
|
248
|
+
// Validate from phone number format if provided
|
|
249
|
+
if (request.from) {
|
|
250
|
+
validatePhoneNumber(request.from);
|
|
251
|
+
}
|
|
252
|
+
// Validate body field
|
|
253
|
+
if (request.body !== undefined &&
|
|
254
|
+
request.body !== null &&
|
|
255
|
+
request.body.trim() === "") {
|
|
256
|
+
throw new Error("body cannot be empty if provided");
|
|
257
|
+
}
|
|
258
|
+
// Validate message_data is an object if provided
|
|
259
|
+
if (request.message_data !== undefined) {
|
|
260
|
+
if (request.message_data === null ||
|
|
261
|
+
typeof request.message_data !== "object" ||
|
|
262
|
+
Array.isArray(request.message_data)) {
|
|
263
|
+
throw new Error("message_data must be an object");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const DEFAULT_CONCURRENCY = 10;
|
|
268
|
+
const MAX_SAFE_CONCURRENCY = 30;
|
|
269
|
+
class CDPClient {
|
|
270
|
+
constructor(config) {
|
|
271
|
+
this.config = config;
|
|
272
|
+
this.customerIoClient = null;
|
|
273
|
+
this.apiRoot =
|
|
274
|
+
config.cdpEndpoint || "https://api.opencdp.io/gateway/data-gateway";
|
|
275
|
+
this.sendToCustomerIo = Boolean(config.sendToCustomerIo && config.customerIo);
|
|
276
|
+
this.timeout = config.timeout || 10000;
|
|
277
|
+
// Create axios instance with connection pooling and reuse
|
|
278
|
+
this.axiosInstance = axios_1.default.create({
|
|
279
|
+
baseURL: this.apiRoot,
|
|
280
|
+
timeout: this.timeout,
|
|
281
|
+
headers: {
|
|
282
|
+
Authorization: this.config.cdpApiKey,
|
|
283
|
+
"Content-Type": "application/json",
|
|
284
|
+
},
|
|
285
|
+
// Enable connection pooling and reuse
|
|
286
|
+
httpAgent: new (require("http").Agent)({
|
|
287
|
+
keepAlive: true,
|
|
288
|
+
keepAliveMsecs: 1000,
|
|
289
|
+
maxSockets: 50,
|
|
290
|
+
maxFreeSockets: 10,
|
|
291
|
+
timeout: 60000,
|
|
292
|
+
freeSocketTimeout: 30000,
|
|
293
|
+
}),
|
|
294
|
+
httpsAgent: new (require("https").Agent)({
|
|
295
|
+
keepAlive: true,
|
|
296
|
+
keepAliveMsecs: 1000,
|
|
297
|
+
maxSockets: 50,
|
|
298
|
+
maxFreeSockets: 10,
|
|
299
|
+
timeout: 60000,
|
|
300
|
+
freeSocketTimeout: 30000,
|
|
301
|
+
}),
|
|
302
|
+
});
|
|
303
|
+
let requestedConcurrency = config.maxConcurrentRequests || DEFAULT_CONCURRENCY;
|
|
304
|
+
if (requestedConcurrency < 1) {
|
|
305
|
+
requestedConcurrency = DEFAULT_CONCURRENCY;
|
|
306
|
+
}
|
|
307
|
+
const concurrencyLimit = Math.min(requestedConcurrency, MAX_SAFE_CONCURRENCY);
|
|
308
|
+
if (config.cdpLogger) {
|
|
309
|
+
this.logger = config.cdpLogger;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
this.logger = {
|
|
313
|
+
debug: console.debug.bind(console),
|
|
314
|
+
error: console.error.bind(console),
|
|
315
|
+
warn: console.warn.bind(console),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (requestedConcurrency > MAX_SAFE_CONCURRENCY && this.config.debug) {
|
|
319
|
+
this.logger.debug(`[CDP] maxConcurrentRequests (${requestedConcurrency}) exceeds limit. Using capped value: ${concurrencyLimit}`);
|
|
320
|
+
}
|
|
321
|
+
// Initialize the concurrency limiter
|
|
322
|
+
this.limit = (0, p_limit_1.default)(concurrencyLimit);
|
|
323
|
+
if (this.sendToCustomerIo && config.customerIo) {
|
|
324
|
+
const region = config.customerIo.region === "eu" ? customerio_node_1.RegionEU : customerio_node_1.RegionUS;
|
|
325
|
+
try {
|
|
326
|
+
this.customerIoClient = new customerio_node_1.TrackClient(config.customerIo.siteId, config.customerIo.apiKey, { region });
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
if (this.config.debug) {
|
|
330
|
+
this.logger.error("[Customer.io] Initialize error", { error });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Tests the connection to the OpenCDP API server.
|
|
337
|
+
* Sends a ping request to verify that the configured endpoint is reachable and valid.
|
|
338
|
+
*
|
|
339
|
+
* This method ensures that credentials, and network access are configured correctly.
|
|
340
|
+
* It does NOT establish a persistent connection.
|
|
341
|
+
*
|
|
342
|
+
* Do not ping before sending each request
|
|
343
|
+
* @throws Error only when config.failOnException === true and the connection fails due to invalid credentials, network issues, or timeouts.
|
|
344
|
+
*/
|
|
345
|
+
ping() {
|
|
346
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
347
|
+
yield this.validateConnection();
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
validateConnection() {
|
|
351
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
352
|
+
var _a, _b, _c;
|
|
353
|
+
try {
|
|
354
|
+
const response = yield this.axiosInstance.get("/v1/health/ping");
|
|
355
|
+
if (this.config.debug) {
|
|
356
|
+
this.logger.debug(`[CDP] Connection Established! Status: ${response.status}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
// Extract details for better debugging
|
|
361
|
+
const statusCode = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
362
|
+
const statusText = (_b = error.response) === null || _b === void 0 ? void 0 : _b.statusText;
|
|
363
|
+
const responseData = (_c = error.response) === null || _c === void 0 ? void 0 : _c.data;
|
|
364
|
+
const dnsError = error.code === "ENOTFOUND";
|
|
365
|
+
const timeoutError = error.code === "ECONNABORTED";
|
|
366
|
+
// Error summary
|
|
367
|
+
const errorSummary = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ message: error.message }, (statusCode && { statusCode })), (statusText && { statusText })), (dnsError && { dnsError: true })), (timeoutError && { timeout: true })), (responseData && { responseData })), { stack: this.config.debug ? error.stack : undefined });
|
|
368
|
+
if (this.config.debug) {
|
|
369
|
+
this.logger.error("[CDP] Failed to connect to CDP Server", errorSummary);
|
|
370
|
+
}
|
|
371
|
+
if (this.config.failOnException) {
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
limited(fn) {
|
|
381
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
382
|
+
return this.limit(fn);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Identify a person in the CDP
|
|
387
|
+
* This method is concurrency-limited using p-limit to avoid overwhelming traffic external traffic.
|
|
388
|
+
* @param identifier The person identifier
|
|
389
|
+
* @param properties Additional properties for the person
|
|
390
|
+
* @throws Error only when config.failOnException === true (e.g., when the identifier is empty or the request fails)
|
|
391
|
+
*/
|
|
392
|
+
identify(identifier, properties) {
|
|
393
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
394
|
+
return this.limited(() => __awaiter(this, void 0, void 0, function* () {
|
|
395
|
+
var _a, _b, _c;
|
|
396
|
+
try {
|
|
397
|
+
validateIdentifier(identifier);
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
if (this.config.debug) {
|
|
401
|
+
this.logger.error("[CDP] Identify validation error", { error });
|
|
402
|
+
}
|
|
403
|
+
if (this.config.failOnException) {
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const normalizedProps = validateProperties(properties);
|
|
409
|
+
if (this.sendToCustomerIo && this.customerIoClient) {
|
|
410
|
+
try {
|
|
411
|
+
yield this.customerIoClient.identify(identifier, normalizedProps);
|
|
412
|
+
if (this.config.debug) {
|
|
413
|
+
this.logger.debug(`[Customer.io] Identified ${identifier}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
if (this.config.debug) {
|
|
418
|
+
this.logger.error("[Customer.io] Identify error", { error });
|
|
419
|
+
}
|
|
420
|
+
if (this.config.failOnException) {
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
yield this.axiosInstance.post("/v1/persons/identify", {
|
|
427
|
+
identifier,
|
|
428
|
+
properties: normalizedProps,
|
|
429
|
+
});
|
|
430
|
+
if (this.config.debug) {
|
|
431
|
+
this.logger.debug(`[CDP] Identified ${identifier}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
// NB: Avoid logging large error objects directly to reduce memory footprint on high traffic apps
|
|
436
|
+
if (this.config.debug) {
|
|
437
|
+
const errorSummary = {
|
|
438
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
439
|
+
status: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
440
|
+
data: ((_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "[truncated]",
|
|
441
|
+
};
|
|
442
|
+
this.logger.error("[CDP] Identify error", { errorSummary });
|
|
443
|
+
}
|
|
444
|
+
// Re-throw the error so users can handle failures
|
|
445
|
+
if (this.config.failOnException) {
|
|
446
|
+
throw error;
|
|
447
|
+
}
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}));
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Track an event for a person.
|
|
455
|
+
* @param identifier The person identifier
|
|
456
|
+
* @param eventName The event name
|
|
457
|
+
* @param properties Additional properties for the event
|
|
458
|
+
* @throws Error only when config.failOnException === true (e.g., when validation or request fails)
|
|
459
|
+
*/
|
|
460
|
+
track(identifier, eventName, properties) {
|
|
461
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
462
|
+
return this.limited(() => __awaiter(this, void 0, void 0, function* () {
|
|
463
|
+
var _a, _b, _c;
|
|
464
|
+
try {
|
|
465
|
+
validateIdentifier(identifier);
|
|
466
|
+
validateEventName(eventName);
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
if (this.config.debug) {
|
|
470
|
+
this.logger.error("[CDP] Track validation error", { error });
|
|
471
|
+
}
|
|
472
|
+
if (this.config.failOnException) {
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const normalizedProps = validateProperties(properties);
|
|
479
|
+
if (this.sendToCustomerIo && this.customerIoClient) {
|
|
480
|
+
try {
|
|
481
|
+
yield this.customerIoClient.track(identifier, {
|
|
482
|
+
name: eventName,
|
|
483
|
+
data: normalizedProps,
|
|
484
|
+
});
|
|
485
|
+
if (this.config.debug) {
|
|
486
|
+
this.logger.debug(`[Customer.io] Tracked event ${eventName} for ${identifier}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
if (this.config.debug) {
|
|
491
|
+
this.logger.error("[Customer.io] Track error", { error });
|
|
492
|
+
}
|
|
493
|
+
if (this.config.failOnException) {
|
|
494
|
+
throw error;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
yield this.axiosInstance.post("/v1/persons/track", {
|
|
499
|
+
identifier,
|
|
500
|
+
eventName: eventName,
|
|
501
|
+
properties: normalizedProps,
|
|
502
|
+
});
|
|
503
|
+
if (this.config.debug) {
|
|
504
|
+
this.logger.debug(`[CDP] Tracked event ${eventName} for ${identifier}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
if (this.config.debug) {
|
|
509
|
+
const errorSummary = {
|
|
510
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
511
|
+
status: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
512
|
+
data: ((_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "[truncated]",
|
|
513
|
+
};
|
|
514
|
+
this.logger.error("[CDP] Track error:", { errorSummary });
|
|
515
|
+
}
|
|
516
|
+
if (this.config.failOnException) {
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
}));
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Register a device for a person. A device must be registered to send push notifications
|
|
526
|
+
* @param identifier
|
|
527
|
+
* @param deviceRegistrationParameters
|
|
528
|
+
* @throws Error only when config.failOnException === true (e.g., when validation or request fails)
|
|
529
|
+
*/
|
|
530
|
+
registerDevice(identifier, deviceRegistrationParameters) {
|
|
531
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
532
|
+
return this.limited(() => __awaiter(this, void 0, void 0, function* () {
|
|
533
|
+
var _a, _b, _c;
|
|
534
|
+
try {
|
|
535
|
+
validateIdentifier(identifier);
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
if (this.config.debug) {
|
|
539
|
+
this.logger.error("[CDP] Register device validation error", {
|
|
540
|
+
error,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
if (this.config.failOnException) {
|
|
544
|
+
throw error;
|
|
545
|
+
}
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (this.sendToCustomerIo && this.customerIoClient) {
|
|
549
|
+
try {
|
|
550
|
+
yield this.customerIoClient.addDevice(identifier, deviceRegistrationParameters.deviceId, deviceRegistrationParameters.platform, deviceRegistrationParameters);
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
if (this.config.debug) {
|
|
554
|
+
this.logger.error("[Customer.io] Register device error", { error });
|
|
555
|
+
}
|
|
556
|
+
if (this.config.failOnException) {
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
try {
|
|
562
|
+
yield this.axiosInstance.post("/v1/persons/registerDevice", Object.assign({ identifier }, deviceRegistrationParameters));
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
if (this.config.debug) {
|
|
566
|
+
// NB: Avoid logging large error objects directly to reduce memory footprint on high traffic apps
|
|
567
|
+
const errorSummary = {
|
|
568
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
569
|
+
status: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
570
|
+
data: ((_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "[truncated]",
|
|
571
|
+
};
|
|
572
|
+
this.logger.error("[CDP] Register device error:", { errorSummary });
|
|
573
|
+
}
|
|
574
|
+
if (this.config.failOnException) {
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}));
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Send an email using the CDP transactional email service
|
|
583
|
+
* @param request The send email request parameters
|
|
584
|
+
* @returns Promise that resolves when the email is sent
|
|
585
|
+
* @throws Error only when config.failOnException === true and validation or the request fails
|
|
586
|
+
*/
|
|
587
|
+
sendEmail(request) {
|
|
588
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
589
|
+
return this.limited(() => __awaiter(this, void 0, void 0, function* () {
|
|
590
|
+
var _a, _b, _c, _d;
|
|
591
|
+
try {
|
|
592
|
+
validateSendEmailRequest(request);
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
if (this.config.debug) {
|
|
596
|
+
this.logger.error("[CDP] Send email validation error", { error });
|
|
597
|
+
}
|
|
598
|
+
if (this.config.failOnException) {
|
|
599
|
+
throw error;
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
// Check for unsupported fields and log warnings
|
|
604
|
+
this.warnUnsupportedFields(request);
|
|
605
|
+
// Build the request payload - pass through all fields as they match the schema
|
|
606
|
+
const message = request.message;
|
|
607
|
+
const emailPayload = {
|
|
608
|
+
to: message.to,
|
|
609
|
+
identifiers: message.identifiers,
|
|
610
|
+
message_data: message.message_data,
|
|
611
|
+
send_at: message.send_at,
|
|
612
|
+
disable_message_retention: message.disable_message_retention,
|
|
613
|
+
send_to_unsubscribed: message.send_to_unsubscribed,
|
|
614
|
+
queue_draft: message.queue_draft,
|
|
615
|
+
bcc: message.bcc,
|
|
616
|
+
cc: message.cc,
|
|
617
|
+
fake_bcc: message.fake_bcc,
|
|
618
|
+
reply_to: message.reply_to,
|
|
619
|
+
preheader: message.preheader,
|
|
620
|
+
headers: message.headers,
|
|
621
|
+
disable_css_preprocessing: message.disable_css_preprocessing,
|
|
622
|
+
tracked: message.tracked,
|
|
623
|
+
transactional_message_id: "transactional_message_id" in message
|
|
624
|
+
? message.transactional_message_id
|
|
625
|
+
: undefined,
|
|
626
|
+
body: "body" in message ? message.body : undefined,
|
|
627
|
+
body_amp: message.amp_body,
|
|
628
|
+
body_plain: message.plaintext_body,
|
|
629
|
+
subject: "subject" in message ? message.subject : undefined,
|
|
630
|
+
from: "from" in message ? message.from : undefined,
|
|
631
|
+
language: message.language,
|
|
632
|
+
};
|
|
633
|
+
// Remove undefined values to keep the payload clean
|
|
634
|
+
const cleanPayload = Object.fromEntries(Object.entries(emailPayload).filter(([_, value]) => value !== undefined));
|
|
635
|
+
if (this.sendToCustomerIo && this.customerIoClient && this.config.debug) {
|
|
636
|
+
// Warning that to avoid sending twice it will not be sent to CIO. to turn this off set sendToCustomerIo to false.
|
|
637
|
+
this.logger.warn("[CDP] Warning: Transactional messaging email will NOT be sent to Customer.io to avoid sending twice. To turn this warning off set `sendToCustomerIo` to false.");
|
|
638
|
+
}
|
|
639
|
+
try {
|
|
640
|
+
const response = yield this.axiosInstance.post("/v1/send/email", cleanPayload);
|
|
641
|
+
if (this.config.debug) {
|
|
642
|
+
this.logger.debug(`[CDP] Email sent successfully to ${message.to}`);
|
|
643
|
+
}
|
|
644
|
+
return response.data;
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
const errorSummary = {
|
|
648
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
649
|
+
status: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
650
|
+
data: ((_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "[truncated]",
|
|
651
|
+
};
|
|
652
|
+
if (this.config.debug) {
|
|
653
|
+
this.logger.error("[CDP] Send email error:", { errorSummary });
|
|
654
|
+
}
|
|
655
|
+
// Clean up the error to remove technical details while preserving helpful information
|
|
656
|
+
const cleanError = error;
|
|
657
|
+
// Set consistent error properties
|
|
658
|
+
if (errorSummary.data) {
|
|
659
|
+
cleanError.message = errorSummary.data;
|
|
660
|
+
}
|
|
661
|
+
else if (errorSummary.message) {
|
|
662
|
+
cleanError.message = errorSummary.message;
|
|
663
|
+
}
|
|
664
|
+
cleanError.name = "CDPEmailError";
|
|
665
|
+
cleanError.code = "EMAIL_SEND_FAILED";
|
|
666
|
+
cleanError.summary = errorSummary;
|
|
667
|
+
cleanError.status = ((_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status) || 400;
|
|
668
|
+
// Remove technical details that clutter the error
|
|
669
|
+
delete cleanError.config;
|
|
670
|
+
delete cleanError.request;
|
|
671
|
+
// delete cleanError.response;
|
|
672
|
+
delete cleanError.stack;
|
|
673
|
+
if (this.config.failOnException) {
|
|
674
|
+
throw cleanError;
|
|
675
|
+
}
|
|
676
|
+
return { ok: false, error: cleanError };
|
|
677
|
+
}
|
|
678
|
+
}));
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Warns about unsupported fields that are accepted but not processed by the backend
|
|
683
|
+
* @param request The send email request parameters
|
|
684
|
+
*/
|
|
685
|
+
warnUnsupportedFields(request) {
|
|
686
|
+
const message = request.message;
|
|
687
|
+
const unsupportedFields = [];
|
|
688
|
+
if (message.send_at !== undefined) {
|
|
689
|
+
unsupportedFields.push("send_at");
|
|
690
|
+
}
|
|
691
|
+
if (message.disable_message_retention !== undefined) {
|
|
692
|
+
unsupportedFields.push("disable_message_retention");
|
|
693
|
+
}
|
|
694
|
+
if (message.send_to_unsubscribed !== undefined) {
|
|
695
|
+
unsupportedFields.push("send_to_unsubscribed");
|
|
696
|
+
}
|
|
697
|
+
if (message.queue_draft !== undefined) {
|
|
698
|
+
unsupportedFields.push("queue_draft");
|
|
699
|
+
}
|
|
700
|
+
if (message.headers !== undefined) {
|
|
701
|
+
unsupportedFields.push("headers");
|
|
702
|
+
}
|
|
703
|
+
if (message.disable_css_preprocessing !== undefined) {
|
|
704
|
+
unsupportedFields.push("disable_css_preprocessing");
|
|
705
|
+
}
|
|
706
|
+
if (message.tracked !== undefined) {
|
|
707
|
+
unsupportedFields.push("tracked");
|
|
708
|
+
}
|
|
709
|
+
if (message.fake_bcc !== undefined) {
|
|
710
|
+
unsupportedFields.push("fake_bcc");
|
|
711
|
+
}
|
|
712
|
+
if (message.reply_to !== undefined) {
|
|
713
|
+
unsupportedFields.push("reply_to");
|
|
714
|
+
}
|
|
715
|
+
if (message.preheader !== undefined) {
|
|
716
|
+
unsupportedFields.push("preheader");
|
|
717
|
+
}
|
|
718
|
+
if (message.attachments !== undefined) {
|
|
719
|
+
unsupportedFields.push("attachments");
|
|
720
|
+
}
|
|
721
|
+
if (unsupportedFields.length > 0) {
|
|
722
|
+
this.logger.warn(`[CDP] Warning: The following fields are not yet supported by the backend and will be ignored: ${unsupportedFields.join(", ")}. ` +
|
|
723
|
+
"These fields are included for future compatibility but have no effect on email delivery.");
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Send a push notification using the OpenCDP transactional push service
|
|
728
|
+
* @param request The send push request parameters
|
|
729
|
+
* @returns Promise that resolves when the push notification is sent
|
|
730
|
+
* @throws Error only when config.failOnException === true and validation or the request fails
|
|
731
|
+
*/
|
|
732
|
+
sendPush(request) {
|
|
733
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
734
|
+
return this.limited(() => __awaiter(this, void 0, void 0, function* () {
|
|
735
|
+
var _a, _b, _c, _d;
|
|
736
|
+
try {
|
|
737
|
+
validateSendPushRequest(request);
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
if (this.config.debug) {
|
|
741
|
+
this.logger.error("[CDP] Send push validation error", { error });
|
|
742
|
+
}
|
|
743
|
+
if (this.config.failOnException) {
|
|
744
|
+
throw error;
|
|
745
|
+
}
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
// Build the request payload - pass through all fields as they match the schema
|
|
749
|
+
const pushPayload = {
|
|
750
|
+
identifiers: request.identifiers,
|
|
751
|
+
transactional_message_id: request.transactional_message_id,
|
|
752
|
+
title: request.title,
|
|
753
|
+
body: request.body,
|
|
754
|
+
message_data: request.message_data,
|
|
755
|
+
};
|
|
756
|
+
// Remove undefined values to keep the payload clean
|
|
757
|
+
const cleanPayload = Object.fromEntries(Object.entries(pushPayload).filter(([_, value]) => value !== undefined));
|
|
758
|
+
if (this.sendToCustomerIo && this.customerIoClient && this.config.debug) {
|
|
759
|
+
// Warning that to avoid sending twice it will not be sent to CIO. to turn this off set sendToCustomerIo to false.
|
|
760
|
+
this.logger.warn("[CDP] Warning: Transactional messaging push will NOT be sent to Customer.io to avoid sending twice. To turn this warning off set `sendToCustomerIo` to false.");
|
|
761
|
+
}
|
|
762
|
+
try {
|
|
763
|
+
const response = yield this.axiosInstance.post("/v1/send/push", cleanPayload);
|
|
764
|
+
if (this.config.debug) {
|
|
765
|
+
this.logger.debug(`[CDP] Push notification sent successfully`);
|
|
766
|
+
}
|
|
767
|
+
return response.data;
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
const errorSummary = {
|
|
771
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
772
|
+
status: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
773
|
+
data: ((_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "[truncated]",
|
|
774
|
+
};
|
|
775
|
+
if (this.config.debug) {
|
|
776
|
+
this.logger.error("[CDP] Send push error:", { errorSummary });
|
|
777
|
+
}
|
|
778
|
+
// Clean up the error to remove technical details while preserving helpful information
|
|
779
|
+
const cleanError = error;
|
|
780
|
+
if (errorSummary.data) {
|
|
781
|
+
cleanError.message = errorSummary.data;
|
|
782
|
+
}
|
|
783
|
+
else if (errorSummary.message) {
|
|
784
|
+
cleanError.message = errorSummary.message;
|
|
785
|
+
}
|
|
786
|
+
// Set consistent error properties
|
|
787
|
+
cleanError.name = "CDPPushError";
|
|
788
|
+
cleanError.code = "PUSH_SEND_FAILED";
|
|
789
|
+
cleanError.summary = errorSummary;
|
|
790
|
+
cleanError.status = ((_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status) || 400;
|
|
791
|
+
// Remove technical details that clutter the error
|
|
792
|
+
delete cleanError.config;
|
|
793
|
+
delete cleanError.request;
|
|
794
|
+
delete cleanError.response;
|
|
795
|
+
delete cleanError.stack;
|
|
796
|
+
if (this.config.failOnException) {
|
|
797
|
+
throw cleanError;
|
|
798
|
+
}
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
}));
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Send an SMS using the OpenCDP transactional SMS service
|
|
806
|
+
* @param request The send SMS request parameters
|
|
807
|
+
* @returns Promise that resolves when the SMS is sent
|
|
808
|
+
* @throws Error only when config.failOnException === true and validation or the request fails
|
|
809
|
+
*/
|
|
810
|
+
sendSms(request) {
|
|
811
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
812
|
+
return this.limited(() => __awaiter(this, void 0, void 0, function* () {
|
|
813
|
+
var _a, _b, _c, _d;
|
|
814
|
+
try {
|
|
815
|
+
validateSendSmsRequest(request);
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
if (this.config.debug) {
|
|
819
|
+
this.logger.error("[CDP] Send SMS validation error", { error });
|
|
820
|
+
}
|
|
821
|
+
if (this.config.failOnException) {
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
// Build the request payload - pass through all fields as they match the schema
|
|
827
|
+
// Convert transactional_message_id to string if it's a number (backend expects string)
|
|
828
|
+
const transactionalMessageId = request.transactional_message_id !== undefined &&
|
|
829
|
+
request.transactional_message_id !== null &&
|
|
830
|
+
request.transactional_message_id !== ""
|
|
831
|
+
? String(request.transactional_message_id)
|
|
832
|
+
: undefined;
|
|
833
|
+
const smsPayload = {
|
|
834
|
+
identifiers: request.identifiers,
|
|
835
|
+
transactional_message_id: transactionalMessageId,
|
|
836
|
+
to: request.to,
|
|
837
|
+
from: request.from,
|
|
838
|
+
body: request.body,
|
|
839
|
+
message_data: request.message_data,
|
|
840
|
+
};
|
|
841
|
+
// Remove undefined values to keep the payload clean
|
|
842
|
+
const cleanPayload = Object.fromEntries(Object.entries(smsPayload).filter(([_, value]) => value !== undefined));
|
|
843
|
+
if (this.sendToCustomerIo && this.customerIoClient && this.config.debug) {
|
|
844
|
+
// Warning that to avoid sending twice it will not be sent to CIO. to turn this off set sendToCustomerIo to false.
|
|
845
|
+
this.logger.warn("[CDP] Warning: Transactional messaging SMS will NOT be sent to Customer.io to avoid sending twice. To turn this warning off set `sendToCustomerIo` to false.");
|
|
846
|
+
}
|
|
847
|
+
try {
|
|
848
|
+
const response = yield this.axiosInstance.post("/v1/send/sms", cleanPayload);
|
|
849
|
+
if (this.config.debug) {
|
|
850
|
+
this.logger.debug(`[CDP] SMS sent successfully`);
|
|
851
|
+
}
|
|
852
|
+
return response.data;
|
|
853
|
+
}
|
|
854
|
+
catch (error) {
|
|
855
|
+
const errorSummary = {
|
|
856
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
857
|
+
status: (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status,
|
|
858
|
+
data: ((_c = (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "[truncated]",
|
|
859
|
+
};
|
|
860
|
+
if (this.config.debug) {
|
|
861
|
+
this.logger.error("[CDP] Send SMS error:", { errorSummary });
|
|
862
|
+
}
|
|
863
|
+
// Clean up the error to remove technical details while preserving helpful information
|
|
864
|
+
const cleanError = error;
|
|
865
|
+
if (errorSummary.data) {
|
|
866
|
+
cleanError.message = errorSummary.data;
|
|
867
|
+
}
|
|
868
|
+
else if (errorSummary.message) {
|
|
869
|
+
cleanError.message = errorSummary.message;
|
|
870
|
+
}
|
|
871
|
+
// Set consistent error properties
|
|
872
|
+
cleanError.name = "CDPSmsError";
|
|
873
|
+
cleanError.code = "SMS_SEND_FAILED";
|
|
874
|
+
cleanError.summary = errorSummary;
|
|
875
|
+
cleanError.status = ((_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status) || 400;
|
|
876
|
+
// Remove technical details that clutter the error
|
|
877
|
+
delete cleanError.config;
|
|
878
|
+
delete cleanError.request;
|
|
879
|
+
delete cleanError.response;
|
|
880
|
+
delete cleanError.stack;
|
|
881
|
+
if (this.config.failOnException) {
|
|
882
|
+
throw cleanError;
|
|
883
|
+
}
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
}));
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
exports.CDPClient = CDPClient;
|