-
Notifications
You must be signed in to change notification settings - Fork 331
Expand file tree
/
Copy pathposthog-config.ts
More file actions
2360 lines (2124 loc) · 90.5 KB
/
Copy pathposthog-config.ts
File metadata and controls
2360 lines (2124 loc) · 90.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* PostHog configuration types
*/
import type { JsonType, Properties } from './common'
import type { LogAttributes, BeforeSendLogFn } from './capture-log'
import type { MetricAttributes, BeforeSendMetricFn } from './capture-metric'
import type { BeforeSendFn, CaptureResult } from './capture'
import type { RequestResponse } from './request'
import type {
CanvasMaskRegion,
CapturedNetworkRequest,
NetworkRequest,
SessionRecordingCanvasOptions,
} from './session-recording'
import type { SegmentAnalytics } from './segment'
import type { PostHog } from './posthog'
export type AutocaptureCompatibleElement = 'a' | 'button' | 'form' | 'input' | 'select' | 'textarea' | 'label'
export type DomAutocaptureEvents = 'click' | 'change' | 'submit'
/**
* If an array is passed for an allowlist, autocapture events will only be sent for elements matching
* at least one of the elements in the array. Multiple allowlists can be used
*/
export interface AutocaptureConfig {
/**
* List of URLs to allow autocapture on, can be strings to match
* or regexes e.g. ['https://example.com', 'test.com/.*']
* this is useful when you want to autocapture on specific pages only
*
* if you set both url_allowlist and url_ignorelist,
* we check the allowlist first and then the ignorelist.
* the ignorelist can override the allowlist
*/
url_allowlist?: (string | RegExp)[]
/**
* List of URLs to not allow autocapture on, can be strings to match
* or regexes e.g. ['https://example.com', 'test.com/.*']
* this is useful when you want to autocapture on most pages but not some specific ones
*
* if you set both url_allowlist and url_ignorelist,
* we check the allowlist first and then the ignorelist.
* the ignorelist can override the allowlist
*/
url_ignorelist?: (string | RegExp)[]
/**
* List of DOM events to allow autocapture on e.g. ['click', 'change', 'submit']
*/
dom_event_allowlist?: DomAutocaptureEvents[]
/**
* List of DOM elements to allow autocapture on
* e.g. ['a', 'button', 'form', 'input', 'select', 'textarea', 'label']
*
* We consider the tree of elements from the root to the target element of the click event
* so for the tree `div > div > button > svg`
* if the allowlist has `button` then we allow the capture when the `button` or the `svg` is the click target
* but not if either of the `div`s are detected as the click target
*/
element_allowlist?: AutocaptureCompatibleElement[]
/**
* List of CSS selectors to allow autocapture on
* e.g. ['[ph-capture]']
* we consider the tree of elements from the root to the target element of the click event
* so for the tree div > div > button > svg
* and allow list config `['[id]']`
* we will capture the click if the click-target or its parents has any id
*
* Everything is allowed when there's no allowlist
*/
css_selector_allowlist?: string[]
/**
* List of CSS selectors to ignore autocapture on
* e.g. ['[data-ph-no-autocapture]']
* we consider the tree of elements from the root to the target element of the click event
* so for the tree div > div > button > svg
* and ignore list config `['[id]']`
* we will ignore the click if the click-target or its parents has any id
*
* Nothing is ignored when there's an empty ignorelist, e.g. []
* If no ignorelist is set, we default to ignoring .ph-no-autocapture and [data-ph-no-autocapture]
* Note: providing any custom list fully replaces the defaults — include .ph-no-autocapture
* and [data-ph-no-autocapture] explicitly if you still want them honoured.
*/
css_selector_ignorelist?: string[]
/**
* Exclude certain element attributes from autocapture
* E.g. ['aria-label'] or [data-attr-pii]
*/
element_attribute_ignorelist?: string[]
/**
* When true, autocapture captures cut, copy, and paste interactions. Paste events do not contain pasted text.
*/
capture_copied_text?: boolean
}
export interface RageclickConfig {
/**
* List of CSS selectors to ignore rageclicks on
* e.g. ['.my-calendar-button']
* we consider the tree of elements from the root to the target element of the click event
* so for the tree div > div > button > svg
* and ignore list config `['[id]']`
* we will ignore the rageclick if the click-target or its parents has any id
*
* Nothing is ignored when there's an empty ignorelist, e.g. []
* If no ignorelist is set, we default to ignoring .ph-no-rageclick
* If an element has .ph-no-capture, it will always be ignored by rageclick and autocapture
*/
css_selector_ignorelist?: string[]
/**
* Controls automatic exclusion of elements by text content from rageclick detection.
* Useful for pagination buttons, loading spinners, and other repeatedly-clicked UI elements.
* - `true`: Use default keywords ['next', 'previous', 'prev', '>', '<']
* - `false`: Disable content-based exclusion
* - `string[]`: Use custom keywords (max 10 items, otherwise use css_selector_ignorelist)
*
* Checks if element text content or aria-label matches any of the keywords (case-insensitive).
* Word keywords match as substrings; symbol-only keywords (e.g. '+', '-', '>') match exactly,
* so they don't suppress text like "sign-up", "5 > 3", or "C++".
*
* @default undefined
* (`true` when `defaults` is `'2025-11-30'` or later;
* `['next', 'previous', 'prev', '>', '<', '+', '-', '−', '–']` when `defaults` is `'2026-05-30'` or later)
*/
content_ignorelist?: boolean | string[]
/**
* Excludes text-editing surfaces (textarea, text-like inputs, and contenteditable elements)
* from rageclick detection, since rapid repeated clicks there are double/triple-click text
* selection rather than rage.
* Enabled by default from the 2026-05-30 config defaults onwards.
* @default false
*/
ignore_text_selection?: boolean
/**
* Maximum pixel distance between clicks to still be considered a rage click.
* @default 30
*/
threshold_px?: number
/**
* Number of consecutive clicks within the timeout to qualify as a rage click.
* @default 3
*/
click_count?: number
/**
* Maximum time window (in milliseconds) between the first and last click.
* @default 1000
*/
timeout_ms?: number
}
export interface BootstrapConfig {
/**
* Distinct ID to use before the SDK has loaded persisted identity.
*/
distinctID?: string
/**
* Whether `distinctID` already identifies a known person profile.
*/
isIdentifiedID?: boolean
/**
* Feature flag values to use immediately until the SDK fetches fresh values.
*/
featureFlags?: Record<string, boolean | string>
/**
* Feature flag payloads to use together with bootstrapped `featureFlags`.
*/
featureFlagPayloads?: Record<string, JsonType>
/**
* Optionally provide a sessionID, this is so that you can provide an existing sessionID here to continue a user's session across a domain or device. It MUST be:
* - unique to this user
* - a valid UUID v7
* - the timestamp part must be <= the timestamp of the first event in the session
* - the timestamp of the last event in the session must be < the timestamp part + 24 hours
*/
sessionID?: string
}
export interface ResetOptions {
/**
* Whether to generate a new device ID as well as a new distinct ID.
* @default false
*/
resetDeviceID?: boolean
/**
* Identity, feature flag, and session values to apply after resetting.
*/
bootstrap?: BootstrapConfig
}
export type SupportedWebVitalsMetrics = 'LCP' | 'CLS' | 'FCP' | 'INP'
export interface PerformanceCaptureConfig {
/**
* Works with session replay to use the browser's native performance observer to capture performance metrics
*/
network_timing?: boolean
/**
* Use chrome's web vitals library to wrap fetch and capture web vitals
*
* When `cookieless_mode` is active, there is no client-side SessionIdManager; vitals are still
* captured. Nested `$web_vitals_*_event` payloads omit `$session_id` / `$window_id`; PostHog ingestion assigns
* `$session_id` server-side for cookieless traffic when project cookieless settings are enabled (same as other events).
*/
web_vitals?: boolean
/**
* We observe very large values reported by the Chrome web vitals library
* These outliers are likely not real, useful values, and we exclude them
* You can set this to 0 in order to include all values, NB this is not recommended
*
* @default 15 * 60 * 1000 (15 minutes)
*/
__web_vitals_max_value?: number
/**
* By default all 4 metrics are captured
* You can set this config to restrict which metrics are captured
* e.g. ['CLS', 'FCP'] to only capture those two metrics
* NB setting this does not override whether the capture is enabled
*
* @default ['LCP', 'CLS', 'FCP', 'INP']
*/
web_vitals_allowed_metrics?: SupportedWebVitalsMetrics[]
/**
* We delay flushing web vitals metrics to reduce the number of events we send
* This is the maximum time we will wait before sending the metrics
*
* @default 5000
*/
web_vitals_delayed_flush_ms?: number
/**
* Which web vitals metrics include attribution data. Attribution names the
* cause of a metric, such as the slow interaction target for INP or the load-phase
* breakdown for LCP, which is what makes a slow number diagnosable.
*
* Pass `true` to attribute all metrics, `false` for none, or an array to name them.
* The default attributes INP and LCP only. CLS is excluded by default because its
* attribution holds detached DOM nodes and can leak memory in single-page apps.
*
* @default ['INP', 'LCP']
*/
web_vitals_attribution?: boolean | SupportedWebVitalsMetrics[]
/**
* Scope web vitals metrics to the browser's Soft Navigation entries, so that
* client-side route changes in single-page apps each start a fresh measurement
* window instead of accumulating against the original hard-navigation timestamp.
*
* Without this, an SPA's LCP observer keeps treating the "largest paint so far"
* as belonging to the initial page load across every subsequent route change,
* which inflates LCP (and the other metrics) by the time spent on the app.
*
* This is a preview option (opt-in) because it relies on Chrome's Soft Navigation Detection API,
* which is still experimental. When enabled, PostHog loads a pinned stable web-vitals 6.x
* bundle and passes `reportSoftNavs` to the observers. The default path remains on the
* existing web-vitals 5.x bundle. In browsers without soft-nav support, metrics fall
* back to their existing hard-navigation behavior.
*
* @default false
*/
__preview_web_vitals_soft_navs?: boolean
}
export interface DeadClickCandidate {
node: Element
// clicks carry a MouseEvent, swipes carry the TouchEvent that ended the gesture
originalEvent: MouseEvent | TouchEvent
timestamp: number
// whether this candidate came from a click (default) or a touch swipe gesture
type?: 'click' | 'swipe'
// for swipe candidates, the dominant direction of the gesture
swipeDirection?: 'left' | 'right' | 'up' | 'down'
// for swipe candidates, the straight-line distance in CSS pixels between where the gesture started and ended
swipeDistancePx?: number
// time between click and the most recent scroll
scrollDelayMs?: number
// time between click and the most recent mutation
mutationDelayMs?: number
// time between click and the most recent selection changed event
selectionChangedDelayMs?: number
// delay between the click and the nearest visibility change within the suppression window, on
// either side — a tab going to or from hidden near a click (opening a new tab, or waking the
// tab) is a liveness signal, so it only ever suppresses a dead click, never causes one. recorded
// as the event fires (not read from a shared timestamp at check time) so a later transition
// can't overwrite the click-correlated one
visibilityChangedDelayMs?: number
// as above for window focus/blur — a click that opens a new window/popup may only surface as
// the current window losing focus, so this is the liveness signal for that case
focusChangedDelayMs?: number
// if neither scroll nor mutation seen before threshold passed
absoluteDelayMs?: number
}
/**
* Controls buffering and payload limits for exception steps added via `addExceptionStep`.
*
* NOTE: This type is also defined in `@posthog/core` (exception-steps.ts). Keep both in sync.
*/
export type ExceptionStepsConfig = {
/**
* Determines whether PostHog should collect exception steps and attach them to the next captured exception.
*
* @default true
*/
enabled?: boolean
/**
* The maximum UTF-8 byte budget for exception steps buffered in memory.
* Oldest steps are evicted when the budget is exceeded.
*
* @default 32768 (~32KB)
*/
max_bytes?: number
}
export type ExceptionAutoCaptureConfig = {
/**
* Determines whether PostHog should capture unhandled errors.
*
* @default true
*/
capture_unhandled_errors?: boolean
/**
* Determines whether PostHog should capture unhandled promise rejections.
*
* @default true
*/
capture_unhandled_rejections?: boolean
/**
* Determines whether PostHog should capture console errors.
*
* @default false
*/
capture_console_errors?: boolean
}
export type DeadClicksAutoCaptureConfig = {
/**
* We'll not consider a click to be a dead click, if it's followed by a scroll within `scroll_threshold_ms` milliseconds
*
* @default 100
*/
scroll_threshold_ms?: number
/**
* We'll not consider a click to be a dead click, if it's followed by a selection change within `selection_change_threshold_ms` milliseconds
*
* @default 100
*/
selection_change_threshold_ms?: number
/**
* We'll not consider a click to be a dead click, if it's followed by a mutation within `mutation_threshold_ms` milliseconds
*
* @default 2500
*/
mutation_threshold_ms?: number
/**
* By default, clicks with modifier keys (ctrl, shift, alt, meta/cmd) held down are not considered dead clicks,
* since these typically indicate intentional actions like "open in new tab".
*
* Set this to true to capture dead clicks even when modifier keys are held.
*
* @default false
*/
capture_clicks_with_modifier_keys?: boolean
/**
* Determines whether PostHog should also detect "dead swipes" — touch swipe gestures
* (typically on mobile/touch devices) that produce no observable screen change
* (no scroll, mutation or selection change while the gesture is in progress, and no
* scroll, mutation, selection or visibility change afterwards). These usually indicate
* a failed navigation, e.g. swiping to go back or to move a carousel with nothing
* happening.
*
* Dead swipes are captured as `$dead_swipe` events. This only applies to the dead-click
* autocapture path, not the heatmaps path.
*
* Swipes over surfaces whose response cannot be observed — canvas, video and other
* media/plugin elements under the finger — are never captured, and capture is limited
* per page load (see `max_dead_swipes_per_page_load`).
*
* @default true
*/
capture_dead_swipes?: boolean
/**
* The minimum straight-line distance in CSS pixels between where a touch gesture starts
* and ends for it to be considered a swipe (rather than a tap). Only used when
* `capture_dead_swipes` is enabled.
*
* @default 30
*/
swipe_threshold_px?: number
/**
* The maximum number of dead swipes captured per page load. Swipe gestures are plentiful
* on touch devices, so a page whose responses the detector cannot see is capped rather
* than allowed to flood events. Only used when `capture_dead_swipes` is enabled.
*
* @default 10
*/
max_dead_swipes_per_page_load?: number
/**
* List of CSS selectors to ignore dead clicks on
* e.g. ['.my-download-link']
* we consider the tree of elements from the root to the target element of the click event
* so for the tree div > div > a > svg
* and ignore list config `['[download]']`
* we will ignore the dead click if the click-target or its parents has any download attribute
*
* Nothing is ignored when there's an empty ignorelist, e.g. []
* If no ignorelist is set, we default to ignoring .ph-no-deadclick and .ph-no-capture
* A custom ignorelist fully replaces the default — include .ph-no-capture if you want it to suppress dead-click capture as well
*/
css_selector_ignorelist?: string[]
/**
* Allows setting behavior for when a dead click is captured.
* For e.g. to support capture to heatmaps
*
* If not provided the default behavior is to auto-capture dead click events
*
* Only intended to be provided by our own SDK
*/
__onCapture?: ((click: DeadClickCandidate, properties: Properties) => void) | undefined
} & Pick<AutocaptureConfig, 'element_attribute_ignorelist'>
export interface HeatmapConfig {
/**
* How often to send batched data in `$heatmap_data` events
* If set to 0 or not set, sends using the default interval of 1 second
*
* @default 1000
*/
flush_interval_milliseconds: number
}
/**
* Configuration defaults snapshot used by `PostHogConfig.defaults`.
* Later dates include all earlier default changes.
*/
export type ConfigDefaults =
| '2026-08-30'
| '2026-08-29'
| '2026-06-25'
| '2026-05-30'
| '2026-01-30'
| '2025-11-30'
| '2025-05-24'
| 'unset'
export type ExternalIntegrationKind = 'intercom' | 'crispChat'
/**
* Shared configuration for the error tracking burst-protection rate limiter.
*
* Burst protection is scoped **per exception type** — the limiter is keyed by exception type, so
* each distinct `$exception` type gets its own token bucket and there is no aggregate cap across
* all types. It applies only to autocaptured exceptions; manual `captureException` calls are
* never rate limited. These options let customers with high-cardinality exception types tune the
* per-type allowance, and are shared between the browser and Node SDKs.
*/
export interface ExceptionRateLimiterConfig {
/**
* ADVANCED: alters the refill rate for the error tracking rate limiter's token bucket.
* Normally only altered alongside PostHog support guidance.
* Accepts values between 0 and 100.
*
* @default 1
*/
exceptionRateLimiterRefillRate?: number
/**
* ADVANCED: alters the bucket size for the error tracking rate limiter's token bucket.
* Normally only altered alongside PostHog support guidance.
* Accepts values between 0 and 100.
*
* @default 10
*/
exceptionRateLimiterBucketSize?: number
}
export interface ErrorTrackingOptions extends ExceptionRateLimiterConfig {
/**
* Decide whether exceptions thrown by browser extensions or by scripts injected by the
* browser itself (for example Firefox for iOS and Chrome for iOS user scripts) should be
* captured. When false, both categories are dropped before capture.
*
* @default false
*/
captureExtensionExceptions?: boolean
/**
* UNSTABLE: determines whether exception caused by the PostHog SDK will be captured
*
* @default false
*/
__capturePostHogExceptions?: boolean
/**
* @deprecated Use {@link ExceptionRateLimiterConfig.exceptionRateLimiterRefillRate} instead.
* Still honoured as a fallback, but will be removed in a future major version.
*/
__exceptionRateLimiterRefillRate?: number
/**
* @deprecated Use {@link ExceptionRateLimiterConfig.exceptionRateLimiterBucketSize} instead.
* Still honoured as a fallback, but will be removed in a future major version.
*/
__exceptionRateLimiterBucketSize?: number
/**
* Controls buffering and payload limits for exception steps added via `addExceptionStep`.
*/
exception_steps?: ExceptionStepsConfig
}
/**
* Mask input options for session recording
*/
export interface MaskInputOptions {
color?: boolean
date?: boolean
'datetime-local'?: boolean
email?: boolean
month?: boolean
number?: boolean
range?: boolean
search?: boolean
tel?: boolean
text?: boolean
time?: boolean
url?: boolean
week?: boolean
textarea?: boolean
select?: boolean
password?: boolean
}
/**
* Slim DOM options for session recording
*/
export interface SlimDOMOptions {
script?: boolean
comment?: boolean
headFavicon?: boolean
headWhitespace?: boolean
headMetaDescKeywords?: boolean
headMetaSocial?: boolean
headMetaRobots?: boolean
headMetaHttpEquiv?: boolean
headMetaAuthorship?: boolean
headMetaVerification?: boolean
headTitleMutations?: boolean
}
/**
* Sampling options for session recording, a subset of rrweb's sampling strategy
*/
export interface SessionRecordingSamplingConfig {
/**
* Controls capture of mouse movement within a recorded session.
* `false` disables capture entirely; NB this also disables touchmove and drag capture.
* A number throttles capture so that positions are captured at most once every N milliseconds.
* When `undefined` (or `true`), rrweb's default applies: capture throttled to every 50ms.
* @default undefined
*/
mousemove?: boolean | number
/**
* When `false`, disables capture of mouse interaction events
* (click, mouse up/down, hover, and touch start/end).
* NB replays will not show clicks when this is disabled.
* @default undefined
*/
mouseInteraction?: boolean
}
export interface SessionRecordingOptions {
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default 'ph-no-capture'
*/
blockClass?: string | RegExp
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default null
*/
blockSelector?: string | null
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default 'ph-ignore-input'
*/
ignoreClass?: string | RegExp
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default 'ph-mask'
*/
maskTextClass?: string | RegExp
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
*/
maskTextSelector?: string | null
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
*/
maskTextFn?: ((text: string, element?: HTMLElement) => string) | null
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
*/
maskAllInputs?: boolean
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
*/
maskInputOptions?: Partial<MaskInputOptions>
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
*/
maskInputFn?: ((text: string, element?: HTMLElement) => string) | null
/**
* Derived from `rrweb.record` options. Masks every string-valued source DOM attribute,
* including rendering attributes such as `class`, `id`, `style`, `src`, `href`, and
* synthesized form values. Only rrweb-generated layout metadata is retained, so this
* option intentionally reduces replay fidelity. Mutually exclusive with
* `maskAttributeFn`: when both are set this option wins and the callback is ignored.
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default false
*/
maskAllElementAttributes?: boolean
/**
* Derived from `rrweb.record` options. Called with `(name, value, element)` for every
* non-empty string-valued attribute in the final serialized representation so you can mask
* specific attributes. Returning the original value leaves it visible. Mutually exclusive
* with `maskAllElementAttributes`: when both are set that option wins and this callback
* is ignored, so a callback cannot accidentally unmask what the coarse option hides.
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
*/
maskAttributeFn?: ((name: string, value: string, element?: Element) => string) | null
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default {}
*/
slimDOMOptions?: true | Partial<SlimDOMOptions> | 'all'
/**
* Captures sanitized Schema.org JSON-LD as session replay custom events.
* JSON-LD inside a text mask or blocked element is never captured.
* The recorder keeps `@id` values without changes.
* The event tag is `$json_ld`. The payload is a JSON-LD object or array.
* The recorder removes all script nodes from snapshots when this option is enabled.
* The JSON-LD observer starts only when this option is true at recording start.
* @see https://github.com/PostHog/posthog-js/blob/main/packages/browser/src/extensions/replay/external/json-ld.ts
* @default false before the `2026-08-30` defaults, otherwise true
*/
captureJsonLd?: boolean
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default false
*/
collectFonts?: boolean
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default true
*/
inlineStylesheet?: boolean
/**
* Max CSSRules inlined synchronously per full snapshot. Sheets past the
* budget keep their `rel`/`href` and are inlined across idle callbacks
* instead of blocking the snapshot; the queue is flushed synchronously
* (bounded) when recording stops and on `pagehide`. The residual risk:
* replay falls back to loading a sheet from its original href (which may
* be purged, auth-gated, or renamed by replay time) only if the tab dies
* without `pagehide` firing, the teardown flush hits its safety cap, or
* stringifying the sheet fails.
* The default is applied by posthog-js when it starts the recorder; the
* recorder itself is unbounded without it.
* Set 0 to inline everything up front (the pre-budget behaviour).
* @default 10000
*/
inlineStylesheetBudgetRules?: number
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default false
*/
recordCrossOriginIframes?: boolean
/**
* ADVANCED: limit which DOM attributes are observed for mutations, by passing
* the list to the native `MutationObserver` `attributeFilter`. Mutations to
* unlisted attributes never reach the recorder at all, so they cost no
* recording CPU - useful to exclude high-frequency inline `style` mutations
* from JS-driven animations on animation-heavy pages.
*
* Attributes left off the list are invisible to replay, so only set this when
* that loss of fidelity is acceptable. When unset (the default) or set to an
* empty array, all attributes are observed.
*
* Normally only altered alongside posthog support guidance.
*/
attributeFilter?: string[]
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default false
*/
recordHeaders?: boolean
/**
* Derived from `rrweb.record` options
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default false
*/
recordBody?: boolean
/**
* When recording network bodies, read them through a streaming reader that stops at the
* payload size limit instead of buffering the whole body and then discarding it. Bounds the
* memory and pre-request latency of capturing a very large body. Reads only a clone of the
* body, never the stream the page consumes.
* @default false
*/
streamNetworkBody?: boolean
/**
* Allows local config to override remote canvas recording settings from the flags response.
* To mask content inside a recorded canvas, see `canvasCapture.maskRegionsFn`.
*/
captureCanvas?: SessionRecordingCanvasOptions
/**
* Tune how canvas frames are captured for replay. Only has any effect when canvas recording
* is enabled.
*
* - `resolutionScale`: capture canvas frames at a fraction of their display resolution. A
* number in `(0, 1]`; `1` is full-resolution capture (the default) and, e.g., `0.6` captures
* at 60%. Out-of-range or non-finite values are clamped into `(0, 1]`. Aspect ratio is
* preserved and replay upscales the frame back to the original display size, so playback
* dimensions are unchanged, just softer. Resolution is the highest-leverage lever for canvas
* byte size, since bytes scale with pixel area.
* - `maskRegionsFn`: mask regions of a recorded canvas — see its doc comment.
*/
canvasCapture?: {
resolutionScale?: number
/**
* If set, called once per canvas per captured frame; the returned regions
* (CSS pixels, relative to the canvas element) are painted black before the
* frame is encoded. Lets apps that render into canvas (e.g. Flutter web)
* mask content that DOM-based masking cannot see. Re-read from config on
* every frame, so the real provider can be swapped in after recording has
* started.
*
* Return `[]` for a frame with nothing to mask (recorded as is), or `null`
* if regions could not be computed — that frame is skipped rather than
* recorded unmasked. Anything other than an array — `null`, a thrown error,
* or an implicit `undefined` from an untyped caller — skips that frame.
* Not setting this at all records the canvas unmasked.
*
* The provider is called for every canvas on the page, including
* canvases inside shadow DOM. For a canvas it does not manage, return
* `[]` ("nothing to mask") — returning `null` skips that canvas's
* frames entirely.
*
* Called synchronously on the main thread for every captured frame (canvas
* FPS is 4 by default, 12 max), so keep it cheap — avoid forcing layout,
* and return few regions.
*
* Setting this also changes DOM full snapshots (taken at recording start and
* at each `full_snapshot_interval_millis`): they normally serialize canvas
* pixels on a separate path (`rr_dataURL`) that never sees these regions, so
* when this option is set that serialization is skipped entirely. Whether
* to skip is re-evaluated at each snapshot, so a provider installed via
* `set_config` after recording started is honored at the next snapshot
* without a recorder restart. A canvas appears blank in a snapshot until
* the next canvas frame paints it — ~250ms at the default 4 fps while its
* pixels are changing, and at most 30s otherwise, because every canvas
* the provider answers (with regions or `[]`) re-sends an unchanged frame
* as a keyframe every 30s; a canvas whose frames are skipped (`null`)
* stays blank. Without this option, snapshot behavior is unchanged.
*
* An app whose real provider only exists once its runtime has booted picks
* what happens in between by what it declares in `posthog.init`: a function
* covering the whole canvas blacks those frames out, `() => null` skips
* them, and declaring nothing records them.
*
* Client-side only, cannot be set via remote configuration.
*
* @default undefined
*/
maskRegionsFn?: ((canvas: HTMLCanvasElement) => CanvasMaskRegion[] | null | undefined) | null
}
/**
* Modify the network request before it is captured. Returning null or undefined stops it being captured.
*
* Initial navigation and performance-timing entries are also passed to this function. They have
* `isInitial === true`, can have `method === undefined`, and contain the page URL in `name`. If the
* function returns null or undefined for an initial entry, PostHog retains the replay-required timing
* metadata but omits its URL, headers, and body. Return a modified entry to retain a redacted URL.
*/
maskCapturedNetworkRequestFn?: ((data: CapturedNetworkRequest) => CapturedNetworkRequest | null | undefined) | null
/** @deprecated - use maskCapturedNetworkRequestFn instead */
maskNetworkRequestFn?: ((data: NetworkRequest) => NetworkRequest | null | undefined) | null
/**
* ADVANCED: while a user is active we take a full snapshot of the browser every interval.
* For very few sites playback performance might be better with different interval.
* Set to 0 to disable
*
* @default 1000 * 60 * 5 (5 minutes)
*/
full_snapshot_interval_millis?: number
/**
* ADVANCED: controls how much recent replay data is kept in memory while session recording waits for a
* conditional trigger. The recorder periodically takes a full snapshot and discards older buffered events,
* so increasing this interval retains more pre-trigger history but can increase memory usage and
* CPU usage. Performance impacts on your site can start to be visible to users with larger values.
* Values must be between 1,000 ms and 3,600,000 ms (1 hour, inclusive); values outside this range,
* or non-finite values, are ignored.
*
* @default 1000 * 60 (1 minute)
*/
trigger_pending_buffer_interval_millis?: number
/**
* ADVANCED: whether to partially compress rrweb events before sending them to the server,
* defaults to true, can be set to false to disable partial compression
* NB requests are still compressed when sent to the server regardless of this setting
*
* @default true
*/
compress_events?: boolean
/**
* ADVANCED: Controls when session recording considers the user idle.
*
* If no replay user interaction occurs for this many milliseconds, the recorder marks the recording idle,
* emits a `sessionIdle` replay marker, flushes buffered replay events, and drops most subsequent replay
* events until user activity resumes. If activity resumes before `session_idle_timeout_seconds`, recording
* continues under the same `$session_id`.
*
* This does not control `$session_id` rotation. Session rotation is controlled by `session_idle_timeout_seconds`,
* so this value should be lower than `session_idle_timeout_seconds * 1000`.
*
* @default 1000 * 60 * 5 (5 minutes)
*/
session_idle_threshold_ms?: number
/**
* ADVANCED: alters the refill rate for the token bucket mutation throttling
* Normally only altered alongside posthog support guidance.
* Accepts values between 0 and 100
*
* @default 10
*/
__mutationThrottlerRefillRate?: number
/**
* ADVANCED: alters the bucket size for the token bucket mutation throttling
* Normally only altered alongside posthog support guidance.
* Accepts values between 0 and 100
*
* @default 100
*/
__mutationThrottlerBucketSize?: number
/**
* ADVANCED: the sustained mutation byte budget, in bytes per second.
* Mutation events beyond the budget are dropped and the recording resyncs with a full snapshot.
* Only takes effect when `__mutationBytesBucketSize` is set.
* Normally only altered alongside posthog support guidance.
*
* @default 25600
*/
__mutationBytesRefillRate?: number
/**
* ADVANCED: enables the mutation byte budget by setting its burst allowance, in bytes.
* Also the largest single mutation event that will be recorded.
* Unset (the default), the byte budget is off. 1048576 (1MB) is a sensible starting point.
* Normally only altered alongside posthog support guidance.
*
* @default undefined
*/
__mutationBytesBucketSize?: number
/**
* When true, minimum duration is checked against the actual buffer data (first to last timestamp)
* rather than session duration. This ensures recordings are not sent until they contain the minimum
* duration of actual data, even across page navigations.
*
* @default false
*/
strictMinimumDuration?: boolean
/**
* Derived from `rrweb.record` options. Controls how often certain event types are captured
* within an already-recorded session, e.g. `{ mousemove: false }` stops recording mouse movement.
*
* Not to be confused with `sampleRate` below, which controls whether a session is recorded
* at all, or with `posthog.startSessionRecording({ sampling: true })`, which overrides that
* session-level sample rate.
*
* NB disabled event types no longer count as user activity for replay idle detection
* (`session_idle_threshold_ms`). For example, with `mousemove: false` pure mouse movement
* no longer keeps a session active, while clicks, scrolls, and inputs still do.
*
* @see https://github.com/rrweb-io/rrweb/blob/master/guide.md
* @default undefined
*/
sampling?: SessionRecordingSamplingConfig
/**
* The sample rate for session recordings, a number between 0 and 1.
* For example, 0.5 means roughly 50% of sessions will be recorded.
*
* When `undefined`, falls back to the remote config setting.
* When set, takes precedence over the remote config.
*
* @default undefined
*/
sampleRate?: number
}
// we used to call a request that was sent to the queue with options attached `RequestQueueOptions`
// so we can't call the options used to configure the behavior of the RequestQueue that as well,
// so instead we call them config
export interface RequestQueueConfig {
/**
* ADVANCED - alters the frequency which PostHog sends events to the server.
* generally speaking this is only set when apps have automatic page refreshes, or very short visits.
* Defaults to 3 seconds when not set
* Allowed values between 250 and 5000
* */
flush_interval_ms?: number
}
/**
* Survey configuration options
*/
export interface SurveyConfig {
prefillFromUrl?: boolean
autoSubmitIfComplete?: boolean
autoSubmitDelay?: number
}