@croct/sdk 0.16.2 → 0.17.0
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/cache/cookieCache.d.ts +1 -1
- package/cache/cookieCache.js +12 -2
- package/cache/cookieCache.js.map +1 -1
- package/channel/channel.d.ts +7 -0
- package/channel/channel.js +23 -0
- package/channel/channel.js.map +1 -1
- package/channel/guaranteedChannel.js +4 -3
- package/channel/guaranteedChannel.js.map +1 -1
- package/channel/httpBeaconChannel.d.ts +23 -0
- package/channel/httpBeaconChannel.js +89 -0
- package/channel/httpBeaconChannel.js.map +1 -0
- package/channel/index.d.ts +1 -2
- package/channel/index.js +3 -5
- package/channel/index.js.map +1 -1
- package/channel/queuedChannel.js +5 -4
- package/channel/queuedChannel.js.map +1 -1
- package/channel/retryChannel.js +8 -4
- package/channel/retryChannel.js.map +1 -1
- package/channel/sandboxChannel.js +4 -0
- package/channel/sandboxChannel.js.map +1 -1
- package/constants.d.ts +2 -2
- package/constants.js +1 -1
- package/container.d.ts +3 -0
- package/container.js +15 -14
- package/container.js.map +1 -1
- package/contentFetcher.d.ts +3 -4
- package/contentFetcher.js +37 -27
- package/contentFetcher.js.map +1 -1
- package/evaluator.d.ts +2 -4
- package/evaluator.js +47 -39
- package/evaluator.js.map +1 -1
- package/facade/contentFetcherFacade.d.ts +1 -6
- package/facade/contentFetcherFacade.js +2 -5
- package/facade/contentFetcherFacade.js.map +1 -1
- package/facade/sdkFacade.d.ts +3 -6
- package/facade/sdkFacade.js +3 -7
- package/facade/sdkFacade.js.map +1 -1
- package/help.d.ts +3 -0
- package/help.js +26 -0
- package/help.js.map +1 -0
- package/package.json +1 -3
- package/queue/persistentQueue.d.ts +1 -3
- package/queue/persistentQueue.js +9 -16
- package/queue/persistentQueue.js.map +1 -1
- package/schema/eventSchemas.js +0 -16
- package/schema/eventSchemas.js.map +1 -1
- package/schema/sdkFacadeSchemas.js +5 -1
- package/schema/sdkFacadeSchemas.js.map +1 -1
- package/schema/sdkSchemas.js +7 -0
- package/schema/sdkSchemas.js.map +1 -1
- package/sdk.d.ts +2 -0
- package/sdk.js +1 -2
- package/sdk.js.map +1 -1
- package/src/cache/cookieCache.ts +13 -2
- package/src/channel/channel.ts +32 -0
- package/src/channel/guaranteedChannel.ts +4 -4
- package/src/channel/httpBeaconChannel.ts +128 -0
- package/src/channel/index.ts +1 -2
- package/src/channel/queuedChannel.ts +5 -5
- package/src/channel/retryChannel.ts +9 -5
- package/src/channel/sandboxChannel.ts +5 -1
- package/src/container.ts +17 -19
- package/src/contentFetcher.ts +41 -29
- package/src/evaluator.ts +74 -65
- package/src/facade/contentFetcherFacade.ts +2 -11
- package/src/facade/sdkFacade.ts +5 -14
- package/src/help.ts +24 -0
- package/src/queue/persistentQueue.ts +11 -22
- package/src/schema/eventSchemas.ts +0 -16
- package/src/schema/sdkFacadeSchemas.ts +14 -2
- package/src/schema/sdkSchemas.ts +7 -0
- package/src/sdk.ts +3 -2
- package/src/trackingEvents.ts +0 -4
- package/trackingEvents.d.ts +0 -4
- package/trackingEvents.js.map +1 -1
- package/channel/beaconSocketChannel.d.ts +0 -37
- package/channel/beaconSocketChannel.js +0 -83
- package/channel/beaconSocketChannel.js.map +0 -1
- package/channel/socketChannel.d.ts +0 -31
- package/channel/socketChannel.js +0 -145
- package/channel/socketChannel.js.map +0 -1
- package/src/channel/beaconSocketChannel.ts +0 -153
- package/src/channel/socketChannel.ts +0 -217
package/src/contentFetcher.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {BASE_ENDPOINT_URL, CLIENT_LIBRARY} from './constants';
|
|
|
5
5
|
import {formatMessage} from './error';
|
|
6
6
|
import {Logger, NullLogger} from './logging';
|
|
7
7
|
import type {ApiKey} from './apiKey';
|
|
8
|
+
import {Help} from './help';
|
|
8
9
|
|
|
9
10
|
export type ErrorResponse = {
|
|
10
11
|
type: string,
|
|
@@ -54,10 +55,6 @@ export type DynamicContentOptions = BasicOptions & {
|
|
|
54
55
|
clientId?: string,
|
|
55
56
|
clientIp?: string,
|
|
56
57
|
clientAgent?: string,
|
|
57
|
-
/**
|
|
58
|
-
* @deprecated Use `clientAgent` instead. This option will be removed in future releases.
|
|
59
|
-
*/
|
|
60
|
-
userAgent?: string,
|
|
61
58
|
userToken?: Token|string,
|
|
62
59
|
previewToken?: Token|string,
|
|
63
60
|
context?: EvaluationContext,
|
|
@@ -80,11 +77,15 @@ export type Configuration = {
|
|
|
80
77
|
apiKey?: string|ApiKey,
|
|
81
78
|
baseEndpointUrl?: string,
|
|
82
79
|
logger?: Logger,
|
|
80
|
+
defaultTimeout?: number,
|
|
81
|
+
defaultPreferredLocale?: string,
|
|
83
82
|
};
|
|
84
83
|
|
|
85
84
|
type InternalConfiguration = {
|
|
86
85
|
appId?: string,
|
|
87
86
|
apiKey?: string,
|
|
87
|
+
defaultTimeout?: number,
|
|
88
|
+
defaultPreferredLocale?: string,
|
|
88
89
|
};
|
|
89
90
|
|
|
90
91
|
export class ContentFetcher {
|
|
@@ -117,6 +118,8 @@ export class ContentFetcher {
|
|
|
117
118
|
this.configuration = {
|
|
118
119
|
appId: configuration.appId,
|
|
119
120
|
apiKey: apiKey,
|
|
121
|
+
defaultTimeout: configuration.defaultTimeout,
|
|
122
|
+
defaultPreferredLocale: configuration.defaultPreferredLocale,
|
|
120
123
|
};
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -127,34 +130,44 @@ export class ContentFetcher {
|
|
|
127
130
|
|
|
128
131
|
return new Promise((resolve, reject) => {
|
|
129
132
|
const abortController = new AbortController();
|
|
133
|
+
const timeout = options.timeout ?? this.configuration.defaultTimeout;
|
|
130
134
|
|
|
131
|
-
if (
|
|
135
|
+
if (timeout !== undefined) {
|
|
132
136
|
setTimeout(
|
|
133
137
|
() => {
|
|
134
138
|
const response: ErrorResponse = {
|
|
135
139
|
title: 'Maximum timeout reached before content could be loaded.',
|
|
136
140
|
type: ContentErrorType.TIMEOUT,
|
|
137
|
-
detail: `The content took more than ${
|
|
141
|
+
detail: `The content took more than ${timeout}ms to load.`,
|
|
138
142
|
status: 408, // Request Timeout
|
|
139
143
|
};
|
|
140
144
|
|
|
141
145
|
abortController.abort();
|
|
142
146
|
|
|
147
|
+
this.logHelp(response.status);
|
|
148
|
+
|
|
143
149
|
reject(new ContentError(response));
|
|
144
150
|
},
|
|
145
|
-
|
|
151
|
+
timeout,
|
|
146
152
|
);
|
|
147
153
|
}
|
|
148
154
|
|
|
149
155
|
this.load(slotId, abortController.signal, options)
|
|
150
|
-
.then(
|
|
151
|
-
|
|
156
|
+
.then(response => {
|
|
157
|
+
const region = response.headers.get('X-Croct-Region');
|
|
158
|
+
const timing = response.headers.get('X-Croct-Timing');
|
|
159
|
+
|
|
160
|
+
this.logger.debug(`Request processed by region ${region} in ${timing}`);
|
|
161
|
+
|
|
162
|
+
return response.json()
|
|
152
163
|
.then(body => {
|
|
153
164
|
if (response.ok) {
|
|
154
|
-
resolve(body);
|
|
155
|
-
} else {
|
|
156
|
-
reject(new ContentError(body));
|
|
165
|
+
return resolve(body);
|
|
157
166
|
}
|
|
167
|
+
|
|
168
|
+
this.logHelp(response.status);
|
|
169
|
+
|
|
170
|
+
reject(new ContentError(body));
|
|
158
171
|
})
|
|
159
172
|
.catch(error => {
|
|
160
173
|
if (!response.ok) {
|
|
@@ -162,8 +175,8 @@ export class ContentFetcher {
|
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
throw error;
|
|
165
|
-
})
|
|
166
|
-
)
|
|
178
|
+
});
|
|
179
|
+
})
|
|
167
180
|
.catch(error => {
|
|
168
181
|
if (!abortController.signal.aborted) {
|
|
169
182
|
reject(
|
|
@@ -204,22 +217,15 @@ export class ContentFetcher {
|
|
|
204
217
|
payload.version = `${options.version}`;
|
|
205
218
|
}
|
|
206
219
|
|
|
207
|
-
|
|
208
|
-
|
|
220
|
+
const preferredLocale = options.preferredLocale ?? this.configuration.defaultPreferredLocale;
|
|
221
|
+
|
|
222
|
+
if (preferredLocale !== undefined) {
|
|
223
|
+
payload.preferredLocale = preferredLocale;
|
|
209
224
|
}
|
|
210
225
|
|
|
211
226
|
const dynamic = ContentFetcher.isDynamicContent(options);
|
|
212
227
|
|
|
213
228
|
if (dynamic) {
|
|
214
|
-
if (options.userAgent !== undefined) {
|
|
215
|
-
this.logger.warn(
|
|
216
|
-
'The `userAgent` option is deprecated and '
|
|
217
|
-
+ 'will be removed in future releases. '
|
|
218
|
-
+ 'Please update the part of your code calling the `fetch` method '
|
|
219
|
-
+ 'to use the `clientAgent` option instead.',
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
229
|
if (options.clientId !== undefined) {
|
|
224
230
|
headers['X-Client-Id'] = options.clientId;
|
|
225
231
|
}
|
|
@@ -232,10 +238,8 @@ export class ContentFetcher {
|
|
|
232
238
|
headers['X-Token'] = options.userToken.toString();
|
|
233
239
|
}
|
|
234
240
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (clientAgent !== undefined) {
|
|
238
|
-
headers['X-Client-Agent'] = clientAgent;
|
|
241
|
+
if (options.clientAgent !== undefined) {
|
|
242
|
+
headers['X-Client-Agent'] = options.clientAgent;
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
if (options.context !== undefined) {
|
|
@@ -264,6 +268,14 @@ export class ContentFetcher {
|
|
|
264
268
|
});
|
|
265
269
|
}
|
|
266
270
|
|
|
271
|
+
private logHelp(statusCode: number): void {
|
|
272
|
+
const help = Help.forStatusCode(statusCode);
|
|
273
|
+
|
|
274
|
+
if (help !== undefined) {
|
|
275
|
+
this.logger.error(help);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
267
279
|
private static isDynamicContent(options: FetchOptions): options is DynamicContentOptions {
|
|
268
280
|
return options.static !== true;
|
|
269
281
|
}
|
package/src/evaluator.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {formatMessage} from './error';
|
|
|
5
5
|
import {getLength, getLocation, Location} from './sourceLocation';
|
|
6
6
|
import {Logger, NullLogger} from './logging';
|
|
7
7
|
import type {ApiKey} from './apiKey';
|
|
8
|
+
import {Help} from './help';
|
|
8
9
|
|
|
9
10
|
export type Campaign = {
|
|
10
11
|
name?: string,
|
|
@@ -37,10 +38,6 @@ export type EvaluationOptions = {
|
|
|
37
38
|
clientId?: string,
|
|
38
39
|
clientIp?: string,
|
|
39
40
|
clientAgent?: string,
|
|
40
|
-
/**
|
|
41
|
-
* @deprecated Use `clientAgent` instead. This option will be removed in future releases.
|
|
42
|
-
*/
|
|
43
|
-
userAgent?: string,
|
|
44
41
|
userToken?: Token|string,
|
|
45
42
|
timeout?: number,
|
|
46
43
|
context?: EvaluationContext,
|
|
@@ -98,11 +95,13 @@ export type Configuration = {
|
|
|
98
95
|
apiKey?: string|ApiKey,
|
|
99
96
|
baseEndpointUrl?: string,
|
|
100
97
|
logger?: Logger,
|
|
98
|
+
defaultTimeout?: number,
|
|
101
99
|
};
|
|
102
100
|
|
|
103
101
|
type InternalConfiguration = {
|
|
104
102
|
appId?: string,
|
|
105
103
|
apiKey?: string,
|
|
104
|
+
defaultTimeout?: number,
|
|
106
105
|
};
|
|
107
106
|
|
|
108
107
|
export class Evaluator {
|
|
@@ -132,6 +131,7 @@ export class Evaluator {
|
|
|
132
131
|
this.configuration = {
|
|
133
132
|
appId: configuration.appId,
|
|
134
133
|
apiKey: apiKey,
|
|
134
|
+
defaultTimeout: configuration.defaultTimeout,
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -154,98 +154,99 @@ export class Evaluator {
|
|
|
154
154
|
return Promise.reject(new QueryError(response));
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
const
|
|
157
|
+
const payload: JsonObject = {
|
|
158
158
|
query: query,
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
if (options.context !== undefined) {
|
|
162
|
-
|
|
162
|
+
payload.context = options.context;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
return new Promise((resolve, reject) => {
|
|
166
166
|
const abortController = new AbortController();
|
|
167
|
+
const timeout = options.timeout ?? this.configuration.defaultTimeout;
|
|
167
168
|
|
|
168
|
-
if (
|
|
169
|
+
if (timeout !== undefined) {
|
|
169
170
|
setTimeout(
|
|
170
171
|
() => {
|
|
171
172
|
const response: ErrorResponse = {
|
|
172
173
|
title: 'Maximum evaluation timeout reached before evaluation could complete.',
|
|
173
174
|
type: EvaluationErrorType.TIMEOUT,
|
|
174
|
-
detail: `The evaluation took more than ${
|
|
175
|
+
detail: `The evaluation took more than ${timeout}ms to complete.`,
|
|
175
176
|
status: 408, // Request Timeout
|
|
176
177
|
};
|
|
177
178
|
|
|
178
179
|
abortController.abort();
|
|
179
180
|
|
|
181
|
+
this.logHelp(response.status);
|
|
182
|
+
|
|
180
183
|
reject(new EvaluationError(response));
|
|
181
184
|
},
|
|
182
|
-
|
|
185
|
+
timeout,
|
|
183
186
|
);
|
|
184
187
|
}
|
|
185
188
|
|
|
186
|
-
const promise = this.fetch(
|
|
189
|
+
const promise = this.fetch(payload, abortController.signal, options);
|
|
187
190
|
|
|
188
191
|
promise.then(
|
|
189
|
-
response =>
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
192
|
+
response => {
|
|
193
|
+
const region = response.headers.get('X-Croct-Region');
|
|
194
|
+
const timing = response.headers.get('X-Croct-Timing');
|
|
195
|
+
|
|
196
|
+
this.logger.debug(`Request processed by region ${region} in ${timing}`);
|
|
197
|
+
|
|
198
|
+
return response.json()
|
|
199
|
+
.then(body => {
|
|
200
|
+
if (response.ok) {
|
|
201
|
+
return resolve(body);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.logHelp(response.status);
|
|
205
|
+
|
|
206
|
+
const problem: ErrorResponse = body;
|
|
207
|
+
|
|
208
|
+
switch (problem.type) {
|
|
209
|
+
case EvaluationErrorType.INVALID_QUERY:
|
|
210
|
+
case EvaluationErrorType.EVALUATION_FAILED:
|
|
211
|
+
case EvaluationErrorType.TOO_COMPLEX_QUERY:
|
|
212
|
+
reject(new QueryError(problem as QueryErrorResponse));
|
|
213
|
+
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
default:
|
|
217
|
+
reject(new EvaluationError(problem));
|
|
218
|
+
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
.catch(error => {
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
throw new Error(`Error ${response.status} - ${response.statusText}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
throw error;
|
|
228
|
+
});
|
|
229
|
+
},
|
|
230
|
+
).catch(
|
|
231
|
+
error => {
|
|
232
|
+
if (!abortController.signal.aborted) {
|
|
233
|
+
reject(
|
|
234
|
+
new EvaluationError({
|
|
235
|
+
title: formatMessage(error),
|
|
236
|
+
type: EvaluationErrorType.UNEXPECTED_ERROR,
|
|
237
|
+
detail: 'Please try again or contact Croct support if the error persists.',
|
|
238
|
+
status: 500, // Internal Server Error
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
);
|
|
233
244
|
});
|
|
234
245
|
}
|
|
235
246
|
|
|
236
247
|
private fetch(body: JsonObject, signal: AbortSignal, options: EvaluationOptions): Promise<Response> {
|
|
237
248
|
const {appId, apiKey} = this.configuration;
|
|
238
|
-
const {clientId, clientIp, userToken} = options;
|
|
239
|
-
const clientAgent = options.clientAgent ?? options.userAgent;
|
|
240
|
-
|
|
241
|
-
if (options.userAgent !== undefined) {
|
|
242
|
-
this.logger.warn(
|
|
243
|
-
'The `userAgent` option is deprecated and '
|
|
244
|
-
+ 'will be removed in future releases. '
|
|
245
|
-
+ 'Please update the part of your code calling the `evaluate` method '
|
|
246
|
-
+ 'to use the `clientAgent` option instead.',
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
+
const {clientId, clientIp, userToken, clientAgent} = options;
|
|
249
250
|
|
|
250
251
|
const headers: Record<string, string> = {
|
|
251
252
|
'Content-Type': 'application/json',
|
|
@@ -292,6 +293,14 @@ export class Evaluator {
|
|
|
292
293
|
});
|
|
293
294
|
}
|
|
294
295
|
|
|
296
|
+
private logHelp(statusCode: number): void {
|
|
297
|
+
const help = Help.forStatusCode(statusCode);
|
|
298
|
+
|
|
299
|
+
if (help !== undefined) {
|
|
300
|
+
this.logger.error(help);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
295
304
|
public toJSON(): never {
|
|
296
305
|
// Prevent sensitive configuration from being serialized
|
|
297
306
|
throw new Error('Unserializable value.');
|
|
@@ -21,11 +21,7 @@ function validate(options: unknown): asserts options is FetchOptions {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
type
|
|
25
|
-
preferredLocale?: string,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type Configuration = Options & {
|
|
24
|
+
export type Configuration = {
|
|
29
25
|
contentFetcher: ContentFetcher,
|
|
30
26
|
contextFactory: ContextFactory,
|
|
31
27
|
previewTokenProvider: TokenProvider,
|
|
@@ -44,17 +40,12 @@ export class ContentFetcherFacade {
|
|
|
44
40
|
|
|
45
41
|
private readonly cidAssigner: CidAssigner;
|
|
46
42
|
|
|
47
|
-
private readonly options: Options;
|
|
48
|
-
|
|
49
43
|
public constructor(configuration: Configuration) {
|
|
50
44
|
this.fetcher = configuration.contentFetcher;
|
|
51
45
|
this.previewTokenProvider = configuration.previewTokenProvider;
|
|
52
46
|
this.userTokenProvider = configuration.userTokenProvider;
|
|
53
47
|
this.cidAssigner = configuration.cidAssigner;
|
|
54
48
|
this.contextFactory = configuration.contextFactory;
|
|
55
|
-
this.options = {
|
|
56
|
-
preferredLocale: configuration.preferredLocale,
|
|
57
|
-
};
|
|
58
49
|
}
|
|
59
50
|
|
|
60
51
|
public async fetch<P extends JsonObject>(slotId: string, options: FetchOptions = {}): Promise<FetchResponse<P>> {
|
|
@@ -72,7 +63,7 @@ export class ContentFetcherFacade {
|
|
|
72
63
|
version: options.version,
|
|
73
64
|
context: this.contextFactory.createContext(options.attributes),
|
|
74
65
|
timeout: options.timeout,
|
|
75
|
-
preferredLocale: options.preferredLocale
|
|
66
|
+
preferredLocale: options.preferredLocale,
|
|
76
67
|
});
|
|
77
68
|
}
|
|
78
69
|
}
|
package/src/facade/sdkFacade.ts
CHANGED
|
@@ -17,11 +17,7 @@ import {ContentFetcherFacade} from './contentFetcherFacade';
|
|
|
17
17
|
import {CookieCacheConfiguration} from '../cache/cookieCache';
|
|
18
18
|
import {EventSubjectProcessor} from '../eventSubjectProcessor';
|
|
19
19
|
|
|
20
|
-
type
|
|
21
|
-
preferredLocale?: string,
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type Configuration = Options & {
|
|
20
|
+
export type Configuration = {
|
|
25
21
|
appId: string,
|
|
26
22
|
tokenScope?: TokenScope,
|
|
27
23
|
debug?: boolean,
|
|
@@ -41,6 +37,8 @@ export type Configuration = Options & {
|
|
|
41
37
|
userToken?: CookieCacheConfiguration,
|
|
42
38
|
previewToken?: CookieCacheConfiguration,
|
|
43
39
|
},
|
|
40
|
+
defaultFetchTimeout?: number,
|
|
41
|
+
defaultPreferredLocale?: string,
|
|
44
42
|
};
|
|
45
43
|
|
|
46
44
|
function validateConfiguration(configuration: unknown): asserts configuration is Configuration {
|
|
@@ -64,17 +62,14 @@ export class SdkFacade {
|
|
|
64
62
|
|
|
65
63
|
private contentFetcherFacade?: ContentFetcherFacade;
|
|
66
64
|
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
private constructor(sdk: Sdk, options: Options = {}) {
|
|
65
|
+
private constructor(sdk: Sdk) {
|
|
70
66
|
this.sdk = sdk;
|
|
71
|
-
this.options = options;
|
|
72
67
|
}
|
|
73
68
|
|
|
74
69
|
public static init(configuration: Configuration): SdkFacade {
|
|
75
70
|
validateConfiguration(configuration);
|
|
76
71
|
|
|
77
|
-
const {track = true, userId, token,
|
|
72
|
+
const {track = true, userId, token, ...containerConfiguration} = configuration;
|
|
78
73
|
|
|
79
74
|
if (userId !== undefined && token !== undefined) {
|
|
80
75
|
throw new Error('Either the user ID or token can be specified, but not both.');
|
|
@@ -89,9 +84,6 @@ export class SdkFacade {
|
|
|
89
84
|
disableCidMirroring: containerConfiguration.disableCidMirroring ?? false,
|
|
90
85
|
eventProcessor: container => new EventSubjectProcessor(container.getLogger('EventSubjectProcessor')),
|
|
91
86
|
}),
|
|
92
|
-
{
|
|
93
|
-
preferredLocale: preferredLocale,
|
|
94
|
-
},
|
|
95
87
|
);
|
|
96
88
|
|
|
97
89
|
if (userId !== undefined) {
|
|
@@ -184,7 +176,6 @@ export class SdkFacade {
|
|
|
184
176
|
cidAssigner: this.sdk.cidAssigner,
|
|
185
177
|
previewTokenProvider: this.sdk.previewTokenStore,
|
|
186
178
|
userTokenProvider: this.sdk.userTokenStore,
|
|
187
|
-
preferredLocale: this.options.preferredLocale,
|
|
188
179
|
});
|
|
189
180
|
}
|
|
190
181
|
|
package/src/help.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export namespace Help {
|
|
2
|
+
export function forStatusCode(statusCode: number): string|undefined {
|
|
3
|
+
switch (statusCode) {
|
|
4
|
+
case 401:
|
|
5
|
+
return 'The request was not authorized, most likely due to invalid credentials. '
|
|
6
|
+
+ 'For help, see https://croct.help/sdk/js/invalid-credentials';
|
|
7
|
+
|
|
8
|
+
case 403:
|
|
9
|
+
return 'The origin of the request is not allowed in your application settings. '
|
|
10
|
+
+ 'For help, see https://croct.help/sdk/js/cors';
|
|
11
|
+
|
|
12
|
+
case 408:
|
|
13
|
+
return 'The request timed out. '
|
|
14
|
+
+ 'For help, see https://croct.help/sdk/js/timeout';
|
|
15
|
+
|
|
16
|
+
case 423:
|
|
17
|
+
return 'The application has exceeded the monthly active users (MAU) quota. '
|
|
18
|
+
+ 'For help, see https://croct.help/sdk/js/mau-exceeded';
|
|
19
|
+
|
|
20
|
+
default:
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {Queue} from './queue';
|
|
2
2
|
|
|
3
3
|
export class PersistentQueue<T> implements Queue<T> {
|
|
4
|
-
private cache: T[];
|
|
5
|
-
|
|
6
4
|
private readonly storage: Storage;
|
|
7
5
|
|
|
8
6
|
private readonly key: string;
|
|
@@ -17,7 +15,7 @@ export class PersistentQueue<T> implements Queue<T> {
|
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
public getCapacity(): number {
|
|
20
|
-
return
|
|
18
|
+
return Number.MAX_SAFE_INTEGER;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
public isEmpty(): boolean {
|
|
@@ -29,9 +27,7 @@ export class PersistentQueue<T> implements Queue<T> {
|
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
public push(value: T): void {
|
|
32
|
-
this.queue
|
|
33
|
-
|
|
34
|
-
this.flush();
|
|
30
|
+
this.save([...this.queue, value]);
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
public peek(): T | null {
|
|
@@ -45,30 +41,19 @@ export class PersistentQueue<T> implements Queue<T> {
|
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
public shift(): T {
|
|
48
|
-
const
|
|
44
|
+
const queue = [...this.queue];
|
|
45
|
+
const value = queue.shift();
|
|
49
46
|
|
|
50
47
|
if (value === undefined) {
|
|
51
48
|
throw new Error('The queue is empty.');
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
this.
|
|
51
|
+
this.save(queue);
|
|
55
52
|
|
|
56
53
|
return value;
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
private get queue(): T[] {
|
|
60
|
-
if (this.cache === undefined) {
|
|
61
|
-
this.cache = this.load();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return this.cache;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private flush(): void {
|
|
68
|
-
this.storage.setItem(this.key, JSON.stringify(this.cache ?? []));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private load(): T[] {
|
|
56
|
+
private get queue(): readonly T[] {
|
|
72
57
|
const data = this.storage.getItem(this.key);
|
|
73
58
|
|
|
74
59
|
if (data === null) {
|
|
@@ -77,8 +62,12 @@ export class PersistentQueue<T> implements Queue<T> {
|
|
|
77
62
|
|
|
78
63
|
try {
|
|
79
64
|
return JSON.parse(data);
|
|
80
|
-
} catch
|
|
65
|
+
} catch {
|
|
81
66
|
return [];
|
|
82
67
|
}
|
|
83
68
|
}
|
|
69
|
+
|
|
70
|
+
private save(data: T[]): void {
|
|
71
|
+
this.storage.setItem(this.key, JSON.stringify(data));
|
|
72
|
+
}
|
|
84
73
|
}
|
|
@@ -114,22 +114,6 @@ export const eventOccurred = new ObjectType({
|
|
|
114
114
|
minLength: 1,
|
|
115
115
|
maxLength: 50,
|
|
116
116
|
}),
|
|
117
|
-
testId: new StringType({
|
|
118
|
-
minLength: 1,
|
|
119
|
-
maxLength: 50,
|
|
120
|
-
}),
|
|
121
|
-
groupId: new StringType({
|
|
122
|
-
minLength: 1,
|
|
123
|
-
maxLength: 50,
|
|
124
|
-
}),
|
|
125
|
-
personalizationId: new StringType({
|
|
126
|
-
minLength: 1,
|
|
127
|
-
maxLength: 50,
|
|
128
|
-
}),
|
|
129
|
-
audience: new StringType({
|
|
130
|
-
minLength: 1,
|
|
131
|
-
maxLength: 50,
|
|
132
|
-
}),
|
|
133
117
|
details: new ObjectType({
|
|
134
118
|
additionalProperties: new UnionType(
|
|
135
119
|
new NullType(),
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ObjectType,
|
|
3
|
+
StringType,
|
|
4
|
+
BooleanType,
|
|
5
|
+
UnionType,
|
|
6
|
+
NullType,
|
|
7
|
+
FunctionType,
|
|
8
|
+
NumberType,
|
|
9
|
+
} from '../validation';
|
|
2
10
|
import {tokenScopeSchema} from './contextSchemas';
|
|
3
11
|
import {cookieOptionsSchema, eventMetadataSchema} from './sdkSchemas';
|
|
4
12
|
import {loggerSchema} from './loggerSchema';
|
|
@@ -45,7 +53,11 @@ export const sdkFacadeConfigurationSchema = new ObjectType({
|
|
|
45
53
|
previewToken: cookieOptionsSchema,
|
|
46
54
|
},
|
|
47
55
|
}),
|
|
48
|
-
|
|
56
|
+
defaultFetchTimeout: new NumberType({
|
|
57
|
+
integer: true,
|
|
58
|
+
minimum: 1,
|
|
59
|
+
}),
|
|
60
|
+
defaultPreferredLocale: new StringType({
|
|
49
61
|
pattern: /^[a-z]{2,3}([-_][a-z]{2,3})?$/i,
|
|
50
62
|
}),
|
|
51
63
|
},
|
package/src/schema/sdkSchemas.ts
CHANGED
|
@@ -71,5 +71,12 @@ export const sdkConfigurationSchema = new ObjectType({
|
|
|
71
71
|
urlSanitizer: new FunctionType(),
|
|
72
72
|
eventMetadata: eventMetadataSchema,
|
|
73
73
|
eventProcessor: new FunctionType(),
|
|
74
|
+
defaultFetchTimeout: new NumberType({
|
|
75
|
+
integer: true,
|
|
76
|
+
minimum: 1,
|
|
77
|
+
}),
|
|
78
|
+
defaultPreferredLocale: new StringType({
|
|
79
|
+
pattern: /^[a-z]{2,3}([-_][a-z]{2,3})?$/i,
|
|
80
|
+
}),
|
|
74
81
|
},
|
|
75
82
|
});
|
package/src/sdk.ts
CHANGED
|
@@ -33,6 +33,8 @@ export type Configuration = {
|
|
|
33
33
|
previewToken?: CookieCacheConfiguration,
|
|
34
34
|
},
|
|
35
35
|
eventProcessor?: DependencyResolver<TrackingEventProcessor>,
|
|
36
|
+
defaultFetchTimeout?: number,
|
|
37
|
+
defaultPreferredLocale?: string,
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
function validateConfiguration(configuration: unknown): asserts configuration is Configuration {
|
|
@@ -75,13 +77,12 @@ export class Sdk {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
const baseHttpEndpoint = baseEndpointUrl.replace(/\/+$/, '');
|
|
78
|
-
const baseWsEndpoint = baseHttpEndpoint.replace(/^http/i, 'ws');
|
|
79
80
|
|
|
80
81
|
const container = new Container({
|
|
81
82
|
...containerConfiguration,
|
|
82
83
|
evaluationBaseEndpointUrl: baseHttpEndpoint,
|
|
83
84
|
contentBaseEndpointUrl: baseHttpEndpoint,
|
|
84
|
-
trackerEndpointUrl: `${
|
|
85
|
+
trackerEndpointUrl: `${baseHttpEndpoint}/client/web/track`,
|
|
85
86
|
cidAssignerEndpointUrl: cidAssignerEndpointUrl ?? `${baseHttpEndpoint}/client/web/cid`,
|
|
86
87
|
beaconQueueSize: containerConfiguration.beaconQueueSize ?? 100,
|
|
87
88
|
eventMetadata: eventMetadata,
|
package/src/trackingEvents.ts
CHANGED
|
@@ -325,10 +325,6 @@ export interface PostViewed extends BaseEvent {
|
|
|
325
325
|
export interface EventOccurred extends BaseEvent {
|
|
326
326
|
type: 'eventOccurred';
|
|
327
327
|
name: string;
|
|
328
|
-
testId?: string;
|
|
329
|
-
groupId?: string;
|
|
330
|
-
personalizationId?: string;
|
|
331
|
-
audience?: string;
|
|
332
328
|
details?: {[key: string]: string|number|boolean|null};
|
|
333
329
|
}
|
|
334
330
|
|