posthog-js-lite 3.6.0 → 4.0.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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Next
2
2
 
3
+ # 4.0.0 - 2025-06-10
4
+
5
+ ## Removed
6
+
7
+ 1. Remove `captureMode` in favor of `json` capture mode only
8
+ 2. Remove deprecated `personProperties` and `groupProperties` in favor of `setPersonPropertiesForFlags` and `setGroupPropertiesForFlags`
9
+
3
10
  # 3.6.0 – 2025-06-05
4
11
 
5
12
  ## Added
package/lib/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version = "3.6.0";
5
+ var version = "4.0.0";
6
6
 
7
7
  var PostHogPersistedProperty;
8
8
  (function (PostHogPersistedProperty) {
@@ -32,6 +32,12 @@ var PostHogPersistedProperty;
32
32
  PostHogPersistedProperty["Surveys"] = "surveys";
33
33
  PostHogPersistedProperty["RemoteConfig"] = "remote_config";
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";
@@ -377,436 +383,33 @@ function allSettled(promises) {
377
383
  return Promise.all(promises.map((p) => (p ?? Promise.resolve()).then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason }))));
378
384
  }
379
385
 
380
- // Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
381
- // This work is free. You can redistribute it and/or modify it
382
- // under the terms of the WTFPL, Version 2
383
- // For more information see LICENSE.txt or http://www.wtfpl.net/
384
- //
385
- // For more information, the home page:
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];
386
+ /**
387
+ * Older browsers and some runtimes don't support this yet
388
+ * This API (as of 2025-05-07) is not available on React Native.
389
+ */
390
+ function isGzipSupported() {
391
+ return 'CompressionStream' in globalThis;
401
392
  }
402
- const LZString = {
403
- compressToBase64: function (input) {
404
- if (input == null) {
405
- return '';
406
- }
407
- const res = LZString._compress(input, 6, function (a) {
408
- return keyStrBase64.charAt(a);
409
- });
410
- switch (res.length % 4 // To produce valid Base64
411
- ) {
412
- default: // When could this happen ?
413
- case 0:
414
- return res;
415
- case 1:
416
- return res + '===';
417
- case 2:
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
- }
393
+ /**
394
+ * Gzip a string using Compression Streams API if it's available
395
+ */
396
+ async function gzipCompress(input, isDebug = true) {
397
+ try {
398
+ // Turn the string into a stream using a Blob, and then compress it
399
+ const dataStream = new Blob([input], {
400
+ type: 'text/plain',
401
+ }).stream();
402
+ const compressedStream = dataStream.pipeThrough(new CompressionStream('gzip'));
403
+ // Using a Response to easily extract the readablestream value. Decoding into a string for fetch
404
+ return await new Response(compressedStream).blob();
405
+ }
406
+ catch (error) {
407
+ if (isDebug) {
408
+ console.error('Failed to gzip compress data', error);
807
409
  }
808
- },
809
- };
410
+ return null;
411
+ }
412
+ }
810
413
 
811
414
  class SimpleEventEmitter {
812
415
  constructor() {
@@ -1306,7 +909,6 @@ class PostHogCoreStateless {
1306
909
  this.maxBatchSize = Math.max(this.flushAt, options?.maxBatchSize ?? 100);
1307
910
  this.maxQueueSize = Math.max(this.flushAt, options?.maxQueueSize ?? 1000);
1308
911
  this.flushInterval = options?.flushInterval ?? 10000;
1309
- this.captureMode = options?.captureMode || 'json';
1310
912
  this.preloadFeatureFlags = options?.preloadFeatureFlags ?? true;
1311
913
  // If enable is explicitly set to false we override the optout
1312
914
  this.defaultOptIn = options?.defaultOptIn ?? true;
@@ -1325,6 +927,7 @@ class PostHogCoreStateless {
1325
927
  // Init promise allows the derived class to block calls until it is ready
1326
928
  this._initPromise = Promise.resolve();
1327
929
  this._isInitialized = true;
930
+ this.disableCompression = !isGzipSupported() || (options?.disableCompression ?? false);
1328
931
  }
1329
932
  logMsgIfDebug(fn) {
1330
933
  if (this.isDebug) {
@@ -1746,22 +1349,17 @@ class PostHogCoreStateless {
1746
1349
  data.historical_migration = true;
1747
1350
  }
1748
1351
  const payload = JSON.stringify(data);
1749
- const url = this.captureMode === 'form'
1750
- ? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
1751
- : `${this.host}/batch/`;
1752
- const fetchOptions = this.captureMode === 'form'
1753
- ? {
1754
- method: 'POST',
1755
- mode: 'no-cors',
1756
- credentials: 'omit',
1757
- headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
1758
- body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
1759
- }
1760
- : {
1761
- method: 'POST',
1762
- headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
1763
- body: payload,
1764
- };
1352
+ const url = `${this.host}/batch/`;
1353
+ const gzippedPayload = !this.disableCompression ? await gzipCompress(payload, this.isDebug) : null;
1354
+ const fetchOptions = {
1355
+ method: 'POST',
1356
+ headers: {
1357
+ ...this.getCustomHeaders(),
1358
+ 'Content-Type': 'application/json',
1359
+ ...(gzippedPayload !== null && { 'Content-Encoding': 'gzip' }),
1360
+ },
1361
+ body: gzippedPayload || payload,
1362
+ };
1765
1363
  try {
1766
1364
  await this.fetchWithRetry(url, fetchOptions);
1767
1365
  }
@@ -1885,22 +1483,17 @@ class PostHogCoreStateless {
1885
1483
  data.historical_migration = true;
1886
1484
  }
1887
1485
  const payload = JSON.stringify(data);
1888
- const url = this.captureMode === 'form'
1889
- ? `${this.host}/e/?ip=1&_=${currentTimestamp()}&v=${this.getLibraryVersion()}`
1890
- : `${this.host}/batch/`;
1891
- const fetchOptions = this.captureMode === 'form'
1892
- ? {
1893
- method: 'POST',
1894
- mode: 'no-cors',
1895
- credentials: 'omit',
1896
- headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/x-www-form-urlencoded' },
1897
- body: `data=${encodeURIComponent(LZString.compressToBase64(payload))}&compression=lz64`,
1898
- }
1899
- : {
1900
- method: 'POST',
1901
- headers: { ...this.getCustomHeaders(), 'Content-Type': 'application/json' },
1902
- body: payload,
1903
- };
1486
+ const url = `${this.host}/batch/`;
1487
+ const gzippedPayload = !this.disableCompression ? await gzipCompress(payload, this.isDebug) : null;
1488
+ const fetchOptions = {
1489
+ method: 'POST',
1490
+ headers: {
1491
+ ...this.getCustomHeaders(),
1492
+ 'Content-Type': 'application/json',
1493
+ ...(gzippedPayload !== null && { 'Content-Encoding': 'gzip' }),
1494
+ },
1495
+ body: gzippedPayload || payload,
1496
+ };
1904
1497
  const retryOptions = {
1905
1498
  retryCheck: (err) => {
1906
1499
  // don't automatically retry on 413 errors, we want to reduce the batch size first
@@ -1945,11 +1538,21 @@ class PostHogCoreStateless {
1945
1538
  const body = options.body ? options.body : '';
1946
1539
  let reqByteLength = -1;
1947
1540
  try {
1948
- reqByteLength = Buffer.byteLength(body, STRING_FORMAT);
1541
+ if (body instanceof Blob) {
1542
+ reqByteLength = body.size;
1543
+ }
1544
+ else {
1545
+ reqByteLength = Buffer.byteLength(body, STRING_FORMAT);
1546
+ }
1949
1547
  }
1950
1548
  catch {
1951
- const encoded = new TextEncoder().encode(body);
1952
- reqByteLength = encoded.length;
1549
+ if (body instanceof Blob) {
1550
+ reqByteLength = body.size;
1551
+ }
1552
+ else {
1553
+ const encoded = new TextEncoder().encode(body);
1554
+ reqByteLength = encoded.length;
1555
+ }
1953
1556
  }
1954
1557
  return await retriable(async () => {
1955
1558
  let res = null;
@@ -2303,10 +1906,6 @@ class PostHogCore extends PostHogCoreStateless {
2303
1906
  this.setPersistedProperty(PostHogPersistedProperty.PersonProperties, null);
2304
1907
  });
2305
1908
  }
2306
- /** @deprecated - Renamed to setPersonPropertiesForFlags */
2307
- personProperties(properties) {
2308
- return this.setPersonPropertiesForFlags(properties);
2309
- }
2310
1909
  setGroupPropertiesForFlags(properties) {
2311
1910
  this.wrap(() => {
2312
1911
  // Get persisted group properties
@@ -2332,12 +1931,6 @@ class PostHogCore extends PostHogCoreStateless {
2332
1931
  this.setPersistedProperty(PostHogPersistedProperty.GroupProperties, null);
2333
1932
  });
2334
1933
  }
2335
- /** @deprecated - Renamed to setGroupPropertiesForFlags */
2336
- groupProperties(properties) {
2337
- this.wrap(() => {
2338
- this.setGroupPropertiesForFlags(properties);
2339
- });
2340
- }
2341
1934
  async remoteConfigAsync() {
2342
1935
  await this._initPromise;
2343
1936
  if (this._remoteConfigResponsePromise) {
@@ -2411,6 +2004,9 @@ class PostHogCore extends PostHogCoreStateless {
2411
2004
  else if (this.preloadFeatureFlags !== false) {
2412
2005
  this.reloadFeatureFlags();
2413
2006
  }
2007
+ if (!response.supportedCompression?.includes(Compression.GZipJS)) {
2008
+ this.disableCompression = true;
2009
+ }
2414
2010
  remoteConfig = response;
2415
2011
  }
2416
2012
  return remoteConfig;