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