@contentful/optimization-api-client 0.1.0-alpha
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/LICENSE +21 -0
- package/README.md +282 -0
- package/dist/ApiClient.d.ts +74 -0
- package/dist/ApiClient.d.ts.map +1 -0
- package/dist/ApiClient.js +61 -0
- package/dist/ApiClient.js.map +1 -0
- package/dist/ApiClientBase.d.ts +113 -0
- package/dist/ApiClientBase.d.ts.map +1 -0
- package/dist/ApiClientBase.js +94 -0
- package/dist/ApiClientBase.js.map +1 -0
- package/dist/builders/EventBuilder.d.ts +589 -0
- package/dist/builders/EventBuilder.d.ts.map +1 -0
- package/dist/builders/EventBuilder.js +349 -0
- package/dist/builders/EventBuilder.js.map +1 -0
- package/dist/builders/index.d.ts +3 -0
- package/dist/builders/index.d.ts.map +1 -0
- package/dist/builders/index.js +3 -0
- package/dist/builders/index.js.map +1 -0
- package/dist/experience/ExperienceApiClient.d.ts +267 -0
- package/dist/experience/ExperienceApiClient.d.ts.map +1 -0
- package/dist/experience/ExperienceApiClient.js +324 -0
- package/dist/experience/ExperienceApiClient.js.map +1 -0
- package/dist/experience/index.d.ts +4 -0
- package/dist/experience/index.d.ts.map +1 -0
- package/dist/experience/index.js +4 -0
- package/dist/experience/index.js.map +1 -0
- package/dist/fetch/Fetch.d.ts +96 -0
- package/dist/fetch/Fetch.d.ts.map +1 -0
- package/dist/fetch/Fetch.js +27 -0
- package/dist/fetch/Fetch.js.map +1 -0
- package/dist/fetch/createProtectedFetchMethod.d.ts +40 -0
- package/dist/fetch/createProtectedFetchMethod.d.ts.map +1 -0
- package/dist/fetch/createProtectedFetchMethod.js +53 -0
- package/dist/fetch/createProtectedFetchMethod.js.map +1 -0
- package/dist/fetch/createRetryFetchMethod.d.ts +60 -0
- package/dist/fetch/createRetryFetchMethod.d.ts.map +1 -0
- package/dist/fetch/createRetryFetchMethod.js +138 -0
- package/dist/fetch/createRetryFetchMethod.js.map +1 -0
- package/dist/fetch/createTimeoutFetchMethod.d.ts +51 -0
- package/dist/fetch/createTimeoutFetchMethod.d.ts.map +1 -0
- package/dist/fetch/createTimeoutFetchMethod.js +51 -0
- package/dist/fetch/createTimeoutFetchMethod.js.map +1 -0
- package/dist/fetch/index.d.ts +7 -0
- package/dist/fetch/index.d.ts.map +1 -0
- package/dist/fetch/index.js +7 -0
- package/dist/fetch/index.js.map +1 -0
- package/dist/index.cjs +708 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +583 -0
- package/dist/index.mjs.map +1 -0
- package/dist/insights/InsightsApiClient.d.ts +130 -0
- package/dist/insights/InsightsApiClient.d.ts.map +1 -0
- package/dist/insights/InsightsApiClient.js +142 -0
- package/dist/insights/InsightsApiClient.js.map +1 -0
- package/dist/insights/index.d.ts +4 -0
- package/dist/insights/index.d.ts.map +1 -0
- package/dist/insights/index.js +4 -0
- package/dist/insights/index.js.map +1 -0
- package/package.json +27 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
import * as __rspack_external__contentful_optimization_api_schemas_66dcf407 from "@contentful/optimization-api-schemas";
|
|
2
|
+
import { diary, enable } from "diary";
|
|
3
|
+
import "diary/utils";
|
|
4
|
+
import p_retry from "p-retry";
|
|
5
|
+
import { merge } from "es-toolkit";
|
|
6
|
+
import { boolean as mini_boolean, extend, number, object, optional, partial, prefault, string } from "zod/mini";
|
|
7
|
+
class Logger {
|
|
8
|
+
name = '@contentful/optimization';
|
|
9
|
+
PREFIX_PARTS = [
|
|
10
|
+
'Ctfl',
|
|
11
|
+
'O10n'
|
|
12
|
+
];
|
|
13
|
+
DELIMITER = ':';
|
|
14
|
+
diary;
|
|
15
|
+
sinks = [];
|
|
16
|
+
constructor(){
|
|
17
|
+
this.diary = diary(this.name, this.onLogEvent.bind(this));
|
|
18
|
+
enable(this.name);
|
|
19
|
+
}
|
|
20
|
+
assembleLocationPrefix(logLocation) {
|
|
21
|
+
return `[${[
|
|
22
|
+
...this.PREFIX_PARTS,
|
|
23
|
+
logLocation
|
|
24
|
+
].join(this.DELIMITER)}]`;
|
|
25
|
+
}
|
|
26
|
+
addSink(sink) {
|
|
27
|
+
this.sinks = [
|
|
28
|
+
...this.sinks.filter((existingSink)=>existingSink.name !== sink.name),
|
|
29
|
+
sink
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
removeSink(name) {
|
|
33
|
+
this.sinks = this.sinks.filter((sink)=>sink.name !== name);
|
|
34
|
+
}
|
|
35
|
+
removeSinks() {
|
|
36
|
+
this.sinks = [];
|
|
37
|
+
}
|
|
38
|
+
debug(logLocation, message, ...args) {
|
|
39
|
+
this.diary.debug(`${this.assembleLocationPrefix(logLocation)} ${message}`, ...args);
|
|
40
|
+
}
|
|
41
|
+
info(logLocation, message, ...args) {
|
|
42
|
+
this.diary.info(`${this.assembleLocationPrefix(logLocation)} ${message}`, ...args);
|
|
43
|
+
}
|
|
44
|
+
log(logLocation, message, ...args) {
|
|
45
|
+
this.diary.log(`${this.assembleLocationPrefix(logLocation)} ${message}`, ...args);
|
|
46
|
+
}
|
|
47
|
+
warn(logLocation, message, ...args) {
|
|
48
|
+
this.diary.warn(`${this.assembleLocationPrefix(logLocation)} ${message}`, ...args);
|
|
49
|
+
}
|
|
50
|
+
error(logLocation, message, ...args) {
|
|
51
|
+
this.diary.error(`${this.assembleLocationPrefix(logLocation)} ${message}`, ...args);
|
|
52
|
+
}
|
|
53
|
+
fatal(logLocation, message, ...args) {
|
|
54
|
+
this.diary.fatal(`${this.assembleLocationPrefix(logLocation)} ${message}`, ...args);
|
|
55
|
+
}
|
|
56
|
+
onLogEvent(event) {
|
|
57
|
+
this.sinks.forEach((sink)=>{
|
|
58
|
+
sink.ingest(event);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const logger = new Logger();
|
|
63
|
+
function createScopedLogger(location) {
|
|
64
|
+
return {
|
|
65
|
+
debug: (message, ...args)=>{
|
|
66
|
+
logger.debug(location, message, ...args);
|
|
67
|
+
},
|
|
68
|
+
info: (message, ...args)=>{
|
|
69
|
+
logger.info(location, message, ...args);
|
|
70
|
+
},
|
|
71
|
+
log: (message, ...args)=>{
|
|
72
|
+
logger.log(location, message, ...args);
|
|
73
|
+
},
|
|
74
|
+
warn: (message, ...args)=>{
|
|
75
|
+
logger.warn(location, message, ...args);
|
|
76
|
+
},
|
|
77
|
+
error: (message, ...args)=>{
|
|
78
|
+
logger.error(location, message, ...args);
|
|
79
|
+
},
|
|
80
|
+
fatal: (message, ...args)=>{
|
|
81
|
+
logger.fatal(location, message, ...args);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const createRetryFetchMethod_logger = createScopedLogger('ApiClient:Retry');
|
|
86
|
+
const DEFAULT_INTERVAL_TIMEOUT = 0;
|
|
87
|
+
const DEFAULT_RETRY_COUNT = 1;
|
|
88
|
+
const RETRY_RESPONSE_STATUS = 503;
|
|
89
|
+
const HTTP_ERROR_RESPONSE_STATUS = 500;
|
|
90
|
+
class HttpError extends Error {
|
|
91
|
+
status;
|
|
92
|
+
constructor(message, status = HTTP_ERROR_RESPONSE_STATUS){
|
|
93
|
+
super(message);
|
|
94
|
+
Object.setPrototypeOf(this, HttpError.prototype);
|
|
95
|
+
this.status = status;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function createRetryFetchCallback({ apiName = 'Optimization', controller, fetchMethod = fetch, init, url }) {
|
|
99
|
+
return async ()=>{
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetchMethod(url, init);
|
|
102
|
+
if (response.status === RETRY_RESPONSE_STATUS) throw new HttpError(`${apiName} API request to "${url.toString()}" failed with status: "[${response.status}] ${response.statusText}".`, RETRY_RESPONSE_STATUS);
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const httpError = new Error(`Request to "${url.toString()}" failed with status: [${response.status}] ${response.statusText} - traceparent: ${response.headers.get('traceparent')}`);
|
|
105
|
+
createRetryFetchMethod_logger.error('Request failed with non-OK status:', httpError);
|
|
106
|
+
controller.abort();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
createRetryFetchMethod_logger.debug(`Response from "${url.toString()}":`, response);
|
|
110
|
+
return response;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error instanceof HttpError && error.status === RETRY_RESPONSE_STATUS) throw error;
|
|
113
|
+
createRetryFetchMethod_logger.error(`Request to "${url.toString()}" failed:`, error);
|
|
114
|
+
controller.abort();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function createRetryFetchMethod({ apiName = 'Optimization', fetchMethod = fetch, intervalTimeout = DEFAULT_INTERVAL_TIMEOUT, onFailedAttempt, retries = DEFAULT_RETRY_COUNT } = {}) {
|
|
119
|
+
return async (url, init)=>{
|
|
120
|
+
const controller = new AbortController();
|
|
121
|
+
let retryResponse;
|
|
122
|
+
try {
|
|
123
|
+
retryResponse = await p_retry(createRetryFetchCallback({
|
|
124
|
+
apiName,
|
|
125
|
+
controller,
|
|
126
|
+
fetchMethod,
|
|
127
|
+
init,
|
|
128
|
+
url
|
|
129
|
+
}), {
|
|
130
|
+
minTimeout: intervalTimeout,
|
|
131
|
+
onFailedAttempt: (options)=>onFailedAttempt?.({
|
|
132
|
+
...options,
|
|
133
|
+
apiName
|
|
134
|
+
}),
|
|
135
|
+
retries,
|
|
136
|
+
signal: controller.signal
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (!(error instanceof Error) || 'AbortError' !== error.name) throw error;
|
|
140
|
+
}
|
|
141
|
+
if (!retryResponse) throw new Error(`${apiName} API request to "${url.toString()}" may not be retried.`);
|
|
142
|
+
return retryResponse;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const createTimeoutFetchMethod_logger = createScopedLogger('ApiClient:Timeout');
|
|
146
|
+
const DEFAULT_REQUEST_TIMEOUT = 3000;
|
|
147
|
+
function createTimeoutFetchMethod({ apiName = 'Optimization', fetchMethod = fetch, onRequestTimeout, requestTimeout = DEFAULT_REQUEST_TIMEOUT } = {}) {
|
|
148
|
+
return async (url, init)=>{
|
|
149
|
+
const controller = new AbortController();
|
|
150
|
+
const id = setTimeout(()=>{
|
|
151
|
+
if ('function' == typeof onRequestTimeout) onRequestTimeout({
|
|
152
|
+
apiName
|
|
153
|
+
});
|
|
154
|
+
else createTimeoutFetchMethod_logger.error(`Request to "${url.toString()}" timed out`, new Error('Request timeout'));
|
|
155
|
+
controller.abort();
|
|
156
|
+
}, requestTimeout);
|
|
157
|
+
const response = await fetchMethod(url, {
|
|
158
|
+
...init,
|
|
159
|
+
signal: controller.signal
|
|
160
|
+
});
|
|
161
|
+
clearTimeout(id);
|
|
162
|
+
return response;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const createProtectedFetchMethod_logger = createScopedLogger('ApiClient:Fetch');
|
|
166
|
+
function createProtectedFetchMethod(options) {
|
|
167
|
+
try {
|
|
168
|
+
const timeoutFetchMethod = createTimeoutFetchMethod(options);
|
|
169
|
+
const retryFetchMethod = createRetryFetchMethod({
|
|
170
|
+
...options,
|
|
171
|
+
fetchMethod: timeoutFetchMethod
|
|
172
|
+
});
|
|
173
|
+
return retryFetchMethod;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error instanceof Error) if ('AbortError' === error.name) createProtectedFetchMethod_logger.warn('Request aborted due to network issues. This request may not be retried.');
|
|
176
|
+
else createProtectedFetchMethod_logger.error('Request failed:', error);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const Fetch = {
|
|
181
|
+
create: createProtectedFetchMethod
|
|
182
|
+
};
|
|
183
|
+
const fetch_Fetch = Fetch;
|
|
184
|
+
const src_fetch = fetch_Fetch;
|
|
185
|
+
const ApiClientBase_logger = createScopedLogger('ApiClient');
|
|
186
|
+
const DEFAULT_ENVIRONMENT = 'main';
|
|
187
|
+
class ApiClientBase {
|
|
188
|
+
name;
|
|
189
|
+
clientId;
|
|
190
|
+
environment;
|
|
191
|
+
fetch;
|
|
192
|
+
constructor(name, { fetchOptions, clientId, environment }){
|
|
193
|
+
this.clientId = clientId;
|
|
194
|
+
this.environment = environment ?? DEFAULT_ENVIRONMENT;
|
|
195
|
+
this.name = name;
|
|
196
|
+
this.fetch = src_fetch.create({
|
|
197
|
+
...fetchOptions ?? {},
|
|
198
|
+
apiName: name
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
logRequestError(error, { requestName }) {
|
|
202
|
+
if (error instanceof Error) if ('AbortError' === error.name) ApiClientBase_logger.warn(`[${this.name}] "${requestName}" request aborted due to network issues. This request may not be retried.`);
|
|
203
|
+
else ApiClientBase_logger.error(`[${this.name}] "${requestName}" request failed:`, error);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const src_ApiClientBase = ApiClientBase;
|
|
207
|
+
const ExperienceApiClient_logger = createScopedLogger('ApiClient:Experience');
|
|
208
|
+
const EXPERIENCE_BASE_URL = 'https://experience.ninetailed.co/';
|
|
209
|
+
class ExperienceApiClient extends src_ApiClientBase {
|
|
210
|
+
baseUrl;
|
|
211
|
+
enabledFeatures;
|
|
212
|
+
ip;
|
|
213
|
+
locale;
|
|
214
|
+
plainText;
|
|
215
|
+
preflight;
|
|
216
|
+
constructor(config){
|
|
217
|
+
super('Experience', config);
|
|
218
|
+
const { baseUrl, enabledFeatures, ip, locale, plainText, preflight } = config;
|
|
219
|
+
this.baseUrl = baseUrl || EXPERIENCE_BASE_URL;
|
|
220
|
+
this.enabledFeatures = enabledFeatures;
|
|
221
|
+
this.ip = ip;
|
|
222
|
+
this.locale = locale;
|
|
223
|
+
this.plainText = plainText;
|
|
224
|
+
this.preflight = preflight;
|
|
225
|
+
}
|
|
226
|
+
async getProfile(id, options = {}) {
|
|
227
|
+
if (!id) throw new Error('Valid profile ID required.');
|
|
228
|
+
const requestName = 'Get Profile';
|
|
229
|
+
ExperienceApiClient_logger.info(`Sending "${requestName}" request`);
|
|
230
|
+
try {
|
|
231
|
+
const response = await this.fetch(this.constructUrl(`v2/organizations/${this.clientId}/environments/${this.environment}/profiles/${id}`, options), {
|
|
232
|
+
method: 'GET'
|
|
233
|
+
});
|
|
234
|
+
const { data: { changes, experiences, profile } } = __rspack_external__contentful_optimization_api_schemas_66dcf407.ExperienceResponse.parse(await response.json());
|
|
235
|
+
const data = {
|
|
236
|
+
changes,
|
|
237
|
+
personalizations: experiences,
|
|
238
|
+
profile
|
|
239
|
+
};
|
|
240
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request successfully completed`);
|
|
241
|
+
return data;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
this.logRequestError(error, {
|
|
244
|
+
requestName
|
|
245
|
+
});
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async makeProfileMutationRequest({ url, body, options }) {
|
|
250
|
+
return await this.fetch(this.constructUrl(url, options), {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
headers: this.constructHeaders(options),
|
|
253
|
+
body: JSON.stringify(body),
|
|
254
|
+
keepalive: true
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
async createProfile({ events }, options = {}) {
|
|
258
|
+
const requestName = 'Create Profile';
|
|
259
|
+
ExperienceApiClient_logger.info(`Sending "${requestName}" request`);
|
|
260
|
+
const body = {
|
|
261
|
+
events: __rspack_external__contentful_optimization_api_schemas_66dcf407.ExperienceEventArray.parse(events),
|
|
262
|
+
options: this.constructBodyOptions(options)
|
|
263
|
+
};
|
|
264
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request body:`, body);
|
|
265
|
+
try {
|
|
266
|
+
const response = await this.makeProfileMutationRequest({
|
|
267
|
+
url: `v2/organizations/${this.clientId}/environments/${this.environment}/profiles`,
|
|
268
|
+
body,
|
|
269
|
+
options
|
|
270
|
+
});
|
|
271
|
+
const { data: { changes, experiences, profile } } = __rspack_external__contentful_optimization_api_schemas_66dcf407.ExperienceResponse.parse(await response.json());
|
|
272
|
+
const data = {
|
|
273
|
+
changes,
|
|
274
|
+
personalizations: experiences,
|
|
275
|
+
profile
|
|
276
|
+
};
|
|
277
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request successfully completed`);
|
|
278
|
+
return data;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
this.logRequestError(error, {
|
|
281
|
+
requestName
|
|
282
|
+
});
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async updateProfile({ profileId, events }, options = {}) {
|
|
287
|
+
if (!profileId) throw new Error('Valid profile ID required.');
|
|
288
|
+
const requestName = 'Update Profile';
|
|
289
|
+
ExperienceApiClient_logger.info(`Sending "${requestName}" request`);
|
|
290
|
+
const body = {
|
|
291
|
+
events: __rspack_external__contentful_optimization_api_schemas_66dcf407.ExperienceEventArray.parse(events),
|
|
292
|
+
options: this.constructBodyOptions(options)
|
|
293
|
+
};
|
|
294
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request body:`, body);
|
|
295
|
+
try {
|
|
296
|
+
const response = await this.makeProfileMutationRequest({
|
|
297
|
+
url: `v2/organizations/${this.clientId}/environments/${this.environment}/profiles/${profileId}`,
|
|
298
|
+
body,
|
|
299
|
+
options
|
|
300
|
+
});
|
|
301
|
+
const { data: { changes, experiences, profile } } = __rspack_external__contentful_optimization_api_schemas_66dcf407.ExperienceResponse.parse(await response.json());
|
|
302
|
+
const data = {
|
|
303
|
+
changes,
|
|
304
|
+
personalizations: experiences,
|
|
305
|
+
profile
|
|
306
|
+
};
|
|
307
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request successfully completed`);
|
|
308
|
+
return data;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
this.logRequestError(error, {
|
|
311
|
+
requestName
|
|
312
|
+
});
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async upsertProfile({ profileId, events }, options) {
|
|
317
|
+
if (!profileId) return await this.createProfile({
|
|
318
|
+
events
|
|
319
|
+
}, options);
|
|
320
|
+
return await this.updateProfile({
|
|
321
|
+
profileId,
|
|
322
|
+
events
|
|
323
|
+
}, options);
|
|
324
|
+
}
|
|
325
|
+
async upsertManyProfiles({ events }, options = {}) {
|
|
326
|
+
const requestName = 'Upsert Many Profiles';
|
|
327
|
+
ExperienceApiClient_logger.info(`Sending "${requestName}" request`);
|
|
328
|
+
const body = {
|
|
329
|
+
events: __rspack_external__contentful_optimization_api_schemas_66dcf407.ExperienceEventArray.parse(events),
|
|
330
|
+
options: this.constructBodyOptions(options)
|
|
331
|
+
};
|
|
332
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request body:`, body);
|
|
333
|
+
try {
|
|
334
|
+
const response = await this.makeProfileMutationRequest({
|
|
335
|
+
url: `v2/organizations/${this.clientId}/environments/${this.environment}/events`,
|
|
336
|
+
body,
|
|
337
|
+
options: {
|
|
338
|
+
plainText: false,
|
|
339
|
+
...options
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
const { data: { profiles } } = __rspack_external__contentful_optimization_api_schemas_66dcf407.BatchExperienceResponse.parse(await response.json());
|
|
343
|
+
ExperienceApiClient_logger.debug(`"${requestName}" request successfully completed`);
|
|
344
|
+
return profiles;
|
|
345
|
+
} catch (error) {
|
|
346
|
+
this.logRequestError(error, {
|
|
347
|
+
requestName
|
|
348
|
+
});
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
constructUrl(path, options) {
|
|
353
|
+
const url = new URL(path, this.baseUrl);
|
|
354
|
+
const locale = options.locale ?? this.locale;
|
|
355
|
+
const preflight = options.preflight ?? this.preflight;
|
|
356
|
+
if (locale) url.searchParams.set('locale', locale);
|
|
357
|
+
if (preflight) url.searchParams.set('type', 'preflight');
|
|
358
|
+
return url.toString();
|
|
359
|
+
}
|
|
360
|
+
constructHeaders({ ip = this.ip, plainText = this.plainText }) {
|
|
361
|
+
const headers = new Map();
|
|
362
|
+
if (ip) headers.set('X-Force-IP', ip);
|
|
363
|
+
if (plainText ?? this.plainText ?? true) headers.set('Content-Type', 'text/plain');
|
|
364
|
+
else headers.set('Content-Type', 'application/json');
|
|
365
|
+
return Object.fromEntries(headers);
|
|
366
|
+
}
|
|
367
|
+
constructBodyOptions = ({ enabledFeatures = this.enabledFeatures })=>{
|
|
368
|
+
const bodyOptions = {};
|
|
369
|
+
if (enabledFeatures && Array.isArray(enabledFeatures) && enabledFeatures.length > 0) bodyOptions.features = enabledFeatures;
|
|
370
|
+
else bodyOptions.features = [
|
|
371
|
+
'ip-enrichment',
|
|
372
|
+
'location'
|
|
373
|
+
];
|
|
374
|
+
return bodyOptions;
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const experience = ExperienceApiClient;
|
|
378
|
+
const InsightsApiClient_logger = createScopedLogger('ApiClient:Insights');
|
|
379
|
+
const INSIGHTS_BASE_URL = 'https://ingest.insights.ninetailed.co/';
|
|
380
|
+
class InsightsApiClient extends src_ApiClientBase {
|
|
381
|
+
baseUrl;
|
|
382
|
+
beaconHandler;
|
|
383
|
+
constructor(config){
|
|
384
|
+
super('Insights', config);
|
|
385
|
+
const { baseUrl, beaconHandler } = config;
|
|
386
|
+
this.baseUrl = baseUrl ?? INSIGHTS_BASE_URL;
|
|
387
|
+
this.beaconHandler = beaconHandler;
|
|
388
|
+
}
|
|
389
|
+
async sendBatchEvents(batches, options = {}) {
|
|
390
|
+
const { beaconHandler = this.beaconHandler } = options;
|
|
391
|
+
const url = new URL(`v1/organizations/${this.clientId}/environments/${this.environment}/events`, this.baseUrl);
|
|
392
|
+
const body = __rspack_external__contentful_optimization_api_schemas_66dcf407.BatchInsightsEventArray.parse(batches);
|
|
393
|
+
if ('function' == typeof beaconHandler) {
|
|
394
|
+
InsightsApiClient_logger.debug('Queueing events via beaconHandler');
|
|
395
|
+
const beaconSuccessfullyQueued = beaconHandler(url, body);
|
|
396
|
+
if (beaconSuccessfullyQueued) return true;
|
|
397
|
+
InsightsApiClient_logger.warn('beaconHandler failed to queue events; events will be emitted immediately via fetch');
|
|
398
|
+
}
|
|
399
|
+
const requestName = 'Event Batches';
|
|
400
|
+
InsightsApiClient_logger.info(`Sending "${requestName}" request`);
|
|
401
|
+
InsightsApiClient_logger.debug(`"${requestName}" request body:`, body);
|
|
402
|
+
try {
|
|
403
|
+
await this.fetch(url, {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: {
|
|
406
|
+
'Content-Type': 'application/json'
|
|
407
|
+
},
|
|
408
|
+
body: JSON.stringify(body),
|
|
409
|
+
keepalive: true
|
|
410
|
+
});
|
|
411
|
+
InsightsApiClient_logger.debug(`"${requestName}" request successfully completed`);
|
|
412
|
+
return true;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
this.logRequestError(error, {
|
|
415
|
+
requestName
|
|
416
|
+
});
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const insights = InsightsApiClient;
|
|
422
|
+
class ApiClient {
|
|
423
|
+
config;
|
|
424
|
+
experience;
|
|
425
|
+
insights;
|
|
426
|
+
constructor(config){
|
|
427
|
+
const { personalization, analytics, ...apiConfig } = config;
|
|
428
|
+
this.config = apiConfig;
|
|
429
|
+
this.experience = new experience({
|
|
430
|
+
...apiConfig,
|
|
431
|
+
...personalization
|
|
432
|
+
});
|
|
433
|
+
this.insights = new insights({
|
|
434
|
+
...apiConfig,
|
|
435
|
+
...analytics
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const UniversalEventBuilderArgs = object({
|
|
440
|
+
campaign: optional(__rspack_external__contentful_optimization_api_schemas_66dcf407.Campaign),
|
|
441
|
+
locale: optional(string()),
|
|
442
|
+
location: optional(__rspack_external__contentful_optimization_api_schemas_66dcf407.GeoLocation),
|
|
443
|
+
page: optional(__rspack_external__contentful_optimization_api_schemas_66dcf407.Page),
|
|
444
|
+
screen: optional(__rspack_external__contentful_optimization_api_schemas_66dcf407.Screen),
|
|
445
|
+
userAgent: optional(string())
|
|
446
|
+
});
|
|
447
|
+
const ComponentViewBuilderArgs = extend(UniversalEventBuilderArgs, {
|
|
448
|
+
componentId: string(),
|
|
449
|
+
experienceId: optional(string()),
|
|
450
|
+
variantIndex: optional(number()),
|
|
451
|
+
sticky: optional(mini_boolean())
|
|
452
|
+
});
|
|
453
|
+
const IdentifyBuilderArgs = extend(UniversalEventBuilderArgs, {
|
|
454
|
+
traits: optional(__rspack_external__contentful_optimization_api_schemas_66dcf407.Traits),
|
|
455
|
+
userId: string()
|
|
456
|
+
});
|
|
457
|
+
const PageViewBuilderArgs = extend(UniversalEventBuilderArgs, {
|
|
458
|
+
properties: optional(partial(__rspack_external__contentful_optimization_api_schemas_66dcf407.Page))
|
|
459
|
+
});
|
|
460
|
+
const ScreenViewBuilderArgs = extend(UniversalEventBuilderArgs, {
|
|
461
|
+
name: string(),
|
|
462
|
+
properties: __rspack_external__contentful_optimization_api_schemas_66dcf407.Properties
|
|
463
|
+
});
|
|
464
|
+
const TrackBuilderArgs = extend(UniversalEventBuilderArgs, {
|
|
465
|
+
event: string(),
|
|
466
|
+
properties: optional(prefault(__rspack_external__contentful_optimization_api_schemas_66dcf407.Properties, {}))
|
|
467
|
+
});
|
|
468
|
+
const DEFAULT_PAGE_PROPERTIES = {
|
|
469
|
+
path: '',
|
|
470
|
+
query: {},
|
|
471
|
+
referrer: '',
|
|
472
|
+
search: '',
|
|
473
|
+
title: '',
|
|
474
|
+
url: ''
|
|
475
|
+
};
|
|
476
|
+
class EventBuilder {
|
|
477
|
+
app;
|
|
478
|
+
channel;
|
|
479
|
+
library;
|
|
480
|
+
getLocale;
|
|
481
|
+
getPageProperties;
|
|
482
|
+
getUserAgent;
|
|
483
|
+
constructor(config){
|
|
484
|
+
const { app, channel, library, getLocale, getPageProperties, getUserAgent } = config;
|
|
485
|
+
this.app = app;
|
|
486
|
+
this.channel = channel;
|
|
487
|
+
this.library = library;
|
|
488
|
+
this.getLocale = getLocale ?? (()=>'en-US');
|
|
489
|
+
this.getPageProperties = getPageProperties ?? (()=>DEFAULT_PAGE_PROPERTIES);
|
|
490
|
+
this.getUserAgent = getUserAgent ?? (()=>void 0);
|
|
491
|
+
}
|
|
492
|
+
buildUniversalEventProperties({ campaign = {}, locale, location, page, screen, userAgent }) {
|
|
493
|
+
const timestamp = new Date().toISOString();
|
|
494
|
+
return {
|
|
495
|
+
channel: this.channel,
|
|
496
|
+
context: {
|
|
497
|
+
app: this.app,
|
|
498
|
+
campaign,
|
|
499
|
+
gdpr: {
|
|
500
|
+
isConsentGiven: true
|
|
501
|
+
},
|
|
502
|
+
library: this.library,
|
|
503
|
+
locale: locale ?? this.getLocale() ?? 'en-US',
|
|
504
|
+
location,
|
|
505
|
+
page: page ?? this.getPageProperties(),
|
|
506
|
+
screen,
|
|
507
|
+
userAgent: userAgent ?? this.getUserAgent()
|
|
508
|
+
},
|
|
509
|
+
messageId: crypto.randomUUID(),
|
|
510
|
+
originalTimestamp: timestamp,
|
|
511
|
+
sentAt: timestamp,
|
|
512
|
+
timestamp
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
buildComponentView(args) {
|
|
516
|
+
const { componentId, experienceId, variantIndex, ...universal } = ComponentViewBuilderArgs.parse(args);
|
|
517
|
+
return {
|
|
518
|
+
...this.buildUniversalEventProperties(universal),
|
|
519
|
+
type: 'component',
|
|
520
|
+
componentType: 'Entry',
|
|
521
|
+
componentId,
|
|
522
|
+
experienceId,
|
|
523
|
+
variantIndex: variantIndex ?? 0
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
buildFlagView(args) {
|
|
527
|
+
return {
|
|
528
|
+
...this.buildComponentView(args),
|
|
529
|
+
componentType: 'Variable'
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
buildIdentify(args) {
|
|
533
|
+
const { traits = {}, userId, ...universal } = IdentifyBuilderArgs.parse(args);
|
|
534
|
+
return {
|
|
535
|
+
...this.buildUniversalEventProperties(universal),
|
|
536
|
+
type: 'identify',
|
|
537
|
+
traits,
|
|
538
|
+
userId
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
buildPageView(args = {}) {
|
|
542
|
+
const { properties = {}, ...universal } = PageViewBuilderArgs.parse(args);
|
|
543
|
+
const pageProperties = this.getPageProperties();
|
|
544
|
+
const merged = merge({
|
|
545
|
+
...pageProperties,
|
|
546
|
+
title: pageProperties.title ?? DEFAULT_PAGE_PROPERTIES.title
|
|
547
|
+
}, properties);
|
|
548
|
+
const { context: { screen: _, ...universalContext }, ...universalProperties } = this.buildUniversalEventProperties(universal);
|
|
549
|
+
const context = __rspack_external__contentful_optimization_api_schemas_66dcf407.PageEventContext.parse(universalContext);
|
|
550
|
+
return {
|
|
551
|
+
...universalProperties,
|
|
552
|
+
context,
|
|
553
|
+
type: 'page',
|
|
554
|
+
properties: merged
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
buildScreenView(args) {
|
|
558
|
+
const { name, properties, ...universal } = ScreenViewBuilderArgs.parse(args);
|
|
559
|
+
const { context: { page: _, ...universalContext }, ...universalProperties } = this.buildUniversalEventProperties(universal);
|
|
560
|
+
const context = __rspack_external__contentful_optimization_api_schemas_66dcf407.ScreenEventContext.parse(universalContext);
|
|
561
|
+
return {
|
|
562
|
+
...universalProperties,
|
|
563
|
+
context,
|
|
564
|
+
type: 'screen',
|
|
565
|
+
name,
|
|
566
|
+
properties
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
buildTrack(args) {
|
|
570
|
+
const { event, properties = {}, ...universal } = TrackBuilderArgs.parse(args);
|
|
571
|
+
return {
|
|
572
|
+
...this.buildUniversalEventProperties(universal),
|
|
573
|
+
type: 'track',
|
|
574
|
+
event,
|
|
575
|
+
properties
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const builders_EventBuilder = EventBuilder;
|
|
580
|
+
export * from "@contentful/optimization-api-schemas";
|
|
581
|
+
export { ApiClient, DEFAULT_PAGE_PROPERTIES, EXPERIENCE_BASE_URL, INSIGHTS_BASE_URL, builders_EventBuilder as EventBuilder };
|
|
582
|
+
|
|
583
|
+
//# sourceMappingURL=index.mjs.map
|