@coframe/sdk 1.1.47 → 1.1.48
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 +174 -0
- package/dist/pkg/cf.cjs.js +89 -49
- package/dist/pkg/cf.esm.mjs +89 -49
- package/dist/pkg/feature-flags.cjs.js +4 -2
- package/dist/pkg/feature-flags.esm.mjs +4 -2
- package/dist/pkg/server.cjs.js +410 -40
- package/dist/pkg/server.esm.mjs +409 -41
- package/dist/pkg/types/index.d.ts +8 -1
- package/dist/pkg/types/react.d.ts +8 -1
- package/dist/pkg/types/server.d.ts +62 -4
- package/package.json +1 -1
package/dist/pkg/server.cjs.js
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const LAST_VISIT_TIME_KEY = 'coframe.lastVisitTime';
|
|
4
|
+
const VISIT_COUNT_COOKIE_KEY = 'coframe.cvc';
|
|
5
|
+
const LANE_COOKIE_KEY = 'coframe.lanes';
|
|
6
|
+
function readCookieHeader(cookieHeader, name) {
|
|
7
|
+
if (!cookieHeader)
|
|
8
|
+
return undefined;
|
|
9
|
+
let value;
|
|
10
|
+
for (const part of cookieHeader.split(';')) {
|
|
11
|
+
const entry = part.trim();
|
|
12
|
+
const separator = entry.indexOf('=');
|
|
13
|
+
if (separator >= 0 && entry.slice(0, separator) === name) {
|
|
14
|
+
value = entry.slice(separator + 1);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function getGlobalScope() {
|
|
20
|
+
if (typeof globalThis !== 'undefined') {
|
|
21
|
+
return globalThis;
|
|
22
|
+
}
|
|
23
|
+
if (typeof window !== 'undefined') {
|
|
24
|
+
return window;
|
|
25
|
+
}
|
|
26
|
+
if (typeof self !== 'undefined') {
|
|
27
|
+
return self;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const generateUUID = () => {
|
|
31
|
+
var _a;
|
|
32
|
+
const cryptoObject = (_a = getGlobalScope()) === null || _a === void 0 ? void 0 : _a.crypto;
|
|
33
|
+
if (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID)
|
|
34
|
+
return cryptoObject.randomUUID();
|
|
35
|
+
const randomByte = (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues)
|
|
36
|
+
? () => cryptoObject.getRandomValues(new Uint8Array(1))[0]
|
|
37
|
+
: () => Math.floor(Math.random() * 256);
|
|
38
|
+
return `${1e7}${ -1e3}${ -4e3}${ -8e3}${ -1e11}`.replace(/[018]/g, (c) => {
|
|
39
|
+
const randomValue = randomByte();
|
|
40
|
+
return (c ^
|
|
41
|
+
(randomValue & (15 >> (c / 4)))).toString(16);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const VISIT_BOUNDARY_MS = 2 * 60 * 60 * 1000;
|
|
45
|
+
function startsNewVisit(lastVisitTime, now) {
|
|
46
|
+
return !lastVisitTime || now - lastVisitTime > VISIT_BOUNDARY_MS;
|
|
47
|
+
}
|
|
48
|
+
function nextVisitCount(existing, lastVisitTime, now) {
|
|
49
|
+
return startsNewVisit(lastVisitTime, now) ? existing + 1 : existing;
|
|
50
|
+
}
|
|
51
|
+
function parseNonNegativeInt(raw) {
|
|
52
|
+
if (!raw || !/^\d+$/.test(raw))
|
|
53
|
+
return undefined;
|
|
54
|
+
const parsed = Number(raw);
|
|
55
|
+
return parsed <= 9007199254740991 ? parsed : undefined;
|
|
56
|
+
}
|
|
57
|
+
function parseVisitCount(raw) {
|
|
58
|
+
var _a;
|
|
59
|
+
return (_a = parseNonNegativeInt(raw)) !== null && _a !== void 0 ? _a : 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isRecord(value) {
|
|
63
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function inferBrowserPlatform(userAgent) {
|
|
67
|
+
const ua = userAgent !== null && userAgent !== void 0 ? userAgent : '';
|
|
68
|
+
if (/iphone/i.test(ua))
|
|
69
|
+
return 'iPhone';
|
|
70
|
+
if (/ipad/i.test(ua))
|
|
71
|
+
return 'iPad';
|
|
72
|
+
if (/android/i.test(ua))
|
|
73
|
+
return 'Linux armv8l';
|
|
74
|
+
if (/windows/i.test(ua))
|
|
75
|
+
return 'Win32';
|
|
76
|
+
if (/macintosh|mac os/i.test(ua))
|
|
77
|
+
return 'MacIntel';
|
|
78
|
+
if (/cros|linux/i.test(ua))
|
|
79
|
+
return 'Linux x86_64';
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
|
|
3
83
|
const MAX_USER_CONTEXT_JSON_BYTES = 4096;
|
|
4
84
|
function userContextJsonByteLength(userContext) {
|
|
5
85
|
try {
|
|
@@ -22,29 +102,152 @@ function sanitizeUserContext(userContext) {
|
|
|
22
102
|
return userContext;
|
|
23
103
|
}
|
|
24
104
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
105
|
+
const ASSIGNMENT_FIELD_LIMITS = {
|
|
106
|
+
projectId: 200,
|
|
107
|
+
experimentId: 512,
|
|
108
|
+
userToken: 512,
|
|
109
|
+
currentUrl: 4096,
|
|
110
|
+
referrer: 4096,
|
|
111
|
+
userAgent: 2048,
|
|
112
|
+
browserPlatform: 128,
|
|
113
|
+
country: 8,
|
|
114
|
+
region: 128,
|
|
115
|
+
city: 256,
|
|
116
|
+
verifiedBotCategory: 128,
|
|
117
|
+
clientIp: 64,
|
|
118
|
+
languages: 20,
|
|
119
|
+
languageLength: 64,
|
|
120
|
+
botScoreMin: 1,
|
|
121
|
+
botScoreMax: 99,
|
|
122
|
+
};
|
|
123
|
+
const ASSIGNMENT_DEVICES = [
|
|
124
|
+
'desktop',
|
|
125
|
+
'mobile',
|
|
126
|
+
'tablet',
|
|
127
|
+
];
|
|
128
|
+
function isInvalidString(value, max) {
|
|
129
|
+
return (value !== undefined && (typeof value !== 'string' || value.length > max));
|
|
130
|
+
}
|
|
131
|
+
function isInvalidLanguages(value) {
|
|
132
|
+
return (value !== undefined &&
|
|
133
|
+
(!Array.isArray(value) ||
|
|
134
|
+
value.length > ASSIGNMENT_FIELD_LIMITS.languages ||
|
|
135
|
+
!value.every((language) => typeof language === 'string' &&
|
|
136
|
+
language.length <= ASSIGNMENT_FIELD_LIMITS.languageLength)));
|
|
137
|
+
}
|
|
138
|
+
function invalidUserContext(value) {
|
|
139
|
+
if (value === undefined)
|
|
140
|
+
return null;
|
|
141
|
+
if (!isRecord(value)) {
|
|
142
|
+
return { field: 'userContext', message: 'must be an object' };
|
|
31
143
|
}
|
|
32
|
-
if (
|
|
33
|
-
return
|
|
144
|
+
if (userContextJsonByteLength(value) > MAX_USER_CONTEXT_JSON_BYTES) {
|
|
145
|
+
return { field: 'userContext', message: 'is too large', tooLarge: true };
|
|
34
146
|
}
|
|
147
|
+
return null;
|
|
35
148
|
}
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
(randomValue & (15 >> (c / 4)))).toString(16);
|
|
46
|
-
});
|
|
149
|
+
const ASSIGNMENT_CONTEXT_HEADERS = {
|
|
150
|
+
userAgent: 'user-agent',
|
|
151
|
+
country: 'cf-ipcountry',
|
|
152
|
+
region: 'cf-region-code',
|
|
153
|
+
city: 'cf-ipcity',
|
|
154
|
+
device: 'cf-device-type',
|
|
155
|
+
botScore: 'cf-bot-score',
|
|
156
|
+
verifiedBotCategory: 'cf-verified-bot-category',
|
|
157
|
+
clientIp: 'cf-connecting-ip',
|
|
47
158
|
};
|
|
159
|
+
const HEADER_STRING_FIELDS = [
|
|
160
|
+
'userAgent',
|
|
161
|
+
'country',
|
|
162
|
+
'region',
|
|
163
|
+
'city',
|
|
164
|
+
'verifiedBotCategory',
|
|
165
|
+
'clientIp',
|
|
166
|
+
];
|
|
167
|
+
const CONTEXT_STRING_FIELDS = [
|
|
168
|
+
'referrer',
|
|
169
|
+
'userAgent',
|
|
170
|
+
'browserPlatform',
|
|
171
|
+
'country',
|
|
172
|
+
'region',
|
|
173
|
+
'city',
|
|
174
|
+
'verifiedBotCategory',
|
|
175
|
+
'clientIp',
|
|
176
|
+
];
|
|
177
|
+
function invalidAssignmentContextField(context) {
|
|
178
|
+
if (!isRecord(context)) {
|
|
179
|
+
return { field: 'context', message: 'must be an object' };
|
|
180
|
+
}
|
|
181
|
+
const { currentUrl } = context;
|
|
182
|
+
if (typeof currentUrl !== 'string' || !currentUrl) {
|
|
183
|
+
return { field: 'currentUrl', message: 'is required' };
|
|
184
|
+
}
|
|
185
|
+
if (currentUrl.length > ASSIGNMENT_FIELD_LIMITS.currentUrl) {
|
|
186
|
+
return {
|
|
187
|
+
field: 'currentUrl',
|
|
188
|
+
message: `must be at most ${ASSIGNMENT_FIELD_LIMITS.currentUrl} characters`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
new URL(currentUrl);
|
|
193
|
+
}
|
|
194
|
+
catch (_a) {
|
|
195
|
+
return { field: 'currentUrl', message: 'must be an absolute URL' };
|
|
196
|
+
}
|
|
197
|
+
for (const field of CONTEXT_STRING_FIELDS) {
|
|
198
|
+
const max = ASSIGNMENT_FIELD_LIMITS[field];
|
|
199
|
+
if (isInvalidString(context[field], max)) {
|
|
200
|
+
return {
|
|
201
|
+
field,
|
|
202
|
+
message: `must be a string of at most ${max} characters`,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (context.device !== undefined &&
|
|
207
|
+
!ASSIGNMENT_DEVICES.includes(context.device)) {
|
|
208
|
+
return { field: 'device', message: 'must be desktop, mobile, or tablet' };
|
|
209
|
+
}
|
|
210
|
+
const { botScore, languages, userVisitCount, userContext } = context;
|
|
211
|
+
if (botScore !== undefined &&
|
|
212
|
+
(!Number.isInteger(botScore) ||
|
|
213
|
+
botScore < ASSIGNMENT_FIELD_LIMITS.botScoreMin ||
|
|
214
|
+
botScore > ASSIGNMENT_FIELD_LIMITS.botScoreMax)) {
|
|
215
|
+
return {
|
|
216
|
+
field: 'botScore',
|
|
217
|
+
message: `must be an integer from ${ASSIGNMENT_FIELD_LIMITS.botScoreMin} to ${ASSIGNMENT_FIELD_LIMITS.botScoreMax}`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (isInvalidLanguages(languages)) {
|
|
221
|
+
return {
|
|
222
|
+
field: 'languages',
|
|
223
|
+
message: `must contain at most ${ASSIGNMENT_FIELD_LIMITS.languages} strings of ${ASSIGNMENT_FIELD_LIMITS.languageLength} characters`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (userVisitCount !== undefined &&
|
|
227
|
+
(!Number.isSafeInteger(userVisitCount) || userVisitCount < 0)) {
|
|
228
|
+
return {
|
|
229
|
+
field: 'userVisitCount',
|
|
230
|
+
message: 'must be a non-negative safe integer',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return invalidUserContext(userContext);
|
|
234
|
+
}
|
|
235
|
+
const ID_FIELDS = ['projectId', 'experimentId', 'userToken'];
|
|
236
|
+
function invalidAssignmentIdField(body) {
|
|
237
|
+
for (const field of ID_FIELDS) {
|
|
238
|
+
const value = body[field];
|
|
239
|
+
if (typeof value !== 'string' || !value) {
|
|
240
|
+
return { field, message: 'is required' };
|
|
241
|
+
}
|
|
242
|
+
if (value.length > ASSIGNMENT_FIELD_LIMITS[field]) {
|
|
243
|
+
return {
|
|
244
|
+
field,
|
|
245
|
+
message: `must be at most ${ASSIGNMENT_FIELD_LIMITS[field]} characters`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
48
251
|
|
|
49
252
|
async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext, previewVariantId, headers, referrer) {
|
|
50
253
|
const payload = { projectId, currentUrl };
|
|
@@ -312,12 +515,189 @@ async function flushFeatureFlagEvents() {
|
|
|
312
515
|
}
|
|
313
516
|
}
|
|
314
517
|
|
|
518
|
+
const DEFAULT_ASSIGNMENT_TIMEOUT_MS = 1500;
|
|
519
|
+
function normalizeRequestHeaders(headers) {
|
|
520
|
+
const normalized = new Headers();
|
|
521
|
+
if (typeof (headers === null || headers === void 0 ? void 0 : headers.forEach) === 'function') {
|
|
522
|
+
headers.forEach((value, name) => {
|
|
523
|
+
normalized.set(name, value);
|
|
524
|
+
});
|
|
525
|
+
return normalized;
|
|
526
|
+
}
|
|
527
|
+
for (const [name, value] of Object.entries(headers !== null && headers !== void 0 ? headers : {})) {
|
|
528
|
+
const joined = typeof value === 'string'
|
|
529
|
+
? value
|
|
530
|
+
: Array.isArray(value)
|
|
531
|
+
? value.join(name.toLowerCase() === 'cookie' ? '; ' : ', ')
|
|
532
|
+
: undefined;
|
|
533
|
+
if (joined === undefined)
|
|
534
|
+
continue;
|
|
535
|
+
try {
|
|
536
|
+
normalized.set(name, joined);
|
|
537
|
+
}
|
|
538
|
+
catch (_a) { }
|
|
539
|
+
}
|
|
540
|
+
return normalized;
|
|
541
|
+
}
|
|
542
|
+
function createAssignmentHeaders(incoming) {
|
|
543
|
+
const headers = new Headers({ 'Content-Type': 'application/json' });
|
|
544
|
+
const laneCookie = readCookieHeader(incoming.get('cookie'), LANE_COOKIE_KEY);
|
|
545
|
+
if (laneCookie !== undefined) {
|
|
546
|
+
headers.set('cookie', `${LANE_COOKIE_KEY}=${laneCookie}`);
|
|
547
|
+
}
|
|
548
|
+
return headers;
|
|
549
|
+
}
|
|
550
|
+
function requestLanguages(headers) {
|
|
551
|
+
var _a;
|
|
552
|
+
const languages = ((_a = headers.get('accept-language')) !== null && _a !== void 0 ? _a : '')
|
|
553
|
+
.split(',')
|
|
554
|
+
.map((entry) => { var _a; return (_a = entry.split(';')[0]) === null || _a === void 0 ? void 0 : _a.trim(); })
|
|
555
|
+
.filter((entry) => !!entry && entry.length <= ASSIGNMENT_FIELD_LIMITS.languageLength)
|
|
556
|
+
.slice(0, ASSIGNMENT_FIELD_LIMITS.languages);
|
|
557
|
+
return languages.length > 0 ? languages : undefined;
|
|
558
|
+
}
|
|
559
|
+
function clampHeader(value, maxLength) {
|
|
560
|
+
if (!value)
|
|
561
|
+
return undefined;
|
|
562
|
+
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
563
|
+
}
|
|
564
|
+
function headerStrings(options, incoming) {
|
|
565
|
+
var _a;
|
|
566
|
+
const values = {};
|
|
567
|
+
for (const field of HEADER_STRING_FIELDS) {
|
|
568
|
+
const value = (_a = options[field]) !== null && _a !== void 0 ? _a : clampHeader(incoming.get(ASSIGNMENT_CONTEXT_HEADERS[field]), ASSIGNMENT_FIELD_LIMITS[field]);
|
|
569
|
+
if (value)
|
|
570
|
+
values[field] = value;
|
|
571
|
+
}
|
|
572
|
+
return values;
|
|
573
|
+
}
|
|
574
|
+
function requestDevice(headers) {
|
|
575
|
+
var _a;
|
|
576
|
+
const device = (_a = headers.get(ASSIGNMENT_CONTEXT_HEADERS.device)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
577
|
+
return ASSIGNMENT_DEVICES.includes(device)
|
|
578
|
+
? device
|
|
579
|
+
: undefined;
|
|
580
|
+
}
|
|
581
|
+
function requestBotScore(headers) {
|
|
582
|
+
const score = parseNonNegativeInt(headers.get(ASSIGNMENT_CONTEXT_HEADERS.botScore));
|
|
583
|
+
return score !== undefined &&
|
|
584
|
+
score >= ASSIGNMENT_FIELD_LIMITS.botScoreMin &&
|
|
585
|
+
score <= ASSIGNMENT_FIELD_LIMITS.botScoreMax
|
|
586
|
+
? score
|
|
587
|
+
: undefined;
|
|
588
|
+
}
|
|
589
|
+
function edgeBaseUrl(edgeUrl) {
|
|
590
|
+
return (edgeUrl || "https://edge.cofra.me").replace(/\/+$/, '');
|
|
591
|
+
}
|
|
592
|
+
function validateAssignmentContextOptions(options) {
|
|
593
|
+
const problem = invalidAssignmentContextField(options);
|
|
594
|
+
if (problem)
|
|
595
|
+
throw new Error(`${problem.field} ${problem.message}`);
|
|
596
|
+
}
|
|
597
|
+
function effectiveVisitCount(headers) {
|
|
598
|
+
const cookie = headers.get('cookie');
|
|
599
|
+
const existing = parseVisitCount(readCookieHeader(cookie, VISIT_COUNT_COOKIE_KEY));
|
|
600
|
+
const lastVisitTime = parseVisitCount(readCookieHeader(cookie, LAST_VISIT_TIME_KEY));
|
|
601
|
+
const next = nextVisitCount(existing, lastVisitTime, Date.now());
|
|
602
|
+
return Number.isSafeInteger(next) ? next : existing;
|
|
603
|
+
}
|
|
604
|
+
function isExperimentAssignmentResult(value, experimentId) {
|
|
605
|
+
if (!isRecord(value) || !isRecord(value.projectData))
|
|
606
|
+
return false;
|
|
607
|
+
const { projectData } = value;
|
|
608
|
+
const context = projectData.assignmentContext;
|
|
609
|
+
if (!isRecord(projectData.project) ||
|
|
610
|
+
!Array.isArray(projectData.project.frames) ||
|
|
611
|
+
!isRecord(projectData.personalizationHeaders) ||
|
|
612
|
+
!isRecord(context) ||
|
|
613
|
+
!Array.isArray(context.languages) ||
|
|
614
|
+
!context.languages.every((language) => typeof language === 'string') ||
|
|
615
|
+
typeof context.browserPlatform !== 'string' ||
|
|
616
|
+
!Number.isSafeInteger(context.userVisitCount) ||
|
|
617
|
+
context.userVisitCount < 0) {
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
if (value.variant === null) {
|
|
621
|
+
return typeof value.reason === 'string';
|
|
622
|
+
}
|
|
623
|
+
if (!isRecord(value.variant))
|
|
624
|
+
return false;
|
|
625
|
+
return (typeof value.variant.id === 'string' &&
|
|
626
|
+
typeof value.variant.slug === 'string' &&
|
|
627
|
+
value.variant.experimentId === experimentId &&
|
|
628
|
+
typeof value.variant.iterationId === 'string');
|
|
629
|
+
}
|
|
630
|
+
function generateUserToken() {
|
|
631
|
+
return generateUUID();
|
|
632
|
+
}
|
|
633
|
+
async function getExperimentAssignment(options) {
|
|
634
|
+
var _a, _b, _c, _d, _e, _f;
|
|
635
|
+
const idProblem = invalidAssignmentIdField(options);
|
|
636
|
+
if (idProblem)
|
|
637
|
+
throw new Error(`${idProblem.field} ${idProblem.message}`);
|
|
638
|
+
if (options.timeoutMs !== undefined &&
|
|
639
|
+
!(Number.isFinite(options.timeoutMs) && options.timeoutMs > 0)) {
|
|
640
|
+
throw new Error('timeoutMs must be a positive finite number');
|
|
641
|
+
}
|
|
642
|
+
validateAssignmentContextOptions(options);
|
|
643
|
+
const currentUrl = new URL(options.currentUrl);
|
|
644
|
+
if (/cf_disable/.test(currentUrl.search))
|
|
645
|
+
return undefined;
|
|
646
|
+
const incoming = normalizeRequestHeaders(options.headers);
|
|
647
|
+
const controller = new AbortController();
|
|
648
|
+
const timeout = setTimeout(() => controller.abort(), (_a = options.timeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_ASSIGNMENT_TIMEOUT_MS);
|
|
649
|
+
try {
|
|
650
|
+
const fromHeaders = headerStrings(options, incoming);
|
|
651
|
+
const languages = (_b = options.languages) !== null && _b !== void 0 ? _b : requestLanguages(incoming);
|
|
652
|
+
const browserPlatform = (_c = options.browserPlatform) !== null && _c !== void 0 ? _c : inferBrowserPlatform(fromHeaders.userAgent);
|
|
653
|
+
const device = (_d = options.device) !== null && _d !== void 0 ? _d : requestDevice(incoming);
|
|
654
|
+
const botScore = (_e = options.botScore) !== null && _e !== void 0 ? _e : requestBotScore(incoming);
|
|
655
|
+
const userVisitCount = (_f = options.userVisitCount) !== null && _f !== void 0 ? _f : effectiveVisitCount(incoming);
|
|
656
|
+
const referrer = options.referrer !== undefined
|
|
657
|
+
? options.referrer
|
|
658
|
+
: clampHeader(incoming.get('referer'), ASSIGNMENT_FIELD_LIMITS.referrer);
|
|
659
|
+
const requestBody = {
|
|
660
|
+
projectId: options.projectId,
|
|
661
|
+
experimentId: options.experimentId,
|
|
662
|
+
userToken: options.userToken,
|
|
663
|
+
context: {
|
|
664
|
+
currentUrl: options.currentUrl,
|
|
665
|
+
...(referrer !== undefined ? { referrer } : {}),
|
|
666
|
+
...(options.userContext ? { userContext: options.userContext } : {}),
|
|
667
|
+
...fromHeaders,
|
|
668
|
+
...(languages !== undefined ? { languages } : {}),
|
|
669
|
+
...(browserPlatform !== undefined ? { browserPlatform } : {}),
|
|
670
|
+
...(device ? { device } : {}),
|
|
671
|
+
...(botScore !== undefined ? { botScore } : {}),
|
|
672
|
+
userVisitCount,
|
|
673
|
+
},
|
|
674
|
+
};
|
|
675
|
+
const response = await fetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/assignment`, {
|
|
676
|
+
method: 'POST',
|
|
677
|
+
headers: createAssignmentHeaders(incoming),
|
|
678
|
+
signal: controller.signal,
|
|
679
|
+
body: JSON.stringify(requestBody),
|
|
680
|
+
});
|
|
681
|
+
if (!response.ok)
|
|
682
|
+
return undefined;
|
|
683
|
+
const result = await response.json();
|
|
684
|
+
return isExperimentAssignmentResult(result, options.experimentId)
|
|
685
|
+
? result
|
|
686
|
+
: undefined;
|
|
687
|
+
}
|
|
688
|
+
catch (_g) {
|
|
689
|
+
return undefined;
|
|
690
|
+
}
|
|
691
|
+
finally {
|
|
692
|
+
clearTimeout(timeout);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
315
695
|
async function requestFeatureFlags(options) {
|
|
316
|
-
var _a
|
|
696
|
+
var _a;
|
|
317
697
|
const controller = new AbortController();
|
|
318
698
|
const timeout = setTimeout(() => controller.abort(), (_a = options.requestTimeoutMs) !== null && _a !== void 0 ? _a : 3000);
|
|
319
699
|
try {
|
|
320
|
-
const response = await fetch(`${(
|
|
700
|
+
const response = await fetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/flags?v=1`, {
|
|
321
701
|
method: 'POST',
|
|
322
702
|
headers: {
|
|
323
703
|
'Content-Type': 'application/json',
|
|
@@ -345,7 +725,7 @@ async function requestFeatureFlags(options) {
|
|
|
345
725
|
return undefined;
|
|
346
726
|
return (await response.json());
|
|
347
727
|
}
|
|
348
|
-
catch (
|
|
728
|
+
catch (_b) {
|
|
349
729
|
return undefined;
|
|
350
730
|
}
|
|
351
731
|
finally {
|
|
@@ -389,19 +769,6 @@ async function getFeatureFlagPayload(key, options) {
|
|
|
389
769
|
return 'payload' in result ? ((_a = result.payload) !== null && _a !== void 0 ? _a : null) : null;
|
|
390
770
|
}
|
|
391
771
|
const getRemoteConfig = getFeatureFlagPayload;
|
|
392
|
-
function getRequestReferrer(headers) {
|
|
393
|
-
const get = headers.get;
|
|
394
|
-
if (typeof get === 'function') {
|
|
395
|
-
const value = get.call(headers, 'referer');
|
|
396
|
-
return typeof value === 'string' ? value : undefined;
|
|
397
|
-
}
|
|
398
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
399
|
-
if (key.toLowerCase() === 'referer') {
|
|
400
|
-
return typeof value === 'string' ? value : undefined;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
return undefined;
|
|
404
|
-
}
|
|
405
772
|
async function fetchAntiflickerFromEdge(projectId, currentUrl, referrer, userContext, antiflickerMode, headers) {
|
|
406
773
|
try {
|
|
407
774
|
const payload = { projectId, currentUrl };
|
|
@@ -429,23 +796,24 @@ async function fetchAntiflickerFromEdge(projectId, currentUrl, referrer, userCon
|
|
|
429
796
|
}
|
|
430
797
|
}
|
|
431
798
|
async function getProjectData(options) {
|
|
432
|
-
var _a;
|
|
799
|
+
var _a, _b;
|
|
433
800
|
try {
|
|
434
801
|
if (/cf_disable/.test(new URL(options.currentUrl).search)) {
|
|
435
802
|
return null;
|
|
436
803
|
}
|
|
437
804
|
}
|
|
438
|
-
catch (
|
|
805
|
+
catch (_c) {
|
|
439
806
|
}
|
|
440
807
|
const includeProject = options.includeProject !== false;
|
|
441
808
|
const includeCSS = options.includeCSS !== false;
|
|
442
|
-
const
|
|
809
|
+
const headers = normalizeRequestHeaders(options.headers);
|
|
810
|
+
const referrer = (_b = (_a = options.referrer) !== null && _a !== void 0 ? _a : headers.get('referer')) !== null && _b !== void 0 ? _b : undefined;
|
|
443
811
|
const [projectResult, antiflickerCSS] = await Promise.all([
|
|
444
812
|
includeProject
|
|
445
|
-
? fetchProjectFromEdge(options.projectId, options.currentUrl, undefined, options.userContext, undefined,
|
|
813
|
+
? fetchProjectFromEdge(options.projectId, options.currentUrl, undefined, options.userContext, undefined, headers, referrer)
|
|
446
814
|
: Promise.resolve(null),
|
|
447
815
|
includeCSS
|
|
448
|
-
? fetchAntiflickerFromEdge(options.projectId, options.currentUrl, referrer, options.userContext, options.antiflickerMode,
|
|
816
|
+
? fetchAntiflickerFromEdge(options.projectId, options.currentUrl, referrer, options.userContext, options.antiflickerMode, headers)
|
|
449
817
|
: Promise.resolve(null),
|
|
450
818
|
]);
|
|
451
819
|
const config = {};
|
|
@@ -467,6 +835,8 @@ async function getProjectData(options) {
|
|
|
467
835
|
|
|
468
836
|
exports.evaluateFlags = evaluateFlags;
|
|
469
837
|
exports.flushFeatureFlagEvents = flushFeatureFlagEvents;
|
|
838
|
+
exports.generateUserToken = generateUserToken;
|
|
839
|
+
exports.getExperimentAssignment = getExperimentAssignment;
|
|
470
840
|
exports.getFeatureFlag = getFeatureFlag;
|
|
471
841
|
exports.getFeatureFlagPayload = getFeatureFlagPayload;
|
|
472
842
|
exports.getFeatureFlagResult = getFeatureFlagResult;
|