posthog-js-lite 3.6.0 → 4.1.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/CHANGELOG.md +11 -0
- package/lib/index.cjs +129 -645
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.ts +39 -39
- package/lib/index.mjs +129 -645
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/posthog-web.spec.ts +1 -1
package/lib/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var version = "
|
|
5
|
+
var version = "4.1.0";
|
|
6
6
|
|
|
7
7
|
var PostHogPersistedProperty;
|
|
8
8
|
(function (PostHogPersistedProperty) {
|
|
@@ -26,12 +26,18 @@ var PostHogPersistedProperty;
|
|
|
26
26
|
PostHogPersistedProperty["InstalledAppBuild"] = "installed_app_build";
|
|
27
27
|
PostHogPersistedProperty["InstalledAppVersion"] = "installed_app_version";
|
|
28
28
|
PostHogPersistedProperty["SessionReplay"] = "session_replay";
|
|
29
|
-
PostHogPersistedProperty["DecideEndpointWasHit"] = "decide_endpoint_was_hit";
|
|
30
29
|
PostHogPersistedProperty["SurveyLastSeenDate"] = "survey_last_seen_date";
|
|
31
30
|
PostHogPersistedProperty["SurveysSeen"] = "surveys_seen";
|
|
32
31
|
PostHogPersistedProperty["Surveys"] = "surveys";
|
|
33
32
|
PostHogPersistedProperty["RemoteConfig"] = "remote_config";
|
|
33
|
+
PostHogPersistedProperty["FlagsEndpointWasHit"] = "flags_endpoint_was_hit";
|
|
34
34
|
})(PostHogPersistedProperty || (PostHogPersistedProperty = {}));
|
|
35
|
+
// Any key prefixed with `attr__` can be added
|
|
36
|
+
var Compression;
|
|
37
|
+
(function (Compression) {
|
|
38
|
+
Compression["GZipJS"] = "gzip-js";
|
|
39
|
+
Compression["Base64"] = "base64";
|
|
40
|
+
})(Compression || (Compression = {}));
|
|
35
41
|
var SurveyPosition;
|
|
36
42
|
(function (SurveyPosition) {
|
|
37
43
|
SurveyPosition["Left"] = "left";
|
|
@@ -92,27 +98,27 @@ var ActionStepStringMatching;
|
|
|
92
98
|
ActionStepStringMatching["Regex"] = "regex";
|
|
93
99
|
})(ActionStepStringMatching || (ActionStepStringMatching = {}));
|
|
94
100
|
|
|
95
|
-
const
|
|
96
|
-
if ('flags' in
|
|
97
|
-
// Convert
|
|
98
|
-
const featureFlags = getFlagValuesFromFlags(
|
|
99
|
-
const featureFlagPayloads = getPayloadsFromFlags(
|
|
101
|
+
const normalizeFlagsResponse = (flagsResponse) => {
|
|
102
|
+
if ('flags' in flagsResponse) {
|
|
103
|
+
// Convert v2 format to v1 format
|
|
104
|
+
const featureFlags = getFlagValuesFromFlags(flagsResponse.flags);
|
|
105
|
+
const featureFlagPayloads = getPayloadsFromFlags(flagsResponse.flags);
|
|
100
106
|
return {
|
|
101
|
-
...
|
|
107
|
+
...flagsResponse,
|
|
102
108
|
featureFlags,
|
|
103
109
|
featureFlagPayloads,
|
|
104
110
|
};
|
|
105
111
|
}
|
|
106
112
|
else {
|
|
107
|
-
// Convert
|
|
108
|
-
const featureFlags =
|
|
109
|
-
const featureFlagPayloads = Object.fromEntries(Object.entries(
|
|
113
|
+
// Convert v1 format to v2 format
|
|
114
|
+
const featureFlags = flagsResponse.featureFlags ?? {};
|
|
115
|
+
const featureFlagPayloads = Object.fromEntries(Object.entries(flagsResponse.featureFlagPayloads || {}).map(([k, v]) => [k, parsePayload(v)]));
|
|
110
116
|
const flags = Object.fromEntries(Object.entries(featureFlags).map(([key, value]) => [
|
|
111
117
|
key,
|
|
112
118
|
getFlagDetailFromFlagAndPayload(key, value, featureFlagPayloads[key]),
|
|
113
119
|
]));
|
|
114
120
|
return {
|
|
115
|
-
...
|
|
121
|
+
...flagsResponse,
|
|
116
122
|
featureFlags,
|
|
117
123
|
featureFlagPayloads,
|
|
118
124
|
flags,
|
|
@@ -184,7 +190,7 @@ const parsePayload = (response) => {
|
|
|
184
190
|
* @param featureFlagPayloads - The feature flag payloads
|
|
185
191
|
* @returns The normalized flag details
|
|
186
192
|
*/
|
|
187
|
-
const
|
|
193
|
+
const createFlagsResponseFromFlagsAndPayloads = (featureFlags, featureFlagPayloads) => {
|
|
188
194
|
// If a feature flag payload key is not in the feature flags, we treat it as true feature flag.
|
|
189
195
|
const allKeys = [...new Set([...Object.keys(featureFlags ?? {}), ...Object.keys(featureFlagPayloads ?? {})])];
|
|
190
196
|
const enabledFlags = allKeys
|
|
@@ -194,7 +200,7 @@ const createDecideResponseFromFlagsAndPayloads = (featureFlags, featureFlagPaylo
|
|
|
194
200
|
featureFlags: enabledFlags,
|
|
195
201
|
featureFlagPayloads: featureFlagPayloads ?? {},
|
|
196
202
|
};
|
|
197
|
-
return
|
|
203
|
+
return normalizeFlagsResponse(flagDetails);
|
|
198
204
|
};
|
|
199
205
|
const updateFlagValue = (flag, value) => {
|
|
200
206
|
return {
|
|
@@ -210,90 +216,6 @@ function getVariantFromValue(value) {
|
|
|
210
216
|
return typeof value === 'string' ? value : undefined;
|
|
211
217
|
}
|
|
212
218
|
|
|
213
|
-
// Rollout constants
|
|
214
|
-
const NEW_FLAGS_ROLLOUT_PERCENTAGE = 1;
|
|
215
|
-
// The fnv1a hashes of the tokens that are explicitly excluded from the rollout
|
|
216
|
-
// see https://github.com/PostHog/posthog-js-lite/blob/main/posthog-core/src/utils.ts#L84
|
|
217
|
-
// are hashed API tokens from our top 10 for each category supported by this SDK.
|
|
218
|
-
const NEW_FLAGS_EXCLUDED_HASHES = new Set([
|
|
219
|
-
// Node
|
|
220
|
-
'61be3dd8',
|
|
221
|
-
'96f6df5f',
|
|
222
|
-
'8cfdba9b',
|
|
223
|
-
'bf027177',
|
|
224
|
-
'e59430a8',
|
|
225
|
-
'7fa5500b',
|
|
226
|
-
'569798e9',
|
|
227
|
-
'04809ff7',
|
|
228
|
-
'0ebc61a5',
|
|
229
|
-
'32de7f98',
|
|
230
|
-
'3beeb69a',
|
|
231
|
-
'12d34ad9',
|
|
232
|
-
'733853ec',
|
|
233
|
-
'0645bb64',
|
|
234
|
-
'5dcbee21',
|
|
235
|
-
'b1f95fa3',
|
|
236
|
-
'2189e408',
|
|
237
|
-
'82b460c2',
|
|
238
|
-
'3a8cc979',
|
|
239
|
-
'29ef8843',
|
|
240
|
-
'2cdbf767',
|
|
241
|
-
'38084b54',
|
|
242
|
-
// React Native
|
|
243
|
-
'50f9f8de',
|
|
244
|
-
'41d0df91',
|
|
245
|
-
'5c236689',
|
|
246
|
-
'c11aedd3',
|
|
247
|
-
'ada46672',
|
|
248
|
-
'f4331ee1',
|
|
249
|
-
'42fed62a',
|
|
250
|
-
'c957462c',
|
|
251
|
-
'd62f705a',
|
|
252
|
-
// Web (lots of teams per org, hence lots of API tokens)
|
|
253
|
-
'e0162666',
|
|
254
|
-
'01b3e5cf',
|
|
255
|
-
'441cef7f',
|
|
256
|
-
'bb9cafee',
|
|
257
|
-
'8f348eb0',
|
|
258
|
-
'b2553f3a',
|
|
259
|
-
'97469d7d',
|
|
260
|
-
'39f21a76',
|
|
261
|
-
'03706dcc',
|
|
262
|
-
'27d50569',
|
|
263
|
-
'307584a7',
|
|
264
|
-
'6433e92e',
|
|
265
|
-
'150c7fbb',
|
|
266
|
-
'49f57f22',
|
|
267
|
-
'3772f65b',
|
|
268
|
-
'01eb8256',
|
|
269
|
-
'3c9e9234',
|
|
270
|
-
'f853c7f7',
|
|
271
|
-
'c0ac4b67',
|
|
272
|
-
'cd609d40',
|
|
273
|
-
'10ca9b1a',
|
|
274
|
-
'8a87f11b',
|
|
275
|
-
'8e8e5216',
|
|
276
|
-
'1f6b63b3',
|
|
277
|
-
'db7943dd',
|
|
278
|
-
'79b7164c',
|
|
279
|
-
'07f78e33',
|
|
280
|
-
'2d21b6fd',
|
|
281
|
-
'952db5ee',
|
|
282
|
-
'a7d3b43f',
|
|
283
|
-
'1924dd9c',
|
|
284
|
-
'84e1b8f6',
|
|
285
|
-
'dff631b6',
|
|
286
|
-
'c5aa8a79',
|
|
287
|
-
'fa133a95',
|
|
288
|
-
'498a4508',
|
|
289
|
-
'24748755',
|
|
290
|
-
'98f3d658',
|
|
291
|
-
'21bbda67',
|
|
292
|
-
'7dbfed69',
|
|
293
|
-
'be3ec24c',
|
|
294
|
-
'fc80b8e2',
|
|
295
|
-
'75cc0998',
|
|
296
|
-
]);
|
|
297
219
|
const STRING_FORMAT = 'utf8';
|
|
298
220
|
function assert(truthyValue, message) {
|
|
299
221
|
if (!truthyValue || typeof truthyValue !== 'string' || isEmpty(truthyValue)) {
|
|
@@ -349,464 +271,37 @@ const isError = (x) => {
|
|
|
349
271
|
function getFetch() {
|
|
350
272
|
return typeof fetch !== 'undefined' ? fetch : typeof globalThis.fetch !== 'undefined' ? globalThis.fetch : undefined;
|
|
351
273
|
}
|
|
352
|
-
// FNV-1a hash function
|
|
353
|
-
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
|
354
|
-
// I know, I know, I'm rolling my own hash function, but I didn't want to take on
|
|
355
|
-
// a crypto dependency and this is just temporary anyway
|
|
356
|
-
function fnv1a(str) {
|
|
357
|
-
let hash = 0x811c9dc5; // FNV offset basis
|
|
358
|
-
for (let i = 0; i < str.length; i++) {
|
|
359
|
-
hash ^= str.charCodeAt(i);
|
|
360
|
-
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
361
|
-
}
|
|
362
|
-
// Convert to hex string, padding to 8 chars
|
|
363
|
-
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
364
|
-
}
|
|
365
|
-
function isTokenInRollout(token, percentage = 0, excludedHashes) {
|
|
366
|
-
const tokenHash = fnv1a(token);
|
|
367
|
-
// Check excluded hashes (we're explicitly including these tokens from the rollout)
|
|
368
|
-
if (excludedHashes?.has(tokenHash)) {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
// Convert hash to int and divide by max value to get number between 0-1
|
|
372
|
-
const hashInt = parseInt(tokenHash, 16);
|
|
373
|
-
const hashFloat = hashInt / 0xffffffff;
|
|
374
|
-
return hashFloat < percentage;
|
|
375
|
-
}
|
|
376
274
|
function allSettled(promises) {
|
|
377
275
|
return Promise.all(promises.map((p) => (p ?? Promise.resolve()).then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason }))));
|
|
378
276
|
}
|
|
379
277
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// http://pieroxy.net/blog/pages/lz-string/testing.html
|
|
387
|
-
//
|
|
388
|
-
// LZ-based compression algorithm, version 1.4.4
|
|
389
|
-
// private property
|
|
390
|
-
const f = String.fromCharCode;
|
|
391
|
-
const keyStrBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
392
|
-
const baseReverseDic = {};
|
|
393
|
-
function getBaseValue(alphabet, character) {
|
|
394
|
-
if (!baseReverseDic[alphabet]) {
|
|
395
|
-
baseReverseDic[alphabet] = {};
|
|
396
|
-
for (let i = 0; i < alphabet.length; i++) {
|
|
397
|
-
baseReverseDic[alphabet][alphabet.charAt(i)] = i;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return baseReverseDic[alphabet][character];
|
|
278
|
+
/**
|
|
279
|
+
* Older browsers and some runtimes don't support this yet
|
|
280
|
+
* This API (as of 2025-05-07) is not available on React Native.
|
|
281
|
+
*/
|
|
282
|
+
function isGzipSupported() {
|
|
283
|
+
return 'CompressionStream' in globalThis;
|
|
401
284
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
return res + '==';
|
|
419
|
-
case 3:
|
|
420
|
-
return res + '=';
|
|
421
|
-
}
|
|
422
|
-
},
|
|
423
|
-
decompressFromBase64: function (input) {
|
|
424
|
-
if (input == null) {
|
|
425
|
-
return '';
|
|
426
|
-
}
|
|
427
|
-
if (input == '') {
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
return LZString._decompress(input.length, 32, function (index) {
|
|
431
|
-
return getBaseValue(keyStrBase64, input.charAt(index));
|
|
432
|
-
});
|
|
433
|
-
},
|
|
434
|
-
compress: function (uncompressed) {
|
|
435
|
-
return LZString._compress(uncompressed, 16, function (a) {
|
|
436
|
-
return f(a);
|
|
437
|
-
});
|
|
438
|
-
},
|
|
439
|
-
_compress: function (uncompressed, bitsPerChar, getCharFromInt) {
|
|
440
|
-
if (uncompressed == null) {
|
|
441
|
-
return '';
|
|
442
|
-
}
|
|
443
|
-
const context_dictionary = {}, context_dictionaryToCreate = {}, context_data = [];
|
|
444
|
-
let i, value, context_c = '', context_wc = '', context_w = '', context_enlargeIn = 2, // Compensate for the first entry which should not count
|
|
445
|
-
context_dictSize = 3, context_numBits = 2, context_data_val = 0, context_data_position = 0, ii;
|
|
446
|
-
for (ii = 0; ii < uncompressed.length; ii += 1) {
|
|
447
|
-
context_c = uncompressed.charAt(ii);
|
|
448
|
-
if (!Object.prototype.hasOwnProperty.call(context_dictionary, context_c)) {
|
|
449
|
-
context_dictionary[context_c] = context_dictSize++;
|
|
450
|
-
context_dictionaryToCreate[context_c] = true;
|
|
451
|
-
}
|
|
452
|
-
context_wc = context_w + context_c;
|
|
453
|
-
if (Object.prototype.hasOwnProperty.call(context_dictionary, context_wc)) {
|
|
454
|
-
context_w = context_wc;
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
|
|
458
|
-
if (context_w.charCodeAt(0) < 256) {
|
|
459
|
-
for (i = 0; i < context_numBits; i++) {
|
|
460
|
-
context_data_val = context_data_val << 1;
|
|
461
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
462
|
-
context_data_position = 0;
|
|
463
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
464
|
-
context_data_val = 0;
|
|
465
|
-
}
|
|
466
|
-
else {
|
|
467
|
-
context_data_position++;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
value = context_w.charCodeAt(0);
|
|
471
|
-
for (i = 0; i < 8; i++) {
|
|
472
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
473
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
474
|
-
context_data_position = 0;
|
|
475
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
476
|
-
context_data_val = 0;
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
context_data_position++;
|
|
480
|
-
}
|
|
481
|
-
value = value >> 1;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
else {
|
|
485
|
-
value = 1;
|
|
486
|
-
for (i = 0; i < context_numBits; i++) {
|
|
487
|
-
context_data_val = (context_data_val << 1) | value;
|
|
488
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
489
|
-
context_data_position = 0;
|
|
490
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
491
|
-
context_data_val = 0;
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
context_data_position++;
|
|
495
|
-
}
|
|
496
|
-
value = 0;
|
|
497
|
-
}
|
|
498
|
-
value = context_w.charCodeAt(0);
|
|
499
|
-
for (i = 0; i < 16; i++) {
|
|
500
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
501
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
502
|
-
context_data_position = 0;
|
|
503
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
504
|
-
context_data_val = 0;
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
context_data_position++;
|
|
508
|
-
}
|
|
509
|
-
value = value >> 1;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
context_enlargeIn--;
|
|
513
|
-
if (context_enlargeIn == 0) {
|
|
514
|
-
context_enlargeIn = Math.pow(2, context_numBits);
|
|
515
|
-
context_numBits++;
|
|
516
|
-
}
|
|
517
|
-
delete context_dictionaryToCreate[context_w];
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
value = context_dictionary[context_w];
|
|
521
|
-
for (i = 0; i < context_numBits; i++) {
|
|
522
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
523
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
524
|
-
context_data_position = 0;
|
|
525
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
526
|
-
context_data_val = 0;
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
context_data_position++;
|
|
530
|
-
}
|
|
531
|
-
value = value >> 1;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
context_enlargeIn--;
|
|
535
|
-
if (context_enlargeIn == 0) {
|
|
536
|
-
context_enlargeIn = Math.pow(2, context_numBits);
|
|
537
|
-
context_numBits++;
|
|
538
|
-
}
|
|
539
|
-
// Add wc to the dictionary.
|
|
540
|
-
context_dictionary[context_wc] = context_dictSize++;
|
|
541
|
-
context_w = String(context_c);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
// Output the code for w.
|
|
545
|
-
if (context_w !== '') {
|
|
546
|
-
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
|
|
547
|
-
if (context_w.charCodeAt(0) < 256) {
|
|
548
|
-
for (i = 0; i < context_numBits; i++) {
|
|
549
|
-
context_data_val = context_data_val << 1;
|
|
550
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
551
|
-
context_data_position = 0;
|
|
552
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
553
|
-
context_data_val = 0;
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
context_data_position++;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
value = context_w.charCodeAt(0);
|
|
560
|
-
for (i = 0; i < 8; i++) {
|
|
561
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
562
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
563
|
-
context_data_position = 0;
|
|
564
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
565
|
-
context_data_val = 0;
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
context_data_position++;
|
|
569
|
-
}
|
|
570
|
-
value = value >> 1;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
value = 1;
|
|
575
|
-
for (i = 0; i < context_numBits; i++) {
|
|
576
|
-
context_data_val = (context_data_val << 1) | value;
|
|
577
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
578
|
-
context_data_position = 0;
|
|
579
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
580
|
-
context_data_val = 0;
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
context_data_position++;
|
|
584
|
-
}
|
|
585
|
-
value = 0;
|
|
586
|
-
}
|
|
587
|
-
value = context_w.charCodeAt(0);
|
|
588
|
-
for (i = 0; i < 16; i++) {
|
|
589
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
590
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
591
|
-
context_data_position = 0;
|
|
592
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
593
|
-
context_data_val = 0;
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
context_data_position++;
|
|
597
|
-
}
|
|
598
|
-
value = value >> 1;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
context_enlargeIn--;
|
|
602
|
-
if (context_enlargeIn == 0) {
|
|
603
|
-
context_enlargeIn = Math.pow(2, context_numBits);
|
|
604
|
-
context_numBits++;
|
|
605
|
-
}
|
|
606
|
-
delete context_dictionaryToCreate[context_w];
|
|
607
|
-
}
|
|
608
|
-
else {
|
|
609
|
-
value = context_dictionary[context_w];
|
|
610
|
-
for (i = 0; i < context_numBits; i++) {
|
|
611
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
612
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
613
|
-
context_data_position = 0;
|
|
614
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
615
|
-
context_data_val = 0;
|
|
616
|
-
}
|
|
617
|
-
else {
|
|
618
|
-
context_data_position++;
|
|
619
|
-
}
|
|
620
|
-
value = value >> 1;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
context_enlargeIn--;
|
|
624
|
-
if (context_enlargeIn == 0) {
|
|
625
|
-
context_enlargeIn = Math.pow(2, context_numBits);
|
|
626
|
-
context_numBits++;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// Mark the end of the stream
|
|
630
|
-
value = 2;
|
|
631
|
-
for (i = 0; i < context_numBits; i++) {
|
|
632
|
-
context_data_val = (context_data_val << 1) | (value & 1);
|
|
633
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
634
|
-
context_data_position = 0;
|
|
635
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
636
|
-
context_data_val = 0;
|
|
637
|
-
}
|
|
638
|
-
else {
|
|
639
|
-
context_data_position++;
|
|
640
|
-
}
|
|
641
|
-
value = value >> 1;
|
|
642
|
-
}
|
|
643
|
-
// Flush the last char
|
|
644
|
-
while (true) {
|
|
645
|
-
context_data_val = context_data_val << 1;
|
|
646
|
-
if (context_data_position == bitsPerChar - 1) {
|
|
647
|
-
context_data.push(getCharFromInt(context_data_val));
|
|
648
|
-
break;
|
|
649
|
-
}
|
|
650
|
-
else {
|
|
651
|
-
context_data_position++;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
return context_data.join('');
|
|
655
|
-
},
|
|
656
|
-
decompress: function (compressed) {
|
|
657
|
-
if (compressed == null) {
|
|
658
|
-
return '';
|
|
659
|
-
}
|
|
660
|
-
if (compressed == '') {
|
|
661
|
-
return null;
|
|
662
|
-
}
|
|
663
|
-
return LZString._decompress(compressed.length, 32768, function (index) {
|
|
664
|
-
return compressed.charCodeAt(index);
|
|
665
|
-
});
|
|
666
|
-
},
|
|
667
|
-
_decompress: function (length, resetValue, getNextValue) {
|
|
668
|
-
const dictionary = [], result = [], data = { val: getNextValue(0), position: resetValue, index: 1 };
|
|
669
|
-
let enlargeIn = 4, dictSize = 4, numBits = 3, entry = '', i, w, bits, resb, maxpower, power, c;
|
|
670
|
-
for (i = 0; i < 3; i += 1) {
|
|
671
|
-
dictionary[i] = i;
|
|
672
|
-
}
|
|
673
|
-
bits = 0;
|
|
674
|
-
maxpower = Math.pow(2, 2);
|
|
675
|
-
power = 1;
|
|
676
|
-
while (power != maxpower) {
|
|
677
|
-
resb = data.val & data.position;
|
|
678
|
-
data.position >>= 1;
|
|
679
|
-
if (data.position == 0) {
|
|
680
|
-
data.position = resetValue;
|
|
681
|
-
data.val = getNextValue(data.index++);
|
|
682
|
-
}
|
|
683
|
-
bits |= (resb > 0 ? 1 : 0) * power;
|
|
684
|
-
power <<= 1;
|
|
685
|
-
}
|
|
686
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
687
|
-
switch ((bits)) {
|
|
688
|
-
case 0:
|
|
689
|
-
bits = 0;
|
|
690
|
-
maxpower = Math.pow(2, 8);
|
|
691
|
-
power = 1;
|
|
692
|
-
while (power != maxpower) {
|
|
693
|
-
resb = data.val & data.position;
|
|
694
|
-
data.position >>= 1;
|
|
695
|
-
if (data.position == 0) {
|
|
696
|
-
data.position = resetValue;
|
|
697
|
-
data.val = getNextValue(data.index++);
|
|
698
|
-
}
|
|
699
|
-
bits |= (resb > 0 ? 1 : 0) * power;
|
|
700
|
-
power <<= 1;
|
|
701
|
-
}
|
|
702
|
-
c = f(bits);
|
|
703
|
-
break;
|
|
704
|
-
case 1:
|
|
705
|
-
bits = 0;
|
|
706
|
-
maxpower = Math.pow(2, 16);
|
|
707
|
-
power = 1;
|
|
708
|
-
while (power != maxpower) {
|
|
709
|
-
resb = data.val & data.position;
|
|
710
|
-
data.position >>= 1;
|
|
711
|
-
if (data.position == 0) {
|
|
712
|
-
data.position = resetValue;
|
|
713
|
-
data.val = getNextValue(data.index++);
|
|
714
|
-
}
|
|
715
|
-
bits |= (resb > 0 ? 1 : 0) * power;
|
|
716
|
-
power <<= 1;
|
|
717
|
-
}
|
|
718
|
-
c = f(bits);
|
|
719
|
-
break;
|
|
720
|
-
case 2:
|
|
721
|
-
return '';
|
|
722
|
-
}
|
|
723
|
-
dictionary[3] = c;
|
|
724
|
-
w = c;
|
|
725
|
-
result.push(c);
|
|
726
|
-
while (true) {
|
|
727
|
-
if (data.index > length) {
|
|
728
|
-
return '';
|
|
729
|
-
}
|
|
730
|
-
bits = 0;
|
|
731
|
-
maxpower = Math.pow(2, numBits);
|
|
732
|
-
power = 1;
|
|
733
|
-
while (power != maxpower) {
|
|
734
|
-
resb = data.val & data.position;
|
|
735
|
-
data.position >>= 1;
|
|
736
|
-
if (data.position == 0) {
|
|
737
|
-
data.position = resetValue;
|
|
738
|
-
data.val = getNextValue(data.index++);
|
|
739
|
-
}
|
|
740
|
-
bits |= (resb > 0 ? 1 : 0) * power;
|
|
741
|
-
power <<= 1;
|
|
742
|
-
}
|
|
743
|
-
switch ((c = bits)) {
|
|
744
|
-
case 0:
|
|
745
|
-
bits = 0;
|
|
746
|
-
maxpower = Math.pow(2, 8);
|
|
747
|
-
power = 1;
|
|
748
|
-
while (power != maxpower) {
|
|
749
|
-
resb = data.val & data.position;
|
|
750
|
-
data.position >>= 1;
|
|
751
|
-
if (data.position == 0) {
|
|
752
|
-
data.position = resetValue;
|
|
753
|
-
data.val = getNextValue(data.index++);
|
|
754
|
-
}
|
|
755
|
-
bits |= (resb > 0 ? 1 : 0) * power;
|
|
756
|
-
power <<= 1;
|
|
757
|
-
}
|
|
758
|
-
dictionary[dictSize++] = f(bits);
|
|
759
|
-
c = dictSize - 1;
|
|
760
|
-
enlargeIn--;
|
|
761
|
-
break;
|
|
762
|
-
case 1:
|
|
763
|
-
bits = 0;
|
|
764
|
-
maxpower = Math.pow(2, 16);
|
|
765
|
-
power = 1;
|
|
766
|
-
while (power != maxpower) {
|
|
767
|
-
resb = data.val & data.position;
|
|
768
|
-
data.position >>= 1;
|
|
769
|
-
if (data.position == 0) {
|
|
770
|
-
data.position = resetValue;
|
|
771
|
-
data.val = getNextValue(data.index++);
|
|
772
|
-
}
|
|
773
|
-
bits |= (resb > 0 ? 1 : 0) * power;
|
|
774
|
-
power <<= 1;
|
|
775
|
-
}
|
|
776
|
-
dictionary[dictSize++] = f(bits);
|
|
777
|
-
c = dictSize - 1;
|
|
778
|
-
enlargeIn--;
|
|
779
|
-
break;
|
|
780
|
-
case 2:
|
|
781
|
-
return result.join('');
|
|
782
|
-
}
|
|
783
|
-
if (enlargeIn == 0) {
|
|
784
|
-
enlargeIn = Math.pow(2, numBits);
|
|
785
|
-
numBits++;
|
|
786
|
-
}
|
|
787
|
-
if (dictionary[c]) {
|
|
788
|
-
entry = dictionary[c];
|
|
789
|
-
}
|
|
790
|
-
else {
|
|
791
|
-
if (c === dictSize) {
|
|
792
|
-
entry = w + w.charAt(0);
|
|
793
|
-
}
|
|
794
|
-
else {
|
|
795
|
-
return null;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
result.push(entry);
|
|
799
|
-
// Add w+entry[0] to the dictionary.
|
|
800
|
-
dictionary[dictSize++] = w + entry.charAt(0);
|
|
801
|
-
enlargeIn--;
|
|
802
|
-
w = entry;
|
|
803
|
-
if (enlargeIn == 0) {
|
|
804
|
-
enlargeIn = Math.pow(2, numBits);
|
|
805
|
-
numBits++;
|
|
806
|
-
}
|
|
285
|
+
/**
|
|
286
|
+
* Gzip a string using Compression Streams API if it's available
|
|
287
|
+
*/
|
|
288
|
+
async function gzipCompress(input, isDebug = true) {
|
|
289
|
+
try {
|
|
290
|
+
// Turn the string into a stream using a Blob, and then compress it
|
|
291
|
+
const dataStream = new Blob([input], {
|
|
292
|
+
type: 'text/plain',
|
|
293
|
+
}).stream();
|
|
294
|
+
const compressedStream = dataStream.pipeThrough(new CompressionStream('gzip'));
|
|
295
|
+
// Using a Response to easily extract the readablestream value. Decoding into a string for fetch
|
|
296
|
+
return await new Response(compressedStream).blob();
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
if (isDebug) {
|
|
300
|
+
console.error('Failed to gzip compress data', error);
|
|
807
301
|
}
|
|
808
|
-
|
|
809
|
-
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
810
305
|
|
|
811
306
|
class SimpleEventEmitter {
|
|
812
307
|
constructor() {
|
|
@@ -1306,7 +801,6 @@ class PostHogCoreStateless {
|
|
|
1306
801
|
this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
|
|
1307
802
|
this.maxQueueSize = Math.max(this.flushAt, options?.maxQueueSize ?? 1000);
|
|
1308
803
|
this.flushInterval = options?.flushInterval ?? 10000;
|
|
1309
|
-
this.captureMode = options?.captureMode || 'json';
|
|
1310
804
|
this.preloadFeatureFlags = options?.preloadFeatureFlags ?? true;
|
|
1311
805
|
// If enable is explicitly set to false we override the optout
|
|
1312
806
|
this.defaultOptIn = options?.defaultOptIn ?? true;
|
|
@@ -1325,6 +819,7 @@ class PostHogCoreStateless {
|
|
|
1325
819
|
// Init promise allows the derived class to block calls until it is ready
|
|
1326
820
|
this._initPromise = Promise.resolve();
|
|
1327
821
|
this._isInitialized = true;
|
|
822
|
+
this.disableCompression = !isGzipSupported() || (options?.disableCompression ?? false);
|
|
1328
823
|
}
|
|
1329
824
|
logMsgIfDebug(fn) {
|
|
1330
825
|
if (this.isDebug) {
|
|
@@ -1507,13 +1002,9 @@ class PostHogCoreStateless {
|
|
|
1507
1002
|
/***
|
|
1508
1003
|
*** FEATURE FLAGS
|
|
1509
1004
|
***/
|
|
1510
|
-
async
|
|
1005
|
+
async getFlags(distinctId, groups = {}, personProperties = {}, groupProperties = {}, extraPayload = {}) {
|
|
1511
1006
|
await this._initPromise;
|
|
1512
|
-
|
|
1513
|
-
// This is a temporary measure to ensure that we can still use the old flags API
|
|
1514
|
-
// while we migrate to the new flags API
|
|
1515
|
-
const useFlags = isTokenInRollout(this.apiKey, NEW_FLAGS_ROLLOUT_PERCENTAGE, NEW_FLAGS_EXCLUDED_HASHES);
|
|
1516
|
-
const url = useFlags ? `${this.host}/flags/?v=2` : `${this.host}/decide/?v=4`;
|
|
1007
|
+
const url = `${this.host}/flags/?v=2&config=true`;
|
|
1517
1008
|
const fetchOptions = {
|
|
1518
1009
|
method: 'POST',
|
|
1519
1010
|
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
@@ -1526,11 +1017,11 @@ class PostHogCoreStateless {
|
|
|
1526
1017
|
...extraPayload,
|
|
1527
1018
|
}),
|
|
1528
1019
|
};
|
|
1529
|
-
this.logMsgIfDebug(() => console.log('PostHog Debug', '
|
|
1530
|
-
// Don't retry /
|
|
1020
|
+
this.logMsgIfDebug(() => console.log('PostHog Debug', 'Flags URL', url));
|
|
1021
|
+
// Don't retry /flags API calls
|
|
1531
1022
|
return this.fetchWithRetry(url, fetchOptions, { retryCount: 0 }, this.featureFlagsRequestTimeoutMs)
|
|
1532
1023
|
.then((response) => response.json())
|
|
1533
|
-
.then((response) =>
|
|
1024
|
+
.then((response) => normalizeFlagsResponse(response))
|
|
1534
1025
|
.catch((error) => {
|
|
1535
1026
|
this._events.emit('error', error);
|
|
1536
1027
|
return undefined;
|
|
@@ -1559,15 +1050,15 @@ class PostHogCoreStateless {
|
|
|
1559
1050
|
}
|
|
1560
1051
|
async getFeatureFlagDetailStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
1561
1052
|
await this._initPromise;
|
|
1562
|
-
const
|
|
1563
|
-
if (
|
|
1053
|
+
const flagsResponse = await this.getFeatureFlagDetailsStateless(distinctId, groups, personProperties, groupProperties, disableGeoip, [key]);
|
|
1054
|
+
if (flagsResponse === undefined) {
|
|
1564
1055
|
return undefined;
|
|
1565
1056
|
}
|
|
1566
|
-
const featureFlags =
|
|
1057
|
+
const featureFlags = flagsResponse.flags;
|
|
1567
1058
|
const flagDetail = featureFlags[key];
|
|
1568
1059
|
return {
|
|
1569
1060
|
response: flagDetail,
|
|
1570
|
-
requestId:
|
|
1061
|
+
requestId: flagsResponse.requestId,
|
|
1571
1062
|
};
|
|
1572
1063
|
}
|
|
1573
1064
|
async getFeatureFlagPayloadStateless(key, distinctId, groups = {}, personProperties = {}, groupProperties = {}, disableGeoip) {
|
|
@@ -1617,26 +1108,26 @@ class PostHogCoreStateless {
|
|
|
1617
1108
|
if (flagKeysToEvaluate) {
|
|
1618
1109
|
extraPayload['flag_keys_to_evaluate'] = flagKeysToEvaluate;
|
|
1619
1110
|
}
|
|
1620
|
-
const
|
|
1621
|
-
if (
|
|
1111
|
+
const flagsResponse = await this.getFlags(distinctId, groups, personProperties, groupProperties, extraPayload);
|
|
1112
|
+
if (flagsResponse === undefined) {
|
|
1622
1113
|
// We probably errored out, so return undefined
|
|
1623
1114
|
return undefined;
|
|
1624
1115
|
}
|
|
1625
|
-
// if there's an error on the
|
|
1626
|
-
if (
|
|
1116
|
+
// if there's an error on the flagsResponse, log a console error, but don't throw an error
|
|
1117
|
+
if (flagsResponse.errorsWhileComputingFlags) {
|
|
1627
1118
|
console.error('[FEATURE FLAGS] Error while computing feature flags, some flags may be missing or incorrect. Learn more at https://posthog.com/docs/feature-flags/best-practices');
|
|
1628
1119
|
}
|
|
1629
1120
|
// Add check for quota limitation on feature flags
|
|
1630
|
-
if (
|
|
1121
|
+
if (flagsResponse.quotaLimited?.includes(QuotaLimitedFeature.FeatureFlags)) {
|
|
1631
1122
|
console.warn('[FEATURE FLAGS] Feature flags quota limit exceeded - feature flags unavailable. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts');
|
|
1632
1123
|
return {
|
|
1633
1124
|
flags: {},
|
|
1634
1125
|
featureFlags: {},
|
|
1635
1126
|
featureFlagPayloads: {},
|
|
1636
|
-
requestId:
|
|
1127
|
+
requestId: flagsResponse?.requestId,
|
|
1637
1128
|
};
|
|
1638
1129
|
}
|
|
1639
|
-
return
|
|
1130
|
+
return flagsResponse;
|
|
1640
1131
|
}
|
|
1641
1132
|
/***
|
|
1642
1133
|
*** SURVEYS
|
|
@@ -1746,22 +1237,17 @@ class PostHogCoreStateless {
|
|
|
1746
1237
|
data.historical_migration = true;
|
|
1747
1238
|
}
|
|
1748
1239
|
const payload = JSON.stringify(data);
|
|
1749
|
-
const url = this.
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
: {
|
|
1761
|
-
method: 'POST',
|
|
1762
|
-
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1763
|
-
body: payload,
|
|
1764
|
-
};
|
|
1240
|
+
const url = `${this.host}/batch/`;
|
|
1241
|
+
const gzippedPayload = !this.disableCompression ? await gzipCompress(payload, this.isDebug) : null;
|
|
1242
|
+
const fetchOptions = {
|
|
1243
|
+
method: 'POST',
|
|
1244
|
+
headers: {
|
|
1245
|
+
...this.getCustomHeaders(),
|
|
1246
|
+
'Content-Type': 'application/json',
|
|
1247
|
+
...(gzippedPayload !== null && { 'Content-Encoding': 'gzip' }),
|
|
1248
|
+
},
|
|
1249
|
+
body: gzippedPayload || payload,
|
|
1250
|
+
};
|
|
1765
1251
|
try {
|
|
1766
1252
|
await this.fetchWithRetry(url, fetchOptions);
|
|
1767
1253
|
}
|
|
@@ -1885,22 +1371,17 @@ class PostHogCoreStateless {
|
|
|
1885
1371
|
data.historical_migration = true;
|
|
1886
1372
|
}
|
|
1887
1373
|
const payload = JSON.stringify(data);
|
|
1888
|
-
const url = this.
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
: {
|
|
1900
|
-
method: 'POST',
|
|
1901
|
-
headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
|
|
1902
|
-
body: payload,
|
|
1903
|
-
};
|
|
1374
|
+
const url = `${this.host}/batch/`;
|
|
1375
|
+
const gzippedPayload = !this.disableCompression ? await gzipCompress(payload, this.isDebug) : null;
|
|
1376
|
+
const fetchOptions = {
|
|
1377
|
+
method: 'POST',
|
|
1378
|
+
headers: {
|
|
1379
|
+
...this.getCustomHeaders(),
|
|
1380
|
+
'Content-Type': 'application/json',
|
|
1381
|
+
...(gzippedPayload !== null && { 'Content-Encoding': 'gzip' }),
|
|
1382
|
+
},
|
|
1383
|
+
body: gzippedPayload || payload,
|
|
1384
|
+
};
|
|
1904
1385
|
const retryOptions = {
|
|
1905
1386
|
retryCheck: (err) => {
|
|
1906
1387
|
// don't automatically retry on 413 errors, we want to reduce the batch size first
|
|
@@ -1945,11 +1426,21 @@ class PostHogCoreStateless {
|
|
|
1945
1426
|
const body = options.body ? options.body : '';
|
|
1946
1427
|
let reqByteLength = -1;
|
|
1947
1428
|
try {
|
|
1948
|
-
|
|
1429
|
+
if (body instanceof Blob) {
|
|
1430
|
+
reqByteLength = body.size;
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
reqByteLength = Buffer.byteLength(body, STRING_FORMAT);
|
|
1434
|
+
}
|
|
1949
1435
|
}
|
|
1950
1436
|
catch {
|
|
1951
|
-
|
|
1952
|
-
|
|
1437
|
+
if (body instanceof Blob) {
|
|
1438
|
+
reqByteLength = body.size;
|
|
1439
|
+
}
|
|
1440
|
+
else {
|
|
1441
|
+
const encoded = new TextEncoder().encode(body);
|
|
1442
|
+
reqByteLength = encoded.length;
|
|
1443
|
+
}
|
|
1953
1444
|
}
|
|
1954
1445
|
return await retriable(async () => {
|
|
1955
1446
|
let res = null;
|
|
@@ -2068,7 +1559,7 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2068
1559
|
const bootstrapFeatureFlags = bootstrap.featureFlags;
|
|
2069
1560
|
const bootstrapFeatureFlagPayloads = bootstrap.featureFlagPayloads ?? {};
|
|
2070
1561
|
if (bootstrapFeatureFlags && Object.keys(bootstrapFeatureFlags).length) {
|
|
2071
|
-
const normalizedBootstrapFeatureFlagDetails =
|
|
1562
|
+
const normalizedBootstrapFeatureFlagDetails = createFlagsResponseFromFlagsAndPayloads(bootstrapFeatureFlags, bootstrapFeatureFlagPayloads);
|
|
2072
1563
|
if (Object.keys(normalizedBootstrapFeatureFlagDetails.flags).length > 0) {
|
|
2073
1564
|
this.setBootstrappedFeatureFlagDetails(normalizedBootstrapFeatureFlagDetails);
|
|
2074
1565
|
const currentFeatureFlagDetails = this.getKnownFeatureFlagDetails() || { flags: {}, requestId: undefined };
|
|
@@ -2210,7 +1701,7 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2210
1701
|
...maybeAdd('$set_once', userPropsOnce),
|
|
2211
1702
|
});
|
|
2212
1703
|
if (distinctId !== previousDistinctId) {
|
|
2213
|
-
// We keep the AnonymousId to be used by
|
|
1704
|
+
// We keep the AnonymousId to be used by flags calls and identify to link the previousId
|
|
2214
1705
|
this.setPersistedProperty(PostHogPersistedProperty.AnonymousId, previousDistinctId);
|
|
2215
1706
|
this.setPersistedProperty(PostHogPersistedProperty.DistinctId, distinctId);
|
|
2216
1707
|
this.reloadFeatureFlags();
|
|
@@ -2303,10 +1794,6 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2303
1794
|
this.setPersistedProperty(PostHogPersistedProperty.PersonProperties, null);
|
|
2304
1795
|
});
|
|
2305
1796
|
}
|
|
2306
|
-
/** @deprecated - Renamed to setPersonPropertiesForFlags */
|
|
2307
|
-
personProperties(properties) {
|
|
2308
|
-
return this.setPersonPropertiesForFlags(properties);
|
|
2309
|
-
}
|
|
2310
1797
|
setGroupPropertiesForFlags(properties) {
|
|
2311
1798
|
this.wrap(() => {
|
|
2312
1799
|
// Get persisted group properties
|
|
@@ -2332,12 +1819,6 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2332
1819
|
this.setPersistedProperty(PostHogPersistedProperty.GroupProperties, null);
|
|
2333
1820
|
});
|
|
2334
1821
|
}
|
|
2335
|
-
/** @deprecated - Renamed to setGroupPropertiesForFlags */
|
|
2336
|
-
groupProperties(properties) {
|
|
2337
|
-
this.wrap(() => {
|
|
2338
|
-
this.setGroupPropertiesForFlags(properties);
|
|
2339
|
-
});
|
|
2340
|
-
}
|
|
2341
1822
|
async remoteConfigAsync() {
|
|
2342
1823
|
await this._initPromise;
|
|
2343
1824
|
if (this._remoteConfigResponsePromise) {
|
|
@@ -2348,12 +1829,12 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2348
1829
|
/***
|
|
2349
1830
|
*** FEATURE FLAGS
|
|
2350
1831
|
***/
|
|
2351
|
-
async
|
|
1832
|
+
async flagsAsync(sendAnonDistinctId = true) {
|
|
2352
1833
|
await this._initPromise;
|
|
2353
|
-
if (this.
|
|
2354
|
-
return this.
|
|
1834
|
+
if (this._flagsResponsePromise) {
|
|
1835
|
+
return this._flagsResponsePromise;
|
|
2355
1836
|
}
|
|
2356
|
-
return this.
|
|
1837
|
+
return this._flagsAsync(sendAnonDistinctId);
|
|
2357
1838
|
}
|
|
2358
1839
|
cacheSessionReplay(source, response) {
|
|
2359
1840
|
const sessionReplay = response?.sessionRecording;
|
|
@@ -2411,6 +1892,9 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2411
1892
|
else if (this.preloadFeatureFlags !== false) {
|
|
2412
1893
|
this.reloadFeatureFlags();
|
|
2413
1894
|
}
|
|
1895
|
+
if (!response.supportedCompression?.includes(Compression.GZipJS)) {
|
|
1896
|
+
this.disableCompression = true;
|
|
1897
|
+
}
|
|
2414
1898
|
remoteConfig = response;
|
|
2415
1899
|
}
|
|
2416
1900
|
return remoteConfig;
|
|
@@ -2421,8 +1905,8 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2421
1905
|
});
|
|
2422
1906
|
return this._remoteConfigResponsePromise;
|
|
2423
1907
|
}
|
|
2424
|
-
async
|
|
2425
|
-
this.
|
|
1908
|
+
async _flagsAsync(sendAnonDistinctId = true) {
|
|
1909
|
+
this._flagsResponsePromise = this._initPromise
|
|
2426
1910
|
.then(async () => {
|
|
2427
1911
|
const distinctId = this.getDistinctId();
|
|
2428
1912
|
const groups = this.props.$groups || {};
|
|
@@ -2432,7 +1916,7 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2432
1916
|
const extraProperties = {
|
|
2433
1917
|
$anon_distinct_id: sendAnonDistinctId ? this.getAnonymousId() : undefined,
|
|
2434
1918
|
};
|
|
2435
|
-
const res = await super.
|
|
1919
|
+
const res = await super.getFlags(distinctId, groups, personProperties, groupProperties, extraProperties);
|
|
2436
1920
|
// Add check for quota limitation on feature flags
|
|
2437
1921
|
if (res?.quotaLimited?.includes(QuotaLimitedFeature.FeatureFlags)) {
|
|
2438
1922
|
// Unset all feature flags by setting to null
|
|
@@ -2456,22 +1940,22 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2456
1940
|
};
|
|
2457
1941
|
}
|
|
2458
1942
|
this.setKnownFeatureFlagDetails(newFeatureFlagDetails);
|
|
2459
|
-
// Mark that we hit the /
|
|
2460
|
-
this.setPersistedProperty(PostHogPersistedProperty.
|
|
2461
|
-
this.cacheSessionReplay('
|
|
1943
|
+
// Mark that we hit the /flags endpoint so we can capture this in the $feature_flag_called event
|
|
1944
|
+
this.setPersistedProperty(PostHogPersistedProperty.FlagsEndpointWasHit, true);
|
|
1945
|
+
this.cacheSessionReplay('flags', res);
|
|
2462
1946
|
}
|
|
2463
1947
|
return res;
|
|
2464
1948
|
})
|
|
2465
1949
|
.finally(() => {
|
|
2466
|
-
this.
|
|
1950
|
+
this._flagsResponsePromise = undefined;
|
|
2467
1951
|
});
|
|
2468
|
-
return this.
|
|
1952
|
+
return this._flagsResponsePromise;
|
|
2469
1953
|
}
|
|
2470
1954
|
// We only store the flags and request id in the feature flag details storage key
|
|
2471
|
-
setKnownFeatureFlagDetails(
|
|
1955
|
+
setKnownFeatureFlagDetails(flagsResponse) {
|
|
2472
1956
|
this.wrap(() => {
|
|
2473
|
-
this.setPersistedProperty(PostHogPersistedProperty.FeatureFlagDetails,
|
|
2474
|
-
this._events.emit('featureflags', getFlagValuesFromFlags(
|
|
1957
|
+
this.setPersistedProperty(PostHogPersistedProperty.FeatureFlagDetails, flagsResponse);
|
|
1958
|
+
this._events.emit('featureflags', getFlagValuesFromFlags(flagsResponse?.flags ?? {}));
|
|
2475
1959
|
});
|
|
2476
1960
|
}
|
|
2477
1961
|
getKnownFeatureFlagDetails() {
|
|
@@ -2483,9 +1967,9 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2483
1967
|
if (featureFlags === undefined && featureFlagPayloads === undefined) {
|
|
2484
1968
|
return undefined;
|
|
2485
1969
|
}
|
|
2486
|
-
return
|
|
1970
|
+
return createFlagsResponseFromFlagsAndPayloads(featureFlags ?? {}, featureFlagPayloads ?? {});
|
|
2487
1971
|
}
|
|
2488
|
-
return
|
|
1972
|
+
return normalizeFlagsResponse(storedDetails);
|
|
2489
1973
|
}
|
|
2490
1974
|
getKnownFeatureFlags() {
|
|
2491
1975
|
const featureFlagDetails = this.getKnownFeatureFlagDetails();
|
|
@@ -2549,8 +2033,8 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2549
2033
|
...maybeAdd('$feature_flag_reason', featureFlag?.reason?.description ?? featureFlag?.reason?.code),
|
|
2550
2034
|
...maybeAdd('$feature_flag_bootstrapped_response', bootstrappedResponse),
|
|
2551
2035
|
...maybeAdd('$feature_flag_bootstrapped_payload', bootstrappedPayload),
|
|
2552
|
-
// If we haven't yet received a response from the /
|
|
2553
|
-
$used_bootstrap_value: !this.getPersistedProperty(PostHogPersistedProperty.
|
|
2036
|
+
// If we haven't yet received a response from the /flags endpoint, we must have used the bootstrapped value
|
|
2037
|
+
$used_bootstrap_value: !this.getPersistedProperty(PostHogPersistedProperty.FlagsEndpointWasHit),
|
|
2554
2038
|
...maybeAdd('$feature_flag_request_id', details.requestId),
|
|
2555
2039
|
});
|
|
2556
2040
|
}
|
|
@@ -2599,7 +2083,7 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2599
2083
|
...details,
|
|
2600
2084
|
flags,
|
|
2601
2085
|
};
|
|
2602
|
-
return
|
|
2086
|
+
return normalizeFlagsResponse(result);
|
|
2603
2087
|
}
|
|
2604
2088
|
getFeatureFlagsAndPayloads() {
|
|
2605
2089
|
const flags = this.getFeatureFlags();
|
|
@@ -2617,14 +2101,14 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2617
2101
|
return !!response;
|
|
2618
2102
|
}
|
|
2619
2103
|
// Used when we want to trigger the reload but we don't care about the result
|
|
2620
|
-
reloadFeatureFlags(
|
|
2621
|
-
this.
|
|
2104
|
+
reloadFeatureFlags(options) {
|
|
2105
|
+
this.flagsAsync(true)
|
|
2622
2106
|
.then((res) => {
|
|
2623
|
-
cb?.(undefined, res?.featureFlags);
|
|
2107
|
+
options?.cb?.(undefined, res?.featureFlags);
|
|
2624
2108
|
})
|
|
2625
2109
|
.catch((e) => {
|
|
2626
|
-
cb?.(e, undefined);
|
|
2627
|
-
if (!cb) {
|
|
2110
|
+
options?.cb?.(e, undefined);
|
|
2111
|
+
if (!options?.cb) {
|
|
2628
2112
|
this.logMsgIfDebug(() => console.log('PostHog Debug', 'Error reloading feature flags', e));
|
|
2629
2113
|
}
|
|
2630
2114
|
});
|
|
@@ -2632,8 +2116,8 @@ class PostHogCore extends PostHogCoreStateless {
|
|
|
2632
2116
|
async reloadRemoteConfigAsync() {
|
|
2633
2117
|
return await this.remoteConfigAsync();
|
|
2634
2118
|
}
|
|
2635
|
-
async reloadFeatureFlagsAsync(sendAnonDistinctId
|
|
2636
|
-
return (await this.
|
|
2119
|
+
async reloadFeatureFlagsAsync(sendAnonDistinctId) {
|
|
2120
|
+
return (await this.flagsAsync(sendAnonDistinctId ?? true))?.featureFlags;
|
|
2637
2121
|
}
|
|
2638
2122
|
onFeatureFlags(cb) {
|
|
2639
2123
|
return this.on('featureflags', async () => {
|