@bentonow/bento-node-sdk 1.0.4 → 1.0.6
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 +270 -100
- package/dist/index.js +106 -65
- package/dist/sdk/batch/index.d.ts +1 -1
- package/dist/sdk/broadcasts/index.d.ts +2 -1
- package/dist/sdk/broadcasts/types.d.ts +4 -0
- package/dist/sdk/client/errors.d.ts +3 -0
- package/dist/sdk/client/index.d.ts +23 -3
- package/dist/sdk/interfaces.d.ts +4 -0
- package/dist/sdk/sequences/index.d.ts +12 -3
- package/dist/sdk/sequences/types.d.ts +14 -0
- package/dist/sdk/workflows/index.d.ts +2 -2
- package/dist/versions/v1/index.d.ts +7 -3
- package/package.json +5 -2
- package/src/sdk/batch/index.ts +10 -5
- package/src/sdk/broadcasts/index.ts +8 -7
- package/src/sdk/broadcasts/types.ts +5 -1
- package/src/sdk/client/errors.ts +7 -0
- package/src/sdk/client/index.ts +122 -49
- package/src/sdk/interfaces.ts +4 -0
- package/src/sdk/sequences/index.ts +26 -3
- package/src/sdk/sequences/types.ts +17 -0
- package/src/sdk/workflows/index.ts +4 -4
- package/src/versions/v1/index.ts +16 -10
package/dist/index.js
CHANGED
|
@@ -626,7 +626,7 @@ class BentoBatch {
|
|
|
626
626
|
}
|
|
627
627
|
const result = await this._client.post(`${this._url}/subscribers`, {
|
|
628
628
|
subscribers: parameters.subscribers
|
|
629
|
-
});
|
|
629
|
+
}, { timeout: null });
|
|
630
630
|
return result.results;
|
|
631
631
|
}
|
|
632
632
|
async importEvents(parameters) {
|
|
@@ -638,7 +638,7 @@ class BentoBatch {
|
|
|
638
638
|
}
|
|
639
639
|
const result = await this._client.post(`${this._url}/events`, {
|
|
640
640
|
events: parameters.events
|
|
641
|
-
});
|
|
641
|
+
}, { timeout: null });
|
|
642
642
|
return result.results;
|
|
643
643
|
}
|
|
644
644
|
async sendTransactionalEmails(parameters) {
|
|
@@ -679,6 +679,13 @@ class AuthorNotAuthorizedError extends Error {
|
|
|
679
679
|
}
|
|
680
680
|
}
|
|
681
681
|
|
|
682
|
+
class RequestTimeoutError extends Error {
|
|
683
|
+
constructor(message = "Request timed out") {
|
|
684
|
+
super(message);
|
|
685
|
+
this.name = "RequestTimeoutError";
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
682
689
|
// src/sdk/client/index.ts
|
|
683
690
|
function encodeBase64(str) {
|
|
684
691
|
if (typeof btoa === "function") {
|
|
@@ -704,61 +711,83 @@ class BentoClient {
|
|
|
704
711
|
_baseUrl = "https://app.bentonow.com/api/v1";
|
|
705
712
|
_siteUuid = "";
|
|
706
713
|
_logErrors = false;
|
|
714
|
+
_timeout = 30000;
|
|
707
715
|
constructor(options) {
|
|
708
716
|
this._baseUrl = options.clientOptions?.baseUrl || this._baseUrl;
|
|
709
717
|
this._siteUuid = options.siteUuid;
|
|
710
718
|
this._headers = this._extractHeaders(options.authentication, options.siteUuid);
|
|
711
719
|
this._logErrors = options.logErrors || false;
|
|
720
|
+
this._timeout = options.clientOptions?.timeout ?? this._timeout;
|
|
721
|
+
}
|
|
722
|
+
async get(endpoint, payload = {}, requestOptions = {}) {
|
|
723
|
+
const queryParameters = this._getQueryParameters(payload);
|
|
724
|
+
const url = `${this._baseUrl}${endpoint}?${queryParameters}`;
|
|
725
|
+
const timeoutMs = requestOptions.timeout === undefined ? this._timeout : requestOptions.timeout;
|
|
726
|
+
const response = await this._fetchWithTimeout(url, {
|
|
727
|
+
method: "GET",
|
|
728
|
+
headers: this._headers
|
|
729
|
+
}, timeoutMs);
|
|
730
|
+
return this._handleResponse(response);
|
|
731
|
+
}
|
|
732
|
+
async post(endpoint, payload = {}, requestOptions = {}) {
|
|
733
|
+
const body = this._getBody(payload);
|
|
734
|
+
const url = `${this._baseUrl}${endpoint}`;
|
|
735
|
+
const timeoutMs = requestOptions.timeout === undefined ? this._timeout : requestOptions.timeout;
|
|
736
|
+
const response = await this._fetchWithTimeout(url, {
|
|
737
|
+
method: "POST",
|
|
738
|
+
headers: {
|
|
739
|
+
...this._headers,
|
|
740
|
+
"Content-Type": "application/json"
|
|
741
|
+
},
|
|
742
|
+
body
|
|
743
|
+
}, timeoutMs);
|
|
744
|
+
return this._handleResponse(response);
|
|
745
|
+
}
|
|
746
|
+
async patch(endpoint, payload = {}, requestOptions = {}) {
|
|
747
|
+
const body = this._getBody(payload);
|
|
748
|
+
const url = `${this._baseUrl}${endpoint}`;
|
|
749
|
+
const timeoutMs = requestOptions.timeout === undefined ? this._timeout : requestOptions.timeout;
|
|
750
|
+
const response = await this._fetchWithTimeout(url, {
|
|
751
|
+
method: "PATCH",
|
|
752
|
+
headers: {
|
|
753
|
+
...this._headers,
|
|
754
|
+
"Content-Type": "application/json"
|
|
755
|
+
},
|
|
756
|
+
body
|
|
757
|
+
}, timeoutMs);
|
|
758
|
+
return this._handleResponse(response);
|
|
759
|
+
}
|
|
760
|
+
async _fetchWithTimeout(url, options, timeout) {
|
|
761
|
+
if (timeout === null) {
|
|
762
|
+
return import_cross_fetch.default(url, options);
|
|
763
|
+
}
|
|
764
|
+
const controller = new AbortController;
|
|
765
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
766
|
+
try {
|
|
767
|
+
const response = await import_cross_fetch.default(url, {
|
|
768
|
+
...options,
|
|
769
|
+
signal: controller.signal
|
|
770
|
+
});
|
|
771
|
+
return response;
|
|
772
|
+
} catch (error) {
|
|
773
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
774
|
+
throw new RequestTimeoutError(`Request timed out after ${timeout}ms`);
|
|
775
|
+
}
|
|
776
|
+
throw error;
|
|
777
|
+
} finally {
|
|
778
|
+
clearTimeout(timeoutId);
|
|
779
|
+
}
|
|
712
780
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
throw await this._getErrorForResponse(result);
|
|
724
|
-
}).then((data) => resolve(data)).catch((error) => reject(error));
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
post(endpoint, payload = {}) {
|
|
728
|
-
return new Promise((resolve, reject) => {
|
|
729
|
-
const body = this._getBody(payload);
|
|
730
|
-
import_cross_fetch.default(`${this._baseUrl}${endpoint}`, {
|
|
731
|
-
method: "POST",
|
|
732
|
-
headers: {
|
|
733
|
-
...this._headers,
|
|
734
|
-
"Content-Type": "application/json"
|
|
735
|
-
},
|
|
736
|
-
body
|
|
737
|
-
}).then(async (result) => {
|
|
738
|
-
if (this._isSuccessfulStatus(result.status)) {
|
|
739
|
-
return result.json();
|
|
740
|
-
}
|
|
741
|
-
throw await this._getErrorForResponse(result);
|
|
742
|
-
}).then((data) => resolve(data)).catch((error) => reject(error));
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
patch(endpoint, payload = {}) {
|
|
746
|
-
return new Promise((resolve, reject) => {
|
|
747
|
-
const body = this._getBody(payload);
|
|
748
|
-
import_cross_fetch.default(`${this._baseUrl}${endpoint}`, {
|
|
749
|
-
method: "PATCH",
|
|
750
|
-
headers: {
|
|
751
|
-
...this._headers,
|
|
752
|
-
"Content-Type": "application/json"
|
|
753
|
-
},
|
|
754
|
-
body
|
|
755
|
-
}).then(async (result) => {
|
|
756
|
-
if (this._isSuccessfulStatus(result.status)) {
|
|
757
|
-
return result.json();
|
|
758
|
-
}
|
|
759
|
-
throw await this._getErrorForResponse(result);
|
|
760
|
-
}).then((data) => resolve(data)).catch((error) => reject(error));
|
|
761
|
-
});
|
|
781
|
+
async _handleResponse(response) {
|
|
782
|
+
if (this._isSuccessfulStatus(response.status)) {
|
|
783
|
+
try {
|
|
784
|
+
const data = await response.json();
|
|
785
|
+
return data;
|
|
786
|
+
} catch {
|
|
787
|
+
throw new Error(`[${response.status}] - Invalid JSON response from server`);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
throw await this._getErrorForResponse(response);
|
|
762
791
|
}
|
|
763
792
|
_extractHeaders(authentication, siteUuid) {
|
|
764
793
|
const authenticationKey = encodeBase64(`${authentication.publishableKey}:${authentication.secretKey}`);
|
|
@@ -780,7 +809,7 @@ class BentoClient {
|
|
|
780
809
|
};
|
|
781
810
|
const queryParameters = new URLSearchParams;
|
|
782
811
|
for (const [key, value] of Object.entries(body)) {
|
|
783
|
-
queryParameters.append(key, value);
|
|
812
|
+
queryParameters.append(key, String(value));
|
|
784
813
|
}
|
|
785
814
|
return queryParameters.toString();
|
|
786
815
|
}
|
|
@@ -1034,6 +1063,14 @@ class BentoSequences {
|
|
|
1034
1063
|
}
|
|
1035
1064
|
async getSequences() {
|
|
1036
1065
|
const result = await this._client.get(this._url);
|
|
1066
|
+
if (!result || Object.keys(result).length === 0)
|
|
1067
|
+
return [];
|
|
1068
|
+
return result.data ?? [];
|
|
1069
|
+
}
|
|
1070
|
+
async createSequenceEmail(sequenceId, parameters) {
|
|
1071
|
+
const result = await this._client.post(`${this._url}/${sequenceId}/emails/templates`, {
|
|
1072
|
+
email_template: parameters
|
|
1073
|
+
});
|
|
1037
1074
|
if (Object.keys(result).length === 0 || !result.data)
|
|
1038
1075
|
return null;
|
|
1039
1076
|
return result.data;
|
|
@@ -1092,16 +1129,17 @@ class BentoWorkflows {
|
|
|
1092
1129
|
}
|
|
1093
1130
|
async getWorkflows() {
|
|
1094
1131
|
const result = await this._client.get(this._url);
|
|
1095
|
-
if (Object.keys(result).length === 0
|
|
1096
|
-
return
|
|
1097
|
-
return result.data;
|
|
1132
|
+
if (!result || Object.keys(result).length === 0)
|
|
1133
|
+
return [];
|
|
1134
|
+
return result.data ?? [];
|
|
1098
1135
|
}
|
|
1099
1136
|
}
|
|
1100
1137
|
// src/sdk/broadcasts/index.ts
|
|
1101
1138
|
class BentoBroadcasts {
|
|
1102
1139
|
_client;
|
|
1103
|
-
|
|
1104
|
-
|
|
1140
|
+
_fetchUrl = "/fetch/broadcasts";
|
|
1141
|
+
_batchUrl = "/batch/broadcasts";
|
|
1142
|
+
_emailsUrl = "/batch/emails";
|
|
1105
1143
|
constructor(_client) {
|
|
1106
1144
|
this._client = _client;
|
|
1107
1145
|
}
|
|
@@ -1112,11 +1150,11 @@ class BentoBroadcasts {
|
|
|
1112
1150
|
return result.results;
|
|
1113
1151
|
}
|
|
1114
1152
|
async getBroadcasts() {
|
|
1115
|
-
const result = await this._client.get(this.
|
|
1153
|
+
const result = await this._client.get(this._fetchUrl);
|
|
1116
1154
|
return result.data ?? [];
|
|
1117
1155
|
}
|
|
1118
1156
|
async createBroadcast(broadcasts) {
|
|
1119
|
-
const result = await this._client.post(this.
|
|
1157
|
+
const result = await this._client.post(this._batchUrl, {
|
|
1120
1158
|
broadcasts
|
|
1121
1159
|
});
|
|
1122
1160
|
return result.data ?? [];
|
|
@@ -1248,12 +1286,14 @@ class BentoAPIV1 {
|
|
|
1248
1286
|
}
|
|
1249
1287
|
async upsertSubscriber(parameters) {
|
|
1250
1288
|
await this.Batch.importSubscribers({
|
|
1251
|
-
subscribers: [
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1289
|
+
subscribers: [
|
|
1290
|
+
{
|
|
1291
|
+
email: parameters.email,
|
|
1292
|
+
...parameters.fields,
|
|
1293
|
+
...parameters.tags && { tags: parameters.tags },
|
|
1294
|
+
...parameters.remove_tags && { remove_tags: parameters.remove_tags }
|
|
1295
|
+
}
|
|
1296
|
+
]
|
|
1257
1297
|
});
|
|
1258
1298
|
return this.Subscribers.getSubscribers({
|
|
1259
1299
|
email: parameters.email
|
|
@@ -1269,6 +1309,7 @@ class Analytics {
|
|
|
1269
1309
|
}
|
|
1270
1310
|
}
|
|
1271
1311
|
export {
|
|
1312
|
+
RequestTimeoutError,
|
|
1272
1313
|
RateLimitedError,
|
|
1273
1314
|
NotAuthorizedError,
|
|
1274
1315
|
AuthorNotAuthorizedError,
|
|
@@ -44,7 +44,7 @@ export declare class BentoBatch<S, E extends string> {
|
|
|
44
44
|
* Each email must have a `to` address, a `from` address, a `subject`, an `html_body`
|
|
45
45
|
* and `transactional: true`.
|
|
46
46
|
* In addition you can add a `personalizations` object to provide
|
|
47
|
-
* liquid
|
|
47
|
+
* liquid tags that will be injected into the email.
|
|
48
48
|
*
|
|
49
49
|
* Returns the number of events that were imported.
|
|
50
50
|
*
|
|
@@ -2,7 +2,8 @@ import type { BentoClient } from '../client';
|
|
|
2
2
|
import type { Broadcast, CreateBroadcastInput, EmailData } from './types';
|
|
3
3
|
export declare class BentoBroadcasts {
|
|
4
4
|
private readonly _client;
|
|
5
|
-
private readonly
|
|
5
|
+
private readonly _fetchUrl;
|
|
6
|
+
private readonly _batchUrl;
|
|
6
7
|
private readonly _emailsUrl;
|
|
7
8
|
constructor(_client: BentoClient);
|
|
8
9
|
/**
|
|
@@ -18,6 +18,10 @@ export type BroadcastAttributes = {
|
|
|
18
18
|
};
|
|
19
19
|
export type Broadcast = BaseEntity<BroadcastAttributes>;
|
|
20
20
|
export type CreateBroadcastInput = Omit<BroadcastAttributes, 'created_at'>;
|
|
21
|
+
/**
|
|
22
|
+
* Email data for transactional emails.
|
|
23
|
+
* Note: This is the same structure as TransactionalEmail in batch/types.ts
|
|
24
|
+
*/
|
|
21
25
|
export type EmailData = {
|
|
22
26
|
to: string;
|
|
23
27
|
from: string;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { AnalyticsOptions } from '../interfaces';
|
|
2
|
+
interface RequestOptions {
|
|
3
|
+
timeout?: number | null;
|
|
4
|
+
}
|
|
2
5
|
export declare class BentoClient {
|
|
3
6
|
private readonly _headers;
|
|
4
7
|
private readonly _baseUrl;
|
|
5
8
|
private readonly _siteUuid;
|
|
6
9
|
private readonly _logErrors;
|
|
10
|
+
private readonly _timeout;
|
|
7
11
|
constructor(options: AnalyticsOptions);
|
|
8
12
|
/**
|
|
9
13
|
* Wraps a GET request to the Bento API and automatically adds the required
|
|
@@ -13,7 +17,7 @@ export declare class BentoClient {
|
|
|
13
17
|
* @param payload object
|
|
14
18
|
* @returns Promise\<T\>
|
|
15
19
|
* */
|
|
16
|
-
get<T>(endpoint: string, payload?: Record<string, unknown
|
|
20
|
+
get<T>(endpoint: string, payload?: Record<string, unknown>, requestOptions?: RequestOptions): Promise<T>;
|
|
17
21
|
/**
|
|
18
22
|
* Wraps a POST request to the Bento API and automatically adds the required
|
|
19
23
|
* headers.
|
|
@@ -22,7 +26,7 @@ export declare class BentoClient {
|
|
|
22
26
|
* @param payload object
|
|
23
27
|
* @returns Promise\<T\>
|
|
24
28
|
* */
|
|
25
|
-
post<T>(endpoint: string, payload?: Record<string, unknown
|
|
29
|
+
post<T>(endpoint: string, payload?: Record<string, unknown>, requestOptions?: RequestOptions): Promise<T>;
|
|
26
30
|
/**
|
|
27
31
|
* Wraps a PATCH request to the Bento API and automatically adds the required
|
|
28
32
|
* headers.
|
|
@@ -31,7 +35,22 @@ export declare class BentoClient {
|
|
|
31
35
|
* @param payload object
|
|
32
36
|
* @returns Promise\<T\>
|
|
33
37
|
* */
|
|
34
|
-
patch<T>(endpoint: string, payload?: Record<string, unknown
|
|
38
|
+
patch<T>(endpoint: string, payload?: Record<string, unknown>, requestOptions?: RequestOptions): Promise<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Performs a fetch request with a configurable timeout.
|
|
41
|
+
*
|
|
42
|
+
* @param url The URL to fetch
|
|
43
|
+
* @param options Fetch options
|
|
44
|
+
* @returns Promise<Response>
|
|
45
|
+
*/
|
|
46
|
+
private _fetchWithTimeout;
|
|
47
|
+
/**
|
|
48
|
+
* Handles the response from a fetch request, parsing JSON or throwing appropriate errors.
|
|
49
|
+
*
|
|
50
|
+
* @param response The fetch Response object
|
|
51
|
+
* @returns Promise<T> The parsed response data
|
|
52
|
+
*/
|
|
53
|
+
private _handleResponse;
|
|
35
54
|
/**
|
|
36
55
|
* Extracts the `publishableKey` and `secretKey` from the `authentication` options,
|
|
37
56
|
* adds the `Authorization` header, and includes a `User-Agent` header with the site UUID.
|
|
@@ -72,3 +91,4 @@ export declare class BentoClient {
|
|
|
72
91
|
*/
|
|
73
92
|
private _getErrorForResponse;
|
|
74
93
|
}
|
|
94
|
+
export {};
|
package/dist/sdk/interfaces.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BentoClient } from '../client';
|
|
2
|
-
import type {
|
|
2
|
+
import type { EmailTemplate } from '../email-templates/types';
|
|
3
|
+
import type { CreateSequenceEmailParameters, Sequence } from './types';
|
|
3
4
|
export declare class BentoSequences {
|
|
4
5
|
private readonly _client;
|
|
5
6
|
private readonly _url;
|
|
@@ -7,7 +8,15 @@ export declare class BentoSequences {
|
|
|
7
8
|
/**
|
|
8
9
|
* Returns all of the sequences for the site, including their email templates.
|
|
9
10
|
*
|
|
10
|
-
* @returns Promise\<Sequence[]
|
|
11
|
+
* @returns Promise\<Sequence[]\>
|
|
11
12
|
*/
|
|
12
|
-
getSequences(): Promise<Sequence[]
|
|
13
|
+
getSequences(): Promise<Sequence[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new email template inside a sequence.
|
|
16
|
+
*
|
|
17
|
+
* @param sequenceId string
|
|
18
|
+
* @param parameters CreateSequenceEmailParameters
|
|
19
|
+
* @returns Promise\<EmailTemplate | null\>
|
|
20
|
+
*/
|
|
21
|
+
createSequenceEmail(sequenceId: string, parameters: CreateSequenceEmailParameters): Promise<EmailTemplate | null>;
|
|
13
22
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BaseEntity } from '../types';
|
|
2
|
+
import type { EmailTemplate } from '../email-templates/types';
|
|
2
3
|
/**
|
|
3
4
|
* Embedded Email Template in Sequence
|
|
4
5
|
*/
|
|
@@ -16,3 +17,16 @@ export type SequenceAttributes = {
|
|
|
16
17
|
email_templates: SequenceEmailTemplate[];
|
|
17
18
|
};
|
|
18
19
|
export type Sequence = BaseEntity<SequenceAttributes>;
|
|
20
|
+
export type SequenceDelayInterval = 'minutes' | 'hours' | 'days' | 'months';
|
|
21
|
+
export type CreateSequenceEmailParameters = {
|
|
22
|
+
subject: string;
|
|
23
|
+
html: string;
|
|
24
|
+
inbox_snippet?: string;
|
|
25
|
+
delay_interval?: SequenceDelayInterval;
|
|
26
|
+
delay_interval_count?: number;
|
|
27
|
+
editor_choice?: string;
|
|
28
|
+
cc?: string;
|
|
29
|
+
bcc?: string;
|
|
30
|
+
to?: string;
|
|
31
|
+
};
|
|
32
|
+
export type CreateSequenceEmailResponse = EmailTemplate | null;
|
|
@@ -7,7 +7,7 @@ export declare class BentoWorkflows {
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns all of the workflows for the site, including their email templates.
|
|
9
9
|
*
|
|
10
|
-
* @returns Promise\<Workflow[]
|
|
10
|
+
* @returns Promise\<Workflow[]\>
|
|
11
11
|
*/
|
|
12
|
-
getWorkflows(): Promise<Workflow[]
|
|
12
|
+
getWorkflows(): Promise<Workflow[]>;
|
|
13
13
|
}
|
|
@@ -3,7 +3,7 @@ import type { AnalyticsOptions } from '../../sdk/interfaces';
|
|
|
3
3
|
import type { AddSubscriberParameters, RemoveSubscriberParameters, TagSubscriberParameters, TrackParameters, TrackPurchaseParameters, UpdateFieldsParameters } from './types';
|
|
4
4
|
import { BentoBroadcasts } from '../../sdk/broadcasts';
|
|
5
5
|
import { BentoStats } from '../../sdk/stats';
|
|
6
|
-
import { Subscriber } from '../../sdk/subscribers/types';
|
|
6
|
+
import type { Subscriber } from '../../sdk/subscribers/types';
|
|
7
7
|
export declare class BentoAPIV1<S = {
|
|
8
8
|
[key: string]: unknown;
|
|
9
9
|
}, E extends string = '$custom'> {
|
|
@@ -141,9 +141,13 @@ export declare class BentoAPIV1<S = {
|
|
|
141
141
|
* Upserts a subscriber in Bento. If the subscriber exists, their data will be updated.
|
|
142
142
|
* If they don't exist, they will be created with the provided data.
|
|
143
143
|
*
|
|
144
|
+
* This method still relies on the batch import queue (which can take 1-5 minutes to
|
|
145
|
+
* finish processing), but it automatically attempts to fetch and return the subscriber
|
|
146
|
+
* record after the import has been queued.
|
|
147
|
+
*
|
|
144
148
|
* @example
|
|
145
149
|
* ```typescript
|
|
146
|
-
* await analytics.V1.upsertSubscriber({
|
|
150
|
+
* const subscriber = await analytics.V1.upsertSubscriber({
|
|
147
151
|
* email: 'user@example.com',
|
|
148
152
|
* fields: {
|
|
149
153
|
* firstName: 'John',
|
|
@@ -155,7 +159,7 @@ export declare class BentoAPIV1<S = {
|
|
|
155
159
|
* ```
|
|
156
160
|
*
|
|
157
161
|
* @param parameters Object containing subscriber data including email, fields, and tags
|
|
158
|
-
* @returns Promise<Subscriber<S
|
|
162
|
+
* @returns Promise<Subscriber<S> | null> The created or updated subscriber
|
|
159
163
|
*/
|
|
160
164
|
upsertSubscriber(parameters: Omit<AddSubscriberParameters<S>, 'date'> & {
|
|
161
165
|
tags?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bentonow/bento-node-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "🍱 Bento Node.JS SDK and tracking library",
|
|
5
5
|
"author": "Backpack Internet",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,14 +34,17 @@
|
|
|
34
34
|
"homepage": "https://bentonow.com",
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@size-limit/preset-small-lib": "^11.1.5",
|
|
37
|
+
"@types/jest": "^30.0.0",
|
|
37
38
|
"@types/node": "^22.10.5",
|
|
38
39
|
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
39
40
|
"@typescript-eslint/parser": "^8.24.0",
|
|
40
41
|
"bun-types": "latest",
|
|
41
42
|
"eslint": "^9.20.1",
|
|
43
|
+
"jest": "^30.2.0",
|
|
42
44
|
"prettier": "^3.4.2",
|
|
43
45
|
"size-limit": "^11.1.6",
|
|
44
|
-
"
|
|
46
|
+
"ts-jest": "^29.4.6",
|
|
47
|
+
"typescript": "^5.7.3"
|
|
45
48
|
},
|
|
46
49
|
"dependencies": {
|
|
47
50
|
"cross-fetch": "^4.1.0"
|
package/src/sdk/batch/index.ts
CHANGED
|
@@ -54,7 +54,8 @@ export class BentoBatch<S, E extends string> {
|
|
|
54
54
|
`${this._url}/subscribers`,
|
|
55
55
|
{
|
|
56
56
|
subscribers: parameters.subscribers,
|
|
57
|
-
}
|
|
57
|
+
},
|
|
58
|
+
{ timeout: null }
|
|
58
59
|
);
|
|
59
60
|
|
|
60
61
|
return result.results;
|
|
@@ -80,9 +81,13 @@ export class BentoBatch<S, E extends string> {
|
|
|
80
81
|
throw new TooManyEventsError(`You must send between 1 and 1,000 events.`);
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
const result = await this._client.post<BatchImportEventsResponse>(
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
const result = await this._client.post<BatchImportEventsResponse>(
|
|
85
|
+
`${this._url}/events`,
|
|
86
|
+
{
|
|
87
|
+
events: parameters.events,
|
|
88
|
+
},
|
|
89
|
+
{ timeout: null }
|
|
90
|
+
);
|
|
86
91
|
|
|
87
92
|
return result.results;
|
|
88
93
|
}
|
|
@@ -94,7 +99,7 @@ export class BentoBatch<S, E extends string> {
|
|
|
94
99
|
* Each email must have a `to` address, a `from` address, a `subject`, an `html_body`
|
|
95
100
|
* and `transactional: true`.
|
|
96
101
|
* In addition you can add a `personalizations` object to provide
|
|
97
|
-
* liquid
|
|
102
|
+
* liquid tags that will be injected into the email.
|
|
98
103
|
*
|
|
99
104
|
* Returns the number of events that were imported.
|
|
100
105
|
*
|
|
@@ -3,8 +3,9 @@ import type { DataResponse } from '../client/types';
|
|
|
3
3
|
import type { Broadcast, CreateBroadcastInput, EmailData } from './types';
|
|
4
4
|
|
|
5
5
|
export class BentoBroadcasts {
|
|
6
|
-
private readonly
|
|
7
|
-
private readonly
|
|
6
|
+
private readonly _fetchUrl = '/fetch/broadcasts';
|
|
7
|
+
private readonly _batchUrl = '/batch/broadcasts';
|
|
8
|
+
private readonly _emailsUrl = '/batch/emails';
|
|
8
9
|
|
|
9
10
|
constructor(private readonly _client: BentoClient) {}
|
|
10
11
|
|
|
@@ -15,7 +16,7 @@ export class BentoBroadcasts {
|
|
|
15
16
|
*/
|
|
16
17
|
public async createEmails(emails: EmailData[]): Promise<number> {
|
|
17
18
|
const result = await this._client.post<{ results: number }>(this._emailsUrl, {
|
|
18
|
-
emails
|
|
19
|
+
emails,
|
|
19
20
|
});
|
|
20
21
|
return result.results;
|
|
21
22
|
}
|
|
@@ -25,7 +26,7 @@ export class BentoBroadcasts {
|
|
|
25
26
|
* @returns Promise<Broadcast[]>
|
|
26
27
|
*/
|
|
27
28
|
public async getBroadcasts(): Promise<Broadcast[]> {
|
|
28
|
-
const result = await this._client.get<DataResponse<Broadcast[]>>(this.
|
|
29
|
+
const result = await this._client.get<DataResponse<Broadcast[]>>(this._fetchUrl);
|
|
29
30
|
return result.data ?? [];
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -35,9 +36,9 @@ export class BentoBroadcasts {
|
|
|
35
36
|
* @returns Promise<Broadcast[]>
|
|
36
37
|
*/
|
|
37
38
|
public async createBroadcast(broadcasts: CreateBroadcastInput[]): Promise<Broadcast[]> {
|
|
38
|
-
const result = await this._client.post<DataResponse<Broadcast[]>>(this.
|
|
39
|
-
broadcasts
|
|
39
|
+
const result = await this._client.post<DataResponse<Broadcast[]>>(this._batchUrl, {
|
|
40
|
+
broadcasts,
|
|
40
41
|
});
|
|
41
42
|
return result.data ?? [];
|
|
42
43
|
}
|
|
43
|
-
}
|
|
44
|
+
}
|
|
@@ -24,6 +24,10 @@ export type Broadcast = BaseEntity<BroadcastAttributes>;
|
|
|
24
24
|
|
|
25
25
|
export type CreateBroadcastInput = Omit<BroadcastAttributes, 'created_at'>;
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Email data for transactional emails.
|
|
29
|
+
* Note: This is the same structure as TransactionalEmail in batch/types.ts
|
|
30
|
+
*/
|
|
27
31
|
export type EmailData = {
|
|
28
32
|
to: string;
|
|
29
33
|
from: string;
|
|
@@ -31,4 +35,4 @@ export type EmailData = {
|
|
|
31
35
|
html_body: string;
|
|
32
36
|
transactional: boolean;
|
|
33
37
|
personalizations?: Record<string, string | number | boolean>;
|
|
34
|
-
};
|
|
38
|
+
};
|
package/src/sdk/client/errors.ts
CHANGED
|
@@ -18,3 +18,10 @@ export class AuthorNotAuthorizedError extends Error {
|
|
|
18
18
|
this.name = 'AuthorNotAuthorizedError';
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
export class RequestTimeoutError extends Error {
|
|
23
|
+
constructor(message = 'Request timed out') {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = 'RequestTimeoutError';
|
|
26
|
+
}
|
|
27
|
+
}
|