import { FeatureFlagValue, JsonType, PostHogCoreOptions } from '@posthog/core' import { KnownUnsafeEditableEvent } from '@posthog/core' import { Leanbase } from './leanbase' export const COPY_AUTOCAPTURE_EVENT = '$copy_autocapture' export type Property = any export type Properties = Record export type AutocaptureCompatibleElement = 'a' | 'button' | 'form' | 'input' | 'select' | 'textarea' | 'label' export type DomAutocaptureEvents = 'click' | 'change' | 'submit' export interface BootstrapConfig { distinctId?: string isIdentifiedId?: boolean featureFlags?: Record featureFlagPayloads?: Record /** * 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 } /** * 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[] /** * Exclude certain element attributes from autocapture * E.g. ['aria-label'] or [data-attr-pii] */ element_attribute_ignorelist?: string[] /** * When set to true, autocapture will capture the text of any element that is cut or copied. */ capture_copied_text?: boolean } export interface LeanbaseConfig extends Partial { /** * API host for Leanbase * @default 'https://i.leanbase.co' */ host?: string /** * The token for your Leanbase project. * It should NOT be provided manually in the config, but rather passed as the first parameter to `leanbase.init()`. */ token: string /** * Enables debug mode, which provides more verbose logging for development purposes. * @default false */ debug?: boolean /** * Determines whether Leanbase should autocapture events. * This setting does not affect capturing pageview events (see `capture_pageview`). * * by default autocapture is ignored on elements that match a `ph-no-capture` css class on the element or a parent * @default true */ autocapture: boolean | AutocaptureConfig /** * Determines whether Leanbase should capture pageview events automatically. * Can be: * - `true`: Capture regular pageviews (default) * - `false`: Don't capture any pageviews * - `'history_change'`: Only capture pageviews on history API changes (pushState, replaceState, popstate) * * @default true */ capture_pageview: boolean | 'history_change' /** * Determines the session idle timeout in seconds. * Any new event that's happened after this timeout will create a new session. * * @default 30 * 60 -- 30 minutes */ session_idle_timeout_seconds: number /** * An object containing the `distinctID`, `isIdentifiedID`, and `featureFlags` keys, * where `distinctID` is a string, and `featureFlags` is an object of key-value pairs. * * Since there is a delay between initializing PostHog and fetching feature flags, * feature flags are not always available immediately. * This makes them unusable if you want to do something like redirecting a user * to a different page based on a feature flag. * * You can, therefore, fetch the feature flags in your server and pre-fill them here, * allowing PostHog to know the feature flag values immediately. * * After the SDK fetches feature flags from PostHog, it will use those flag values instead of bootstrapped ones. * * @default {} */ bootstrap: BootstrapConfig /** * Determines whether Leanbase should capture pageleave events. * If set to `true`, it will capture pageleave events for all pages. * If set to `'if_capture_pageview'`, it will only capture pageleave events if `capture_pageview` is also set to `true` or `'history_change'`. * * @default 'if_capture_pageview' */ capture_pageleave: boolean | 'if_capture_pageview' /** * Determines whether Leanbase should capture rage clicks. * * by default rageclicks are ignored on elements that match a `ph-no-capture` or `ph-no-rageclick` css class on the element or a parent * @default true */ rageclick: boolean | RageclickConfig /** * Determines where to store the Leanbase persistence information. */ persistence: 'localStorage' | 'cookie' | 'memory' | 'localStorage+cookie' | 'sessionStorage' /** * The name for the super properties persistent store * * @default '' */ persistence_name: string /** * Prevent autocapture from capturing any attribute names on elements. * * @default false */ mask_all_element_attributes: boolean /** * Prevent autocapture from capturing `textContent` on elements. * * @default false */ mask_all_text: boolean /** * Used to extend the list of campaign parameters that are saved by default. * * @see {CAMPAIGN_PARAMS} from './utils/event-utils' - Default campaign parameters like utm_source, utm_medium, etc. * @default [] */ custom_campaign_params: string[] /** * Mask personal data properties from the current URL. * This will mask personal data properties such as advertising IDs (gclid, fbclid, etc.), and you can also add * custom properties to mask with `custom_personal_data_properties`. * @default false * @see {PERSONAL_DATA_CAMPAIGN_PARAMS} - Default campaign parameters that are masked by default. * @see {LeanbaseConfig.custom_personal_data_properties} - Custom list of personal data properties to mask. */ mask_personal_data_properties: boolean /** * Custom list of personal data properties to mask. * * E.g. if you added `email` to this list, then any `email` property in the URL will be masked. * https://www.example.com/login?email=john.doe%40example.com => https://www.example.com/login?email= * * @default [] * @see {LeanbaseConfig.mask_personal_data_properties} - Must be enabled for this to take effect. */ custom_personal_data_properties: string[] /** * Determines the number of days to store cookies for. * * @default 365 */ cookie_expiration: number /** * Determines whether Leanbase should use secure cookies. * If this is `true`, Leanbase cookies will be marked as secure, * meaning they will only be transmitted over HTTPS. * * @default window.location.protocol === 'https:' */ secure_cookie: boolean /** * Determines if cookie should be set on the top level domain (example.com). * If leanbase-js is loaded on a subdomain (test.example.com), and `cross_subdomain_cookie` is set to false, * it'll set the cookie on the subdomain only (test.example.com). * * NOTE: It will be set to `false` if we detect that the domain is a subdomain of a platform that is excluded from cross-subdomain cookie setting. * The current list of excluded platforms is `herokuapp.com`, `vercel.app`, and `netlify.app`. * * @see `isCrossDomainCookie` * @default true */ cross_subdomain_cookie: boolean /** * Determines whether Leanbase should disable persistence. * If set to `true`, the library will not save any data to the browser. It will also delete any data previously saved to the browser. * * @default false */ disable_persistence: boolean /** * Enables cookieless mode. In this mode, Leanbase will not set any cookies, or use session or local storage. User * identity is handled by generating a privacy-preserving hash on Leanbase's servers. * - 'always' - enable cookieless mode immediately on startup, use this if you do not intend to show a cookie banner * - 'on_reject' - enable cookieless mode only if the user rejects cookies, use this if you want to show a cookie banner. If the user accepts cookies, cookieless mode will not be used, and PostHog will use cookies and local storage as usual. * * Note that you MUST enable cookieless mode in your Leanbase project's settings, otherwise all your cookieless events will be ignored. We plan to remove this requirement in the future. * */ cookieless_mode?: 'always' | 'on_reject' /** * Determines whether PostHog should save referrer information. * * @default true */ save_referrer: boolean /** * Determines whether PostHog should save marketing parameters. * These are `utm_*` paramaters and friends. * * @see {CAMPAIGN_PARAMS} from './utils/event-utils' - Default campaign parameters like utm_source, utm_medium, etc. * @default true */ save_campaign_params: boolean /** * Determines whether to disable scroll properties. * These allow you to keep track of how far down someone scrolled in your website. * * @default false */ disable_scroll_properties?: boolean /** * Let the pageview scroll stats use a custom css selector for the root element, e.g. `main` * It will use `window.document.documentElement` if not specified. */ scroll_root_selector?: string | string[] /** * Determines if users should be opted out of user agent filtering such as googlebot or other bots. * If this is set to `true`, PostHog will set `$browser_type` to either `bot` or `browser` for all events, * but will process all events as if they were from a browser. * * @default false */ opt_out_useragent_filter: boolean /** * Determines the maximum length of the properties string that can be sent with capture calls. * * @default 65535 */ properties_string_max_length: number /** * A function to be called once the Leanbase scripts have loaded successfully. * * @param instance - The Leanbase instance that has been loaded. */ loaded: (instance: Leanbase) => void } 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[] } export type PropertyMatchType = 'regex' | 'not_regex' | 'exact' | 'is_not' | 'icontains' | 'not_icontains' export interface ErrorTrackingSuppressionRule { type: 'AND' | 'OR' values: ErrorTrackingSuppressionRuleValue[] } export interface ErrorTrackingSuppressionRuleValue { key: '$exception_types' | '$exception_values' operator: PropertyMatchType value: string | string[] type: string } export enum Compression { GZipJS = 'gzip-js', Base64 = 'base64', } 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 */ 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 } /** * Remote configuration for the Leanbase instance * * All of these settings can be configured directly in your Leanbase instance * Any configuration set in the client overrides the information from the server */ export interface RemoteConfig { /** * Supported compression algorithms */ supportedCompression: Compression[] /** * If set, disables autocapture */ autocapture_opt_out?: boolean /** * originally capturePerformance was replay only and so boolean true * is equivalent to { network_timing: true } * now capture performance can be separately enabled within replay * and as a standalone web vitals tracker * people can have them enabled separately * they work standalone but enhance each other * TODO: deprecate this so we make a new config that doesn't need this explanation */ capturePerformance?: boolean | PerformanceCaptureConfig /** * Whether we should use a custom endpoint for analytics * * @default { endpoint: "/e" } */ analytics?: { endpoint?: string } /** * Whether the `$elements_chain` property should be sent as a string or as an array * * @default false */ elementsChainAsString?: boolean /** * Error tracking configuration options */ errorTracking?: { autocaptureExceptions?: boolean captureExtensionExceptions?: boolean suppressionRules?: ErrorTrackingSuppressionRule[] } /** * This is currently in development and may have breaking changes without a major version bump */ autocaptureExceptions?: boolean | { endpoint?: string } /** * @deprecated, moved to toolbarParams */ toolbarVersion: 'toolbar' /** * Whether the user is authenticated */ isAuthenticated: boolean /** * List of site apps with their IDs and URLs */ siteApps: { id: string; url: string }[] /** * Whether heatmaps are enabled */ heatmaps?: boolean /** * Whether to only capture identified users by default */ defaultIdentifiedOnly?: boolean /** * Whether to capture dead clicks */ captureDeadClicks?: boolean /** * Indicates if the team has any flags enabled (if not we don't need to load them) */ hasFeatureFlags?: boolean } /** * These are known events Leanbase events that can be processed by the `beforeCapture` function * That means Leanbase functionality does not rely on receiving 100% of these for calculations * So, it is safe to sample them to reduce the volume of events sent to Leanbase */ export type KnownEventName = | '$heatmaps_data' | '$opt_in' | '$exception' | '$$heatmap' | '$web_vitals' | '$dead_click' | '$autocapture' | typeof COPY_AUTOCAPTURE_EVENT | '$rageclick' export type EventName = KnownUnsafeEditableEvent | KnownEventName | (string & {}) export interface PersistentStore { _is_supported: () => boolean _error: (error: any) => void _parse: (name: string) => any _get: (name: string) => any _set: ( name: string, value: any, expire_days?: number | null, cross_subdomain?: boolean, secure?: boolean, debug?: boolean ) => void _remove: (name: string, cross_subdomain?: boolean) => void } export interface RequestResponse { statusCode: number text?: string json?: any } export type RequestCallback = (response: RequestResponse) => void // See https://nextjs.org/docs/app/api-reference/functions/fetch#fetchurl-options type NextOptions = { revalidate: false | 0 | number; tags: string[] } export interface RequestWithOptions { url: string data?: Record | Record[] headers?: Record transport?: 'XHR' | 'fetch' | 'sendBeacon' method?: 'POST' | 'GET' urlQueryArgs?: { compression: Compression } callback?: RequestCallback timeout?: number noRetries?: boolean disableTransport?: ('XHR' | 'fetch' | 'sendBeacon')[] disableXHRCredentials?: boolean compression?: Compression | 'best-available' fetchOptions?: { cache?: RequestInit['cache'] next?: NextOptions } } export type SessionIdChangedCallback = ( sessionId: string, windowId: string | null | undefined, changeReason?: { noSessionId: boolean; activityTimeout: boolean; sessionPastMaximumLength: boolean } ) => void export type LeanbasegCaptureOptions = { /** If provided overrides the auto-generated event ID */ uuid?: string disableGeoip?: boolean /** * Used when `$identify` is called * Will set person properties overriding previous values */ $set?: Properties /** * Used when `$identify` is called * Will set person properties but only once, it will NOT override previous values */ $set_once?: Properties /** * Used to override the desired endpoint for the captured event */ _url?: string /** * key of queue, e.g. 'sessionRecording' vs 'event' */ _batchKey?: string /** * If set, overrides and disables config.properties_string_max_length */ _noTruncate?: boolean /** * If set, skips the batched queue */ send_instantly?: boolean /** * If set, skips the client side rate limiting */ skip_client_rate_limiting?: boolean /** * If set, overrides the desired transport method */ transport?: RequestWithOptions['transport'] /** * If set, overrides the current timestamp */ timestamp?: Date } export interface CaptureResult { uuid: string event: EventName properties: Properties $set?: Properties $set_once?: Properties timestamp?: Date }